Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-Studio-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : /**
24 : \file
25 : Implementation of class Score (partial).
26 : */
27 :
28 : #include "score.h"
29 :
30 : #include <cmath>
31 : #include <map>
32 :
33 : #include "async/channel.h"
34 : #include "containers.h"
35 :
36 : #include "iengravingconfiguration.h" // IWYU pragma: keep
37 : #include "iengravingcontextconfiguration.h" // IWYU pragma: keep
38 : #include "ipalettescoreprovider.h"
39 :
40 : #include "editing/addremoveelement.h"
41 : #include "editing/editclef.h"
42 : #include "editing/editkeysig.h"
43 : #include "editing/editstavesharing.h"
44 : #include "editing/mscoreview.h"
45 : #include "editing/splitjoinmeasure.h"
46 : #include "editing/transaction/transaction.h"
47 : #include "editing/transaction/undostack.h"
48 : #include "editing/transpose.h"
49 : #include "editing/editstaffbrackets.h"
50 :
51 : #include "rendering/iscorerenderer.h"
52 :
53 : #include "style/style.h"
54 : #include "style/defaultstyle.h"
55 :
56 : #include "iengravingfont.h"
57 : #include "types/translatablestring.h"
58 : #include "types/typesconv.h"
59 :
60 : #include "anchors.h"
61 : #include "beam.h"
62 : #include "box.h"
63 : #include "bracket.h"
64 : #include "capo.h"
65 : #include "chord.h"
66 : #include "clef.h"
67 : #include "dummyparent.h"
68 : #include "excerpt.h"
69 : #include "factory.h"
70 : #include "glissando.h"
71 : #include "guitarbend.h"
72 : #include "fret.h"
73 : #include "harmony.h"
74 : #include "imageStore.h"
75 : #include "instrchange.h"
76 : #include "instrtemplate.h"
77 : #include "key.h"
78 : #include "keysig.h"
79 : #include "linkedobjects.h"
80 : #include "lyrics.h"
81 : #include "masterscore.h"
82 : #include "measure.h"
83 : #include "mscore.h"
84 : #include "note.h"
85 : #include "ottava.h"
86 : #include "page.h"
87 : #include "palmmute.h"
88 : #include "part.h"
89 : #include "partialtie.h"
90 : #include "pitchspelling.h"
91 : #include "rehearsalmark.h"
92 : #include "repeatlist.h"
93 : #include "rest.h"
94 : #include "rootitem.h"
95 : #include "scoreorder.h"
96 : #include "segment.h"
97 : #include "select.h"
98 : #include "shadownote.h"
99 : #include "sig.h"
100 : #include "slur.h"
101 : #include "staff.h"
102 : #include "stafftextbase.h"
103 : #include "stafftype.h"
104 : #include "synthesizerstate.h"
105 : #include "system.h"
106 : #include "systemdivider.h"
107 : #include "tempotext.h"
108 : #include "tempotimeline.h"
109 : #include "text.h"
110 : #include "tie.h"
111 : #include "tiemap.h"
112 : #include "timesig.h"
113 : #include "tuplet.h"
114 : #include "utils.h"
115 : #include "volta.h"
116 :
117 : #ifndef ENGRAVING_NO_ACCESSIBILITY
118 : #include "accessibility/accessibleitem.h"
119 : #include "accessibility/accessibleroot.h"
120 : #endif
121 :
122 : #include "log.h"
123 :
124 : using namespace mu;
125 : using namespace muse;
126 : using namespace mu::engraving;
127 :
128 : namespace mu::engraving {
129 : std::set<Score*> Score::validScores;
130 :
131 : bool noSeq = false;
132 : bool noMidi = false;
133 : bool midiInputTrace = false;
134 : bool midiOutputTrace = false;
135 :
136 3077 : static void markInstrumentsAsPrimary(std::vector<Part*>& parts)
137 : {
138 3092 : TRACEFUNC;
139 :
140 3077 : std::unordered_map<String /*instrumentId*/, int /*count*/> instrumentCount;
141 :
142 19114 : for (Part* part : parts) {
143 16037 : Instrument* instrument = part->instrument();
144 16037 : if (!instrument) {
145 0 : continue;
146 : }
147 :
148 16037 : auto it = instrumentCount.find(instrument->id());
149 16037 : if (it == instrumentCount.cend()) {
150 3561 : it = instrumentCount.insert(instrumentCount.begin(), { instrument->id(), 0 });
151 : }
152 :
153 16037 : it->second++;
154 :
155 16037 : bool isPrimary = (it->second % 2 != 0);
156 16037 : instrument->setIsPrimary(isPrimary);
157 : }
158 3077 : }
159 :
160 : //---------------------------------------------------------
161 : // Score
162 : //---------------------------------------------------------
163 :
164 2334 : Score::Score(const modularity::ContextPtr& iocCtx)
165 0 : : EngravingObject(ElementType::SCORE, nullptr), muse::Contextable(iocCtx),
166 2334 : contextConfiguration{this},
167 2334 : elementsProvider{this},
168 2334 : paletteScoreProvider{this},
169 2334 : m_selection(this),
170 11670 : m_elementDestroyed(muse::async::makeOpt().disableWaitPendingsOnSend())
171 : {
172 2334 : if (elementsProvider()) {
173 0 : elementsProvider()->reg(this);
174 : }
175 :
176 2334 : Score::validScores.insert(this);
177 2334 : m_masterScore = nullptr;
178 :
179 4668 : m_engravingFont = engravingFonts()->fontByName("Leland");
180 :
181 2334 : m_fileDivision = Constants::DIVISION;
182 2334 : m_style = DefaultStyle::defaultStyle();
183 :
184 2334 : m_rootItem = new RootItem(this, RootItem::Kind::Score);
185 2334 : m_rootItem->init();
186 :
187 2334 : m_dummy = new DummyParent(this);
188 2334 : m_dummy->init();
189 :
190 2334 : updatePaddingTables();
191 :
192 2334 : m_shadowNote = new ShadowNote(this);
193 2334 : m_shadowNote->setVisible(false);
194 7002 : }
195 :
196 165 : Score::Score(MasterScore* parent, bool forcePartStyle /* = true */)
197 165 : : Score{parent->iocContext()}
198 : {
199 165 : Score::validScores.insert(this);
200 165 : m_masterScore = parent;
201 :
202 165 : if (DefaultStyle::defaultStyleForParts()) {
203 0 : m_style = *DefaultStyle::defaultStyleForParts();
204 : } else {
205 : // inherit most style settings from parent
206 165 : m_style = parent->style();
207 :
208 : static const Sid styles[] = {
209 : Sid::pageWidth,
210 : Sid::pageHeight,
211 : Sid::pagePrintableWidth,
212 : Sid::pageEvenLeftMargin,
213 : Sid::pageOddLeftMargin,
214 : Sid::pageEvenTopMargin,
215 : Sid::pageEvenBottomMargin,
216 : Sid::pageOddTopMargin,
217 : Sid::pageOddBottomMargin,
218 : Sid::pageTwosided,
219 : Sid::spatium
220 : };
221 : // but borrow defaultStyle page layout settings
222 1980 : for (auto i : styles) {
223 1815 : m_style.set(i, DefaultStyle::defaultStyle().value(i));
224 : }
225 : // and force some style settings that just make sense for parts
226 165 : if (forcePartStyle) {
227 165 : style().set(Sid::concertPitch, false);
228 165 : style().set(Sid::createMultiMeasureRests, true);
229 165 : style().set(Sid::dividerLeft, false);
230 165 : style().set(Sid::dividerRight, false);
231 : }
232 : }
233 : // update style values
234 165 : m_style.precomputeValues();
235 165 : checkChordList();
236 165 : m_synthesizerState = parent->m_synthesizerState;
237 165 : m_mscVersion = parent->m_mscVersion;
238 165 : updatePaddingTables();
239 165 : }
240 :
241 165 : Score::Score(MasterScore* parent, const MStyle& s)
242 165 : : Score{parent}
243 : {
244 165 : Score::validScores.insert(this);
245 165 : m_style = s;
246 165 : updatePaddingTables();
247 165 : }
248 :
249 : //---------------------------------------------------------
250 : // ~Score
251 : //---------------------------------------------------------
252 :
253 2246 : Score::~Score()
254 : {
255 2095 : Score::validScores.erase(this);
256 :
257 2095 : for (MuseScoreView* v : m_viewer) {
258 0 : v->removeScore();
259 : }
260 : // deselectAll();
261 2095 : muse::DeleteAll(m_systems); // systems are layout-only objects so we delete
262 : // them prior to measures.
263 19212 : for (MeasureBase* m = m_measures.first(); m;) {
264 17117 : MeasureBase* nm = m->next();
265 17117 : if (m->isMeasure() && toMeasure(m)->mmRest()) {
266 71 : delete toMeasure(m)->mmRest();
267 : }
268 17117 : delete m;
269 17117 : m = nm;
270 : }
271 :
272 2095 : m_spanner.clear();
273 :
274 2095 : muse::DeleteAll(m_systemLocks.allLocks());
275 2095 : m_systemLocks.clear();
276 :
277 2095 : muse::DeleteAll(m_pageLocks.allLocks());
278 2095 : m_pageLocks.clear();
279 :
280 2095 : muse::DeleteAll(m_parts);
281 2095 : m_parts.clear();
282 :
283 2095 : muse::DeleteAll(m_staves);
284 2095 : m_staves.clear();
285 :
286 2095 : muse::DeleteAll(m_pages);
287 2095 : m_pages.clear();
288 :
289 5277 : for (auto& bracketList : m_brackets) {
290 3182 : muse::DeleteAll(bracketList);
291 : }
292 :
293 2095 : m_masterScore = nullptr;
294 :
295 2095 : imageStore.clearUnused();
296 :
297 2095 : delete m_shadowNote;
298 2095 : m_shadowNote = nullptr;
299 :
300 2095 : delete m_rootItem;
301 2095 : m_rootItem = nullptr;
302 :
303 : // last: everything that is deleted above may park children on it on its way out
304 2095 : delete m_dummy;
305 2095 : m_dummy = nullptr;
306 2246 : }
307 :
308 0 : muse::async::Channel<LoopBoundaryType, unsigned> Score::loopBoundaryTickChanged() const
309 : {
310 0 : return m_loopBoundaryTickChanged;
311 : }
312 :
313 0 : void Score::notifyLoopBoundaryTickChanged(LoopBoundaryType type, unsigned ticks)
314 : {
315 0 : m_loopBoundaryTickChanged.send(type, ticks);
316 0 : }
317 :
318 231241 : muse::async::Channel<EngravingItem*> Score::elementDestroyed()
319 : {
320 231241 : return m_elementDestroyed;
321 : }
322 :
323 10051 : muse::async::Channel<float> Score::layoutProgressChannel() const
324 : {
325 10051 : return m_layoutProgressChannel;
326 : }
327 :
328 : //---------------------------------------------------------
329 : // Score::clone
330 : // To create excerpt clone to show when changing PageSettings
331 : // Use MasterScore::clone() instead
332 : //---------------------------------------------------------
333 :
334 0 : Score* Score::clone()
335 : {
336 : // TODO: see comments regarding setting version in corresponding code in 3.x branch
337 : // and also compare to MasterScore::clone()
338 0 : Excerpt* excerpt = new Excerpt(masterScore());
339 0 : excerpt->setName(name());
340 :
341 0 : TracksMap tracks;
342 :
343 0 : for (Part* part : m_parts) {
344 0 : excerpt->parts().push_back(part);
345 :
346 0 : const TrackRange range = part->trackRange();
347 0 : for (track_idx_t track = range.startTrack; track < range.endTrack; ++track) {
348 0 : tracks.insert({ track, track });
349 : }
350 : }
351 :
352 0 : excerpt->setTracksMapping(tracks);
353 :
354 0 : masterScore()->initAndAddExcerpt(excerpt, true);
355 0 : masterScore()->removeExcerpt(excerpt);
356 :
357 0 : return excerpt->excerptScore();
358 0 : }
359 :
360 709363 : Score* Score::paletteScore() const
361 : {
362 709363 : return paletteScoreProvider() ? paletteScoreProvider()->paletteScore() : nullptr;
363 : }
364 :
365 709363 : bool Score::isPaletteScore() const
366 : {
367 709363 : return this == paletteScore();
368 : }
369 :
370 : //! A bracket is a rendering of its bracket item, rebuilt from it on every layout, so
371 : //! one whose item is gone has nothing left to represent.
372 192 : static void onBracketItemDestruction(Score* score, const BracketItem* item)
373 : {
374 551 : for (System* system : score->systems()) {
375 359 : std::vector<Bracket*>& brackets = system->brackets();
376 577 : for (auto it = brackets.begin(); it != brackets.end();) {
377 218 : Bracket* bracket = *it;
378 218 : if (bracket && bracket->bracketItem() == item) {
379 64 : it = brackets.erase(it);
380 64 : delete bracket;
381 : } else {
382 154 : ++it;
383 : }
384 : }
385 : }
386 192 : }
387 :
388 : //---------------------------------------------------------
389 : // Score::onElementDestruction
390 : // Ensure correct state of the score after destruction
391 : // of the element (e.g. remove invalid pointers etc.).
392 : //---------------------------------------------------------
393 :
394 596767 : void Score::onElementDestruction(EngravingItem* e)
395 : {
396 596767 : Score* score = e->EngravingObject::score();
397 :
398 596767 : if (!score) {
399 365526 : return;
400 : }
401 :
402 : // The EID register lives on the master score, so unregister the item even when
403 : // its own score is being deleted (e.g. when an excerpt is removed), as long as
404 : // the master score, and with it the register, is still alive
405 596767 : MasterScore* masterScore = score->masterScore();
406 596767 : if (masterScore && Score::validScores.find(masterScore) != Score::validScores.end()) {
407 260634 : masterScore->eidRegister()->onItemDestroyed(e);
408 : }
409 :
410 596767 : if (Score::validScores.find(score) == Score::validScores.end()) {
411 : // The score is already deleted
412 365526 : return;
413 : }
414 :
415 231241 : if (e->isBracketItem()) {
416 192 : onBracketItemDestruction(score, toBracketItem(e));
417 : }
418 :
419 231241 : score->selection().remove(e);
420 231241 : score->cmdState().unsetElement(e);
421 231241 : score->elementDestroyed().send(e);
422 : }
423 :
424 : //---------------------------------------------------------
425 : // addMeasure
426 : //---------------------------------------------------------
427 :
428 40 : void Score::addMeasure(MeasureBase* m, MeasureBase* pos)
429 : {
430 40 : m->setNext(pos);
431 40 : m_measures.add(m);
432 40 : }
433 :
434 : //---------------------------------------------------------
435 : // updateTicksAndTimeSigMap
436 : // update:
437 : // - measure ticks
438 : // - time signature map
439 : //---------------------------------------------------------
440 :
441 : /**
442 : This is needed after
443 : - inserting or removing a measure
444 : - changing the sigmap
445 : - after inserting/deleting time (changes the sigmap)
446 : */
447 :
448 2355 : void Score::updateTicksAndTimeSigMap()
449 : {
450 2368 : TRACEFUNC;
451 :
452 2355 : Fraction tick = Fraction(0, 1);
453 2355 : Measure* fm = firstMeasure();
454 2355 : if (!fm) {
455 15 : return;
456 : }
457 :
458 5712 : for (Staff* staff : m_staves) {
459 3372 : staff->clearTimeSig();
460 : }
461 :
462 2340 : if (isMaster()) {
463 2208 : sigmap()->clear();
464 2208 : sigmap()->add(0, SigEvent(fm->ticks(), fm->timesig(), 0));
465 : }
466 :
467 21426 : for (MeasureBase* mb = first(); mb; mb = mb->next()) {
468 19086 : if (!mb->isMeasure()) {
469 1300 : mb->setTick(tick);
470 1300 : continue;
471 : }
472 17786 : Measure* m = toMeasure(mb);
473 17786 : Fraction mtick = m->tick();
474 17786 : Fraction diff = tick - mtick;
475 17786 : Fraction measureTicks = m->ticks();
476 17786 : m->moveTicks(diff);
477 17786 : if (m->mmRest()) {
478 98 : m->mmRest()->moveTicks(diff);
479 : }
480 :
481 17786 : rebuildTimeSigMap(m);
482 :
483 17786 : tick += measureTicks;
484 : }
485 :
486 2340 : m_measures.updateTickIndex();
487 2340 : m_needUpdateTicksAndTimeSigMap = false;
488 2355 : }
489 :
490 : //---------------------------------------------------------
491 : // rebuildTimeSigMap
492 : /// updates the time signature map for a measure
493 : //---------------------------------------------------------
494 :
495 17786 : void Score::rebuildTimeSigMap(Measure* measure)
496 : {
497 66850 : for (Segment& segment : measure->segments()) {
498 49064 : if (!segment.isTimeSigType()) {
499 46772 : continue;
500 : }
501 5489 : for (size_t staffIdx = 0; staffIdx < m_staves.size(); ++staffIdx) {
502 3197 : TimeSig* ts = toTimeSig(segment.element(staffIdx * VOICES));
503 3197 : if (ts) {
504 3123 : staff(staffIdx)->addTimeSig(ts);
505 : }
506 : }
507 : }
508 :
509 17786 : if (!isMaster()) {
510 3250 : return;
511 : }
512 :
513 : // create event if measure len and time signature are different
514 : // even if they are equivalent 4/4 vs 2/2
515 : // also check if nominal time signature has changed
516 :
517 14536 : const Fraction mTicks = measure->isMMRest() ? measure->mmRestFirst()->ticks() : measure->ticks(); // for time signature the underlying measure length matters for MM rests
518 :
519 14536 : const Measure* pm = measure->prevMeasure();
520 : // prevMeasure() doesn't return MM rest so we don't handle it here
521 :
522 14536 : if (pm && (!mTicks.identical(pm->ticks()) || !measure->timesig().identical(pm->timesig()))) {
523 301 : sigmap()->add(measure->tick().ticks(), SigEvent(mTicks, measure->timesig(), measure->measureNumber()));
524 : }
525 : }
526 :
527 : //---------------------------------------------------------
528 : // pos2measureBase
529 : // Return measure base for canvas relative position \a p.
530 : //---------------------------------------------------------
531 :
532 0 : MeasureBase* Score::pos2measureBase(const PointF& p, bool scanMeasuresOnly, staff_idx_t* staffIdx, int* pitch, Segment** seg,
533 : PointF* offset) const
534 : {
535 0 : MeasureBase* mb = searchMeasureBase(p, scanMeasuresOnly);
536 0 : if (!mb) {
537 0 : return nullptr;
538 : }
539 0 : if (!mb->isMeasure()) {
540 : //! NOTE: staffIdx, pitch, and seg are only relevant for Measure types...
541 0 : if (staffIdx) {
542 0 : *staffIdx = muse::nidx;
543 : }
544 0 : if (pitch) {
545 0 : *pitch = -1;
546 : }
547 0 : if (seg) {
548 0 : *seg = nullptr;
549 : }
550 : if (offset) {
551 : // TODO: Relevant?
552 : }
553 0 : return mb;
554 : }
555 :
556 0 : Measure* m = toMeasure(mb);
557 0 : if (!m) {
558 0 : return nullptr;
559 : }
560 :
561 0 : const System* system = m->system();
562 0 : const double y = p.y() - system->canvasPos().y();
563 :
564 0 : const staff_idx_t staffIdxResult = system->searchStaff(y);
565 :
566 : // search for segment + offset
567 0 : const PointF offsetResult = p - m->canvasPos();
568 0 : const staff_idx_t strack = staffIdxResult * VOICES;
569 0 : if (!staff(staffIdxResult)) {
570 0 : return nullptr;
571 : }
572 :
573 : // int etrack = staff(i)->part()->nstaves() * VOICES + strack;
574 0 : track_idx_t etrack = VOICES + strack;
575 :
576 0 : constexpr SegmentType st = SegmentType::ChordRest;
577 0 : Segment* segment = m->searchSegment(offsetResult.x(), st, strack, etrack);
578 0 : if (!segment) {
579 0 : return nullptr;
580 : }
581 :
582 0 : const SysStaff* sysStaff = m->system()->staff(staffIdxResult);
583 0 : if (staffIdx) {
584 0 : *staffIdx = staffIdxResult;
585 : }
586 0 : if (pitch) {
587 0 : const Staff* s1 = m_staves[staffIdxResult];
588 0 : const Fraction tick = segment->tick();
589 0 : const ClefType clef = s1->clef(tick);
590 0 : *pitch = y2pitch(offsetResult.y() - sysStaff->bbox().y(), clef, s1->spatium(tick));
591 : }
592 0 : if (offset) {
593 0 : *offset = offsetResult - PointF(segment->x(), sysStaff->bbox().y());
594 : }
595 0 : if (seg) {
596 0 : *seg = segment;
597 : }
598 :
599 0 : return m;
600 : }
601 :
602 0 : Measure* Score::pos2measure(const PointF& p, staff_idx_t* staffIdx, int* pitch, Segment** seg, PointF* offset) const
603 : {
604 0 : MeasureBase* mb = pos2measureBase(p, /*scanMeasuresOnly*/ true, staffIdx, pitch, seg, offset);
605 0 : return mb && mb->isMeasure() ? toMeasure(mb) : nullptr;
606 : }
607 :
608 : //---------------------------------------------------------
609 : // dragPosition
610 : /// \param p drag position in canvas coordinates
611 : /// \param rst \b input: current staff index \n
612 : /// \b output: new staff index for drag position
613 : /// \param seg \b input: current segment \n
614 : /// \b output: new segment for drag position
615 : //---------------------------------------------------------
616 :
617 0 : void Score::dragPosition(const PointF& pos, staff_idx_t* rst, Segment** seg, double spacingFactor, bool allowTimeAnchor) const
618 : {
619 0 : Measure* m = nullptr;
620 :
621 0 : if (!dragPositionToMeasure(pos, this, &m, rst, spacingFactor)) {
622 0 : return;
623 : }
624 :
625 0 : dragPositionToSegment(pos, m, *rst, seg, spacingFactor, allowTimeAnchor);
626 : }
627 :
628 : //---------------------------------------------------------
629 : // setShowInvisible
630 : //---------------------------------------------------------
631 :
632 65 : void Score::setShowInvisible(bool v)
633 : {
634 65 : m_showInvisible = v;
635 : // BSP tree does not include elements which are not
636 : // displayed, so we need to refresh it to get
637 : // invisible elements displayed or properly hidden.
638 65 : rebuildBspTree();
639 65 : }
640 :
641 : //---------------------------------------------------------
642 : // setShowUnprintable
643 : //---------------------------------------------------------
644 :
645 39 : void Score::setShowUnprintable(bool v)
646 : {
647 39 : m_showUnprintable = v;
648 39 : }
649 :
650 : //---------------------------------------------------------
651 : // setShowFrames
652 : //---------------------------------------------------------
653 :
654 65 : void Score::setShowFrames(bool v)
655 : {
656 65 : m_showFrames = v;
657 65 : }
658 :
659 : //---------------------------------------------------------
660 : // setShowPageborders
661 : //---------------------------------------------------------
662 :
663 39 : void Score::setShowPageborders(bool v)
664 : {
665 39 : m_showPageborders = v;
666 39 : }
667 :
668 0 : void Score::setShowSoundFlags(bool v)
669 : {
670 0 : if (m_showSoundFlags == v) {
671 0 : return;
672 : }
673 :
674 0 : m_showSoundFlags = v;
675 0 : setLayoutAll();
676 : }
677 :
678 : //---------------------------------------------------------
679 : // setMarkIrregularMeasures
680 : //---------------------------------------------------------
681 :
682 0 : void Score::setMarkIrregularMeasures(bool v)
683 : {
684 0 : m_markIrregularMeasures = v;
685 0 : }
686 :
687 0 : void Score::setShowAnchors(const ShowAnchors& showAnchors)
688 : {
689 0 : m_showAnchors = showAnchors;
690 0 : }
691 :
692 : //---------------------------------------------------------
693 : // readOnly
694 : //---------------------------------------------------------
695 :
696 0 : bool Score::readOnly() const
697 : {
698 0 : return m_masterScore->readOnly();
699 : }
700 :
701 : //---------------------------------------------------------
702 : // dirty
703 : //---------------------------------------------------------
704 :
705 1266 : bool Score::dirty() const
706 : {
707 1266 : return !undoStack()->isClean();
708 : }
709 :
710 6465 : void Score::invalidateRepeatList()
711 : {
712 6465 : masterScore()->invalidateRepeatList();
713 6465 : }
714 :
715 229 : bool Score::isOpen() const
716 : {
717 229 : return m_isOpen;
718 : }
719 :
720 22 : void Score::setIsOpen(bool open)
721 : {
722 22 : m_isOpen = open;
723 22 : }
724 :
725 : //---------------------------------------------------------
726 : // searchPage
727 : // p is in canvas coordinates
728 : //---------------------------------------------------------
729 :
730 0 : Page* Score::searchPage(const PointF& p) const
731 : {
732 0 : if (layoutMode() == LayoutMode::LINE) {
733 0 : return m_pages.empty() ? nullptr : m_pages.front();
734 : }
735 :
736 0 : for (Page* page : m_pages) {
737 0 : RectF r = page->ldata()->bbox().translated(page->pos());
738 0 : if (r.contains(p)) {
739 0 : return page;
740 : }
741 : }
742 :
743 0 : return nullptr;
744 : }
745 :
746 : //---------------------------------------------------------
747 : // searchSystem
748 : /// Returns list of systems as there may be more than
749 : /// one system in a row
750 : /// \param pos Position in canvas coordinates
751 : /// \param preferredSystem If not nullptr, will give more
752 : /// space to the given system when searching it by its
753 : /// coordinate.
754 : /// \returns List of found systems.
755 : //---------------------------------------------------------
756 :
757 0 : std::vector<System*> Score::searchSystem(const PointF& pos, bool includeMeasurelessSystems, const System* preferredSystem,
758 : double spacingFactor, double preferredSpacingFactor) const
759 : {
760 0 : std::vector<System*> systems;
761 0 : Page* page = searchPage(pos);
762 0 : if (!page) {
763 0 : return systems;
764 : }
765 0 : double y = pos.y() - page->pos().y(); // transform to page relative
766 0 : const std::vector<System*>& sl = page->systems();
767 : double y2;
768 0 : size_t n = sl.size();
769 0 : for (size_t i = 0; i < n; ++i) {
770 0 : System* s = sl.at(i);
771 0 : if (!s->first() || (!includeMeasurelessSystems && !s->firstMeasure())) {
772 0 : continue;
773 : }
774 0 : System* ns = 0; // next system row
775 0 : size_t ii = i + 1;
776 0 : for (; ii < n; ++ii) {
777 0 : ns = sl.at(ii);
778 0 : if (ns->y() != s->y()) {
779 0 : break;
780 : }
781 : }
782 0 : if ((ii == n) || (ns == 0)) {
783 0 : y2 = page->height();
784 : } else {
785 : double currentSpacingFactor;
786 0 : double sy2 = s->y() + s->ldata()->bbox().height();
787 0 : if (s == preferredSystem) {
788 0 : currentSpacingFactor = preferredSpacingFactor; //y2 = ns->y();
789 0 : } else if (ns == preferredSystem) {
790 0 : currentSpacingFactor = 1.0 - preferredSpacingFactor; //y2 = sy2;
791 : } else {
792 0 : currentSpacingFactor = spacingFactor;
793 : }
794 0 : y2 = sy2 + (ns->y() - sy2) * currentSpacingFactor;
795 : }
796 0 : if (y < y2) {
797 0 : systems.push_back(s);
798 0 : for (size_t iii = i + 1; iii < n; ++iii) {
799 0 : if (sl.at(iii)->y() != s->y()) {
800 0 : break;
801 : }
802 0 : systems.push_back(sl.at(iii));
803 : }
804 0 : return systems;
805 : }
806 : }
807 0 : return systems;
808 0 : }
809 :
810 : //---------------------------------------------------------
811 : // searchMeasure
812 : /// \param p Position in canvas coordinates
813 : /// \param preferredSystem If not nullptr, will give more
814 : /// space to measures in this system when searching.
815 : //---------------------------------------------------------
816 :
817 0 : MeasureBase* Score::searchMeasureBase(const PointF& p, bool scanMeasuresOnly, const System* preferredSystem, double spacingFactor,
818 : double preferredSpacingFactor) const
819 : {
820 0 : std::vector<System*> systems = searchSystem(p, /*includeMeasurelessSystems*/ !scanMeasuresOnly, preferredSystem,
821 0 : spacingFactor, preferredSpacingFactor);
822 0 : MeasureBase* lastMB = nullptr;
823 0 : for (System* system : systems) {
824 0 : const double x = p.x() - system->canvasPos().x();
825 0 : for (MeasureBase* mb : system->measures()) {
826 0 : if (scanMeasuresOnly && !mb->isMeasure()) {
827 0 : continue;
828 : }
829 0 : if (x < (mb->x() + mb->ldata()->bbox().width())) {
830 0 : return mb;
831 : }
832 0 : lastMB = mb;
833 : }
834 : }
835 0 : return lastMB;
836 0 : }
837 :
838 : //---------------------------------------------------------
839 : // getNextValidInputSegment
840 : // - segment is of type SegmentType::ChordRest
841 : //---------------------------------------------------------
842 :
843 0 : static Segment* getNextValidInputSegment(Segment* segment, track_idx_t track, voice_idx_t voice)
844 : {
845 0 : if (!segment) {
846 0 : return nullptr;
847 : }
848 :
849 0 : assert(segment->segmentType() == SegmentType::ChordRest);
850 :
851 0 : Fraction nextTick = segment->measure()->tick();
852 0 : for (Segment* s1 = segment; s1; s1 = s1->prev(SegmentType::ChordRest)) {
853 0 : if (EngravingItem* element = s1->element(track + voice)) {
854 0 : nextTick = toChordRest(element)->endTick();
855 0 : break;
856 : }
857 : }
858 :
859 0 : for (; segment; segment = segment->next(SegmentType::ChordRest)) {
860 0 : if (segment->element(track + voice) || (voice && segment->tick() == nextTick)) {
861 0 : break;
862 : }
863 : }
864 :
865 0 : return segment;
866 : }
867 :
868 : //---------------------------------------------------------
869 : // getPosition
870 : // return true if valid position found
871 : //---------------------------------------------------------
872 :
873 0 : bool Score::getPosition(Position* pos, const PointF& p, voice_idx_t voice) const
874 : {
875 0 : System* preferredSystem = nullptr;
876 0 : staff_idx_t preferredStaffIdx = muse::nidx;
877 0 : const double spacingFactor = 0.5;
878 0 : const double preferredSpacingFactor = 0.75;
879 0 : if (noteEntryMode() && inputState().staffGroup() != StaffGroup::TAB) {
880 : // for non-tab staves, prefer the current system & staff
881 : // this makes it easier to add notes far above or below the staff
882 : // not helpful for tab since notes are not entered above or below
883 0 : Segment* seg = inputState().segment();
884 0 : if (seg) {
885 0 : preferredSystem = seg->system();
886 : }
887 0 : track_idx_t track = inputState().track();
888 0 : if (track != muse::nidx) {
889 0 : preferredStaffIdx = track >> 2;
890 : }
891 : }
892 :
893 : //! NOTE: For Positions we only care about Measure types, we're not interested in all MeasureBases...
894 0 : MeasureBase* mb = searchMeasureBase(p, /*scanMeasuresOnly*/ true, preferredSystem, spacingFactor, preferredSpacingFactor);
895 0 : Measure* measure = mb && mb->isMeasure() ? toMeasure(mb) : nullptr;
896 0 : if (!measure) {
897 0 : return false;
898 : }
899 :
900 0 : pos->fret = INVALID_FRET_INDEX;
901 : //
902 : // search staff
903 : //
904 0 : pos->staffIdx = 0;
905 0 : SysStaff* sstaff = 0;
906 0 : System* system = measure->system();
907 0 : double y = p.y() - system->pagePos().y();
908 0 : for (; pos->staffIdx < nstaves(); ++pos->staffIdx) {
909 0 : Staff* st = staff(pos->staffIdx);
910 0 : if (!st->part()->show()) {
911 0 : continue;
912 : }
913 : double sy2;
914 0 : SysStaff* ss = system->staff(pos->staffIdx);
915 0 : if (!ss->show()) {
916 0 : continue;
917 : }
918 0 : staff_idx_t idx = muse::nidx;
919 0 : SysStaff* nstaff = 0;
920 :
921 : // find next visible staff
922 0 : for (staff_idx_t i = pos->staffIdx + 1; i < nstaves(); ++i) {
923 0 : Staff* sti = staff(i);
924 0 : if (!sti->part()->show()) {
925 0 : continue;
926 : }
927 0 : nstaff = system->staff(i);
928 0 : if (!nstaff->show()) {
929 0 : nstaff = 0;
930 0 : continue;
931 : }
932 0 : if (i == preferredStaffIdx) {
933 0 : idx = i;
934 : }
935 0 : break;
936 : }
937 :
938 0 : if (nstaff) {
939 : double currentSpacingFactor;
940 0 : if (pos->staffIdx == preferredStaffIdx) {
941 0 : currentSpacingFactor = preferredSpacingFactor;
942 0 : } else if (idx == preferredStaffIdx) {
943 0 : currentSpacingFactor = 1.0 - preferredSpacingFactor;
944 : } else {
945 0 : currentSpacingFactor = spacingFactor;
946 : }
947 0 : double s1y2 = ss->bbox().bottom();
948 0 : sy2 = system->page()->canvasPos().y() + s1y2 + (nstaff->bbox().y() - s1y2) * currentSpacingFactor;
949 : } else {
950 0 : sy2 = system->page()->canvasPos().y() + system->page()->height() - system->pagePos().y(); // system->height();
951 : }
952 0 : if (y < sy2) {
953 0 : sstaff = ss;
954 0 : break;
955 : }
956 : }
957 0 : if (sstaff == 0) {
958 0 : return false;
959 : }
960 :
961 : //
962 : // search segment
963 : //
964 0 : PointF pppp(p - measure->canvasPos());
965 0 : double x = pppp.x();
966 0 : Segment* segment = 0;
967 0 : pos->segment = 0;
968 0 : pos->beyondScore = false;
969 :
970 : // int track = pos->staffIdx * VOICES + voice;
971 0 : track_idx_t track = pos->staffIdx * VOICES;
972 :
973 0 : for (segment = measure->first(SegmentType::ChordRest); segment;) {
974 0 : segment = getNextValidInputSegment(segment, track, voice);
975 0 : if (segment == 0) {
976 0 : break;
977 : }
978 0 : Segment* ns = getNextValidInputSegment(segment->next(SegmentType::ChordRest), track, voice);
979 :
980 0 : double x1 = segment->x();
981 0 : if (!ns) {
982 0 : if (measure == score()->lastMeasure() && x > measure->ldata()->bbox().width()) {
983 0 : x = measure->ldata()->bbox().width() + score()->style().spatium();
984 0 : pos->beyondScore = true;
985 : } else {
986 0 : x = x1;
987 : }
988 0 : pos->segment = segment;
989 0 : break;
990 : }
991 :
992 0 : double x2 = ns->x();
993 0 : double d = x2 - x1;
994 0 : if (x < (x1 + d * .5)) {
995 0 : x = x1;
996 0 : pos->segment = segment;
997 0 : break;
998 : }
999 0 : segment = ns;
1000 : }
1001 0 : if (segment == 0) {
1002 0 : return false;
1003 : }
1004 : //
1005 : // TODO: restrict to reasonable values (pitch 0-127)
1006 : //
1007 0 : const Staff* s = staff(pos->staffIdx);
1008 0 : const Fraction tick = segment->tick();
1009 0 : const double mag = s->staffMag(tick);
1010 : // in TABs, step from one string to another; in other staves, step on and between lines
1011 0 : double lineDist = s->staffType(tick)->lineDistance().val()
1012 0 : * (s->isTabStaff(measure->tick()) ? 1 : .5)
1013 0 : * mag
1014 0 : * style().spatium();
1015 :
1016 0 : const double yOff = sstaff->yOffset(); // Get system staff vertical offset (usually for 1-line staves)
1017 0 : pos->line = lrint((pppp.y() - sstaff->bbox().y() - yOff) / lineDist);
1018 0 : if (s->isTabStaff(measure->tick())) {
1019 0 : if (pos->line < -1 || pos->line > s->lines(tick) + 1) {
1020 0 : return false;
1021 : }
1022 0 : if (pos->line < 0) {
1023 0 : pos->line = 0;
1024 0 : } else if (pos->line >= s->lines(tick)) {
1025 0 : pos->line = s->lines(tick) - 1;
1026 : }
1027 : } else {
1028 0 : int minLine = absStep(MIN_PITCH);
1029 0 : ClefType clef = s->clef(segment->tick());
1030 0 : minLine = relStep(minLine, clef);
1031 0 : int maxLine = absStep(MAX_PITCH);
1032 0 : maxLine = relStep(maxLine, clef);
1033 :
1034 0 : if (pos->line > minLine || pos->line < maxLine) {
1035 0 : return false;
1036 : }
1037 : }
1038 :
1039 0 : y = sstaff->y() + pos->line * lineDist;
1040 0 : pos->pos = PointF(x, y) + measure->canvasPos();
1041 0 : return true;
1042 : }
1043 :
1044 : //---------------------------------------------------------
1045 : // checkHasMeasures
1046 : //---------------------------------------------------------
1047 :
1048 5 : bool Score::checkHasMeasures() const
1049 : {
1050 5 : Page* page = pages().empty() ? 0 : pages().front();
1051 5 : const std::vector<System*>* sl = page ? &page->systems() : 0;
1052 5 : if (sl == 0 || sl->empty() || sl->front()->measures().empty()) {
1053 0 : LOGD("first create measure, then repeat operation");
1054 0 : return false;
1055 : }
1056 5 : return true;
1057 : }
1058 :
1059 : //---------------------------------------------------------
1060 : // spatiumChanged
1061 : //---------------------------------------------------------
1062 :
1063 35 : void Score::spatiumChanged(double oldValue, double newValue)
1064 : {
1065 3336 : scanElements([&](EngravingItem* e) { e->spatiumChanged(oldValue, newValue); });
1066 70 : for (Staff* staff : m_staves) {
1067 35 : staff->spatiumChanged(oldValue, newValue);
1068 : }
1069 35 : m_layoutOptions.noteHeadWidth = m_engravingFont->width(SymId::noteheadBlack, newValue / style().defaultSpatium());
1070 35 : updatePaddingTables();
1071 35 : }
1072 :
1073 : //---------------------------------------------------------
1074 : // updateStyle
1075 : //---------------------------------------------------------
1076 :
1077 7938 : static void updateStyle(EngravingItem* e)
1078 : {
1079 7938 : bool v = e->generated();
1080 7938 : e->styleChanged();
1081 7938 : e->setGenerated(v);
1082 7938 : }
1083 :
1084 : //---------------------------------------------------------
1085 : // styleChanged
1086 : // must be called after every style change
1087 : //---------------------------------------------------------
1088 :
1089 93 : void Score::styleChanged()
1090 : {
1091 93 : scanElements(updateStyle);
1092 239 : for (Staff* staff : staves()) {
1093 293 : for (int tick = 0; tick != -1; tick = staff->staffTypeRange(Fraction::fromTicks(tick + 1)).second) {
1094 147 : StaffType* st = staff->staffType(Fraction::fromTicks(tick));
1095 147 : if (!st) {
1096 0 : continue;
1097 : }
1098 147 : st->styleChanged();
1099 : }
1100 : }
1101 93 : updatePaddingTables();
1102 93 : setLayoutAll();
1103 93 : }
1104 :
1105 : //---------------------------------------------------------
1106 : // addElement
1107 : //---------------------------------------------------------
1108 :
1109 : /**
1110 : Add \a element to its parent.
1111 :
1112 : Several elements (clef, keysig, timesig) need special handling, as they may cause
1113 : changes throughout the score.
1114 : */
1115 :
1116 66143 : void Score::addElement(EngravingItem* element)
1117 : {
1118 66143 : EngravingItem* parent = element->ownershipParentItem();
1119 66143 : element->triggerLayout();
1120 :
1121 : // LOGD("Score(%p) EngravingItem(%p)(%s) parent %p(%s)",
1122 : // this, element, element->typeName(), parent, parent ? parent->typeName() : "");
1123 :
1124 66143 : ElementType et = element->type();
1125 66143 : if (et == ElementType::MEASURE
1126 66143 : || (et == ElementType::HBOX && !(parent && parent->isVBox()))
1127 66143 : || et == ElementType::VBOX
1128 66141 : || et == ElementType::TBOX
1129 132286 : || et == ElementType::FBOX
1130 : ) {
1131 2 : measures()->add(toMeasureBase(element));
1132 2 : element->triggerLayout();
1133 2 : return;
1134 : }
1135 :
1136 66141 : if (et == ElementType::BEAM) { // a beam is not attached to its system
1137 1 : parent = nullptr;
1138 : }
1139 :
1140 66141 : if (parent) {
1141 65549 : parent->add(element);
1142 : }
1143 :
1144 66141 : switch (et) {
1145 1 : case ElementType::BEAM:
1146 : {
1147 1 : Beam* b = toBeam(element);
1148 1 : size_t n = b->elements().size();
1149 1 : for (size_t i = 0; i < n; ++i) {
1150 0 : b->elements().at(i)->setBeam(b);
1151 : }
1152 : }
1153 1 : break;
1154 :
1155 228 : case ElementType::SLUR:
1156 : case ElementType::HAMMER_ON_PULL_OFF:
1157 228 : addLayoutFlags(LayoutFlag::PLAY_EVENTS);
1158 : // fall through
1159 :
1160 519 : case ElementType::VOLTA:
1161 : case ElementType::TRILL:
1162 : case ElementType::VIBRATO:
1163 : case ElementType::PEDAL:
1164 : case ElementType::TEXTLINE:
1165 : case ElementType::HAIRPIN:
1166 : case ElementType::LET_RING:
1167 : case ElementType::GRADUAL_TEMPO_CHANGE:
1168 : case ElementType::PALM_MUTE:
1169 : case ElementType::WHAMMY_BAR:
1170 : case ElementType::RASGUEADO:
1171 : case ElementType::HARMONIC_MARK:
1172 : case ElementType::PICK_SCRAPE:
1173 : case ElementType::PARTIAL_LYRICSLINE:
1174 : {
1175 519 : Spanner* spanner = toSpanner(element);
1176 519 : addSpanner(spanner);
1177 579 : for (SpannerSegment* ss : spanner->spannerSegments()) {
1178 60 : if (ss->system()) {
1179 0 : ss->system()->add(ss);
1180 : }
1181 : }
1182 : }
1183 519 : break;
1184 :
1185 72 : case ElementType::OTTAVA:
1186 : {
1187 72 : Ottava* o = toOttava(element);
1188 72 : addSpanner(o);
1189 76 : for (SpannerSegment* ss : o->spannerSegments()) {
1190 4 : if (ss->system()) {
1191 0 : ss->system()->add(ss);
1192 : }
1193 : }
1194 72 : o->staff()->updateOttava();
1195 : }
1196 72 : break;
1197 :
1198 4 : case ElementType::INSTRUMENT_CHANGE: {
1199 4 : addLayoutFlags(LayoutFlag::REBUILD_MIDI_MAPPING);
1200 4 : cmdState().instrumentsChanged = true;
1201 : }
1202 4 : break;
1203 :
1204 2723 : case ElementType::CHORD:
1205 : {
1206 : // May need to reconnect slur when inserting new chord
1207 2723 : SpannerMap& smap = spannerMap();
1208 2723 : Fraction tick = element->tick();
1209 2723 : auto spanners = smap.findOverlapping(tick.ticks(), tick.ticks());
1210 2874 : for (auto interval : spanners) {
1211 151 : Spanner* s = interval.value;
1212 151 : if (s->track() != element->track() || !s->isSlur()) {
1213 150 : continue;
1214 : }
1215 1 : if (!s->startElement() && s->tick() == tick) {
1216 0 : s->setStartElement(element);
1217 : }
1218 1 : if (!s->endElement() && s->tick2() == tick) {
1219 0 : s->setEndElement(element);
1220 : }
1221 : }
1222 2723 : break;
1223 2723 : }
1224 51 : case ElementType::HARMONY:
1225 : case ElementType::FRET_DIAGRAM:
1226 51 : if (element->part()) {
1227 51 : element->part()->updateHarmonyChannels(true);
1228 : }
1229 51 : break;
1230 300 : case ElementType::GUITAR_BEND:
1231 : {
1232 300 : GuitarBend* bend = toGuitarBend(element);
1233 582 : if (bend->bendType() == GuitarBendType::GRACE_NOTE_BEND || bend->bendType() == GuitarBendType::PRE_BEND
1234 582 : || bend->bendType() == GuitarBendType::PRE_DIVE) {
1235 84 : Note* startNote = bend->startNote();
1236 84 : Chord* startChord = startNote ? startNote->chord() : nullptr;
1237 84 : if (startChord) {
1238 84 : startChord->setNoStem(true);
1239 84 : startChord->setBeamMode(BeamMode::NONE);
1240 : }
1241 : }
1242 : }
1243 :
1244 : default:
1245 62771 : break;
1246 : }
1247 :
1248 70239 : if (element->isTextBase() && toTextBase(element)->hasParentSegment()
1249 70239 : && toSegment(element->parent())->isType(SegmentType::Duration)) {
1250 296 : MoveElementAnchors::checkMeasureBoundariesAndMoveIfNeed(element);
1251 : }
1252 :
1253 66141 : element->triggerLayout();
1254 : }
1255 :
1256 38310 : void Score::doUndoAddElement(EngravingItem* element)
1257 : {
1258 38310 : if (element->generated()) {
1259 24047 : addElement(element);
1260 : } else {
1261 14263 : undo(new AddElement(element));
1262 : }
1263 38310 : }
1264 :
1265 : //---------------------------------------------------------
1266 : // removeElement
1267 : /// Remove \a element from its parent.
1268 : /// Several elements (clef, keysig, timesig) need special handling, as they may cause
1269 : /// changes throughout the score.
1270 : //---------------------------------------------------------
1271 :
1272 12370 : void Score::removeElement(EngravingItem* element)
1273 : {
1274 12370 : EngravingItem* parent = element->ownershipParentItem();
1275 12370 : element->triggerLayout();
1276 :
1277 : // special for MEASURE, HBOX, VBOX
1278 : // their parent is not static
1279 :
1280 12370 : ElementType et = element->type();
1281 12370 : bool parentIsVBox = parent && parent->isVBox();
1282 :
1283 12370 : if (et == ElementType::MEASURE
1284 12370 : || (et == ElementType::HBOX && !parentIsVBox)
1285 12369 : || et == ElementType::VBOX
1286 12366 : || et == ElementType::TBOX
1287 12366 : || et == ElementType::FBOX
1288 : ) {
1289 4 : MeasureBase* mb = toMeasureBase(element);
1290 4 : measures()->remove(mb);
1291 :
1292 4 : System* system = mb->system();
1293 4 : if (!system) {
1294 : #ifndef NDEBUG
1295 : // vertical boxes are not shown in continuous view so no system
1296 0 : const bool noSystemMode = lineMode() && element->isVBoxBase();
1297 0 : assert(noSystemMode || !isOpen());
1298 : #endif
1299 0 : return;
1300 : }
1301 :
1302 4 : system->removeMeasure(mb);
1303 :
1304 : // See also InsertRemoveMeasures::removeMeasures()
1305 4 : if (element->isBox() && system->measures().empty()) {
1306 3 : Page* page = system->page();
1307 3 : if (page) {
1308 3 : muse::remove(page->systems(), system);
1309 3 : system->setPage(nullptr);
1310 : }
1311 :
1312 3 : muse::remove(m_systems, system);
1313 3 : system->deleteLater();
1314 :
1315 3 : if (page && page->systems().empty()) {
1316 : // Remove this page, since it is now empty.
1317 : // This involves renumbering and repositioning all subsequent pages.
1318 0 : PointF pos = page->pos();
1319 0 : auto ii = std::find(pages().begin(), pages().end(), page);
1320 0 : pages().erase(ii);
1321 0 : page->deleteLater();
1322 :
1323 0 : while (ii != pages().end()) {
1324 0 : page = *ii;
1325 0 : page->setPageNumber(page->pageNumber() - 1);
1326 0 : PointF p = page->pos();
1327 0 : page->setPos(pos);
1328 0 : pos = p;
1329 0 : ii++;
1330 : }
1331 : }
1332 : }
1333 4 : return;
1334 : }
1335 :
1336 12366 : if (et == ElementType::BEAM) { // a beam is not attached to its system
1337 75 : parent = nullptr;
1338 : }
1339 :
1340 12366 : if (parent) {
1341 12229 : parent->remove(element);
1342 : }
1343 :
1344 12366 : switch (et) {
1345 75 : case ElementType::BEAM:
1346 75 : for (ChordRest* cr : toBeam(element)->elements()) {
1347 0 : cr->setBeam(0);
1348 : }
1349 75 : break;
1350 :
1351 41 : case ElementType::SLUR:
1352 : case ElementType::HAMMER_ON_PULL_OFF:
1353 41 : addLayoutFlags(LayoutFlag::PLAY_EVENTS);
1354 : // fall through
1355 :
1356 59 : case ElementType::VOLTA:
1357 : case ElementType::TRILL:
1358 : case ElementType::VIBRATO:
1359 : case ElementType::PEDAL:
1360 : case ElementType::LET_RING:
1361 : case ElementType::GRADUAL_TEMPO_CHANGE:
1362 : case ElementType::PALM_MUTE:
1363 : case ElementType::PARTIAL_LYRICSLINE:
1364 : case ElementType::WHAMMY_BAR:
1365 : case ElementType::RASGUEADO:
1366 : case ElementType::HARMONIC_MARK:
1367 : case ElementType::PICK_SCRAPE:
1368 : case ElementType::TEXTLINE:
1369 : case ElementType::HAIRPIN:
1370 : {
1371 59 : Spanner* spanner = toSpanner(element);
1372 59 : spanner->triggerLayout();
1373 59 : removeSpanner(spanner);
1374 : }
1375 59 : break;
1376 :
1377 0 : case ElementType::GUITAR_BEND:
1378 : {
1379 0 : Note* startNote = toGuitarBend(element)->startNote();
1380 0 : if (startNote) {
1381 0 : startNote->setParenthesesMode(ParenthesesMode::NONE);
1382 0 : startNote->chord()->setNoStem(false);
1383 0 : startNote->chord()->setBeamMode(BeamMode::AUTO);
1384 : }
1385 0 : Note* endNote = toGuitarBend(element)->endNote();
1386 0 : if (endNote) {
1387 0 : endNote->setGhost(false);
1388 0 : endNote->setVisible(true);
1389 0 : const StringData* stringData = endNote->originPart()->stringData(endNote->tick(), endNote->staffIdx());
1390 0 : int endFret = stringData->fret(endNote->pitch(), endNote->string(), endNote->staff());
1391 0 : endNote->setFret(endFret);
1392 : }
1393 0 : break;
1394 : }
1395 :
1396 3 : case ElementType::OTTAVA:
1397 : {
1398 3 : Ottava* o = toOttava(element);
1399 3 : o->triggerLayout();
1400 3 : removeSpanner(o);
1401 3 : o->staff()->updateOttava();
1402 : }
1403 3 : break;
1404 :
1405 5564 : case ElementType::CHORD:
1406 : case ElementType::REST:
1407 : case ElementType::MMREST:
1408 : {
1409 5564 : ChordRest* cr = toChordRest(element);
1410 5564 : if (cr->beam()) {
1411 1357 : cr->beam()->remove(cr);
1412 : }
1413 5590 : for (Lyrics* lyr : cr->lyrics()) {
1414 26 : lyr->removeFromScore();
1415 : }
1416 : // TODO: check for tuplet?
1417 : }
1418 5564 : break;
1419 1 : case ElementType::INSTRUMENT_CHANGE: {
1420 1 : addLayoutFlags(LayoutFlag::REBUILD_MIDI_MAPPING);
1421 1 : cmdState().instrumentsChanged = true;
1422 : }
1423 1 : break;
1424 :
1425 13 : case ElementType::HARMONY:
1426 : case ElementType::FRET_DIAGRAM:
1427 13 : if (element->part()) {
1428 13 : element->part()->updateHarmonyChannels(true, true);
1429 : }
1430 13 : break;
1431 :
1432 6651 : default:
1433 6651 : break;
1434 : }
1435 : }
1436 :
1437 11452 : void Score::doUndoRemoveElement(EngravingItem* element)
1438 : {
1439 11452 : if (element->generated()) {
1440 436 : removeElement(element);
1441 : //! HACK: don't delete as it may still be used in PropertiesPanel
1442 : // element->deleteLater();
1443 : } else {
1444 11016 : undo(new RemoveElement(element));
1445 : }
1446 11452 : }
1447 :
1448 70 : bool Score::canReselectItem(const EngravingItem* item) const
1449 : {
1450 70 : if (!item || item->selected()) {
1451 43 : return false;
1452 : }
1453 :
1454 27 : EngravingItem* seg = const_cast<EngravingItem*>(item->findAncestor(ElementType::SEGMENT));
1455 27 : if (seg) {
1456 27 : std::vector<EngravingItem*> elements = seg->getChildren(false);
1457 27 : return muse::contains(elements, const_cast<EngravingItem*>(item));
1458 27 : }
1459 :
1460 0 : return true;
1461 : }
1462 :
1463 : //---------------------------------------------------------
1464 : // firstMeasure
1465 : //---------------------------------------------------------
1466 :
1467 72504 : Measure* Score::firstMeasure() const
1468 : {
1469 72504 : MeasureBase* mb = m_measures.first();
1470 118553 : while (mb && !mb->isMeasure()) {
1471 46049 : mb = mb->next();
1472 : }
1473 :
1474 72504 : return toMeasure(mb);
1475 : }
1476 :
1477 : //---------------------------------------------------------
1478 : // firstMeasureMM
1479 : //---------------------------------------------------------
1480 :
1481 3572 : Measure* Score::firstMeasureMM() const
1482 : {
1483 3572 : Measure* m = firstMeasure();
1484 3572 : if (m && style().styleB(Sid::createMultiMeasureRests) && m->hasMMRest()) {
1485 32 : return m->mmRest();
1486 : }
1487 3540 : return m;
1488 : }
1489 :
1490 : //---------------------------------------------------------
1491 : // firstMM
1492 : //---------------------------------------------------------
1493 :
1494 65 : MeasureBase* Score::firstMM() const
1495 : {
1496 65 : MeasureBase* m = m_measures.first();
1497 65 : if (m
1498 65 : && m->isMeasure()
1499 47 : && style().styleB(Sid::createMultiMeasureRests)
1500 130 : && toMeasure(m)->hasMMRest()) {
1501 0 : return toMeasure(m)->mmRest();
1502 : }
1503 65 : return m;
1504 : }
1505 :
1506 : //---------------------------------------------------------
1507 : // measure
1508 : //---------------------------------------------------------
1509 :
1510 70 : MeasureBase* Score::measure(int idx) const
1511 : {
1512 70 : MeasureBase* mb = m_measures.first();
1513 703 : for (int i = 0; i < idx; ++i) {
1514 633 : mb = mb->next();
1515 633 : if (mb == 0) {
1516 0 : return 0;
1517 : }
1518 : }
1519 70 : return mb;
1520 : }
1521 :
1522 : //---------------------------------------------------------
1523 : // crMeasure
1524 : // Returns a measure containing chords and/or rests
1525 : // by its index, skipping other MeasureBase descendants
1526 : //---------------------------------------------------------
1527 :
1528 206 : Measure* Score::crMeasure(int idx) const
1529 : {
1530 206 : int i = -1;
1531 680 : for (MeasureBase* mb = m_measures.first(); mb; mb = mb->next()) {
1532 680 : if (mb->isMeasure()) {
1533 498 : ++i;
1534 498 : if (i == idx) {
1535 206 : return toMeasure(mb);
1536 : }
1537 : }
1538 : }
1539 0 : return nullptr;
1540 : }
1541 :
1542 : //---------------------------------------------------------
1543 : // lastMeasure
1544 : //---------------------------------------------------------
1545 :
1546 91462 : Measure* Score::lastMeasure() const
1547 : {
1548 91462 : MeasureBase* mb = m_measures.last();
1549 :
1550 91462 : if (!mb) {
1551 81 : return nullptr;
1552 : }
1553 :
1554 92460 : while (mb && !mb->isMeasure()) {
1555 1079 : mb = mb->prev();
1556 : }
1557 91381 : return toMeasure(mb);
1558 : }
1559 :
1560 : //---------------------------------------------------------
1561 : // lastMeasureMM
1562 : //---------------------------------------------------------
1563 :
1564 48101 : Measure* Score::lastMeasureMM() const
1565 : {
1566 48101 : Measure* m = lastMeasure();
1567 48101 : return m ? const_cast<Measure*>(m->coveringMMRestOrThis()) : nullptr;
1568 : }
1569 :
1570 : //---------------------------------------------------------
1571 : // endTick
1572 : //---------------------------------------------------------
1573 :
1574 2169 : Fraction Score::endTick() const
1575 : {
1576 2169 : Measure* m = lastMeasure();
1577 2169 : return m ? m->endTick() : Fraction(0, 1);
1578 : }
1579 :
1580 : //---------------------------------------------------------
1581 : // firstSegment
1582 : //---------------------------------------------------------
1583 :
1584 2045 : Segment* Score::firstSegment(SegmentType segType) const
1585 : {
1586 : Segment* seg;
1587 2045 : Measure* m = firstMeasure();
1588 2045 : if (!m) {
1589 3 : seg = 0;
1590 : } else {
1591 2042 : seg = m->first();
1592 2042 : if (seg && !(seg->segmentType() & segType)) {
1593 151 : seg = seg->next1(segType);
1594 : }
1595 : }
1596 2045 : return seg;
1597 : }
1598 :
1599 : //---------------------------------------------------------
1600 : // firstSegmentMM
1601 : //---------------------------------------------------------
1602 :
1603 2 : Segment* Score::firstSegmentMM(SegmentType segType) const
1604 : {
1605 2 : Measure* m = firstMeasureMM();
1606 2 : return m ? m->first(segType) : 0;
1607 : }
1608 :
1609 : //---------------------------------------------------------
1610 : // lastSegment
1611 : //---------------------------------------------------------
1612 :
1613 20120 : Segment* Score::lastSegment() const
1614 : {
1615 20120 : Measure* m = lastMeasure();
1616 20120 : return m ? m->last() : 0;
1617 : }
1618 :
1619 : //---------------------------------------------------------
1620 : // lastSegmentMM
1621 : //---------------------------------------------------------
1622 :
1623 6 : Segment* Score::lastSegmentMM() const
1624 : {
1625 6 : Measure* m = lastMeasureMM();
1626 6 : return m ? m->last() : 0;
1627 : }
1628 :
1629 824 : double Score::utick2utime(int tick) const
1630 : {
1631 824 : return tempoTimeline(/*expandRepeats*/ true).utick2utime(tick);
1632 : }
1633 :
1634 0 : int Score::utime2utick(double utime) const
1635 : {
1636 0 : return tempoTimeline(/*expandRepeats*/ true).utime2utick(utime);
1637 : }
1638 :
1639 8644 : const TempoTimeline& Score::tempoTimeline() const
1640 : {
1641 8644 : return masterScore()->tempoTimeline();
1642 : }
1643 :
1644 887 : const TempoTimeline& Score::tempoTimeline(bool expandRepeats) const
1645 : {
1646 887 : return masterScore()->tempoTimeline(expandRepeats);
1647 : }
1648 :
1649 0 : void Score::setTempoTimelineOverride(std::optional<TempoTimeline> timeline)
1650 : {
1651 0 : masterScore()->setTempoTimelineOverride(std::move(timeline));
1652 0 : }
1653 :
1654 : //---------------------------------------------------------
1655 : // scanElementsInRange
1656 : //---------------------------------------------------------
1657 :
1658 2 : void Score::scanElementsInRange(std::function<void(EngravingItem*)> func)
1659 : {
1660 2 : const Fraction selectionStartTick = m_selection.startSegment() ? m_selection.startSegment()->tick() : Fraction(0, 1);
1661 2 : const Fraction selectionEndTick = m_selection.endSegment() ? m_selection.endSegment()->tick() : endTick();
1662 92 : auto doScanElement = [&](EngravingItem* e) {
1663 92 : if (e->tick() >= selectionStartTick && e->tick() < selectionEndTick) {
1664 88 : func(e);
1665 : }
1666 92 : };
1667 :
1668 2 : System* lastSystem = nullptr;
1669 : // Inculde previous measure/system so we scan items at their end tick
1670 : // Filter anything else out in doScanElement
1671 2 : MeasureBase* startMb = m_selection.startMeasureBase();
1672 2 : if (startMb->prev()) {
1673 0 : startMb = startMb->prev();
1674 : }
1675 8 : for (MeasureBase* mb = startMb; mb && mb->tick() < selectionEndTick; mb = mb->next()) {
1676 6 : mb->scanElements(doScanElement);
1677 :
1678 6 : if (mb->isMeasure() && toMeasure(mb)->mmRest()) {
1679 0 : Measure* mmr = toMeasure(mb)->mmRest();
1680 0 : mmr->scanElements(doScanElement);
1681 : }
1682 6 : System* system = mb ? mb->system() : nullptr;
1683 6 : if (system && system != lastSystem) {
1684 4 : system->scanElements(doScanElement);
1685 4 : lastSystem = system;
1686 : }
1687 : }
1688 2 : }
1689 :
1690 : //---------------------------------------------------------
1691 : // setSelection
1692 : //---------------------------------------------------------
1693 :
1694 0 : void Score::setSelection(const Selection& s)
1695 : {
1696 0 : deselectAll();
1697 0 : m_selection = s;
1698 :
1699 0 : for (EngravingItem* e : m_selection.elements()) {
1700 0 : e->setSelected(true);
1701 : }
1702 0 : }
1703 :
1704 : //---------------------------------------------------------
1705 : // getText
1706 : //---------------------------------------------------------
1707 :
1708 214 : Text* Score::getText(TextStyleType tid) const
1709 : {
1710 214 : MeasureBase* m = first();
1711 214 : if (m && m->isVBox()) {
1712 0 : for (EngravingItem* e : m->el()) {
1713 0 : if (e->isText() && toText(e)->textStyleType() == tid) {
1714 0 : return toText(e);
1715 : }
1716 : }
1717 : }
1718 214 : return nullptr;
1719 : }
1720 :
1721 : //---------------------------------------------------------
1722 : // metaTag
1723 : //---------------------------------------------------------
1724 :
1725 11008 : String Score::metaTag(const String& s) const
1726 : {
1727 11008 : if (muse::contains(m_metaTags, s)) {
1728 9803 : return m_metaTags.at(s);
1729 : }
1730 :
1731 1205 : return muse::value(m_masterScore->m_metaTags, s);
1732 : }
1733 :
1734 : //---------------------------------------------------------
1735 : // setMetaTag
1736 : //---------------------------------------------------------
1737 :
1738 12418 : void Score::setMetaTag(const String& tag, const String& val)
1739 : {
1740 12418 : m_metaTags.insert_or_assign(tag, val);
1741 12418 : }
1742 :
1743 : //---------------------------------------------------------
1744 : // setSynthesizerState
1745 : //---------------------------------------------------------
1746 :
1747 0 : void Score::setSynthesizerState(const SynthesizerState& s)
1748 : {
1749 : // TODO: make undoable
1750 0 : m_synthesizerState = s;
1751 0 : }
1752 :
1753 : //---------------------------------------------------------
1754 : // appendScore
1755 : //---------------------------------------------------------
1756 :
1757 0 : bool Score::appendScore(Score* score, bool addPageBreak, bool addSectionBreak)
1758 : {
1759 0 : if (parts().size() < score->parts().size() || staves().size() < score->staves().size()) {
1760 0 : LOGD("Score to append has %zu parts and %zu staves, but this score only has %zu parts and %zu staves.",
1761 0 : score->parts().size(), score->staves().size(), parts().size(), staves().size());
1762 0 : return false;
1763 : }
1764 :
1765 0 : if (!last()) {
1766 0 : LOGD("This score doesn't have any MeasureBase objects.");
1767 0 : return false;
1768 : }
1769 :
1770 : // apply Page/Section Breaks if desired
1771 0 : if (addPageBreak) {
1772 0 : if (!last()->pageBreak()) {
1773 0 : last()->undoSetBreak(false, LayoutBreakType::LINE); // remove line break if exists
1774 0 : last()->undoSetBreak(true, LayoutBreakType::PAGE); // apply page break
1775 : }
1776 0 : } else if (!last()->lineBreak() && !last()->pageBreak()) {
1777 0 : last()->undoSetBreak(true, LayoutBreakType::LINE);
1778 : }
1779 :
1780 0 : if (addSectionBreak && !last()->sectionBreak()) {
1781 0 : last()->undoSetBreak(true, LayoutBreakType::SECTION);
1782 : }
1783 :
1784 : // match concert pitch states
1785 0 : if (style().styleB(Sid::concertPitch) != score->style().styleB(Sid::concertPitch)) {
1786 0 : score->cmdConcertPitchChanged(style().styleB(Sid::concertPitch));
1787 : }
1788 :
1789 : // clone the measures
1790 0 : appendMeasuresFromScore(score, Fraction(0, 1), score->last()->endTick());
1791 :
1792 0 : setLayoutAll();
1793 0 : return true;
1794 : }
1795 :
1796 : //---------------------------------------------------------
1797 : // appendMeasuresFromScore
1798 : // clone measures from another score to the end of this
1799 : //---------------------------------------------------------
1800 :
1801 0 : bool Score::appendMeasuresFromScore(Score* score, const Fraction& startTick, const Fraction& endTick)
1802 : {
1803 0 : Transaction& tx = transactionManager()->currentOrDummyTransaction();
1804 :
1805 0 : Fraction tickOfAppend = last()->endTick();
1806 0 : TieMap tieMap;
1807 :
1808 0 : MeasureBase* fmb = score->tick2measure(startTick);
1809 0 : MeasureBase* emb = score->tick2measure(endTick);
1810 0 : Fraction curTick = tickOfAppend;
1811 0 : for (MeasureBase* cmb = fmb; cmb != emb; cmb = cmb->next()) {
1812 : MeasureBase* nmb;
1813 0 : if (cmb->isMeasure()) {
1814 0 : Measure* nm = toMeasure(cmb)->cloneMeasure(this, curTick, &tieMap);
1815 0 : curTick += nm->ticks();
1816 0 : nmb = toMeasureBase(nm);
1817 : } else {
1818 0 : nmb = static_cast<MeasureBase*>(cmb->clone());
1819 : }
1820 :
1821 0 : nmb->setScore(this);
1822 0 : measures()->append(nmb);
1823 : }
1824 :
1825 0 : Measure* firstAppendedMeasure = tick2measure(tickOfAppend);
1826 :
1827 : // if the appended score has less staves,
1828 : // make sure the measures have full measure rest
1829 0 : for (Measure* m = firstAppendedMeasure; m; m = m->nextMeasure()) {
1830 0 : for (size_t staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
1831 0 : Fraction f;
1832 0 : for (Segment* s = m->first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
1833 0 : for (voice_idx_t v = 0; v < VOICES; ++v) {
1834 0 : ChordRest* cr = toChordRest(s->element(staffIdx * VOICES + v));
1835 0 : if (cr == 0) {
1836 0 : continue;
1837 : }
1838 0 : f += cr->actualTicks();
1839 : }
1840 : }
1841 0 : if (f.isZero()) {
1842 0 : addRest(m->tick(), staffIdx * VOICES, TDuration(DurationType::V_MEASURE), 0);
1843 : }
1844 : }
1845 : }
1846 :
1847 : // at first added measure, check if we need to add Clef/Key/TimeSig
1848 : // this is needed if it was changed and needs to be changed back
1849 0 : size_t n = nstaves();
1850 0 : Fraction otick = fmb->tick(), ctick = tickOfAppend;
1851 0 : for (staff_idx_t staffIdx = 0; staffIdx < n; ++staffIdx) { // iterate over all staves
1852 0 : track_idx_t trackIdx = staff2track(staffIdx); // idx of irst track on the staff
1853 0 : Staff* staff = this->staff(staffIdx);
1854 0 : Staff* ostaff = score->staff(staffIdx);
1855 :
1856 : // check if key signature needs to be changed
1857 0 : if (ostaff->key(otick) != staff->key(ctick)) {
1858 0 : Segment* ns = firstAppendedMeasure->undoGetSegment(SegmentType::KeySig, ctick);
1859 0 : KeySigEvent nkse = KeySigEvent(ostaff->keySigEvent(otick));
1860 0 : KeySig* nks = Factory::createKeySig(ns);
1861 0 : nks->setScore(this);
1862 0 : nks->setTrack(trackIdx);
1863 :
1864 0 : nks->setKeySigEvent(nkse);
1865 0 : staff->setKey(ctick, nkse);
1866 0 : ns->add(nks);
1867 0 : }
1868 : // check if a key signature is present but is spurious (i.e. no actual change)
1869 0 : else if (staff->currentKeyTick(ctick) == ctick
1870 0 : && staff->key(ctick - Fraction::eps()) == ostaff->key(otick)) {
1871 0 : Segment* ns = firstAppendedMeasure->first(SegmentType::KeySig);
1872 0 : if (ns) {
1873 0 : ns->remove(ns->element(trackIdx));
1874 : }
1875 : }
1876 :
1877 : // check if time signature needs to be changed
1878 0 : TimeSig* ots = ostaff->timeSig(otick), * cts = staff->timeSig(ctick);
1879 0 : TimeSig* pts = staff->timeSig(ctick - Fraction::eps());
1880 0 : if (ots && cts && *ots != *cts) {
1881 0 : Segment* ns = firstAppendedMeasure->undoGetSegment(SegmentType::TimeSig, ctick);
1882 0 : TimeSig* nsig = new TimeSig(*ots);
1883 :
1884 0 : nsig->setScore(this);
1885 0 : nsig->setTrack(trackIdx);
1886 0 : ns->add(nsig);
1887 : }
1888 : // check if a time signature is present but is spurious (i.e. no actual change)
1889 0 : else if (staff->currentTimeSigTick(ctick) == ctick
1890 0 : && ots && pts && *pts == *ots) {
1891 0 : Segment* ns = firstAppendedMeasure->first(SegmentType::TimeSig);
1892 0 : if (ns) {
1893 0 : ns->remove(ns->element(trackIdx));
1894 : }
1895 : }
1896 :
1897 : // check if clef signature needs to be changed
1898 0 : if (ostaff->clef(otick) != staff->clef(ctick)) {
1899 0 : EditClef::undoChangeClef(tx, this, staff, firstAppendedMeasure, ostaff->clef(otick));
1900 : }
1901 : // check if a clef change is present but is spurious (i.e. no actual change)
1902 0 : else if (staff->currentClefTick(ctick) == ctick
1903 0 : && staff->clef(ctick - Fraction::eps()) == ostaff->clef(otick)) {
1904 0 : Segment* ns = firstAppendedMeasure->first(SegmentType::Clef);
1905 0 : if (!ns) {
1906 0 : ns = firstAppendedMeasure->first(SegmentType::HeaderClef);
1907 : }
1908 0 : if (ns) {
1909 0 : ns->remove(ns->element(trackIdx));
1910 : }
1911 : }
1912 : }
1913 :
1914 : // check if section starts with a pick-up measure to be merged with end of previous section
1915 0 : Measure* cm = firstAppendedMeasure, * pm = cm->prevMeasure();
1916 0 : if (pm->timesig() == cm->timesig() && pm->ticks() + cm->ticks() == cm->timesig()) {
1917 0 : SplitJoinMeasure::joinMeasures(tx, m_masterScore, pm->tick(), cm->tick());
1918 : }
1919 :
1920 : // clone the spanners (only in the range currently copied)
1921 0 : auto ospans = score->spanner();
1922 0 : auto lb = ospans.lower_bound(startTick.ticks()), ub = ospans.upper_bound(endTick.ticks());
1923 0 : for (auto sp = lb; sp != ub; sp++) {
1924 0 : Spanner* spanner = sp->second;
1925 :
1926 0 : if (spanner->tick2() > endTick) {
1927 0 : continue; // map is by tick() so this can still happen in theory...
1928 : }
1929 0 : Spanner* ns = toSpanner(spanner->clone());
1930 0 : ns->setScore(this);
1931 0 : ns->moveToDummy();
1932 0 : ns->setTick(spanner->tick() - startTick + tickOfAppend);
1933 0 : ns->setTick2(spanner->tick2() - startTick + tickOfAppend);
1934 0 : ns->computeStartElement();
1935 0 : ns->computeEndElement();
1936 0 : addElement(ns);
1937 : }
1938 :
1939 0 : return true;
1940 0 : }
1941 :
1942 : //---------------------------------------------------------
1943 : // splitStaff
1944 : //---------------------------------------------------------
1945 :
1946 6 : void Score::splitStaff(staff_idx_t staffIdx, int splitPoint)
1947 : {
1948 : // LOGD("split staff %d point %d", staffIdx, splitPoint);
1949 :
1950 : //
1951 : // create second staff
1952 : //
1953 6 : Staff* st = staff(staffIdx);
1954 6 : Part* p = st->part();
1955 6 : Staff* ns = Factory::createStaff(p);
1956 6 : ns->init(st);
1957 :
1958 : // convert staffIdx from score-relative to part-relative
1959 6 : staff_idx_t staffIdxPart = staffIdx - p->staff(0)->idx();
1960 6 : undoInsertStaff(ns, staffIdxPart + 1, false);
1961 :
1962 6 : Segment* seg = firstMeasure()->getSegment(SegmentType::HeaderClef, Fraction(0, 1));
1963 6 : Clef* clef = Factory::createClef(seg);
1964 6 : clef->setClefType(ClefType::F);
1965 6 : clef->setTrack((staffIdx + 1) * VOICES);
1966 6 : undoAddElement(clef);
1967 :
1968 6 : Transaction& tx = transactionManager()->currentOrDummyTransaction();
1969 6 : EditKeySig::undoChangeKeySig(tx, this, ns, Fraction(0, 1), st->keySigEvent(Fraction(0, 1)));
1970 :
1971 6 : masterScore()->rebuildMidiMapping();
1972 6 : cmdState().instrumentsChanged = true;
1973 6 : doLayout();
1974 :
1975 : //
1976 : // move notes
1977 : //
1978 6 : select(0, SelectType::SINGLE, 0);
1979 6 : track_idx_t strack = staffIdx * VOICES;
1980 6 : track_idx_t dtrack = (staffIdx + 1) * VOICES;
1981 :
1982 : // Keep track of ties to be reconnected.
1983 : struct OldTie {
1984 : Tie* tie;
1985 : Note* nnote;
1986 : };
1987 6 : std::map<Note*, OldTie> oldTies;
1988 :
1989 : // Notes under the split point can be part of a tuplet, so keep track
1990 : // of the tuplet mapping too!
1991 6 : std::map<Tuplet*, Tuplet*> tupletMapping;
1992 6 : Tuplet* tupletSrc[VOICES] = { };
1993 6 : Tuplet* tupletDst[VOICES] = { };
1994 :
1995 43 : for (Segment* s = firstSegment(SegmentType::ChordRest); s; s = s->next1(SegmentType::ChordRest)) {
1996 185 : for (voice_idx_t voice = 0; voice < VOICES; ++voice) {
1997 148 : EngravingItem* e = s->element(strack + voice);
1998 :
1999 148 : if (!e) {
2000 111 : continue;
2001 : }
2002 37 : if (toDurationElement(e)->tuplet()) {
2003 12 : tupletSrc[voice] = toDurationElement(e)->tuplet();
2004 12 : if (muse::contains(tupletMapping, tupletSrc[voice])) {
2005 8 : tupletDst[voice] = tupletMapping[tupletSrc[voice]];
2006 : } else {
2007 4 : tupletDst[voice] = Factory::copyTuplet(*tupletSrc[voice]);
2008 4 : tupletDst[voice]->setTrack(dtrack);
2009 4 : tupletMapping.insert({ tupletSrc[voice], tupletDst[voice] });
2010 : }
2011 : } else {
2012 25 : tupletSrc[voice] = nullptr;
2013 25 : tupletDst[voice] = nullptr;
2014 : }
2015 :
2016 37 : bool createRestDst = true;
2017 37 : bool createRestSrc = false;
2018 37 : TDuration lengthDst = toChordRest(e)->actualDurationType();
2019 37 : TDuration lengthSrc = TDuration();
2020 :
2021 37 : if (e->isChord()) {
2022 21 : Chord* c = toChord(e);
2023 21 : std::list<Note*> removeNotes;
2024 46 : for (Note* note : c->notes()) {
2025 25 : if (note->pitch() >= splitPoint) {
2026 13 : continue;
2027 : } else {
2028 12 : Chord* chord = toChord(s->element(dtrack + voice));
2029 12 : assert(!chord || (chord->isChord()));
2030 12 : if (!chord) {
2031 10 : chord = Factory::copyChord(*c);
2032 10 : muse::DeleteAll(chord->notes());
2033 10 : chord->notes().clear();
2034 10 : chord->setTuplet(tupletDst[voice]);
2035 10 : chord->setTrack(dtrack + voice);
2036 10 : undoAddElement(chord);
2037 : }
2038 12 : Note* nnote = Factory::copyNote(*note);
2039 12 : if (note->tieFor()) {
2040 : // Save the note and the tie for processing later.
2041 : // Use the end note as index in the map so, when this is found
2042 : // we know the tie has to be recreated.
2043 0 : oldTies.insert({ note->tieFor()->endNote(), OldTie { note->tieFor(), nnote } });
2044 : }
2045 12 : nnote->setTrack(dtrack + voice);
2046 12 : chord->add(nnote);
2047 12 : nnote->updateLine();
2048 12 : removeNotes.push_back(note);
2049 12 : createRestDst = false;
2050 12 : lengthDst = chord->actualDurationType();
2051 :
2052 : // Is the note the last note of a tie?
2053 12 : if (muse::contains(oldTies, note)) {
2054 : // Yes! Create a tie between the new notes and remove the
2055 : // old tie.
2056 0 : Tie* tie = oldTies[note].tie->clone();
2057 0 : tie->setStartNote(oldTies[note].nnote);
2058 0 : tie->setEndNote(nnote);
2059 0 : tie->setTrack(nnote->track());
2060 0 : undoAddElement(tie);
2061 0 : undoRemoveElement(oldTies[note].tie);
2062 0 : muse::remove(oldTies, note);
2063 : }
2064 : }
2065 : }
2066 21 : createRestSrc = false;
2067 33 : for (Note* note : removeNotes) {
2068 12 : undoRemoveElement(note);
2069 12 : Chord* chord = note->chord();
2070 12 : if (chord->notes().empty()) {
2071 11 : for (auto sp : spanner()) {
2072 2 : Slur* slur = toSlur(sp.second);
2073 2 : if (!slur->isSlur()) {
2074 0 : continue;
2075 : }
2076 2 : if (slur->startCR() == chord) {
2077 1 : slur->undoChangeProperty(Pid::TRACK, slur->track() + VOICES);
2078 2 : for (EngravingObject* ee : slur->linkList()) {
2079 1 : Slur* lslur = toSlur(ee);
2080 1 : lslur->setStartElement(0);
2081 1 : }
2082 : }
2083 2 : if (slur->endCR() == chord) {
2084 1 : slur->undoChangeProperty(Pid::SPANNER_TRACK2, slur->track2() + VOICES);
2085 2 : for (EngravingObject* ee : slur->linkList()) {
2086 1 : Slur* lslur = toSlur(ee);
2087 1 : lslur->setEndElement(0);
2088 1 : }
2089 : }
2090 : }
2091 9 : createRestSrc = true;
2092 9 : lengthSrc = chord->actualDurationType();
2093 9 : undoRemoveElement(chord);
2094 : }
2095 : }
2096 21 : }
2097 :
2098 37 : if (createRestSrc) {
2099 9 : addRest(s, strack + voice, lengthSrc, tupletSrc[voice]);
2100 : }
2101 37 : if (createRestDst) {
2102 27 : addRest(s, dtrack + voice, lengthDst, tupletDst[voice]);
2103 : }
2104 : }
2105 : }
2106 6 : }
2107 :
2108 : //---------------------------------------------------------
2109 : // cmdRemovePart
2110 : //---------------------------------------------------------
2111 :
2112 8 : void Score::cmdRemovePart(Part* part)
2113 : {
2114 8 : if (!part) {
2115 0 : return;
2116 : }
2117 :
2118 8 : Transaction& tx = transactionManager()->currentOrDummyTransaction();
2119 8 : EditStaveSharing::handleRemovePart(tx, part);
2120 :
2121 8 : staff_idx_t sidx = staffIdx(part);
2122 8 : size_t n = part->nstaves();
2123 :
2124 16 : for (staff_idx_t i = 0; i < n; ++i) {
2125 8 : cmdRemoveStaff(sidx);
2126 : }
2127 :
2128 8 : undoRemovePart(part, muse::indexOf(m_parts, part));
2129 :
2130 8 : if (!hasSharedParts()) {
2131 7 : undoChangeStyleVal(Sid::enableStaveSharing, false);
2132 : }
2133 : }
2134 :
2135 : //---------------------------------------------------------
2136 : // insertPart
2137 : //---------------------------------------------------------
2138 :
2139 18 : void Score::insertPart(Part* part, size_t targetPartIdx)
2140 : {
2141 18 : if (!part) {
2142 0 : return;
2143 : }
2144 :
2145 18 : part->setScore(this);
2146 18 : assignIdIfNeed(*part);
2147 :
2148 18 : if (targetPartIdx < m_parts.size()) {
2149 15 : m_parts.insert(m_parts.begin() + targetPartIdx, part);
2150 : } else {
2151 3 : m_parts.push_back(part);
2152 : }
2153 :
2154 18 : masterScore()->rebuildMidiMapping();
2155 18 : setInstrumentsChanged(true);
2156 18 : markInstrumentsAsPrimary(m_parts);
2157 : }
2158 :
2159 3040 : void Score::appendPart(Part* part)
2160 : {
2161 3040 : if (!part) {
2162 0 : return;
2163 : }
2164 :
2165 3040 : part->setScore(this);
2166 3040 : assignIdIfNeed(*part);
2167 3040 : m_parts.push_back(part);
2168 3040 : markInstrumentsAsPrimary(m_parts);
2169 : }
2170 :
2171 : //---------------------------------------------------------
2172 : // removePart
2173 : //---------------------------------------------------------
2174 :
2175 14 : void Score::removePart(Part* part)
2176 : {
2177 14 : part_idx_t index = muse::indexOf(m_parts, part);
2178 :
2179 14 : if (index == muse::nidx) {
2180 0 : for (size_t i = 0; i < m_parts.size(); ++i) {
2181 0 : if (m_parts[i]->id() == part->id()) {
2182 0 : index = i;
2183 0 : break;
2184 : }
2185 : }
2186 : }
2187 :
2188 14 : m_parts.erase(m_parts.begin() + index);
2189 14 : markInstrumentsAsPrimary(m_parts);
2190 :
2191 14 : if (m_excerpt) {
2192 0 : for (Part* excerptPart : m_excerpt->parts()) {
2193 0 : if (excerptPart->id() != part->id()) {
2194 0 : continue;
2195 : }
2196 :
2197 0 : muse::remove(m_excerpt->parts(), excerptPart);
2198 0 : markInstrumentsAsPrimary(m_excerpt->parts());
2199 0 : break;
2200 : }
2201 : }
2202 :
2203 14 : masterScore()->rebuildMidiMapping();
2204 14 : setInstrumentsChanged(true);
2205 14 : }
2206 :
2207 : //---------------------------------------------------------
2208 : // insertStaff
2209 : //---------------------------------------------------------
2210 :
2211 343 : void Score::insertStaff(Staff* staff, staff_idx_t ridx)
2212 : {
2213 343 : if (!staff || !staff->part()) {
2214 0 : return;
2215 : }
2216 :
2217 343 : assignIdIfNeed(*staff);
2218 343 : staff->part()->insertStaff(staff, ridx);
2219 :
2220 343 : staff_idx_t idx = staffIdx(staff->part()) + ridx;
2221 343 : m_staves.insert(m_staves.begin() + idx, staff);
2222 343 : m_brackets.insert(m_brackets.begin() + idx, std::vector<BracketItem*>());
2223 443 : for (staff_idx_t i = idx + 1; i < static_cast<staff_idx_t>(m_brackets.size()); ++i) {
2224 146 : for (BracketItem* bi : m_brackets[i]) {
2225 46 : bi->setStartStaffIdx(i);
2226 : }
2227 : }
2228 :
2229 363 : for (auto i = staff->score()->spanner().cbegin(); i != staff->score()->spanner().cend(); ++i) {
2230 20 : Spanner* s = i->second;
2231 20 : if (!moveDownWhenAddingStaves(s, idx)) {
2232 18 : continue;
2233 : }
2234 2 : if (s->staffIdx() >= idx) {
2235 2 : track_idx_t t = s->track() + VOICES;
2236 2 : if (t >= ntracks()) {
2237 0 : t = ntracks() - 1;
2238 : }
2239 2 : s->setTrack(t);
2240 4 : for (SpannerSegment* ss : s->spannerSegments()) {
2241 2 : ss->setTrack(t);
2242 : }
2243 2 : if (s->track2() != muse::nidx) {
2244 2 : t = s->track2() + VOICES;
2245 2 : s->setTrack2(t < ntracks() ? t : s->track());
2246 : }
2247 : }
2248 : }
2249 :
2250 343 : updateStavesNumberForSystems();
2251 : }
2252 :
2253 3226 : void Score::appendStaff(Staff* staff)
2254 : {
2255 3226 : if (!staff || !staff->part()) {
2256 0 : return;
2257 : }
2258 :
2259 3226 : assignIdIfNeed(*staff);
2260 3226 : staff->part()->appendStaff(staff);
2261 3226 : m_staves.push_back(staff);
2262 3226 : m_brackets.emplace_back();
2263 :
2264 3226 : updateStavesNumberForSystems();
2265 : }
2266 :
2267 3569 : void Score::assignIdIfNeed(Staff& staff) const
2268 : {
2269 3569 : if (staff.id() == INVALID_ID) {
2270 3424 : staff.setId(newStaffId());
2271 : }
2272 3569 : }
2273 :
2274 3058 : void Score::assignIdIfNeed(Part& part) const
2275 : {
2276 3058 : if (part.id() == INVALID_ID) {
2277 2016 : part.setId(newPartId());
2278 : }
2279 3058 : }
2280 :
2281 3606 : void Score::updateStavesNumberForSystems()
2282 : {
2283 3825 : for (System* system : m_systems) {
2284 219 : if (!system->firstMeasure()) {
2285 62 : continue;
2286 : }
2287 :
2288 157 : system->adjustStavesNumber(nstaves());
2289 : }
2290 3606 : }
2291 :
2292 3424 : ID Score::newStaffId() const
2293 : {
2294 3424 : ID maxId = 0;
2295 :
2296 17578 : for (const Staff* staff : score()->staves()) {
2297 14154 : maxId = std::max(maxId, staff->id());
2298 : }
2299 :
2300 3424 : return maxId + 1;
2301 : }
2302 :
2303 2016 : ID Score::newPartId() const
2304 : {
2305 2016 : ID maxId = 0;
2306 :
2307 14539 : for (const Part* part : score()->parts()) {
2308 12523 : maxId = std::max(maxId, part->id());
2309 : }
2310 :
2311 2016 : return maxId + 1;
2312 : }
2313 :
2314 : //---------------------------------------------------------
2315 : // removeStaff
2316 : //---------------------------------------------------------
2317 :
2318 37 : void Score::removeStaff(Staff* staff)
2319 : {
2320 37 : staff_idx_t idx = staff->idx();
2321 42 : for (auto i = staff->score()->spanner().cbegin(); i != staff->score()->spanner().cend(); ++i) {
2322 5 : Spanner* s = i->second;
2323 5 : if (s->staffIdx() > idx) {
2324 2 : track_idx_t t = s->track() >= VOICES ? s->track() - VOICES : 0;
2325 2 : s->setTrack(t);
2326 4 : for (SpannerSegment* ss : s->spannerSegments()) {
2327 2 : ss->setTrack(t);
2328 : }
2329 2 : if (s->track2() != muse::nidx) {
2330 2 : t = s->track2() - VOICES;
2331 2 : s->setTrack2((t != muse::nidx) ? t : s->track());
2332 : }
2333 : }
2334 : }
2335 :
2336 37 : muse::remove(m_staves, staff);
2337 :
2338 37 : muse::DeleteAll(m_brackets[idx]);
2339 37 : m_brackets.erase(m_brackets.begin() + idx);
2340 70 : for (staff_idx_t i = idx; i < static_cast<staff_idx_t>(m_brackets.size()); ++i) {
2341 62 : for (BracketItem* bi : m_brackets[i]) {
2342 29 : bi->setStartStaffIdx(i);
2343 : }
2344 : }
2345 37 : staff->part()->removeStaff(staff);
2346 :
2347 37 : if (staff->isSystemObjectStaff()) {
2348 0 : muse::remove(m_systemObjectStaves, staff);
2349 : }
2350 :
2351 37 : updateStavesNumberForSystems();
2352 37 : }
2353 :
2354 : //---------------------------------------------------------
2355 : // adjustKeySigs
2356 : //---------------------------------------------------------
2357 :
2358 24 : void Score::adjustKeySigs(track_idx_t sidx, track_idx_t eidx, KeyList km)
2359 : {
2360 48 : for (track_idx_t staffIdx = sidx; staffIdx < eidx; ++staffIdx) {
2361 24 : Staff* staff = m_staves[staffIdx];
2362 44 : for (auto i = km.begin(); i != km.end(); ++i) {
2363 20 : Fraction tick = Fraction::fromTicks(i->first);
2364 20 : Measure* measure = tick2measure(tick);
2365 20 : if (!measure) {
2366 0 : continue;
2367 : }
2368 20 : if (staff->isDrumStaff(tick)) {
2369 0 : continue;
2370 : }
2371 20 : KeySigEvent key = i->second;
2372 20 : Interval v = staff->part()->instrument(tick)->transpose();
2373 20 : if (!v.isZero() && !style().styleB(Sid::concertPitch) && !key.isAtonal()) {
2374 0 : v.flip();
2375 0 : key.setKey(Transpose::transposeKey(key.concertKey(), v, staff->part()->preferSharpFlat()));
2376 : }
2377 20 : staff->setKey(tick, key);
2378 :
2379 20 : Segment* s = measure->getSegment(SegmentType::KeySig, tick);
2380 20 : KeySig* keysig = Factory::createKeySig(s);
2381 20 : keysig->setTrack(staffIdx * VOICES);
2382 20 : keysig->setKeySigEvent(key);
2383 20 : doUndoAddElement(keysig);
2384 20 : }
2385 : }
2386 24 : }
2387 :
2388 : //---------------------------------------------------------
2389 : // getKeyList
2390 : // This is taken from MuseScore::editInstrList()
2391 : //---------------------------------------------------------
2392 :
2393 20 : KeyList Score::keyList() const
2394 : {
2395 : // find the keylist of the first pitched staff
2396 20 : KeyList tmpKeymap;
2397 20 : Staff* firstStaff = nullptr;
2398 20 : bool isFirstStaff = true;
2399 70 : for (Staff* s : masterScore()->staves()) {
2400 50 : if (!s->isDrumStaff(Fraction(0, 1))) {
2401 50 : KeyList* km = s->keyList();
2402 50 : if (isFirstStaff) {
2403 20 : isFirstStaff = false;
2404 20 : tmpKeymap.insert(km->begin(), km->end());
2405 20 : firstStaff = s;
2406 20 : continue;
2407 : }
2408 :
2409 : // in terms of ticks, tmpKeymap = intersection(tmpKeymap, km)
2410 : // note that if every single staff has a custom local keysig on this tick,
2411 : // we will just copy the one from the top staff.
2412 30 : auto currKey = km->begin();
2413 30 : KeyList kl;
2414 32 : for (auto key : tmpKeymap) {
2415 28 : if (key.first < (*currKey).first) {
2416 0 : continue;
2417 : }
2418 28 : while ((*currKey).first < key.first && currKey != km->end()) {
2419 0 : currKey++;
2420 : }
2421 28 : if (currKey == km->end()) {
2422 1 : break;
2423 : }
2424 27 : if (key.first == (*currKey).first) {
2425 : // there is a matching key sig on this staff
2426 27 : ++currKey;
2427 27 : kl.insert(key);
2428 27 : if (currKey == km->end()) {
2429 25 : break;
2430 : }
2431 : }
2432 28 : }
2433 30 : tmpKeymap.clear();
2434 30 : tmpKeymap.insert(kl.begin(), kl.end());
2435 30 : }
2436 : }
2437 20 : Key normalizedC = Key::C;
2438 20 : bool needNormalize = firstStaff && !masterScore()->style().styleB(Sid::concertPitch)
2439 40 : && (firstStaff->part()->instrument()->transpose().chromatic || firstStaff->part()->instruments().size() > 1);
2440 : // normalize the keyevents to concert pitch if necessary
2441 20 : KeyList nkl;
2442 38 : for (auto key : tmpKeymap) {
2443 18 : if (firstStaff && !key.second.forInstrumentChange()) {
2444 18 : if (needNormalize) {
2445 0 : Key cKey = key.second.concertKey();
2446 0 : key.second.setKey(cKey);
2447 : }
2448 18 : nkl.insert(key);
2449 : }
2450 18 : }
2451 20 : if (firstStaff && !masterScore()->style().styleB(Sid::concertPitch)
2452 40 : && (firstStaff->part()->instrument()->transpose().chromatic || firstStaff->part()->instruments().size() > 1)) {
2453 0 : int interval = firstStaff->part()->instrument()->transpose().chromatic;
2454 0 : normalizedC = Transpose::transposeKey(normalizedC, interval);
2455 : }
2456 :
2457 : // create initial keyevent for transposing instrument if necessary
2458 20 : auto i = nkl.begin();
2459 20 : if (i == nkl.end() || i->first != 0) {
2460 2 : nkl[0].setConcertKey(normalizedC);
2461 : }
2462 :
2463 20 : return nkl;
2464 20 : }
2465 :
2466 : //---------------------------------------------------------
2467 : // cmdRemoveStaff
2468 : //---------------------------------------------------------
2469 :
2470 12 : void Score::cmdRemoveStaff(staff_idx_t staffIdx)
2471 : {
2472 12 : Staff* s = staff(staffIdx);
2473 12 : EditStaffBrackets::adjustBracketsDel(this, staffIdx, staffIdx + 1);
2474 :
2475 12 : undoRemoveStaff(s);
2476 12 : }
2477 :
2478 5 : void Score::sortSystemObjects(std::vector<staff_idx_t>& dst)
2479 : {
2480 5 : std::vector<staff_idx_t> moveTo;
2481 5 : for (const Staff* staff : m_systemObjectStaves) {
2482 0 : staff_idx_t oldStaffIdx = staff->idx();
2483 0 : staff_idx_t newStaffIfx = oldStaffIdx < dst.size() ? dst[oldStaffIdx] : muse::nidx;
2484 :
2485 0 : if (muse::contains(moveTo, newStaffIfx)) {
2486 0 : newStaffIfx = muse::nidx;
2487 : }
2488 :
2489 0 : moveTo.push_back(newStaffIfx);
2490 : }
2491 :
2492 5 : for (staff_idx_t i = 0; i < m_systemObjectStaves.size(); i++) {
2493 0 : if (moveTo[i] == m_systemObjectStaves[i]->idx()) {
2494 : // this sysobj staff doesn't move
2495 0 : continue;
2496 : } else {
2497 : // move all system objects from systemObjectStaves[i] to _staves[moveTo[i]]
2498 0 : for (MeasureBase* mb = measures()->first(); mb; mb = mb->next()) {
2499 0 : if (!mb->isMeasure()) {
2500 0 : continue;
2501 : }
2502 :
2503 0 : Measure* m = toMeasure(mb);
2504 0 : for (EngravingItem* e : m->el()) {
2505 0 : if ((e->isJump() || e->isMarker()) && e->isLinked() && e->track() == staff2track(m_systemObjectStaves[i]->idx())) {
2506 0 : if (moveTo[i] == muse::nidx) {
2507 : // delete this clone
2508 0 : m->remove(e);
2509 0 : e->unlink();
2510 0 : delete e;
2511 : } else {
2512 0 : e->setTrack(staff2track(m_staves[moveTo[i]]->idx()));
2513 : }
2514 : }
2515 : }
2516 :
2517 0 : for (Segment* s = m->first(); s; s = s->next()) {
2518 0 : if (s->isChordRest() || !s->annotations().empty()) {
2519 0 : const auto annotations = s->annotations(); // make a copy since we alter the list
2520 0 : for (EngravingItem* e : annotations) {
2521 0 : if (e->isRehearsalMark()
2522 0 : || e->isSystemText()
2523 0 : || e->isTripletFeel()
2524 0 : || e->isTempoText()
2525 0 : || isSystemTextLine(e)) {
2526 0 : if (e->track() == staff2track(m_systemObjectStaves[i]->idx()) && e->isLinked()) {
2527 0 : if (moveTo[i] == muse::nidx) {
2528 0 : s->removeAnnotation(e);
2529 0 : e->unlink();
2530 0 : delete e;
2531 : } else {
2532 0 : e->setTrack(staff2track(m_staves[moveTo[i]]->idx()));
2533 : }
2534 : }
2535 : }
2536 : }
2537 0 : }
2538 : }
2539 : }
2540 :
2541 : // update systemObjectStaves with the correct staff
2542 0 : if (moveTo[i] == muse::nidx) {
2543 0 : m_systemObjectStaves.erase(m_systemObjectStaves.begin() + i);
2544 0 : moveTo.erase(moveTo.begin() + i);
2545 : } else {
2546 0 : m_systemObjectStaves[i] = m_staves[moveTo[i]];
2547 : }
2548 : }
2549 : }
2550 5 : }
2551 :
2552 : //---------------------------------------------------------
2553 : // sortStaves
2554 : //---------------------------------------------------------
2555 :
2556 5 : void Score::sortStaves(std::vector<staff_idx_t>& dst)
2557 : {
2558 5 : sortSystemObjects(dst);
2559 5 : muse::DeleteAll(systems());
2560 5 : systems().clear(); //??
2561 5 : m_parts.clear();
2562 5 : Part* curPart = nullptr;
2563 5 : std::vector<Staff*> dl;
2564 5 : std::map<size_t, size_t> trackMap;
2565 5 : track_idx_t track = 0;
2566 17 : for (staff_idx_t idx : dst) {
2567 12 : Staff* staff = m_staves[idx];
2568 12 : if (staff->part() != curPart) {
2569 10 : curPart = staff->part();
2570 10 : curPart->clearStaves();
2571 10 : m_parts.push_back(curPart);
2572 : }
2573 12 : curPart->appendStaff(staff);
2574 12 : dl.push_back(staff);
2575 60 : for (size_t itrack = 0; itrack < VOICES; ++itrack) {
2576 48 : trackMap.insert({ idx* VOICES + itrack, track++ });
2577 : }
2578 : }
2579 5 : m_staves = dl;
2580 :
2581 5 : for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
2582 0 : m->sortStaves(dst);
2583 0 : if (m->hasMMRest()) {
2584 0 : m->mmRest()->sortStaves(dst);
2585 : }
2586 : }
2587 5 : for (auto i : m_spanner.map()) {
2588 0 : Spanner* sp = i.second;
2589 0 : voice_idx_t voice = sp->voice();
2590 0 : staff_idx_t staffIdx = sp->staffIdx();
2591 0 : staff_idx_t idx = muse::indexOf(dst, staffIdx);
2592 0 : if (idx != muse::nidx && !sp->isTopSystemObject()) {
2593 0 : sp->setTrack(idx * VOICES + voice);
2594 0 : if (sp->track2() != muse::nidx) {
2595 0 : sp->setTrack2(idx * VOICES + (sp->track2() % VOICES)); // at least keep the voice...
2596 : }
2597 : }
2598 : }
2599 5 : setLayoutAll();
2600 5 : markInstrumentsAsPrimary(m_parts);
2601 5 : }
2602 :
2603 : //---------------------------------------------------------
2604 : // cmdConcertPitchChanged
2605 : //---------------------------------------------------------
2606 :
2607 6 : void Score::cmdConcertPitchChanged(bool flag)
2608 : {
2609 6 : if (flag == style().styleB(Sid::concertPitch)) {
2610 0 : return;
2611 : }
2612 :
2613 6 : Transaction& tx = transactionManager()->currentOrDummyTransaction();
2614 :
2615 6 : undoChangeStyleVal(Sid::concertPitch, flag); // change style flag
2616 :
2617 14 : for (Staff* staff : m_staves) {
2618 8 : if (staff->staffType(Fraction(0, 1))->group() == StaffGroup::PERCUSSION) { // TODO
2619 1 : continue;
2620 : }
2621 : // if this staff has no transposition, and no instrument changes, we can skip it
2622 8 : Interval interval = staff->part()->instrument()->transpose(); //tick?
2623 8 : if (interval.isZero() && staff->part()->instruments().size() == 1) {
2624 1 : continue;
2625 : }
2626 7 : if (!flag) {
2627 3 : interval.flip();
2628 : }
2629 :
2630 7 : staff_idx_t staffIdx = staff->idx();
2631 7 : track_idx_t startTrack = staffIdx * VOICES;
2632 7 : track_idx_t endTrack = startTrack + VOICES;
2633 :
2634 7 : Transpose::transposeKeys(tx, this, staffIdx, staffIdx + 1, Fraction(0, 1), lastSegment()->tick(), !flag);
2635 :
2636 62 : for (Segment* segment = firstSegment(SegmentType::ChordRest); segment; segment = segment->next1(SegmentType::ChordRest)) {
2637 55 : interval = staff->transpose(segment->tick());
2638 55 : if (!flag) {
2639 37 : interval.flip();
2640 : }
2641 80 : for (EngravingItem* e : segment->annotations()) {
2642 25 : if (!e->isHarmony() || (e->track() < startTrack) || (e->track() >= endTrack)) {
2643 0 : continue;
2644 : }
2645 25 : Harmony* h = toHarmony(e);
2646 50 : for (EngravingObject* se : h->linkList()) {
2647 : // don't transpose all links
2648 : // just ones resulting from mmrests
2649 25 : Harmony* he = toHarmony(se); // toHarmony() does not work as e is an ScoreElement
2650 25 : if (he->staff() == h->staff()) {
2651 25 : Transpose::undoTransposeHarmony(tx, he, interval);
2652 : }
2653 25 : }
2654 : //realized harmony should be invalid after a transpose command
2655 25 : assert(!h->realizedHarmony().valid());
2656 : }
2657 : }
2658 : }
2659 : }
2660 :
2661 1883 : static void onFocusedItemChanged(EngravingItem* item)
2662 : {
2663 : #ifndef ENGRAVING_NO_ACCESSIBILITY
2664 1883 : if (!item || !item->selected()) {
2665 1883 : return;
2666 : }
2667 :
2668 1883 : if (item->isSpannerSegment()) {
2669 72 : item = toSpannerSegment(item)->spanner();
2670 72 : if (!item) {
2671 0 : return;
2672 : }
2673 : }
2674 :
2675 1883 : item->initAccessibleIfNeed();
2676 :
2677 1883 : AccessibleItemPtr accessible = item->accessible();
2678 1883 : if (!accessible) {
2679 1883 : return;
2680 : }
2681 :
2682 0 : const Score* score = item->score();
2683 0 : if (!score) {
2684 0 : return;
2685 : }
2686 :
2687 0 : AccessibleRoot* currAccRoot = accessible->accessibleRoot();
2688 0 : AccessibleRoot* accRoot = score->rootItem()->accessible()->accessibleRoot();
2689 0 : AccessibleRoot* dummyAccRoot = score->dummy()->rootItem()->accessible()->accessibleRoot();
2690 :
2691 0 : if (accRoot && currAccRoot == accRoot && accRoot->registered() && accRoot->enabled()) {
2692 0 : accRoot->setFocusedElement(accessible);
2693 :
2694 0 : if (AccessibleItemPtr focusedElement = dummyAccRoot->focusedElement().lock()) {
2695 0 : accRoot->updateStaffInfo(accessible, focusedElement);
2696 0 : }
2697 :
2698 0 : dummyAccRoot->setFocusedElement(nullptr);
2699 : }
2700 :
2701 0 : if (dummyAccRoot && currAccRoot == dummyAccRoot && dummyAccRoot->registered() && dummyAccRoot->enabled()) {
2702 0 : dummyAccRoot->setFocusedElement(accessible);
2703 :
2704 0 : if (AccessibleItemPtr focusedElement = accRoot->focusedElement().lock()) {
2705 0 : dummyAccRoot->updateStaffInfo(accessible, focusedElement);
2706 0 : }
2707 :
2708 0 : accRoot->setFocusedElement(nullptr);
2709 : }
2710 : #else
2711 : UNUSED(item);
2712 : #endif
2713 1883 : }
2714 :
2715 : //---------------------------------------------------------
2716 : // deselect
2717 : //---------------------------------------------------------
2718 :
2719 41 : void Score::deselect(EngravingItem* el)
2720 : {
2721 41 : addRefresh(el->pageBoundingRect());
2722 41 : m_selection.remove(el);
2723 41 : setSelectionChanged(true);
2724 41 : m_selection.update();
2725 41 : }
2726 :
2727 : //---------------------------------------------------------
2728 : // select
2729 : // staffIdx is valid, if element is of type MEASURE
2730 : //---------------------------------------------------------
2731 :
2732 1739 : void Score::select(EngravingItem* item, SelectType type, staff_idx_t staffIdx)
2733 : {
2734 3478 : select(std::vector<EngravingItem*> { item }, type, staffIdx);
2735 1739 : }
2736 :
2737 1934 : void Score::select(const std::vector<EngravingItem*>& items, SelectType type, staff_idx_t staffIdx)
2738 : {
2739 4746 : for (EngravingItem* item : items) {
2740 2812 : doSelect(item, type, staffIdx);
2741 : }
2742 :
2743 1934 : if (!m_selection.elements().empty()) {
2744 1883 : onFocusedItemChanged(m_selection.elements().back());
2745 : }
2746 1934 : }
2747 :
2748 2916 : void Score::doSelect(EngravingItem* e, SelectType type, staff_idx_t staffIdx)
2749 : {
2750 2916 : if (MScore::debugMode) {
2751 0 : LOGD("select element <%s> type %d(state %d) staff %zu",
2752 0 : e ? e->typeName() : "", int(type), int(selection().state()), e ? e->staffIdx() : -1);
2753 : }
2754 :
2755 2916 : switch (type) {
2756 1485 : case SelectType::SINGLE:
2757 1485 : selectSingle(e, staffIdx);
2758 1485 : break;
2759 1077 : case SelectType::ADD:
2760 1077 : selectAdd(e);
2761 1077 : break;
2762 354 : case SelectType::RANGE:
2763 354 : selectRange(e, staffIdx);
2764 354 : break;
2765 0 : case SelectType::REPLACE:
2766 0 : break;
2767 : }
2768 2916 : setSelectionChanged(true);
2769 2916 : }
2770 :
2771 : //---------------------------------------------------------
2772 : // selectSingle
2773 : // staffIdx is valid, if element is of type MEASURE
2774 : //---------------------------------------------------------
2775 :
2776 1485 : void Score::selectSingle(EngravingItem* e, staff_idx_t staffIdx)
2777 : {
2778 1485 : SelState selState = m_selection.state();
2779 1485 : deselectAll();
2780 1485 : if (e == 0) {
2781 49 : selState = SelState::NONE;
2782 49 : setUpdateAll();
2783 : } else {
2784 1436 : if (e->isMeasure()) {
2785 86 : doSelect(toMeasure(e)->coveringMMRestOrThis(), SelectType::RANGE, staffIdx);
2786 86 : return;
2787 : }
2788 1350 : addRefresh(e->pageBoundingRect());
2789 1350 : m_selection.add(e);
2790 1350 : m_is.setTrack(e->track());
2791 1350 : selState = SelState::LIST;
2792 1350 : if (e->isNote()) {
2793 329 : e = toNote(e)->chord();
2794 : }
2795 1350 : if (e->isChordRest()) {
2796 1291 : m_is.setLastSegment(m_is.segment());
2797 1291 : m_is.setSegment(toChordRest(e)->segment());
2798 : }
2799 : }
2800 1399 : m_selection.setActiveSegment(0);
2801 1399 : m_selection.setActiveTrack(0);
2802 :
2803 1399 : m_selection.setState(selState);
2804 : }
2805 :
2806 : //---------------------------------------------------------
2807 : // switchToPageMode
2808 : //---------------------------------------------------------
2809 :
2810 0 : void Score::switchToPageMode()
2811 : {
2812 0 : if (layoutMode() != LayoutMode::PAGE) {
2813 0 : setLayoutMode(LayoutMode::PAGE);
2814 0 : doLayout();
2815 : }
2816 0 : }
2817 :
2818 : //---------------------------------------------------------
2819 : // selectAdd
2820 : //---------------------------------------------------------
2821 :
2822 1077 : void Score::selectAdd(EngravingItem* e)
2823 : {
2824 1077 : SelState selState = m_selection.state();
2825 :
2826 1077 : if (m_selection.isRange()) {
2827 1 : doSelect(e, SelectType::SINGLE, 0);
2828 1 : return;
2829 : }
2830 :
2831 1076 : if (e->isMeasure()) {
2832 0 : Measure* m = toMeasure(e)->coveringMMRestOrThis();
2833 0 : Fraction tick = m->tick();
2834 0 : if (m_selection.isNone()) {
2835 0 : m_selection.setRange(m->tick2segment(tick),
2836 0 : m == lastMeasureMM() ? 0 : m->last(),
2837 : 0,
2838 : nstaves());
2839 0 : setUpdateAll();
2840 0 : selState = SelState::RANGE;
2841 0 : m_selection.updateSelectedElements();
2842 : }
2843 1076 : } else if (!muse::contains(m_selection.elements(), e)) {
2844 1060 : addRefresh(e->pageBoundingRect());
2845 1060 : selState = SelState::LIST;
2846 1060 : m_selection.add(e);
2847 : }
2848 :
2849 1076 : m_selection.setState(selState);
2850 : }
2851 :
2852 : //---------------------------------------------------------
2853 : // selectRange
2854 : // staffIdx is valid, if element is of type MEASURE
2855 : //---------------------------------------------------------
2856 :
2857 207 : static Segment* findElementStartSegment(Score* score, EngravingItem* e)
2858 : {
2859 207 : if (Segment* ancestor = toSegment(e->findAncestor(ElementType::SEGMENT))) {
2860 207 : if (ancestor->isType(SegmentType::Duration)) {
2861 207 : return ancestor;
2862 : }
2863 : }
2864 :
2865 0 : return score->tick2segmentMM(e->tick(), true, SegmentType::Duration);
2866 : }
2867 :
2868 : /// Returns `nullptr` when the end segment is the end of the score;
2869 : /// returns `def` if no end segment is found.
2870 207 : static Segment* findElementEndSegment(Score* score, EngravingItem* e, Segment* def)
2871 : {
2872 207 : ChordRest* cr = nullptr;
2873 :
2874 207 : if (e->isNote()) {
2875 179 : cr = toNote(e)->chord();
2876 28 : } else if (e->isChordRest()) {
2877 28 : cr = toChordRest(e);
2878 0 : } else if (EngravingItem* c = e->findAncestor(ElementType::CHORD)) {
2879 0 : cr = toChordRest(c);
2880 0 : } else if (EngravingItem* r = e->findAncestor(ElementType::REST)) {
2881 0 : cr = toChordRest(r);
2882 : }
2883 :
2884 207 : if (cr) {
2885 207 : return cr->nextSegmentAfterCR(SegmentType::ChordRest | SegmentType::EndBarLine | SegmentType::Clef);
2886 : }
2887 :
2888 0 : if (e->isSpanner() || e->isSpannerSegment()) {
2889 0 : Spanner* sp = e->isSpanner() ? toSpanner(e) : toSpannerSegment(e)->spanner();
2890 0 : return score->tick2segmentMM(sp->tick2(), true, SegmentType::Duration);
2891 : }
2892 :
2893 0 : if (Segment* seg = toSegment(e->findAncestor(ElementType::SEGMENT))) {
2894 0 : if (seg->isType(SegmentType::Duration)) {
2895 : // https://github.com/musescore/MuseScore/pull/25821#issuecomment-2617369881
2896 0 : return seg->nextCR(e->track(), true);
2897 : }
2898 : // Strictly speaking redundant, but more efficient than `tick2segmentMM`
2899 0 : else if (Segment* crSegAtSameTick = seg->measure()->findSegmentR(SegmentType::Duration, seg->rtick())) {
2900 0 : return crSegAtSameTick;
2901 : }
2902 : }
2903 :
2904 0 : const Fraction& tick = e->tick();
2905 0 : if (tick == score->endTick()) {
2906 0 : return nullptr;
2907 : }
2908 :
2909 0 : if (Segment* seg = score->tick2segmentMM(tick, true, SegmentType::Duration)) {
2910 0 : return seg;
2911 : }
2912 :
2913 0 : return def;
2914 : }
2915 :
2916 22 : static std::pair</*startStaff*/ staff_idx_t, /*endStaff*/ staff_idx_t> boxStartEndStaves(const Box* box, size_t scoreNstaves)
2917 : {
2918 22 : IF_ASSERT_FAILED(box) {
2919 0 : return { muse::nidx, muse::nidx };
2920 : }
2921 :
2922 22 : if (box->isVBoxBase()) {
2923 18 : if (scoreNstaves == 0) {
2924 0 : return { muse::nidx, muse::nidx };
2925 : }
2926 18 : return { 0, scoreNstaves };
2927 : }
2928 :
2929 4 : const System* system = box->system();
2930 4 : if (!system) {
2931 0 : return { muse::nidx, muse::nidx };
2932 : }
2933 :
2934 4 : const staff_idx_t firstVisible = system->firstVisibleStaff();
2935 4 : const staff_idx_t lastVisible = system->lastVisibleStaff();
2936 4 : if (firstVisible == muse::nidx || lastVisible == muse::nidx) {
2937 0 : return { muse::nidx, muse::nidx };
2938 : }
2939 4 : return { firstVisible, lastVisible + 1 };
2940 : }
2941 :
2942 482 : void Score::selectRange(EngravingItem* e, staff_idx_t staffIdx)
2943 : {
2944 482 : if (m_selection.isSingle()) {
2945 65 : if (!e->isMeasureBase()
2946 46 : && !e->isNote()
2947 111 : && !e->isChordRest()) {
2948 : // Try to select similar in range
2949 0 : bool success = trySelectSimilarInRange(e);
2950 0 : if (success) {
2951 0 : return;
2952 : }
2953 : }
2954 :
2955 : // Try to extend single selection into range selection
2956 65 : bool success = tryExtendSingleSelectionToRange(e, staffIdx);
2957 65 : if (success) {
2958 65 : return;
2959 : }
2960 : }
2961 :
2962 417 : if (e->isMeasure()) {
2963 244 : Measure* m = toMeasure(e)->coveringMMRestOrThis();
2964 244 : Segment* startSegment = m->first(SegmentType::ChordRest);
2965 244 : Segment* endSegment = m->last();
2966 244 : Fraction tick = m->tick();
2967 244 : Fraction etick = tick + m->ticks();
2968 :
2969 244 : if (m_selection.isRange()) {
2970 : // Extend existing range selection
2971 88 : m_selection.extendRangeSelection(startSegment, endSegment, staffIdx, tick, etick);
2972 : } else {
2973 : // Create new range selection
2974 156 : if (!m_selection.isNone()) {
2975 0 : deselectAll();
2976 : }
2977 156 : m_selection.setRange(startSegment, endSegment, staffIdx, staffIdx + 1);
2978 : }
2979 :
2980 244 : m_selection.updateSelectedElements();
2981 244 : m_selection.setActiveTrack(staffIdx * VOICES);
2982 244 : return;
2983 : }
2984 :
2985 173 : if (m_selection.isRange()) {
2986 : // Try to extend existing range selection to e
2987 115 : bool success = tryExtendRangeSelectionToElem(e);
2988 115 : if (success) {
2989 115 : return;
2990 : }
2991 : }
2992 :
2993 58 : if (e->isNote() || e->isChordRest()) {
2994 : // Create new range selection
2995 41 : if (!m_selection.isNone()) {
2996 0 : deselectAll();
2997 : }
2998 :
2999 41 : ChordRest* cr = e->isNote() ? static_cast<ChordRest*>(toNote(e)->chord()) : toChordRest(e);
3000 :
3001 41 : SegmentType st = SegmentType::ChordRest | SegmentType::EndBarLine | SegmentType::Clef;
3002 41 : m_selection.setRange(cr->segment(), cr->nextSegmentAfterCR(st), cr->staffIdx(), cr->staffIdx() + 1);
3003 41 : m_selection.updateSelectedElements();
3004 :
3005 41 : m_selection.setActiveTrack(cr->track());
3006 41 : return;
3007 : }
3008 :
3009 : // Nothing worked; just select the new element
3010 17 : doSelect(e, SelectType::SINGLE, staffIdx);
3011 : }
3012 :
3013 0 : bool Score::trySelectSimilarInRange(EngravingItem* e)
3014 : {
3015 0 : EngravingItem* selectedElement = m_selection.element();
3016 0 : if (!selectedElement || e->type() != selectedElement->type()) {
3017 0 : return false;
3018 : }
3019 :
3020 0 : staff_idx_t idx1 = selectedElement->staffIdx();
3021 0 : staff_idx_t idx2 = e->staffIdx();
3022 :
3023 0 : if (idx1 == muse::nidx || idx2 == muse::nidx) {
3024 0 : return false;
3025 : }
3026 :
3027 0 : if (idx2 < idx1) {
3028 0 : std::swap(idx1, idx2);
3029 : }
3030 :
3031 0 : Fraction t1 = selectedElement->tick();
3032 0 : Fraction t2 = e->tick();
3033 0 : if (t1 > t2) {
3034 0 : std::swap(t1, t2);
3035 : }
3036 :
3037 0 : Segment* s1 = tick2segmentMM(t1, true, SegmentType::ChordRest);
3038 0 : Segment* s2 = tick2segmentMM(t2, true, SegmentType::ChordRest);
3039 0 : if (s2) {
3040 0 : s2 = s2->next1MM(SegmentType::ChordRest);
3041 : }
3042 :
3043 0 : if (!s1) {
3044 0 : return false;
3045 : }
3046 :
3047 0 : m_selection.setRange(s1, s2, idx1, idx2 + 1);
3048 0 : selectSimilarInRange(e);
3049 0 : if (selectedElement->track() == e->track()) {
3050 : // limit to this voice only
3051 0 : const std::vector<EngravingItem*>& list = m_selection.elements();
3052 0 : for (EngravingItem* el : list) {
3053 0 : if (el->track() != e->track()) {
3054 0 : m_selection.remove(el);
3055 : }
3056 : }
3057 : }
3058 :
3059 0 : return true;
3060 : }
3061 :
3062 65 : bool Score::tryExtendSingleSelectionToRange(EngravingItem* newElement, staff_idx_t staffIdx)
3063 : {
3064 65 : EngravingItem* selectedElement = m_selection.element();
3065 65 : if (!selectedElement) {
3066 0 : return false;
3067 : }
3068 :
3069 65 : Segment* startSegment = nullptr;
3070 65 : Segment* endSegment = nullptr;
3071 :
3072 65 : Box* selectedBox = selectedElement->isBox() ? toBox(selectedElement) : nullptr;
3073 65 : if (selectedBox) {
3074 19 : if (selectedBox->tick() == endTick()) {
3075 0 : startSegment = lastSegmentMM();
3076 : } else {
3077 19 : startSegment = tick2segmentMM(selectedBox->tick(), true, SegmentType::Duration);
3078 19 : endSegment = startSegment;
3079 : }
3080 : } else {
3081 46 : startSegment = findElementStartSegment(this, selectedElement);
3082 46 : endSegment = findElementEndSegment(this, selectedElement, startSegment);
3083 : }
3084 65 : if (!startSegment) { //! NOTE: endSegment is allowed to be null (meaning "extend to end of score")...
3085 0 : return false;
3086 : }
3087 :
3088 65 : Box* newBox = newElement->isBox() ? toBox(newElement) : nullptr;
3089 :
3090 65 : staff_idx_t startStaffIdx = muse::nidx;
3091 65 : staff_idx_t endStaffIdx = muse::nidx;
3092 :
3093 65 : if (selectedBox) {
3094 19 : const std::pair<staff_idx_t, staff_idx_t> boxStaves = boxStartEndStaves(selectedBox, nstaves());
3095 19 : startStaffIdx = boxStaves.first;
3096 19 : endStaffIdx = boxStaves.second;
3097 : } else {
3098 46 : startStaffIdx = selectedElement->staffIdx();
3099 46 : endStaffIdx = startStaffIdx + 1;
3100 : }
3101 :
3102 65 : if (startStaffIdx == muse::nidx) {
3103 0 : return false;
3104 : }
3105 :
3106 65 : track_idx_t activeTrack = newElement->track();
3107 65 : bool activeSegmentIsStart = false;
3108 65 : bool extendedBackwards = false;
3109 65 : bool extendedForwards = false;
3110 :
3111 : // TODO: Quite a lot of nesting - should clean this up...
3112 65 : if (newElement->isMeasure()) {
3113 16 : Measure* m = toMeasure(newElement)->coveringMMRestOrThis();
3114 16 : const Fraction tick = m->tick();
3115 :
3116 16 : if (tick < startSegment->tick()) {
3117 0 : startSegment = m->first(SegmentType::ChordRest);
3118 0 : activeSegmentIsStart = true;
3119 0 : extendedBackwards = true;
3120 : }
3121 16 : if (m == lastMeasureMM()) {
3122 16 : if (endSegment) {
3123 16 : extendedForwards = true;
3124 : }
3125 16 : endSegment = nullptr;
3126 0 : } else if (endSegment && tick + m->ticks() > endSegment->tick()) {
3127 0 : endSegment = m->last();
3128 0 : extendedForwards = true;
3129 : }
3130 :
3131 16 : startStaffIdx = std::min(startStaffIdx, staffIdx);
3132 16 : endStaffIdx = std::max(endStaffIdx, staffIdx + 1);
3133 :
3134 16 : activeTrack = staffIdx * VOICES;
3135 : } else {
3136 49 : Segment* newStartSegment = nullptr;
3137 49 : Segment* newEndSegment = nullptr;
3138 49 : if (newBox) {
3139 3 : if (newBox->tick() == endTick()) {
3140 2 : newStartSegment = lastSegmentMM();
3141 : } else {
3142 1 : newStartSegment = tick2segmentMM(newBox->tick(), true, SegmentType::Duration);
3143 1 : newEndSegment = newStartSegment;
3144 : }
3145 : } else {
3146 46 : newStartSegment = findElementStartSegment(this, newElement);
3147 46 : newEndSegment = findElementEndSegment(this, newElement, newStartSegment);
3148 : }
3149 :
3150 98 : if (newStartSegment && (newStartSegment->tick() != startSegment->tick()
3151 98 : ? newStartSegment->tick() < startSegment->tick()
3152 0 : : newBox && (!selectedBox || newBox->isBefore(selectedBox)))) {
3153 0 : startSegment = newStartSegment;
3154 0 : activeSegmentIsStart = true;
3155 0 : extendedBackwards = true;
3156 : }
3157 :
3158 49 : const Fraction newEndTick = newEndSegment ? newEndSegment->tick() : endTick();
3159 49 : const Fraction rangeEndTick = endSegment ? endSegment->tick() : endTick();
3160 50 : if (newEndTick != rangeEndTick ? newEndTick > rangeEndTick
3161 1 : : newBox && (!selectedBox || selectedBox->isBefore(newBox))) {
3162 48 : endSegment = newEndSegment;
3163 48 : extendedForwards = true;
3164 : }
3165 :
3166 49 : staff_idx_t newStaffIdx = newElement->staffIdx();
3167 49 : if (newBox) {
3168 3 : const std::pair<staff_idx_t, staff_idx_t> boxStaves = boxStartEndStaves(newBox, nstaves());
3169 3 : if (boxStaves.first != muse::nidx) {
3170 3 : startStaffIdx = std::min(startStaffIdx, boxStaves.first);
3171 3 : endStaffIdx = std::max(endStaffIdx, boxStaves.second);
3172 : }
3173 3 : activeTrack = staff2track(startStaffIdx);
3174 46 : } else if (newStaffIdx != muse::nidx) {
3175 46 : startStaffIdx = std::min(startStaffIdx, newStaffIdx);
3176 46 : endStaffIdx = std::max(endStaffIdx, newStaffIdx + 1);
3177 : }
3178 : }
3179 :
3180 65 : Box* startBox = extendedBackwards ? newBox : selectedBox;
3181 65 : Box* endBox = extendedForwards ? newBox : selectedBox;
3182 :
3183 65 : m_selection.setRange(startSegment, endSegment, startStaffIdx, endStaffIdx, startBox, endBox);
3184 65 : m_selection.updateSelectedElements();
3185 :
3186 65 : m_selection.setActiveTrack(activeTrack);
3187 65 : m_selection.setActiveSegment(activeSegmentIsStart ? startSegment : endSegment);
3188 :
3189 65 : return true;
3190 : }
3191 :
3192 115 : bool Score::tryExtendRangeSelectionToElem(EngravingItem* e)
3193 : {
3194 115 : Box* box = e->isBox() ? toBox(e) : nullptr;
3195 :
3196 115 : Segment* elemStartSeg = nullptr;
3197 115 : Segment* elemEndSeg = nullptr;
3198 115 : if (box) {
3199 0 : if (box->tick() == endTick()) {
3200 0 : elemStartSeg = lastSegmentMM();
3201 : } else {
3202 0 : elemStartSeg = tick2segmentMM(box->tick(), true, SegmentType::Duration);
3203 0 : elemEndSeg = elemStartSeg;
3204 : }
3205 : } else {
3206 115 : elemStartSeg = findElementStartSegment(this, e);
3207 115 : elemEndSeg = findElementEndSegment(this, e, m_selection.endSegment());
3208 : }
3209 :
3210 115 : if (!elemStartSeg) { //! NOTE: elemEndSeg allowed to be null (meaning "extend to end of score")...
3211 0 : return false;
3212 : }
3213 :
3214 115 : staff_idx_t elementStaffIdx = muse::nidx;
3215 :
3216 115 : if (box) {
3217 0 : const std::pair<staff_idx_t, staff_idx_t> boxStaves = boxStartEndStaves(box, nstaves());
3218 0 : if (boxStaves.first == muse::nidx) {
3219 0 : return false;
3220 : }
3221 0 : m_selection.setStaffStart(static_cast<int>(std::min(m_selection.staffStart(), boxStaves.first)));
3222 0 : m_selection.setStaffEnd(static_cast<int>(std::max(m_selection.staffEnd(), boxStaves.second)));
3223 0 : elementStaffIdx = boxStaves.first;
3224 : } else {
3225 115 : elementStaffIdx = e->staffIdx();
3226 : }
3227 :
3228 115 : if (elementStaffIdx == muse::nidx) {
3229 0 : return false;
3230 : }
3231 :
3232 115 : const Fraction elemStartTick = elemStartSeg->tick();
3233 115 : const Fraction elemEndTick = elemEndSeg ? elemEndSeg->tick() : endTick();
3234 :
3235 115 : m_selection.extendRangeSelection(elemStartSeg, elemEndSeg,
3236 : elementStaffIdx,
3237 : elemStartTick, elemEndTick,
3238 : box);
3239 :
3240 115 : m_selection.updateSelectedElements();
3241 115 : m_selection.setActiveTrack(box ? staff2track(m_selection.staffStart()) : e->track());
3242 115 : return true;
3243 : }
3244 :
3245 : //---------------------------------------------------------
3246 : // collectMatch
3247 : //---------------------------------------------------------
3248 :
3249 3368 : void Score::collectMatch(ElementPattern* p, EngravingItem* e)
3250 : {
3251 3368 : if (p->type != int(ElementType::INVALID) && p->type != int(e->type())) {
3252 2740 : return;
3253 : }
3254 :
3255 628 : if (p->type == int(ElementType::NOTE)) {
3256 0 : if (p->subtype < 0) {
3257 0 : if (!(toNote(e)->chord()->isGrace())) {
3258 0 : return;
3259 : }
3260 0 : } else if ((toNote(e)->chord()->isGrace()) || (p->subtype != e->subtype())) {
3261 0 : return;
3262 : }
3263 628 : } else if (p->subtypeValid && p->subtype != e->subtype()) {
3264 0 : return;
3265 : }
3266 :
3267 1256 : if ((p->staffStart != muse::nidx)
3268 628 : && ((p->staffStart > e->staffIdx()) || (p->staffEnd <= e->staffIdx()))) {
3269 4 : return;
3270 : }
3271 :
3272 624 : if (p->voice != muse::nidx && p->voice != e->voice()) {
3273 0 : return;
3274 : }
3275 :
3276 624 : if (p->system) {
3277 0 : EngravingItem* ee = e;
3278 : do {
3279 0 : if (ee->isSystem()) {
3280 0 : if (p->system != ee) {
3281 0 : return;
3282 : }
3283 0 : break;
3284 : }
3285 0 : ee = ee->layoutParent();
3286 0 : } while (ee);
3287 : }
3288 :
3289 624 : if (e->isRest() && p->durationTicks != Fraction(-1, 1)) {
3290 0 : const Rest* r = toRest(e);
3291 0 : if (p->durationTicks != r->actualTicks()) {
3292 0 : return;
3293 : }
3294 : }
3295 :
3296 624 : if (p->measure) {
3297 0 : auto eMeasure = e->findMeasure();
3298 0 : if (!eMeasure && e->isSpannerSegment()) {
3299 0 : if (auto s = toSpannerSegment(e)->spanner()) {
3300 0 : if (auto se = s->startElement()) {
3301 0 : if (auto mse = se->findMeasure()) {
3302 0 : eMeasure = mse;
3303 : }
3304 : }
3305 : }
3306 : }
3307 0 : if (p->measure != eMeasure) {
3308 0 : return;
3309 : }
3310 : }
3311 :
3312 624 : if ((p->beat.isValid()) && (p->beat != e->beat())) {
3313 0 : return;
3314 : }
3315 :
3316 624 : p->el.push_back(e);
3317 : }
3318 :
3319 : //---------------------------------------------------------
3320 : // collectNoteMatch
3321 : //---------------------------------------------------------
3322 :
3323 0 : void Score::collectNoteMatch(NotePattern* p, EngravingItem* e)
3324 : {
3325 0 : if (!e->isNote()) {
3326 0 : return;
3327 : }
3328 0 : Note* n = toNote(e);
3329 0 : if (p->type != NoteType::INVALID && p->type != n->noteType()) {
3330 0 : return;
3331 : }
3332 0 : if (p->pitch != -1 && p->pitch != n->pitch()) {
3333 0 : return;
3334 : }
3335 0 : if (p->string != INVALID_STRING_INDEX && p->string != n->string()) {
3336 0 : return;
3337 : }
3338 0 : if (p->tpc != Tpc::TPC_INVALID && p->tpc != n->tpc()) {
3339 0 : return;
3340 : }
3341 0 : if (p->notehead != NoteHeadGroup::HEAD_INVALID && p->notehead != n->headGroup()) {
3342 0 : return;
3343 : }
3344 0 : if (p->durationType.type() != DurationType::V_INVALID && p->durationType != n->chord()->actualDurationType()) {
3345 0 : return;
3346 : }
3347 0 : if (p->durationTicks != Fraction(-1, 1) && p->durationTicks != n->chord()->actualTicks()) {
3348 0 : return;
3349 : }
3350 0 : if ((p->staffStart != muse::nidx)
3351 0 : && ((p->staffStart > e->staffIdx()) || (p->staffEnd <= e->staffIdx()))) {
3352 0 : return;
3353 : }
3354 0 : if (p->voice != muse::nidx && p->voice != e->voice()) {
3355 0 : return;
3356 : }
3357 0 : if (p->system && (p->system != n->chord()->segment()->system())) {
3358 0 : return;
3359 : }
3360 0 : if (p->measure && (p->measure != n->findMeasure())) {
3361 0 : return;
3362 : }
3363 0 : if ((p->beat.isValid()) && (p->beat != n->beat())) {
3364 0 : return;
3365 : }
3366 0 : p->el.push_back(n);
3367 : }
3368 :
3369 : //---------------------------------------------------------
3370 : // selectSimilar
3371 : //---------------------------------------------------------
3372 :
3373 21 : void Score::selectSimilar(EngravingItem* e, bool sameStaff)
3374 : {
3375 21 : Score* score = e->score();
3376 :
3377 21 : ElementPattern pattern;
3378 21 : pattern.type = int(e->type());
3379 21 : pattern.subtype = 0;
3380 21 : pattern.subtypeValid = false;
3381 21 : if (e->isNote()) {
3382 0 : if (toNote(e)->chord()->isGrace()) {
3383 0 : pattern.subtype = -1; // hack
3384 : } else {
3385 0 : pattern.subtype = e->subtype();
3386 : }
3387 21 : } else if (e->isHairpinSegment() || e->isHarmony()) {
3388 14 : pattern.subtype = e->subtype();
3389 14 : pattern.subtypeValid = true;
3390 : }
3391 21 : pattern.staffStart = sameStaff ? e->staffIdx() : muse::nidx;
3392 21 : pattern.staffEnd = sameStaff ? e->staffIdx() + 1 : muse::nidx;
3393 21 : pattern.voice = muse::nidx;
3394 :
3395 3301 : score->scanElements([&](EngravingItem* item) { collectMatch(&pattern, item); });
3396 :
3397 21 : score->select(0, SelectType::SINGLE, 0);
3398 21 : score->select(pattern.el, SelectType::ADD, 0);
3399 21 : }
3400 :
3401 : //---------------------------------------------------------
3402 : // selectSimilarInRange
3403 : //---------------------------------------------------------
3404 :
3405 2 : void Score::selectSimilarInRange(EngravingItem* e)
3406 : {
3407 2 : Score* score = e->score();
3408 :
3409 2 : ElementPattern pattern;
3410 2 : pattern.type = int(e->type());
3411 2 : pattern.subtype = 0;
3412 2 : pattern.subtypeValid = false;
3413 2 : if (e->isNote()) {
3414 0 : if (toNote(e)->chord()->isGrace()) {
3415 0 : pattern.subtype = -1; //hack
3416 : } else {
3417 0 : pattern.subtype = e->subtype();
3418 : }
3419 0 : pattern.subtypeValid = true;
3420 2 : } else if (e->isHairpinSegment() || e->isHarmony()) {
3421 0 : pattern.subtype = e->subtype();
3422 0 : pattern.subtypeValid = true;
3423 : }
3424 2 : pattern.staffStart = selection().staffStart();
3425 2 : pattern.staffEnd = selection().staffEnd();
3426 2 : pattern.voice = muse::nidx;
3427 :
3428 90 : score->scanElementsInRange([&](EngravingItem* item) { collectMatch(&pattern, item); });
3429 :
3430 2 : score->select(0, SelectType::SINGLE, 0);
3431 2 : score->select(pattern.el, SelectType::ADD, 0);
3432 2 : }
3433 :
3434 : //---------------------------------------------------------
3435 : // scoreOrder
3436 : //---------------------------------------------------------
3437 :
3438 26 : ScoreOrder Score::scoreOrder() const
3439 : {
3440 26 : ScoreOrder order = m_scoreOrder;
3441 26 : order.customized = !order.isScoreOrder(this);
3442 26 : return order;
3443 0 : }
3444 :
3445 : //---------------------------------------------------------
3446 : // setScoreOrder
3447 : //---------------------------------------------------------
3448 :
3449 277 : void Score::setScoreOrder(ScoreOrder order)
3450 : {
3451 277 : m_scoreOrder = order;
3452 277 : }
3453 :
3454 : //---------------------------------------------------------
3455 : // updateBracesAndBarlines
3456 : //---------------------------------------------------------
3457 :
3458 4 : void Score::updateBracesAndBarlines(Part* part, size_t newIndex)
3459 : {
3460 4 : bool noBracesFound = true;
3461 12 : for (size_t indexInPart = 0; indexInPart < part->nstaves(); ++indexInPart) {
3462 8 : size_t indexInScore = part->staves()[indexInPart]->idx();
3463 8 : const std::vector<BracketItem*>& staffBrackets = brackets(indexInScore);
3464 8 : for (BracketItem* bi : staffBrackets) {
3465 0 : noBracesFound = false;
3466 0 : if (bi->bracketType() == BracketType::BRACE) {
3467 0 : if ((indexInPart <= newIndex) && (newIndex <= (bi->bracketSpan()))) {
3468 0 : bi->undoChangeProperty(Pid::BRACKET_SPAN, bi->bracketSpan() + 1);
3469 : }
3470 : }
3471 : }
3472 : }
3473 :
3474 0 : auto updateBracketSpan = [this, part](size_t modIndex, size_t refIndex) {
3475 0 : Staff* modStaff = staff(part->staves()[modIndex]->idx());
3476 0 : Staff* refStaff = staff(part->staves()[refIndex]->idx());
3477 0 : modStaff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, refStaff->getProperty(Pid::STAFF_BARLINE_SPAN));
3478 0 : };
3479 :
3480 4 : if (newIndex >= 2) {
3481 0 : updateBracketSpan(newIndex - 1, newIndex - 2);
3482 : }
3483 :
3484 4 : if (part->nstaves() == 2) {
3485 4 : const InstrumentTemplate* tp = searchTemplate(part->instrumentId());
3486 4 : if (tp) {
3487 0 : const bool firstStaffBarLineSpan = tp->barlineSpan[0];
3488 0 : if (firstStaffBarLineSpan) {
3489 0 : staff(part->staff(0)->idx())->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, firstStaffBarLineSpan);
3490 : }
3491 0 : if (noBracesFound && (tp->bracket[0] != BracketType::NO_BRACKET)) {
3492 0 : EditStaffBrackets::undoAddBracket(this, part->staves()[0], 0, tp->bracket[0], part->nstaves());
3493 : }
3494 : }
3495 : } else {
3496 0 : updateBracketSpan(newIndex, newIndex - 1);
3497 : }
3498 4 : }
3499 :
3500 : //---------------------------------------------------------
3501 : // setBracketsAndBarlines
3502 : //---------------------------------------------------------
3503 :
3504 19 : void Score::setBracketsAndBarlines()
3505 : {
3506 19 : scoreOrder().setBracketsAndBarlines(this);
3507 19 : }
3508 :
3509 : //---------------------------------------------------------
3510 : // remapBracketsAndBarlines
3511 : /// Reconstructs brackets and barlines based on how they
3512 : /// are in the masterScore. Must be called after creating
3513 : /// an excerpt or adding new parts to it.
3514 : //---------------------------------------------------------
3515 :
3516 56 : void Score::remapBracketsAndBarlines()
3517 : {
3518 56 : if (isMaster()) {
3519 0 : return;
3520 : }
3521 :
3522 : // Remove all brackets
3523 114 : for (Staff* staff : staves()) {
3524 58 : std::vector<BracketItem*>& staffBrackets = brackets(staff->idx());
3525 58 : for (BracketItem* bracket : staffBrackets) {
3526 0 : bracket->setBracketType(BracketType::NO_BRACKET);
3527 : }
3528 : }
3529 : // Remap all brackets from masterScore
3530 56 : Score* master = masterScore();
3531 154 : for (staff_idx_t masterStaffIdx = 0; masterStaffIdx < master->nstaves(); ++masterStaffIdx) {
3532 98 : Staff* masterStaff = master->staff(masterStaffIdx);
3533 98 : std::vector<BracketItem*>& staffBrackets = master->brackets(masterStaffIdx);
3534 :
3535 107 : for (size_t bracketIdx = 0; bracketIdx < staffBrackets.size(); ++bracketIdx) {
3536 9 : BracketItem* bracket = staffBrackets.at(bracketIdx);
3537 9 : Staff* firstBracketed = nullptr;
3538 9 : int span = 0;
3539 :
3540 : // There is no guarantee that bracket->bracketSpan() is correctly bounded,
3541 : // so masterStaffIdx + bracket->bracketSpan() might be > master->nstaves(),
3542 : // in which case masterStaff below will become null.
3543 9 : size_t endSpannedMasterStaffIdx = std::min(masterStaffIdx + bracket->bracketSpan(), master->nstaves());
3544 :
3545 23 : for (staff_idx_t spannedMasterStIdx = masterStaffIdx; spannedMasterStIdx < endSpannedMasterStaffIdx; ++spannedMasterStIdx) {
3546 14 : masterStaff = master->staff(spannedMasterStIdx);
3547 14 : IF_ASSERT_FAILED(masterStaff) {
3548 0 : continue;
3549 : }
3550 :
3551 14 : Staff* linkedStaff = toStaff(masterStaff->findLinkedInScore(this));
3552 14 : if (!linkedStaff) {
3553 8 : continue;
3554 : }
3555 :
3556 6 : if (!firstBracketed) {
3557 5 : firstBracketed = linkedStaff;
3558 : }
3559 :
3560 6 : ++span;
3561 : }
3562 :
3563 9 : if (firstBracketed && span > 1) {
3564 1 : EditStaffBrackets::setBracketType(this, firstBracketed->idx(), bracketIdx, bracket->bracketType());
3565 1 : EditStaffBrackets::setBracketSpan(this, firstBracketed->idx(), bracketIdx, span);
3566 : }
3567 : }
3568 : }
3569 :
3570 : // Remap all barline spans from masterScore
3571 114 : for (Staff* staff : staves()) {
3572 58 : Staff* masterStaff = toStaff(staff->findLinkedInScore(masterScore()));
3573 58 : if (!masterStaff || !masterStaff->barLineSpan()) {
3574 55 : continue;
3575 : }
3576 : // Look in the masterScore for all the staves spanned by a common barline.
3577 : // If at least one of them is also in this score, then connect it through.
3578 3 : bool extendBarline = false;
3579 3 : bool span = masterStaff->barLineSpan();
3580 5 : while (!extendBarline && span && masterStaff->idx() + 1 < master->nstaves()) {
3581 3 : masterStaff = masterScore()->staff(masterStaff->idx() + 1);
3582 3 : span = masterStaff->barLineSpan();
3583 3 : if (masterStaff->findLinkedInScore(this)) {
3584 1 : extendBarline = true;
3585 1 : break;
3586 : }
3587 : }
3588 3 : if (extendBarline) {
3589 1 : staff->setBarLineSpan(true);
3590 : }
3591 : }
3592 : }
3593 :
3594 : //---------------------------------------------------------
3595 : // lassoSelect
3596 : //---------------------------------------------------------
3597 :
3598 0 : void Score::lassoSelect(const RectF& bbox)
3599 : {
3600 0 : select(0, SelectType::SINGLE, 0);
3601 0 : RectF fr(bbox.normalized());
3602 0 : for (Page* page : pages()) {
3603 0 : RectF pr(page->ldata()->bbox());
3604 0 : RectF frr(fr.translated(-page->pos()));
3605 0 : if (pr.right() < frr.left()) {
3606 0 : continue;
3607 : }
3608 0 : if (pr.left() > frr.right()) {
3609 0 : break;
3610 : }
3611 :
3612 0 : std::vector<EngravingItem*> items = page->items(frr);
3613 0 : std::vector<EngravingItem*> itemsToSelect;
3614 :
3615 0 : for (EngravingItem* item : items) {
3616 0 : if (frr.contains(item->pageBoundingRect())) {
3617 0 : if (!item->isMeasure() && item->selectable()) {
3618 0 : itemsToSelect.push_back(item);
3619 : }
3620 : }
3621 : }
3622 :
3623 0 : select(itemsToSelect, SelectType::ADD, 0);
3624 0 : }
3625 0 : }
3626 :
3627 : //---------------------------------------------------------
3628 : // lassoSelectEnd
3629 : //---------------------------------------------------------
3630 :
3631 0 : void Score::lassoSelectEnd()
3632 : {
3633 0 : int noteRestCount = 0;
3634 0 : Segment* startSegment = 0;
3635 0 : Segment* endSegment = 0;
3636 0 : staff_idx_t startStaff = 0x7fffffff;
3637 0 : staff_idx_t endStaff = 0;
3638 0 : const ChordRest* endCR = 0;
3639 :
3640 0 : if (m_selection.elements().empty()) {
3641 0 : m_selection.setState(SelState::NONE);
3642 0 : setUpdateAll();
3643 0 : return;
3644 : }
3645 0 : m_selection.setState(SelState::LIST);
3646 :
3647 0 : for (const EngravingItem* e : m_selection.elements()) {
3648 0 : if (!e->isNote() && !e->isRest()) {
3649 0 : continue;
3650 : }
3651 0 : ++noteRestCount;
3652 0 : if (e->isNote()) {
3653 0 : e = toNote(e)->chord();
3654 : }
3655 0 : Segment* seg = toChordRest(e)->segment();
3656 0 : if ((startSegment == 0) || (*seg < *startSegment)) {
3657 0 : startSegment = seg;
3658 : }
3659 0 : if ((endSegment == 0) || (*seg > *endSegment)) {
3660 0 : endSegment = seg;
3661 0 : endCR = toChordRest(e);
3662 : }
3663 0 : staff_idx_t idx = e->staffIdx();
3664 0 : if (idx < startStaff) {
3665 0 : startStaff = idx;
3666 : }
3667 0 : if (idx > endStaff) {
3668 0 : endStaff = idx;
3669 : }
3670 : }
3671 0 : if (noteRestCount > 0) {
3672 0 : endSegment = endCR->nextSegmentAfterCR(SegmentType::ChordRest
3673 : | SegmentType::EndBarLine
3674 : | SegmentType::Clef);
3675 :
3676 0 : m_selection.setRange(startSegment, endSegment, startStaff, endStaff + 1);
3677 :
3678 0 : if (!m_selection.isRange()) {
3679 0 : m_selection.setState(SelState::RANGE);
3680 : }
3681 0 : m_selection.updateSelectedElements();
3682 : }
3683 0 : setUpdateAll();
3684 : }
3685 :
3686 : //---------------------------------------------------------
3687 : // addLyrics
3688 : //---------------------------------------------------------
3689 :
3690 12 : void Score::addLyrics(const Fraction& tick, staff_idx_t staffIdx, const String& txt)
3691 : {
3692 12 : if (txt.trimmed().isEmpty()) {
3693 0 : return;
3694 : }
3695 12 : Measure* measure = tick2measure(tick);
3696 12 : Segment* seg = measure->findSegment(SegmentType::ChordRest, tick);
3697 12 : if (seg == 0) {
3698 0 : LOGD("no segment found for lyrics<%s> at tick %d",
3699 0 : muPrintable(txt), tick.ticks());
3700 0 : return;
3701 : }
3702 :
3703 12 : bool lyricsAdded = false;
3704 14 : for (voice_idx_t voice = 0; voice < VOICES; ++voice) {
3705 14 : track_idx_t track = staffIdx * VOICES + voice;
3706 14 : ChordRest* cr = toChordRest(seg->element(track));
3707 14 : if (cr) {
3708 12 : Lyrics* l = Factory::createLyrics(cr);
3709 12 : l->setXmlText(txt);
3710 12 : l->setTrack(track);
3711 12 : cr->add(l);
3712 12 : lyricsAdded = true;
3713 12 : break;
3714 : }
3715 : }
3716 12 : if (!lyricsAdded) {
3717 0 : LOGD("no chord/rest for lyrics<%s> at tick %d, staff %zu",
3718 0 : muPrintable(txt), tick.ticks(), staffIdx);
3719 : }
3720 : }
3721 :
3722 0 : BeatsPerSecond Score::tempo(const Fraction& tick) const
3723 : {
3724 0 : return tempoTimeline(/*expandRepeats*/ false).tempo(tick.ticks());
3725 : }
3726 :
3727 51 : BeatsPerSecond Score::multipliedTempo(const Fraction& tick) const
3728 : {
3729 51 : const TempoTimeline& timeline = tempoTimeline(/*expandRepeats*/ false);
3730 51 : return timeline.tempo(tick.ticks()) * timeline.tempoMultiplier();
3731 : }
3732 :
3733 1901 : BeatsPerSecond Score::multipliedTempoAtUtick(int utick) const
3734 : {
3735 : // utick is only truly expanded when repeats are expanded; otherwise it's a plain tick
3736 1901 : const TempoTimeline& timeline = tempoTimeline();
3737 1901 : return timeline.tempo(utick) * timeline.tempoMultiplier();
3738 : }
3739 :
3740 : //---------------------------------------------------------
3741 : // cmdSelectAll
3742 : //---------------------------------------------------------
3743 :
3744 64 : void Score::cmdSelectAll()
3745 : {
3746 65 : TRACEFUNC;
3747 :
3748 64 : if (m_measures.size() == 0) {
3749 0 : return;
3750 : }
3751 64 : deselectAll();
3752 64 : selectRange(firstMM(), 0);
3753 64 : selectRange(!last()->isMeasure() ? last() : lastMeasureMM(), nstaves() - 1);
3754 64 : setUpdateAll();
3755 64 : update();
3756 64 : }
3757 :
3758 : //---------------------------------------------------------
3759 : // cmdSelectSection
3760 : //---------------------------------------------------------
3761 :
3762 0 : void Score::cmdSelectSection()
3763 : {
3764 0 : TRACEFUNC;
3765 :
3766 0 : Segment* s = m_selection.startSegment();
3767 0 : if (s == 0) {
3768 0 : return;
3769 : }
3770 0 : MeasureBase* sm = s->measure();
3771 0 : MeasureBase* em = sm;
3772 0 : while (sm->prev()) {
3773 0 : if (sm->prev()->sectionBreak()) {
3774 0 : break;
3775 : }
3776 0 : sm = sm->prev();
3777 : }
3778 0 : while (em->next()) {
3779 0 : if (em->sectionBreak()) {
3780 0 : break;
3781 : }
3782 0 : em = em->next();
3783 : }
3784 0 : while (sm && !sm->isMeasure()) {
3785 0 : sm = sm->next();
3786 : }
3787 0 : while (em && !em->isMeasure()) {
3788 0 : em = em->next();
3789 : }
3790 0 : if (sm == 0 || em == 0) {
3791 0 : return;
3792 : }
3793 :
3794 0 : m_selection.setRange(toMeasure(sm)->first(), toMeasure(em)->last(), 0, nstaves());
3795 :
3796 0 : setUpdateAll();
3797 0 : update();
3798 0 : }
3799 :
3800 : //---------------------------------------------------------
3801 : // scoreList
3802 : // return a list of scores containing the root score
3803 : // and all part scores (if there are any)
3804 : //---------------------------------------------------------
3805 :
3806 10062 : std::list<Score*> Score::scoreList()
3807 : {
3808 10062 : std::list<Score*> scores;
3809 10062 : MasterScore* root = masterScore();
3810 10062 : scores.push_back(root);
3811 11435 : for (const Excerpt* ex : root->excerpts()) {
3812 1373 : if (ex->excerptScore()) {
3813 1373 : scores.push_back(ex->excerptScore());
3814 : }
3815 : }
3816 10062 : return scores;
3817 0 : }
3818 :
3819 : //---------------------------------------------------------
3820 : // appendPart
3821 : //---------------------------------------------------------
3822 :
3823 0 : void Score::appendPart(const InstrumentTemplate* t)
3824 : {
3825 0 : Part* part = new Part(this);
3826 0 : part->initFromInstrTemplate(t);
3827 0 : for (staff_idx_t i = 0; i < t->staffCount; ++i) {
3828 0 : Staff* staff = Factory::createStaff(part);
3829 0 : StaffType* stt = staff->staffType(Fraction(0, 1));
3830 0 : undoInsertStaff(staff, i);
3831 0 : staff->init(t, stt, int(i));
3832 : }
3833 0 : undoInsertPart(part, m_parts.size());
3834 0 : masterScore()->rebuildMidiMapping();
3835 0 : }
3836 :
3837 : //---------------------------------------------------------
3838 : // appendMeasures
3839 : //---------------------------------------------------------
3840 :
3841 6 : void Score::appendMeasures(int n)
3842 : {
3843 6 : InsertMeasureOptions options;
3844 6 : options.createMeasureRests = false;
3845 :
3846 51 : for (int i = 0; i < n; ++i) {
3847 45 : insertMeasure(ElementType::MEASURE, nullptr, options);
3848 : }
3849 6 : }
3850 :
3851 : //---------------------------------------------------------
3852 : // addSpanner
3853 : //---------------------------------------------------------
3854 :
3855 1513 : void Score::addSpanner(Spanner* s, bool computeStartEnd)
3856 : {
3857 1513 : m_spanner.addSpanner(s);
3858 1513 : s->added();
3859 1513 : if (computeStartEnd) {
3860 971 : s->computeStartElement();
3861 971 : s->computeEndElement();
3862 : }
3863 1513 : }
3864 :
3865 : //---------------------------------------------------------
3866 : // spannerList
3867 : //---------------------------------------------------------
3868 :
3869 2 : std::vector<Spanner*> Score::spannerList() const
3870 : {
3871 2 : std::vector<Spanner*> result;
3872 2 : const std::multimap<int, Spanner*>& spannerMap = m_spanner.map();
3873 2 : result.reserve(spannerMap.size());
3874 2 : for (auto it = spannerMap.begin(); it != spannerMap.end(); ++it) {
3875 0 : result.push_back(it->second);
3876 : }
3877 2 : return result;
3878 0 : }
3879 :
3880 : //---------------------------------------------------------
3881 : // removeSpanner
3882 : //---------------------------------------------------------
3883 :
3884 63 : void Score::removeSpanner(Spanner* s)
3885 : {
3886 63 : m_spanner.removeSpanner(s);
3887 63 : s->removed();
3888 :
3889 63 : EngravingItem* startElement = s->startElement();
3890 63 : Chord* startChord = startElement && startElement->isChord() ? toChord(startElement) : nullptr;
3891 63 : if (startChord) {
3892 53 : startChord->removeStartingSpanner(s);
3893 : }
3894 :
3895 63 : EngravingItem* endElement = s->endElement();
3896 63 : Chord* endChord = endElement && endElement->isChord() ? toChord(endElement) : nullptr;
3897 63 : if (endChord) {
3898 59 : endChord->removeEndingSpanner(s);
3899 : }
3900 63 : }
3901 :
3902 : //---------------------------------------------------------
3903 : // isSpannerStartEnd
3904 : // does is spanner start or end at tick position tick
3905 : // for track ?
3906 : //---------------------------------------------------------
3907 :
3908 24 : bool Score::isSpannerStartEnd(const Fraction& tick, track_idx_t track) const
3909 : {
3910 29 : for (auto i : m_spanner.map()) {
3911 5 : if (i.second->track() != track) {
3912 0 : continue;
3913 : }
3914 5 : if (i.second->tick() == tick || i.second->tick2() == tick) {
3915 0 : return true;
3916 : }
3917 : }
3918 24 : return false;
3919 : }
3920 :
3921 475 : void Score::insertTime(const Fraction& tick, const Fraction& len, const std::vector<RepeatSegmentInfo>& oldSegments)
3922 : {
3923 1043 : for (Staff* staff : staves()) {
3924 568 : staff->insertTime(tick, len);
3925 : }
3926 1017 : for (Part* part : parts()) {
3927 542 : part->insertTime(tick, len);
3928 : }
3929 :
3930 475 : onTimeInserted(tick, len, oldSegments);
3931 475 : }
3932 :
3933 24 : void Score::onTimeInserted(const Fraction&, const Fraction&, const std::vector<RepeatSegmentInfo>&)
3934 : {
3935 24 : }
3936 :
3937 : //---------------------------------------------------------
3938 : // addUnmanagedSpanner
3939 : //---------------------------------------------------------
3940 :
3941 243 : void Score::addUnmanagedSpanner(Spanner* s)
3942 : {
3943 243 : m_unmanagedSpanner.insert(s);
3944 243 : }
3945 :
3946 : //---------------------------------------------------------
3947 : // removeSpanner
3948 : //---------------------------------------------------------
3949 :
3950 235 : void Score::removeUnmanagedSpanner(Spanner* s)
3951 : {
3952 235 : m_unmanagedSpanner.erase(s);
3953 235 : }
3954 :
3955 : //---------------------------------------------------------
3956 : // uniqueStaves
3957 : //---------------------------------------------------------
3958 :
3959 108 : std::vector<staff_idx_t> Score::uniqueStaves() const
3960 : {
3961 108 : std::vector<staff_idx_t> sl;
3962 :
3963 228 : for (size_t staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
3964 120 : Staff* s = staff(staffIdx);
3965 120 : if (s->links()) {
3966 6 : bool alreadyInList = false;
3967 9 : for (staff_idx_t idx : sl) {
3968 3 : if (s->links()->contains(staff(idx))) {
3969 0 : alreadyInList = true;
3970 0 : break;
3971 : }
3972 : }
3973 6 : if (alreadyInList) {
3974 0 : continue;
3975 : }
3976 : }
3977 120 : sl.push_back(staffIdx);
3978 : }
3979 108 : return sl;
3980 0 : }
3981 :
3982 : //---------------------------------------------------------
3983 : // findCR
3984 : // find chord/rest <= tick in track
3985 : //---------------------------------------------------------
3986 :
3987 291 : ChordRest* Score::findCR(Fraction tick, track_idx_t track) const
3988 : {
3989 291 : Measure* m = tick2measureMM(tick);
3990 291 : if (!m) {
3991 : //LOGD("findCR: no measure for tick %d", tick.ticks());
3992 16 : return nullptr;
3993 : }
3994 : // attach to first rest all spanner when mmRest
3995 275 : if (m->isMMRest()) {
3996 1 : tick = m->tick();
3997 : }
3998 275 : Segment* s = m->first(SegmentType::ChordRest);
3999 1090 : for (Segment* ns = s;; ns = ns->next(SegmentType::ChordRest)) {
4000 1090 : if (!ns || ns->tick() > tick) {
4001 275 : break;
4002 : }
4003 815 : EngravingItem* el = ns->element(track);
4004 815 : if (el && el->isRest() && toRest(el)->isGap()) {
4005 3 : continue;
4006 812 : } else if (el) {
4007 691 : s = ns;
4008 : }
4009 815 : }
4010 :
4011 275 : if (!s) {
4012 0 : return nullptr;
4013 : }
4014 275 : EngravingItem* el = s->element(track);
4015 275 : if (el && el->isRest() && toRest(el)->isGap()) {
4016 1 : return nullptr;
4017 : }
4018 :
4019 274 : return toChordRest(el);
4020 : }
4021 :
4022 : //---------------------------------------------------------
4023 : // findChordRestEndingBeforeTickInStaff
4024 : // find last chord/rest on staff that ends before tick
4025 : //---------------------------------------------------------
4026 :
4027 483 : ChordRest* Score::findChordRestEndingBeforeTickInStaff(const Fraction& tick, staff_idx_t staffIdx) const
4028 : {
4029 483 : return findChordRestEndingBeforeTickInStaffAndVoice(tick, staffIdx, false, 0);
4030 : }
4031 :
4032 : //-----------------------------------------------------------------
4033 : // findChordRestEndingBeforeTickInStaffAndVoice
4034 : // find last chord/rest on staff and voice that ends before tick
4035 : //-----------------------------------------------------------------
4036 211 : ChordRest* Score::findChordRestEndingBeforeTickInStaffAndVoice(const Fraction& tick, staff_idx_t staffIdx, voice_idx_t voice) const
4037 : {
4038 211 : return findChordRestEndingBeforeTickInStaffAndVoice(tick, staffIdx, true, voice);
4039 : }
4040 :
4041 : //-----------------------------------------------------------------
4042 : // findChordRestEndingBeforeTickInStaffAndVoice
4043 : // find last chord/rest on staff and voice that ends before tick
4044 : // allow no specific voice
4045 : //-----------------------------------------------------------------
4046 694 : ChordRest* Score::findChordRestEndingBeforeTickInStaffAndVoice(const Fraction& tick, staff_idx_t staffIdx, bool forceVoice,
4047 : voice_idx_t voice) const
4048 : {
4049 694 : Fraction ptick = tick - Fraction::eps();
4050 694 : Measure* m = tick2measureMM(ptick);
4051 694 : if (!m) {
4052 33 : LOGD("findCRinStaff: no measure for tick %d", ptick.ticks());
4053 33 : return nullptr;
4054 : }
4055 : // attach to first rest all spanner when mmRest
4056 661 : if (m->isMMRest()) {
4057 0 : ptick = m->tick();
4058 : }
4059 :
4060 661 : Segment* s = m->first(SegmentType::ChordRest);
4061 661 : track_idx_t strack = forceVoice ? staffIdx * VOICES + voice : staffIdx * VOICES;
4062 661 : track_idx_t etrack = forceVoice ? strack + 1 : strack + VOICES;
4063 661 : track_idx_t actualTrack = strack;
4064 :
4065 661 : Fraction lastTick = Fraction(-1, 1);
4066 2161 : for (Segment* ns = s;; ns = ns->next(SegmentType::ChordRest)) {
4067 2161 : if (!ns || ns->tick() > ptick) {
4068 661 : break;
4069 : }
4070 : // found a segment; now find longest cr on this staff that does not overlap tick
4071 6537 : for (track_idx_t t = strack; t < etrack; ++t) {
4072 5037 : ChordRest* cr = toChordRest(ns->element(t));
4073 5037 : if ((cr) && ((!forceVoice) || voice == cr->voice())) {
4074 1455 : Fraction endTick = cr->endTick();
4075 1455 : if (endTick >= lastTick && endTick <= tick) {
4076 1435 : s = ns;
4077 1435 : actualTrack = t;
4078 1435 : lastTick = endTick;
4079 : }
4080 : }
4081 : }
4082 1500 : }
4083 661 : if (s) {
4084 661 : return toChordRest(s->element(actualTrack));
4085 : }
4086 0 : return nullptr;
4087 : }
4088 :
4089 104 : ChordRest* Score::findChordRestEndingBeforeTickInTrack(const Fraction& tick, track_idx_t trackIdx) const
4090 : {
4091 104 : Measure* m = tick2measureMM(tick - Fraction::eps());
4092 104 : if (!m) {
4093 0 : LOGD("findCRinStaff: no measure for tick %d", tick.ticks());
4094 0 : return nullptr;
4095 : }
4096 :
4097 283 : for (const Segment* s = m->last(SegmentType::ChordRest); s; s = s->prev(SegmentType::ChordRest)) {
4098 282 : if (ChordRest* cr = toChordRest(s->element(trackIdx))) {
4099 254 : if (cr->endTick() <= tick) {
4100 103 : return cr;
4101 : }
4102 : }
4103 : }
4104 :
4105 1 : return nullptr;
4106 : }
4107 :
4108 : //---------------------------------------------------------
4109 : // cmdNextPrevSystem
4110 : //---------------------------------------------------------
4111 :
4112 0 : ChordRest* Score::cmdNextPrevSystem(ChordRest* cr, bool next)
4113 : {
4114 0 : IF_ASSERT_FAILED(cr) {
4115 0 : return nullptr;
4116 : }
4117 :
4118 0 : auto newCR = cr;
4119 0 : auto currentMeasure = cr->measure();
4120 0 : auto currentSystem = currentMeasure->system() ? currentMeasure->system() : currentMeasure->coveringMMRestOrThis()->system();
4121 0 : if (!currentSystem) {
4122 0 : return cr;
4123 : }
4124 0 : auto destinationMeasure = currentSystem->firstMeasure();
4125 0 : if (!destinationMeasure) {
4126 0 : return cr;
4127 : }
4128 :
4129 0 : auto firstSegment = destinationMeasure->first(SegmentType::ChordRest);
4130 :
4131 : // Case: Go to next system
4132 0 : if (next) {
4133 0 : if ((destinationMeasure = currentSystem->lastMeasure()->nextMeasure())) {
4134 : // There is a next system present: get it and accommodate for MMRest
4135 0 : currentSystem = destinationMeasure->system()
4136 0 : ? destinationMeasure->system()
4137 0 : : destinationMeasure->coveringMMRestOrThis()->system();
4138 0 : if (!currentSystem) {
4139 0 : return cr;
4140 : }
4141 0 : if ((destinationMeasure = currentSystem->firstMeasure())) {
4142 0 : if ((newCR = destinationMeasure->first()->nextChordRest(trackZeroVoice(cr->track()), false))) {
4143 0 : cr = newCR;
4144 : }
4145 : }
4146 0 : } else if (currentMeasure != lastMeasure()) {
4147 : // There is no next system present: go to last measure of current system
4148 0 : if ((destinationMeasure = lastMeasure())) {
4149 0 : if ((newCR = destinationMeasure->first()->nextChordRest(trackZeroVoice(cr->track()), false))) {
4150 0 : if (!destinationMeasure->isMMRest()) {
4151 0 : cr = newCR;
4152 : }
4153 : // Last visual measure is a MMRest: go to very last measure within that MMRest
4154 0 : else if ((destinationMeasure = lastMeasureMM())
4155 0 : && (newCR = destinationMeasure->first()->nextChordRest(trackZeroVoice(cr->track()), false))) {
4156 0 : cr = newCR;
4157 : }
4158 : }
4159 : }
4160 : }
4161 : }
4162 : // Case: Go to previous system
4163 : else {
4164 0 : auto currentSegment = cr->segment();
4165 : // Only go to previous system's beginning if user is already at the absolute beginning of current system
4166 : // and not in first measure of entire score
4167 0 : if ((destinationMeasure != firstMeasure() && destinationMeasure != firstMeasureMM())
4168 0 : && (currentSegment == firstSegment || (currentMeasure->mmRest() && currentMeasure->mmRest()->isFirstInSystem()))) {
4169 0 : if (!(destinationMeasure = destinationMeasure->prevMeasureMM())) {
4170 0 : return cr;
4171 : }
4172 0 : if (!(currentSystem = destinationMeasure->system()
4173 0 : ? destinationMeasure->system()
4174 0 : : destinationMeasure->coveringMMRestOrThis()->system())) {
4175 0 : return cr;
4176 : }
4177 0 : destinationMeasure = currentSystem->firstMeasure();
4178 : }
4179 0 : if (destinationMeasure) {
4180 0 : if ((newCR = destinationMeasure->first()->nextChordRest(trackZeroVoice(cr->track()), false))) {
4181 0 : cr = newCR;
4182 : }
4183 : }
4184 : }
4185 0 : return cr;
4186 : }
4187 :
4188 : //---------------------------------------------------------
4189 : // cmdNextPrevFrame
4190 : // Return next/previous [Vertical/Horizontal/Text] frame
4191 : // to be used as a navigation command
4192 : //---------------------------------------------------------
4193 :
4194 0 : Box* Score::cmdNextPrevFrame(MeasureBase* currentMeasureBase, bool next) const
4195 : {
4196 0 : Box* selectedBox { nullptr };
4197 0 : while (!selectedBox && (currentMeasureBase = (next ? currentMeasureBase->next() : currentMeasureBase->prev()))) {
4198 0 : if (currentMeasureBase->isBox()) {
4199 0 : selectedBox = toBox(currentMeasureBase);
4200 : }
4201 : }
4202 0 : return selectedBox;
4203 : }
4204 :
4205 : //---------------------------------------------------------
4206 : // cmdNextPrevSection
4207 : // Return [Box* or ChordRest*] of next/previous section
4208 : //---------------------------------------------------------
4209 :
4210 0 : EngravingItem* Score::cmdNextPrevSection(EngravingItem* el, bool dir) const
4211 : {
4212 0 : auto currentMeasureBase = el->findMeasureBase();
4213 0 : auto destination = currentMeasureBase;
4214 0 : if (currentMeasureBase) {
4215 : // -----------------------
4216 : // Next Section of Score
4217 : // -----------------------
4218 0 : if (dir) {
4219 0 : if ((destination = getNextPrevSectionBreak(currentMeasureBase, true))) {
4220 0 : el = getScoreElementOfMeasureBase(destination->next());
4221 : }
4222 : }
4223 : // -------------------------
4224 : // Previous Section of Score
4225 : // -------------------------
4226 : else {
4227 0 : auto currentSegment = el->isChordRest() ? toChordRest(el)->segment() : nullptr;
4228 0 : if ((destination = getNextPrevSectionBreak(currentMeasureBase, false))) {
4229 0 : if (currentSegment) {
4230 0 : if ((el = getScoreElementOfMeasureBase((score()->first() == destination) ? destination : destination->next()))) {
4231 0 : if (el->isChordRest() && (toChordRest(el)->segment() == currentSegment)) {
4232 0 : if ((destination = getNextPrevSectionBreak(destination, false))) {
4233 0 : el = !(destination->sectionBreak()) ? destination : getScoreElementOfMeasureBase(destination->next());
4234 : }
4235 : }
4236 : }
4237 0 : } else if ((score()->first() != currentMeasureBase) && (el = getScoreElementOfMeasureBase(destination->next()))) {
4238 0 : if (el->findMeasureBase() == currentMeasureBase) {
4239 0 : if ((destination = getNextPrevSectionBreak(destination, false))) {
4240 0 : el = !(destination->sectionBreak()) ? el : getScoreElementOfMeasureBase(destination->next());
4241 : }
4242 : }
4243 : }
4244 : }
4245 : }
4246 : }
4247 0 : return el;
4248 : }
4249 :
4250 : //---------------------------------------------------------
4251 : // getNextPrevSectionBreak
4252 : // Condition: MeasureBase* must be valid before call
4253 : // If no previous section break exists selects first
4254 : // MeasureBase within score
4255 : //---------------------------------------------------------
4256 :
4257 0 : MeasureBase* Score::getNextPrevSectionBreak(MeasureBase* mb, bool dir) const
4258 : {
4259 0 : auto destination = mb;
4260 0 : if (destination) {
4261 0 : if (dir) {
4262 : // Find next section break
4263 0 : auto endOfSection { false };
4264 0 : while (!endOfSection) {
4265 0 : if ((destination = destination->next())) {
4266 0 : endOfSection = destination->sectionBreak();
4267 : } else {
4268 0 : break;
4269 : }
4270 : }
4271 : } else {
4272 : // Find previous section break
4273 0 : auto inCurrentSection { true };
4274 0 : while (inCurrentSection && destination) {
4275 0 : if (destination->index()) {
4276 : // Safety: SegFaults if invoking prev() when index=0
4277 : // even when MeasureBase* is valid!
4278 0 : destination = destination->prev();
4279 0 : inCurrentSection = !(destination->sectionBreak());
4280 : } else {
4281 0 : destination = nullptr;
4282 : }
4283 : }
4284 0 : if (inCurrentSection || !destination) {
4285 0 : destination = score()->first();
4286 : }
4287 : }
4288 : }
4289 0 : return destination;
4290 : }
4291 :
4292 : //---------------------------------------------------------
4293 : // getScoreElementOfMeasureBase
4294 : // Helper function
4295 : // Get an EngravingItem* as Box or ChordRest depending on
4296 : // MeasureBase
4297 : //---------------------------------------------------------
4298 :
4299 0 : EngravingItem* Score::getScoreElementOfMeasureBase(MeasureBase* mb) const
4300 : {
4301 0 : EngravingItem* el { nullptr };
4302 0 : ChordRest* cr { nullptr };
4303 0 : const Measure* currentMeasure { nullptr };
4304 0 : if (mb) {
4305 0 : if (mb->isBox()) {
4306 0 : el = toBox(mb);
4307 0 : } else if ((currentMeasure = mb->findMeasure())) {
4308 : // Accommodate for MMRest
4309 0 : if (style().styleB(Sid::createMultiMeasureRests) && currentMeasure->hasMMRest()) {
4310 0 : currentMeasure = currentMeasure->coveringMMRestOrThis();
4311 : }
4312 0 : if ((cr = currentMeasure->first()->nextChordRest(0, false))) {
4313 0 : el = cr;
4314 : }
4315 : }
4316 : }
4317 0 : return el;
4318 : }
4319 :
4320 : //---------------------------------------------------------
4321 : // nmeasure
4322 : //---------------------------------------------------------
4323 :
4324 6 : size_t Score::nmeasures() const
4325 : {
4326 6 : size_t n = 0;
4327 33 : for (const Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
4328 27 : n++;
4329 : }
4330 6 : return n;
4331 : }
4332 :
4333 : //---------------------------------------------------------
4334 : // firstTrailingMeasure
4335 : //---------------------------------------------------------
4336 :
4337 0 : Measure* Score::firstTrailingMeasure(ChordRest** cr)
4338 : {
4339 0 : Measure* firstMeasure = nullptr;
4340 0 : auto m = lastMeasure();
4341 :
4342 0 : if (!cr) {
4343 : // No active selection: prepare first empty trailing measure of entire score
4344 0 : while (m && m->isEmpty(muse::nidx)) {
4345 0 : firstMeasure = m;
4346 0 : m = m->prevMeasure();
4347 : }
4348 : } else {
4349 : // Active selection: select full measure rest of active staff's empty trailing measure
4350 0 : ChordRest* tempCR = *cr;
4351 0 : while (m && (tempCR = m->first()->nextChordRest(trackZeroVoice((*cr)->track()), false))->isFullMeasureRest()) {
4352 0 : *cr = tempCR;
4353 0 : firstMeasure = m;
4354 0 : m = m->prevMeasure();
4355 : }
4356 : }
4357 :
4358 0 : return firstMeasure;
4359 : }
4360 :
4361 : //---------------------------------------------------------
4362 : // hasLyrics
4363 : //---------------------------------------------------------
4364 :
4365 0 : bool Score::hasLyrics() const
4366 : {
4367 0 : if (!firstMeasure()) {
4368 0 : return false;
4369 : }
4370 :
4371 0 : SegmentType st = SegmentType::ChordRest;
4372 0 : for (Segment* seg = firstMeasure()->first(st); seg; seg = seg->next1(st)) {
4373 0 : for (size_t i = 0; i < ntracks(); ++i) {
4374 0 : ChordRest* cr = toChordRest(seg->element(static_cast<int>(i)));
4375 0 : if (cr && !cr->lyrics().empty()) {
4376 0 : return true;
4377 : }
4378 : }
4379 : }
4380 0 : return false;
4381 : }
4382 :
4383 : //---------------------------------------------------------
4384 : // hasHarmonies
4385 : //---------------------------------------------------------
4386 :
4387 0 : bool Score::hasHarmonies() const
4388 : {
4389 0 : if (!firstMeasure()) {
4390 0 : return false;
4391 : }
4392 :
4393 0 : SegmentType st = SegmentType::ChordRest;
4394 0 : for (Segment* seg = firstMeasure()->first(st); seg; seg = seg->next1(st)) {
4395 0 : for (EngravingItem* e : seg->annotations()) {
4396 0 : if (e->isHarmony()) {
4397 0 : return true;
4398 : }
4399 : }
4400 : }
4401 0 : return false;
4402 : }
4403 :
4404 : //---------------------------------------------------------
4405 : // lyricCount
4406 : //---------------------------------------------------------
4407 :
4408 0 : int Score::lyricCount() const
4409 : {
4410 0 : return int(lyrics().size());
4411 : }
4412 :
4413 0 : std::vector<Lyrics*> Score::lyrics() const
4414 : {
4415 0 : std::vector<Lyrics*> result;
4416 0 : SegmentType st = SegmentType::ChordRest;
4417 0 : for (size_t track = 0; track < ntracks(); track++) {
4418 0 : size_t maxLyrics = 1;
4419 0 : for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
4420 0 : m->setPlaybackCount(0);
4421 : }
4422 : // follow the repeat segments
4423 0 : const RepeatList& rlist = expandedRepeatList();
4424 0 : for (const RepeatSegment* rs : rlist) {
4425 0 : Fraction startTick = Fraction::fromTicks(rs->tick);
4426 0 : Fraction endTick = Fraction::fromTicks(rs->endTick());
4427 0 : for (Measure* m = tick2measure(startTick); m; m = m->nextMeasure()) {
4428 0 : size_t playCount = m->playbackCount();
4429 0 : for (Segment* seg = m->first(st); seg; seg = seg->next(st)) {
4430 0 : ChordRest* cr = toChordRest(seg->element(track));
4431 0 : if (!cr || cr->lyrics().empty()) {
4432 0 : continue;
4433 : }
4434 0 : if (cr->lyrics().size() > maxLyrics) {
4435 0 : maxLyrics = cr->lyrics().size();
4436 : }
4437 0 : if (playCount > cr->lyrics().size()) {
4438 0 : continue;
4439 : }
4440 0 : Lyrics* l = cr->lyrics(static_cast<int>(playCount));
4441 0 : if (!l) {
4442 0 : continue;
4443 : }
4444 0 : result.push_back(l);
4445 : }
4446 0 : m->setPlaybackCount(m->playbackCount() + 1);
4447 0 : if (m->endTick() >= endTick) {
4448 0 : break;
4449 : }
4450 : }
4451 : }
4452 : // consider remaining lyrics
4453 0 : for (size_t lyricsNumber = 0; lyricsNumber < maxLyrics; lyricsNumber++) {
4454 0 : for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
4455 0 : size_t playCount = m->playbackCount();
4456 0 : if (lyricsNumber < playCount) {
4457 0 : continue;
4458 : }
4459 0 : for (Segment* seg = m->first(st); seg; seg = seg->next(st)) {
4460 0 : ChordRest* cr = toChordRest(seg->element(track));
4461 0 : if (!cr || cr->lyrics().empty()) {
4462 0 : continue;
4463 : }
4464 0 : if (cr->lyrics().size() > maxLyrics) {
4465 0 : maxLyrics = cr->lyrics().size();
4466 : }
4467 0 : if (lyricsNumber > cr->lyrics().size()) {
4468 0 : continue;
4469 : }
4470 0 : Lyrics* l = cr->lyrics(static_cast<int>(lyricsNumber));
4471 0 : if (!l) {
4472 0 : continue;
4473 : }
4474 0 : result.push_back(l);
4475 : }
4476 : }
4477 : }
4478 : }
4479 0 : return result;
4480 0 : }
4481 :
4482 : //---------------------------------------------------------
4483 : // extractLyrics
4484 : //---------------------------------------------------------
4485 :
4486 0 : String Score::extractLyrics() const
4487 : {
4488 0 : String result;
4489 0 : std::vector<Lyrics*> list = lyrics();
4490 0 : for (const Lyrics* l : list) {
4491 0 : String lyric = l->plainText().trimmed();
4492 0 : LyricsSyllabic ls = l->syllabic();
4493 0 : if (ls == LyricsSyllabic::SINGLE || ls == LyricsSyllabic::END) {
4494 0 : result += lyric + u" ";
4495 0 : } else if (ls == LyricsSyllabic::BEGIN || ls == LyricsSyllabic::MIDDLE) {
4496 0 : result += lyric;
4497 : }
4498 0 : }
4499 0 : return result.trimmed();
4500 0 : }
4501 :
4502 : //---------------------------------------------------------
4503 : // harmonyCount
4504 : //---------------------------------------------------------
4505 :
4506 0 : int Score::harmonyCount() const
4507 : {
4508 0 : int count = 0;
4509 0 : SegmentType st = SegmentType::ChordRest;
4510 0 : for (Segment* seg = firstMeasure()->first(st); seg; seg = seg->next1(st)) {
4511 0 : for (EngravingItem* e : seg->annotations()) {
4512 0 : if (e->isHarmony()) {
4513 0 : count++;
4514 : }
4515 : }
4516 : }
4517 0 : return count;
4518 : }
4519 :
4520 : //---------------------------------------------------------
4521 : // keysig
4522 : //---------------------------------------------------------
4523 :
4524 0 : int Score::keysig() const
4525 : {
4526 0 : Key result = Key::C;
4527 0 : for (size_t staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
4528 0 : Staff* st = staff(staffIdx);
4529 0 : constexpr Fraction t(0, 1);
4530 0 : Key key = st->concertKey(t);
4531 0 : if (st->staffType(t)->group() == StaffGroup::PERCUSSION || st->keySigEvent(t).isAtonal()) { // ignore percussion and custom / atonal key
4532 0 : continue;
4533 : }
4534 0 : result = key;
4535 : //TODO keySigs and pitched to unpitched instr changes (Igor Korsukov 2021-02-05) ??? why here?
4536 0 : break;
4537 : }
4538 0 : return int(result);
4539 : }
4540 :
4541 : //---------------------------------------------------------
4542 : // duration
4543 : //---------------------------------------------------------
4544 :
4545 0 : int Score::duration() const
4546 : {
4547 0 : const RepeatList& rl = masterScore()->repeatList(true);
4548 0 : if (rl.empty()) {
4549 0 : return 0;
4550 : }
4551 0 : const RepeatSegment* rs = rl.back();
4552 0 : return lrint(utick2utime(rs->utick + rs->len()));
4553 : }
4554 :
4555 : //---------------------------------------------------------
4556 : // durationWithoutRepeats
4557 : //---------------------------------------------------------
4558 :
4559 0 : int Score::durationWithoutRepeats() const
4560 : {
4561 0 : const RepeatList& rl = masterScore()->repeatList(false);
4562 0 : if (rl.empty()) {
4563 0 : return 0;
4564 : }
4565 0 : const RepeatSegment* rs = rl.back();
4566 0 : return lrint(utick2utime(rs->utick + rs->len()));
4567 : }
4568 :
4569 34 : std::set<staff_idx_t> Score::staffIdxSetFromRange(const track_idx_t trackFrom, const track_idx_t trackTo, StaffAccepted staffAccepted) const
4570 : {
4571 34 : std::set<staff_idx_t> result;
4572 :
4573 130 : for (const Part* part : m_score->parts()) {
4574 96 : const TrackRange range = part->trackRange();
4575 96 : if (trackTo < range.startTrack || trackFrom >= range.endTrack) {
4576 16 : continue;
4577 : }
4578 :
4579 86 : std::set<staff_idx_t> staffIdxList = part->staveIdxList();
4580 86 : if (!staffAccepted) {
4581 6 : result.insert(staffIdxList.cbegin(), staffIdxList.cend());
4582 6 : continue;
4583 : }
4584 :
4585 172 : for (staff_idx_t idx : staffIdxList) {
4586 92 : const Staff* staff = m_score->staff(idx);
4587 92 : if (!staff) {
4588 0 : continue;
4589 : }
4590 :
4591 92 : if (staffAccepted(*staff)) {
4592 91 : result.insert(idx);
4593 : }
4594 : }
4595 86 : }
4596 :
4597 34 : return result;
4598 0 : }
4599 :
4600 : //---------------------------------------------------------
4601 : // getProperty
4602 : //---------------------------------------------------------
4603 :
4604 989 : PropertyValue Score::getProperty(Pid /*id*/) const
4605 : {
4606 989 : LOGD("Score::getProperty: unhandled id");
4607 989 : return PropertyValue();
4608 : }
4609 :
4610 : //---------------------------------------------------------
4611 : // setProperty
4612 : //---------------------------------------------------------
4613 :
4614 5797 : bool Score::setProperty(Pid /*id*/, const PropertyValue& /*v*/)
4615 : {
4616 5797 : LOGD("Score::setProperty: unhandled id");
4617 5797 : setLayoutAll();
4618 5797 : return true;
4619 : }
4620 :
4621 : //---------------------------------------------------------
4622 : // propertyDefault
4623 : //---------------------------------------------------------
4624 :
4625 1194 : PropertyValue Score::propertyDefault(Pid /*id*/) const
4626 : {
4627 1194 : return PropertyValue();
4628 : }
4629 :
4630 26 : void Score::resetStyleValue(Sid styleToReset)
4631 : {
4632 26 : const MStyle& defStyle = DefaultStyle::defaultStyle();
4633 26 : undoChangeStyleVal(styleToReset, defStyle.value(styleToReset));
4634 26 : }
4635 :
4636 0 : void Score::resetStyleValues(const StyleIdSet& styleIdSet)
4637 : {
4638 0 : if (styleIdSet.empty()) {
4639 0 : return;
4640 : }
4641 :
4642 0 : std::unordered_map<Sid, PropertyValue> values;
4643 0 : const MStyle& defStyle = DefaultStyle::defaultStyle();
4644 :
4645 0 : for (Sid sid : styleIdSet) {
4646 0 : values.emplace(sid, defStyle.value(sid));
4647 : }
4648 :
4649 0 : undoChangeStyleValues(std::move(values));
4650 0 : }
4651 :
4652 : //---------------------------------------------------------
4653 : // setStyle
4654 : //---------------------------------------------------------
4655 :
4656 2644 : void Score::setStyle(const MStyle& s, const bool overlap)
4657 : {
4658 2644 : if (!overlap) {
4659 2644 : style() = s;
4660 2644 : return;
4661 : }
4662 :
4663 0 : MStyle styleCopy = s;
4664 :
4665 0 : for (int i = static_cast<int>(Sid::NOSTYLE) + 1; i < static_cast<int>(Sid::STYLES); i++) {
4666 0 : Sid sid = static_cast<Sid>(i);
4667 :
4668 0 : if (!styleCopy.isDefault(sid)) {
4669 0 : style().set(sid, styleCopy.value(sid));
4670 : }
4671 : }
4672 0 : }
4673 :
4674 : //---------------------------------------------------------
4675 : // getTextStyleUserName
4676 : //---------------------------------------------------------
4677 :
4678 0 : TranslatableString Score::getTextStyleUserName(TextStyleType tid) const
4679 : {
4680 0 : if (int(tid) >= int(TextStyleType::USER1) && int(tid) <= int(TextStyleType::USER12)) {
4681 0 : int idx = int(tid) - int(TextStyleType::USER1);
4682 0 : Sid sid[] = { Sid::user1Name, Sid::user2Name, Sid::user3Name, Sid::user4Name, Sid::user5Name, Sid::user6Name,
4683 : Sid::user7Name, Sid::user8Name, Sid::user9Name, Sid::user10Name, Sid::user11Name, Sid::user12Name };
4684 0 : String userName = style().styleSt(sid[idx]);
4685 0 : if (!userName.empty()) {
4686 0 : return TranslatableString::untranslatable(userName);
4687 : }
4688 0 : }
4689 0 : return TConv::userName(tid);
4690 : }
4691 :
4692 12 : String Score::name() const
4693 : {
4694 12 : return m_excerpt ? m_excerpt->name() : String();
4695 : }
4696 :
4697 : //---------------------------------------------------------
4698 : // addRefresh
4699 : //---------------------------------------------------------
4700 :
4701 16858 : void Score::addRefresh(const RectF& r)
4702 : {
4703 16858 : m_updateState.refresh.unite(r);
4704 16858 : cmdState().setUpdateMode(UpdateMode::Update);
4705 16858 : }
4706 :
4707 : //---------------------------------------------------------
4708 : // staffIdx
4709 : //
4710 : // Return index for the staff in the score.
4711 : //---------------------------------------------------------
4712 :
4713 0 : staff_idx_t Score::staffIdx(const Staff* staff) const
4714 : {
4715 0 : staff_idx_t idx = 0;
4716 0 : for (Staff* s : m_staves) {
4717 0 : if (s == staff) {
4718 0 : break;
4719 : }
4720 0 : ++idx;
4721 : }
4722 0 : return idx;
4723 : }
4724 :
4725 : //---------------------------------------------------------
4726 : // staffIdx
4727 : //
4728 : /// Return index for the first staff of \a part.
4729 : //---------------------------------------------------------
4730 :
4731 25842 : staff_idx_t Score::staffIdx(const Part* part) const
4732 : {
4733 25842 : staff_idx_t idx = 0;
4734 38795 : for (Part* p : m_parts) {
4735 38552 : if (p == part) {
4736 25599 : break;
4737 : }
4738 12953 : idx += p->nstaves();
4739 : }
4740 25842 : return idx;
4741 : }
4742 :
4743 740 : Staff* Score::staffById(const ID& staffId) const
4744 : {
4745 1101 : for (Staff* staff : m_staves) {
4746 1101 : if (staff->id() == staffId) {
4747 740 : return staff;
4748 : }
4749 : }
4750 :
4751 0 : return nullptr;
4752 : }
4753 :
4754 43 : Part* Score::partById(const ID& partId) const
4755 : {
4756 241 : for (Part* part : m_parts) {
4757 241 : if (part->id() == partId) {
4758 43 : return part;
4759 : }
4760 : }
4761 :
4762 0 : return nullptr;
4763 : }
4764 :
4765 1 : void Score::clearSystemObjectStaves()
4766 : {
4767 1 : m_systemObjectStaves.clear();
4768 1 : }
4769 :
4770 23 : void Score::addSystemObjectStaff(Staff* staff)
4771 : {
4772 23 : m_systemObjectStaves.push_back(staff);
4773 23 : }
4774 :
4775 0 : void Score::removeSystemObjectStaff(Staff* staff)
4776 : {
4777 0 : muse::remove(m_systemObjectStaves, staff);
4778 0 : }
4779 :
4780 0 : const std::vector<Staff*> Score::systemObjectStavesWithTopStaff() const
4781 : {
4782 0 : std::vector<Staff*> result;
4783 0 : if (Staff* topStaff = staff(0)) {
4784 0 : result.push_back(topStaff);
4785 : }
4786 :
4787 0 : muse::join(result, systemObjectStaves());
4788 :
4789 0 : return result;
4790 0 : }
4791 :
4792 181919 : const std::vector<Part*>& Score::parts() const
4793 : {
4794 181919 : return m_parts;
4795 : }
4796 :
4797 38123 : size_t Score::visiblePartCount() const
4798 : {
4799 38123 : int count = 0;
4800 205603 : for (const Part* part : m_parts) {
4801 167480 : if (part->show()) {
4802 165940 : ++count;
4803 : }
4804 : }
4805 38123 : return count;
4806 : }
4807 :
4808 23794 : std::vector<Part*> Score::visibleParts() const
4809 : {
4810 23794 : std::vector<Part*> result;
4811 23794 : result.reserve(m_parts.size());
4812 :
4813 49086 : for (Part* p : m_parts) {
4814 25292 : if (p->show()) {
4815 23794 : result.push_back(p);
4816 : }
4817 : }
4818 :
4819 23794 : return result;
4820 0 : }
4821 :
4822 10 : std::vector<SharedPart*> Score::sharedParts() const
4823 : {
4824 10 : std::vector<SharedPart*> sharedParts;
4825 30 : for (Part* part : m_parts) {
4826 20 : if (part->isSharedPart()) {
4827 0 : sharedParts.push_back(toSharedPart(part));
4828 : }
4829 : }
4830 :
4831 10 : return sharedParts;
4832 0 : }
4833 :
4834 8 : bool Score::hasSharedParts() const
4835 : {
4836 13 : for (Part* part : m_parts) {
4837 6 : if (part->isSharedPart()) {
4838 1 : return true;
4839 : }
4840 : }
4841 :
4842 7 : return false;
4843 : }
4844 :
4845 0 : size_t Score::visibleStavesCount() const
4846 : {
4847 0 : int count = 0;
4848 0 : for (const Staff* staff : m_staves) {
4849 0 : if (staff->show()) {
4850 0 : ++count;
4851 : }
4852 : }
4853 0 : return count;
4854 : }
4855 :
4856 111901 : bool Score::allStavesInvisible() const
4857 : {
4858 111936 : for (const Staff* staff : m_staves) {
4859 111648 : if (staff->show()) {
4860 111613 : return false;
4861 : }
4862 : }
4863 :
4864 288 : return true;
4865 : }
4866 :
4867 0 : ShadowNote* Score::shadowNote() const
4868 : {
4869 0 : return m_shadowNote;
4870 : }
4871 :
4872 65 : void Score::rebuildBspTree()
4873 : {
4874 65 : for (Page* page : pages()) {
4875 0 : page->invalidateBspTree();
4876 : }
4877 65 : }
4878 :
4879 : //---------------------------------------------------------
4880 : // scanElements
4881 : // scan all elements
4882 : //---------------------------------------------------------
4883 :
4884 163 : void Score::scanElements(std::function<void(EngravingItem*)> func)
4885 : {
4886 3452 : for (MeasureBase* mb = first(); mb; mb = mb->next()) {
4887 3289 : mb->scanElements(func);
4888 3289 : if (mb->isMeasure()) {
4889 3162 : Measure* m = toMeasure(mb);
4890 3162 : Measure* mmr = m->mmRest();
4891 3162 : if (mmr) {
4892 7 : mmr->scanElements(func);
4893 : }
4894 : }
4895 : }
4896 328 : for (Page* page : pages()) {
4897 790 : for (System* s :page->systems()) {
4898 625 : s->scanElements(func);
4899 : }
4900 165 : func(page);
4901 : }
4902 163 : }
4903 :
4904 : //---------------------------------------------------------
4905 : // connectTies
4906 : /// Rebuild tie connections.
4907 : //---------------------------------------------------------
4908 :
4909 2490 : void Score::connectTies(bool silent)
4910 : {
4911 2490 : size_t tracks = nstaves() * VOICES;
4912 2490 : Measure* m = firstMeasure();
4913 2490 : if (!m) {
4914 1 : return;
4915 : }
4916 :
4917 67579 : auto connectTiesForChord = [silent](Chord* c, Segment* s, track_idx_t track) -> void {
4918 209822 : for (Note* n : c->notes()) {
4919 142243 : if (n->laissezVib()) {
4920 14 : continue;
4921 : }
4922 : // connect a tie without end note
4923 142229 : Tie* tie = n->tieFor();
4924 142229 : if (tie) {
4925 12746 : tie->updatePossibleJumpPoints();
4926 : }
4927 142229 : if (tie && !tie->isPartialTie() && !tie->endNote()) {
4928 : Note* nnote;
4929 81 : nnote = searchTieNote(n);
4930 81 : if (nnote == 0) {
4931 0 : if (!silent) {
4932 0 : LOGD("next note at %d track %zu for tie not found", s->tick().ticks(), track);
4933 0 : delete tie;
4934 0 : n->setTieFor(0);
4935 : }
4936 : } else {
4937 81 : tie->setEndNote(nnote);
4938 81 : nnote->setTieBack(tie);
4939 : }
4940 : }
4941 : // connect a glissando without initial note (old glissando format)
4942 142453 : for (Spanner* spanner : n->spannerBack()) {
4943 224 : if (spanner->isGlissando() && !spanner->startElement()) {
4944 0 : Note* initialNote = Glissando::guessInitialNote(n->chord());
4945 0 : n->removeSpannerBack(spanner);
4946 0 : if (initialNote) {
4947 0 : spanner->setStartElement(initialNote);
4948 0 : spanner->setEndElement(n);
4949 0 : spanner->setTick(initialNote->chord()->tick());
4950 0 : spanner->setTick2(n->chord()->tick());
4951 0 : spanner->setTrack(n->track());
4952 0 : spanner->setTrack2(n->track());
4953 0 : spanner->setOwnershipParent(initialNote);
4954 0 : initialNote->add(spanner);
4955 : } else {
4956 0 : delete spanner;
4957 : }
4958 : }
4959 : }
4960 : // spanner with no end element can happen during copy/paste
4961 142453 : for (Spanner* spanner : n->spannerFor()) {
4962 224 : if (spanner->endElement() == nullptr) {
4963 0 : n->removeSpannerFor(spanner);
4964 0 : delete spanner;
4965 : }
4966 : }
4967 : }
4968 67579 : };
4969 :
4970 2489 : SegmentType st = SegmentType::ChordRest;
4971 102725 : for (Segment* s = m->first(st); s; s = s->next1(st)) {
4972 644628 : for (track_idx_t track = 0; track < tracks; ++track) {
4973 544392 : EngravingItem* e = s->element(track);
4974 544392 : if (e == 0 || !e->isChord()) {
4975 477163 : continue;
4976 : }
4977 67229 : Chord* c = toChord(e);
4978 67229 : connectTiesForChord(c, s, track);
4979 67579 : for (Chord* gc : c->graceNotes()) {
4980 350 : connectTiesForChord(gc, s, track);
4981 :
4982 747 : for (Note* n : gc->notes()) {
4983 : // spanner with no end element apparently happens when reading some 206 files
4984 : // (and possibly in other situations too)
4985 426 : for (Spanner* spanner : n->spannerFor()) {
4986 29 : if (spanner->endElement() == nullptr) {
4987 0 : n->removeSpannerFor(spanner);
4988 0 : delete spanner;
4989 : }
4990 : }
4991 : }
4992 : }
4993 : }
4994 : }
4995 : }
4996 :
4997 0 : bool Score::autoLayoutEnabled() const
4998 : {
4999 0 : return isOpen();
5000 : }
5001 :
5002 : //---------------------------------------------------------
5003 : // doLayout
5004 : // do a complete (re-) layout
5005 : //---------------------------------------------------------
5006 :
5007 2910 : void Score::doLayout()
5008 : {
5009 2910 : TRACEFUNC;
5010 :
5011 2910 : doLayoutRange(Fraction(0, 1), Fraction(-1, 1));
5012 2910 : }
5013 :
5014 4000 : void Score::doLayoutRange(const Fraction& st, const Fraction& et)
5015 : {
5016 4015 : TRACEFUNC;
5017 :
5018 4000 : Fraction start = st;
5019 4000 : Fraction end = et;
5020 :
5021 4000 : auto spanners = score()->spannerMap().findOverlapping(st.ticks(), et.ticks());
5022 4163 : for (auto interval : spanners) {
5023 163 : Spanner* spanner = interval.value;
5024 163 : if (!spanner->staff()->visible()) {
5025 0 : continue;
5026 : }
5027 163 : start = std::min(st, spanner->tick());
5028 163 : end = std::max(et, spanner->tick2());
5029 : }
5030 :
5031 4000 : m_engravingFont = engravingFonts()->fontByName(style().value(Sid::musicalSymbolFont).value<String>().toStdString());
5032 4000 : m_layoutOptions.noteHeadWidth = m_engravingFont->width(SymId::noteheadBlack, style().spatium() / style().defaultSpatium());
5033 :
5034 4000 : if (this->cmdState().layoutFlags & LayoutFlag::REBUILD_MIDI_MAPPING) {
5035 4 : if (this->isMaster()) {
5036 4 : this->masterScore()->rebuildMidiMapping();
5037 : }
5038 : }
5039 :
5040 4000 : renderer()->layoutScore(this, start, end);
5041 4000 : }
5042 :
5043 2792 : void Score::updatePaddingTables()
5044 : {
5045 2792 : m_paddingTables.update(style());
5046 2792 : }
5047 :
5048 68 : void Score::addSystemLock(const RangeLock* lock)
5049 : {
5050 68 : m_systemLocks.add(lock);
5051 :
5052 68 : lock->startMB()->triggerLayout();
5053 68 : lock->endMB()->triggerLayout();
5054 68 : }
5055 :
5056 27 : void Score::removeSystemLock(const RangeLock* lock)
5057 : {
5058 27 : m_systemLocks.remove(lock);
5059 :
5060 27 : lock->startMB()->triggerLayout();
5061 27 : lock->endMB()->triggerLayout();
5062 27 : }
5063 :
5064 46 : void Score::addPageLock(const RangeLock* lock)
5065 : {
5066 46 : m_pageLocks.add(lock);
5067 :
5068 46 : lock->startMB()->triggerLayout();
5069 46 : lock->endMB()->triggerLayout();
5070 46 : }
5071 :
5072 23 : void Score::removePageLock(const RangeLock* lock)
5073 : {
5074 23 : m_pageLocks.remove(lock);
5075 :
5076 23 : lock->startMB()->triggerLayout();
5077 23 : lock->endMB()->triggerLayout();
5078 23 : }
5079 :
5080 : //---------------------------------------------------------
5081 : // updateSwing
5082 : //---------------------------------------------------------
5083 :
5084 87 : void Score::updateSwing()
5085 : {
5086 356 : for (Staff* s : m_staves) {
5087 269 : s->clearSwingMap();
5088 : }
5089 87 : Measure* fm = firstMeasure();
5090 87 : if (!fm) {
5091 3 : return;
5092 : }
5093 850 : for (Segment* s = fm->first(SegmentType::ChordRest); s; s = s->next1(SegmentType::ChordRest)) {
5094 938 : for (const EngravingItem* e : s->annotations()) {
5095 172 : if (!e->isStaffTextBase()) {
5096 162 : continue;
5097 : }
5098 18 : const StaffTextBase* st = toStaffTextBase(e);
5099 18 : if (st->xmlText().isEmpty()) {
5100 0 : continue;
5101 : }
5102 18 : Staff* staff = st->staff();
5103 18 : if (!st->swing()) {
5104 8 : continue;
5105 : }
5106 10 : SwingParameters sp;
5107 10 : sp.swingRatio = st->swingParameters().swingRatio;
5108 10 : sp.swingUnit = st->swingParameters().swingUnit;
5109 10 : if (st->systemFlag()) {
5110 20 : for (Staff* sta : m_staves) {
5111 10 : sta->insertIntoSwingMap(s->tick(), sp);
5112 : }
5113 : } else {
5114 0 : staff->insertIntoSwingMap(s->tick(), sp);
5115 : }
5116 : }
5117 : }
5118 : }
5119 :
5120 : //---------------------------------------------------------
5121 : // updateCapo
5122 : //---------------------------------------------------------
5123 :
5124 105 : void Score::updateCapo(bool ignoreNotationUpdate /* = false */)
5125 : {
5126 105 : Measure* fm = firstMeasure();
5127 105 : if (!fm) {
5128 11 : return;
5129 : }
5130 :
5131 639 : for (Segment* s = fm->first(SegmentType::ChordRest); s; s = s->next1(SegmentType::ChordRest)) {
5132 545 : Fraction segmentTick = s->tick();
5133 :
5134 712 : for (EngravingItem* e : s->annotations()) {
5135 167 : if (e->isHarmony()) {
5136 0 : toHarmony(e)->realizedHarmony().setDirty(true);
5137 167 : } else if (e->isFretDiagram()) {
5138 0 : Harmony* harmony = toFretDiagram(e)->harmony();
5139 0 : if (harmony) {
5140 0 : harmony->realizedHarmony().setDirty(true);
5141 : }
5142 : }
5143 :
5144 167 : if (!e->isCapo()) {
5145 152 : continue;
5146 : }
5147 :
5148 37 : for (Staff* staff : e->staff()->staffList()) {
5149 22 : staff->insertCapoParams(segmentTick, toCapo(e)->params(), ignoreNotationUpdate);
5150 15 : }
5151 : }
5152 : }
5153 : }
5154 :
5155 : //---------------------------------------------------------
5156 : // updateChannel
5157 : //---------------------------------------------------------
5158 :
5159 1308 : void Score::updateChannel()
5160 : {
5161 3698 : for (Staff* s : staves()) {
5162 11950 : for (voice_idx_t i = 0; i < VOICES; ++i) {
5163 9560 : s->clearChannelList(i);
5164 : }
5165 : }
5166 1308 : Measure* fm = firstMeasure();
5167 1308 : if (!fm) {
5168 1 : return;
5169 : }
5170 26266 : for (Segment* s = fm->first(SegmentType::ChordRest); s; s = s->next1(SegmentType::ChordRest)) {
5171 28127 : for (const EngravingItem* e : s->annotations()) {
5172 3168 : if (e->isInstrumentChange()) {
5173 151 : for (Staff* staff : e->part()->staves()) {
5174 470 : for (voice_idx_t voice = 0; voice < VOICES; ++voice) {
5175 376 : staff->insertIntoChannelList(voice, s->tick(), 0);
5176 : }
5177 : }
5178 57 : continue;
5179 57 : }
5180 3111 : if (!e->isStaffTextBase()) {
5181 2585 : continue;
5182 : }
5183 526 : const StaffTextBase* st = toStaffTextBase(e);
5184 2630 : for (voice_idx_t voice = 0; voice < VOICES; ++voice) {
5185 2104 : String an(st->channelName(voice));
5186 2104 : if (an.isEmpty()) {
5187 2104 : continue;
5188 : }
5189 0 : Staff* staff = Score::staff(st->staffIdx());
5190 0 : int a = staff->part()->instrument(s->tick())->channelIdx(an);
5191 0 : if (a != -1) {
5192 0 : staff->insertIntoChannelList(voice, s->tick(), a);
5193 : }
5194 2104 : }
5195 : }
5196 : }
5197 :
5198 2319 : for (auto it = spanner().cbegin(); it != spanner().cend(); ++it) {
5199 1012 : Spanner* spanner = (*it).second;
5200 1012 : if (spanner->isPalmMute()) {
5201 16 : toPalmMute(spanner)->setChannel();
5202 : }
5203 1012 : if (spanner->isVolta()) {
5204 125 : toVolta(spanner)->setChannel();
5205 : }
5206 : }
5207 :
5208 26266 : for (Segment* s = fm->first(SegmentType::ChordRest); s; s = s->next1(SegmentType::ChordRest)) {
5209 64396 : for (Staff* st : staves()) {
5210 39437 : track_idx_t strack = st->idx() * VOICES;
5211 39437 : track_idx_t etrack = strack + VOICES;
5212 197185 : for (track_idx_t track = strack; track < etrack; ++track) {
5213 157748 : if (!s->element(track)) {
5214 119351 : continue;
5215 : }
5216 38397 : EngravingItem* e = s->element(track);
5217 38397 : if (!e->isChord()) {
5218 19508 : continue;
5219 : }
5220 18889 : Chord* c = toChord(e);
5221 18889 : size_t channel = st->channel(c->tick(), c->voice());
5222 18889 : Instrument* instr = c->part()->instrument(c->tick());
5223 18889 : if (channel >= instr->channel().size()) {
5224 0 : LOGD() << "Channel " << channel << " too high. Max " << instr->channel().size();
5225 0 : channel = 0;
5226 : }
5227 39667 : for (Note* note : c->notes()) {
5228 20778 : if (note->hidden()) {
5229 0 : continue;
5230 : }
5231 20778 : note->setSubchannel(static_cast<int>(channel));
5232 : }
5233 : }
5234 : }
5235 : }
5236 : }
5237 :
5238 28 : SystemDivider* Score::systemDivider(size_t systemIdx, SystemDividerType type) const
5239 : {
5240 28 : if (muse::contains(m_systemDividers, systemIdx)) {
5241 21 : return m_systemDividers.at(systemIdx).at(static_cast<size_t>(type));
5242 : }
5243 :
5244 7 : return nullptr;
5245 : }
5246 :
5247 14 : void Score::addSystemDivider(size_t systemIdx, SystemDivider* divider)
5248 : {
5249 14 : if (!muse::contains(m_systemDividers, systemIdx)) {
5250 7 : m_systemDividers.emplace(systemIdx, std::array<SystemDivider*, 2> { nullptr, nullptr });
5251 : }
5252 :
5253 14 : m_systemDividers.at(systemIdx)[static_cast<size_t>(divider->dividerType())] = divider;
5254 14 : }
5255 :
5256 752 : BracketType Score::bracketType(staff_idx_t staffIdx, size_t idx) const
5257 : {
5258 752 : const std::vector<BracketItem*>& staffBrackets = brackets(staffIdx);
5259 752 : if (idx < staffBrackets.size()) {
5260 247 : return staffBrackets[idx]->bracketType();
5261 : }
5262 505 : return BracketType::NO_BRACKET;
5263 : }
5264 :
5265 159 : size_t Score::bracketSpan(staff_idx_t staffIdx, size_t idx) const
5266 : {
5267 159 : const std::vector<BracketItem*>& staffBrackets = brackets(staffIdx);
5268 159 : if (idx < staffBrackets.size()) {
5269 159 : return staffBrackets[idx]->bracketSpan();
5270 : }
5271 0 : return 0;
5272 : }
5273 :
5274 34605 : const std::vector<BracketItem*>& Score::brackets(staff_idx_t staffIdx) const
5275 : {
5276 34605 : return m_brackets.at(staffIdx);
5277 : }
5278 :
5279 28140 : std::vector<BracketItem*>& Score::brackets(staff_idx_t staffIdx)
5280 : {
5281 28140 : const size_t newSize = std::max(static_cast<size_t>(staffIdx) + 1, m_staves.size());
5282 28140 : if (m_brackets.size() < newSize) {
5283 0 : m_brackets.resize(newSize);
5284 : }
5285 28140 : return m_brackets[staffIdx];
5286 : }
5287 :
5288 : //---------------------------------------------------------
5289 : // bracketLevels
5290 : //---------------------------------------------------------
5291 :
5292 1405 : size_t Score::bracketLevels(staff_idx_t staffIdx) const
5293 : {
5294 1405 : const std::vector<BracketItem*>& staffBrackets = brackets(staffIdx);
5295 1405 : size_t columns = 0;
5296 1886 : for (auto bi : staffBrackets) {
5297 481 : columns = std::max(columns, bi->column());
5298 : }
5299 1405 : return columns;
5300 : }
5301 :
5302 0 : AutomationDataConstPtr Score::automationData() const { return m_masterScore->automationData(); }
5303 :
5304 0 : void Score::editAutomationPoints(const AutomationCurveKey& key, AutomationPointEdits& edits, bool undoable)
5305 : {
5306 0 : m_masterScore->editAutomationPoints(key, edits, undoable);
5307 0 : }
5308 :
5309 11360 : TransactionManager* Score::transactionManager() const { return m_masterScore->transactionManager(); }
5310 1918 : UndoStack* Score::undoStack() const { return m_masterScore->undoStack(); }
5311 1238 : const RepeatList& Score::repeatList() const { return m_masterScore->repeatList(); }
5312 2665 : const RepeatList& Score::repeatList(bool expandRepeats, bool updateTies) const
5313 : {
5314 2665 : return m_masterScore->repeatList(expandRepeats, updateTies);
5315 : }
5316 :
5317 475 : std::vector<RepeatSegmentInfo> Score::repeatSegmentInfoList(bool expandRepeats) const
5318 : {
5319 : // Read-only: don't trigger tie/jump-point cleanup, safe to call mid-edit
5320 475 : return repeatList(expandRepeats, false).segmentInfoList();
5321 : }
5322 :
5323 348 : const RepeatList& Score::expandedRepeatList() const
5324 : {
5325 348 : return repeatList(/*expandRepeats*/ true);
5326 : }
5327 :
5328 0 : TimeSigMap* Score::sigmap() const { return m_masterScore->sigmap(); }
5329 0 : muse::async::Channel<ScoreChanges> Score::changesChannel() const { return m_masterScore->changesChannel(); }
5330 :
5331 1 : void Score::setUpdateAll() { m_masterScore->setUpdateAll(); }
5332 :
5333 9043 : void Score::setLayoutAll(staff_idx_t staff, const EngravingItem* e) { m_masterScore->setLayoutAll(staff, e); }
5334 919228 : void Score::setLayout(const Fraction& tick, staff_idx_t staff, const EngravingItem* e) { m_masterScore->setLayout(tick, staff, e); }
5335 15454 : void Score::setLayout(const Fraction& tick1, const Fraction& tick2, staff_idx_t staff1, staff_idx_t staff2, const EngravingItem* e)
5336 : {
5337 15454 : m_masterScore->setLayout(tick1, tick2, staff1, staff2, e);
5338 15454 : }
5339 :
5340 2151 : CmdState& Score::cmdState() { return m_masterScore->cmdState(); }
5341 0 : const CmdState& Score::cmdState() const { return m_masterScore->cmdState(); }
5342 25 : void Score::addLayoutFlags(LayoutFlags f) { m_masterScore->addLayoutFlags(f); }
5343 3 : void Score::setInstrumentsChanged(bool v) { m_masterScore->setInstrumentsChanged(v); }
5344 :
5345 0 : Fraction Score::loopBoundaryTick(LoopBoundaryType type) const { return m_masterScore->loopBoundaryTick(type); }
5346 0 : void Score::setLoopBoundaryTick(LoopBoundaryType type, Fraction tick) { m_masterScore->setLoopBoundaryTick(type, tick); }
5347 :
5348 : //---------------------------------------------------------
5349 : // ScoreLoad::_loading
5350 : // If the _loading > 0 then pushes and pops to
5351 : // the undo stack do not emit a warning.
5352 : // Usually pushes and pops to the undo stack are only
5353 : // valid inside a startCmd() - endCmd(). Exceptions
5354 : // occurred during score loading.
5355 : //---------------------------------------------------------
5356 :
5357 : int ScoreLoad::m_loading = 0;
5358 : }
|