LCOV - code coverage report
Current view: top level - src/engraving/editing - edit.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 58.5 % 3248 1899
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              : #include <map>
      24              : #include <set>
      25              : 
      26              : #include "../dom/accidental.h"
      27              : #include "../dom/arpeggio.h"
      28              : #include "../dom/articulation.h"
      29              : #include "../dom/barline.h"
      30              : #include "../dom/beam.h"
      31              : #include "../dom/box.h"
      32              : #include "../dom/bracket.h"
      33              : #include "../dom/breath.h"
      34              : #include "../dom/chord.h"
      35              : #include "../dom/chordbracket.h"
      36              : #include "../dom/chordline.h"
      37              : #include "../dom/clef.h"
      38              : #include "../dom/dynamic.h"
      39              : #include "../dom/excerpt.h"
      40              : #include "../dom/expression.h"
      41              : #include "../dom/factory.h"
      42              : #include "../dom/fingering.h"
      43              : #include "../dom/figuredbass.h"
      44              : #include "../dom/fret.h"
      45              : #include "../dom/glissando.h"
      46              : #include "../dom/guitarbend.h"
      47              : #include "../dom/hammeronpulloff.h"
      48              : #include "../dom/harmony.h"
      49              : #include "../dom/harppedaldiagram.h"
      50              : #include "../dom/hook.h"
      51              : #include "../dom/instrchange.h"
      52              : #include "../dom/instrumentname.h"
      53              : #include "../dom/key.h"
      54              : #include "../dom/keylist.h"
      55              : #include "../dom/keysig.h"
      56              : #include "../dom/laissezvib.h"
      57              : #include "../dom/layoutbreak.h"
      58              : #include "../dom/linkedobjects.h"
      59              : #include "../dom/lyrics.h"
      60              : #include "../dom/marker.h"
      61              : #include "../dom/masterscore.h"
      62              : #include "../dom/measure.h"
      63              : #include "../dom/measurerepeat.h"
      64              : #include "../dom/note.h"
      65              : #include "../dom/noteline.h"
      66              : #include "../dom/ottava.h"
      67              : #include "../dom/pagelockindicator.h"
      68              : #include "../dom/part.h"
      69              : #include "../dom/partialtie.h"
      70              : #include "../dom/playcounttext.h"
      71              : #include "../dom/range.h"
      72              : #include "../dom/rehearsalmark.h"
      73              : #include "../dom/rest.h"
      74              : #include "../dom/score.h"
      75              : #include "../dom/segment.h"
      76              : #include "../dom/sig.h"
      77              : #include "../dom/slur.h"
      78              : #include "../dom/staff.h"
      79              : #include "../dom/stafftext.h"
      80              : #include "../dom/stem.h"
      81              : #include "../dom/sticking.h"
      82              : #include "../dom/stringtunings.h"
      83              : #include "../dom/system.h"
      84              : #include "../dom/systemlockindicator.h"
      85              : #include "../dom/systemtext.h"
      86              : #include "../dom/tapping.h"
      87              : #include "../dom/tempotext.h"
      88              : #include "../dom/text.h"
      89              : #include "../dom/textline.h"
      90              : #include "../dom/tie.h"
      91              : #include "../dom/tiemap.h"
      92              : #include "../dom/timesig.h"
      93              : #include "../dom/tremolosinglechord.h"
      94              : #include "../dom/tremolotwochord.h"
      95              : #include "../dom/tuplet.h"
      96              : #include "../dom/tupletmap.h"
      97              : #include "../dom/utils.h"
      98              : #include "../dom/volta.h"
      99              : #include "../infrastructure/messagebox.h"
     100              : 
     101              : #include "addremoveelement.h"
     102              : #include "editstaffbrackets.h"
     103              : #include "editchord.h"
     104              : #include "editclef.h"
     105              : #include "editkeysig.h"
     106              : #include "editmeasures.h"
     107              : #include "editnote.h"
     108              : #include "editpagelocks.h"
     109              : #include "editpart.h"
     110              : #include "editproperty.h"
     111              : #include "editrehearsalmark.h"
     112              : #include "editscoreproperties.h"
     113              : #include "editspanner.h"
     114              : #include "editstaff.h"
     115              : #include "editstyle.h"
     116              : #include "editsystemlocks.h"
     117              : #include "edittimesig.h"
     118              : #include "edittremolo.h"
     119              : #include "inserttime.h"
     120              : #include "mscoreview.h"
     121              : #include "navigation.h"
     122              : #include "splitjoinmeasure.h"
     123              : #include "transaction/transaction.h"
     124              : #include "transaction/undostack.h"
     125              : #include "transpose.h"
     126              : 
     127              : #include "log.h"
     128              : 
     129              : using namespace mu;
     130              : using namespace mu::engraving;
     131              : 
     132              : namespace mu::engraving {
     133            0 : static ChordRest* chordOrRest(EngravingItem* el)
     134              : {
     135            0 :     if (el) {
     136            0 :         if (el->isNote()) {
     137            0 :             return toNote(el)->chord();
     138            0 :         } else if (el->isRestFamily()) {
     139            0 :             return toRest(el);
     140            0 :         } else if (el->isChord()) {
     141            0 :             return toChord(el);
     142              :         }
     143              :     }
     144              : 
     145            0 :     return nullptr;
     146              : }
     147              : 
     148              : //---------------------------------------------------------
     149              : //   getSelectedNote
     150              : //---------------------------------------------------------
     151              : 
     152            0 : Note* Score::getSelectedNote()
     153              : {
     154            0 :     EngravingItem* el = selection().element();
     155            0 :     if (el && el->isNote()) {
     156            0 :         return toNote(el);
     157              :     }
     158            0 :     MScore::setError(MsError::NO_NOTE_SELECTED);
     159            0 :     return 0;
     160              : }
     161              : 
     162              : //---------------------------------------------------------
     163              : //   getSelectedChordRest
     164              : //---------------------------------------------------------
     165              : 
     166            0 : ChordRest* Score::getSelectedChordRest() const
     167              : {
     168            0 :     ChordRest* cr = chordOrRest(selection().element());
     169            0 :     if (!cr) {
     170            0 :         MScore::setError(MsError::NO_NOTE_REST_SELECTED);
     171              :     }
     172              : 
     173            0 :     return cr;
     174              : }
     175              : 
     176              : //---------------------------------------------------------
     177              : //   getSelectedChordRest2
     178              : //---------------------------------------------------------
     179              : 
     180            0 : void Score::getSelectedStartEndChordRests(ChordRest*& cr1, ChordRest*& cr2) const
     181              : {
     182            0 :     cr1 = 0;
     183            0 :     cr2 = 0;
     184            0 :     for (EngravingItem* e : selection().elements()) {
     185            0 :         if (e->isNote()) {
     186            0 :             e = toNote(e)->chord();
     187              :         }
     188            0 :         if (e->isChordRest()) {
     189            0 :             ChordRest* cr = toChordRest(e);
     190            0 :             if (cr1 == 0 || (cr1)->tick() > cr->tick()) {
     191            0 :                 cr1 = cr;
     192              :             }
     193            0 :             if (cr2 == 0 || (cr2)->tick() < cr->tick()) {
     194            0 :                 cr2 = cr;
     195              :             }
     196              :         }
     197              :     }
     198            0 :     if (cr1 == 0) {
     199            0 :         MScore::setError(MsError::NO_NOTE_REST_SELECTED);
     200              :     }
     201            0 :     if (cr1 == cr2) {
     202            0 :         cr2 = 0;
     203              :     }
     204            0 : }
     205              : 
     206              : //---------------------------------------------------------
     207              : //   getSelectedChordRests
     208              : //---------------------------------------------------------
     209              : 
     210          101 : std::set<ChordRest*> Score::getSelectedChordRests() const
     211              : {
     212          101 :     std::set<ChordRest*> set;
     213          442 :     for (EngravingItem* e : selection().elements()) {
     214          341 :         if (e->isNote()) {
     215          140 :             e = toNote(e)->chord();
     216              :         }
     217          341 :         if (e->isChordRest()) {
     218          200 :             set.insert(toChordRest(e));
     219              :         }
     220              :     }
     221          101 :     return set;
     222            0 : }
     223              : 
     224              : //---------------------------------------------------------
     225              : //   pos
     226              : //---------------------------------------------------------
     227              : 
     228            0 : Fraction Score::pos()
     229              : {
     230            0 :     EngravingItem* el = selection().element();
     231            0 :     if (selection().activeCR()) {
     232            0 :         el = selection().activeCR();
     233              :     }
     234            0 :     if (el) {
     235            0 :         switch (el->type()) {
     236            0 :         case ElementType::NOTE:
     237            0 :             el = toNote(el)->chord();
     238              :         // fall through
     239            0 :         case ElementType::MEASURE_REPEAT:
     240              :         case ElementType::REST:
     241              :         case ElementType::MMREST:
     242              :         case ElementType::CHORD:
     243            0 :             return toChordRest(el)->tick();
     244            0 :         default:
     245            0 :             break;
     246              :         }
     247              :     }
     248            0 :     return Fraction(0, 1);
     249              : }
     250              : 
     251           23 : Tuplet* Score::addTuplet(ChordRest* destinationChordRest, Fraction ratio, TupletNumberType numberType, TupletBracketType bracketType)
     252              : {
     253           23 :     if (destinationChordRest->durationType() < TDuration(DurationType::V_512TH)
     254           23 :         && destinationChordRest->durationType() != TDuration(DurationType::V_MEASURE)) {
     255            0 :         return nullptr;
     256              :     }
     257              : 
     258           23 :     Measure* measure = destinationChordRest->measure();
     259           23 :     if (measure && measure->isMMRest()) {
     260            0 :         return nullptr;
     261              :     }
     262              : 
     263           23 :     Fraction f(destinationChordRest->ticks());
     264              : 
     265           23 :     f.reduce();         //measure duration might not be reduced
     266              : 
     267           23 :     Fraction _ratio;
     268           23 :     _ratio.setNumerator(ratio.numerator() != -1 ? ratio.numerator() : f.numerator());
     269           23 :     _ratio.setDenominator(ratio.denominator() != -1 ? ratio.denominator() : f.numerator());
     270              : 
     271           23 :     Fraction fr = f * Fraction(1, _ratio.denominator());
     272           23 :     if (!TDuration::isValid(fr)) {
     273            0 :         MessageBox(iocContext()).warning(muse::mtrc("engraving", "Cannot create tuplet with ratio %1 for duration %2")
     274            0 :                                          .arg(_ratio.toString(), f.toString()).toStdString(),
     275            0 :                                          std::string(), { MessageBox::Ok });
     276            0 :         return nullptr;
     277              :     }
     278              : 
     279           23 :     return addTuplet(destinationChordRest, _ratio, TDuration(fr), numberType, bracketType);
     280              : }
     281              : 
     282          126 : static bool isValidTuplet(const Fraction& ratio, const TDuration& baseLen)
     283              : {
     284          126 :     return baseLen.isValid() && !baseLen.isMeasure() && ratio.numerator() > 0 && ratio.denominator() > 0;
     285              : }
     286              : 
     287          162 : static Fraction tupletEnd(const Staff* staff, const Fraction& tick, Tuplet* parent, const Fraction& ratio, const TDuration& baseLen)
     288              : {
     289          162 :     const Fraction ticks = baseLen.fraction() * ratio.denominator();
     290          162 :     return tick + actualTicks(ticks, parent, staff->timeStretch(tick));
     291              : }
     292              : 
     293              : // A tuplet must not cross a barline or the end of its parent tuplet
     294          119 : static Fraction tupletLimit(const Tuplet* parent, const Measure* measure)
     295              : {
     296          119 :     return parent ? parent->tick() + parent->actualTicks() : measure->endTick();
     297              : }
     298              : 
     299              : //---------------------------------------------------------
     300              : //   canAddTuplet
     301              : //    Whether addTuplet() can create a tuplet with this ratio
     302              : //    and base length at chordRest
     303              : //---------------------------------------------------------
     304              : 
     305          117 : bool Score::canAddTuplet(const ChordRest* chordRest, const Fraction& ratio, const TDuration& baseLen) const
     306              : {
     307          117 :     if (!chordRest || !isValidTuplet(ratio, baseLen)) {
     308            6 :         return false;
     309              :     }
     310              : 
     311          111 :     const Measure* measure = chordRest->measure();
     312          111 :     if (!measure || measure->isMMRest() || chordRest->isGrace()) {
     313            0 :         return false;
     314              :     }
     315              : 
     316              :     // changeCRlen() can't change the length of a measure repeat
     317          111 :     if (chordRest->isMeasureRepeat() && baseLen.fraction() * ratio.denominator() != chordRest->ticks()) {
     318            0 :         return false;
     319              :     }
     320              : 
     321          111 :     return tupletEndTick(chordRest, ratio, baseLen) <= tupletLimit(chordRest->tuplet(), measure);
     322              : }
     323              : 
     324              : //---------------------------------------------------------
     325              : //   canAddTuplet
     326              : //    The same at segment in track, where no chord/rest of
     327              : //    the track may start: an empty voice, or inside a
     328              : //    longer chord/rest
     329              : //---------------------------------------------------------
     330              : 
     331           11 : bool Score::canAddTuplet(const Segment* segment, track_idx_t track, const Fraction& ratio, const TDuration& baseLen) const
     332              : {
     333           11 :     if (!segment || track == muse::nidx) {
     334            1 :         return false;
     335              :     }
     336              : 
     337           10 :     if (const ChordRest* chordRest = segment->cr(track)) {
     338            1 :         return canAddTuplet(chordRest, ratio, baseLen);
     339              :     }
     340              : 
     341            9 :     const Measure* measure = segment->measure();
     342            9 :     const Staff* staff = this->staff(track2staff(track));
     343            9 :     if (!isValidTuplet(ratio, baseLen) || !measure || measure->isMMRest() || !staff) {
     344            1 :         return false;
     345              :     }
     346              : 
     347              :     // inside a longer chord/rest, the tuplet would be in that chord/rest's tuplet
     348            8 :     const ChordRest* previous = segment->nextChordRest(track, /*backwards*/ true, /*stopAtMeasureBoundary*/ true);
     349            8 :     Tuplet* parent = previous && previous->endTick() > segment->tick() ? previous->tuplet() : nullptr;
     350              : 
     351            8 :     return tupletEnd(staff, segment->tick(), parent, ratio, baseLen) <= tupletLimit(parent, measure);
     352              : }
     353              : 
     354              : //---------------------------------------------------------
     355              : //   tupletEndTick
     356              : //    Where a tuplet of ratio.denominator() * baseLen that
     357              : //    starts at chordRest ends
     358              : //---------------------------------------------------------
     359              : 
     360          154 : Fraction Score::tupletEndTick(const ChordRest* chordRest, const Fraction& ratio, const TDuration& baseLen) const
     361              : {
     362          154 :     return tupletEnd(chordRest->staff(), chordRest->tick(), chordRest->tuplet(), ratio, baseLen);
     363              : }
     364              : 
     365              : //---------------------------------------------------------
     366              : //   addTuplet
     367              : //    Create a tuplet of ratio.denominator() * baseLen, starting
     368              : //    at destinationChordRest and replacing everything in that
     369              : //    time in its track. The tuplet can be longer or shorter than
     370              : //    destinationChordRest, but must not cross a barline or the end
     371              : //    of a parent tuplet.
     372              : //---------------------------------------------------------
     373              : 
     374           52 : Tuplet* Score::addTuplet(ChordRest* destinationChordRest, const Fraction& ratio, const TDuration& baseLen,
     375              :                          TupletNumberType numberType, TupletBracketType bracketType)
     376              : {
     377           52 :     if (!canAddTuplet(destinationChordRest, ratio, baseLen)) {
     378            0 :         const Fraction duration = baseLen.fraction() * ratio.denominator();
     379            0 :         MessageBox(iocContext()).warning(muse::mtrc("engraving", "Cannot create tuplet with ratio %1 for duration %2")
     380            0 :                                          .arg(ratio.toString(), duration.toString()).toStdString(),
     381            0 :                                          std::string(), { MessageBox::Ok });
     382            0 :         return nullptr;
     383              :     }
     384              : 
     385           52 :     Measure* measure = destinationChordRest->measure();
     386           52 :     Tuplet* ot = destinationChordRest->tuplet();
     387           52 :     const Fraction f = baseLen.fraction() * ratio.denominator();
     388              : 
     389              :     // Shorten a longer chord/rest first, so that the remainder is filled with rests. Shorten it to
     390              :     // the base length, since changeCRlen() only shortens correctly to a single note value
     391           52 :     if (destinationChordRest->ticks() > f) {
     392            4 :         changeCRlen(destinationChordRest, baseLen.fraction());
     393              :     }
     394              : 
     395              :     // Clear the rest of the time the tuplet will take
     396           52 :     if (destinationChordRest->ticks() < f) {
     397           28 :         makeGap(destinationChordRest->segment(), destinationChordRest->track(), f, ot, /*keepChord*/ true);
     398              :     }
     399              : 
     400           52 :     Tuplet* tuplet = Factory::createTuplet(measure);
     401           52 :     tuplet->setRatio(ratio);
     402              : 
     403           52 :     tuplet->setNumberType(numberType);
     404           52 :     if (tuplet->numberType() == TupletNumberType(tuplet->style().styleI(Sid::tupletNumberType))) {
     405           52 :         tuplet->setPropertyFlags(Pid::NUMBER_TYPE, PropertyFlags::STYLED);
     406              :     } else {
     407            0 :         tuplet->setPropertyFlags(Pid::NUMBER_TYPE, PropertyFlags::UNSTYLED);
     408              :     }
     409              : 
     410           52 :     tuplet->setBracketType(bracketType);
     411           52 :     if (tuplet->bracketType() == TupletBracketType(tuplet->style().styleI(Sid::tupletBracketType))) {
     412           52 :         tuplet->setPropertyFlags(Pid::BRACKET_TYPE, PropertyFlags::STYLED);
     413              :     } else {
     414            0 :         tuplet->setPropertyFlags(Pid::BRACKET_TYPE, PropertyFlags::UNSTYLED);
     415              :     }
     416              : 
     417           52 :     tuplet->setTicks(f);
     418           52 :     tuplet->setBaseLen(baseLen);
     419              : 
     420           52 :     tuplet->setTrack(destinationChordRest->track());
     421           52 :     tuplet->setTick(destinationChordRest->tick());
     422              : 
     423           52 :     if (ot) {
     424            6 :         tuplet->setTuplet(ot);
     425              :     }
     426              : 
     427           52 :     cmdCreateTuplet(destinationChordRest, tuplet);
     428              : 
     429           52 :     return tuplet;
     430              : }
     431              : 
     432              : //---------------------------------------------------------
     433              : //   selectNewTuplet
     434              : //    Select where input continues in a new tuplet, and use
     435              : //    its base length as the input duration
     436              : //---------------------------------------------------------
     437              : 
     438            0 : void Score::selectNewTuplet(Tuplet* tuplet)
     439              : {
     440            0 :     const std::vector<DurationElement*>& elements = tuplet->elements();
     441            0 :     DurationElement* elementForSelect = nullptr;
     442            0 :     if (!elements.empty()) {
     443            0 :         DurationElement* firstElement = elements.front();
     444            0 :         if (firstElement->isRest()) {
     445            0 :             elementForSelect = firstElement;
     446            0 :         } else if (elements.size() > 1) {
     447            0 :             elementForSelect = elements[1];
     448              :         }
     449              :     }
     450              : 
     451            0 :     if (elementForSelect) {
     452            0 :         score()->select(elementForSelect, SelectType::SINGLE, 0);
     453            0 :         score()->inputState().setDuration(tuplet->baseLen());
     454              :     }
     455            0 : }
     456              : 
     457              : //---------------------------------------------------------
     458              : //   addRest
     459              : //    create one Rest at tick with duration d
     460              : //    create segment if necessary
     461              : //---------------------------------------------------------
     462              : 
     463         1053 : Rest* Score::addRest(const Fraction& tick, track_idx_t track, TDuration d, Tuplet* tuplet)
     464              : {
     465         1053 :     Measure* measure = tick2measure(tick);
     466         1053 :     Rest* rest = Factory::createRest(this->dummy(), d);
     467         1053 :     if (d.type() == DurationType::V_MEASURE) {
     468          674 :         rest->setTicks(measure->stretchedLen(staff(track2staff(track))));
     469              :     } else {
     470          379 :         rest->setTicks(d.fraction());
     471              :     }
     472         1053 :     rest->setTrack(track);
     473         1053 :     rest->setTuplet(tuplet);
     474         1053 :     undoAddCR(rest, measure, tick);
     475         1053 :     return rest;
     476              : }
     477              : 
     478              : //---------------------------------------------------------
     479              : //   addRest
     480              : //---------------------------------------------------------
     481              : 
     482           74 : Rest* Score::addRest(Segment* s, track_idx_t track, TDuration d, Tuplet* tuplet)
     483              : {
     484           74 :     Rest* rest = Factory::createRest(s, d);
     485           74 :     if (d.type() == DurationType::V_MEASURE) {
     486           41 :         rest->setTicks(s->measure()->stretchedLen(staff(track / VOICES)));
     487              :     } else {
     488           33 :         rest->setTicks(d.fraction());
     489              :     }
     490           74 :     rest->setTrack(track);
     491           74 :     rest->setTuplet(tuplet);
     492           74 :     undoAddCR(rest, tick2measure(s->tick()), s->tick());
     493           74 :     return rest;
     494              : }
     495              : 
     496              : //---------------------------------------------------------
     497              : //   addChord
     498              : //    Create one Chord at tick with duration d
     499              : //    - create segment if necessary.
     500              : //    - Use chord "oc" as prototype;
     501              : //    - if "genTie" then tie to chord "oc"
     502              : //---------------------------------------------------------
     503              : 
     504            1 : Chord* Score::addChord(const Fraction& tick, TDuration d, Chord* oc, bool genTie, Tuplet* tuplet)
     505              : {
     506            1 :     Measure* measure = tick2measure(tick);
     507            1 :     if (measure->endTick() <= tick) {
     508            0 :         LOGD("Score::addChord(): end of score?");
     509            0 :         return 0;
     510              :     }
     511              : 
     512            1 :     Chord* chord = Factory::createChord(this->dummy());
     513            1 :     chord->setTuplet(tuplet);
     514            1 :     chord->setTrack(oc->track());
     515            1 :     chord->setDurationType(d);
     516            1 :     chord->setTicks(d.fraction());
     517            1 :     chord->setStemDirection(oc->stemDirection());
     518              : 
     519            2 :     for (Note* n : oc->notes()) {
     520            1 :         Note* nn = Factory::createNote(chord);
     521            1 :         nn->setPitch(n->pitch());
     522            1 :         nn->setTpc1(n->tpc1());
     523            1 :         nn->setTpc2(n->tpc2());
     524            1 :         chord->add(nn);
     525              :     }
     526            1 :     undoAddCR(chord, measure, tick);
     527              : 
     528              :     //
     529              :     // now as both chords are in place
     530              :     // (have segments as parent) we can add ties:
     531              :     //
     532            1 :     if (genTie) {
     533            1 :         size_t n = oc->notes().size();
     534            2 :         for (size_t i = 0; i < n; ++i) {
     535            1 :             Note* n1  = oc->notes()[i];
     536            1 :             Note* n2 = chord->notes()[i];
     537            1 :             Tie* tie = Factory::createTie(n1);
     538            1 :             tie->setStartNote(n1);
     539            1 :             tie->setEndNote(n2);
     540            1 :             tie->setTick(tie->startNote()->tick());
     541            1 :             tie->setTick2(tie->endNote()->tick());
     542            1 :             tie->setTrack(n1->track());
     543            1 :             undoAddElement(tie);
     544              :         }
     545              :     }
     546              : 
     547            1 :     return chord;
     548              : }
     549              : 
     550              : //---------------------------------------------------------
     551              : //   addClone
     552              : //---------------------------------------------------------
     553              : 
     554          437 : ChordRest* Score::addClone(ChordRest* cr, const Fraction& tick, const TDuration& d)
     555              : {
     556              :     ChordRest* newcr;
     557              :     // change a MeasureRepeat into an Rest
     558          437 :     if (cr->isMeasureRepeat()) {
     559            0 :         newcr = Factory::copyRest(*toRest(cr));
     560            0 :         toRest(newcr)->hack_toRestType();
     561              :     } else {
     562          437 :         newcr = toChordRest(cr->clone());
     563              :     }
     564          437 :     newcr->mutldata()->setPosX(0.0);
     565          437 :     newcr->setDurationType(d);
     566          437 :     newcr->setTicks(d.isMeasure() ? cr->measure()->stretchedLen(cr->staff()) : d.fraction());
     567          437 :     newcr->setTuplet(cr->tuplet());
     568          437 :     newcr->setSelected(false);
     569          437 :     if (newcr->isChord()) {
     570            3 :         muse::DeleteAll(toChord(newcr)->graceNotes());
     571            3 :         toChord(newcr)->removeAllGraceNotes();
     572              :     }
     573              : 
     574          437 :     undoAddCR(newcr, cr->measure(), tick);
     575          437 :     return newcr;
     576              : }
     577              : 
     578              : //---------------------------------------------------------
     579              : //   setRest
     580              : //    sets rests and returns the first one
     581              : //    "l" is in local (stretched) time
     582              : //---------------------------------------------------------
     583              : 
     584          721 : Rest* Score::setRest(const Fraction& _tick, track_idx_t track, const Fraction& _l, bool useDots, Tuplet* tuplet, bool useFullMeasureRest)
     585              : {
     586          721 :     std::vector<Rest*> rests = setRests(_tick, track, _l, useDots, tuplet, useFullMeasureRest);
     587         1442 :     return rests.empty() ? nullptr : rests.front();
     588          721 : }
     589              : 
     590              : //---------------------------------------------------------
     591              : //   setRests
     592              : //    create one or more rests to fill "l"
     593              : //    "l" is in local (stretched) time
     594              : //---------------------------------------------------------
     595              : 
     596         1113 : std::vector<Rest*> Score::setRests(const Fraction& _tick, track_idx_t track, const Fraction& _l, bool useDots, Tuplet* tuplet,
     597              :                                    bool useFullMeasureRest)
     598              : {
     599         1113 :     Fraction l       = _l;
     600         1113 :     Fraction tick    = _tick;
     601         1113 :     Measure* measure = tick2measure(tick);
     602         1113 :     std::vector<Rest*> rests;
     603         1113 :     Staff* staff     = Score::staff(track / VOICES);
     604              : 
     605         1735 :     while (!l.isZero()) {
     606              :         //
     607              :         // divide into measures
     608              :         //
     609          975 :         Fraction f;
     610          975 :         if (tuplet) {
     611           75 :             f = tuplet->baseLen().fraction() * tuplet->ratio().numerator();
     612          156 :             for (DurationElement* de : tuplet->elements()) {
     613          119 :                 if (de->tick() >= tick) {
     614           38 :                     break;
     615              :                 }
     616           81 :                 f -= de->ticks();
     617              :             }
     618              :             //
     619              :             // restrict to tuplet len
     620              :             //
     621           75 :             if (f < l) {
     622            0 :                 l = f;
     623              :             }
     624              :         } else {
     625          900 :             if (measure->tick() < tick) {
     626          184 :                 f = measure->endTick() - tick;
     627              :             } else {
     628          716 :                 f = measure->ticks();
     629              :             }
     630          900 :             f *= staff->timeStretch(tick);
     631              :         }
     632              : 
     633          975 :         if (f > l) {
     634          144 :             f = l;
     635              :         }
     636              : 
     637              :         // Don't fill a full measure with rests on a non-zero voice, *unless* it has links in voice zero
     638          975 :         bool fullMeasure = tick == measure->tick() && f == measure->stretchedLen(staff);
     639          975 :         bool emptyNonZeroVoice = track2voice(track) != 0 && !measure->hasVoice(track);
     640          975 :         if (emptyNonZeroVoice && fullMeasure && !staff->trackHasLinksInVoiceZero(track)) {
     641           17 :             l -= f;
     642           17 :             measure = measure->nextMeasure();
     643           17 :             if (!measure) {
     644          353 :                 break;
     645              :             }
     646           17 :             tick = measure->tick();
     647           17 :             continue;
     648              :         }
     649              : 
     650          958 :         if ((measure->timesig() == measure->ticks())       // not in pickup measure
     651          945 :             && fullMeasure
     652          659 :             && !tuplet
     653         1903 :             && useFullMeasureRest) {
     654          659 :             Rest* rest = addRest(tick, track, TDuration(DurationType::V_MEASURE), tuplet);
     655          659 :             tick += rest->actualTicks();
     656          659 :             rests.push_back(rest);
     657              :         } else {
     658              :             //
     659              :             // compute list of durations which will fit l
     660              :             //
     661          299 :             std::vector<TDuration> dList;
     662          299 :             if (tuplet || f == Fraction(0, 1)) {
     663           75 :                 dList = toDurationList(l, useDots);
     664           75 :                 std::reverse(dList.begin(), dList.end());
     665              :             } else {
     666          224 :                 Fraction timeStretch = staff->timeStretch(tick);
     667          672 :                 dList = toRhythmicDurationList(f, true, (tick - measure->tick()) * timeStretch, sigmap()->timesig(
     668          672 :                                                    tick).nominal(), measure, useDots ? 1 : 0, timeStretch);
     669              :             }
     670          299 :             if (dList.empty()) {
     671            0 :                 return rests;
     672              :             }
     673              : 
     674          299 :             Rest* rest = 0;
     675          678 :             for (const TDuration& d : dList) {
     676          379 :                 rest = addRest(tick, track, d, tuplet);
     677              :                 // If we're filling an empty non-zero voice make these gaps
     678          379 :                 rest->setGap(emptyNonZeroVoice && !fullMeasure);
     679          379 :                 rests.push_back(rest);
     680          379 :                 tick += rest->actualTicks();
     681              :             }
     682          299 :         }
     683          958 :         l -= f;
     684              : 
     685          958 :         measure = measure->nextMeasure();
     686          958 :         if (!measure) {
     687          353 :             break;
     688              :         }
     689          605 :         tick = measure->tick();
     690              :     }
     691         1113 :     return rests;
     692            0 : }
     693              : 
     694            0 : Slur* Score::addSlur(ChordRest* firstChordRest, ChordRest* secondChordRest, const Slur* slurTemplate)
     695              : {
     696            0 :     if (!secondChordRest) {
     697            0 :         ChordRestNavigateOptions options;
     698            0 :         options.disableOverRepeats = true;
     699            0 :         secondChordRest = Navigation::nextChordRest(firstChordRest, options);
     700              : 
     701            0 :         if (!secondChordRest || !secondChordRest->isChord()) {
     702            0 :             if (slurTemplate && slurTemplate->isHammerOnPullOff() && firstChordRest->isChord()) {
     703            0 :                 Note* endNote = GuitarBend::createEndNote(toChord(firstChordRest)->upNote());
     704            0 :                 if (endNote) {
     705            0 :                     secondChordRest = endNote->chord();
     706              :                 }
     707              :             }
     708            0 :             if (!secondChordRest) {
     709            0 :                 secondChordRest = firstChordRest;
     710              :             }
     711            0 :         } else if (secondChordRest->isChord()) {
     712            0 :             bool firstChordRestIsTiedToSecond = firstChordRest->isChord() && toChord(firstChordRest)->allNotesTiedToNext()
     713            0 :                                                 && toChord(firstChordRest)->upNote()->tieFor()->endNote()->parent() == secondChordRest;
     714              : 
     715              :             // Follow chain of tied notes and slur until the last
     716            0 :             if (firstChordRestIsTiedToSecond || !firstChordRest->isGrace()) {
     717            0 :                 while (toChord(secondChordRest)->allNotesTiedToNext()) {
     718            0 :                     secondChordRest = toChord(secondChordRest)->upNote()->tieFor()->endNote()->chord();
     719              :                 }
     720              :             }
     721              : 
     722              :             // If the first chord rest is also tied to this chain, slur to the next non-tied note
     723            0 :             if (firstChordRestIsTiedToSecond) {
     724            0 :                 ChordRest* nextCandidate = Navigation::nextChordRest(secondChordRest, options);
     725            0 :                 if (nextCandidate) {
     726            0 :                     secondChordRest = nextCandidate;
     727              :                 }
     728              :             }
     729              :         }
     730              :     }
     731              : 
     732            0 :     Slur* slur = slurTemplate ? slurTemplate->clone() : Factory::createSlur(score()->dummy());
     733            0 :     slur->setScore(firstChordRest->score());
     734            0 :     slur->setTick(firstChordRest->tick());
     735            0 :     slur->setTick2(secondChordRest->tick());
     736            0 :     slur->setTrack(firstChordRest->track());
     737            0 :     if (secondChordRest->staff()->part() == firstChordRest->staff()->part()
     738            0 :         && !secondChordRest->staff()->isLinked(firstChordRest->staff())) {
     739            0 :         slur->setTrack2(secondChordRest->track());
     740              :     } else {
     741            0 :         slur->setTrack2(firstChordRest->track());
     742              :     }
     743            0 :     slur->setStartElement(firstChordRest);
     744            0 :     slur->setEndElement(secondChordRest);
     745              : 
     746            0 :     firstChordRest->score()->undoAddElement(slur);
     747            0 :     SlurTieSegment* ss = slur->newSlurTieSegment();
     748            0 :     ss->setSpannerSegmentType(SpannerSegmentType::SINGLE);
     749            0 :     if (firstChordRest == secondChordRest && !(slur->isOutgoing() || slur->isIncoming())) {
     750            0 :         ss->setSlurOffset(Grip::END, PointF(3.0 * firstChordRest->style().spatium(), 0.0));
     751              :     }
     752            0 :     slur->add(ss);
     753              : 
     754            0 :     return slur;
     755              : }
     756              : 
     757              : /// The segment that holds the annotations of `chordRest`. For a multimeasure rest,
     758              : /// that is the corresponding segment of the first underlying measure.
     759            0 : static Segment* annotationSegment(const ChordRest* chordRest)
     760              : {
     761            0 :     const Measure* measure = chordRest->measure();
     762            0 :     if (measure && measure->isMMRest()) {
     763            0 :         return measure->mmRestFirst()->findSegmentR(SegmentType::ChordRest, Fraction(0, 1));
     764              :     }
     765              : 
     766            0 :     return chordRest->segment();
     767              : }
     768              : 
     769            1 : TextBase* Score::addText(TextStyleType type, EngravingItem* destinationElement)
     770              : {
     771            1 :     TextBase* textBox = nullptr;
     772              : 
     773            1 :     switch (type) {
     774            0 :     case TextStyleType::TITLE:
     775              :     case TextStyleType::SUBTITLE:
     776              :     case TextStyleType::COMPOSER:
     777              :     case TextStyleType::LYRICIST:
     778              :     case TextStyleType::INSTRUMENT_EXCERPT: {
     779            0 :         MeasureBase* frame = nullptr;
     780              : 
     781            0 :         if (destinationElement && destinationElement->isVBox()) {
     782            0 :             frame = toMeasureBase(destinationElement);
     783              :         } else {
     784            0 :             frame = first();
     785            0 :             if (!frame || !frame->isBox()) {
     786            0 :                 frame = insertBox(ElementType::VBOX, frame);
     787              :             }
     788              :         }
     789              : 
     790            0 :         textBox = Factory::createText(frame, type);
     791            0 :         undoAddElement(textBox);
     792            0 :         break;
     793              :     }
     794            0 :     case TextStyleType::FRAME: {
     795            0 :         if (!destinationElement) {
     796            0 :             break;
     797              :         }
     798            0 :         textBox = Factory::createText(destinationElement, type);
     799            0 :         undoAddElement(textBox);
     800            0 :         break;
     801              :     }
     802            0 :     case TextStyleType::REHEARSAL_MARK: {
     803            0 :         ChordRest* chordRest = chordOrRest(destinationElement);
     804            0 :         if (!chordRest) {
     805            0 :             break;
     806              :         }
     807            0 :         textBox = Factory::createRehearsalMark(annotationSegment(chordRest));
     808            0 :         textBox->setTrack(chordRest->track());
     809            0 :         RehearsalMark* r = toRehearsalMark(textBox);
     810            0 :         textBox->setXmlText(EditRehearsalMark::createRehearsalMarkText(this, r));
     811            0 :         undoAddElement(textBox);
     812            0 :         break;
     813              :     }
     814            0 :     case TextStyleType::STAFF: {
     815            0 :         ChordRest* chordRest = chordOrRest(destinationElement);
     816            0 :         if (!chordRest) {
     817            0 :             break;
     818              :         }
     819            0 :         textBox = Factory::createStaffText(annotationSegment(chordRest), TextStyleType::STAFF);
     820            0 :         textBox->setTrack(chordRest->track());
     821            0 :         undoAddElement(textBox);
     822            0 :         break;
     823              :     }
     824            0 :     case TextStyleType::SYSTEM: {
     825            0 :         ChordRest* chordRest = chordOrRest(destinationElement);
     826            0 :         if (!chordRest) {
     827            0 :             break;
     828              :         }
     829            0 :         textBox = Factory::createSystemText(annotationSegment(chordRest), TextStyleType::SYSTEM);
     830            0 :         textBox->setTrack(chordRest->track());
     831            0 :         undoAddElement(textBox);
     832            0 :         break;
     833              :     }
     834            0 :     case TextStyleType::DYNAMICS: {
     835            0 :         ChordRest* chordRest = chordOrRest(destinationElement);
     836            0 :         if (!chordRest) {
     837            0 :             break;
     838              :         }
     839            0 :         textBox = Factory::createDynamic(annotationSegment(chordRest));
     840            0 :         textBox->setTrack(chordRest->track());
     841            0 :         undoAddElement(textBox);
     842            0 :         break;
     843              :     }
     844            0 :     case TextStyleType::EXPRESSION: {
     845            0 :         ChordRest* chordRest = chordOrRest(destinationElement);
     846            0 :         if (!chordRest) {
     847            0 :             break;
     848              :         }
     849            0 :         textBox = Factory::createExpression(annotationSegment(chordRest));
     850            0 :         textBox->setTrack(chordRest->track());
     851            0 :         undoAddElement(textBox);
     852            0 :         break;
     853              :     }
     854            0 :     case TextStyleType::INSTRUMENT_CHANGE: {
     855            0 :         ChordRest* chordRest = chordOrRest(destinationElement);
     856            0 :         if (!chordRest) {
     857            0 :             break;
     858              :         }
     859            0 :         textBox = Factory::createInstrumentChange(annotationSegment(chordRest));
     860            0 :         textBox->setTrack(chordRest->track());
     861            0 :         undoAddElement(textBox);
     862            0 :         break;
     863              :     }
     864            0 :     case TextStyleType::STICKING: {
     865            0 :         ChordRest* chordRest = chordOrRest(destinationElement);
     866            0 :         if (!chordRest) {
     867            0 :             break;
     868              :         }
     869            0 :         textBox = Factory::createSticking(annotationSegment(chordRest));
     870            0 :         textBox->setTrack(chordRest->track());
     871            0 :         undoAddElement(textBox);
     872            0 :         break;
     873              :     }
     874            0 :     case TextStyleType::FINGERING:
     875              :     case TextStyleType::LH_GUITAR_FINGERING:
     876              :     case TextStyleType::RH_GUITAR_FINGERING:
     877              :     case TextStyleType::STRING_NUMBER: {
     878            0 :         if (!destinationElement || !destinationElement->isNote()) {
     879            0 :             break;
     880              :         }
     881              : 
     882            0 :         const Staff* staff = destinationElement->staff();
     883            0 :         bool isTablature = staff->isTabStaff(destinationElement->tick());
     884            0 :         bool tabFingering = staff->staffType(destinationElement->tick())->showTabFingering();
     885            0 :         if (isTablature && !tabFingering) {
     886            0 :             break;
     887              :         }
     888              : 
     889            0 :         textBox = Factory::createFingering(toNote(destinationElement), type);
     890            0 :         textBox->setTrack(destinationElement->track());
     891            0 :         undoAddElement(textBox);
     892            0 :         break;
     893              :     }
     894            1 :     case TextStyleType::HARMONY_A:
     895              :     case TextStyleType::HARMONY_ROMAN:
     896              :     case TextStyleType::HARMONY_NASHVILLE: {
     897            1 :         track_idx_t track = muse::nidx;
     898            1 :         EngravingItem* newParent = nullptr;
     899            1 :         if (destinationElement && destinationElement->isFretDiagram()) {
     900            1 :             FretDiagram* fretDiagram = toFretDiagram(destinationElement);
     901            1 :             track = fretDiagram->track();
     902            1 :             newParent = fretDiagram->harmony() ? toEngravingItem(fretDiagram->segment()) : toEngravingItem(fretDiagram);
     903              :         } else {
     904            0 :             ChordRest* chordRest = chordOrRest(destinationElement);
     905            0 :             if (chordRest) {
     906            0 :                 track = chordRest->track();
     907            0 :                 newParent = chordRest->segment();
     908              :             }
     909              :         }
     910              : 
     911            1 :         if (track == muse::nidx || !newParent) {
     912              :             break;
     913              :         }
     914              : 
     915            1 :         Harmony* harmony = Factory::createHarmony(newParent);
     916            1 :         harmony->setTrack(track);
     917              : 
     918              :         static const std::map<TextStyleType, HarmonyType> harmonyTypes = {
     919              :             { TextStyleType::HARMONY_A, HarmonyType::STANDARD },
     920              :             { TextStyleType::HARMONY_ROMAN, HarmonyType::ROMAN },
     921              :             { TextStyleType::HARMONY_NASHVILLE, HarmonyType::NASHVILLE }
     922            3 :         };
     923            1 :         harmony->setHarmonyType(harmonyTypes.at(type));
     924              : 
     925            1 :         textBox = harmony;
     926            1 :         undoAddElement(textBox);
     927            1 :         break;
     928              :     }
     929            0 :     case TextStyleType::LYRICS_ODD: {
     930            0 :         if (!destinationElement || (!destinationElement->isNote() && !destinationElement->isLyrics() && !destinationElement->isRest())) {
     931            0 :             break;
     932              :         }
     933            0 :         ChordRest* chordRest = nullptr;
     934            0 :         if (destinationElement->isNote()) {
     935            0 :             chordRest = toNote(destinationElement)->chord();
     936            0 :             if (chordRest->isGrace()) {
     937            0 :                 chordRest = toChordRest(chordRest->ownershipParent());
     938              :             }
     939            0 :         } else if (destinationElement->isLyrics()) {
     940            0 :             chordRest = toLyrics(destinationElement)->chordRest();
     941            0 :         } else if (destinationElement->isRest()) {
     942            0 :             chordRest = toChordRest(destinationElement);
     943              :         } else {
     944            0 :             break;
     945              :         }
     946              : 
     947            0 :         int no = static_cast<int>(chordRest->lyrics().size());
     948            0 :         const auto& spanners = spannerMap().findOverlapping(chordRest->tick().ticks(), chordRest->endTick().ticks());
     949            0 :         for (auto& spanner : spanners) {
     950            0 :             if (!spanner.value->isPartialLyricsLine() || spanner.start != chordRest->tick().ticks()) {
     951            0 :                 continue;
     952              :             }
     953            0 :             const PartialLyricsLine* line = toPartialLyricsLine(spanner.value);
     954              : 
     955            0 :             no = std::max(no, line->verse() + 1);
     956              :         }
     957              : 
     958              :         // Also check how many partial lines there are
     959            0 :         Lyrics* lyrics = Factory::createLyrics(chordRest);
     960            0 :         lyrics->setTrack(chordRest->track());
     961            0 :         lyrics->setProperty(Pid::VERSE, no);
     962              : 
     963            0 :         textBox = lyrics;
     964            0 :         undoAddElement(textBox);
     965            0 :         break;
     966              :     }
     967            0 :     case TextStyleType::TEMPO: {
     968            0 :         ChordRest* chordRest = chordOrRest(destinationElement);
     969            0 :         if (!chordRest) {
     970            0 :             break;
     971              :         }
     972              : 
     973            0 :         SigEvent event = sigmap()->timesig(chordRest->tick());
     974            0 :         Fraction f = event.nominal();
     975            0 :         String text(u"<sym>metNoteQuarterUp</sym> = 80");
     976            0 :         switch (f.denominator()) {
     977            0 :         case 1:
     978            0 :             text = u"<sym>metNoteWhole</sym> = 80";
     979            0 :             break;
     980            0 :         case 2:
     981            0 :             text = u"<sym>metNoteHalfUp</sym> = 80";
     982            0 :             break;
     983            0 :         case 4:
     984            0 :             text = u"<sym>metNoteQuarterUp</sym> = 80";
     985            0 :             break;
     986            0 :         case 8:
     987            0 :             if (f.numerator() % 3 == 0) {
     988            0 :                 text = u"<sym>metNoteQuarterUp</sym><sym>space</sym><sym>metAugmentationDot</sym> = 80";
     989              :             } else {
     990            0 :                 text = u"<sym>metNote8thUp</sym> = 80";
     991              :             }
     992            0 :             break;
     993            0 :         case 16:
     994            0 :             if (f.numerator() % 3 == 0) {
     995            0 :                 text = u"<sym>metNote8thUp</sym><sym>space</sym><sym>metAugmentationDot</sym> = 80";
     996              :             } else {
     997            0 :                 text = u"<sym>metNote16thUp</sym> = 80";
     998              :             }
     999            0 :             break;
    1000            0 :         case 32:
    1001            0 :             if (f.numerator() % 3 == 0) {
    1002            0 :                 text = u"<sym>metNote16thUp</sym><sym>space</sym><sym>metAugmentationDot</sym> = 80";
    1003              :             } else {
    1004            0 :                 text = u"<sym>metNote32ndUp</sym> = 80";
    1005              :             }
    1006            0 :             break;
    1007            0 :         case 64:
    1008            0 :             if (f.numerator() % 3 == 0) {
    1009            0 :                 text = u"<sym>metNote32ndUp</sym><sym>space</sym><sym>metAugmentationDot</sym> = 80";
    1010              :             } else {
    1011            0 :                 text = u"<sym>metNote64thUp</sym> = 80";
    1012              :             }
    1013            0 :             break;
    1014            0 :         case 128:
    1015            0 :             if (f.numerator() % 3 == 0) {
    1016            0 :                 text = u"<sym>metNote64ndUp</sym><sym>space</sym><sym>metAugmentationDot</sym> = 80";
    1017              :             } else {
    1018            0 :                 text = u"<sym>metNote128thUp</sym> = 80";
    1019              :             }
    1020            0 :             break;
    1021            0 :         default:
    1022            0 :             break;
    1023              :         }
    1024              : 
    1025            0 :         TempoText* tempoText = Factory::createTempoText(chordRest->segment());
    1026            0 :         tempoText->setTrack(0);
    1027            0 :         tempoText->setXmlText(text);
    1028            0 :         tempoText->setFollowText(true);
    1029            0 :         tempoText->updateTempo();
    1030              : 
    1031            0 :         textBox = tempoText;
    1032            0 :         undoAddElement(textBox);
    1033            0 :         break;
    1034            0 :     }
    1035            0 :     case TextStyleType::HARP_PEDAL_DIAGRAM:
    1036              :     case TextStyleType::HARP_PEDAL_TEXT_DIAGRAM: {
    1037            0 :         ChordRest* chordRest = getSelectedChordRest();
    1038            0 :         if (!chordRest) {
    1039            0 :             break;
    1040              :         }
    1041            0 :         textBox = Factory::createHarpPedalDiagram(annotationSegment(chordRest));
    1042            0 :         textBox->setTrack(chordRest->track());
    1043            0 :         undoAddElement(textBox);
    1044            0 :         break;
    1045              :     }
    1046            0 :     default:
    1047            0 :         break;
    1048              :     }
    1049              : 
    1050            1 :     return textBox;
    1051              : }
    1052              : 
    1053              : //---------------------------------------------------------
    1054              : //   searchNote
    1055              : //    search for note or rest before or at tick position tick
    1056              : //    in staff
    1057              : //---------------------------------------------------------
    1058              : 
    1059            0 : ChordRest* Score::searchNote(const Fraction& tick, track_idx_t track) const
    1060              : {
    1061            0 :     ChordRest* ipe = 0;
    1062            0 :     SegmentType st = SegmentType::ChordRest;
    1063            0 :     for (Segment* segment = firstSegment(st); segment; segment = segment->next1(st)) {
    1064            0 :         ChordRest* cr = segment->cr(track);
    1065            0 :         if (!cr) {
    1066            0 :             continue;
    1067              :         }
    1068            0 :         if (cr->tick() == tick) {
    1069            0 :             return cr;
    1070              :         }
    1071            0 :         if (cr->tick() > tick) {
    1072            0 :             return ipe ? ipe : cr;
    1073              :         }
    1074            0 :         ipe = cr;
    1075              :     }
    1076            0 :     return 0;
    1077              : }
    1078              : 
    1079              : //---------------------------------------------------------
    1080              : //   cmdAddOttava
    1081              : //---------------------------------------------------------
    1082              : 
    1083            0 : void Score::cmdAddOttava(OttavaType type)
    1084              : {
    1085            0 :     const Selection sel = selection();   // copy selection state before the operation.
    1086              :     // add on each staff if possible
    1087            0 :     if (sel.isRange() && sel.staffStart() != sel.staffEnd() - 1) {
    1088            0 :         for (staff_idx_t staffIdx = sel.staffStart(); staffIdx < sel.staffEnd(); ++staffIdx) {
    1089            0 :             ChordRest* cr1 = sel.firstChordRest(staffIdx * VOICES);
    1090            0 :             ChordRest* cr2 = sel.lastChordRest(staffIdx * VOICES);
    1091            0 :             if (!cr1) {
    1092            0 :                 continue;
    1093              :             }
    1094            0 :             if (!cr2) {
    1095            0 :                 cr2 = cr1;
    1096              :             }
    1097            0 :             Ottava* ottava = Factory::createOttava(this->dummy());
    1098            0 :             ottava->setOttavaType(type);
    1099            0 :             if (type == OttavaType::OTTAVA_8VB /*|| type == OttavaType::OTTAVA_15MB || type == OttavaType::OTTAVA_22MB*/) {
    1100            0 :                 ottava->setPlacement(PlacementV::BELOW);
    1101            0 :                 ottava->styleChanged();
    1102              :             }
    1103            0 :             ottava->setTrack(cr1->track());
    1104            0 :             ottava->setTrack2(cr1->track());
    1105            0 :             ottava->setTick(cr1->tick());
    1106            0 :             ottava->setTick2(cr2->endTick());
    1107            0 :             undoAddElement(ottava);
    1108              :         }
    1109              :     } else {
    1110              :         ChordRest* cr1;
    1111              :         ChordRest* cr2;
    1112            0 :         getSelectedStartEndChordRests(cr1, cr2);
    1113            0 :         if (!cr1) {
    1114            0 :             return;
    1115              :         }
    1116            0 :         if (!cr2) {
    1117            0 :             cr2 = cr1;
    1118              :         }
    1119              : 
    1120            0 :         Ottava* ottava = Factory::createOttava(this->dummy());
    1121            0 :         ottava->setOttavaType(type);
    1122            0 :         if (type == OttavaType::OTTAVA_8VB /*|| type == OttavaType::OTTAVA_15MB || type == OttavaType::OTTAVA_22MB*/) {
    1123            0 :             ottava->setPlacement(PlacementV::BELOW);
    1124            0 :             ottava->styleChanged();
    1125              :         }
    1126            0 :         ottava->setTrack(cr1->track());
    1127            0 :         ottava->setTrack2(cr1->track());
    1128            0 :         ottava->setTick(cr1->tick());
    1129            0 :         ottava->setTick2(cr2->endTick());
    1130            0 :         undoAddElement(ottava);
    1131            0 :         if (!noteEntryMode()) {
    1132            0 :             select(ottava, SelectType::SINGLE, 0);
    1133              :         }
    1134              :     }
    1135            0 : }
    1136              : 
    1137              : //---------------------------------------------------------
    1138              : //   cmdAddNoteLine
    1139              : //---------------------------------------------------------
    1140              : 
    1141            0 : void Score::addNoteLine()
    1142              : {
    1143            0 :     std::vector<Note*> selectedNotes;
    1144              : 
    1145            0 :     if (selection().isRange()) {
    1146            0 :         track_idx_t startTrack = selection().staffStart() * VOICES;
    1147            0 :         track_idx_t endTrack = selection().staffEnd() * VOICES;
    1148              : 
    1149            0 :         for (track_idx_t track = startTrack; track < endTrack; ++track) {
    1150            0 :             std::vector<Note*> notes = selection().noteList(track);
    1151            0 :             selectedNotes.insert(selectedNotes.end(), notes.begin(), notes.end());
    1152            0 :         }
    1153              :     } else {
    1154            0 :         std::vector<Note*> notes = selection().noteList();
    1155            0 :         selectedNotes.insert(selectedNotes.end(), notes.begin(), notes.end());
    1156            0 :     }
    1157              : 
    1158            0 :     Note* startNote = nullptr;
    1159            0 :     Note* endNote  = nullptr;
    1160              : 
    1161            0 :     for (Note* note : selectedNotes) {
    1162            0 :         if (startNote == nullptr || startNote->chord()->tick() > note->chord()->tick()) {
    1163            0 :             startNote = note;
    1164              :         }
    1165            0 :         if (endNote == nullptr || endNote->chord()->tick() < note->chord()->tick()) {
    1166            0 :             endNote = note;
    1167              :         }
    1168              :     }
    1169              : 
    1170            0 :     if (!startNote) {
    1171            0 :         LOGD("addNoteLine: no first note %p", startNote);
    1172            0 :         return;
    1173              :     }
    1174              : 
    1175            0 :     if (startNote == endNote) {
    1176            0 :         endNote = SLine::guessFinalNote(startNote);
    1177              :     }
    1178              : 
    1179            0 :     if (!endNote) {
    1180            0 :         LOGD("addNoteLine: no last note note %p", endNote);
    1181            0 :         return;
    1182              :     }
    1183              : 
    1184            0 :     NoteLine* line = Factory::createNoteLine(startNote);
    1185            0 :     line->setStartElement(startNote);
    1186            0 :     line->setTick(startNote->chord()->tick());
    1187            0 :     line->setEndElement(endNote);
    1188              : 
    1189            0 :     undoAddElement(line);
    1190            0 : }
    1191              : 
    1192              : //---------------------------------------------------------
    1193              : //   deleteItem
    1194              : //---------------------------------------------------------
    1195              : 
    1196           73 : void Score::deleteItem(EngravingItem* el)
    1197              : {
    1198           73 :     if (!el) {
    1199            0 :         return;
    1200              :     }
    1201              : 
    1202           73 :     if (el->generated()) {
    1203           15 :         switch (el->type()) {
    1204              :         // These types can be removed, even if generated
    1205            9 :         case ElementType::BAR_LINE:
    1206              :         case ElementType::BRACKET:
    1207              :         case ElementType::CLEF:
    1208              :         case ElementType::INSTRUMENT_NAME:
    1209              :         case ElementType::KEYSIG:
    1210              :         case ElementType::MEASURE_NUMBER:
    1211              :         case ElementType::SYSTEM_LOCK_INDICATOR:
    1212              :         case ElementType::PAGE_LOCK_INDICATOR:
    1213              :         case ElementType::HAMMER_ON_PULL_OFF_TEXT:
    1214              :         case ElementType::PLAY_COUNT_TEXT:
    1215              :         case ElementType::LYRICSLINE_SEGMENT:
    1216            9 :             break;
    1217              :         // All other types cannot be removed if generated
    1218            6 :         default:
    1219            6 :             return;
    1220              :         }
    1221              :     }
    1222              : //      LOGD("%s", el->typeName());
    1223              : 
    1224           67 :     Transaction& tx = transactionManager()->currentOrDummyTransaction();
    1225              : 
    1226           67 :     switch (el->type()) {
    1227            0 :     case ElementType::INSTRUMENT_NAME: {
    1228            0 :         Part* part = el->part();
    1229            0 :         InstrumentName* in = toInstrumentName(el);
    1230            0 :         if (in->instrumentNameType() == InstrumentNameType::LONG) {
    1231            0 :             tx.push(new ChangeInstrumentLong(Fraction(0, 1), part, String()));
    1232            0 :         } else if (in->instrumentNameType() == InstrumentNameType::SHORT) {
    1233            0 :             tx.push(new ChangeInstrumentShort(Fraction(0, 1), part, String()));
    1234              :         }
    1235              :     }
    1236            0 :     break;
    1237              : 
    1238            0 :     case ElementType::TIMESIG: {
    1239              :         // timesig might already be removed
    1240            0 :         TimeSig* ts = toTimeSig(el);
    1241            0 :         Segment* s = ts->segment();
    1242            0 :         Measure* m = s->measure();
    1243            0 :         Segment* ns = m->findSegment(s->segmentType(), s->tick());
    1244            0 :         if (!ns || (ns->element(ts->track()) != ts)) {
    1245            0 :             LOGD("deleteItem: not found");
    1246            0 :             break;
    1247              :         }
    1248            0 :         EditTimeSig::removeTimeSig(tx, this, ts);
    1249              :     }
    1250            0 :     break;
    1251              : 
    1252            2 :     case ElementType::KEYSIG:
    1253              :     {
    1254            2 :         KeySig* k = toKeySig(el);
    1255            2 :         Segment* nextCrSeg = k->segment()->next1(SegmentType::ChordRest);
    1256            2 :         const TrackRange trackRange = el->part()->trackRange();
    1257            4 :         bool ic = nextCrSeg && nextCrSeg->findAnnotation(ElementType::INSTRUMENT_CHANGE,
    1258            2 :                                                          trackRange.startTrack, trackRange.endTrack - 1);
    1259            2 :         undoRemoveElement(k);
    1260            2 :         if (ic) {
    1261            0 :             KeySigEvent ke = k->keySigEvent();
    1262            0 :             ke.setForInstrumentChange(true);
    1263            0 :             Key cKey = k->staff()->keySigEvent(k->tick()).concertKey();
    1264            0 :             Key tKey = cKey;
    1265            0 :             if (!style().styleB(Sid::concertPitch)) {
    1266            0 :                 Interval v = k->part()->instrument(k->tick())->transpose();
    1267            0 :                 v.flip();
    1268            0 :                 tKey = Transpose::transposeKey(cKey, v, k->part()->preferSharpFlat());
    1269              :             }
    1270            0 :             ke.setConcertKey(cKey);
    1271            0 :             ke.setKey(tKey);
    1272            0 :             EditKeySig::undoChangeKeySig(tx, this, k->staff(), k->tick(), ke);
    1273            0 :         }
    1274            4 :         for (size_t i = 0; i < k->part()->nstaves(); i++) {
    1275            6 :             for (Staff* staff : k->part()->staff(i)->staffList()) {
    1276            4 :                 KeySigEvent e = staff->keySigEvent(k->tick());
    1277            4 :                 staff->score()->updateInstrumentChangeTranspositions(e, staff, k->tick());
    1278            6 :             }
    1279              :         }
    1280              :     }
    1281            2 :     break;
    1282              : 
    1283            4 :     case ElementType::NOTE:
    1284              :     {
    1285            4 :         Chord* chord = toChord(el->ownershipParent());
    1286            4 :         if (chord->notes().size() > 1) {
    1287            0 :             undoRemoveElement(el);
    1288            0 :             select(chord->downNote(), SelectType::SINGLE, 0);
    1289            0 :             break;
    1290              :         }
    1291              :         // else fall through
    1292            4 :         el = chord;
    1293              :     }
    1294              :     // fall through
    1295              : 
    1296           11 :     case ElementType::CHORD:
    1297              :     {
    1298           11 :         Chord* chord = toChord(el);
    1299           11 :         removeChordRest(chord, false);
    1300              : 
    1301              :         // replace with rest
    1302           11 :         if (chord->noteType() == NoteType::NORMAL) {
    1303           10 :             Segment* segment = chord->segment();
    1304           10 :             Rest* rest = Factory::createRest(segment, chord->durationType());
    1305           10 :             rest->setTicks(chord->ticks());
    1306           10 :             rest->setTrack(el->track());
    1307              : 
    1308           10 :             undoAddCR(rest, segment->measure(), segment->tick());
    1309              : 
    1310           10 :             Tuplet* tuplet = chord->tuplet();
    1311           10 :             if (tuplet) {
    1312            0 :                 std::list<EngravingObject*> tl = tuplet->linkList();
    1313            0 :                 for (EngravingObject* e : rest->linkList()) {
    1314            0 :                     DurationElement* de = toDurationElement(e);
    1315            0 :                     for (EngravingObject* ee : tl) {
    1316            0 :                         Tuplet* t = toTuplet(ee);
    1317            0 :                         if (t->score() == de->score() && t->track() == de->track()) {
    1318            0 :                             de->setTuplet(t);
    1319            0 :                             t->add(de);
    1320            0 :                             break;
    1321              :                         }
    1322              :                     }
    1323            0 :                 }
    1324            0 :             }
    1325              :             //select(rest, SelectType::SINGLE, 0);
    1326              :         } else {
    1327              :             // remove segment if empty
    1328            1 :             Segment* seg = chord->segment();
    1329            1 :             if (seg->empty()) {
    1330            0 :                 undoRemoveElement(seg);
    1331              :             }
    1332              :         }
    1333              :     }
    1334           11 :     break;
    1335              : 
    1336            3 :     case ElementType::MEASURE_REPEAT:
    1337              :     {
    1338            3 :         MeasureRepeat* mr = toMeasureRepeat(el);
    1339            3 :         removeChordRest(mr, false);
    1340            3 :         Segment* segment = mr->segment();
    1341            3 :         Rest* rest = Factory::createRest(segment);
    1342            3 :         rest->setDurationType(DurationType::V_MEASURE);
    1343            3 :         rest->setTicks(mr->measure()->stretchedLen(mr->staff()));
    1344            3 :         rest->setTrack(mr->track());
    1345            3 :         undoAddCR(rest, segment->measure(), segment->tick());
    1346              : 
    1347              :         // tell measures they're not part of measure repeat group anymore
    1348            3 :         Measure* m = mr->firstMeasureOfGroup();
    1349            9 :         for (int i = 1; i <= mr->numMeasures(); ++i) {
    1350            6 :             undoChangeMeasureRepeatCount(m, 0, mr->staffIdx());
    1351              :             // don't remove grouping if within measure repeat group on another staff
    1352            6 :             bool otherStavesStillNeedGroup = false;
    1353           15 :             for (size_t staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
    1354           12 :                 if (m->isMeasureRepeatGroupWithNextM(staffIdx) && staffIdx != mr->staffIdx()) {
    1355            3 :                     otherStavesStillNeedGroup = true;
    1356            3 :                     break;
    1357              :                 }
    1358              :             }
    1359            6 :             if (!otherStavesStillNeedGroup) {
    1360            3 :                 m->undoSetNoBreak(false);
    1361              :             }
    1362            6 :             m = m->nextMeasure();
    1363              :         }
    1364              :     }
    1365              :     // fall through
    1366              :     case ElementType::MMREST:
    1367              :     case ElementType::REST:
    1368              :         //
    1369              :         // only allow for voices != 0
    1370              :         //    e.g. voice 0 rests cannot be removed
    1371              :         //
    1372              :     {
    1373            6 :         Rest* rest = toRest(el);
    1374            6 :         if (rest->tuplet() && rest->tuplet()->elements().empty()) {
    1375            0 :             undoRemoveElement(rest->tuplet());
    1376              :         }
    1377            6 :         if ((el->voice() != 0) && !rest->tuplet()) {
    1378            3 :             rest->undoChangeProperty(Pid::GAP, true);
    1379            3 :             rest->undoChangeProperty(Pid::BEAM_MODE, BeamMode::AUTO);
    1380            9 :             for (EngravingObject* r : el->linkList()) {
    1381            6 :                 Rest* rr = toRest(r);
    1382            6 :                 if (rr->track() % VOICES) {
    1383            6 :                     rr->undoChangeProperty(Pid::GAP, true);
    1384              :                 }
    1385            3 :             }
    1386              : 
    1387              :             // delete them really when only gap rests are in the actual measure.
    1388            3 :             Measure* m = toRest(el)->measure();
    1389            3 :             track_idx_t track = el->track();
    1390            3 :             if (m->isOnlyGapRests(track) && !el->staff()->trackHasLinksInVoiceZero(track)) {
    1391              :                 static const SegmentType st { SegmentType::ChordRest };
    1392            0 :                 for (const Segment* s = m->first(st); s; s = s->next(st)) {
    1393            0 :                     EngravingItem* del = s->element(track);
    1394            0 :                     if (s->segmentType() != st || !del) {
    1395            0 :                         continue;
    1396              :                     }
    1397            0 :                     if (toRest(del)->isGap()) {
    1398            0 :                         undoRemoveElement(del);
    1399              :                     }
    1400              :                 }
    1401            0 :                 checkSpanner(m->tick(), m->endTick());
    1402              :             } else {
    1403              :                 // check if the other rest could be combined
    1404            3 :                 Segment* s = toRest(el)->segment();
    1405              : 
    1406            3 :                 std::vector<Rest*> rests;
    1407              :                 // find previous segment with cr in this track
    1408            3 :                 EngravingItem* pe = 0;
    1409            3 :                 for (Segment* ps = s->prev(SegmentType::ChordRest); ps; ps = ps->prev(SegmentType::ChordRest)) {
    1410            2 :                     EngravingItem* elm = ps->element(track);
    1411            2 :                     if (elm && elm->isRest() && toRest(elm)->isGap()) {
    1412            0 :                         pe = el;
    1413            0 :                         rests.push_back(toRest(elm));
    1414            2 :                     } else if (elm) {
    1415            2 :                         break;
    1416              :                     }
    1417              :                 }
    1418              :                 // find next segment with cr in this track
    1419              :                 Segment* ns;
    1420            3 :                 EngravingItem* ne = 0;
    1421            4 :                 for (ns = s->next(SegmentType::ChordRest); ns; ns = ns->next(SegmentType::ChordRest)) {
    1422            4 :                     EngravingItem* elm = ns->element(track);
    1423            4 :                     if (elm && elm->isRest() && toRest(elm)->isGap()) {
    1424            1 :                         ne = elm;
    1425            1 :                         rests.push_back(toRest(elm));
    1426            3 :                     } else if (elm) {
    1427            3 :                         break;
    1428              :                     }
    1429              :                 }
    1430              : 
    1431            3 :                 Fraction stick = pe ? pe->tick() : s->tick();
    1432            3 :                 Fraction ticks = { 0, 1 };
    1433              : 
    1434            3 :                 if (ne) {
    1435            1 :                     ticks = ne->tick() - stick + toRest(ne)->actualTicks();
    1436            2 :                 } else if (ns) {
    1437            2 :                     ticks = ns->tick() - stick;
    1438              :                 } else {
    1439            0 :                     ticks = m->endTick() - stick;
    1440              :                 }
    1441              : 
    1442            3 :                 if (ticks != m->ticks() && ticks != s->ticks()) {
    1443            1 :                     undoRemoveElement(rest);
    1444            2 :                     for (Rest* r : rests) {
    1445            1 :                         undoRemoveElement(r);
    1446              :                     }
    1447              : 
    1448            1 :                     Fraction f = ticks * el->staff()->timeStretch(el->tick());
    1449              : 
    1450            1 :                     std::vector<TDuration> dList = toDurationList(f, true);
    1451            1 :                     if (dList.empty()) {
    1452            0 :                         break;
    1453              :                     }
    1454              : 
    1455            1 :                     Fraction curTick = stick;
    1456            2 :                     for (const TDuration& d : dList) {
    1457            1 :                         Rest* rr = Factory::createRest(this->dummy());
    1458            1 :                         rr->setTicks(d.fraction());
    1459            1 :                         rr->setDurationType(d);
    1460            1 :                         rr->setTrack(track);
    1461            1 :                         rr->setGap(true);
    1462            1 :                         undoAddCR(rr, m, curTick);
    1463            1 :                         curTick += rr->actualTicks();
    1464              :                     }
    1465            1 :                 }
    1466            3 :             }
    1467              :             // Set input position
    1468              :             // TODO If deleted element is last of a sequence, use prev?
    1469            3 :             if (noteEntryMode()) {
    1470            0 :                 Navigation::move(score(), u"prev-chord");
    1471              :             }
    1472              :         }
    1473              :     }
    1474            6 :     break;
    1475              : 
    1476            2 :     case ElementType::ACCIDENTAL:
    1477            2 :         if (el->ownershipParent()->isNote()) {
    1478            2 :             EditNote::changeAccidental(this, toNote(el->ownershipParent()), AccidentalType::NONE);
    1479              :         } else {
    1480            0 :             undoRemoveElement(el);
    1481              :         }
    1482            2 :         break;
    1483              : 
    1484            9 :     case ElementType::BAR_LINE: {
    1485            9 :         BarLine* bl = toBarLine(el);
    1486            9 :         Segment* s = bl->segment();
    1487            9 :         Measure* m = s->measure();
    1488            9 :         if (s->isBeginBarLineType() || s->isBarLineType()) {
    1489            0 :             undoRemoveElement(el);
    1490              :         } else {
    1491            9 :             if (bl->barLineType() == BarLineType::START_REPEAT) {
    1492            0 :                 Measure* m2 = m->isMMRest() ? m->mmRestFirst() : m;
    1493            0 :                 for (Score* lscore : score()->scoreList()) {
    1494            0 :                     Measure* lmeasure = lscore->tick2measure(m2->tick());
    1495            0 :                     if (lmeasure) {
    1496            0 :                         lmeasure->undoChangeProperty(Pid::REPEAT_START, false);
    1497            0 :                         lmeasure->undoChangeProperty(Pid::REPEAT_COUNT, 2);
    1498              :                     }
    1499            0 :                 }
    1500            0 :                 if (!bl->generated()) {
    1501            0 :                     undoRemoveElement(el);
    1502              :                 }
    1503            9 :             } else if (bl->barLineType() == BarLineType::END_REPEAT) {
    1504            0 :                 Measure* m2 = m->isMMRest() ? m->mmRestLast() : m;
    1505            0 :                 for (Score* lscore : score()->scoreList()) {
    1506            0 :                     Measure* lmeasure = lscore->tick2measure(m2->tick());
    1507            0 :                     if (lmeasure) {
    1508            0 :                         lmeasure->undoChangeProperty(Pid::REPEAT_END, false);
    1509            0 :                         lmeasure->undoChangeProperty(Pid::REPEAT_COUNT, 2);
    1510              :                     }
    1511            0 :                 }
    1512            0 :                 if (!bl->generated()) {
    1513            0 :                     undoRemoveElement(el);
    1514              :                 }
    1515              :             } else {
    1516            9 :                 bl->undoChangeProperty(Pid::BARLINE_TYPE, PropertyValue::fromValue(BarLineType::NORMAL));
    1517              :             }
    1518              :         }
    1519              :     }
    1520            9 :     break;
    1521              : 
    1522            2 :     case ElementType::TUPLET:
    1523            2 :         cmdDeleteTuplet(toTuplet(el), true);
    1524            2 :         break;
    1525              : 
    1526            0 :     case ElementType::MEASURE: {
    1527            0 :         Measure* m = toMeasure(el);
    1528            0 :         undoRemoveMeasures(m, m);
    1529            0 :         undoInsertTime(m->tick(), -(m->endTick() - m->tick()));
    1530              :     }
    1531            0 :     break;
    1532              : 
    1533            0 :     case ElementType::BRACKET:
    1534            0 :         EditStaffBrackets::undoRemoveBracket(this, toBracket(el));
    1535            0 :         break;
    1536            0 :     case ElementType::CLEF:
    1537              :     {
    1538            0 :         Clef* clef = toClef(el);
    1539            0 :         Measure* m = clef->measure();
    1540            0 :         if (clef->generated()) {
    1541              :             // find the real clef if this is a cautionary one
    1542            0 :             if (m && m->prevMeasure()) {
    1543            0 :                 Fraction tick = m->tick();
    1544            0 :                 m = m->prevMeasure();
    1545            0 :                 Segment* s = m->findSegment(SegmentType::Clef, tick);
    1546            0 :                 if (s && s->element(clef->track())) {
    1547            0 :                     clef = toClef(s->element(clef->track()));
    1548              :                 }
    1549              :             }
    1550              :         }
    1551            0 :         undoRemoveElement(clef);
    1552              :     }
    1553            0 :     break;
    1554              : 
    1555            0 :     case ElementType::MEASURE_NUMBER:
    1556              :     {
    1557            0 :         Measure* mea = toMeasure(el->ownershipParent());
    1558            0 :         switch (mea->measureNumberMode()) {
    1559              :         // If the user tries to remove an automatically generated measure number,
    1560              :         // we should force the measure not to show any measure number
    1561            0 :         case MeasureNumberMode::AUTO:
    1562            0 :             mea->undoChangeProperty(Pid::MEASURE_NUMBER_MODE, static_cast<int>(MeasureNumberMode::HIDE));
    1563            0 :             break;
    1564              : 
    1565              :         // If the user tries to remove a measure number that he added manually,
    1566              :         // then we should set the MeasureNumberMode to AUTO only if will not show if set to auto.
    1567              :         // If after setting the MeasureNumberMode to AUTO, the measure number still shows,
    1568              :         // We need to force the measure to hide its measure number.
    1569            0 :         case MeasureNumberMode::SHOW:
    1570            0 :             if (mea->showMeasureNumberInAutoMode()) {
    1571            0 :                 mea->undoChangeProperty(Pid::MEASURE_NUMBER_MODE, static_cast<int>(MeasureNumberMode::HIDE));
    1572              :             } else {
    1573            0 :                 mea->undoChangeProperty(Pid::MEASURE_NUMBER_MODE, static_cast<int>(MeasureNumberMode::AUTO));
    1574              :             }
    1575            0 :             break;
    1576            0 :         case MeasureNumberMode::HIDE:
    1577            0 :             break;
    1578              :         }
    1579              :     }
    1580            0 :     break;
    1581            0 :     case ElementType::LYRICSLINE_SEGMENT:
    1582              :     {
    1583            0 :         el = toLyricsLineSegment(el)->lyricsLine();
    1584            0 :         Lyrics* lyrics = toLyricsLine(el)->lyrics();
    1585            0 :         undoRemoveElement(el);
    1586            0 :         lyrics->undoResetProperty(Pid::LYRIC_TICKS);
    1587            0 :         lyrics->undoResetProperty(Pid::SYLLABIC);
    1588              : 
    1589            0 :         break;
    1590              :     }
    1591           10 :     case ElementType::OTTAVA_SEGMENT:
    1592              :     case ElementType::HAIRPIN_SEGMENT:
    1593              :     case ElementType::TRILL_SEGMENT:
    1594              :     case ElementType::VIBRATO_SEGMENT:
    1595              :     case ElementType::TEXTLINE_SEGMENT:
    1596              :     case ElementType::VOLTA_SEGMENT:
    1597              :     case ElementType::SLUR_SEGMENT:
    1598              :     case ElementType::TIE_SEGMENT:
    1599              :     case ElementType::LAISSEZ_VIB_SEGMENT:
    1600              :     case ElementType::PARTIAL_TIE_SEGMENT:
    1601              :     case ElementType::PARTIAL_LYRICSLINE_SEGMENT:
    1602              :     case ElementType::PEDAL_SEGMENT:
    1603              :     case ElementType::GLISSANDO_SEGMENT:
    1604              :     case ElementType::NOTELINE_SEGMENT:
    1605              :     case ElementType::LET_RING_SEGMENT:
    1606              :     case ElementType::GRADUAL_TEMPO_CHANGE_SEGMENT:
    1607              :     case ElementType::PALM_MUTE_SEGMENT:
    1608              :     case ElementType::WHAMMY_BAR_SEGMENT:
    1609              :     case ElementType::RASGUEADO_SEGMENT:
    1610              :     case ElementType::HARMONIC_MARK_SEGMENT:
    1611              :     case ElementType::PICK_SCRAPE_SEGMENT:
    1612              :     case ElementType::GUITAR_BEND_SEGMENT:
    1613              :     case ElementType::HAMMER_ON_PULL_OFF_SEGMENT:
    1614              :     {
    1615           10 :         el = toSpannerSegment(el)->spanner();
    1616           10 :         if (el->isTie()) {
    1617           10 :             Tie* tie = toTie(el);
    1618           10 :             if (tie->tieJumpPoints()) {
    1619            6 :                 tie->undoRemoveTiesFromJumpPoints();
    1620              :             }
    1621           10 :             if (tie->jumpPoint()) {
    1622            8 :                 tie->updateStartTieOnRemoval();
    1623              :             }
    1624              :         }
    1625           10 :         undoRemoveElement(el);
    1626              : 
    1627           10 :         if (el->isGuitarBend()) {
    1628            0 :             GuitarBend* bend = toGuitarBend(el);
    1629            0 :             if (bend->bendType() == GuitarBendType::PRE_BEND || bend->bendType() == GuitarBendType::PRE_DIVE) {
    1630            0 :                 deleteItem(bend->startNote());
    1631              :             }
    1632              :         }
    1633              :     }
    1634           10 :     break;
    1635              : 
    1636            0 :     case ElementType::TAPPING_HALF_SLUR_SEGMENT:
    1637              :     {
    1638            0 :         TappingHalfSlur* halfSlur = toTappingHalfSlur(toTappingHalfSlurSegment(el)->spanner());
    1639            0 :         Tapping* tapping = toTapping(halfSlur->parent());
    1640            0 :         undoRemoveElement(tapping);
    1641            0 :         break;
    1642              :     }
    1643              : 
    1644            0 :     case ElementType::HAMMER_ON_PULL_OFF_TEXT:
    1645            0 :         undoRemoveHopoText(toHammerOnPullOffText(el));
    1646            0 :         break;
    1647              : 
    1648            1 :     case ElementType::STEM_SLASH:                   // cannot delete this elements
    1649              :     case ElementType::HOOK:
    1650              :     case ElementType::GUITAR_BEND_TEXT:
    1651              :     case ElementType::GUITAR_BEND_HOLD_SEGMENT:
    1652            1 :         LOGD("cannot remove %s", el->typeName());
    1653            1 :         break;
    1654              : 
    1655            0 :     case ElementType::TEXT:
    1656            0 :         if ((el->ownershipParent() && el->ownershipParent()->isTBox()) || el->isTBox()) {
    1657            0 :             el->undoChangeProperty(Pid::TEXT, String());
    1658              :         } else {
    1659            0 :             undoRemoveElement(el);
    1660              :         }
    1661            0 :         break;
    1662            0 :     case ElementType::PLAY_COUNT_TEXT: {
    1663            0 :         PlayCountText* pct = toPlayCountText(el);
    1664            0 :         pct->barline()->undoChangeProperty(Pid::PLAY_COUNT_TEXT_SETTING, AutoCustomHide::HIDE);
    1665            0 :     } break;
    1666            1 :     case ElementType::INSTRUMENT_CHANGE:
    1667              :     {
    1668            1 :         InstrumentChange* ic = static_cast<InstrumentChange*>(el);
    1669            1 :         Fraction tickStart = ic->segment()->tick();
    1670            1 :         Part* part = ic->part();
    1671            1 :         Interval oldV = part->staff(0)->transpose(tickStart);
    1672            1 :         undoRemoveElement(el);
    1673            1 :         if (tickStart != Fraction(0, 1)) {
    1674            1 :             for (KeySig* keySig : ic->keySigs()) {
    1675            0 :                 deleteItem(keySig);
    1676            1 :             }
    1677              :         }
    1678            1 :         for (Clef* clef : ic->clefs()) {
    1679            0 :             deleteItem(clef);
    1680            1 :         }
    1681            1 :         if (part->staff(0)->transpose(tickStart) != oldV) {
    1682            1 :             auto i = part->instruments().upper_bound(tickStart.ticks());
    1683            1 :             Fraction tickEnd;
    1684            1 :             if (i == part->instruments().end()) {
    1685            1 :                 tickEnd = Fraction(-1, 1);
    1686              :             } else {
    1687            0 :                 tickEnd = Fraction::fromTicks(i->first);
    1688              :             }
    1689            1 :             Transpose::transpositionChanged(tx, this, part, oldV, tickStart, tickEnd);
    1690              :         }
    1691              :     }
    1692            1 :     break;
    1693            0 :     case ElementType::SYSTEM_LOCK_INDICATOR:
    1694              :     {
    1695            0 :         const RangeLock* systemLock = toSystemLockIndicator(el)->systemLock();
    1696            0 :         EditSystemLocks::undoRemoveSystemLock(tx, systemLock);
    1697              :     }
    1698            0 :     break;
    1699            0 :     case ElementType::PAGE_LOCK_INDICATOR:
    1700              :     {
    1701            0 :         const RangeLock* pageLock = toPageLockIndicator(el)->pageLock();
    1702            0 :         EditPageLocks::undoRemovePageLock(tx, pageLock);
    1703              :     }
    1704            0 :     break;
    1705            0 :     case ElementType::PARENTHESIS: {
    1706            0 :         Parenthesis* paren = toParenthesis(el);
    1707              :         // Use EditChord::removeChordParentheses when parent is a chord
    1708            0 :         if (el->parent() && el->parent()->isChord()) {
    1709            0 :             Chord* chord = toChord(el->parent());
    1710            0 :             NoteParenthesisInfo* parenInfo = chord->findNoteParenthesisInfo(paren);
    1711            0 :             IF_ASSERT_FAILED(parenInfo) {
    1712            0 :                 LOGD() << "deleteItem: This parenthesis does not belong to this chord";
    1713            0 :                 return;
    1714              :             }
    1715            0 :             for (Note* note : parenInfo->notes()) {
    1716            0 :                 note->undoChangeProperty(Pid::HIDE_GENERATED_PARENTHESES, true);
    1717            0 :                 note->undoChangeProperty(Pid::HAS_PARENTHESES, ParenthesesMode::NONE);
    1718              :             }
    1719            0 :             EditChord::removeChordParentheses(chord, parenInfo->notes());
    1720              :         } else {
    1721            0 :             undoRemoveElement(el);
    1722              :         }
    1723              :     }
    1724            0 :     break;
    1725           23 :     default:
    1726           23 :         undoRemoveElement(el);
    1727           23 :         break;
    1728              :     }
    1729              : }
    1730              : 
    1731              : //---------------------------------------------------------
    1732              : //   deleteMeasures
    1733              : //---------------------------------------------------------
    1734              : 
    1735           20 : void Score::deleteMeasures(MeasureBase* mbStart, MeasureBase* mbEnd, bool preserveTies)
    1736              : {
    1737           20 :     select(0, SelectType::SINGLE, 0);
    1738              : 
    1739           20 :     if (mbEnd->isMeasure()) {
    1740           20 :         Measure* mbEndMeasure = toMeasure(mbEnd);
    1741           20 :         if (mbEndMeasure->isMMRest()) {
    1742            0 :             mbEnd = mbEndMeasure->mmRestLast();
    1743              :         }
    1744              :     }
    1745              : 
    1746           20 :     if (!preserveTies) {
    1747              :         // we're not preserving slurs and ties where they are, so the endpoints need to be either
    1748              :         // moved or the slur has to be deleted
    1749            9 :         reconnectSlurs(mbStart, mbEnd);
    1750              :     }
    1751              : 
    1752              :     // get the last deleted timesig & keysig in order to restore after deletion
    1753           20 :     std::map<staff_idx_t, TimeSig*> lastDeletedTimeSigs;
    1754              : 
    1755           31 :     for (MeasureBase* mb = mbEnd;; mb = mb->prev()) {
    1756           31 :         if (mb->isMeasure()) {
    1757           31 :             Measure* m = toMeasure(mb);
    1758           31 :             Segment* sts = m->findSegment(SegmentType::TimeSig, m->tick());
    1759              : 
    1760           31 :             if (sts) {
    1761           18 :                 for (staff_idx_t staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
    1762            9 :                     if (!muse::contains(lastDeletedTimeSigs, staffIdx)) {
    1763            9 :                         if (TimeSig* ts = toTimeSig(sts->element(staffIdx * VOICES))) {
    1764            9 :                             lastDeletedTimeSigs.insert({ staffIdx, ts });
    1765              :                         }
    1766              :                     }
    1767              :                 }
    1768              :             }
    1769              : 
    1770           31 :             if (lastDeletedTimeSigs.size() == nstaves()) {
    1771            9 :                 break;
    1772              :             }
    1773              :         }
    1774           22 :         if (mb == mbStart) {
    1775           11 :             break;
    1776              :         }
    1777           11 :     }
    1778              : 
    1779           20 :     std::vector<KeySigEvent> lastDeletedKeySigEvents;
    1780              : 
    1781           40 :     for (Staff* staff : score()->staves()) {
    1782           20 :         KeySigEvent kse = staff->keySigEvent(mbEnd->tick());
    1783           20 :         lastDeletedKeySigEvents.push_back(kse);
    1784           20 :     }
    1785              : 
    1786           20 :     Fraction startTick = mbStart->tick();
    1787           20 :     Fraction endTick   = mbEnd->tick();
    1788              : 
    1789           40 :     for (Score* score : scoreList()) {
    1790           20 :         Measure* startMeasure = score->tick2measure(startTick);
    1791           20 :         Measure* endMeasure = score->tick2measure(endTick);
    1792              : 
    1793           20 :         score->undoRemoveMeasures(startMeasure, endMeasure, preserveTies);
    1794              : 
    1795              :         // adjust views
    1796           20 :         Measure* focusOn = startMeasure->prevMeasure() ? startMeasure->prevMeasure() : score->firstMeasure();
    1797           20 :         for (MuseScoreView* v : score->m_viewer) {
    1798            0 :             v->adjustCanvasPosition(focusOn);
    1799              :         }
    1800              : 
    1801              :         // insert correct timesig after deletion
    1802           20 :         Measure* mBeforeSel = startMeasure->prevMeasure();
    1803           20 :         Measure* mAfterSel  = mBeforeSel ? mBeforeSel->nextMeasure() : score->firstMeasure();
    1804           20 :         if (mAfterSel) {
    1805           14 :             Segment* s = mAfterSel->findSegment(SegmentType::TimeSig, mAfterSel->tick());
    1806              : 
    1807           28 :             for (staff_idx_t staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
    1808           14 :                 if (s && s->element(staffIdx * VOICES)) {
    1809            0 :                     continue;
    1810              :                 }
    1811              : 
    1812           14 :                 TimeSig* lastDeletedForThisStaff = muse::value(lastDeletedTimeSigs, staffIdx, nullptr);
    1813           14 :                 if (!lastDeletedForThisStaff) {
    1814            9 :                     continue;
    1815              :                 }
    1816              : 
    1817            5 :                 if (mBeforeSel) {
    1818            0 :                     const Staff* staff = this->staff(staffIdx);
    1819            0 :                     if (!staff) {
    1820            0 :                         continue;
    1821              :                     }
    1822              : 
    1823            0 :                     const TimeSig* timeSig = staff->timeSig(mBeforeSel->tick());
    1824            0 :                     if (!timeSig || timeSig->sig() == lastDeletedForThisStaff->sig()) {
    1825            0 :                         continue;
    1826              :                     }
    1827              :                 }
    1828              : 
    1829            5 :                 if (!s) {
    1830            5 :                     s = mAfterSel->undoGetSegment(SegmentType::TimeSig, mAfterSel->tick());
    1831              :                 }
    1832              : 
    1833            5 :                 TimeSig* nts = Factory::createTimeSig(s);
    1834            5 :                 nts->setTrack(staffIdx * VOICES);
    1835            5 :                 nts->setFrom(lastDeletedForThisStaff);
    1836            5 :                 nts->setStretch(nts->sig() / mAfterSel->timesig());
    1837            5 :                 score->undoAddElement(nts);
    1838              :             }
    1839              :         }
    1840              : 
    1841              :         // insert correct keysig if necessary
    1842           20 :         if (mAfterSel && !mBeforeSel) {
    1843            5 :             Segment* s = mAfterSel->undoGetSegment(SegmentType::KeySig, mAfterSel->tick());
    1844              : 
    1845            5 :             bool userCustomizedKeySig = false;
    1846           25 :             for (EngravingItem* e : s->elist()) {
    1847           20 :                 if (e && e->isKeySig() && !toKeySig(e)->generated()) {
    1848            0 :                     userCustomizedKeySig = true;
    1849            0 :                     break;
    1850              :                 }
    1851              :             }
    1852            5 :             if (userCustomizedKeySig) {
    1853            0 :                 continue;
    1854              :             }
    1855              : 
    1856            5 :             bool concertPitch = score->style().styleB(Sid::concertPitch);
    1857           10 :             for (Staff* staff : score->staves()) {
    1858            5 :                 Part* part = staff->part();
    1859            5 :                 Instrument* instrument = part->instrument(Fraction(0, 1));
    1860            5 :                 KeySigEvent nkse;
    1861            5 :                 staff_idx_t staffIdx = staff->idx();
    1862              : 
    1863            5 :                 if (instrument->useDrumset()) {
    1864            0 :                     nkse.setConcertKey(Key::C);
    1865            0 :                     nkse.setCustom(true);
    1866            0 :                     nkse.setMode(KeyMode::NONE);
    1867              :                 } else {
    1868            5 :                     nkse = lastDeletedKeySigEvents.at(staffIdx);
    1869            5 :                     if (!concertPitch && !nkse.isAtonal()) {
    1870            5 :                         Interval v = instrument->transpose();
    1871            5 :                         v.flip();
    1872            5 :                         nkse.setKey(Transpose::transposeKey(nkse.concertKey(), v, part->preferSharpFlat()));
    1873              :                     }
    1874              :                 }
    1875              : 
    1876            5 :                 KeySig* nks = (KeySig*)s->element(staff2track(staffIdx));
    1877            5 :                 if (!nks) {
    1878            5 :                     nks = Factory::createKeySig(s);
    1879            5 :                     nks->setTrack(staffIdx * VOICES);
    1880            5 :                     nks->setKeySigEvent(nkse);
    1881            5 :                     score->undoAddElement(nks);
    1882              :                 } else {
    1883            0 :                     nks->setGenerated(false);
    1884            0 :                     nks->setKeySigEvent(nkse);
    1885            0 :                     staff->setKey(mAfterSel->tick(), nkse);
    1886              :                 }
    1887            5 :             }
    1888              :         }
    1889           20 :     }
    1890              : 
    1891           20 :     undoInsertTime(mbStart->tick(), -(mbEnd->endTick() - mbStart->tick()));
    1892           20 :     m_is.setSegment(0);          // invalidate position
    1893           20 : }
    1894              : 
    1895            9 : void Score::reconnectSlurs(MeasureBase* mbStart, MeasureBase* mbEnd)
    1896              : {
    1897              :     // Reconnect or remove slurs that intersect with these deleted measures
    1898            9 :     Fraction sTick = mbStart->tick();
    1899            9 :     Fraction eTick = mbEnd->tick();
    1900           10 :     for (auto overlappingSlur : spannerMap().findOverlapping(sTick.ticks(), eTick.ticks())) {
    1901            1 :         Spanner* sp = overlappingSlur.value;
    1902            1 :         if (!sp->isSlur()) {
    1903            1 :             continue;
    1904              :         }
    1905            0 :         ChordRest* cr1 = sp->startCR();
    1906            0 :         ChordRest* cr2 = sp->endCR();
    1907            0 :         if (!cr1 || !cr2) {
    1908              :             // this is an invalid slur
    1909            0 :             continue;
    1910              :         }
    1911            0 :         MeasureBase* m = mbStart;
    1912            0 :         bool adjust1 = false;
    1913            0 :         bool adjust2 = false;
    1914            0 :         Measure* measure1 = cr1->measure();
    1915            0 :         Measure* measure2 = cr2->measure();
    1916              :         do {
    1917            0 :             adjust1 = adjust1 ? true : measure1 == m;
    1918            0 :             adjust2 = adjust2 ? true : measure2 == m;
    1919            0 :             if (m == mbEnd) {
    1920            0 :                 break;
    1921              :             } else {
    1922            0 :                 m = m->next();
    1923              :             }
    1924            0 :         } while (m && !(adjust1 && adjust2));
    1925            0 :         if (adjust1 && adjust2) {
    1926              :             // if both endpoints of this slur are inside this deleted range, remove the slur.
    1927            0 :             undoRemoveElement(sp);
    1928            0 :             continue;
    1929            0 :         } else if (adjust1) {
    1930              :             // endpoint 1 is in deleted range. move endpoint forward to first non-deleted CR after range
    1931            0 :             Measure* mNext = mbEnd->next() && mbEnd->next()->isMeasure() ? toMeasure(mbEnd->next()) : nullptr;
    1932            0 :             if (!mNext) {
    1933            0 :                 undoRemoveElement(sp);
    1934            0 :                 continue;
    1935              :             }
    1936            0 :             ChordRest* firstAvailableCr = nullptr;
    1937              :             // search for last cr in this voice
    1938            0 :             Segment* seg = mNext->first(SegmentType::ChordRest);
    1939            0 :             track_idx_t spTrack = sp->track();
    1940            0 :             ChordRest* spEndCr = sp->endCR();
    1941            0 :             Fraction spEndCrTick = spEndCr->tick();
    1942            0 :             while (seg) {
    1943            0 :                 firstAvailableCr = seg->cr(spTrack);
    1944            0 :                 if (!firstAvailableCr || !firstAvailableCr->isChord()) {
    1945            0 :                     seg = seg->next(SegmentType::ChordRest);
    1946            0 :                     continue;
    1947              :                 }
    1948            0 :                 auto gracesBefore = toChord(firstAvailableCr)->graceNotesBefore();
    1949            0 :                 if (firstAvailableCr->tick() > spEndCrTick) {
    1950              :                     // we would create a negative-length slur. remove it entirely.
    1951            0 :                     undoRemoveElement(sp);
    1952            0 :                     break;
    1953            0 :                 } else if (spEndCr == firstAvailableCr) {
    1954              :                     // this would be a zero-length slur, but a possibility exists that this cr has grace after
    1955            0 :                     if (gracesBefore.empty()) {
    1956            0 :                         undoRemoveElement(sp);
    1957            0 :                         break;
    1958              :                     }
    1959              :                 }
    1960            0 :                 if (!gracesBefore.empty()) {
    1961            0 :                     firstAvailableCr = gracesBefore.front();
    1962              :                 }
    1963            0 :                 Spanner* newSp = toSpanner(sp->clone());
    1964            0 :                 newSp->setStartElement(firstAvailableCr);
    1965            0 :                 newSp->setTick(firstAvailableCr->tick());
    1966            0 :                 undoChangeElement(sp, newSp);
    1967            0 :                 break;
    1968            0 :             }
    1969            0 :         } else if (adjust2) {
    1970              :             // endpoint 2 is in deleted range. move endpoint backward to last non-deleted CR before range
    1971            0 :             Measure* mPrev = mbStart->prev() && mbStart->prev()->isMeasure() ? toMeasure(mbStart->prev()) : nullptr;
    1972            0 :             if (!mPrev) {
    1973            0 :                 undoRemoveElement(sp);
    1974            0 :                 continue;
    1975              :             }
    1976            0 :             ChordRest* firstAvailableCr = nullptr;
    1977              :             // search for last cr in this voice
    1978            0 :             Segment* seg = mPrev->last();
    1979            0 :             if (seg->segmentType() != SegmentType::ChordRest) {
    1980            0 :                 seg = seg->prev(SegmentType::ChordRest);
    1981              :             }
    1982            0 :             ChordRest* spStartCr = sp->startCR();
    1983            0 :             Fraction spStartCrTick = spStartCr->tick();
    1984            0 :             while (seg) {
    1985            0 :                 firstAvailableCr = seg->cr(sp->track2());
    1986            0 :                 if (!firstAvailableCr || !firstAvailableCr->isChord()) {
    1987            0 :                     seg = seg->prev(SegmentType::ChordRest);
    1988            0 :                     continue;
    1989              :                 }
    1990            0 :                 auto gracesAfter = toChord(firstAvailableCr)->graceNotesAfter();
    1991            0 :                 if (firstAvailableCr->tick() < spStartCrTick) {
    1992              :                     // we would create a negative-length slur. remove it entirely.
    1993            0 :                     undoRemoveElement(sp);
    1994            0 :                     break;
    1995            0 :                 } else if (spStartCr == toChordRest(firstAvailableCr)) {
    1996              :                     // this would be a zero-length slur, but a possibility exists that this cr has grace after
    1997            0 :                     if (gracesAfter.empty()) {
    1998            0 :                         undoRemoveElement(sp);
    1999            0 :                         break;
    2000              :                     }
    2001              :                 }
    2002            0 :                 if (!gracesAfter.empty()) {
    2003            0 :                     firstAvailableCr = gracesAfter.back();
    2004              :                 }
    2005            0 :                 Spanner* newSp = toSpanner(sp->clone());
    2006            0 :                 newSp->setEndElement(firstAvailableCr);
    2007            0 :                 newSp->setTick2(firstAvailableCr->tick());
    2008            0 :                 undoChangeElement(sp, newSp);
    2009            0 :                 break;
    2010            0 :             }
    2011              :         } else {
    2012              :             // both of the endpoints are outside of the deleted range. there is no reason to change or delete anything
    2013              :         }
    2014              :     }
    2015            9 : }
    2016              : 
    2017              : //---------------------------------------------------------
    2018              : //   deleteSpannersFromRange
    2019              : ///   Deletes spanners in the given range that match the
    2020              : ///   given selection filter.
    2021              : //---------------------------------------------------------
    2022              : 
    2023           61 : void Score::deleteOrShortenOutSpannersFromRange(const Fraction& t1, const Fraction& t2, track_idx_t track1, track_idx_t track2,
    2024              :                                                 const SelectionFilter& filter)
    2025              : {
    2026              :     static const std::set<ElementType> SPANNER_TYPES_TO_SHORTEN_OUT {
    2027              :         ElementType::HAIRPIN,
    2028              :         ElementType::OTTAVA,
    2029              :         ElementType::TRILL,
    2030              :         ElementType::VIBRATO,
    2031           63 :     };
    2032              : 
    2033              :     // undoRemoveElement and undoChangeProperty also handle linked elements, so we need
    2034              :     // to make sure each linked group is handled only once if multiple elements per linked
    2035              :     // group are found in the range, for example in the case of linked staves.
    2036           61 :     std::unordered_set<EngravingObject*> handledMainElements;
    2037              : 
    2038           61 :     auto spanners = m_spanner.findOverlapping(t1.ticks(), t2.ticks() - 1);
    2039           94 :     for (auto i : spanners) {
    2040           33 :         Spanner* sp = i.value;
    2041           33 :         Fraction spStartTick = sp->tick();
    2042           33 :         Fraction spEndTick = sp->tick2();
    2043           33 :         const bool trackValid = sp->track() >= track1 && sp->track() < track2;
    2044           33 :         if (!trackValid || sp->isVolta() || sp->systemFlag()) {
    2045           15 :             continue;
    2046              :         }
    2047              : 
    2048           31 :         if (sp->links() && !handledMainElements.insert(sp->links()->mainElement()).second) {
    2049              :             // already handled this linked group
    2050            4 :             continue;
    2051              :         }
    2052              : 
    2053              :         // Special handling for grace notes...
    2054           27 :         const Chord* startChord = sp->startChord();
    2055           27 :         const bool startChordIsSelectableGrace = startChord && startChord->isGrace() && filter.canSelect(startChord);
    2056              : 
    2057           27 :         const Chord* endChord = sp->endChord();
    2058           27 :         const bool endChordIsSelectableGrace = endChord && endChord->isGrace() && filter.canSelect(endChord);
    2059              : 
    2060           27 :         if (startChordIsSelectableGrace || endChordIsSelectableGrace) {
    2061            6 :             undoRemoveElement(sp);
    2062            6 :             continue;
    2063              :         }
    2064              : 
    2065           21 :         if (!filter.canSelectVoice(sp->track()) || !filter.canSelect(sp)) {
    2066            3 :             continue;
    2067              :         }
    2068           29 :         if (spStartTick >= t1 && spStartTick < t2
    2069           29 :             && spEndTick > t1 && spEndTick <= t2) {
    2070            7 :             undoRemoveElement(sp);
    2071           14 :         } else if (sp->isSlur() && ((spStartTick >= t1 && spStartTick < t2)
    2072            3 :                                     || (spEndTick >= t1 && spEndTick < t2))) {
    2073            3 :             undoRemoveElement(sp);
    2074            8 :         } else if (muse::contains(SPANNER_TYPES_TO_SHORTEN_OUT, sp->type())) {
    2075            7 :             bool moveStart = spStartTick >= t1 && spStartTick < t2;
    2076            7 :             bool moveEnd = spEndTick > t1 && spEndTick <= t2;
    2077            7 :             if (moveStart) {
    2078            3 :                 Fraction tickDiff = t2 - spStartTick;
    2079            3 :                 sp->undoChangeProperty(Pid::SPANNER_TICK, t2);
    2080            3 :                 sp->undoChangeProperty(Pid::SPANNER_TICKS, sp->ticks() - tickDiff);
    2081            4 :             } else if (moveEnd) {
    2082            3 :                 Fraction tickDiff = spEndTick - t1;
    2083            3 :                 sp->undoChangeProperty(Pid::SPANNER_TICKS, sp->ticks() - tickDiff);
    2084              :             }
    2085              :         }
    2086              :     }
    2087           61 : }
    2088              : 
    2089         1113 : void Score::deleteSlursFromRange(const Fraction& t1, const Fraction& t2, track_idx_t trackStart, track_idx_t trackEnd,
    2090              :                                  const SelectionFilter& filter)
    2091              : {
    2092         1113 :     auto spanners = m_spanner.findOverlapping(t1.ticks(), t2.ticks() - 1);
    2093         1113 :     for (auto i : spanners) {
    2094            0 :         Spanner* sp = i.value;
    2095            0 :         Fraction spStartTick = sp->tick();
    2096            0 :         Fraction spEndTick = sp->tick2();
    2097            0 :         if (!sp->isSlur()) {
    2098            0 :             continue;
    2099              :         }
    2100            0 :         if (!filter.canSelectVoice(sp->track()) || !filter.canSelect(sp)) {
    2101            0 :             continue;
    2102              :         }
    2103              : 
    2104            0 :         if (sp->track() >= trackStart && sp->track() < trackEnd) {
    2105            0 :             if ((spStartTick >= t1 && spStartTick < t2)
    2106            0 :                 || (spEndTick >= t1 && spEndTick < t2)) {
    2107            0 :                 undoRemoveElement(sp);
    2108              :             }
    2109              :         }
    2110              :     }
    2111         1113 : }
    2112              : 
    2113              : //---------------------------------------------------------
    2114              : //   deleteAnnotationsFromRange
    2115              : ///   Deletes annotations in the given range that match the
    2116              : ///   given selection filter.
    2117              : //---------------------------------------------------------
    2118              : 
    2119           61 : void Score::deleteAnnotationsFromRange(Segment* s1, Segment* s2, track_idx_t track1, track_idx_t track2, const SelectionFilter& filter)
    2120              : {
    2121           61 :     if (!s1) {
    2122            0 :         return;
    2123              :     }
    2124           61 :     if (s2 && (*s2) < (*s1)) {
    2125            0 :         return;
    2126              :     }
    2127              : 
    2128          321 :     for (track_idx_t track = track1; track < track2; ++track) {
    2129          260 :         if (!filter.canSelectVoice(track)) {
    2130            2 :             continue;
    2131              :         }
    2132         2882 :         for (Segment* s = s1; s && s != s2; s = s->next1()) {
    2133         2624 :             const auto annotations = s->annotations(); // make a copy since we alter the list
    2134         2792 :             for (EngravingItem* annotation : annotations) {
    2135              :                 // skip if not included in selection (eg, filter)
    2136          168 :                 if (!filter.canSelect(annotation)) {
    2137          152 :                     continue;
    2138              :                 }
    2139           16 :                 if (!annotation->systemFlag() && annotation->track() == track) {
    2140            4 :                     deleteItem(annotation);
    2141              :                 }
    2142              :             }
    2143         2624 :         }
    2144              :     }
    2145              : }
    2146              : 
    2147          340 : void Score::deleteRangeAtTrack(std::vector<ChordRest*>& crsToSelect, const track_idx_t track, Segment* startSeg, const Fraction& endTick,
    2148              :                                Tuplet* currentTuplet, const SelectionFilter& filter, bool selectionContainsMultiNoteChords)
    2149              : {
    2150        11919 :     while (startSeg && !(startSeg->isChordRestType() && startSeg->cr(track))) {
    2151              :         // Range should always start at a ChordRest segment - find the next one for this track...
    2152        11579 :         startSeg = startSeg->next1(SegmentType::ChordRest);
    2153              :     }
    2154              : 
    2155          340 :     if (!startSeg) {
    2156          186 :         return;
    2157              :     }
    2158              : 
    2159          154 :     Fraction restStartTick = startSeg->tick();
    2160          154 :     Fraction restDuration;
    2161              : 
    2162              :     // When we find a deselected DurationElement - write rests up to the start of the deselected element (using the existing values for
    2163              :     // restStartTick and restDuration), skip the deselected element (move restStartTick forward), and reset restDuration...
    2164          238 :     const auto foundDeselected = [&](const DurationElement* deselectedElement) {
    2165          238 :         const std::vector<Rest*> rests = setRests(restStartTick, track, restDuration, /*useDots*/ !currentTuplet, currentTuplet);
    2166          238 :         crsToSelect.insert(crsToSelect.end(), rests.begin(), rests.end());
    2167          238 :         restStartTick = deselectedElement->endTick();
    2168          238 :         restDuration = Fraction();
    2169          238 :     };
    2170              : 
    2171         1101 :     for (Segment* s = startSeg; s && (s->tick() < endTick); s = s->next1()) {
    2172          947 :         EngravingItem* e = s->element(track);
    2173          947 :         if (!e) {
    2174          490 :             continue;
    2175              :         }
    2176              : 
    2177          833 :         if (s->isBreathType()) {
    2178            0 :             deleteItem(e);
    2179            0 :             continue;
    2180              :         }
    2181              : 
    2182          833 :         if (!s->isChordRestType()) {
    2183              :             // do not delete TimeSig/KeySig, it doesn't make sense to do it (except on full system)
    2184          222 :             if (!s->isTimeTickType() && !s->isTimeSigType() && !s->isKeySigType()
    2185          222 :                 && !s->isType(SegmentType::BarLineTypes) /*covers all barLine types*/) {
    2186            9 :                 undoRemoveElement(e);
    2187              :             }
    2188          111 :             continue;
    2189              :         }
    2190              : 
    2191          722 :         if (e->isMeasureRepeat()) {
    2192              :             // MeasureRepeat has its own special handling, so it is skipped here
    2193            0 :             foundDeselected(toDurationElement(e));
    2194            0 :             deleteItem(e);
    2195            0 :             continue;
    2196              :         }
    2197              : 
    2198          722 :         ChordRest* cr1 = toChordRest(e);
    2199              : 
    2200              :         // currentTuplet refers to the tuplet that we're currently performing a delete range "inside". It is possible that currentTuplet contains nested
    2201              :         // tuplets, in which case we should find the "top-most nested tuplet" (nextTuplet) and:
    2202              :         //      1. If it is selectable, delete it completely using cmdDeleteTuplet (deletes all elements contained in that tuplet)
    2203              :         //      2. If it is not selectable, call this method recursively "inside" that tuplet (nextTuplet becomes currentTuplet for the recursive call)
    2204              : 
    2205          722 :         Tuplet* nextTuplet = nullptr;
    2206          722 :         if (cr1->tuplet() && cr1->tuplet() != currentTuplet) {
    2207          102 :             nextTuplet = cr1->tuplet();
    2208          150 :             while (nextTuplet->tuplet() && nextTuplet->tuplet() != currentTuplet) {
    2209           48 :                 nextTuplet = nextTuplet->tuplet();
    2210              :             }
    2211              :         }
    2212              : 
    2213          722 :         if (nextTuplet) {
    2214              :             // The range of nextTuplet will be handled by cmdDeleteTuplet or the recursive call - skip it at this level of recursion...
    2215          102 :             const DurationElement* lastInTuplet = nextTuplet->elements().back();
    2216          123 :             while (lastInTuplet->isTuplet()) {
    2217           21 :                 lastInTuplet = toTuplet(lastInTuplet)->elements().back();
    2218              :             }
    2219          102 :             IF_ASSERT_FAILED(lastInTuplet->isChordRest()) {
    2220            0 :                 break;
    2221              :             }
    2222          102 :             s = toChordRest(lastInTuplet)->segment();
    2223              : 
    2224          102 :             if (filter.canSelectTuplet(nextTuplet, cr1->tick(), endTick, selectionContainsMultiNoteChords)) {
    2225           20 :                 restDuration += nextTuplet->ticks();
    2226           20 :                 cmdDeleteTuplet(nextTuplet, /*replaceWithRest*/ false);
    2227              :             } else {
    2228           82 :                 const Fraction recursionEnd = std::min(nextTuplet->endTick(), endTick);
    2229           82 :                 deleteRangeAtTrack(crsToSelect, track, cr1->segment(), recursionEnd, nextTuplet, filter, selectionContainsMultiNoteChords);
    2230           82 :                 foundDeselected(nextTuplet);
    2231              :             }
    2232              : 
    2233          102 :             if (!s) {
    2234            0 :                 break;
    2235              :             }
    2236              : 
    2237          102 :             continue;
    2238          102 :         }
    2239              : 
    2240          636 :         for (EngravingItem* elem : collectElementsAnchoredToChordRest(cr1)) {
    2241           16 :             if (!elem->isChord()) {
    2242            0 :                 if (filter.canSelect(elem)) {
    2243            0 :                     undoRemoveElement(elem);
    2244              :                 }
    2245            0 :                 continue;
    2246              :             }
    2247              :             // Special handling for grace notes...
    2248           16 :             const Chord* chord = toChord(elem);
    2249           16 :             IF_ASSERT_FAILED(chord->isGrace()) {
    2250            0 :                 continue;
    2251              :             }
    2252           16 :             std::unordered_set<EngravingItem*> anchoredToGrace = collectElementsAnchoredToChordRest(chord);
    2253           32 :             for (const Note* graceNote : chord->notes()) {
    2254           16 :                 const std::unordered_set<EngravingItem*> noteAnchored = collectElementsAnchoredToNote(graceNote, true, false);
    2255           16 :                 anchoredToGrace.insert(noteAnchored.begin(), noteAnchored.end());
    2256           16 :             }
    2257           16 :             for (EngravingItem* graceElem : anchoredToGrace) {
    2258            0 :                 if (filter.canSelect(graceElem)) {
    2259              :                     // Delete elements anchored to grace note...
    2260            0 :                     undoRemoveElement(graceElem);
    2261              :                 }
    2262              :             }
    2263           16 :             if (filter.canSelect(elem)) {
    2264              :                 // Delete grace note itself...
    2265            8 :                 undoRemoveElement(elem);
    2266              :             }
    2267          636 :         }
    2268              : 
    2269          620 :         if (cr1->isRestFamily()) {
    2270          163 :             if (cr1->selected()) {
    2271          163 :                 restDuration += cr1->ticks();
    2272          163 :                 removeChordRest(cr1, true);
    2273              :             } else {
    2274            0 :                 foundDeselected(cr1);
    2275              :             }
    2276          163 :             continue;
    2277              :         }
    2278              : 
    2279          457 :         Chord* chord = toChord(cr1);
    2280              : 
    2281          457 :         const std::vector<Note*> allNotes = chord->notes();
    2282          457 :         std::unordered_set<Note*> notesToRemove;
    2283         1213 :         for (size_t noteIdx = 0; noteIdx < allNotes.size(); ++noteIdx) {
    2284          756 :             Note* note = allNotes.at(noteIdx);
    2285          756 :             if (filter.canSelectNoteIdx(noteIdx, allNotes.size(), selectionContainsMultiNoteChords)) {
    2286          534 :                 notesToRemove.emplace(note);
    2287              :                 //! NOTE: Don't need to remove anchored elements - they won't survive the deletion of their parent note...
    2288          756 :                 continue;
    2289              :             }
    2290          222 :             for (EngravingItem* elem : collectElementsAnchoredToNote(note, true, false)) {
    2291            0 :                 if (!filter.canSelect(elem)) {
    2292            0 :                     continue;
    2293              :                 }
    2294            0 :                 if (!elem->isSpanner() || elem->isPartialTie() || elem->isLaissezVib()) {
    2295            0 :                     undoRemoveElement(elem);
    2296            0 :                     continue;
    2297              :                 }
    2298            0 :                 if (noteAnchoredSpannerIsInRange(toSpanner(elem), cr1->tick(), endTick)) {
    2299            0 :                     undoRemoveElement(elem);
    2300              :                 }
    2301          222 :             }
    2302          222 :             if (!filter.canSelectNoteIdx(noteIdx, allNotes.size(), selectionContainsMultiNoteChords)) {
    2303          222 :                 continue;
    2304              :             }
    2305            0 :             notesToRemove.emplace(note);
    2306              :         }
    2307              : 
    2308          457 :         if (notesToRemove.size() == allNotes.size()) {
    2309          301 :             restDuration += cr1->ticks();
    2310          301 :             removeChordRest(cr1, true);
    2311              :         } else {
    2312          314 :             for (Note* note : notesToRemove) {
    2313          158 :                 undoRemoveElement(note);
    2314              :             }
    2315          156 :             foundDeselected(cr1);
    2316              :         }
    2317          457 :     }
    2318              : 
    2319          154 :     const std::vector<Rest*> rests = setRests(restStartTick, track, restDuration, /*useDots*/ !currentTuplet, currentTuplet);
    2320          154 :     crsToSelect.insert(crsToSelect.end(), rests.begin(), rests.end());
    2321          154 : }
    2322              : 
    2323              : //---------------------------------------------------------
    2324              : //   deleteRange
    2325              : ///   Deletes elements in the given range that match the
    2326              : ///   given selection filter.
    2327              : ///   \return A chord/rest inside the selected range
    2328              : ///   that can be used to establish a selection after this
    2329              : ///   deletion operation.
    2330              : //---------------------------------------------------------
    2331              : 
    2332           61 : std::vector<ChordRest*> Score::deleteRange(Segment* s1, Segment* s2, track_idx_t track1, track_idx_t track2, const SelectionFilter& filter,
    2333              :                                            bool selectionContainsMultiNoteChords)
    2334              : {
    2335           61 :     std::vector<ChordRest*> crsForSelection;
    2336           61 :     IF_ASSERT_FAILED(s1) {
    2337            0 :         return crsForSelection;
    2338              :     }
    2339              : 
    2340              :     // delete content from measures underlying mmrests
    2341           61 :     if (s1->measure() && s1->measure()->isMMRest()) {
    2342            0 :         s1 = s1->measure()->mmRestFirst()->first();
    2343              :     }
    2344           61 :     if (s2 && s2->measure() && s2->measure()->isMMRest()) {
    2345            0 :         s2 = s2->measure()->mmRestLast()->last();
    2346              :     }
    2347              : 
    2348           61 :     const Fraction startTick = s1->tick();
    2349           61 :     const Fraction endTick = s2 ? s2->tick() : lastMeasure()->endTick();
    2350              : 
    2351           61 :     deleteOrShortenOutSpannersFromRange(startTick, endTick, track1, track2, filter);
    2352           61 :     deleteAnnotationsFromRange(s1, s2, track1, track2, filter);
    2353              : 
    2354          321 :     for (track_idx_t track = track1; track < track2; ++track) {
    2355          260 :         if (!filter.canSelectVoice(track)) {
    2356            2 :             continue;
    2357              :         }
    2358          258 :         deleteRangeAtTrack(crsForSelection, track, s1, endTick, /*currentTuplet*/ nullptr, filter, selectionContainsMultiNoteChords);
    2359              :     }
    2360              : 
    2361           61 :     return crsForSelection;
    2362            0 : }
    2363              : 
    2364              : //---------------------------------------------------------
    2365              : //   cmdDeleteSelection
    2366              : //---------------------------------------------------------
    2367              : 
    2368           94 : void Score::cmdDeleteSelection()
    2369              : {
    2370           94 :     std::vector<ChordRest*> crsSelectedAfterDeletion;              // select something after deleting notes
    2371           94 :     EngravingItem* elSelectedAfterDeletion = nullptr;
    2372              : 
    2373           94 :     if (selection().isRange()) {
    2374          183 :         crsSelectedAfterDeletion = deleteRange(selection().startSegment(), selection().endSegment(),
    2375          122 :                                                staff2track(selection().staffStart()), staff2track(selection().staffEnd()),
    2376          122 :                                                selectionFilter(), selection().rangeContainsMultiNoteChords());
    2377              :     } else {
    2378              :         // deleteItem modifies selection().elements() list,
    2379              :         // so we need a local copy:
    2380           33 :         std::vector<EngravingItem*> el = selection().elements();
    2381              : 
    2382              :         // keep track of linked elements that are deleted implicitly
    2383              :         // so we don't try to delete them twice if they are also in selection
    2384           33 :         std::set<EngravingObject*> deletedElements;
    2385              :         // Similarly, deleting one spanner segment, will delete all of them
    2386              :         // so we don't try to delete them twice if they are also in selection
    2387           33 :         std::set<Spanner*> deletedSpanners;
    2388              : 
    2389           45 :         auto selectCRAtTickAndTrack = [this, &crsSelectedAfterDeletion](Fraction tick, track_idx_t track) {
    2390           45 :             ChordRest* cr = findCR(tick, track);
    2391           45 :             if (cr) {
    2392           42 :                 crsSelectedAfterDeletion.push_back(cr);
    2393              :             }
    2394           45 :         };
    2395              : 
    2396           78 :         for (EngravingItem* e : el) {
    2397              :             // these are the linked elements we are about to delete
    2398           45 :             std::list<EngravingObject*> links;
    2399           45 :             if (e->links()) {
    2400           24 :                 links = *e->links();
    2401              :             }
    2402              : 
    2403              :             // find location of element to select after deleting notes
    2404              :             // get tick of element itself if that is valid
    2405              :             // or of spanner or parent if that is more valid
    2406           45 :             Fraction tick  = { -1, 1 };
    2407           45 :             track_idx_t track = muse::nidx;
    2408           45 :             if (e->isNote()) {
    2409            4 :                 tick = toNote(e)->chord()->tick();
    2410           41 :             } else if (e->isRest() || e->isMMRest()) {
    2411            3 :                 tick = toRest(e)->tick();
    2412           38 :             } else if (e->isMeasureRepeat()) { // may be attached in different measure than it appears
    2413            3 :                 tick = toMeasureRepeat(e)->firstMeasureOfGroup()->first()->tick();
    2414           35 :             } else if (e->isSpannerSegment()) {
    2415            0 :                 tick = toSpannerSegment(e)->spanner()->tick();
    2416           35 :             } else if (e->isBreath()) {
    2417              :                 // we want the tick of the ChordRest that precedes the breath mark (in the same track)
    2418            5 :                 for (Segment* s = toBreath(e)->segment()->prev(); s; s = s->prev()) {
    2419            5 :                     if (s->isChordRestType() && s->element(e->track())) {
    2420            5 :                         tick = s->tick();
    2421            5 :                         break;
    2422              :                     }
    2423              :                 }
    2424           30 :             } else if (e->isBarLine() && toBarLine(e)->barLineType() != BarLineType::START_REPEAT) {
    2425              :                 // we want the tick of the ChordRest that precedes the barline (in the same track)
    2426            0 :                 for (Segment* s = toBarLine(e)->segment()->prev(); s; s = s->prev()) {
    2427            0 :                     if (s->isChordRestType() && s->element(e->track())) {
    2428            0 :                         tick = s->tick();
    2429            0 :                         break;
    2430              :                     }
    2431              :                 }
    2432           30 :             } else if (e->isSoundFlag()) {
    2433            0 :                 tick = e->tick();
    2434           30 :             } else if (e->isTuplet()) {
    2435              :                 // the rests replacing the tuplet start at its tick
    2436            2 :                 tick = toTuplet(e)->tick();
    2437           28 :             } else if (e->ownershipParent()
    2438           30 :                        && (e->ownershipParent()->isSegment() || e->ownershipParent()->isChord() || e->ownershipParent()->isNote()
    2439            2 :                            || e->ownershipParent()->isRest())) {
    2440           25 :                 tick = e->ownershipParentItem()->tick();
    2441              :             }
    2442              :             //else tick < 0
    2443           45 :             track = e->track();
    2444              : 
    2445              :             // We should not allow deleting the very first keySig of the piece, because it is
    2446              :             // logically incorrect and leads to a state of undefined key/transposition.
    2447              :             // Also instrument change key signatures should be undeletable.
    2448              :             // The correct action is for the user to set an atonal/custom keySig as needed.
    2449           45 :             if (e->isKeySig()) {
    2450            2 :                 if (e->tick() == Fraction(0, 1) || toKeySig(e)->forInstrumentChange()) {
    2451            0 :                     MScore::setError(MsError::CANNOT_REMOVE_KEY_SIG);
    2452            0 :                     selectCRAtTickAndTrack(tick, track);
    2453            0 :                     continue;
    2454              :                 }
    2455              :             }
    2456              : 
    2457              :             // Don't allow deleting the trill cue note
    2458           45 :             if (e->isNote() && toNote(e)->isTrillCueNote()) {
    2459            0 :                 selectCRAtTickAndTrack(tick, track);
    2460            0 :                 continue;
    2461              :             }
    2462              : 
    2463           45 :             if (e->isFretDiagram() || e->isHarmony()) {
    2464            0 :                 FBox* fbox = toFBox(e->findAncestor(ElementType::FBOX));
    2465            0 :                 if (fbox) {
    2466              :                     // We can't delete elements inside fret box, instead we hide them
    2467            0 :                     if (FretDiagram* fretDiagram = e->isFretDiagram() ? toFretDiagram(e) : toFretDiagram(e->parent())) {
    2468            0 :                         fretDiagram->undoChangeProperty(Pid::VISIBLE, false);
    2469            0 :                         continue;
    2470              :                     }
    2471            0 :                 } else if (e->isFretDiagram()) {
    2472            0 :                     FretDiagram* fretDiagram = toFretDiagram(e);
    2473            0 :                     Harmony* harmony = fretDiagram->harmony();
    2474            0 :                     if (harmony) {
    2475            0 :                         undoChangeParent(harmony, fretDiagram->segment(), track2staff(fretDiagram->track()));
    2476            0 :                         elSelectedAfterDeletion = harmony;
    2477              :                     }
    2478            0 :                 } else if (e->isHarmony()) {
    2479            0 :                     Harmony* harmony = toHarmony(e);
    2480            0 :                     if (harmony->ownershipParentItem()->isFretDiagram()) {
    2481            0 :                         elSelectedAfterDeletion = harmony->ownershipParentItem();
    2482              :                     }
    2483              :                 }
    2484              :             }
    2485              : 
    2486              :             // delete element if we have not done so already
    2487           45 :             if (deletedElements.find(e) == deletedElements.end()) {
    2488              :                 // do not delete two spanner segments from the same spanner
    2489           45 :                 if (e->isSpannerSegment()) {
    2490            0 :                     Spanner* spanner = toSpannerSegment(e)->spanner();
    2491            0 :                     if (deletedSpanners.find(spanner) != deletedSpanners.end()) {
    2492            0 :                         continue;
    2493              :                     } else {
    2494            0 :                         std::list<EngravingObject*> linkedSpanners;
    2495            0 :                         if (spanner->links()) {
    2496            0 :                             linkedSpanners = *spanner->links();
    2497              :                         } else {
    2498            0 :                             linkedSpanners.push_back(spanner);
    2499              :                         }
    2500            0 :                         for (EngravingObject* se : linkedSpanners) {
    2501            0 :                             deletedSpanners.insert(toSpanner(se));
    2502              :                         }
    2503            0 :                     }
    2504              :                 }
    2505           45 :                 deleteItem(e);
    2506              :             }
    2507              : 
    2508           45 :             if (!elSelectedAfterDeletion) {
    2509           45 :                 selectCRAtTickAndTrack(tick, track);
    2510              :             }
    2511              : 
    2512              :             // add these linked elements to list of already-deleted elements
    2513           93 :             for (EngravingObject* se : links) {
    2514           48 :                 deletedElements.insert(se);
    2515              :             }
    2516           45 :         }
    2517           33 :     }
    2518              : 
    2519           94 :     deselectAll();
    2520              :     // make new selection if appropriate
    2521           94 :     if (noteEntryMode()) {
    2522            1 :         if (!crsSelectedAfterDeletion.empty()) {
    2523            1 :             m_is.setSegment(crsSelectedAfterDeletion.front()->segment());
    2524              :         } else {
    2525            0 :             crsSelectedAfterDeletion.push_back(m_is.cr());
    2526              :         }
    2527              :     }
    2528           94 :     if (elSelectedAfterDeletion) {
    2529            0 :         select(elSelectedAfterDeletion);
    2530           94 :     } else if (!crsSelectedAfterDeletion.empty()) {
    2531           86 :         std::vector<EngravingItem*> elementsToSelect;
    2532          388 :         for (ChordRest* cr : crsSelectedAfterDeletion) {
    2533          302 :             if (cr) {
    2534          302 :                 if (cr->isChord()) {
    2535           21 :                     elementsToSelect.push_back(dynamic_cast<EngravingItem*>(toChord(cr)->upNote()));
    2536              :                 }
    2537          302 :                 if (cr->isRest()) {
    2538          280 :                     elementsToSelect.push_back(dynamic_cast<EngravingItem*>(cr));
    2539              :                 }
    2540              :             }
    2541              :         }
    2542           86 :         select(elementsToSelect, SelectType::ADD, 0);
    2543           86 :     }
    2544           94 : }
    2545              : 
    2546              : //---------------------------------------------------------
    2547              : //   cmdFullMeasureRest
    2548              : //---------------------------------------------------------
    2549              : 
    2550            0 : void Score::cmdFullMeasureRest()
    2551              : {
    2552            0 :     Segment* s1     = nullptr;
    2553            0 :     Segment* s2     = nullptr;
    2554            0 :     Fraction stick1 = { -1, 1 };
    2555            0 :     Fraction stick2 = { -1, 1 };
    2556            0 :     track_idx_t track1 = muse::nidx;
    2557            0 :     track_idx_t track2 = muse::nidx;
    2558            0 :     Rest* r = nullptr;
    2559              : 
    2560            0 :     if (noteEntryMode()) {
    2561            0 :         s1 = inputState().segment();
    2562            0 :         if (!s1 || s1->rtick().isNotZero()) {
    2563            0 :             return;
    2564              :         }
    2565            0 :         Measure* m = s1->measure();
    2566            0 :         s2 = m->last();
    2567            0 :         stick1 = s1->tick();
    2568            0 :         stick2 = s2->tick();
    2569            0 :         track1 = inputState().track();
    2570            0 :         track2 = track1 + 1;
    2571            0 :     } else if (selection().isRange()) {
    2572            0 :         s1 = selection().startSegment();
    2573            0 :         s2 = selection().endSegment();
    2574            0 :         if (style().styleB(Sid::createMultiMeasureRests)) {
    2575              :             // use underlying measures
    2576            0 :             if (s1 && s1->measure()->isMMRest()) {
    2577            0 :                 s1 = tick2segment(stick1);
    2578              :             }
    2579            0 :             if (s2 && s2->measure()->isMMRest()) {
    2580            0 :                 s2 = tick2segment(stick2, true);
    2581              :             }
    2582              :         }
    2583            0 :         stick1 = selection().tickStart();
    2584            0 :         stick2 = selection().tickEnd();
    2585            0 :         Segment* ss1 = s1;
    2586            0 :         if (ss1 && ss1->segmentType() != SegmentType::ChordRest) {
    2587            0 :             ss1 = ss1->next1(SegmentType::ChordRest);
    2588              :         }
    2589            0 :         bool fullMeasure = ss1 && (ss1->measure()->first(SegmentType::ChordRest) == ss1)
    2590            0 :                            && (s2 == 0 || (s2->segmentType() == SegmentType::EndBarLine)
    2591            0 :                                || (s2->segmentType() == SegmentType::TimeSigAnnounce)
    2592            0 :                                || (s2->segmentType() == SegmentType::KeySigAnnounce));
    2593            0 :         if (!fullMeasure) {
    2594            0 :             return;
    2595              :         }
    2596            0 :         track1 = selection().staffStart() * VOICES;
    2597            0 :         track2 = selection().staffEnd() * VOICES;
    2598            0 :     } else if (selection().cr()) {
    2599            0 :         ChordRest* cr = selection().cr();
    2600            0 :         if (!cr || cr->rtick().isNotZero()) {
    2601            0 :             return;
    2602              :         }
    2603            0 :         Measure* m = cr->measure();
    2604            0 :         s1 = m->first();
    2605            0 :         s2 = m->last();
    2606            0 :         stick1 = s1->tick();
    2607            0 :         stick2 = s2->tick();
    2608            0 :         track1 = selection().cr()->track();
    2609            0 :         track2 = track1 + 1;
    2610              :     } else {
    2611            0 :         return;
    2612              :     }
    2613              : 
    2614            0 :     for (track_idx_t track = track1; track < track2; ++track) {
    2615            0 :         if (selection().isRange() && !selectionFilter().canSelectVoice(track)) {
    2616            0 :             continue;
    2617              :         }
    2618              :         // first pass - remove non-initial rests from empty measures/voices
    2619            0 :         for (Segment* s = s1; s != s2; s = s->next1()) {
    2620            0 :             if (!(s->measure()->isOnlyRests(track))) {     // Don't remove anything from measures that contain notes
    2621            0 :                 continue;
    2622              :             }
    2623            0 :             if (s->segmentType() != SegmentType::ChordRest || !s->element(track)) {
    2624            0 :                 continue;
    2625              :             }
    2626            0 :             ChordRest* cr = toChordRest(s->element(track));
    2627              :             // keep first rest of measure as placeholder (replaced in second pass)
    2628              :             // but delete all others
    2629            0 :             if (s->rtick().isNotZero()) {
    2630            0 :                 removeChordRest(cr, true);
    2631              :             }
    2632              :         }
    2633              :         // second pass - replace placeholders with full measure rests
    2634            0 :         for (Measure* m = s1->measure(); m; m = m->nextMeasure()) {
    2635            0 :             if (m->isOnlyRests(track)) {
    2636            0 :                 ChordRest* cr = m->findChordRest(m->tick(), track);
    2637            0 :                 if (cr) {
    2638            0 :                     removeChordRest(cr, true);
    2639            0 :                     r = addRest(m->tick(), track, TDuration(DurationType::V_MEASURE), 0);
    2640            0 :                 } else if (noteEntryMode()) {
    2641              :                     // might be no cr at input position
    2642            0 :                     r = addRest(m->tick(), track, TDuration(DurationType::V_MEASURE), 0);
    2643              :                 }
    2644              :             }
    2645            0 :             if (s2 && (m == s2->measure())) {
    2646            0 :                 break;
    2647              :             }
    2648              :         }
    2649              :     }
    2650              : 
    2651              :     // selected range is probably empty now and possibly subsumed by an mmrest
    2652              :     // so updating selection requires forcing mmrests to be updated first
    2653            0 :     s1 = tick2segmentMM(stick1);
    2654            0 :     s2 = tick2segmentMM(stick2, true);
    2655            0 :     if (selection().isRange() && s1 && s2) {
    2656            0 :         m_selection.setStartSegment(s1);
    2657            0 :         m_selection.setEndSegment(s2);
    2658            0 :         m_selection.updateSelectedElements();
    2659            0 :     } else if (r) {
    2660              :         // note entry mode
    2661            0 :         select(r, SelectType::SINGLE);
    2662              :     } else {
    2663            0 :         deselectAll();
    2664              :     }
    2665              : }
    2666              : 
    2667              : //---------------------------------------------------------
    2668              : //   cmdCreateTuplet
    2669              : //    replace cr with tuplet
    2670              : //---------------------------------------------------------
    2671              : 
    2672           54 : void Score::cmdCreateTuplet(ChordRest* ocr, Tuplet* tuplet)
    2673              : {
    2674           54 :     track_idx_t track = ocr->track();
    2675           54 :     Measure* measure = ocr->measure();
    2676           54 :     Fraction tick = ocr->tick();
    2677           54 :     Fraction an = (tuplet->ticks() * tuplet->ratio()) / tuplet->baseLen().fraction();
    2678           54 :     if (!an.denominator()) {
    2679            0 :         return;
    2680              :     }
    2681              : 
    2682           54 :     if (ocr->tuplet()) {
    2683            6 :         tuplet->setTuplet(ocr->tuplet());
    2684              :     }
    2685           54 :     removeChordRest(ocr, false);
    2686              : 
    2687              :     ChordRest* cr;
    2688           54 :     if (ocr->isChord()) {
    2689           47 :         cr = Factory::createChord(this->dummy());
    2690           47 :         toChord(cr)->setStemDirection(toChord(ocr)->stemDirection());
    2691           94 :         for (Note* oldNote : toChord(ocr)->notes()) {
    2692           47 :             Note* note = Factory::createNote(toChord(cr));
    2693           47 :             note->setPitch(oldNote->pitch());
    2694           47 :             note->setTpc1(oldNote->tpc1());
    2695           47 :             note->setTpc2(oldNote->tpc2());
    2696           47 :             cr->add(note);
    2697              :         }
    2698              :     } else {
    2699            7 :         cr = Factory::createRest(this->dummy());
    2700              :     }
    2701              : 
    2702           54 :     int actualNotes = an.numerator() / an.denominator();
    2703              : 
    2704           54 :     tuplet->setTrack(track);
    2705           54 :     cr->setTuplet(tuplet);
    2706           54 :     cr->setTrack(track);
    2707           54 :     cr->setDurationType(tuplet->baseLen());
    2708           54 :     cr->setTicks(tuplet->baseLen().fraction());
    2709              : 
    2710           54 :     undoAddCR(cr, measure, tick);
    2711              : 
    2712           54 :     Fraction ticks = cr->actualTicks();
    2713              : 
    2714          178 :     for (int i = 0; i < (actualNotes - 1); ++i) {
    2715          124 :         tick += ticks;
    2716          124 :         Rest* rest = Factory::createRest(this->dummy());
    2717          124 :         rest->setTuplet(tuplet);
    2718          124 :         rest->setTrack(track);
    2719          124 :         rest->setDurationType(tuplet->baseLen());
    2720          124 :         rest->setTicks(tuplet->baseLen().fraction());
    2721          124 :         undoAddCR(rest, measure, tick);
    2722              :     }
    2723              : }
    2724              : 
    2725              : //---------------------------------------------------------
    2726              : //   cmdEnterRest
    2727              : //---------------------------------------------------------
    2728              : 
    2729            1 : void Score::cmdEnterRest(const TDuration& d)
    2730              : {
    2731            1 :     if (m_is.track() == muse::nidx) {
    2732            0 :         LOGD("cmdEnterRest: track invalid");
    2733            0 :         return;
    2734              :     }
    2735            1 :     startCmd(TranslatableString("undoableAction", "Enter rest"));
    2736            1 :     enterRest(d);
    2737            1 :     endCmd();
    2738              : }
    2739              : 
    2740              : //---------------------------------------------------------
    2741              : //   enterRest
    2742              : //---------------------------------------------------------
    2743              : 
    2744            1 : void Score::enterRest(const TDuration& d, InputState* externalInputState)
    2745              : {
    2746            1 :     InputState& is = externalInputState ? (*externalInputState) : m_is;
    2747              : 
    2748            1 :     if (is.beyondScore()) {
    2749            0 :         appendMeasures(1);
    2750            0 :         is.moveToNextInputPos();
    2751              :     }
    2752              : 
    2753            1 :     expandVoice(is.segment(), is.track());
    2754              : 
    2755            1 :     if (!is.cr()) {
    2756            0 :         LOGD("cannot enter rest here");
    2757            0 :         return;
    2758              :     }
    2759              : 
    2760            1 :     const track_idx_t track = is.track();
    2761            1 :     NoteVal nval;
    2762            1 :     setNoteRest(is.segment(), track, nval,
    2763              :                 d.fraction(), DirectionV::AUTO, /* forceAccidental */ false, is.articulationIds(), /* rhythmic */ false,
    2764              :                 externalInputState);
    2765            1 :     is.moveToNextInputPos();
    2766            1 :     if (!is.noteEntryMode() || is.usingNoteEntryMethod(NoteEntryMethod::BY_NOTE_NAME)) {
    2767            1 :         is.setRest(false);  // continue with normal note entry
    2768              :     }
    2769              : }
    2770              : 
    2771              : //---------------------------------------------------------
    2772              : //   removeChordRest
    2773              : //    remove chord or rest
    2774              : //    remove associated segment if empty
    2775              : //    remove beam
    2776              : //    remove slurs
    2777              : //---------------------------------------------------------
    2778              : 
    2779          666 : void Score::removeChordRest(ChordRest* cr, bool clearSegment)
    2780              : {
    2781          666 :     std::set<Segment*> segments;
    2782         1361 :     for (EngravingObject* e : cr->linkList()) {
    2783          695 :         if (cr->isChord()) {
    2784          468 :             std::set<Spanner*> startingSpanners = toChord(e)->startingSpanners();
    2785          469 :             for (Spanner* spanner : startingSpanners) {
    2786            1 :                 if (spanner->isTrill() || spanner->isSlur()) {
    2787            0 :                     doUndoRemoveElement(spanner);
    2788              :                 }
    2789              :             }
    2790          468 :         }
    2791          695 :         doUndoRemoveElement(static_cast<EngravingItem*>(e));
    2792          695 :         if (clearSegment) {
    2793          622 :             Segment* s = toChordRest(e)->segment();
    2794          622 :             if (segments.find(s) == segments.end()) {
    2795          618 :                 segments.insert(s);
    2796              :             }
    2797              :         }
    2798          666 :     }
    2799         1284 :     for (Segment* s : segments) {
    2800          618 :         if (s->empty()) {
    2801          419 :             doUndoRemoveElement(s);
    2802              :         }
    2803              :     }
    2804          666 :     if (cr->beam()) {
    2805            0 :         Beam* beam = cr->beam();
    2806            0 :         if (beam->generated()) {
    2807            0 :             removeElement(beam);
    2808            0 :             delete beam;
    2809              :         } else {
    2810            0 :             undoRemoveElement(beam);
    2811              :         }
    2812              :     }
    2813          666 : }
    2814              : 
    2815              : //---------------------------------------------------------
    2816              : //   cmdDeleteTuplet
    2817              : //    remove tuplet and replace with rest
    2818              : //---------------------------------------------------------
    2819              : 
    2820           48 : void Score::cmdDeleteTuplet(Tuplet* tuplet, bool replaceWithRest)
    2821              : {
    2822           48 :     std::vector<DurationElement*> elements = tuplet->elements();
    2823          199 :     for (DurationElement* de : elements) {
    2824          151 :         if (de->isChordRest()) {
    2825          134 :             removeChordRest(toChordRest(de), true);
    2826              :         } else {
    2827           17 :             assert(de->isTuplet());
    2828           17 :             cmdDeleteTuplet(toTuplet(de), false);
    2829              :         }
    2830              :     }
    2831           48 :     if (replaceWithRest) {
    2832            7 :         setRest(tuplet->tick(), tuplet->track(), tuplet->ticks(), true, tuplet->tuplet());
    2833              :     }
    2834           48 : }
    2835              : 
    2836              : //---------------------------------------------------------
    2837              : //   insertMeasure
    2838              : //    if inserting Measure, act on master score
    2839              : //    (and add to all linked scores)
    2840              : //    if inserting Box, add to local score only
    2841              : //---------------------------------------------------------
    2842              : 
    2843          202 : MeasureBase* Score::insertMeasure(ElementType type, MeasureBase* beforeMeasure, const InsertMeasureOptions& options)
    2844              : {
    2845          202 :     MeasureBase* localInsertMeasureBase = nullptr;
    2846          202 :     if (type == ElementType::MEASURE) {
    2847          202 :         if (MeasureBase* masterInsertMeasure = masterScore()->insertMeasure(beforeMeasure, options)) {
    2848          202 :             localInsertMeasureBase = tick2measure(masterInsertMeasure->tick());
    2849              :         }
    2850              :     } else {
    2851            0 :         localInsertMeasureBase = insertBox(type, beforeMeasure, options);
    2852              :     }
    2853              : 
    2854          202 :     if (localInsertMeasureBase && options.needDeselectAll) {
    2855          202 :         deselectAll();
    2856              :     }
    2857              : 
    2858          202 :     return localInsertMeasureBase;
    2859              : }
    2860              : 
    2861              : //---------------------------------------------------------
    2862              : //   insertBox
    2863              : //---------------------------------------------------------
    2864              : 
    2865           66 : MeasureBase* Score::insertBox(ElementType type, MeasureBase* beforeMeasure, const InsertMeasureOptions& options)
    2866              : {
    2867           66 :     const bool isFrame = type == ElementType::FBOX || type == ElementType::HBOX || type == ElementType::TBOX || type == ElementType::VBOX;
    2868           66 :     if (!isFrame) {
    2869            0 :         return nullptr;
    2870              :     }
    2871              : 
    2872           66 :     return this->insertBox(toMeasureBase(Factory::createItem(type, dummy())), beforeMeasure, options);
    2873              : }
    2874              : 
    2875           66 : MeasureBase* Score::insertBox(MeasureBase* box, MeasureBase* beforeMeasure, const InsertMeasureOptions& options)
    2876              : {
    2877           66 :     IF_ASSERT_FAILED(box) {
    2878            0 :         return nullptr;
    2879              :     }
    2880              : 
    2881           66 :     ElementType type = box->type();
    2882           66 :     const bool isFrame = type == ElementType::FBOX || type == ElementType::HBOX || type == ElementType::TBOX || type == ElementType::VBOX;
    2883           66 :     if (!isFrame) {
    2884            0 :         return nullptr;
    2885              :     }
    2886              : 
    2887           66 :     if (box->score() != this) {
    2888            0 :         box->setScore(this);
    2889              :     }
    2890              : 
    2891           66 :     Fraction tick;
    2892           66 :     bool isTitleFrame = type == ElementType::VBOX && beforeMeasure && beforeMeasure == beforeMeasure->score()->first();
    2893           66 :     if (beforeMeasure) {
    2894           66 :         if (beforeMeasure->isMeasure()) {
    2895           66 :             Measure* m = toMeasure(beforeMeasure);
    2896           66 :             if (m->isMMRest()) {
    2897            0 :                 beforeMeasure = m->mmRestFirst();
    2898            0 :                 deselectAll();
    2899              :             }
    2900          145 :             for (size_t staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
    2901           79 :                 if (m->isMeasureRepeatGroupWithPrevM(staffIdx)) {
    2902            0 :                     MScore::setError(MsError::CANNOT_SPLIT_MEASURE_REPEAT);
    2903            0 :                     return nullptr;
    2904              :                 }
    2905              :             }
    2906              :         } else {
    2907            0 :             beforeMeasure = beforeMeasure->top(); // don't try to insert in front of nested frame
    2908              :         }
    2909           66 :         tick = beforeMeasure->tick();
    2910              :     } else {
    2911            0 :         tick = last() ? last()->endTick() : Fraction(0, 1);
    2912              :     }
    2913              : 
    2914           66 :     box->setTick(tick);
    2915           66 :     box->setNext(beforeMeasure);
    2916           66 :     box->setPrev(beforeMeasure ? beforeMeasure->prev() : last());
    2917           66 :     box->setSizeIsSpatiumDependent(!isTitleFrame);
    2918              : 
    2919           66 :     if (type == ElementType::FBOX) {
    2920            5 :         toFBox(box)->init();
    2921              :     }
    2922              : 
    2923           66 :     undo(new InsertMeasures(box, box));
    2924              : 
    2925           66 :     if (options.needDeselectAll) {
    2926           66 :         deselectAll();
    2927              :     }
    2928              : 
    2929           66 :     if (options.cloneBoxToAllParts) {
    2930           47 :         box->manageExclusionFromParts(/*exclude =*/ false);
    2931              :     }
    2932              : 
    2933           66 :     return box;
    2934              : }
    2935              : 
    2936           38 : void Score::restoreInitialKeySigAndTimeSig()
    2937              : {
    2938           38 :     bool concertPitch = style().styleB(Sid::concertPitch);
    2939              :     static constexpr Fraction startTick = Fraction(0, 1);
    2940              : 
    2941           38 :     Measure* firstMeas = firstMeasure();
    2942           83 :     for (Staff* staff : m_staves) {
    2943           45 :         Key concertKey = Key::C;
    2944           45 :         Key transposedKey = concertKey;
    2945           45 :         const Part* part = staff->part();
    2946           45 :         const Instrument* instrument = part->instrument();
    2947           45 :         int transpose = -instrument->transpose().chromatic;
    2948           45 :         if (!concertPitch) {
    2949           45 :             transposedKey = Transpose::transposeKey(transposedKey, transpose, part->preferSharpFlat());
    2950              :         }
    2951              : 
    2952           45 :         Segment* keySegment = firstMeas->undoGetSegment(SegmentType::KeySig, startTick);
    2953              : 
    2954           45 :         KeySigEvent keySigEvent;
    2955           45 :         keySigEvent.setConcertKey(concertKey);
    2956           45 :         keySigEvent.setKey(transposedKey);
    2957              :         // use atonal KS for drums
    2958           45 :         if (instrument->useDrumset()) {
    2959            0 :             keySigEvent.setCustom(true);
    2960            0 :             keySigEvent.setMode(KeyMode::NONE);
    2961              :         }
    2962              : 
    2963           45 :         KeySig* newKeySig = Factory::createKeySig(keySegment);
    2964           45 :         newKeySig->setTrack(staff->idx() * VOICES);
    2965           45 :         keySegment->add(newKeySig);
    2966           45 :         newKeySig->setKeySigEvent(keySigEvent);
    2967           45 :         undoAddElement(newKeySig);
    2968              : 
    2969           45 :         staff->setKey(Fraction(0, 1), keySigEvent);
    2970              : 
    2971           45 :         Segment* timeSegment = firstMeas->undoGetSegment(SegmentType::TimeSig, startTick);
    2972           45 :         TimeSig* newTimeSig = Factory::createTimeSig(timeSegment);
    2973           45 :         newTimeSig->setTrack(staff->idx() * VOICES);
    2974           45 :         newTimeSig->setSig(Fraction(4, 4));
    2975           45 :         timeSegment->add(newTimeSig);
    2976           45 :         undoAddElement(newTimeSig);
    2977           45 :     }
    2978              : 
    2979           38 :     firstMeas->setTimesig(Fraction(4, 4));
    2980           38 :     firstMeas->setTicks(Fraction(4, 4));
    2981           38 : }
    2982              : 
    2983              : //---------------------------------------------------------
    2984              : //   checkSpanner
    2985              : //    check if spanners are still valid as anchors may
    2986              : //    have changed or be removed.
    2987              : //    Spanners need to have a start anchor. Slurs need a
    2988              : //    start and end anchor.
    2989              : //---------------------------------------------------------
    2990              : 
    2991        10491 : void Score::checkSpanner(const Fraction& startTick, const Fraction& endTick, bool removeOrphans)
    2992              : {
    2993        10491 :     std::vector<Spanner*> spannersToRemove;
    2994        10491 :     std::vector<Spanner*> spannersToShorten;
    2995        10491 :     auto spanners = m_spanner.findOverlapping(startTick.ticks(), endTick.ticks());
    2996              : 
    2997              :     // DEBUG: check all spanner
    2998              :     //        there may be spanners outside of score bc. some measures were deleted
    2999              : 
    3000        10491 :     Fraction lastTick = lastMeasure()->endTick();
    3001              : 
    3002        14752 :     for (const auto& i : m_spanner.map()) {
    3003         4261 :         Spanner* s = i.second;
    3004              : 
    3005         4261 :         if (s->isSlur()) {
    3006         2297 :             Segment* seg = tick2segmentMM(s->tick(), false, SegmentType::ChordRest);
    3007         2297 :             if (!seg || !seg->element(s->track())) {
    3008            0 :                 spannersToRemove.push_back(s);
    3009              :             } else {
    3010         2297 :                 seg = tick2segmentMM(s->tick2(), false, SegmentType::ChordRest);
    3011         2297 :                 if (!seg || !seg->element(s->track2())) {
    3012            0 :                     spannersToRemove.push_back(s);
    3013              :                 }
    3014              :             }
    3015              :         } else {
    3016              :             // remove spanner if there is no start element
    3017         1964 :             s->computeStartElement();
    3018         1964 :             if (!s->startElement()) {
    3019            0 :                 spannersToRemove.push_back(s);
    3020            0 :                 LOGD("checkSpanner::remove (3)");
    3021              :             } else {
    3022         1964 :                 if (s->tick2() > lastTick) {
    3023            0 :                     spannersToShorten.push_back(s);              //s->undoChangeProperty(Pid::SPANNER_TICKS, lastTick - s->tick());
    3024              :                 } else {
    3025         1964 :                     s->computeEndElement();
    3026              :                 }
    3027              :             }
    3028              :         }
    3029              :     }
    3030        10491 :     if (removeOrphans) {
    3031            2 :         for (auto s : spannersToRemove) {       // actually remove scheduled spanners
    3032            0 :             doUndoRemoveElement(s);
    3033              :         }
    3034              :     }
    3035        10491 :     for (Spanner* s : spannersToShorten) {      // shorten spanners that extended past end of score
    3036            0 :         undo(new ChangeProperty(s, Pid::SPANNER_TICKS, lastTick - s->tick()));
    3037            0 :         s->computeEndElement();
    3038              :     }
    3039        10491 : }
    3040              : 
    3041              : //---------------------------------------------------------
    3042              : //   checkTimeDelete
    3043              : //---------------------------------------------------------
    3044              : 
    3045           12 : bool Score::checkTimeDelete(Segment* startSegment, Segment* endSegment)
    3046              : {
    3047           12 :     Measure* startMeasure = startSegment->measure();
    3048              :     Measure* endMeasure;
    3049              : 
    3050           12 :     if (endSegment) {
    3051           12 :         endMeasure = endSegment->prev() ? endSegment->measure() : endSegment->measure()->prevMeasure();
    3052              :     } else {
    3053            0 :         endMeasure = lastMeasure();
    3054              :     }
    3055              : 
    3056           12 :     Fraction endTick = endSegment ? endSegment->tick() : endMeasure->endTick();
    3057           12 :     Fraction tick = startSegment->tick();
    3058           12 :     Fraction etick = (startMeasure == endMeasure ? endTick : startMeasure->endTick());
    3059              : 
    3060              :     // check for MeasureRepeat
    3061           12 :     bool startsAtBeginningOfMeasure = (tick == startMeasure->tick());
    3062           12 :     bool endsAtEndOfMeasure = (endTick == endMeasure->endTick());
    3063           27 :     for (size_t staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
    3064           15 :         if ((startMeasure->isMeasureRepeatGroup(staffIdx) && !startsAtBeginningOfMeasure)
    3065           15 :             || (endMeasure->isMeasureRepeatGroup(staffIdx) && !endsAtEndOfMeasure)
    3066           15 :             || startMeasure->isMeasureRepeatGroupWithPrevM(staffIdx)
    3067           30 :             || endMeasure->isMeasureRepeatGroupWithNextM(staffIdx)) {
    3068            0 :             MScore::setError(MsError::CANNOT_REMOVE_TIME_MEASURE_REPEAT);
    3069            0 :             return false;
    3070              :         }
    3071              :     }
    3072              : 
    3073           12 :     bool canDeleteTime = true;
    3074           12 :     while (canDeleteTime) {
    3075           72 :         for (size_t track = 0; canDeleteTime && track < m_staves.size() * VOICES; ++track) {
    3076           60 :             if (startMeasure->hasVoice(track)) {
    3077           15 :                 Segment* fs = startMeasure->first(SegmentType::ChordRest);
    3078           51 :                 for (Segment* s = fs; s; s = s->next(SegmentType::ChordRest)) {
    3079           42 :                     if (s->element(track)) {
    3080           42 :                         ChordRest* cr       = toChordRest(s->element(track));
    3081           42 :                         Tuplet* t           = cr->tuplet();
    3082           42 :                         DurationElement* de = t ? toDurationElement(t) : toDurationElement(cr);
    3083           42 :                         Fraction f          = de->tick() + de->actualTicks();
    3084           42 :                         Fraction cetick     = f;
    3085           42 :                         if (cetick <= tick) {
    3086            0 :                             continue;
    3087              :                         }
    3088           42 :                         if (de->tick() >= etick) {
    3089            6 :                             break;
    3090              :                         }
    3091           36 :                         if (t && (t->tick() < tick || cetick > etick)) {
    3092            0 :                             canDeleteTime = false;
    3093            0 :                             break;
    3094              :                         }
    3095              :                     }
    3096              :                 }
    3097              :             }
    3098              :         }
    3099           12 :         if (startMeasure == endMeasure) {
    3100           12 :             break;
    3101              :         }
    3102            0 :         startMeasure = endMeasure;
    3103            0 :         tick  = startMeasure->tick();
    3104            0 :         etick = endTick;
    3105              :     }
    3106           12 :     if (!canDeleteTime) {
    3107            0 :         MScore::setError(MsError::CANNOT_REMOVE_TIME_TUPLET);
    3108            0 :         return false;
    3109              :     }
    3110           12 :     return true;
    3111              : }
    3112              : 
    3113              : //---------------------------------------------------------
    3114              : //   cmdTimeDelete
    3115              : ///    delete time by decreasing measure length if partial measures are selected
    3116              : //---------------------------------------------------------
    3117              : 
    3118           12 : void Score::cmdTimeDelete()
    3119              : {
    3120           12 :     Transaction& tx = transactionManager()->currentOrDummyTransaction();
    3121              : 
    3122           12 :     EngravingItem* e = selection().element();
    3123              : 
    3124           12 :     if (e && e->isBarLine() && toBarLine(e)->segment()->isEndBarLineType()) {
    3125            0 :         const Measure* m = toBarLine(e)->segment()->measure();
    3126            0 :         const Measure* next = m->nextMeasure();
    3127            0 :         if (next) {
    3128            0 :             SplitJoinMeasure::joinMeasures(tx, m_masterScore, m->tick(), next->tick());
    3129              :         }
    3130            0 :         return;
    3131              :     }
    3132              : 
    3133           12 :     Segment* startSegment = nullptr;
    3134           12 :     Segment* endSegment = nullptr;
    3135              : 
    3136           12 :     if (selection().state() != SelState::RANGE) {
    3137            3 :         if (!e) {
    3138            0 :             return;
    3139              :         }
    3140              : 
    3141            3 :         ChordRest* cr = nullptr;
    3142            3 :         if (e->isNote()) {
    3143            3 :             cr = toNote(e)->chord();
    3144            0 :         } else if (e->isChordRest()) {
    3145            0 :             cr = toChordRest(e);
    3146              :         } else {
    3147            0 :             return;
    3148              :         }
    3149              : 
    3150            3 :         startSegment     = cr->segment();
    3151            3 :         Fraction endTick = cr->endTick();
    3152            3 :         endSegment       = tick2measure(endTick)->findSegment(SegmentType::ChordRest, endTick);
    3153              :     } else {
    3154            9 :         startSegment = selection().startSegment();
    3155            9 :         endSegment   = selection().endSegment();
    3156              :     }
    3157              : 
    3158           12 :     if (!isMaster() && masterScore()) {
    3159            0 :         Fraction startTick = startSegment->tick();
    3160            0 :         Measure* masterStartMeas = masterScore()->tick2measure(startTick);
    3161            0 :         Segment* masterStartSeg = masterStartMeas->findSegment(startSegment->segmentType(), startSegment->tick());
    3162            0 :         Segment* masterEndSeg = nullptr;
    3163              : 
    3164            0 :         if (endSegment) {
    3165            0 :             Fraction endTick = endSegment->tick();
    3166            0 :             Measure* masterEndMeas = masterScore()->tick2measure(endTick);
    3167            0 :             if (endSegment->isEndBarLineType()) {
    3168            0 :                 Measure* prevMasterEndMeasure = masterEndMeas->prevMeasure();
    3169            0 :                 masterEndMeas = prevMasterEndMeasure ? prevMasterEndMeasure : masterEndMeas;
    3170              :             }
    3171            0 :             masterEndSeg = masterEndMeas->findSegment(endSegment->segmentType(), endSegment->tick());
    3172              :         }
    3173              : 
    3174            0 :         masterScore()->doTimeDelete(masterStartSeg, masterEndSeg);
    3175              :     } else {
    3176           12 :         doTimeDelete(startSegment, endSegment);
    3177              :     }
    3178              : 
    3179           12 :     if (noteEntryMode()) {
    3180            1 :         Segment* currentSegment = endSegment;
    3181            1 :         ChordRest* cr = nullptr;
    3182            1 :         if (!currentSegment && lastMeasureMM()) {
    3183              :             // deleted to end of score - get last cr on current track
    3184            0 :             currentSegment = lastMeasureMM()->last();
    3185            0 :             if (currentSegment) {
    3186            0 :                 cr = currentSegment->nextChordRest(m_is.track(), true);
    3187            0 :                 if (cr) {
    3188            0 :                     currentSegment = cr->segment();
    3189              :                 }
    3190              :             }
    3191              :         }
    3192            1 :         if (!currentSegment) {
    3193              :             // no cr found - append a new measure
    3194            0 :             appendMeasures(1);
    3195            0 :             currentSegment = lastMeasureMM()->first(SegmentType::ChordRest);
    3196              :         }
    3197            1 :         m_is.setSegment(currentSegment);
    3198            1 :         cr = m_is.cr();
    3199            1 :         if (cr) {
    3200            1 :             if (cr->isChord()) {
    3201            1 :                 select(toChord(cr)->upNote(), SelectType::SINGLE);
    3202              :             } else {
    3203            0 :                 select(cr, SelectType::SINGLE);
    3204              :             }
    3205              :         } else {
    3206              :             // could not find cr to select,
    3207              :             // may be that there is a "hole" in the current track
    3208            0 :             deselectAll();
    3209              :         }
    3210              :     } else {
    3211           11 :         deselectAll();
    3212              :     }
    3213              : }
    3214              : 
    3215           12 : void Score::doTimeDelete(Segment* startSegment, Segment* endSegment)
    3216              : {
    3217           12 :     if (!checkTimeDelete(startSegment, endSegment)) {
    3218            0 :         return;
    3219              :     }
    3220              : 
    3221           12 :     MeasureBase* mbStart = startSegment->measure();
    3222           12 :     if (mbStart->isMeasure() && toMeasure(mbStart)->isMMRest()) {
    3223            0 :         mbStart = toMeasure(mbStart)->mmRestFirst();
    3224              :     }
    3225              :     MeasureBase* mbEnd;
    3226              : 
    3227           12 :     if (endSegment) {
    3228           12 :         mbEnd = endSegment->prev(SegmentType::ChordRest) ? endSegment->measure() : endSegment->measure()->prevMeasure();
    3229              :     } else {
    3230            0 :         mbEnd = lastMeasure();
    3231              :     }
    3232              : 
    3233           12 :     Fraction endTick = endSegment ? endSegment->tick() : mbEnd->endTick();
    3234              : 
    3235              :     for (;;) {
    3236           12 :         if (mbStart->tick() != startSegment->tick()) {
    3237            0 :             Fraction tick = startSegment->tick();
    3238            0 :             Fraction len;
    3239            0 :             if (mbEnd == mbStart) {
    3240            0 :                 len = endTick - tick;
    3241              :             } else {
    3242            0 :                 len = mbStart->endTick() - tick;
    3243              :             }
    3244            0 :             doTimeDeleteForMeasure(toMeasure(mbStart), startSegment, len);
    3245            0 :             if (mbStart == mbEnd) {
    3246            0 :                 break;
    3247              :             }
    3248            0 :             mbStart = mbStart->next();
    3249              :         }
    3250           12 :         endTick = endSegment ? endSegment->tick() : mbEnd->endTick();
    3251           12 :         if (mbEnd->endTick() != endTick) {
    3252            3 :             Fraction len = endTick - mbEnd->tick();
    3253            3 :             doTimeDeleteForMeasure(toMeasure(mbEnd), toMeasure(mbEnd)->first(), len);
    3254            3 :             if (mbStart == mbEnd) {
    3255            3 :                 break;
    3256              :             }
    3257            0 :             mbEnd = mbEnd->prev();
    3258              :         }
    3259            9 :         deleteMeasures(mbStart, mbEnd);
    3260            9 :         break;
    3261              :     }
    3262              : }
    3263              : 
    3264            3 : void Score::doTimeDeleteForMeasure(Measure* m, Segment* startSegment, const Fraction& f)
    3265              : {
    3266            3 :     if (f.isZero()) {
    3267            0 :         return;
    3268              :     }
    3269              : 
    3270            3 :     const Fraction tick  = startSegment->rtick();
    3271            3 :     const Fraction len   = f;
    3272            3 :     const Fraction etick = tick + len;
    3273              : 
    3274            3 :     Segment* fs = m->first(SegmentType::ChordRest);
    3275              : 
    3276           27 :     for (size_t track = 0; track < m_staves.size() * VOICES; ++track) {
    3277           24 :         if (m->hasVoice(track)) {
    3278           12 :             for (Segment* s = fs; s; s = s->next(SegmentType::ChordRest)) {
    3279           12 :                 if (s->element(track)) {
    3280           12 :                     ChordRest* cr   = toChordRest(s->element(track));
    3281           12 :                     Fraction cetick = cr->rtick() + cr->actualTicks();
    3282              : 
    3283           12 :                     if (cetick <= tick) {
    3284            0 :                         continue;
    3285              :                     }
    3286           12 :                     if (s->rtick() >= etick) {
    3287            6 :                         break;
    3288              :                     }
    3289              : 
    3290            6 :                     if (cr->isFullMeasureRest()) {
    3291            0 :                         if (cr->rtick() >= tick) {
    3292              :                             // Move full-measure rest from the deleted area
    3293            0 :                             undoRemoveElement(cr);
    3294            0 :                             ChordRest* newCR = toChordRest(cr->clone());
    3295            0 :                             newCR->setTicks(cr->ticks() - f);
    3296            0 :                             undoAddCR(newCR, m, m->tick() + etick);
    3297              :                         } else {
    3298            0 :                             cr->undoChangeProperty(Pid::DURATION, cr->ticks() - f);
    3299              :                         }
    3300              :                     }
    3301              :                     // inside deleted area
    3302            6 :                     else if (s->rtick() >= tick && cetick <= etick) {
    3303              :                         // inside
    3304            6 :                         undoRemoveElement(cr);
    3305            0 :                     } else if (s->rtick() >= tick) {
    3306              :                         // running out
    3307            0 :                         Fraction ff = cetick - etick;
    3308            0 :                         undoRemoveElement(cr);
    3309            0 :                         createCRSequence(ff, cr, tick + len);
    3310            0 :                     } else if (s->rtick() < tick && cetick <= etick) {
    3311              :                         // running in
    3312            0 :                         Fraction f1 = tick - s->rtick();
    3313            0 :                         changeCRlen(cr, f1, false);
    3314              :                     } else {
    3315              :                         // running in/out
    3316            0 :                         Fraction f1 = cr->ticks() - f;
    3317            0 :                         changeCRlen(cr, f1, false);
    3318              :                     }
    3319              :                 }
    3320              :             }
    3321              :         }
    3322              :     }
    3323            3 :     const Fraction abstick = startSegment->tick();
    3324            3 :     undoInsertTime(abstick, -len);
    3325              : 
    3326            3 :     std::vector<Segment*> emptySegments;
    3327              : 
    3328            6 :     for (Score* score : masterScore()->scoreList()) {
    3329            3 :         Measure* localMeasure = score->tick2measure(abstick);
    3330              : 
    3331            3 :         undo(new InsertTime(score, abstick, -len));
    3332              : 
    3333            3 :         Fraction updatedTick = tick;
    3334              :         // tuplets store their tick: move those after the removed time back with their segments
    3335            3 :         std::set<Tuplet*> tuplets;
    3336           24 :         for (Segment* s = localMeasure->first(SegmentType::ChordRest); s; s = s->next()) {
    3337           21 :             if (s->rtick() < etick || s->rtick() == updatedTick) {
    3338            0 :                 continue;
    3339              :             }
    3340              : 
    3341           21 :             if (s->isChordRestType()) {
    3342          162 :                 for (EngravingItem* e : s->elist()) {
    3343          161 :                     for (Tuplet* t = e ? toChordRest(e)->tuplet() : nullptr; t && t->tick() >= abstick + len; t = t->tuplet()) {
    3344              :                         // shared tuplets follow their origin
    3345           17 :                         if (t->originItems().empty()) {
    3346           14 :                             tuplets.insert(t);
    3347              :                         }
    3348              :                     }
    3349              :                 }
    3350              :             }
    3351           21 :             s->undoChangeProperty(Pid::TICK, updatedTick);
    3352           21 :             updatedTick += s->ticks();
    3353              : 
    3354           21 :             if (score->isMaster()) {
    3355           21 :                 if (s->isChordRestType() && !s->hasElements()) {
    3356            0 :                     emptySegments.push_back(s);
    3357              :                 }
    3358              :             }
    3359              :         }
    3360              : 
    3361            7 :         for (Tuplet* t : tuplets) {
    3362            4 :             t->undoChangeProperty(Pid::TICK, t->tick() - len);
    3363              :         }
    3364              : 
    3365            3 :         undo(new ChangeMeasureLen(localMeasure, localMeasure->ticks() - f));
    3366            6 :     }
    3367              : 
    3368            3 :     for (Segment* s : emptySegments) {
    3369            0 :         if (Segment* ns = s->next(SegmentType::ChordRest)) {
    3370              :             // Move annotations from the empty segment.
    3371              :             // TODO: do we need to preserve annotations at all?
    3372              :             // Maybe only some types (Tempo etc.)?
    3373            0 :             const auto annotations = s->annotations(); // make a copy since we alter the list
    3374            0 :             for (EngravingItem* a : annotations) {
    3375            0 :                 EngravingItem* a1 = a->clone();
    3376            0 :                 a1->setOwnershipParent(ns);
    3377            0 :                 undoRemoveElement(a);
    3378            0 :                 undoAddElement(a1);
    3379              :             }
    3380            0 :         }
    3381              :     }
    3382            3 : }
    3383              : 
    3384              : //---------------------------------------------------------
    3385              : //   undoPropertyChanged
    3386              : //    return true if an property was actually changed
    3387              : //---------------------------------------------------------
    3388              : 
    3389            0 : bool Score::undoPropertyChanged(EngravingItem* item, Pid propId, const PropertyValue& propValue, PropertyFlags propFlags)
    3390              : {
    3391            0 :     Transaction& tx = masterScore()->transactionManager()->currentOrDummyTransaction();
    3392              : 
    3393            0 :     bool changed = false;
    3394              : 
    3395            0 :     const PropertyValue currentPropValue = item->getProperty(propId);
    3396            0 :     const PropertyFlags currentPropFlags = item->propertyFlags(propId);
    3397              : 
    3398            0 :     if ((currentPropValue != propValue) || (currentPropFlags != propFlags)) {
    3399            0 :         item->setPropertyFlags(propId, propFlags);
    3400            0 :         tx.pushWithoutPerforming(new ChangeProperty(item, propId, propValue, propFlags));
    3401            0 :         changed = true;
    3402              :     }
    3403              : 
    3404            0 :     const std::list<EngravingObject*> linkedItems = item->linkListForPropertyPropagation();
    3405              : 
    3406            0 :     for (EngravingObject* linkedItem : linkedItems) {
    3407            0 :         if (linkedItem == item) {
    3408            0 :             continue;
    3409              :         }
    3410            0 :         PropertyPropagation propertyPropagate = item->propertyPropagation(toEngravingItem(linkedItem), propId);
    3411            0 :         switch (propertyPropagate) {
    3412            0 :         case PropertyPropagation::PROPAGATE:
    3413            0 :             if (linkedItem->getProperty(propId) != currentPropValue) {
    3414            0 :                 tx.push(new ChangeProperty(linkedItem, propId, currentPropValue, propFlags));
    3415            0 :                 changed = true;
    3416              :             }
    3417            0 :             break;
    3418            0 :         case PropertyPropagation::UNLINK:
    3419            0 :             item->unlinkPropertyFromMaster(propId);
    3420            0 :             break;
    3421            0 :         default:
    3422            0 :             break;
    3423              :         }
    3424              :     }
    3425              : 
    3426            0 :     return changed;
    3427            0 : }
    3428              : 
    3429            0 : void Score::undoPropertyChanged(EngravingObject* e, Pid t, const PropertyValue& st, PropertyFlags ps)
    3430              : {
    3431            0 :     if (e->getProperty(t) != st) {
    3432            0 :         Transaction& tx = masterScore()->transactionManager()->currentOrDummyTransaction();
    3433            0 :         tx.pushWithoutPerforming(new ChangeProperty(e, t, st, ps));
    3434              :     }
    3435            0 : }
    3436              : 
    3437          126 : void Score::undoChangeStyleVal(Sid idx, const PropertyValue& v)
    3438              : {
    3439          126 :     std::unordered_map<Sid, PropertyValue> map;
    3440          126 :     map.emplace(idx, v);
    3441              : 
    3442          126 :     undo(new ChangeStyleValues(this, std::move(map)));
    3443          126 : }
    3444              : 
    3445            1 : void Score::undoChangeStyleValues(std::unordered_map<Sid, PropertyValue> values)
    3446              : {
    3447            1 :     undo(new ChangeStyleValues(this, std::move(values)));
    3448            1 : }
    3449              : 
    3450            0 : void Score::undoChangePageNumberOffset(int po)
    3451              : {
    3452            0 :     undo(new ChangePageNumberOffset(this, po));
    3453            0 : }
    3454              : 
    3455          180 : void Score::undoChangeParent(EngravingItem* element, EngravingItem* parent, staff_idx_t staffIdx, bool changeLinksParents)
    3456              : {
    3457          180 :     if (!element || !parent) {
    3458            0 :         return;
    3459              :     }
    3460              : 
    3461          180 :     if (element->ownershipParent() == parent && staffIdx == element->staffIdx()) {
    3462            0 :         return;
    3463              :     }
    3464              : 
    3465          180 :     Staff* destStaff = staff(staffIdx);
    3466          180 :     bool recreateItemNeeded = false;
    3467              : 
    3468          180 :     const std::list<EngravingObject*> links = element->linkList();
    3469          375 :     for (EngravingObject* obj : links) {
    3470          195 :         EngravingItem* item = toEngravingItem(obj);
    3471          195 :         Score* linkedScore = item->score();
    3472          195 :         Staff* linkedOrigin = item->staff();
    3473          195 :         Staff* linkedDest = linkedScore != this && destStaff != element->staff() ? destStaff->findLinkedInScore(linkedScore) : linkedOrigin; // don't allow staff-change of linked elements within the same score
    3474          195 :         if (!linkedDest && element->systemFlag()) {
    3475            0 :             linkedDest = linkedScore->staff(0);
    3476              :         }
    3477              : 
    3478          195 :         if (!linkedScore || (!changeLinksParents && item != element)) {
    3479           15 :             continue;
    3480              :         }
    3481          180 :         if (item == element) {
    3482              :             // Master score
    3483          180 :             undo(new ChangeParent(element, parent, staffIdx));
    3484            0 :         } else if (linkedOrigin && linkedDest) {
    3485              :             // Part - origin and destination staves are in this part
    3486              :             EngravingItem* linkedParent;
    3487            0 :             if (parent->isSegment()) {
    3488              :                 // Get parent segment in linked score
    3489            0 :                 Segment* oldSeg = toSegment(parent);
    3490            0 :                 Measure* oldMeas = oldSeg->measure();
    3491            0 :                 Measure* newMeas = linkedScore->tick2measure(oldMeas->tick());
    3492            0 :                 linkedParent = newMeas->tick2segment(oldSeg->tick(), oldSeg->segmentType());
    3493            0 :                 if (!linkedParent && oldSeg->isType(SegmentType::Duration)) {
    3494              :                     // A ChordRest segment that exists in the score may not exist in the part.
    3495              :                     // In that case we create a TimeTick segment as new parent for the linked item.
    3496            0 :                     linkedParent = newMeas->getSegment(SegmentType::TimeTick, oldSeg->tick());
    3497              :                 }
    3498              :             } else {
    3499            0 :                 linkedParent = parent->findLinkedInStaff(linkedDest);
    3500              :             }
    3501            0 :             IF_ASSERT_FAILED(linkedParent) {
    3502            0 :                 continue;
    3503              :             }
    3504            0 :             linkedScore->undo(new ChangeParent(item, linkedParent, linkedScore->staffIdx(linkedDest)));
    3505            0 :         } else if (linkedOrigin && !linkedDest) {
    3506              :             // Part - move is to a different staff
    3507              :             // Remove original item
    3508            0 :             linkedScore->undoRemoveElement(item, false);
    3509            0 :             recreateItemNeeded = true;
    3510              :         }
    3511              :     }
    3512              : 
    3513          180 :     if (recreateItemNeeded) {
    3514              :         // Need to create item in some parts
    3515            0 :         const std::list<EngravingObject*> destStaffLinks = destStaff->linkList();
    3516            0 :         for (EngravingObject* obj : destStaffLinks) {
    3517            0 :             Staff* linkedDest = toStaff(obj);
    3518            0 :             Score* linkedScore = linkedDest->score();
    3519            0 :             if (linkedDest != destStaff && !element->findLinkedInScore(linkedScore)) {
    3520              :                 // Item should be in this score and isn't already
    3521            0 :                 EngravingItem* newItem = element->linkedClone();
    3522              :                 EngravingItem* linkedParent;
    3523              : 
    3524            0 :                 if (parent->isSegment()) {
    3525              :                     // Get parent segment in linked score
    3526            0 :                     Segment* oldSeg = toSegment(parent);
    3527            0 :                     Measure* m = linkedScore->tick2measure(oldSeg->tick());
    3528            0 :                     linkedParent = m->tick2segment(oldSeg->tick(), oldSeg->segmentType());
    3529              :                 } else {
    3530            0 :                     linkedParent = parent->findLinkedInScore(linkedScore);
    3531              :                 }
    3532              : 
    3533            0 :                 newItem->setOwnershipParent(linkedParent);
    3534            0 :                 newItem->setTrack(linkedScore->staffIdx(linkedDest) * VOICES);
    3535            0 :                 newItem->setOffset(PointF());
    3536            0 :                 linkedParent->undoAddElement(newItem, false);
    3537              :             }
    3538              :         }
    3539            0 :     }
    3540          180 : }
    3541              : 
    3542            0 : void Score::undoResetPlayCountTextSettings(BarLine* bl)
    3543              : {
    3544            0 :     if (!bl || !bl->segment()) {
    3545            0 :         return;
    3546              :     }
    3547            0 :     Segment* seg = bl->segment();
    3548              : 
    3549            0 :     for (staff_idx_t staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
    3550            0 :         BarLine* curBl = toBarLine(seg->element(staff2track(staffIdx)));
    3551              : 
    3552            0 :         if (!curBl) {
    3553            0 :             continue;
    3554              :         }
    3555              : 
    3556            0 :         curBl->undoResetProperty(Pid::PLAY_COUNT_TEXT_SETTING);
    3557            0 :         curBl->undoResetProperty(Pid::PLAY_COUNT_TEXT);
    3558              :     }
    3559              : }
    3560              : 
    3561          277 : void Score::undoUpdatePlayCountText(Measure* m)
    3562              : {
    3563          277 :     if (!m) {
    3564            0 :         return;
    3565              :     }
    3566          277 :     const MStyle& _style = style();
    3567          277 :     const bool showText = _style.styleB(Sid::repeatPlayCountShow);
    3568          277 :     const bool singleRepeats = _style.styleB(Sid::repeatPlayCountShowSingleRepeats);
    3569          277 :     const int playCount = m->repeatCount();
    3570          277 :     const bool showPlayCount = showText && (playCount == 2 ? singleRepeats : true) && m->repeatEnd();
    3571              : 
    3572          277 :     Segment* endBarSeg = m->last(SegmentType::BarLineTypes);
    3573          277 :     BarLine* topBl = endBarSeg ? toBarLine(endBarSeg->element(0)) : nullptr;
    3574          277 :     if (!topBl) {
    3575           76 :         return;
    3576              :     }
    3577              : 
    3578          201 :     PlayCountText* topPlayCountText = toPlayCountText(endBarSeg->findAnnotation(ElementType::PLAY_COUNT_TEXT, 0, 0));
    3579          201 :     if (showPlayCount) {
    3580            0 :         if (!topPlayCountText) {
    3581            0 :             topPlayCountText = Factory::createPlayCountText(endBarSeg);
    3582            0 :             topPlayCountText->setTrack(0);
    3583            0 :             topPlayCountText->setSelected(topBl->selected());
    3584            0 :             undoAddElement(topPlayCountText);
    3585              :         }
    3586              :     } else {
    3587          201 :         undoRemoveElement(topPlayCountText);
    3588          201 :         return;
    3589              :     }
    3590              : }
    3591              : 
    3592         1378 : Measure* Score::undoGetMeasure(const Fraction& tick)
    3593              : {
    3594         1378 :     Measure* last = lastMeasure();
    3595         1378 :     if (!last || last->endTick() <= tick) {
    3596          157 :         Fraction lastTick  = last ? last->endTick() : Fraction(0, 1);
    3597          314 :         while (tick >= lastTick) {
    3598          157 :             MeasureBase* m = score()->insertMeasure(ElementType::MEASURE);
    3599          157 :             lastTick += m->ticks();
    3600              :         }
    3601              :     }
    3602         1378 :     return tick2measure(tick);
    3603              : }
    3604              : 
    3605           21 : void Score::undoChangeBarLineType(BarLine* bl, BarLineType barType, bool allStaves, bool replace)
    3606              : {
    3607           21 :     if (barType == bl->barLineType()) {
    3608            9 :         return;
    3609              :     }
    3610              : 
    3611           12 :     Measure* m = bl->measure();
    3612           12 :     if (!m) {
    3613            0 :         return;
    3614              :     }
    3615              : 
    3616           12 :     bool keepStartRepeat = false;
    3617              : 
    3618           12 :     if (barType == BarLineType::START_REPEAT && bl->segment()->segmentType() != SegmentType::BeginBarLine) {
    3619            0 :         m = m->nextMeasure();
    3620            0 :         if (!m) {  // we were in last measure
    3621            0 :             return;
    3622              :         }
    3623           12 :     } else if (bl->barLineType() == BarLineType::START_REPEAT) {
    3624            3 :         if (m->system() && m->isFirstInSystem()) {
    3625            2 :             if (barType != BarLineType::END_REPEAT) {
    3626            4 :                 for (Score* lscore : m->score()->scoreList()) {
    3627            2 :                     Measure* lmeasure = lscore->tick2measure(m->tick());
    3628            2 :                     if (lmeasure) {
    3629            2 :                         lmeasure->undoChangeProperty(Pid::REPEAT_START, false);
    3630              :                     }
    3631            2 :                 }
    3632              :             }
    3633            2 :             return;
    3634              :         }
    3635              : 
    3636            1 :         m = m->prevMeasureMM();
    3637            1 :         if (!m) {
    3638            0 :             return;
    3639              :         }
    3640              : 
    3641            1 :         bl = const_cast<BarLine*>(m->endBarLine());
    3642            1 :         if (!bl) {
    3643            0 :             return;
    3644              :         }
    3645              : 
    3646            1 :         if (barType == BarLineType::END_REPEAT) {
    3647            0 :             keepStartRepeat = true;
    3648              :         }
    3649              :     }
    3650              : 
    3651              :     // when setting barline type on mmrest, set for underlying measure (and linked staves)
    3652              :     // createMMRest will then set for the mmrest directly
    3653           10 :     Measure* m2 = m->isMMRest() ? m->mmRestLast() : m;
    3654              : 
    3655           10 :     switch (barType) {
    3656            9 :     case BarLineType::END:
    3657              :     case BarLineType::NORMAL:
    3658              :     case BarLineType::DOUBLE:
    3659              :     case BarLineType::BROKEN:
    3660              :     case BarLineType::DOTTED:
    3661              :     case BarLineType::REVERSE_END:
    3662              :     case BarLineType::HEAVY:
    3663              :     case BarLineType::DOUBLE_HEAVY: {
    3664            9 :         if (m->nextMeasureMM() && m->nextMeasureMM()->system() && m->nextMeasureMM()->isFirstInSystem()) {
    3665            2 :             keepStartRepeat = true;
    3666              :         }
    3667              : 
    3668            9 :         Segment* segment = bl->segment();
    3669            9 :         SegmentType segmentType = segment->segmentType();
    3670              : 
    3671            9 :         if (segmentType == SegmentType::EndBarLine) {
    3672            9 :             bool generated = false;
    3673            9 :             if (bl->barLineType() == barType) {
    3674            1 :                 generated = bl->generated();                // no change: keep current status
    3675            8 :             } else if (!bl->generated() && (barType == BarLineType::NORMAL)) {
    3676            3 :                 generated = true;                           // currently non-generated, changing to normal: assume generated
    3677              :             }
    3678            9 :             if (allStaves) {
    3679              :                 // use all staves of master score; we will take care of parts in loop through linked staves below
    3680            9 :                 Score* mScore = bl->masterScore();
    3681            9 :                 if (mScore->style().styleB(Sid::createMultiMeasureRests)) {
    3682            0 :                     m2 = mScore->tick2measureMM(m2->tick());
    3683            0 :                     if (!m2) {
    3684            0 :                         return;
    3685              :                     }
    3686            0 :                     segment = m2->undoGetSegment(segment->segmentType(), segment->tick());
    3687            0 :                     m2 = m2->isMMRest() ? m2->mmRestLast() : m2;
    3688            0 :                     if (!m2) {
    3689            0 :                         return;
    3690              :                     }
    3691              :                 } else {
    3692            9 :                     m2 = mScore->tick2measure(m2->tick());
    3693            9 :                     if (!m2) {
    3694            0 :                         return;
    3695              :                     }
    3696            9 :                     segment = m2->undoGetSegment(segment->segmentType(), segment->tick());
    3697              :                 }
    3698              :             }
    3699            9 :             const std::vector<EngravingItem*>& elist = allStaves ? segment->elist() : std::vector<EngravingItem*> { bl };
    3700           45 :             for (EngravingItem* e : elist) {
    3701           36 :                 if (!e || !e->staff() || !e->isBarLine()) {
    3702           27 :                     continue;
    3703              :                 }
    3704              : 
    3705              :                 // handle linked staves/parts:
    3706              :                 // barlines themselves are not necessarily linked,
    3707              :                 // so use staffList to find linked staves
    3708            9 :                 BarLine* sbl = toBarLine(e);
    3709           18 :                 for (Staff* lstaff : sbl->staff()->staffList()) {
    3710            9 :                     Score* lscore = lstaff->score();
    3711            9 :                     track_idx_t ltrack = lstaff->idx() * VOICES;
    3712              : 
    3713              :                     // handle mmrests:
    3714              :                     // set the barline on the underlying measure
    3715              :                     // this will copied to the mmrest during layout, in createMMRest
    3716            9 :                     Measure* lmeasure = lscore->tick2measure(m2->tick());
    3717            9 :                     if (!lmeasure) {
    3718            0 :                         continue;
    3719              :                     }
    3720              : 
    3721            9 :                     lmeasure->undoChangeProperty(Pid::REPEAT_END, false);
    3722              : 
    3723            9 :                     Measure* nextMeasure = lmeasure->nextMeasure();
    3724            9 :                     if (nextMeasure && !keepStartRepeat) {
    3725            6 :                         nextMeasure->undoChangeProperty(Pid::REPEAT_START, false);
    3726              :                     }
    3727            9 :                     Segment* lsegment = lmeasure->undoGetSegmentR(SegmentType::EndBarLine, lmeasure->ticks());
    3728            9 :                     BarLine* lbl = toBarLine(lsegment->element(ltrack));
    3729            9 :                     if (!lbl) {
    3730            0 :                         lbl = Factory::createBarLine(lsegment);
    3731            0 :                         lbl->setTrack(ltrack);
    3732            0 :                         lbl->setSpanStaff(lstaff->barLineSpan());
    3733            0 :                         lbl->setSpanFrom(lstaff->barLineFrom());
    3734            0 :                         lbl->setSpanTo(lstaff->barLineTo());
    3735            0 :                         lbl->setBarLineType(barType);
    3736            0 :                         lbl->setGenerated(generated);
    3737            0 :                         lscore->addElement(lbl);
    3738            0 :                         if (!generated) {
    3739            0 :                             lbl->linkTo(sbl);
    3740              :                         }
    3741              :                     } else {
    3742            9 :                         lscore->undo(new ChangeProperty(lbl, Pid::GENERATED, generated, PropertyFlags::NOSTYLE));
    3743            9 :                         lscore->undo(new ChangeProperty(lbl, Pid::BARLINE_TYPE, PropertyValue::fromValue(barType), PropertyFlags::NOSTYLE));
    3744              :                         // set generated flag before and after so it sticks on type change and also works on undo/redo
    3745            9 :                         lscore->undo(new ChangeProperty(lbl, Pid::GENERATED, generated, PropertyFlags::NOSTYLE));
    3746            9 :                         if (lbl != sbl && !generated && !lbl->links()) {
    3747            0 :                             lscore->undo(new Link(lbl, sbl));
    3748            9 :                         } else if (lbl != sbl && generated && lbl->isLinked(sbl)) {
    3749            0 :                             lscore->undo(new Unlink(lbl));
    3750            0 :                             while (EngravingItem* linkedOnCoveringMMRest = sbl->findLinkedInScore(lscore)) {
    3751              :                                 // Edge case: an additional link of this barline exists on the covering mmRest of lbl's measure
    3752            0 :                                 lscore->undo(new Unlink(linkedOnCoveringMMRest));
    3753            0 :                                 lscore->doUndoRemoveElement(linkedOnCoveringMMRest);
    3754            0 :                             }
    3755              :                         }
    3756              :                     }
    3757            9 :                 }
    3758              :             }
    3759            9 :         } else if (segmentType == SegmentType::BeginBarLine) {
    3760            0 :             for (Score* lscore : m2->score()->scoreList()) {
    3761            0 :                 Measure* lmeasure = lscore->tick2measure(m2->tick());
    3762            0 :                 Segment* segment1 = lmeasure->undoGetSegmentR(SegmentType::BeginBarLine, Fraction(0, 1));
    3763            0 :                 for (EngravingItem* e : segment1->elist()) {
    3764            0 :                     if (e) {
    3765            0 :                         lscore->undo(new ChangeProperty(e, Pid::GENERATED, false, PropertyFlags::NOSTYLE));
    3766            0 :                         lscore->undo(new ChangeProperty(e, Pid::BARLINE_TYPE, PropertyValue::fromValue(barType), PropertyFlags::NOSTYLE));
    3767              :                         // set generated flag before and after so it sticks on type change and also works on undo/redo
    3768            0 :                         lscore->undo(new ChangeProperty(e, Pid::GENERATED, false, PropertyFlags::NOSTYLE));
    3769              :                     }
    3770              :                 }
    3771            0 :             }
    3772              :         }
    3773              :     }
    3774            9 :     break;
    3775            0 :     case BarLineType::START_REPEAT: {
    3776            0 :         for (size_t staffIdx = 0; staffIdx < m2->score()->nstaves(); ++staffIdx) {
    3777            0 :             if (m2->isMeasureRepeatGroupWithPrevM(staffIdx)) {
    3778            0 :                 MScore::setError(MsError::CANNOT_SPLIT_MEASURE_REPEAT);
    3779            0 :                 return;
    3780              :             }
    3781              :         }
    3782            0 :         for (Score* lscore : m2->score()->scoreList()) {
    3783            0 :             Measure* lmeasure = lscore->tick2measure(m2->tick());
    3784            0 :             if (lmeasure) {
    3785            0 :                 lmeasure->undoChangeProperty(Pid::REPEAT_START, true);
    3786            0 :                 lmeasure = lmeasure->prevMeasure();
    3787            0 :                 if (lmeasure && replace) {
    3788            0 :                     lmeasure->undoChangeProperty(Pid::REPEAT_END, false);
    3789              :                 }
    3790              :             }
    3791            0 :         }
    3792              :     }
    3793            0 :     break;
    3794            1 :     case BarLineType::END_REPEAT: {
    3795            3 :         for (size_t staffIdx = 0; staffIdx < m2->score()->nstaves(); ++staffIdx) {
    3796            2 :             if (m2->isMeasureRepeatGroupWithNextM(staffIdx)) {
    3797            0 :                 MScore::setError(MsError::CANNOT_SPLIT_MEASURE_REPEAT);
    3798            0 :                 return;
    3799              :             }
    3800              :         }
    3801            4 :         for (Score* lscore : m2->score()->scoreList()) {
    3802            3 :             Measure* lmeasure = lscore->tick2measure(m2->tick());
    3803            3 :             if (lmeasure) {
    3804            3 :                 lmeasure->undoChangeProperty(Pid::REPEAT_END, true);
    3805            3 :                 lmeasure = lmeasure->nextMeasure();
    3806            3 :                 if (lmeasure && replace) {
    3807            0 :                     lmeasure->undoChangeProperty(Pid::REPEAT_START, false);
    3808              :                 }
    3809              :             }
    3810            1 :         }
    3811              :     }
    3812            1 :     break;
    3813            0 :     case BarLineType::END_START_REPEAT: {
    3814            0 :         for (size_t staffIdx = 0; staffIdx < m2->score()->nstaves(); ++staffIdx) {
    3815            0 :             if (m2->isMeasureRepeatGroupWithNextM(staffIdx)) {
    3816            0 :                 MScore::setError(MsError::CANNOT_SPLIT_MEASURE_REPEAT);
    3817            0 :                 return;
    3818              :             }
    3819              :         }
    3820            0 :         for (Score* lscore : m2->score()->scoreList()) {
    3821            0 :             Measure* lmeasure = lscore->tick2measure(m2->tick());
    3822            0 :             if (lmeasure) {
    3823            0 :                 lmeasure->undoChangeProperty(Pid::REPEAT_END, true);
    3824            0 :                 lmeasure = lmeasure->nextMeasure();
    3825            0 :                 if (lmeasure) {
    3826            0 :                     lmeasure->undoChangeProperty(Pid::REPEAT_START, true);
    3827              :                 }
    3828              :             }
    3829            0 :         }
    3830              :     }
    3831            0 :     break;
    3832              :     }
    3833              : 
    3834           10 :     undoUpdatePlayCountText(m);
    3835              : }
    3836              : 
    3837              : //---------------------------------------------------------
    3838              : //   undoChangeElement
    3839              : //---------------------------------------------------------
    3840              : 
    3841          118 : void Score::undoChangeElement(EngravingItem* oldElement, EngravingItem* newElement)
    3842              : {
    3843          118 :     if (!oldElement) {
    3844            3 :         undoAddElement(newElement);
    3845              :     } else {
    3846          115 :         const std::list<EngravingObject*> links = oldElement->linkList();
    3847          230 :         for (EngravingObject* obj : links) {
    3848          115 :             EngravingItem* item = toEngravingItem(obj);
    3849          115 :             if (item == oldElement) {
    3850          115 :                 undo(new ChangeElement(oldElement, newElement));
    3851              :             } else {
    3852            0 :                 if (item->score()) {
    3853            0 :                     EngravingItem* newClone = newElement->clone();
    3854            0 :                     item->score()->undo(new ChangeElement(item, newClone));
    3855              :                 }
    3856              :             }
    3857              :         }
    3858          115 :     }
    3859          118 : }
    3860              : 
    3861           35 : void Score::updateInstrumentChangeTranspositions(KeySigEvent& key, Staff* staff, const Fraction& tick)
    3862              : {
    3863           35 :     DO_ASSERT(staff->score() == this);
    3864           35 :     if (!key.forInstrumentChange()) {
    3865           35 :         KeyList* kl = staff->keyList();
    3866           35 :         int nextTick = kl->nextKeyTick(tick.ticks());
    3867              : 
    3868           43 :         while (nextTick != -1) {
    3869            8 :             KeySigEvent e = kl->key(nextTick);
    3870            8 :             if (e.forInstrumentChange()) {
    3871            8 :                 if (key.isAtonal() && !e.isAtonal()) {
    3872            0 :                     e.setMode(KeyMode::NONE);
    3873            0 :                     e.setConcertKey(Key::C);
    3874              :                 } else {
    3875            8 :                     e.setMode(key.mode());
    3876            8 :                     Interval transposeInterval = staff->part()->instrument(Fraction::fromTicks(nextTick))->transpose();
    3877            8 :                     transposeInterval.flip();
    3878            8 :                     Key ckey = key.concertKey();
    3879            8 :                     Key nkey = Transpose::transposeKey(ckey, transposeInterval, staff->part()->preferSharpFlat());
    3880            8 :                     e.setConcertKey(ckey);
    3881            8 :                     e.setKey(nkey);
    3882              :                 }
    3883            8 :                 Measure* m = tick2measure(Fraction::fromTicks(nextTick));
    3884            8 :                 Segment* s = m ? m->tick2segment(Fraction::fromTicks(nextTick), SegmentType::KeySig) : nullptr;
    3885            8 :                 track_idx_t track = staff->idx() * VOICES;
    3886            8 :                 KeySig* keySig = nullptr;
    3887            8 :                 EngravingItem* keySigElem = s ? s->element(track) : nullptr;
    3888            8 :                 if (keySigElem && keySigElem->isKeySig()) {
    3889            8 :                     keySig = toKeySig(keySigElem);
    3890              :                 }
    3891            8 :                 if (keySig) {
    3892            8 :                     undo(new ChangeKeySig(keySig, e, keySig->showCourtesy()));
    3893              :                 }
    3894            8 :                 nextTick = kl->nextKeyTick(nextTick);
    3895              :             } else {
    3896            0 :                 nextTick = -1;
    3897              :             }
    3898            8 :         }
    3899              :     }
    3900           35 : }
    3901              : 
    3902              : //---------------------------------------------------------
    3903              : //   findLinkedChord
    3904              : //---------------------------------------------------------
    3905              : 
    3906          537 : static Chord* findLinkedChord(Chord* c, Staff* nstaff)
    3907              : {
    3908          537 :     Excerpt* se = c->score()->excerpt();
    3909          537 :     Excerpt* de = nstaff->score()->excerpt();
    3910          537 :     track_idx_t strack = c->track();
    3911              : 
    3912          537 :     if (se) {
    3913            0 :         strack = muse::key(se->tracksMapping(), strack);
    3914              :     }
    3915          537 :     track_idx_t dtrack = nstaff->idx() * VOICES + strack % VOICES;
    3916              : 
    3917          537 :     if (de) {
    3918            0 :         std::vector<track_idx_t> l = muse::values(de->tracksMapping(), strack);
    3919            0 :         if (l.empty()) {
    3920              :             // simply return the first linked chord whose staff is equal to nstaff
    3921            0 :             for (EngravingObject* ee : c->linkList()) {
    3922            0 :                 Chord* ch = toChord(ee);
    3923            0 :                 if (ch->staff() == nstaff) {
    3924            0 :                     return ch;
    3925              :                 }
    3926            0 :             }
    3927            0 :             return nullptr;
    3928              :         }
    3929            0 :         for (track_idx_t i : l) {
    3930            0 :             if (nstaff->idx() * VOICES <= i && (nstaff->idx() + 1) * VOICES > i) {
    3931            0 :                 dtrack = i;
    3932            0 :                 break;
    3933              :             }
    3934              :         }
    3935            0 :     }
    3936              : 
    3937          537 :     Segment* s = c->segment();
    3938          537 :     Measure* nm = nstaff->score()->tick2measure(s->tick());
    3939          537 :     Segment* ns = nm->findSegment(s->segmentType(), s->tick());
    3940          537 :     EngravingItem* ne = ns ? ns->element(dtrack) : nullptr;
    3941          537 :     if (!ne || !ne->isChord()) {
    3942            0 :         return nullptr;
    3943              :     }
    3944          537 :     Chord* nc = toChord(ne);
    3945          537 :     if (c->isGrace()) {
    3946            3 :         Chord* pc = toChord(c->ownershipParent());
    3947            3 :         size_t index = 0;
    3948            3 :         for (Chord* gc : pc->graceNotes()) {
    3949            3 :             if (c == gc) {
    3950            3 :                 break;
    3951              :             }
    3952            0 :             index++;
    3953              :         }
    3954            3 :         if (index < nc->graceNotes().size()) {
    3955            3 :             nc = nc->graceNotes().at(index);
    3956              :         }
    3957              :     }
    3958          537 :     return nc;
    3959              : }
    3960              : 
    3961              : //---------------------------------------------------------
    3962              : //   undoChangeChordRestLen
    3963              : //---------------------------------------------------------
    3964              : 
    3965           69 : void Score::undoChangeChordRestLen(ChordRest* cr, const TDuration& d)
    3966              : {
    3967           69 :     cr->undoChangeProperty(Pid::DURATION_TYPE_WITH_DOTS, d.typeWithDots());
    3968           69 :     cr->undoChangeProperty(Pid::DURATION, d.fraction());
    3969           69 : }
    3970              : 
    3971              : //---------------------------------------------------------
    3972              : //   undoRemovePart
    3973              : //---------------------------------------------------------
    3974              : 
    3975            8 : void Score::undoRemovePart(Part* part, size_t partIdx)
    3976              : {
    3977            8 :     undo(new RemovePart(part, partIdx));
    3978            8 : }
    3979              : 
    3980              : //---------------------------------------------------------
    3981              : //   undoInsertPart
    3982              : //---------------------------------------------------------
    3983              : 
    3984           14 : void Score::undoInsertPart(Part* part, size_t targetPartIndex)
    3985              : {
    3986           14 :     undo(new InsertPart(part, targetPartIndex));
    3987           14 : }
    3988              : 
    3989              : //---------------------------------------------------------
    3990              : //   undoRemoveStaff
    3991              : //    idx - index of staff in part
    3992              : //---------------------------------------------------------
    3993              : 
    3994           23 : void Score::undoRemoveStaff(Staff* staff)
    3995              : {
    3996           23 :     const staff_idx_t staffIndex = staff->idx();
    3997           23 :     assert(staffIndex != muse::nidx);
    3998              : 
    3999           23 :     std::vector<Spanner*> spannersToRemove;
    4000              : 
    4001           37 :     for (auto it = m_spanner.cbegin(); it != m_spanner.cend(); ++it) {
    4002           14 :         Spanner* spanner = it->second;
    4003              : 
    4004           14 :         if (allowRemoveWhenRemovingStaves(spanner, staffIndex)) {
    4005           11 :             spannersToRemove.push_back(spanner);
    4006              :         }
    4007              :     }
    4008              : 
    4009           27 :     for (Spanner* spanner : m_unmanagedSpanner) {
    4010            4 :         if (allowRemoveWhenRemovingStaves(spanner, staffIndex)) {
    4011            4 :             spannersToRemove.push_back(spanner);
    4012              :         }
    4013              :     }
    4014              : 
    4015           38 :     for (Spanner* spanner : spannersToRemove) {
    4016           15 :         spanner->undoUnlink();
    4017           15 :         doUndoRemoveElement(spanner);
    4018              :     }
    4019              : 
    4020              :     //
    4021              :     //    adjust measures
    4022              :     //
    4023          276 :     for (Measure* m = staff->score()->firstMeasure(); m; m = m->nextMeasure()) {
    4024          253 :         m->cmdRemoveStaves(staffIndex, staffIndex + 1);
    4025          253 :         if (m->hasMMRest()) {
    4026            0 :             m->mmRest()->cmdRemoveStaves(staffIndex, staffIndex + 1);
    4027              :         }
    4028              :     }
    4029              : 
    4030           23 :     staff->undoUnlink();
    4031              : 
    4032           23 :     undo(new RemoveStaff(staff));
    4033           23 : }
    4034              : 
    4035              : //---------------------------------------------------------
    4036              : //   undoInsertStaff
    4037              : //    idx - index of staff in part
    4038              : //---------------------------------------------------------
    4039              : 
    4040           57 : void Score::undoInsertStaff(Staff* staff, staff_idx_t ridx, bool createRests)
    4041              : {
    4042           57 :     undo(new InsertStaff(staff, ridx));
    4043           57 :     staff_idx_t idx = staffIdx(staff->part()) + ridx;
    4044          433 :     for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
    4045          376 :         m->cmdAddStaves(idx, idx + 1, createRests);
    4046          376 :         if (m->hasMMRest()) {
    4047            0 :             m->mmRest()->cmdAddStaves(idx, idx + 1, false);
    4048              :         }
    4049              :     }
    4050              :     // when newly adding an instrument,
    4051              :     // this was already set when we created the staff
    4052              :     // we don't have any better info at this point
    4053              :     // and it doesn't work to adjust bracket & barlines until all staves are added
    4054              :     // TODO: adjust brackets only when appropriate
    4055              :     //adjustBracketsIns(idx, idx+1);
    4056           57 : }
    4057              : 
    4058              : //---------------------------------------------------------
    4059              : //   undoAddElement
    4060              : //---------------------------------------------------------
    4061              : 
    4062         8681 : void Score::undoAddElement(EngravingItem* element, bool addToLinkedStaves, bool ctrlModifier, EngravingItem* elementToRelink)
    4063              : {
    4064         8681 :     Transaction& tx = transactionManager()->currentOrDummyTransaction();
    4065              : 
    4066         8681 :     Staff* ostaff = element->staff();
    4067         8681 :     track_idx_t strack = element->track();
    4068              : 
    4069         8681 :     ElementType et = element->type();
    4070              : 
    4071              :     //
    4072              :     // some elements are replicated for all parts regardless of
    4073              :     // linking:
    4074              :     //
    4075              : 
    4076         8681 :     bool isSystemLine = isSystemTextLine(element);
    4077              : 
    4078         8681 :     if ((et == ElementType::REHEARSAL_MARK)
    4079         8670 :         || (et == ElementType::SYSTEM_TEXT)
    4080         8661 :         || (et == ElementType::TRIPLET_FEEL)
    4081         8661 :         || (et == ElementType::JUMP)
    4082         8661 :         || (et == ElementType::MARKER)
    4083         8659 :         || (et == ElementType::TEMPO_TEXT)
    4084         8564 :         || (et == ElementType::PLAY_COUNT_TEXT)
    4085         8564 :         || isSystemLine
    4086              :         ) {
    4087          121 :         std::vector<Staff* > staffList;
    4088              : 
    4089          121 :         if (!addToLinkedStaves) {
    4090            0 :             staffList.push_back(element->staff());
    4091          121 :         } else if (ctrlModifier && isSystemLine) {
    4092            0 :             staffList = ostaff->staffList();
    4093            0 :             element->setSystemFlag(false);
    4094              :         } else {
    4095          243 :             for (Score* s : scoreList()) {
    4096          122 :                 staffList.push_back(s->staff(0)); // system objects always appear on the top staff
    4097          182 :                 for (Staff* staff : s->systemObjectStaves()) {
    4098           60 :                     IF_ASSERT_FAILED(staff->idx() != muse::nidx) {
    4099            0 :                         continue;
    4100              :                     }
    4101              : 
    4102           60 :                     staffList.push_back(staff);
    4103              :                 }
    4104          121 :             }
    4105              :         }
    4106              : 
    4107          121 :         if (elementToRelink) {
    4108            1 :             muse::remove(staffList, ostaff);
    4109              :         }
    4110              : 
    4111          121 :         bool originalAdded = false;
    4112          302 :         for (Staff* staff : staffList) {
    4113          181 :             if (!staff) {
    4114            0 :                 continue;
    4115              :             }
    4116              : 
    4117          181 :             Score* score  = staff->score();
    4118          181 :             staff_idx_t staffIdx = staff->idx();
    4119          181 :             size_t ntrack = staffIdx * VOICES;
    4120              :             EngravingItem* ne;
    4121              : 
    4122          181 :             if (ostaff && staff->score() == ostaff->score() && staff == staffList.front() && !originalAdded) {
    4123              :                 // add the element itself to the first system object staff in the score
    4124          120 :                 ne = element;
    4125          120 :                 originalAdded = true;
    4126              :             } else {
    4127              :                 // add linked clones to the other staves
    4128           61 :                 ne = element->linkedClone();
    4129           61 :                 ne->setScore(score);
    4130           61 :                 ne->setSelected(false);
    4131           61 :                 ne->setTrack(staffIdx * VOICES + element->voice());
    4132              :             }
    4133              : 
    4134          181 :             if (isSystemLine) {
    4135            5 :                 Spanner* nsp = toSpanner(ne);
    4136            5 :                 Spanner* sp = toSpanner(element);
    4137            5 :                 long long diff = sp->track2() - sp->track();
    4138            5 :                 nsp->setTrack2(nsp->track() + diff);
    4139            5 :                 nsp->computeStartElement();
    4140            5 :                 nsp->computeEndElement();
    4141            5 :                 doUndoAddElement(nsp);
    4142          176 :             } else if (et == ElementType::MARKER || et == ElementType::JUMP) {
    4143            2 :                 Measure* om = toMeasure(element->ownershipParent());
    4144            2 :                 Measure* m  = score->tick2measure(om->tick());
    4145            2 :                 ne->setTrack(ntrack);
    4146            2 :                 ne->setOwnershipParent(m);
    4147            2 :                 doUndoAddElement(ne);
    4148          176 :             } else if (et == ElementType::MEASURE_NUMBER) {
    4149            0 :                 toMeasure(element->ownershipParent())->undoChangeProperty(Pid::MEASURE_NUMBER_MODE,
    4150              :                                                                           static_cast<int>(MeasureNumberMode::SHOW));
    4151          174 :             } else if (et == ElementType::PLAY_COUNT_TEXT) {
    4152            0 :                 Segment* segment  = toSegment(element->ownershipParent());
    4153            0 :                 Fraction tick     = segment->tick();
    4154            0 :                 Measure* m        = score->tick2measure(tick - Fraction::eps());
    4155            0 :                 Segment* seg      = m->undoGetSegment(SegmentType::EndBarLine, tick);
    4156            0 :                 ne->setTrack(ntrack);
    4157            0 :                 ne->setOwnershipParent(seg);
    4158            0 :                 doUndoAddElement(ne);
    4159              :             } else {
    4160          174 :                 Segment* segment  = toSegment(element->ownershipParent());
    4161          174 :                 Fraction tick     = segment->tick();
    4162          174 :                 Measure* m        = score->tick2measure(tick);
    4163          174 :                 Segment* seg      = m->undoGetSegment(SegmentType::ChordRest, tick);
    4164          174 :                 ne->setTrack(ntrack);
    4165          174 :                 ne->setOwnershipParent(seg);
    4166          174 :                 doUndoAddElement(ne);
    4167              :             }
    4168              :         }
    4169              : 
    4170          121 :         return;
    4171          121 :     }
    4172              : 
    4173        17120 :     if (et == ElementType::FINGERING
    4174         8557 :         || (et == ElementType::IMAGE && !element->ownershipParent()->isSegment())
    4175         8554 :         || (et == ElementType::SYMBOL && !element->ownershipParent()->isSegment())
    4176         8551 :         || et == ElementType::NOTE
    4177         8471 :         || et == ElementType::TEXT
    4178         8471 :         || et == ElementType::GLISSANDO
    4179         8460 :         || et == ElementType::GUITAR_BEND
    4180         8168 :         || et == ElementType::NOTELINE
    4181         8168 :         || et == ElementType::BEND
    4182         8168 :         || (et == ElementType::CHORD && toChord(element)->isGrace())
    4183         8098 :         || et == ElementType::LAISSEZ_VIB
    4184         8080 :         || et == ElementType::PARTIAL_TIE
    4185        17117 :         || et == ElementType::PARENTHESIS
    4186              :         ) {
    4187          540 :         const EngravingObject* parent = element->ownershipParent();
    4188          540 :         const LinkedObjects* links = parent ? parent->links() : nullptr;
    4189              : 
    4190              :         // don't link part name
    4191          540 :         if (et == ElementType::TEXT) {
    4192            0 :             Text* t = toText(element);
    4193            0 :             if (t->textStyleType() == TextStyleType::INSTRUMENT_EXCERPT) {
    4194            0 :                 links = 0;
    4195              :             }
    4196              :         }
    4197          540 :         if (links == 0 || !addToLinkedStaves) {
    4198          523 :             doUndoAddElement(element);
    4199          523 :             return;
    4200              :         }
    4201           51 :         for (EngravingObject* ee : *links) {
    4202           34 :             EngravingItem* e = static_cast<EngravingItem*>(ee);
    4203              :             EngravingItem* ne;
    4204           34 :             if (e == parent) {
    4205           17 :                 ne = element;
    4206              :             } else {
    4207           17 :                 if (element->isGlissando() || element->isGuitarBend() || element->isNoteLine()) {            // and other spanners with Anchor::NOTE
    4208            2 :                     Note* newEnd = Spanner::endElementFromSpanner(toSpanner(element), e);
    4209            2 :                     if (newEnd) {
    4210            2 :                         ne = element->linkedClone();
    4211            2 :                         toSpanner(ne)->setNoteSpan(toNote(e), newEnd);
    4212            2 :                         if (element->isGuitarBend() && element->staffType()->isTabStaff()) {
    4213            0 :                             toGuitarBend(ne)->endNote()->setVisible(true);
    4214              :                         }
    4215              :                     } else {                    //couldn't find suitable start note
    4216            0 :                         continue;
    4217              :                     }
    4218           15 :                 } else if (element->isFingering()) {
    4219            3 :                     bool tabFingering = e->staff()->staffType(e->tick())->showTabFingering();
    4220            3 :                     if (e->staff()->isTabStaff(e->tick()) && !tabFingering) {
    4221            0 :                         continue;
    4222              :                     }
    4223            3 :                     ne = element->linkedClone();
    4224              :                 } else {
    4225           12 :                     ne = element->linkedClone();
    4226              :                 }
    4227              :             }
    4228           34 :             ne->setScore(e->score());
    4229           34 :             ne->setSelected(false);
    4230           34 :             ne->setOwnershipParent(e);
    4231           34 :             doUndoAddElement(ne);
    4232              :         }
    4233           17 :         return;
    4234              :     }
    4235              : 
    4236         8020 :     if (et == ElementType::LAYOUT_BREAK) {
    4237           10 :         LayoutBreak* lb = toLayoutBreak(element);
    4238           10 :         if (lb->layoutBreakType() == LayoutBreakType::SECTION) {
    4239            0 :             doUndoAddElement(lb);
    4240            0 :             MeasureBase* m = lb->measure();
    4241            0 :             if (m->isBox()) {
    4242              :                 // for frames, use linked frames
    4243            0 :                 LinkedObjects* links = m->links();
    4244            0 :                 if (links) {
    4245            0 :                     for (EngravingObject* lo : *links) {
    4246            0 :                         if (lo->isBox()) {
    4247            0 :                             Box* box = toBox(lo);
    4248            0 :                             Score* score = box->score();
    4249            0 :                             if (score != lb->score()) {
    4250            0 :                                 EngravingItem* e = lb->linkedClone();
    4251            0 :                                 e->setScore(score);
    4252            0 :                                 e->setOwnershipParent(box);
    4253            0 :                                 doUndoAddElement(e);
    4254              :                             }
    4255              :                         }
    4256              :                     }
    4257            0 :                     return;
    4258              :                 } else { // if thera are not linked frames, use previous measure
    4259            0 :                     m = m->prevMeasure();
    4260            0 :                     if (!m) {
    4261            0 :                         return;
    4262              :                     }
    4263              :                 }
    4264              :             }
    4265            0 :             for (Score* s : scoreList()) {
    4266            0 :                 if (s != lb->score()) {
    4267            0 :                     EngravingItem* e = lb->linkedClone();
    4268            0 :                     e->setScore(s);
    4269            0 :                     Measure* nm = s->tick2measure(m->tick());
    4270            0 :                     e->setOwnershipParent(nm);
    4271            0 :                     doUndoAddElement(e);
    4272              :                 }
    4273            0 :             }
    4274            0 :             return;
    4275              :         }
    4276              :     }
    4277              : 
    4278         8020 :     if (ostaff == 0 || (
    4279         2396 :             et != ElementType::ARTICULATION
    4280         2349 :             && et != ElementType::ORNAMENT
    4281         2346 :             && et != ElementType::TAPPING
    4282         2346 :             && et != ElementType::CHORDLINE
    4283         2343 :             && et != ElementType::LYRICS
    4284         2331 :             && et != ElementType::SLUR
    4285         2301 :             && et != ElementType::HAMMER_ON_PULL_OFF
    4286         2301 :             && et != ElementType::TIE
    4287         2042 :             && et != ElementType::NOTE
    4288         2042 :             && et != ElementType::INSTRUMENT_CHANGE
    4289         2038 :             && et != ElementType::HAIRPIN
    4290         2022 :             && et != ElementType::OTTAVA
    4291         2019 :             && et != ElementType::TRILL
    4292         2018 :             && et != ElementType::VIBRATO
    4293         2018 :             && et != ElementType::TEXTLINE
    4294         2018 :             && et != ElementType::PEDAL
    4295         2017 :             && et != ElementType::LET_RING
    4296         2017 :             && et != ElementType::PALM_MUTE
    4297         2017 :             && et != ElementType::WHAMMY_BAR
    4298         2017 :             && et != ElementType::PARTIAL_LYRICSLINE
    4299         2017 :             && et != ElementType::BREATH
    4300         2003 :             && et != ElementType::DYNAMIC
    4301         1978 :             && et != ElementType::EXPRESSION
    4302         1978 :             && et != ElementType::STAFF_TEXT
    4303         1950 :             && et != ElementType::STAVE_SHARING_LABEL
    4304         1948 :             && et != ElementType::SYSTEM_TEXT
    4305         1948 :             && et != ElementType::TRIPLET_FEEL
    4306         1948 :             && et != ElementType::PLAYTECH_ANNOTATION
    4307         1948 :             && et != ElementType::CAPO
    4308         1948 :             && et != ElementType::STRING_TUNINGS
    4309         1948 :             && et != ElementType::STICKING
    4310         1933 :             && et != ElementType::TREMOLO_SINGLECHORD
    4311         1915 :             && et != ElementType::TREMOLO_TWOCHORD
    4312         1915 :             && et != ElementType::ARPEGGIO
    4313         1915 :             && et != ElementType::CHORD_BRACKET
    4314         1915 :             && et != ElementType::SYMBOL
    4315         1915 :             && et != ElementType::IMAGE
    4316         1915 :             && et != ElementType::TREMOLOBAR
    4317         1915 :             && et != ElementType::FRET_DIAGRAM
    4318         1890 :             && et != ElementType::FERMATA
    4319         1888 :             && et != ElementType::HARMONY
    4320         1864 :             && et != ElementType::HARP_DIAGRAM
    4321         1859 :             && et != ElementType::FIGURED_BASS
    4322         1859 :             && et != ElementType::CLEF
    4323         1850 :             && et != ElementType::AMBITUS
    4324         1850 :             && et != ElementType::PLAY_COUNT_TEXT)
    4325              :         ) {
    4326         7474 :         doUndoAddElement(element);
    4327         7474 :         return;
    4328              :     }
    4329              : 
    4330          546 :     std::vector<Staff*> staves;
    4331          546 :     if (addToLinkedStaves) {
    4332          543 :         staves = ostaff->staffList();
    4333              :     } else {
    4334            3 :         staves.push_back(ostaff);
    4335              :     }
    4336              : 
    4337          546 :     if (elementToRelink) {
    4338            2 :         muse::remove(staves, ostaff);
    4339              :     }
    4340              : 
    4341         1112 :     for (Staff* staff : staves) {
    4342          566 :         Score* score = staff->score();
    4343          566 :         staff_idx_t staffIdx = staff->idx();
    4344              : 
    4345              :         // Some elements in voice 1 of a staff should be copied to every track which has a linked voice in this staff
    4346              :         static const std::set<ElementType> VOICE1_COPY_TYPES = {
    4347              :             ElementType::SYMBOL,
    4348              :             ElementType::IMAGE,
    4349              :             ElementType::TREMOLOBAR,
    4350              :             ElementType::DYNAMIC,
    4351              :             ElementType::EXPRESSION,
    4352              :             ElementType::STAFF_TEXT,
    4353              :             ElementType::PLAYTECH_ANNOTATION,
    4354              :             ElementType::CAPO,
    4355              :             ElementType::STRING_TUNINGS,
    4356              :             ElementType::STICKING,
    4357              :             ElementType::FRET_DIAGRAM,
    4358              :             ElementType::HARMONY,
    4359              :             ElementType::HAIRPIN,
    4360              :             ElementType::OTTAVA,
    4361              :             ElementType::TRILL,
    4362              :             ElementType::SLUR,
    4363              :             ElementType::HAMMER_ON_PULL_OFF,
    4364              :             ElementType::VIBRATO,
    4365              :             ElementType::TEXTLINE,
    4366              :             ElementType::PEDAL,
    4367              :             ElementType::LYRICS,
    4368              :             ElementType::PARTIAL_LYRICSLINE,
    4369              :             ElementType::CLEF,
    4370              :             ElementType::AMBITUS
    4371          570 :         };
    4372              : 
    4373          566 :         track_idx_t linkedTrack = ostaff->getLinkedTrackInStaff(staff, strack);
    4374          566 :         if (linkedTrack == muse::nidx) {
    4375            0 :             if (track2voice(strack) == 0 && muse::contains(VOICE1_COPY_TYPES, et)) {
    4376            0 :                 linkedTrack = staff2track(staffIdx);
    4377              :             } else {
    4378            0 :                 continue;
    4379              :             }
    4380              :         }
    4381              : 
    4382              :         EngravingItem* ne;
    4383          566 :         if (staff == ostaff) {
    4384          544 :             ne = element;
    4385              :         } else {
    4386           22 :             if (staff->rstaff() != ostaff->rstaff()) {
    4387            1 :                 switch (element->type()) {
    4388              :                 // exclude certain element types except on corresponding staff in part
    4389              :                 // this should be same list excluded in cloneStaff()
    4390            1 :                 case ElementType::STAFF_TEXT:
    4391              :                 case ElementType::SYSTEM_TEXT:
    4392              :                 case ElementType::TRIPLET_FEEL:
    4393              :                 case ElementType::PLAYTECH_ANNOTATION:
    4394              :                 case ElementType::CAPO:
    4395              :                 case ElementType::STRING_TUNINGS:
    4396              :                 case ElementType::FRET_DIAGRAM:
    4397              :                 case ElementType::HARMONY:
    4398              :                 case ElementType::FIGURED_BASS:
    4399              :                 case ElementType::DYNAMIC:
    4400              :                 case ElementType::EXPRESSION:
    4401              :                 case ElementType::LYRICS:                       // not normally segment-attached
    4402              :                 case ElementType::PARTIAL_LYRICSLINE:
    4403              :                 case ElementType::PLAY_COUNT_TEXT:
    4404              :                 case ElementType::WHAMMY_BAR:
    4405            1 :                     continue;
    4406            0 :                 default:
    4407            0 :                     break;
    4408              :                 }
    4409              :             }
    4410           21 :             ne = element->linkedClone();
    4411           21 :             ne->setScore(score);
    4412           21 :             ne->setSelected(false);
    4413           21 :             ne->setTrack(linkedTrack);
    4414              : 
    4415           21 :             if (ne->isFretDiagram()) {
    4416            0 :                 FretDiagram* fd = toFretDiagram(ne);
    4417            0 :                 Harmony* fdHarmony = fd->harmony();
    4418            0 :                 if (fdHarmony) {
    4419              :                     // TODO: this stuff should be done by overriding setScore setSelected and setTrack for FretDiagram [M.S.]
    4420            0 :                     fdHarmony->setScore(score);
    4421            0 :                     fdHarmony->setSelected(false);
    4422            0 :                     fdHarmony->setTrack(linkedTrack);
    4423              :                 }
    4424              :             }
    4425              :         }
    4426              : 
    4427          565 :         if (element->isArticulationFamily()) {
    4428           50 :             Articulation* a  = toArticulation(element);
    4429              :             Segment* segment;
    4430              :             SegmentType st;
    4431              :             Measure* m;
    4432           50 :             Fraction tick;
    4433           50 :             if (a->ownershipParent()->isChordRest()) {
    4434           50 :                 ChordRest* cr = a->chordRest();
    4435           50 :                 segment       = cr->segment();
    4436           50 :                 st            = SegmentType::ChordRest;
    4437           50 :                 tick          = segment->tick();
    4438           50 :                 m             = score->tick2measure(tick);
    4439              :             } else {
    4440            0 :                 segment  = toSegment(a->ownershipParent()->ownershipParent());
    4441            0 :                 st       = SegmentType::EndBarLine;
    4442            0 :                 tick     = segment->tick();
    4443            0 :                 m        = score->tick2measure(tick);
    4444            0 :                 if (m->tick() == tick) {
    4445            0 :                     m = m->prevMeasure();
    4446              :                 }
    4447              :             }
    4448           50 :             Segment* seg = m->findSegment(st, tick);
    4449           50 :             if (seg == 0) {
    4450            0 :                 LOGW("undoAddSegment: segment not found");
    4451            0 :                 break;
    4452              :             }
    4453           50 :             Articulation* na = toArticulation(ne);
    4454           50 :             na->setTrack(linkedTrack);
    4455           50 :             if (a->ownershipParent()->isChordRest()) {
    4456           50 :                 ChordRest* cr = a->chordRest();
    4457              :                 ChordRest* ncr;
    4458           50 :                 if (cr->isGrace()) {
    4459            1 :                     ncr = findLinkedChord(toChord(cr), score->staff(staffIdx));
    4460              :                 } else {
    4461           49 :                     ncr = toChordRest(seg->element(linkedTrack));
    4462              :                 }
    4463           50 :                 na->setOwnershipParent(ncr);
    4464              :             } else {
    4465            0 :                 BarLine* bl = toBarLine(seg->element(linkedTrack));
    4466            0 :                 na->setOwnershipParent(bl);
    4467              :             }
    4468           50 :             doUndoAddElement(na);
    4469          515 :         } else if (element->isChordLine() || element->isLyrics()) {
    4470           18 :             ChordRest* cr    = toChordRest(element->ownershipParent());
    4471           18 :             Segment* segment = cr->segment();
    4472           18 :             Fraction tick    = segment->tick();
    4473           18 :             Measure* m       = score->tick2measure(tick);
    4474           18 :             Segment* seg     = m->findSegment(SegmentType::ChordRest, tick);
    4475           18 :             if (seg == 0) {
    4476            0 :                 LOGW("undoAddSegment: segment not found");
    4477            0 :                 break;
    4478              :             }
    4479           18 :             ne->setTrack(linkedTrack);
    4480           18 :             ChordRest* ncr = toChordRest(seg->element(linkedTrack));
    4481           18 :             ne->setOwnershipParent(ncr);
    4482           18 :             if (element->isChordLine()) {
    4483            6 :                 if (cr->isGrace()) {
    4484            0 :                     ncr = findLinkedChord(toChord(cr), score->staff(staffIdx));
    4485            0 :                     ne->setOwnershipParent(ncr);
    4486              :                 }
    4487            6 :                 ChordLine* oldChordLine = toChordLine(element);
    4488            6 :                 ChordLine* newChordLine = toChordLine(ne);
    4489              :                 // Chordline also needs to know the new note
    4490            6 :                 Note* newNote = toChord(ncr)->findNote(oldChordLine->note()->pitch());
    4491            6 :                 newChordLine->setNote(newNote);
    4492              :             }
    4493           18 :             doUndoAddElement(ne);
    4494          497 :         } else if (element->isHarmony() && element->ownershipParent()->isFretDiagram()) {
    4495            1 :             EngravingItem* parentFd = element->ownershipParentItem();
    4496            1 :             if (parentFd->score() != score) {
    4497              :                 // Find linked fret diagram
    4498            0 :                 EngravingItem* linkedFd = parentFd->findLinkedInScore(score);
    4499            0 :                 ne->setOwnershipParent(linkedFd);
    4500              :             }
    4501            1 :             doUndoAddElement(ne);
    4502              :         }
    4503              :         //
    4504              :         // elements with Segment as parent
    4505              :         //
    4506          496 :         else if (element->isSymbol()
    4507          496 :                  || element->isImage()
    4508          496 :                  || element->isTremoloBar()
    4509          496 :                  || element->isDynamic()
    4510          470 :                  || element->isExpression()
    4511          470 :                  || element->isStaffText()
    4512          442 :                  || element->isStaveSharingLabel()
    4513          440 :                  || element->isPlayTechAnnotation()
    4514          440 :                  || element->isCapo()
    4515          440 :                  || element->isStringTunings()
    4516          440 :                  || element->isSticking()
    4517          425 :                  || element->isFretDiagram()
    4518          400 :                  || element->isFermata()
    4519          397 :                  || element->isHarmony() // Case with parent FretDiagram handled above, so we know it has parent Segment
    4520          373 :                  || element->isHarpPedalDiagram()
    4521          368 :                  || element->isFiguredBass()
    4522          368 :                  || element->isClef()
    4523          992 :                  || element->isAmbitus()) {
    4524          139 :             Segment* segment = toSegment(element->ownershipParent());
    4525          139 :             Fraction tick    = segment->tick();
    4526          139 :             Measure* m       = score->tick2measure(tick);
    4527          139 :             bool addClefToPrevMeasure = segment->isType(SegmentType::Clef) && element->isClef() && !toClef(element)->isHeader();
    4528          139 :             bool addBlToPrevMeasure = segment->isType(SegmentType::EndBarLine);
    4529          139 :             if (m->tick() == tick && (addClefToPrevMeasure || addBlToPrevMeasure) && m->prevMeasure()) {
    4530            1 :                 m = m->prevMeasure();
    4531              :             }
    4532          139 :             Segment* seg     = m->undoGetSegment(segment->segmentType(), tick);
    4533          139 :             ne->setTrack(linkedTrack);
    4534          139 :             ne->setOwnershipParent(seg);
    4535              : 
    4536          139 :             if (ne->isStringTunings()) {
    4537            0 :                 StringTunings* stringTunings = toStringTunings(ne);
    4538            0 :                 if (stringTunings->stringData()->isNull()) {
    4539            0 :                     const StringData* stringData = stringTunings->originPart()->stringData(tick, staff->idx());
    4540            0 :                     int frets = stringData->frets();
    4541            0 :                     std::vector<mu::engraving::instrString> stringList = stringData->stringList();
    4542              : 
    4543            0 :                     stringTunings->setStringData(StringData(frets, stringList));
    4544            0 :                 }
    4545              :             }
    4546              : 
    4547          139 :             doUndoAddElement(ne);
    4548              :             // transpose harmony if necessary
    4549          139 :             if (element->isHarmony() && ne != element) {
    4550            1 :                 Harmony* h = toHarmony(ne);
    4551            1 :                 if (score->style().styleB(Sid::concertPitch) != element->style().styleB(Sid::concertPitch)) {
    4552            0 :                     Staff* staffDest = h->staff();
    4553            0 :                     Interval interval = staffDest->transpose(tick);
    4554            0 :                     if (!interval.isZero()) {
    4555            0 :                         if (!score->style().styleB(Sid::concertPitch)) {
    4556            0 :                             interval.flip();
    4557              :                         }
    4558            0 :                         Transpose::undoTransposeHarmony(tx, h, interval);
    4559              :                     }
    4560              :                 }
    4561              :             }
    4562              : 
    4563              :             // Match vertical align settings of exiting items
    4564          139 :             if (ne->isHarmony() || ne->isFretDiagram()) {
    4565           49 :                 bool exclude = ne->excludeVerticalAlign();
    4566           49 :                 for (EngravingItem* item : seg->annotations()) {
    4567           73 :                     if ((!item->isFretDiagram() && !item->isHarmony())
    4568           73 :                         || item->staffIdx() != ne->staffIdx()) {
    4569            0 :                         continue;
    4570              :                     }
    4571              : 
    4572           49 :                     exclude = item->excludeVerticalAlign();
    4573           49 :                     break;
    4574              :                 }
    4575           49 :                 if (exclude != ne->excludeVerticalAlign()) {
    4576            0 :                     ne->undoChangeProperty(Pid::EXCLUDE_VERTICAL_ALIGN, exclude);
    4577              :                 }
    4578              :             }
    4579          357 :         } else if (element->isSlur()
    4580          326 :                    || element->isHairpin()
    4581          309 :                    || element->isOttava()
    4582          305 :                    || element->isTrill()
    4583          303 :                    || element->isVibrato()
    4584          303 :                    || element->isTextLine()
    4585          303 :                    || element->isPedal()
    4586          301 :                    || element->isLetRing()
    4587          301 :                    || element->isPalmMute()
    4588          301 :                    || element->isWhammyBar()
    4589          683 :                    || element->isPartialLyricsLine()) {
    4590           56 :             Spanner* sp   = toSpanner(element);
    4591           56 :             Spanner* nsp  = toSpanner(ne);
    4592           56 :             track_idx_t tr2 = sp->effectiveTrack2();
    4593           56 :             int diff = static_cast<int>(tr2 - sp->track());
    4594           56 :             nsp->setTrack2(linkedTrack + diff);
    4595           56 :             nsp->setTrack(linkedTrack);
    4596              : 
    4597              :             // determine start/end element for slurs
    4598              :             // this is only necessary if start/end element is
    4599              :             //   a grace note, otherwise the element can be set to zero
    4600              :             //   and will later be calculated from tick/track values
    4601              :             //
    4602           56 :             if (element->isSlur() && sp != nsp) {
    4603            1 :                 if (sp->startElement()) {
    4604            1 :                     std::list<EngravingObject*> sel = sp->startElement()->linkList();
    4605            2 :                     for (EngravingObject* ee : sel) {
    4606            2 :                         EngravingItem* e = static_cast<EngravingItem*>(ee);
    4607            2 :                         if (e->score() == nsp->score() && e->track() == nsp->track()) {
    4608            1 :                             nsp->setStartElement(e);
    4609            1 :                             break;
    4610              :                         }
    4611              :                     }
    4612            1 :                 }
    4613            1 :                 if (sp->endElement()) {
    4614            1 :                     std::list<EngravingObject*> eel = sp->endElement()->linkList();
    4615            2 :                     for (EngravingObject* ee : eel) {
    4616            2 :                         EngravingItem* e = static_cast<EngravingItem*>(ee);
    4617            2 :                         if (e->score() == nsp->score() && e->track() == nsp->track2()) {
    4618            1 :                             nsp->setEndElement(e);
    4619            1 :                             break;
    4620              :                         }
    4621              :                     }
    4622            1 :                 }
    4623              :             }
    4624              : 
    4625           56 :             if (sp->isTextLine() && sp != nsp) {
    4626            0 :                 EngravingItem* parent = sp->ownershipParentItem();
    4627            0 :                 if (parent && parent->isNote()) {
    4628            0 :                     nsp->setOwnershipParent(parent->findLinkedInStaff(staff));
    4629              :                 }
    4630            0 :                 EngravingItem* endEl = sp->endElement();
    4631            0 :                 if (endEl && endEl->isNote()) {
    4632            0 :                     nsp->setEndElement(endEl->findLinkedInStaff(staff));
    4633              :                 }
    4634              :             }
    4635              : 
    4636           56 :             doUndoAddElement(nsp);
    4637          301 :         } else if (et == ElementType::GLISSANDO || et == ElementType::GUITAR_BEND || et == ElementType::NOTELINE) {
    4638            0 :             doUndoAddElement(toSpanner(ne));
    4639          301 :         } else if (element->isType(ElementType::TREMOLO_TWOCHORD)) {
    4640            0 :             TremoloTwoChord* tremolo = item_cast<TremoloTwoChord*>(element);
    4641            0 :             ChordRest* cr1 = toChordRest(tremolo->chord1());
    4642            0 :             ChordRest* cr2 = toChordRest(tremolo->chord2());
    4643            0 :             int diff = static_cast<int>(cr2->track() - cr1->track());
    4644            0 :             Segment* s1    = cr1->segment();
    4645            0 :             Segment* s2    = cr2->segment();
    4646            0 :             Measure* m1    = s1->measure();
    4647            0 :             Measure* m2    = s2->measure();
    4648            0 :             Measure* nm1   = score->tick2measure(m1->tick());
    4649            0 :             Measure* nm2   = score->tick2measure(m2->tick());
    4650            0 :             Segment* ns1   = nm1->findSegment(s1->segmentType(), s1->tick());
    4651            0 :             Segment* ns2   = nm2->findSegment(s2->segmentType(), s2->tick());
    4652            0 :             Chord* c1      = toChord(ns1->element(linkedTrack));
    4653            0 :             Chord* c2      = toChord(ns2->element(linkedTrack + diff));
    4654            0 :             TremoloTwoChord* ntremolo = item_cast<TremoloTwoChord*>(ne);
    4655            0 :             ntremolo->setChords(c1, c2);
    4656            0 :             ntremolo->setOwnershipParent(c1);
    4657            0 :             doUndoAddElement(ntremolo);
    4658          301 :         } else if (element->isType(ElementType::TREMOLO_SINGLECHORD)) {
    4659           18 :             Chord* cr = toChord(element->ownershipParent());
    4660           18 :             Chord* c1 = findLinkedChord(cr, score->staff(staffIdx));
    4661           18 :             ne->setOwnershipParent(c1);
    4662           18 :             doUndoAddElement(ne);
    4663          283 :         } else if (element->isArpeggio() || element->isChordBracket()) {
    4664            0 :             ChordRest* cr = toChordRest(element->ownershipParent());
    4665            0 :             Segment* s    = cr->segment();
    4666            0 :             Measure* m    = s->measure();
    4667            0 :             Measure* nm   = score->tick2measure(m->tick());
    4668            0 :             Segment* ns   = nm->findSegment(s->segmentType(), s->tick());
    4669            0 :             Chord* c1     = toChord(ns->element(linkedTrack));
    4670            0 :             ne->setOwnershipParent(c1);
    4671            0 :             doUndoAddElement(ne);
    4672          283 :         } else if (element->isTie()) {
    4673          259 :             Tie* tie       = toTie(element);
    4674          259 :             Note* n1       = tie->startNote();
    4675          259 :             Note* n2       = tie->endNote();
    4676          259 :             Chord* cr1     = n1->chord();
    4677          259 :             Chord* cr2     = n2 ? n2->chord() : 0;
    4678              : 
    4679              :             // find corresponding notes in linked staff
    4680              :             // accounting for grace notes and cross-staff notation
    4681          259 :             int sm = 0;
    4682          259 :             if (cr1->staffIdx() != cr2->staffIdx()) {
    4683            0 :                 sm = static_cast<int>(cr2->staffIdx() - cr1->staffIdx());
    4684              :             }
    4685          259 :             Chord* c1 = findLinkedChord(cr1, score->staff(staffIdx));
    4686          259 :             Chord* c2 = findLinkedChord(cr2, score->staff(staffIdx + sm));
    4687              : 
    4688          259 :             IF_ASSERT_FAILED(c1) {
    4689            0 :                 return;
    4690              :             }
    4691              : 
    4692          259 :             Note* nn1 = c1->findNote(n1->pitch(), n1->unisonIndex());
    4693          259 :             Note* nn2 = c2 ? c2->findNote(n2->pitch(), n2->unisonIndex()) : 0;
    4694              : 
    4695          259 :             const track_idx_t track1 = c1->track();
    4696          259 :             const track_idx_t track2 = c2 ? c2->track() : track1;
    4697              : 
    4698              :             // create tie
    4699          259 :             Tie* ntie = toTie(ne);
    4700          259 :             ntie->eraseSpannerSegments();
    4701          259 :             ntie->setTrack(track1);
    4702          259 :             ntie->setTrack2(track2);
    4703          259 :             ntie->setStartNote(nn1);
    4704          259 :             ntie->setEndNote(nn2);
    4705          259 :             doUndoAddElement(ntie);
    4706           24 :         } else if (element->isInstrumentChange()) {
    4707            4 :             InstrumentChange* is = toInstrumentChange(element);
    4708            4 :             Segment* s1    = is->segment();
    4709            4 :             Measure* m1    = s1->measure();
    4710            4 :             Measure* nm1   = score->tick2measure(m1->tick());
    4711            4 :             Segment* ns1   = nm1->findSegment(s1->segmentType(), s1->tick());
    4712            4 :             InstrumentChange* nis = toInstrumentChange(ne);
    4713            4 :             nis->setOwnershipParent(ns1);
    4714            4 :             Fraction tickStart = nis->segment()->tick();
    4715            4 :             Part* part = nis->part();
    4716            4 :             Interval oldV = nis->staff()->transpose(tickStart);
    4717              :             // ws: instrument should not be changed here
    4718            4 :             if (is->instrument()->channel().empty() || is->instrument()->channel(0)->program() == -1) {
    4719            1 :                 nis->setInstrument(*staff->part()->instrument(s1->tick()));
    4720            3 :             } else if (nis != is) {
    4721            0 :                 nis->setInstrument(*is->instrument());
    4722              :             }
    4723            4 :             doUndoAddElement(nis);
    4724              :             // transpose root score; parts will follow
    4725            4 :             if (score->isMaster() && nis->staff()->transpose(tickStart) != oldV) {
    4726            1 :                 auto i = part->instruments().upper_bound(tickStart.ticks());
    4727            1 :                 Fraction tickEnd = i == part->instruments().end() ? Fraction(-1, 1) : Fraction::fromTicks(i->first);
    4728            1 :                 Transpose::transpositionChanged(tx, this, part, oldV, tickStart, tickEnd);
    4729              :             }
    4730           20 :         } else if (element->isBreath()) {
    4731           20 :             Breath* breath   = toBreath(element);
    4732           20 :             Fraction tick    = breath->segment()->tick();
    4733           20 :             Measure* m       = score->tick2measure(tick);
    4734              :             // breath appears before barline
    4735           20 :             if (m->tick() == tick) {
    4736            6 :                 m = m->prevMeasure();
    4737              :             }
    4738           20 :             Segment* seg     = m->undoGetSegment(SegmentType::Breath, tick);
    4739           20 :             Breath* nbreath  = toBreath(ne);
    4740           20 :             nbreath->setScore(score);
    4741           20 :             nbreath->setTrack(linkedTrack);
    4742           20 :             nbreath->setOwnershipParent(seg);
    4743           20 :             doUndoAddElement(nbreath);
    4744              :         } else {
    4745            0 :             LOGW("undoAddElement: unhandled: <%s>", element->typeName());
    4746              :         }
    4747          565 :         ne->styleChanged();
    4748              : 
    4749          565 :         if (elementToRelink) {
    4750            2 :             LinkedObjects* links = ne->links();
    4751            2 :             if (!links) {
    4752            0 :                 ne->linkTo(elementToRelink);
    4753              :             } else {
    4754            2 :                 elementToRelink->setLinks(links);
    4755            2 :                 links->push_back(elementToRelink);
    4756              :             }
    4757              :         }
    4758              :     }
    4759          546 : }
    4760              : 
    4761           75 : FBox* Score::findFretBox() const
    4762              : {
    4763          275 :     for (MeasureBase* measure = first(); measure; measure = measure->next()) {
    4764          202 :         if (measure->isFBox()) {
    4765            2 :             return toFBox(measure);
    4766              :         }
    4767              :     }
    4768              : 
    4769           73 :     return nullptr;
    4770              : }
    4771              : 
    4772           75 : void Score::rebuildFretBox()
    4773              : {
    4774           75 :     FBox* fretBox = findFretBox();
    4775           75 :     if (!fretBox) {
    4776           73 :         return;
    4777              :     }
    4778              : 
    4779            2 :     fretBox->setNeedsRebuild(true);
    4780            2 :     fretBox->triggerLayout();
    4781              : }
    4782              : 
    4783              : //---------------------------------------------------------
    4784              : //   undoAddCR
    4785              : //---------------------------------------------------------
    4786              : 
    4787         5304 : void Score::undoAddCR(ChordRest* cr, Measure* measure, const Fraction& tick)
    4788              : {
    4789         5304 :     assert(!cr->isChord() || !(toChord(cr)->notes()).empty());
    4790         5304 :     if (!cr->lyrics().empty()) {
    4791              :         // Add chordrest and lyrics separately for correct
    4792              :         // handling of adding lyrics to linked staves.
    4793            1 :         std::vector<Lyrics*> lyrics;
    4794            1 :         std::swap(lyrics, cr->lyrics());
    4795            1 :         undoAddCR(cr, measure, tick);
    4796            2 :         for (Lyrics* l : lyrics) {
    4797            1 :             undoAddElement(l);
    4798              :         }
    4799            1 :         return;
    4800            1 :     }
    4801              : 
    4802         5303 :     Staff* ostaff = cr->staff();
    4803         5303 :     track_idx_t strack = cr->track();
    4804              : 
    4805         5303 :     SegmentType segmentType = SegmentType::ChordRest;
    4806              : 
    4807        10699 :     for (const Staff* staff : ostaff->staffList()) {
    4808         5396 :         track_idx_t linkedTrack = ostaff->getLinkedTrackInStaff(staff, strack);
    4809              : 
    4810         5396 :         if (linkedTrack == muse::nidx) {
    4811            2 :             continue;
    4812              :         }
    4813              : 
    4814         5394 :         const TrackRange trackRange = staff->part()->trackRange();
    4815         5394 :         if (linkedTrack < trackRange.startTrack || linkedTrack >= trackRange.endTrack) {
    4816            0 :             continue;
    4817              :         }
    4818              : 
    4819         5394 :         Score* score = staff->score();
    4820         5394 :         Measure* m   = (score == this) ? measure : score->tick2measure(tick);
    4821         5394 :         if (!m) {
    4822            0 :             LOGD("measure not found");
    4823            0 :             break;
    4824              :         }
    4825         5394 :         Segment* seg = m->undoGetSegment(segmentType, tick);
    4826              : 
    4827         5394 :         assert(seg->segmentType() == segmentType);
    4828              : 
    4829         5394 :         ChordRest* newcr = (staff == ostaff) ? cr : toChordRest(cr->linkedClone());
    4830         5394 :         newcr->setScore(score);
    4831              : 
    4832         5394 :         newcr->setTrack(linkedTrack);
    4833         5394 :         newcr->setOwnershipParent(seg);
    4834              : 
    4835              : #ifndef QT_NO_DEBUG
    4836         5394 :         if (newcr->isChord()) {
    4837         1937 :             Chord* chord = toChord(newcr);
    4838              :             // setTpcFromPitch needs to know the note tick position
    4839         6046 :             for (Note* note : chord->notes()) {
    4840              :                 // if (note->tpc() == Tpc::TPC_INVALID)
    4841              :                 //      note->setTpcFromPitch();
    4842         4109 :                 assert(note->tpc() != Tpc::TPC_INVALID);
    4843              :             }
    4844              :         }
    4845              : #endif
    4846              :         // Climb up the (possibly nested) tuplets from this chordRest
    4847              :         // Make sure all tuplets are cloned and correctly nested
    4848         5394 :         DurationElement* elementBelow = cr;
    4849         5394 :         Tuplet* tupletAbove = elementBelow->tuplet();
    4850         5403 :         while (tupletAbove) {
    4851          491 :             DurationElement* linkedElementBelow = (DurationElement*)elementBelow->findLinkedInStaff(staff);
    4852          491 :             if (!linkedElementBelow) {     // shouldn't happen
    4853          482 :                 break;
    4854              :             }
    4855            9 :             Tuplet* linkedTuplet = (Tuplet*)tupletAbove->findLinkedInStaff(staff);
    4856            9 :             if (!linkedTuplet) {
    4857            2 :                 linkedTuplet = toTuplet(tupletAbove->linkedClone());
    4858            2 :                 linkedTuplet->setScore(score);
    4859            2 :                 linkedTuplet->setTrack(newcr->track());
    4860            2 :                 linkedTuplet->setOwnershipParent(m);
    4861              :             }
    4862            9 :             linkedElementBelow->setTuplet(linkedTuplet);
    4863              : 
    4864            9 :             elementBelow = tupletAbove;
    4865            9 :             tupletAbove = tupletAbove->tuplet();
    4866              :         }
    4867              : 
    4868         5394 :         if (newcr->isRest() && (toRest(newcr)->isGap()) && !(toRest(newcr)->track() % VOICES)) {
    4869          575 :             toRest(newcr)->setGap(false);
    4870              :         }
    4871              : 
    4872         5394 :         doUndoAddElement(newcr);
    4873         5303 :     }
    4874              : }
    4875              : 
    4876              : //---------------------------------------------------------
    4877              : //   undoRemoveElement
    4878              : //---------------------------------------------------------
    4879              : 
    4880         6769 : void Score::undoRemoveElement(EngravingItem* element, bool removeLinked)
    4881              : {
    4882         6769 :     if (!element) {
    4883          201 :         return;
    4884              :     }
    4885         6568 :     std::vector<Segment*> segments;
    4886        13205 :     for (EngravingObject* ee : element->linkList()) {
    4887         6637 :         EngravingItem* e = static_cast<EngravingItem*>(ee);
    4888         6637 :         if (e == element || removeLinked) {
    4889         6631 :             doUndoRemoveElement(e);
    4890              : 
    4891         6631 :             if (e->ownershipParent() && (e->ownershipParent()->isSegment())) {
    4892         3937 :                 Segment* s = toSegment(e->ownershipParent());
    4893         3937 :                 if (!muse::contains(segments, s)) {
    4894         3935 :                     segments.push_back(s);
    4895              :                 }
    4896              :             }
    4897         6631 :             if (e->ownershipParent() && e->ownershipParent()->isSystem()) {
    4898            0 :                 e->setOwnershipParent(0);   // systems will be regenerated upon redo, so detach
    4899              :             }
    4900              :         }
    4901         6568 :     }
    4902              : 
    4903         6568 :     if (element->isMeasureRepeat()) {
    4904            0 :         const MeasureRepeat* repeat = toMeasureRepeat(element);
    4905            0 :         Measure* measure = repeat->firstMeasureOfGroup();
    4906            0 :         size_t staffIdx = repeat->staffIdx();
    4907              : 
    4908            0 :         if (measure) {
    4909            0 :             for (int i = 0; i < repeat->numMeasures(); ++i) {
    4910            0 :                 undoChangeMeasureRepeatCount(measure, 0, staffIdx);
    4911            0 :                 measure = measure->nextMeasure();
    4912              :             }
    4913              :         }
    4914              :     }
    4915              : 
    4916        10503 :     for (Segment* s : segments) {
    4917         3935 :         if (s->empty()) {
    4918         1708 :             if (s->header() || s->trailer()) {        // probably more segment types (system header)
    4919            8 :                 s->setEnabled(false);
    4920              :             } else {
    4921         1700 :                 doUndoRemoveElement(s);
    4922              :             }
    4923              :         }
    4924              :     }
    4925         6568 : }
    4926              : 
    4927            0 : void Score::undoRemoveHopoText(HammerOnPullOffText* hopoText)
    4928              : {
    4929            0 :     Chord* startChord = hopoText->startChord();
    4930            0 :     Chord* endChord = hopoText->endChord();
    4931            0 :     IF_ASSERT_FAILED(startChord && endChord) {
    4932            0 :         return;
    4933              :     }
    4934              : 
    4935            0 :     HammerOnPullOffSegment* hopoSegment = hopoText->hopoSegment();
    4936            0 :     HammerOnPullOff* hopo = hopoSegment ? hopoSegment->hammerOnPullOff() : nullptr;
    4937            0 :     IF_ASSERT_FAILED(hopo) {
    4938            0 :         return;
    4939              :     }
    4940              : 
    4941            0 :     Chord* hopoStartChord = toChord(hopo->startElement());
    4942            0 :     Chord* hopoEndChord = toChord(hopo->endElement());
    4943            0 :     IF_ASSERT_FAILED(hopoStartChord && hopoEndChord) {
    4944            0 :         return;
    4945              :     }
    4946              : 
    4947            0 :     if (startChord == hopoStartChord && endChord == hopoEndChord) {
    4948            0 :         undoRemoveElement(hopo);
    4949            0 :         return;
    4950              :     }
    4951              : 
    4952            0 :     Fraction hopoStartTick = hopo->tick();
    4953            0 :     Fraction hopoEndTick = hopo->tick2();
    4954            0 :     Fraction hopoTextStartTick = startChord->tick();
    4955            0 :     Fraction hopoTextEndTick = endChord->tick();
    4956              : 
    4957            0 :     bool shortenFromStart = (hopoTextStartTick - hopoStartTick) < (hopoEndTick - hopoTextEndTick);
    4958            0 :     if (shortenFromStart) {
    4959            0 :         Fraction newStartTick = hopoTextEndTick;
    4960            0 :         Fraction newTicks = hopoEndTick - newStartTick;
    4961            0 :         hopo->undoChangeProperty(Pid::SPANNER_TICK, newStartTick);
    4962            0 :         hopo->undoChangeProperty(Pid::SPANNER_TICKS, newTicks);
    4963            0 :         hopo->undoChangeStartEndElements(endChord, hopoEndChord);
    4964            0 :         if (startChord != hopoStartChord) {
    4965            0 :             HammerOnPullOff* newHopo = Factory::createHammerOnPullOff(score()->dummy());
    4966            0 :             newHopo->setTrack(hopo->track());
    4967            0 :             newHopo->setTick(hopoStartTick);
    4968            0 :             newHopo->setTick2(hopoTextStartTick);
    4969            0 :             newHopo->setStartElement(hopoStartChord);
    4970            0 :             newHopo->setEndElement(startChord);
    4971            0 :             score()->undoAddElement(newHopo);
    4972              :         }
    4973              :     } else {
    4974            0 :         Fraction newEndTick = hopoTextStartTick;
    4975            0 :         Fraction newTicks = newEndTick - hopoStartTick;
    4976            0 :         hopo->undoChangeProperty(Pid::SPANNER_TICKS, newTicks);
    4977            0 :         hopo->undoChangeStartEndElements(hopoStartChord, startChord);
    4978            0 :         if (endChord != hopoEndChord) {
    4979            0 :             HammerOnPullOff* newHopo = new HammerOnPullOff(score()->dummy());
    4980            0 :             newHopo->setTrack(hopo->track());
    4981            0 :             newHopo->setTick(hopoTextEndTick);
    4982            0 :             newHopo->setTick2(hopoEndTick);
    4983            0 :             newHopo->setStartElement(endChord);
    4984            0 :             newHopo->setEndElement(hopoEndChord);
    4985            0 :             score()->undoAddElement(newHopo);
    4986              :         }
    4987              :     }
    4988              : }
    4989              : 
    4990              : //---------------------------------------------------------
    4991              : //   undoChangeSpannerElements
    4992              : //---------------------------------------------------------
    4993              : 
    4994           70 : void Score::undoChangeSpannerElements(Spanner* spanner, EngravingItem* startElement, EngravingItem* endElement)
    4995              : {
    4996           70 :     EngravingItem* oldStartElement = spanner->startElement();
    4997           70 :     EngravingItem* oldEndElement = spanner->endElement();
    4998           70 :     track_idx_t startDeltaTrack = startElement && oldStartElement ? startElement->track() - oldStartElement->track() : 0;
    4999           70 :     track_idx_t endDeltaTrack = endElement && oldEndElement ? endElement->track() - oldEndElement->track() : 0;
    5000              :     // scan all spanners linked to this one
    5001          164 :     for (EngravingObject* el : spanner->linkList()) {
    5002           94 :         Spanner* sp = toSpanner(el);
    5003           94 :         EngravingItem* newStartElement = nullptr;
    5004           94 :         EngravingItem* newEndElement = nullptr;
    5005              :         // if not the current spanner, but one linked to it, determine its new start and end elements
    5006              :         // as modifications 'parallel' to the modifications of the current spanner's start and end elements
    5007           94 :         if (sp != spanner) {
    5008           24 :             if (startElement) {
    5009              :                 // determine the track where to expect the 'parallel' start element
    5010           12 :                 track_idx_t newTrack = sp->startElement() ? sp->startElement()->track() + startDeltaTrack : sp->track();
    5011              :                 // look in elements linked to new start element for an element with
    5012              :                 // same score as linked spanner and appropriate track
    5013           24 :                 for (EngravingObject* ee : startElement->linkList()) {
    5014           24 :                     EngravingItem* e = toEngravingItem(ee);
    5015           24 :                     if (e->score() == sp->score() && e->track() == newTrack) {
    5016           12 :                         newStartElement = e;
    5017           12 :                         break;
    5018              :                     }
    5019           12 :                 }
    5020              :             }
    5021              :             // similarly to determine the 'parallel' end element
    5022           24 :             if (endElement) {
    5023           12 :                 track_idx_t newTrack = sp->endElement() ? sp->endElement()->track() + endDeltaTrack : sp->track2();
    5024           24 :                 for (EngravingObject* ee : endElement->linkList()) {
    5025           24 :                     EngravingItem* e = toEngravingItem(ee);
    5026           24 :                     if (e->score() == sp->score() && e->track() == newTrack) {
    5027           12 :                         newEndElement = e;
    5028           12 :                         break;
    5029              :                     }
    5030           12 :                 }
    5031              :             }
    5032              :         }
    5033              :         // if current spanner, just use stored start and end elements
    5034              :         else {
    5035           70 :             newStartElement = startElement;
    5036           70 :             newEndElement = endElement;
    5037              :         }
    5038           94 :         sp->score()->undo(new ChangeSpannerElements(sp, newStartElement, newEndElement));
    5039              : 
    5040           94 :         if (sp->isTie()) {
    5041            0 :             toTie(sp)->updatePossibleJumpPoints();
    5042              :         }
    5043           70 :     }
    5044           70 : }
    5045              : 
    5046              : //---------------------------------------------------------
    5047              : //   undoInsertTime
    5048              : //   acts on the linked scores as well
    5049              : //---------------------------------------------------------
    5050              : 
    5051          393 : void Score::undoInsertTime(const Fraction& tick, const Fraction& len)
    5052              : {
    5053          393 :     if (len.isZero()) {
    5054            9 :         return;
    5055              :     }
    5056              : 
    5057          384 :     std::vector<Spanner*> spannersToAdjust;
    5058          497 :     for (const auto& i : m_spanner.map()) {
    5059          113 :         Spanner* s = i.second;
    5060          113 :         if (s->tick2() < tick) {
    5061           45 :             continue;
    5062              :         }
    5063           68 :         bool append = false;
    5064           68 :         if (len > Fraction(0, 1)) {
    5065           34 :             if (tick > s->tick() && tick < s->tick2()) {
    5066            5 :                 append = true;
    5067           29 :             } else if (tick <= s->tick()) {
    5068           20 :                 append = true;
    5069              :             }
    5070              :         } else {
    5071           34 :             Fraction tick2 = tick - len;
    5072           34 :             if (s->tick() >= tick2) {
    5073           15 :                 append = true;
    5074           19 :             } else if (s->tick() >= tick && s->tick2() <= tick2) {
    5075            6 :                 append = true;
    5076           13 :             } else if ((s->tick() <= tick) && (s->tick2() >= tick2)) {
    5077            6 :                 Fraction t2 = s->tick2() + len;
    5078            6 :                 if (t2 > s->tick()) {
    5079            6 :                     append = true;
    5080              :                 }
    5081            7 :             } else if (s->tick() > tick && s->tick2() > tick2) {
    5082            2 :                 append = true;
    5083            5 :             } else if (s->tick() < tick && s->tick2() < tick2) {
    5084            5 :                 append = true;
    5085              :             }
    5086              :         }
    5087           98 :         for (const Spanner* ss : spannersToAdjust) {
    5088           30 :             if (muse::contains(ss->linkList(), static_cast<EngravingObject*>(s))) {
    5089            0 :                 append = false;
    5090            0 :                 break;
    5091              :             }
    5092              :         }
    5093           68 :         if (append) {
    5094           59 :             spannersToAdjust.push_back(s);
    5095              :         }
    5096              :     }
    5097          443 :     for (Spanner* s : spannersToAdjust) {
    5098           59 :         if (len > Fraction(0, 1)) {
    5099           25 :             if (tick == s->tick() && s->isVolta()) {
    5100            0 :                 s->undoChangeProperty(Pid::SPANNER_TICKS, s->ticks() + len);
    5101           25 :             } else if (tick > s->tick() && tick < s->tick2()) {
    5102              :                 //
    5103              :                 //  case a:
    5104              :                 //  +----spanner--------+
    5105              :                 //    +---add---
    5106              :                 //
    5107            5 :                 s->undoChangeProperty(Pid::SPANNER_TICKS, s->ticks() + len);
    5108           20 :             } else if (tick <= s->tick()) {
    5109              :                 //
    5110              :                 //  case b:
    5111              :                 //       +----spanner--------
    5112              :                 //  +---add---
    5113              :                 // and
    5114              :                 //            +----spanner--------
    5115              :                 //  +---add---+
    5116           20 :                 EngravingItem* startElement = s->startElement();
    5117           20 :                 EngravingItem* endElement = s->endElement();
    5118           20 :                 undoChangeSpannerElements(s, nullptr, nullptr);
    5119           20 :                 s->undoChangeProperty(Pid::SPANNER_TICK, s->tick() + len);
    5120           20 :                 undoChangeSpannerElements(s, startElement, endElement);
    5121              :             }
    5122              :         } else {
    5123           34 :             Fraction tick2 = tick - len;
    5124           34 :             if (s->tick() >= tick2) {
    5125              :                 //
    5126              :                 //  case A:
    5127              :                 //  +----remove---+ +---spanner---+
    5128              :                 //
    5129           15 :                 Fraction t = s->tick() + len;
    5130           15 :                 if (t < Fraction(0, 1)) {
    5131            0 :                     t = Fraction(0, 1);
    5132              :                 }
    5133           15 :                 EngravingItem* startElement = s->startElement();
    5134           15 :                 EngravingItem* endElement = s->endElement();
    5135           15 :                 undoChangeSpannerElements(s, nullptr, nullptr);
    5136           15 :                 s->undoChangeProperty(Pid::SPANNER_TICK, t);
    5137           15 :                 undoChangeSpannerElements(s, startElement, endElement);
    5138           19 :             } else if (s->tick() >= tick && s->tick2() <= tick2) {
    5139              :                 //
    5140              :                 //  case B:
    5141              :                 //    +---spanner---+
    5142              :                 //  +----remove--------+
    5143              :                 //
    5144            6 :                 undoRemoveElement(s);
    5145           13 :             } else if ((s->tick() <= tick) && (s->tick2() >= tick2)) {
    5146              :                 //
    5147              :                 //  case C:
    5148              :                 //  +----spanner--------+
    5149              :                 //    +---remove---+
    5150              :                 //
    5151            6 :                 Fraction t2 = s->tick2() + len;
    5152            6 :                 if (t2 > s->tick()) {
    5153            6 :                     s->undoChangeProperty(Pid::SPANNER_TICKS, s->ticks() + len);
    5154              :                 }
    5155            7 :             } else if (s->tick() > tick && s->tick2() > tick2) {
    5156              :                 //
    5157              :                 //  case D:
    5158              :                 //       +----spanner--------+
    5159              :                 //  +---remove---+
    5160              :                 //
    5161            2 :                 Fraction d1 = s->tick() - tick;
    5162            2 :                 Fraction d2 = tick2 - s->tick();
    5163            2 :                 Fraction le = s->ticks() - d2;
    5164            2 :                 if (le.isZero()) {
    5165            0 :                     undoRemoveElement(s);
    5166              :                 } else {
    5167            2 :                     s->undoChangeProperty(Pid::SPANNER_TICK, s->tick() - d1);
    5168            2 :                     s->undoChangeProperty(Pid::SPANNER_TICKS, le);
    5169              :                 }
    5170            5 :             } else if (s->tick() < tick && s->tick2() < tick2) {
    5171              :                 //
    5172              :                 //  case E:
    5173              :                 //       +----spanner--------+
    5174              :                 //                     +---remove---+
    5175              :                 //
    5176            5 :                 Fraction d  = s->tick2() - tick;
    5177            5 :                 Fraction le = s->ticks() - d;
    5178            5 :                 if (le.isZero()) {
    5179            0 :                     undoRemoveElement(s);
    5180              :                 } else {
    5181            5 :                     s->undoChangeProperty(Pid::SPANNER_TICKS, le);
    5182              :                 }
    5183              :             }
    5184              :         }
    5185              :     }
    5186              : 
    5187          384 :     undo(new InsertTimeUnmanagedSpanner(this, tick, len));
    5188          384 : }
    5189              : 
    5190              : //---------------------------------------------------------
    5191              : //   undoRemoveMeasures
    5192              : //---------------------------------------------------------
    5193              : 
    5194           62 : void Score::undoRemoveMeasures(Measure* m1, Measure* m2, bool preserveTies, bool moveStaffTypeChanges)
    5195              : {
    5196           62 :     assert(m1 && m2);
    5197              : 
    5198           62 :     Transaction& tx = transactionManager()->currentOrDummyTransaction();
    5199              : 
    5200           62 :     const Fraction startTick = m1->tick();
    5201           62 :     const Fraction endTick = m2->endTick();
    5202           62 :     std::set<Spanner*> spannersToRemove;
    5203              : 
    5204              :     //
    5205              :     //  handle ties which start before m1 and end in (m1-m2)
    5206              :     //
    5207          581 :     for (Segment* s = m1->first(); s != m2->last(); s = s->next1()) {
    5208          519 :         IF_ASSERT_FAILED(s) {
    5209              :             // This score is corrupted; handle it without crashing,
    5210              :             // to help the user to fix corruptions by deleting the affected measures
    5211            0 :             LOGE() << "Missing segments detected while deleting measures " << m1->measureNumber() << " to " << m2->measureNumber()
    5212            0 :                    << ". This score (" << name() << ") is corrupted. Continuing without deleting measure contents.";
    5213            0 :             break;
    5214              :         }
    5215              : 
    5216          519 :         if (!s->isChordRestType()) {
    5217          178 :             continue;
    5218              :         }
    5219              : 
    5220              :         // Make sure annotations are removed once, even if this segment contains linked copies of the same annotation
    5221              :         // (the linked copy would be removed by undoRemoveElement)
    5222          355 :         while (!s->annotations().empty()) {
    5223           14 :             EngravingItem* annotation = s->annotations().front();
    5224           14 :             IF_ASSERT_FAILED(annotation) {
    5225            0 :                 continue;
    5226              :             }
    5227           14 :             undoRemoveElement(annotation);
    5228              :         }
    5229              : 
    5230         1989 :         for (track_idx_t track = 0; track < ntracks(); ++track) {
    5231         1648 :             EngravingItem* e = s->element(track);
    5232         1648 :             if (!e || !e->isChord()) {
    5233         1345 :                 continue;
    5234              :             }
    5235          303 :             Chord* c = toChord(e);
    5236          610 :             for (Note* n : c->notes()) {
    5237              :                 // Remove ties crossing measure range boundaries
    5238          307 :                 Tie* t = n->tieBackNonPartial();
    5239          307 :                 if (t && t->startNote() && (t->startNote()->chord()->tick() < startTick)) {
    5240            0 :                     if (preserveTies) {
    5241            0 :                         t->setEndNote(0);
    5242              :                     } else {
    5243            0 :                         undoRemoveElement(t);
    5244              :                     }
    5245              :                 }
    5246          307 :                 t = n->tieForNonPartial();
    5247          307 :                 if (t && t->endNote() && (t->endNote()->chord()->tick() >= endTick)) {
    5248            6 :                     undoRemoveElement(t);
    5249              :                 }
    5250              : 
    5251              :                 // Do the same for other note-anchored spanners (e.g. glissandi).
    5252              :                 // Delay actual removing to avoid modifying lists inside loops over them.
    5253          307 :                 for (Spanner* sb : n->spannerBack()) {
    5254            0 :                     if (sb->tick() < startTick) {
    5255            0 :                         spannersToRemove.insert(sb);
    5256              :                     }
    5257              :                 }
    5258          307 :                 for (Spanner* sf : n->spannerFor()) {
    5259            0 :                     if (sf->tick2() >= endTick) {
    5260            0 :                         spannersToRemove.insert(sf);
    5261              :                     }
    5262              :                 }
    5263              :             }
    5264              :         }
    5265              :     }
    5266              : 
    5267           62 :     for (Spanner* s : spannersToRemove) {
    5268            0 :         undoRemoveElement(s);
    5269              :     }
    5270              : 
    5271              :     // delete staffTypeChanges in removed measures
    5272           62 :     if (moveStaffTypeChanges) {
    5273          124 :         for (Measure* m = m1; m && m != m2->nextMeasure(); m = m->nextMeasure()) {
    5274           86 :             for (size_t i = m->el().size(); i > 0; --i) {
    5275            0 :                 EngravingItem* el = m->el().at(i - 1);
    5276            0 :                 if (el && el->isStaffTypeChange()) {
    5277            0 :                     deleteItem(el);
    5278              :                 }
    5279              :             }
    5280              :         }
    5281              :     }
    5282              : 
    5283           62 :     EditSystemLocks::removeSystemLocksOnRemoveMeasures(tx, this, m1, m2);
    5284           62 :     EditPageLocks::removePageLocksOnRemoveMeasures(tx, this, m1, m2);
    5285              : 
    5286           62 :     undo(new RemoveMeasures(m1, m2, moveStaffTypeChanges));
    5287           62 : }
    5288              : 
    5289              : //---------------------------------------------------------
    5290              : //   undoChangeMeasureRepeatCount
    5291              : //---------------------------------------------------------
    5292              : 
    5293           18 : void Score::undoChangeMeasureRepeatCount(Measure* m, int newCount, staff_idx_t staffIdx)
    5294              : {
    5295           54 :     for (Staff* st : staff(staffIdx)->staffList()) {
    5296           36 :         Score* linkedScore = st->score();
    5297           36 :         staff_idx_t linkedStaffIdx = st->idx();
    5298           36 :         Measure* linkedMeasure = linkedScore->tick2measure(m->tick());
    5299              : 
    5300           36 :         int currCount = linkedMeasure->measureRepeatCount(linkedStaffIdx);
    5301           36 :         if (currCount != newCount) {
    5302           36 :             linkedScore->undo(new ChangeMeasureRepeatCount(linkedMeasure, newCount, linkedStaffIdx));
    5303              :         }
    5304           18 :     }
    5305           18 : }
    5306              : 
    5307         1118 : void Score::doUndoRemoveStaleTieJumpPoints(Tie* tie, bool undo)
    5308              : {
    5309            3 :     auto removeTie = [&](Tie* tie) {
    5310            3 :         if (undo) {
    5311            3 :             const size_t undoIdx = undoStack()->currentIndex();
    5312            3 :             startCmd(TranslatableString("engraving", "Remove stale partial tie"));
    5313            3 :             undoRemoveElement(tie);
    5314            3 :             endCmd();
    5315            3 :             if (undoIdx != undoStack()->currentIndex() && undoStack()->currentIndex() >= 2) {
    5316              :                 // These changes should be merged with the change in repeat structure which caused the ties to become invalid
    5317              :                 // currentIndex returns the next empty index for an undo command
    5318            2 :                 const size_t penultimateCmdIdx = undoStack()->currentIndex() - 2;
    5319            2 :                 undoStack()->mergeTransactions(penultimateCmdIdx);
    5320              :             }
    5321              :         } else {
    5322            0 :             removeElement(tie);
    5323              :         }
    5324            3 :     };
    5325              : 
    5326         1118 :     std::vector<Tie*> oldTies;
    5327         1118 :     if (!tie->tieJumpPoints()) {
    5328            0 :         return;
    5329              :     }
    5330         1162 :     for (TieJumpPoint* jumpPoint : *tie->tieJumpPoints()) {
    5331           44 :         if (jumpPoint->followingNote()) {
    5332            9 :             continue;
    5333              :         }
    5334           35 :         oldTies.push_back(jumpPoint->endTie());
    5335              :     }
    5336              : 
    5337              :     // Update jump points for linked ties
    5338         2270 :     for (EngravingObject* linkedTie : tie->linkList()) {
    5339         1152 :         if (!linkedTie || !linkedTie->isTie()) {
    5340            0 :             continue;
    5341              :         }
    5342         1152 :         toTie(linkedTie)->updatePossibleJumpPoints();
    5343         1118 :     }
    5344              : 
    5345         1153 :     for (Tie* oldTie : oldTies) {
    5346           47 :         auto findEndTie = [&oldTie](const TieJumpPoint* jumpPoint) {
    5347           47 :             return jumpPoint->endTie() == oldTie;
    5348           35 :         };
    5349           35 :         if (!oldTie) {
    5350           35 :             continue;
    5351              :         }
    5352              : 
    5353           34 :         if (std::find_if((*tie->tieJumpPoints()).begin(), (*tie->tieJumpPoints()).end(),
    5354           68 :                          findEndTie) != (*tie->tieJumpPoints()).end()) {
    5355           34 :             continue;
    5356              :         }
    5357            0 :         removeTie(oldTie);
    5358              :     }
    5359              : 
    5360         1118 :     if (tie->isPartialTie() && tie->allJumpPointsInactive() && tie->startNote() && tie->startNote()->tieFor() == tie) {
    5361            3 :         removeTie(tie);
    5362              :     }
    5363         1118 : }
    5364              : 
    5365          339 : void Score::doUndoResetPartialSlur(Slur* slur, bool undo)
    5366              : {
    5367          339 :     const size_t undoIdx = undoStack()->currentIndex();
    5368              : 
    5369          339 :     const ChordRest* startCR = slur ? slur->startCR() : nullptr;
    5370              :     // Slurs can only have the same start & end element when they are partial.
    5371              :     // If they are no longer partial, we should remove them
    5372          339 :     const bool shouldRemove = slur->startElement() == slur->endElement();
    5373          339 :     IF_ASSERT_FAILED(startCR) {
    5374            0 :         LOGE() << "Slur is corrupted";
    5375          339 :     } else if (!startCR->hasPrecedingJumpItem() && slur->isIncoming()) {
    5376            3 :         if (shouldRemove) {
    5377            3 :             if (undo) {
    5378            0 :                 startCmd(TranslatableString("engraving", "Remove invalid incoming partial slur"));
    5379            0 :                 undoRemoveElement(slur);
    5380            0 :                 endCmd();
    5381              :             } else {
    5382            3 :                 removeElement(slur);
    5383              :             }
    5384              :         } else {
    5385            0 :             if (undo) {
    5386            0 :                 startCmd(TranslatableString("engraving", "Reset incoming partial slur"));
    5387            0 :                 slur->undoSetIncoming(false);
    5388            0 :                 endCmd();
    5389              :             } else {
    5390            0 :                 slur->setIncoming(false);
    5391              :             }
    5392              :         }
    5393              :     }
    5394              : 
    5395          339 :     const ChordRest* endCR = slur ? slur->endCR() : nullptr;
    5396          339 :     IF_ASSERT_FAILED(endCR) {
    5397            0 :         LOGE() << "Slur is corrupted";
    5398          339 :     } else if (!endCR->hasFollowingJumpItem() && slur->isOutgoing()) {
    5399            1 :         if (shouldRemove) {
    5400            0 :             if (undo) {
    5401            0 :                 startCmd(TranslatableString("engraving", "Remove invalid outgoing partial slur"));
    5402            0 :                 undoRemoveElement(slur);
    5403            0 :                 endCmd();
    5404              :             } else {
    5405            0 :                 removeElement(slur);
    5406              :             }
    5407              :         } else {
    5408            1 :             if (undo) {
    5409            0 :                 startCmd(TranslatableString("engraving", "Reset outgoing partial slur"));
    5410            0 :                 slur->undoSetOutgoing(false);
    5411            0 :                 endCmd();
    5412              :             } else {
    5413            1 :                 slur->setOutgoing(false);
    5414              :             }
    5415              :         }
    5416              :     }
    5417              : 
    5418          339 :     if (!undo) {
    5419          339 :         return;
    5420              :     }
    5421              : 
    5422            0 :     if (undoIdx != undoStack()->currentIndex() && undoStack()->currentIndex() >= 2) {
    5423              :         // These changes should be merged with the change in repeat structure which caused the ties to become invalid
    5424              :         // currentIdx returns the next empty index for an undo command
    5425            0 :         const size_t penultimateCmdIdx = undoStack()->currentIndex() - 2;
    5426            0 :         undoStack()->mergeTransactions(penultimateCmdIdx);
    5427              :     }
    5428              : }
    5429              : 
    5430         1159 : void Score::undoRemoveStaleTieJumpPoints(bool undo)
    5431              : {
    5432         1159 :     size_t tracks = nstaves() * VOICES;
    5433         1159 :     Measure* m = firstMeasure();
    5434         1159 :     if (!m) {
    5435            1 :         return;
    5436              :     }
    5437              : 
    5438              :     // Copy, invalid slurs could be removed
    5439         1158 :     auto spannerMap = spanner();
    5440         2045 :     for (auto& interval : spannerMap) {
    5441          887 :         Spanner* sp = interval.second;
    5442          887 :         if (!sp || !sp->isSlur()) {
    5443          548 :             continue;
    5444              :         }
    5445          339 :         doUndoResetPartialSlur(toSlur(sp), undo);
    5446              :     }
    5447              : 
    5448         1158 :     std::set<PartialTie*> incomingPartialTies;
    5449         1158 :     SegmentType st = SegmentType::ChordRest;
    5450        24936 :     for (Segment* s = m->first(st); s; s = s->next1(st)) {
    5451       156578 :         for (track_idx_t i = 0; i < tracks; ++i) {
    5452       132800 :             EngravingItem* e = s->element(i);
    5453       132800 :             if (e == 0 || !e->isChordRest()) {
    5454        99090 :                 continue;
    5455              :             }
    5456              : 
    5457        33710 :             if (!toChordRest(e)->hasFollowingJumpItem()) {
    5458              :                 // Remove invalid lyrics dashes
    5459        34372 :                 for (Lyrics* lyrics : toChordRest(e)->lyrics()) {
    5460         1167 :                     LyricsLine* separator = lyrics->separator();
    5461         2334 :                     if ((lyrics->syllabic() == LyricsSyllabic::BEGIN || lyrics->syllabic() == LyricsSyllabic::MIDDLE) && separator
    5462         2334 :                         && !separator->nextLyrics() && !separator->isEndMelisma()) {
    5463            0 :                         lyrics->setNeedRemoveInvalidSegments();
    5464              :                     }
    5465              :                 }
    5466              :             }
    5467              : 
    5468        33710 :             if (!e->isChord()) {
    5469        15039 :                 continue;
    5470              :             }
    5471              : 
    5472        18671 :             Chord* c = toChord(e);
    5473        41125 :             for (Note* n : c->notes()) {
    5474        22454 :                 if (n->incomingPartialTie()) {
    5475           36 :                     incomingPartialTies.emplace(n->incomingPartialTie());
    5476              :                 }
    5477        22454 :                 if (!n->tieFor()) {
    5478        21336 :                     continue;
    5479              :                 }
    5480              : 
    5481         1118 :                 doUndoRemoveStaleTieJumpPoints(n->tieFor());
    5482              :             }
    5483              :         }
    5484              :     }
    5485              : 
    5486         1194 :     for (PartialTie* incomingPT : incomingPartialTies) {
    5487           36 :         if (incomingPT->jumpPoint() || incomingPT->note()->tieBack() != incomingPT) {
    5488           32 :             continue;
    5489              :         }
    5490            4 :         const size_t undoIdx = undoStack()->currentIndex();
    5491            4 :         if (undo) {
    5492            0 :             startCmd(TranslatableString("engraving", "Remove stale partial tie"));
    5493            0 :             undoRemoveElement(incomingPT);
    5494            0 :             endCmd();
    5495            0 :             if (undoIdx != undoStack()->currentIndex() && undoStack()->currentIndex() >= 2) {
    5496            0 :                 undoStack()->mergeTransactions(undoStack()->currentIndex() - 2);
    5497              :             }
    5498              :         } else {
    5499            4 :             removeElement(incomingPT);
    5500              :         }
    5501              :     }
    5502         1158 : }
    5503              : }
        

Generated by: LCOV version 2.0-1