LCOV - code coverage report
Current view: top level - src/engraving/dom - score.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 57.9 % 2774 1605
Test Date: 2026-11-05 04:07:02

            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              : }
        

Generated by: LCOV version 2.0-1