LCOV - code coverage report
Current view: top level - src/engraving/editing - cmd.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 63.4 % 1056 670
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : /**
      24              :  \file
      25              :  Handling of several GUI commands.
      26              : */
      27              : 
      28              : #include <assert.h>
      29              : #include <set>
      30              : 
      31              : #include "translation.h"
      32              : 
      33              : #include "infrastructure/messagebox.h"
      34              : #include "style/style.h"
      35              : 
      36              : #include "rw/xmlreader.h"
      37              : 
      38              : #include "../dom/accidental.h"
      39              : #include "../dom/articulation.h"
      40              : #include "../dom/barline.h"
      41              : #include "../dom/box.h"
      42              : #include "../dom/chord.h"
      43              : #include "../dom/chordrest.h"
      44              : #include "../dom/clef.h"
      45              : #include "../dom/drumset.h"
      46              : #include "../dom/durationtype.h"
      47              : #include "../dom/dynamic.h"
      48              : #include "../dom/factory.h"
      49              : #include "../dom/glissando.h"
      50              : #include "../dom/guitarbend.h"
      51              : #include "../dom/hairpin.h"
      52              : #include "../dom/harmony.h"
      53              : #include "../dom/key.h"
      54              : #include "../dom/laissezvib.h"
      55              : #include "../dom/layoutbreak.h"
      56              : #include "../dom/linkedobjects.h"
      57              : #include "../dom/lyrics.h"
      58              : #include "../dom/masterscore.h"
      59              : #include "../dom/measure.h"
      60              : #include "../dom/measurerepeat.h"
      61              : #include "../dom/mscore.h"
      62              : #include "../dom/note.h"
      63              : #include "../dom/ornament.h"
      64              : #include "../dom/page.h"
      65              : #include "../dom/part.h"
      66              : #include "../dom/pitchspelling.h"
      67              : #include "../dom/rehearsalmark.h"
      68              : #include "../dom/rest.h"
      69              : #include "../dom/score.h"
      70              : #include "../dom/segment.h"
      71              : #include "../dom/sig.h"
      72              : #include "../dom/slur.h"
      73              : #include "../dom/spacer.h"
      74              : #include "../dom/staff.h"
      75              : #include "../dom/stafftype.h"
      76              : #include "../dom/stafftypechange.h"
      77              : #include "../dom/stem.h"
      78              : #include "../dom/stringdata.h"
      79              : #include "../dom/system.h"
      80              : #include "../dom/tapping.h"
      81              : #include "../dom/tie.h"
      82              : #include "../dom/timesig.h"
      83              : #include "../dom/tremolotwochord.h"
      84              : #include "../dom/tuplet.h"
      85              : #include "../dom/utils.h"
      86              : 
      87              : #include "editchord.h"
      88              : #include "editnote.h"
      89              : #include "noteinput.h"
      90              : #include "editpagelocks.h"
      91              : #include "editproperty.h"
      92              : #include "editspanner.h"
      93              : #include "editstaff.h"
      94              : #include "editsystemlocks.h"
      95              : #include "mscoreview.h"
      96              : #include "navigation.h"
      97              : #include "transaction/transaction.h"
      98              : #include "transaction/undostack.h"
      99              : #include "transpose.h"
     100              : 
     101              : #include "log.h"
     102              : 
     103              : using namespace muse::io;
     104              : using namespace mu::engraving;
     105              : 
     106              : namespace mu::engraving {
     107              : //---------------------------------------------------------
     108              : //   reset
     109              : //---------------------------------------------------------
     110              : 
     111         2819 : void CmdState::reset()
     112              : {
     113         2819 :     layoutFlags         = LayoutFlag::NO_FLAGS;
     114         2819 :     m_updateMode         = UpdateMode::DoNothing;
     115         2819 :     m_startTick          = Fraction(-1, 1);
     116         2819 :     m_endTick            = Fraction(-1, 1);
     117              : 
     118         2819 :     m_startStaff = muse::nidx;
     119         2819 :     m_endStaff = muse::nidx;
     120         2819 :     m_el = nullptr;
     121         2819 :     m_oneElement = true;
     122         2819 :     m_mb = nullptr;
     123         2819 :     m_oneMeasureBase = true;
     124         2819 :     m_locked = false;
     125         2819 : }
     126              : 
     127              : //---------------------------------------------------------
     128              : //   setTick
     129              : //---------------------------------------------------------
     130              : 
     131       968330 : void CmdState::setTick(const Fraction& t)
     132              : {
     133       968330 :     if (m_locked) {
     134       595599 :         return;
     135              :     }
     136              : 
     137       372731 :     if (m_startTick == Fraction(-1, 1) || t < m_startTick) {
     138         3283 :         m_startTick = t;
     139              :     }
     140       372731 :     if (m_endTick == Fraction(-1, 1) || t > m_endTick) {
     141        10847 :         m_endTick = t;
     142              :     }
     143       372731 :     setUpdateMode(UpdateMode::Layout);
     144              : }
     145              : 
     146              : //---------------------------------------------------------
     147              : //   setStaff
     148              : //---------------------------------------------------------
     149              : 
     150       901543 : void CmdState::setStaff(staff_idx_t st)
     151              : {
     152       901543 :     if (m_locked || st == muse::nidx) {
     153       818041 :         return;
     154              :     }
     155              : 
     156        83502 :     if (m_startStaff == muse::nidx || st < m_startStaff) {
     157         2385 :         m_startStaff = st;
     158              :     }
     159        83502 :     if (m_endStaff == muse::nidx || st > m_endStaff) {
     160         2883 :         m_endStaff = st;
     161              :     }
     162              : }
     163              : 
     164              : //---------------------------------------------------------
     165              : //   setMeasureBase
     166              : //---------------------------------------------------------
     167              : 
     168        14723 : void CmdState::setMeasureBase(const MeasureBase* mb)
     169              : {
     170        14723 :     if (!mb || m_mb == mb || m_locked) {
     171        10572 :         return;
     172              :     }
     173              : 
     174         4151 :     m_oneMeasureBase = !m_mb;
     175         4151 :     m_mb = mb;
     176              : }
     177              : 
     178              : //---------------------------------------------------------
     179              : //   setElement
     180              : //---------------------------------------------------------
     181              : 
     182       885109 : void CmdState::setElement(const EngravingItem* e)
     183              : {
     184       885109 :     if (!e || m_el == e || m_locked) {
     185       831338 :         return;
     186              :     }
     187              : 
     188        53771 :     m_oneElement = !m_el;
     189        53771 :     m_el = e;
     190              : 
     191        53771 :     if (m_oneMeasureBase) {
     192        14723 :         setMeasureBase(e->findMeasureBase());
     193              :     }
     194              : }
     195              : 
     196              : //---------------------------------------------------------
     197              : //   unsetElement
     198              : //---------------------------------------------------------
     199              : 
     200       231241 : void CmdState::unsetElement(const EngravingItem* e)
     201              : {
     202       231241 :     if (m_el == e) {
     203           66 :         m_el = nullptr;
     204              :     }
     205       231241 :     if (m_mb == e) {
     206            0 :         m_mb = nullptr;
     207              :     }
     208       231241 : }
     209              : 
     210              : //---------------------------------------------------------
     211              : //   element
     212              : //---------------------------------------------------------
     213              : 
     214            0 : const EngravingItem* CmdState::element() const
     215              : {
     216            0 :     if (m_oneElement) {
     217            0 :         return m_el;
     218              :     }
     219            0 :     if (m_oneMeasureBase) {
     220            0 :         return m_mb;
     221              :     }
     222            0 :     return nullptr;
     223              : }
     224              : 
     225              : //---------------------------------------------------------
     226              : //   setUpdateMode
     227              : //---------------------------------------------------------
     228              : 
     229         3142 : void CmdState::_setUpdateMode(UpdateMode m)
     230              : {
     231         3142 :     m_updateMode = m;
     232         3142 : }
     233              : 
     234       389993 : void CmdState::setUpdateMode(UpdateMode m)
     235              : {
     236       389993 :     if (int(m) > int(m_updateMode)) {
     237         3142 :         _setUpdateMode(m);
     238              :     }
     239       389993 : }
     240              : 
     241              : //---------------------------------------------------------
     242              : //   deleteLater
     243              : //---------------------------------------------------------
     244              : 
     245          133 : void CmdState::deleteLater(EngravingObject* e)
     246              : {
     247          133 :     m_postponedDeletions.push_back(e);
     248          133 : }
     249              : 
     250         1555 : std::vector<EngravingObject*> CmdState::takePostponedDeletions()
     251              : {
     252         1555 :     std::vector<EngravingObject*> result;
     253         1555 :     result.swap(m_postponedDeletions);
     254         1555 :     return result;
     255              : }
     256              : 
     257         1555 : static void deletePostponed(CmdState& cmdState)
     258              : {
     259         1624 :     for (EngravingObject* e : cmdState.takePostponedDeletions()) {
     260           69 :         if (e->isSystem()) {
     261           23 :             System* s = toSystem(e);
     262           23 :             std::list<SpannerSegment*> spanners = s->spannerSegments();
     263           25 :             for (SpannerSegment* ss : spanners) {
     264            2 :                 if (ss->system() == s) {
     265            2 :                     ss->moveToSystem(nullptr);
     266              :                 }
     267              :             }
     268           23 :         }
     269           69 :         delete e;
     270         1555 :     }
     271         1555 : }
     272              : 
     273              : //---------------------------------------------------------
     274              : //   startCmd
     275              : ///   Start a GUI command by clearing the redraw area
     276              : ///   and starting a user-visible undo.
     277              : //---------------------------------------------------------
     278              : 
     279          523 : void Score::startCmd(const TranslatableString& actionName)
     280              : {
     281          523 :     masterScore()->transactionManager()->beginTransaction(actionName);
     282          523 : }
     283              : 
     284              : //---------------------------------------------------------
     285              : //   undoRedo
     286              : //---------------------------------------------------------
     287              : 
     288          190 : void Score::undoRedo(bool undo, EditData* ed)
     289              : {
     290          190 :     masterScore()->transactionManager()->undoRedo(undo, ed);
     291          190 : }
     292              : 
     293              : //---------------------------------------------------------
     294              : //   endCmd
     295              : ///   End a GUI command by (if \a undo) ending a user-visible undo
     296              : ///   and (always) updating the redraw area.
     297              : //---------------------------------------------------------
     298              : 
     299          526 : void Score::endCmd(bool rollback, bool layoutAllParts, bool keepRolledBackElements)
     300              : {
     301          526 :     masterScore()->transactionManager()->endTransaction(rollback, layoutAllParts, keepRolledBackElements);
     302          526 : }
     303              : 
     304              : //---------------------------------------------------------
     305              : //   undo
     306              : //    Deprecated: use Transaction::push instead.
     307              : //---------------------------------------------------------
     308              : 
     309        37575 : void Score::undo(UndoableCommand* cmd) const
     310              : {
     311        37575 :     Transaction* tx = masterScore()->transactionManager()->currentTransaction();
     312        37575 :     if (!tx) {
     313              :         // this can happen for layout() outside of a transaction (load)
     314        17169 :         if (!ScoreLoad::loading()) {
     315         7840 :             LOGW() << "called outside of transaction";
     316              :         }
     317              : 
     318        17169 :         cmd->redo();
     319        17169 :         delete cmd;
     320        17169 :         return;
     321              :     }
     322              : 
     323        20406 :     tx->push(cmd);
     324              : }
     325              : 
     326              : #ifndef NDEBUG
     327              : //---------------------------------------------------------
     328              : //   CmdState::dump
     329              : //---------------------------------------------------------
     330              : 
     331            0 : void CmdState::dump()
     332              : {
     333            0 :     LOGD("CmdState: mode %d %d-%d", int(m_updateMode), m_startTick.ticks(), m_endTick.ticks());
     334              :     // bool _excerptsChanged     { false };
     335              :     // bool _instrumentsChanged  { false };
     336            0 : }
     337              : 
     338              : #endif
     339              : 
     340              : //---------------------------------------------------------
     341              : //   update
     342              : //    layout & update
     343              : //---------------------------------------------------------
     344              : 
     345           77 : void Score::update()
     346              : {
     347           77 :     masterScore()->update();
     348           77 : }
     349              : 
     350         1555 : void MasterScore::update(bool resetCmdState, bool layoutAllParts)
     351              : {
     352         1555 :     if (m_updatesLocked) {
     353            0 :         return;
     354              :     }
     355              : 
     356         1558 :     TRACEFUNC;
     357              : 
     358         1555 :     deletePostponed(m_cmdState);
     359              : 
     360         1555 :     bool updateAll = false;
     361         1555 :     if (m_cmdState.layoutRange()) {
     362         2349 :         for (Score* s : scoreList()) {
     363         1289 :             if (s != this && !s->isOpen() && scoreList().size() > 1 && !layoutAllParts) {
     364          199 :                 continue;
     365              :             }
     366         1090 :             s->doLayoutRange(m_cmdState.startTick(), m_cmdState.endTick());
     367         1060 :         }
     368         1060 :         updateAll = true;
     369              :     }
     370              : 
     371         1555 :     if (needUpdateTicksAndTimeSigMap()) {
     372          275 :         updateTicksAndTimeSigMap();
     373              :     }
     374              : 
     375         1555 :     if (updateAll || m_cmdState.updateAll()) {
     376         2457 :         for (Score* s : scoreList()) {
     377         1344 :             for (MuseScoreView* v : s->getViewer()) {
     378            0 :                 v->updateAll();
     379              :             }
     380         1113 :         }
     381          442 :     } else if (m_cmdState.updateRange()) {
     382              :         // Any score that accumulated a refresh rect via addRefresh() calls dataChanged() on its viewers.
     383           60 :         for (Score* s : scoreList()) {
     384           30 :             if (s->refreshRect().isNull()) {
     385            0 :                 continue;
     386              :             }
     387           30 :             const std::list<MuseScoreView*>& viewers = s->getViewer();
     388           30 :             if (!viewers.empty()) {
     389            0 :                 double d = s->style().spatium() * .5;
     390            0 :                 RectF rect = s->refreshRect().adjusted(-d, -d, 2 * d, 2 * d);
     391            0 :                 for (MuseScoreView* v : viewers) {
     392            0 :                     v->dataChanged(rect);
     393              :                 }
     394              :             }
     395           30 :             s->clearRefreshRect();
     396           30 :         }
     397              :     }
     398              : 
     399         1555 :     if (resetCmdState) {
     400           87 :         m_cmdState.reset();
     401              :     }
     402              : 
     403         3344 :     for (Score* score : scoreList()) {
     404         1789 :         Selection& sel = score->selection();
     405         1789 :         if (sel.isRange() && !sel.isLocked()) {
     406          231 :             sel.updateSelectedElements();
     407              :         }
     408         1555 :     }
     409         1555 : }
     410              : 
     411            0 : void MasterScore::lockUpdates(bool locked)
     412              : {
     413            0 :     m_updatesLocked = locked;
     414            0 : }
     415              : 
     416              : //---------------------------------------------------------
     417              : //   expandVoice
     418              : //    fills gaps in voice with rests,
     419              : //    from previous cr (or beginning of measure) to next cr (or end of measure)
     420              : //---------------------------------------------------------
     421              : 
     422          170 : void Score::expandVoice(Segment* s, track_idx_t track)
     423              : {
     424          170 :     if (!s || track == muse::nidx) {
     425            0 :         LOGD("expand voice: no segment or invalid track");
     426          128 :         return;
     427              :     }
     428          170 :     if (s->element(track)) {
     429          128 :         return;
     430              :     }
     431              : 
     432              :     // find previous segment with cr in this track
     433              :     Segment* ps;
     434           91 :     for (ps = s; ps; ps = ps->prev(SegmentType::ChordRest)) {
     435           49 :         if (ps->element(track)) {
     436            0 :             break;
     437              :         }
     438              :     }
     439           42 :     if (ps) {
     440            0 :         ChordRest* cr = toChordRest(ps->element(track));
     441            0 :         Fraction tick = cr->endTick();
     442            0 :         if (tick > s->tick()) {
     443              :             // previous cr extends past current segment
     444            0 :             LOGD("expandVoice: cannot insert element here");
     445            0 :             return;
     446              :         }
     447            0 :         if (cr->isChord()) {
     448              :             // previous cr ends on or before current segment
     449              :             // for chords, move ps to just after cr ends
     450              :             // so we can fill any gap that might exist
     451              :             // but don't move ps if previous cr is a rest
     452              :             // this will be combined with any new rests needed to fill up to s->tick() below
     453            0 :             ps = ps->measure()->undoGetSegment(SegmentType::ChordRest, tick);
     454              :         }
     455              :     }
     456              :     //
     457              :     // fill up to s->tick() with rests
     458              :     //
     459           42 :     Measure* m = s->measure();
     460           42 :     Fraction stick  = ps ? ps->tick() : m->tick();
     461              : 
     462           42 :     const Staff* currStaff = staff(track2staff(track));
     463           42 :     IF_ASSERT_FAILED(currStaff) {
     464            0 :         return;
     465              :     }
     466           42 :     const Fraction stretch = currStaff->timeStretch(stick);
     467              : 
     468           42 :     Fraction ticks  = s->tick() - stick;
     469           42 :     if (ticks.isNotZero()) {
     470            5 :         setRest(stick, track, ticks * stretch, false, 0);
     471              :     }
     472              : 
     473              :     //
     474              :     // fill from s->tick() until next chord/rest in measure
     475              :     //
     476              :     Segment* ns;
     477          180 :     for (ns = s->next(SegmentType::ChordRest); ns; ns = ns->next(SegmentType::ChordRest)) {
     478          140 :         if (ns->element(track)) {
     479            2 :             break;
     480              :         }
     481              :     }
     482           42 :     ticks  = ns ? (ns->tick() - s->tick()) : (m->ticks() - s->rtick());
     483           42 :     if (ticks == m->ticks()) {
     484           35 :         addRest(s, track, TDuration(DurationType::V_MEASURE), 0);
     485              :     } else {
     486            7 :         setRest(s->tick(), track, ticks * stretch, false, 0);
     487              :     }
     488              : }
     489              : 
     490           25 : void Score::expandVoice()
     491              : {
     492           25 :     Segment* s = m_is.segment();
     493           25 :     track_idx_t track = m_is.track();
     494           25 :     expandVoice(s, track);
     495           25 : }
     496              : 
     497              : //---------------------------------------------------------
     498              : //   addInterval
     499              : //---------------------------------------------------------
     500              : 
     501           40 : void Score::addInterval(int val, const std::vector<Note*>& nl)
     502              : {
     503              :     // Prepare note selection in case there are not selected tied notes and sort them
     504           40 :     std::vector<Note*> tmpnl;
     505           40 :     std::vector<Note*> _nl = nl;
     506           40 :     bool selIsList = selection().isList();
     507           40 :     bool selIsSingle = selIsList && _nl.size() == 1;
     508           40 :     bool shouldSelectFirstNote = selIsSingle && _nl.front()->tieFor();
     509              : 
     510           40 :     std::sort(_nl.begin(), _nl.end(), [](const Note* a, const Note* b) -> bool {
     511            0 :         return a->tick() < b->tick();
     512              :     });
     513              : 
     514           80 :     for (Note* currNote : _nl) {
     515           80 :         for (Note* n = currNote; n && !muse::contains(tmpnl, n); n = n->tieFor() ? n->tieFor()->endNote() : nullptr) {
     516           40 :             if (selIsList && n->selected()) {
     517           40 :                 deselect(n);
     518              :             }
     519           40 :             tmpnl.emplace_back(n);
     520              :         }
     521              :     }
     522              : 
     523           40 :     Note* prevTied = nullptr;
     524           40 :     std::vector<EngravingItem*> notesToSelect;
     525           40 :     int deltaLine = val < 0 ? val + 1 : val - 1;
     526           40 :     bool accidental = m_is.noteEntryMode() && m_is.accidentalType() != AccidentalType::NONE;
     527           40 :     bool useOctaveRule = (deltaLine % STEP_DELTA_OCTAVE == 0) && !accidental;  // Both octaves and unison
     528              : 
     529           80 :     for (Note* on : tmpnl) {
     530           40 :         Chord* chord = on->chord();
     531           40 :         Fraction tick = chord->tick();
     532           40 :         NoteVal nval;
     533           40 :         bool forceAccidental = false;
     534           40 :         bool noteValid = false;
     535           40 :         if (on->staff() && on->staff()->isDrumStaff(tick)) {
     536              :             // For drum staves: Don't calculate the new note based on pitch, but choose one using lines
     537            0 :             const Drumset* ds = on->staff()->part()->instrument(tick)->drumset();
     538            0 :             nval.pitch = ds->defaultPitchForLine(on->line() - deltaLine);
     539            0 :             nval.headGroup = ds->noteHead(nval.pitch);
     540            0 :             noteValid = ds->isValid(nval.pitch) && nval.headGroup != NoteHeadGroup::HEAD_INVALID;
     541              :         } else {
     542           40 :             if (useOctaveRule) {
     543           40 :                 int octaves = deltaLine / STEP_DELTA_OCTAVE;
     544           40 :                 nval.pitch = on->pitch() + octaves * PITCH_DELTA_OCTAVE;
     545           40 :                 nval.tpc1 = on->tpc1();
     546           40 :                 nval.tpc2 = on->tpc2();
     547              :             } else {
     548            0 :                 int line      = on->line() - deltaLine;
     549            0 :                 Staff* estaff = staff(chord->vStaffIdx());
     550            0 :                 ClefType clef = estaff->clef(tick);
     551            0 :                 Key key       = estaff->key(tick);
     552              :                 int ntpc;
     553            0 :                 if (accidental) {
     554            0 :                     AccidentalVal acci = Accidental::subtype2value(m_is.accidentalType());
     555            0 :                     int step = absStep(line, clef);
     556            0 :                     int octave = step / 7;
     557            0 :                     nval.pitch = step2pitch(step) + octave * PITCH_DELTA_OCTAVE + int(acci);
     558            0 :                     forceAccidental = (nval.pitch == line2pitch(line, clef, key));
     559            0 :                     ntpc = step2tpc(step % STEP_DELTA_OCTAVE, acci);
     560              :                 } else {
     561            0 :                     nval.pitch = line2pitch(line, clef, key);
     562            0 :                     ntpc = pitch2tpc(nval.pitch, key, Prefer::NEAREST);
     563              :                 }
     564              : 
     565            0 :                 Interval v = estaff->transpose(tick);
     566            0 :                 if (v.isZero()) {
     567            0 :                     nval.tpc1 = nval.tpc2 = ntpc;
     568              :                 } else {
     569            0 :                     if (style().styleB(Sid::concertPitch)) {
     570            0 :                         v.flip();
     571            0 :                         nval.tpc1 = ntpc;
     572            0 :                         nval.tpc2 = Transpose::transposeTpc(ntpc, v, true);
     573              :                     } else {
     574            0 :                         nval.pitch += v.chromatic;
     575            0 :                         nval.tpc2 = ntpc;
     576            0 :                         nval.tpc1 = Transpose::transposeTpc(ntpc, v, true);
     577              :                     }
     578              :                 }
     579              :             }
     580           40 :             noteValid = pitchIsValid(nval.pitch);
     581              :         }
     582              : 
     583           40 :         if (!noteValid) {
     584           30 :             notesToSelect.push_back(toEngravingItem(on));
     585           30 :             continue;
     586              :         }
     587              : 
     588           10 :         Note* note = Factory::createNote(chord);
     589           10 :         note->setTrack(chord->track());
     590           10 :         note->setNval(nval, tick);
     591           10 :         undoAddElement(note);
     592              : 
     593           10 :         if (forceAccidental) {
     594            0 :             Accidental* a = Factory::createAccidental(note);
     595            0 :             a->setAccidentalType(m_is.accidentalType());
     596            0 :             a->setRole(AccidentalRole::USER);
     597            0 :             undoAddElement(a);
     598              :         }
     599           10 :         if (on->tieBack() && prevTied) {
     600            0 :             Tie* tie = prevTied->tieFor();
     601            0 :             tie->setEndNote(note);
     602            0 :             tie->setTick2(note->tick());
     603            0 :             note->setTieBack(tie);
     604            0 :             undoAddElement(tie);
     605            0 :             prevTied = nullptr;
     606              :         }
     607              : 
     608           10 :         Tie* tieFor = on->tieFor();
     609           10 :         if (tieFor) {
     610            0 :             Tie* tie = tieFor->isLaissezVib() ? Factory::createLaissezVib(note) : Factory::createTie(note);
     611            0 :             tie->setStartNote(note);
     612            0 :             tie->setTick(note->tick());
     613            0 :             tie->setTrack(note->track());
     614            0 :             note->setTieFor(tie);
     615            0 :             prevTied = note;
     616              :         }
     617              : 
     618           10 :         setPlayNote(true);
     619           10 :         if (selIsList && note) {
     620           10 :             notesToSelect.push_back(toEngravingItem(note));
     621              :         }
     622              :     }
     623           40 :     if (m_is.noteEntryMode()) {
     624           40 :         m_is.setAccidentalType(AccidentalType::NONE);
     625              :     }
     626           40 :     if (notesToSelect.empty()) {
     627            0 :         return;
     628              :     }
     629           40 :     if (shouldSelectFirstNote) {
     630            0 :         select(notesToSelect.front(), SelectType::SINGLE, 0);
     631              :     } else {
     632           40 :         select(notesToSelect, SelectType::ADD, 0);
     633              :     }
     634           40 :     if (m_is.cr() == toChordRest(_nl.front()->chord()) && selIsSingle) {
     635            0 :         m_is.moveToNextInputPos();
     636              :     }
     637           40 : }
     638              : 
     639              : //---------------------------------------------------------
     640              : //   setGraceNote
     641              : ///   Create a grace note in front of a normal note.
     642              : ///   \arg ch is the chord of the normal note
     643              : ///   \arg pitch is the pitch of the grace note
     644              : ///   \arg is the grace note type
     645              : ///   \len is the visual duration of the grace note (1/16 or 1/32)
     646              : //---------------------------------------------------------
     647              : 
     648           70 : Note* Score::setGraceNote(Chord* ch, int pitch, NoteType type, int len)
     649              : {
     650           70 :     Chord* chord = Factory::createChord(this->dummy());
     651           70 :     Note* note = Factory::createNote(chord);
     652              : 
     653              :     // allow grace notes to be added to other grace notes
     654              :     // by really adding to parent chord
     655           70 :     if (ch->noteType() != NoteType::NORMAL) {
     656            0 :         ch = toChord(ch->ownershipParent());
     657              :     }
     658              : 
     659           70 :     chord->setTrack(ch->track());
     660           70 :     chord->setOwnershipParent(ch);
     661           70 :     chord->add(note);
     662              : 
     663              :     // find corresponding note within chord and use its tpc information
     664              :     // if no note with same pitch found, derive tpc from pitch / key
     665           70 :     if (Note* n = ch->findNote(pitch)) {
     666           65 :         note->setNval(n->noteVal(), ch->tick());
     667              :     } else {
     668            5 :         note->setPitch(pitch);
     669            5 :         note->setTpcFromPitch();
     670              :     }
     671              : 
     672           70 :     TDuration d;
     673           70 :     d.setVal(len);
     674           70 :     chord->setDurationType(d);
     675           70 :     chord->setTicks(d.fraction());
     676           70 :     chord->setNoteType(type);
     677           70 :     chord->setShowStemSlashInAdvance();
     678           70 :     chord->mutldata()->setMag(ch->staff()->staffMag(chord->tick()) * style().styleD(Sid::graceNoteMag));
     679              : 
     680           70 :     undoAddElement(chord);
     681           70 :     return note;
     682              : }
     683              : 
     684          292 : GuitarBend* Score::addGuitarBend(GuitarBendType type, Note* note, Note* endNote)
     685              : {
     686          292 :     if (note->isPreBendOrDiveStart()) {
     687            0 :         if (type == GuitarBendType::BEND && note->staffType()->isTabStaff()) {
     688            0 :             GuitarBend* preBend = note->bendFor();
     689            0 :             Note* mainNote = preBend ? preBend->endNote() : nullptr;
     690            0 :             if (mainNote) {
     691            0 :                 return addGuitarBend(type, mainNote, nullptr);
     692              :             }
     693              :         }
     694            0 :         return nullptr;
     695              :     }
     696              : 
     697          292 :     if (note->isGraceBendStart() && type != GuitarBendType::PRE_BEND) {
     698            0 :         return nullptr;
     699              :     }
     700              : 
     701          398 :     if (GuitarBend* bendBack = note->bendBack(); bendBack && !bendBack->isDive()
     702          398 :         && (type == GuitarBendType::PRE_BEND || type == GuitarBendType::GRACE_NOTE_BEND)) {
     703            0 :         return nullptr;
     704              :     }
     705              : 
     706          292 :     if (GuitarBend* bendFor = note->bendFor(); bendFor && !bendFor->isDive()
     707          292 :         && (type == GuitarBendType::BEND || type == GuitarBendType::SLIGHT_BEND)) {
     708            0 :         return nullptr;
     709              :     }
     710              : 
     711          292 :     if (type == GuitarBendType::DIP) {
     712            0 :         for (Note* n : note->chord()->notes()) {
     713            0 :             if (GuitarBend* bendFor = n->bendFor(); bendFor && bendFor->bendType() == GuitarBendType::DIP) {
     714            0 :                 return nullptr; // Only one dip per chord
     715              :             }
     716              :         }
     717              :     }
     718              : 
     719          292 :     Chord* chord = note->chord();
     720          292 :     bool isDive = static_cast<int>(type) >= static_cast<int>(GuitarBendType::DIVE)
     721          292 :                   && static_cast<int>(type) <= static_cast<int>(GuitarBendType::SCOOP);
     722              : 
     723          292 :     if (type == GuitarBendType::BEND || type == GuitarBendType::DIVE) {
     724          218 :         for (Spanner* sp : note->spannerFor()) {
     725            0 :             if ((sp->isGuitarBend() && toGuitarBend(sp)->isDive() == isDive) || sp->isGlissando()) {
     726            0 :                 return nullptr;
     727              :             }
     728              :         }
     729              : 
     730          218 :         if (!endNote) {
     731            0 :             endNote = SLine::guessFinalNote(note);
     732              :         }
     733              : 
     734          218 :         bool suitableEndNote = endNote;
     735          218 :         if (endNote) {
     736          218 :             for (Spanner* sp : endNote->spannerBack()) {
     737            0 :                 if (sp->isTie() || sp->isGlissando() || (sp->isGuitarBend() && toGuitarBend(sp)->isDive() == isDive)) {
     738            0 :                     suitableEndNote = false;
     739            0 :                     break;
     740              :                 }
     741              :             }
     742              :         }
     743              : 
     744          218 :         if (!suitableEndNote) {
     745            0 :             endNote = GuitarBend::createEndNote(note, type);
     746              :         }
     747              : 
     748          218 :         if (!endNote) {
     749            0 :             return nullptr;
     750              :         }
     751              :     }
     752              : 
     753          292 :     GuitarBend* bend = new GuitarBend(score()->dummy());
     754          292 :     bend->setTick(chord->tick());
     755          292 :     bend->setTrack(chord->track());
     756              : 
     757          292 :     if (type == GuitarBendType::BEND || type == GuitarBendType::DIVE) {
     758          218 :         bend->setBendType(chord->isGrace() && type == GuitarBendType::BEND ? GuitarBendType::GRACE_NOTE_BEND : type);
     759          218 :         bend->setStartElement(note);
     760          218 :         bend->setTick2(endNote->tick());
     761          218 :         bend->setTrack2(endNote->track());
     762          218 :         bend->setEndElement(endNote);
     763          218 :         bend->setOwnershipParent(note);
     764          218 :         if (type == GuitarBendType::BEND) {
     765          118 :             GuitarBend::fixNotesFrettingForStandardBend(note, endNote);
     766              :         }
     767              :     } else {
     768           74 :         bend->setBendType(type);
     769           74 :         bend->setTick2(chord->tick());
     770           74 :         bend->setTrack2(chord->track());
     771              : 
     772           74 :         if (type == GuitarBendType::PRE_BEND || type == GuitarBendType::GRACE_NOTE_BEND || type == GuitarBendType::PRE_DIVE) {
     773           66 :             const GraceNotesGroup& gracesBefore = chord->graceNotesBefore();
     774              : 
     775              :             // Add to existing grace chord only if it already has a bend/dive ghost.
     776           66 :             const bool hasGhostGrace = !gracesBefore.empty()
     777           71 :                                        && std::any_of(gracesBefore.back()->notes().begin(),
     778            5 :                                                       gracesBefore.back()->notes().end(),
     779            5 :                                                       [](const Note* n) {
     780            5 :                 return n->isPreBendOrDiveStart() || n->isGraceBendStart();
     781           66 :             });
     782              :             Note* graceNote = hasGhostGrace
     783           72 :                               ? NoteInput::addNote(transactionManager()->currentOrDummyTransaction(), this, gracesBefore.back(),
     784            3 :                                                    note->noteVal())
     785           66 :                               : setGraceNote(chord, note->pitch(), NoteType::APPOGGIATURA, Constants::DIVISION / 2);
     786           66 :             graceNote->transposeDiatonic(type == GuitarBendType::PRE_DIVE ? 1 : -1, true, false);
     787           66 :             GuitarBend::fixNotesFrettingForGraceBend(graceNote, note);
     788              : 
     789           66 :             Chord* graceChord = graceNote->chord();
     790          132 :             for (EngravingObject* item : graceChord->linkList()) {
     791           66 :                 Chord* linkedGrace = toChord(item);
     792           66 :                 linkedGrace->setNoStem(true);
     793           66 :                 linkedGrace->setBeamMode(BeamMode::NONE);
     794           66 :             }
     795              : 
     796              :             // Add bend
     797           66 :             bend->setOwnershipParent(graceNote);
     798           66 :             bend->setStartElement(graceNote);
     799           66 :             bend->setEndElement(note);
     800           74 :         } else if (type == GuitarBendType::SLIGHT_BEND || type == GuitarBendType::DIP || type == GuitarBendType::SCOOP) {
     801            8 :             bend->setOwnershipParent(note);
     802            8 :             bend->setStartElement(note);
     803              :             // Slight bends don't end on another note
     804            8 :             bend->setEndElement(note);
     805              :         }
     806              :     }
     807              : 
     808          292 :     Chord* startChord = bend->startNote()->chord();
     809          292 :     if (startChord->isGrace()) {
     810          202 :         for (EngravingObject* item : startChord->linkList()) {
     811          101 :             Chord* linkedGrace = toChord(item);
     812          101 :             if (linkedGrace->staffType()->isTabStaff()) {
     813            0 :                 linkedGrace->setNoStem(true);
     814            0 :                 linkedGrace->setBeamMode(BeamMode::NONE);
     815              :             }
     816          101 :         }
     817              :     }
     818              : 
     819          292 :     score()->undoAddElement(bend);
     820              : 
     821          292 :     if (GuitarBend* overlapping = bend->overlappingBendOrDive()) {
     822            0 :         int halfBendAmount = std::floor(overlapping->bendAmountInQuarterTones() / 2);
     823            0 :         if (bend->isDive()) {
     824            0 :             bend->undoChangeProperty(Pid::GUITAR_BEND_AMOUNT, halfBendAmount);
     825              :         } else {
     826            0 :             overlapping->undoChangeProperty(Pid::GUITAR_BEND_AMOUNT, halfBendAmount);
     827              :         }
     828              :     }
     829              : 
     830          292 :     return bend;
     831              : }
     832              : 
     833              : //---------------------------------------------------------
     834              : //   createCRSequence
     835              : //    Create a rest or chord of len f.
     836              : //    If f is not a basic len, create several rests or
     837              : //    tied chords.
     838              : //
     839              : //    f     total len of ChordRest
     840              : //    cr    prototype CR
     841              : //    tick  start position in measure
     842              : //---------------------------------------------------------
     843              : 
     844            0 : void Score::createCRSequence(const Fraction& f, ChordRest* cr, const Fraction& t)
     845              : {
     846            0 :     Fraction tick(t);
     847            0 :     Measure* measure = cr->measure();
     848            0 :     ChordRest* ocr = 0;
     849            0 :     for (TDuration d : toDurationList(f, true)) {
     850            0 :         ChordRest* ncr = toChordRest(cr->clone());
     851            0 :         ncr->setDurationType(d);
     852            0 :         ncr->setTicks(d.fraction());
     853            0 :         undoAddCR(ncr, measure, measure->tick() + tick);
     854            0 :         if (cr->isChord() && ocr) {
     855            0 :             Chord* nc = toChord(ncr);
     856            0 :             Chord* oc = toChord(ocr);
     857            0 :             for (unsigned int i = 0; i < oc->notes().size(); ++i) {
     858            0 :                 Note* on = oc->notes()[i];
     859            0 :                 Note* nn = nc->notes()[i];
     860            0 :                 Tie* tie = Factory::createTie(on);
     861            0 :                 tie->setStartNote(on);
     862            0 :                 tie->setEndNote(nn);
     863            0 :                 tie->setTick(tie->startNote()->tick());
     864            0 :                 tie->setTick2(tie->endNote()->tick());
     865            0 :                 tie->setTrack(cr->track());
     866            0 :                 on->setTieFor(tie);
     867            0 :                 nn->setTieBack(tie);
     868            0 :                 undoAddElement(tie);
     869              :             }
     870              :         }
     871              : 
     872            0 :         tick += ncr->actualTicks();
     873            0 :         ocr = ncr;
     874            0 :     }
     875            0 : }
     876              : 
     877              : //---------------------------------------------------------
     878              : //   setNoteRest
     879              : //    pitch == -1  -> set rest
     880              : //    "sd" is in local (stretched) time
     881              : //    return segment of last created note/rest
     882              : //---------------------------------------------------------
     883              : 
     884          566 : Segment* Score::setNoteRest(Segment* segment, track_idx_t track, NoteVal nval, Fraction sd, DirectionV stemDirection,
     885              :                             bool forceAccidental, const std::set<SymId>& articulationIds, bool rhythmic, InputState* externalInputState)
     886              : {
     887          566 :     assert(segment->segmentType() == SegmentType::ChordRest);
     888          566 :     InputState& is = externalInputState ? (*externalInputState) : m_is;
     889              : 
     890          566 :     bool isRest   = nval.isRest();
     891          566 :     Fraction tick = segment->tick();
     892          566 :     EngravingItem* nr   = nullptr;
     893          566 :     Tie* tie      = nullptr;
     894          566 :     ChordRest* cr = toChordRest(segment->element(track));
     895          566 :     Tuplet* tuplet = cr ? cr->tuplet() : nullptr;
     896          566 :     Measure* measure = nullptr;
     897          566 :     bool targetIsRest = cr && cr->isRest();
     898              : 
     899              :     // preserve lyrics before the ChordRest is removed or replaced
     900          566 :     std::vector<Lyrics*> lyricsToPreserve;
     901          566 :     bool shouldPreserveLyrics = false;
     902          566 :     if (!isRest && cr) {
     903          172 :         lyricsToPreserve = cr->lyrics();
     904          172 :         shouldPreserveLyrics = !lyricsToPreserve.empty();
     905              :     }
     906          566 :     bool lyricsPreserved = false;
     907              : 
     908              :     for (;;) {
     909          596 :         if (track % VOICES) {
     910           25 :             expandVoice(segment, track);
     911              :         }
     912          596 :         if (targetIsRest && !cr->isRest()) {
     913            0 :             undoRemoveElement(cr);
     914            0 :             segment = addRest(segment, track, cr->ticks(), cr->tuplet())->segment();
     915              :         }
     916              :         // the returned gap ends at the measure boundary or at tuplet end
     917          596 :         Fraction dd = makeGap(segment, track, sd, tuplet);
     918              : 
     919          596 :         if (dd.isZero()) {
     920            0 :             LOGD("cannot get gap at %d type: %d/%d", tick.ticks(), sd.numerator(),
     921              :                  sd.denominator());
     922            0 :             break;
     923              :         }
     924              : 
     925          596 :         measure = segment->measure();
     926          596 :         Fraction timeStretch = staff(track2staff(track))->timeStretch(tick);
     927          596 :         std::vector<TDuration> dl;
     928          596 :         if (rhythmic) {
     929         1146 :             dl = toRhythmicDurationList(dd, isRest, segment->rtick() * timeStretch, sigmap()->timesig(
     930         1146 :                                             tick).nominal(), measure, 1, timeStretch);
     931              :         } else {
     932          214 :             dl = toDurationList(dd, true);
     933              :         }
     934          596 :         size_t n = dl.size();
     935         1404 :         for (size_t i = 0; i < n; ++i) {
     936          808 :             const TDuration& d = dl[i];
     937          808 :             ChordRest* ncr = nullptr;
     938          808 :             Note* note = nullptr;
     939          808 :             Tie* addTie = nullptr;
     940          808 :             if (isRest) {
     941          590 :                 nr = ncr = Factory::createRest(this->dummy());
     942          590 :                 nr->setTrack(track);
     943          590 :                 ncr->setDurationType(d);
     944          590 :                 ncr->setTicks(d.isMeasure() ? measure->ticks() * timeStretch : d.fraction());
     945              :             } else {
     946          218 :                 nr = note = Factory::createNote(this->dummy());
     947              : 
     948          218 :                 if (tie) {
     949           39 :                     tie->setEndNote(note);
     950           39 :                     note->setTieBack(tie);
     951           39 :                     addTie = tie;
     952              :                 }
     953          218 :                 Chord* chord = Factory::createChord(this->dummy());
     954          218 :                 chord->setTrack(track);
     955          218 :                 chord->setDurationType(d);
     956          218 :                 chord->setTicks(d.fraction());
     957          218 :                 chord->setStemDirection(stemDirection);
     958          218 :                 chord->add(note);
     959          218 :                 if (cr && cr->isChord()) {
     960           21 :                     std::vector<Chord*> graceNotes = toChord(cr)->graceNotes();
     961           21 :                     for (Chord* grace : graceNotes) {
     962            0 :                         undoChangeParent(grace, chord, chord->staffIdx());
     963              :                     }
     964           21 :                 }
     965          218 :                 note->setNval(nval, tick);
     966          218 :                 if (forceAccidental) {
     967            0 :                     int tpc = style().styleB(Sid::concertPitch) ? nval.tpc1 : nval.tpc2;
     968            0 :                     AccidentalVal alter = tpc2alter(tpc);
     969            0 :                     AccidentalType at = Accidental::value2subtype(alter);
     970            0 :                     Accidental* a = Factory::createAccidental(note);
     971            0 :                     a->setAccidentalType(at);
     972            0 :                     a->setRole(AccidentalRole::USER);
     973            0 :                     note->add(a);
     974              :                 }
     975              : 
     976          218 :                 ncr = chord;
     977          218 :                 if (i + 1 < n) {
     978           13 :                     tie = Factory::createTie(note);
     979           13 :                     tie->setStartNote(note);
     980           13 :                     tie->setTick(tie->startNote()->tick());
     981           13 :                     tie->setTrack(track);
     982           13 :                     note->setTieFor(tie);
     983              :                 }
     984              :             }
     985          808 :             if (tuplet) {
     986           48 :                 ncr->setTuplet(tuplet);
     987              :             }
     988          808 :             undoAddCR(ncr, measure, tick);
     989              : 
     990          808 :             if (shouldPreserveLyrics && !lyricsPreserved) {
     991              :                 // reattach the preserved lyrics to the first replacement ChordRest
     992              :                 // to ensure they are not duplicated during subsequent operations
     993           10 :                 lyricsPreserved = true;
     994           26 :                 for (Lyrics* lyric : lyricsToPreserve) {
     995           16 :                     undoChangeParent(lyric, ncr, ncr->staffIdx());
     996              :                 }
     997              :             }
     998              : 
     999          808 :             if (addTie) {
    1000           39 :                 undoAddElement(addTie);
    1001              :             }
    1002          808 :             setPlayNote(true);
    1003          808 :             segment = ncr->segment();
    1004          808 :             tick += ncr->actualTicks();
    1005              : 
    1006          808 :             if (!articulationIds.empty()) {
    1007            0 :                 if (ncr->isChord()) {
    1008            0 :                     toChord(ncr)->updateArticulations(articulationIds);
    1009              :                 }
    1010              :             }
    1011              :         }
    1012              : 
    1013          596 :         sd -= dd;
    1014          596 :         if (sd.isZero()) {
    1015          566 :             break;
    1016              :         }
    1017              : 
    1018           30 :         if (tick >= score()->endTick()) {
    1019            1 :             appendMeasures(1);
    1020              :         }
    1021           30 :         Segment* nseg = tick2segment(tick, false, SegmentType::ChordRest);
    1022           30 :         if (!nseg) {
    1023            0 :             break;
    1024              :         }
    1025           30 :         segment = nseg;
    1026              : 
    1027           30 :         cr = toChordRest(segment->element(track));
    1028              :         // if the cr was too big for the tuplet, get the next tuplet so we can spill into that one
    1029           30 :         tuplet = cr && cr->tuplet() ? cr->tuplet() : nullptr;
    1030           30 :         if (!cr) {
    1031            0 :             if (track % VOICES) {
    1032            0 :                 cr = addRest(segment, track, TDuration(DurationType::V_MEASURE), 0);
    1033              :             } else {
    1034            0 :                 LOGD("no rest in voice 0");
    1035            0 :                 break;
    1036              :             }
    1037              :         }
    1038              :         //
    1039              :         //  Note does not fit on current measure, create Tie to
    1040              :         //  next part of note
    1041           30 :         if (!isRest) {
    1042           26 :             tie = Factory::createTie((Note*)nr);
    1043           26 :             tie->setStartNote((Note*)nr);
    1044           26 :             tie->setTick(tie->startNote()->tick());
    1045           26 :             tie->setTrack(nr->track());
    1046           26 :             ((Note*)nr)->setTieFor(tie);
    1047              :         }
    1048          626 :     }
    1049          566 :     if (tie) {
    1050           18 :         connectTies();
    1051              :     }
    1052              : 
    1053          566 :     if (nr) {
    1054          566 :         if (is.slur() && nr->isNote()) {
    1055              :             // If the start element was the same as the end element when the slur was created,
    1056              :             // the end grip of the front slur segment was given an x-offset of 3.0 * spatium().
    1057              :             // Now that the slur is about to be given a new end element, this should be reset.
    1058            0 :             if (is.slur()->endElement() == is.slur()->startElement()) {
    1059            0 :                 is.slur()->frontSegment()->reset();
    1060              :             }
    1061              :             //
    1062              :             // extend slur
    1063              :             //
    1064            0 :             Chord* chord = toNote(nr)->chord();
    1065            0 :             is.slur()->undoChangeProperty(Pid::SPANNER_TICKS, chord->tick() - is.slur()->tick());
    1066            0 :             for (EngravingObject* se : is.slur()->linkList()) {
    1067            0 :                 Slur* slur = toSlur(se);
    1068            0 :                 for (EngravingObject* ee : chord->linkList()) {
    1069            0 :                     EngravingItem* e = static_cast<EngravingItem*>(ee);
    1070            0 :                     if (e->score() == slur->score() && e->track() == slur->track2()) {
    1071            0 :                         slur->score()->undo(new ChangeSpannerElements(slur, slur->startElement(), e));
    1072            0 :                         break;
    1073              :                     }
    1074            0 :                 }
    1075            0 :             }
    1076              :         }
    1077          566 :         if (externalInputState) {
    1078           50 :             is.setTrack(nr->track());
    1079           50 :             cr = nr->isRest() ? toChordRest(nr) : toNote(nr)->chord();
    1080           50 :             is.setLastSegment(is.segment());
    1081           50 :             is.setSegment(cr->segment());
    1082              :         } else {
    1083          516 :             select(nr, SelectType::SINGLE, 0);
    1084              :         }
    1085              :     }
    1086          566 :     return segment;
    1087          566 : }
    1088              : 
    1089              : //---------------------------------------------------------
    1090              : //   makeGap
    1091              : //    make time gap at tick by removing/shortening
    1092              : //    chord/rest
    1093              : //
    1094              : //    if keepChord, the chord at tick is not removed
    1095              : //
    1096              : //    "sd" is in local (stretched) time
    1097              : //
    1098              : //    gap does not exceed measure or scope of tuplet
    1099              : //
    1100              : //    return size of actual gap in local time
    1101              : //---------------------------------------------------------
    1102              : 
    1103         2157 : Fraction Score::makeGap(Segment* segment, track_idx_t track, const Fraction& _sd, Tuplet* tuplet, bool keepChord, bool deleteAnnotations)
    1104              : {
    1105         2157 :     assert(_sd.numerator());
    1106              : 
    1107         2157 :     Staff* stf = staff(track2staff(track));
    1108         2157 :     IF_ASSERT_FAILED(stf) {
    1109            0 :         return Fraction();
    1110              :     }
    1111              : 
    1112         2157 :     Measure* measure = segment->measure();
    1113         2157 :     Fraction accumulated;
    1114         2157 :     Fraction sd = _sd;
    1115              : 
    1116              :     //
    1117              :     // remember first segment which should
    1118              :     // not be deleted (it may contain other elements we want to preserve)
    1119              :     //
    1120         2157 :     Segment* firstSegment = segment;
    1121         2157 :     const Fraction firstSegmentEnd = firstSegment->tick() + firstSegment->ticks();
    1122         2157 :     Fraction nextTick = segment->tick();
    1123              : 
    1124         3853 :     for (Segment* seg = firstSegment; seg; seg = seg->next(SegmentType::ChordRest)) {
    1125              :         //
    1126              :         // voices != 0 may have gaps:
    1127              :         //
    1128         3840 :         ChordRest* cr = toChordRest(seg->element(track));
    1129         3840 :         Fraction timeStretch = stf->timeStretch(seg->tick());
    1130         3840 :         if (!cr) {
    1131           25 :             if (seg->tick() < nextTick) {
    1132           23 :                 continue;
    1133              :             }
    1134            2 :             Segment* seg1 = seg->next(SegmentType::ChordRest);
    1135            2 :             Fraction tick2 = seg1 ? seg1->tick() : seg->measure()->endTick();
    1136            2 :             Fraction td(tick2 - seg->tick());
    1137            2 :             td /= actualTicks(Fraction(1, 1), tuplet, timeStretch);
    1138            2 :             if (td > sd) {
    1139            1 :                 td = sd;
    1140              :             }
    1141            2 :             accumulated += td;
    1142            2 :             sd -= td;
    1143            2 :             if (sd.isZero()) {
    1144            2 :                 break;
    1145              :             }
    1146            0 :             nextTick = tick2;
    1147            0 :             continue;
    1148            0 :         }
    1149         3815 :         if (seg->tick() > nextTick) {
    1150              :             // there was a gap
    1151            0 :             Fraction td(seg->tick() - nextTick);
    1152            0 :             td /= actualTicks(Fraction(1, 1), tuplet, timeStretch);
    1153            0 :             if (td > sd) {
    1154            0 :                 td = sd;
    1155              :             }
    1156            0 :             accumulated += td;
    1157            0 :             sd -= td;
    1158            0 :             if (sd.isZero()) {
    1159            0 :                 break;
    1160              :             }
    1161              :         }
    1162              :         //
    1163              :         // limit to tuplet level
    1164              :         //
    1165         3815 :         if (tuplet) {
    1166          151 :             bool tupletEnd = true;
    1167          151 :             Tuplet* t = cr->tuplet();
    1168          165 :             while (t) {
    1169          141 :                 if (t == tuplet) {
    1170          127 :                     tupletEnd = false;
    1171          127 :                     break;
    1172              :                 }
    1173           14 :                 t = t->tuplet();
    1174              :             }
    1175          151 :             if (tupletEnd) {
    1176           24 :                 break;
    1177              :             }
    1178              :         }
    1179         3791 :         Fraction td(cr->ticks());
    1180              : 
    1181              :         // remove tremolo between 2 notes, if present
    1182         3791 :         if (cr->isChord()) {
    1183         1547 :             Chord* c = toChord(cr);
    1184         1547 :             if (c->tremoloTwoChord()) {
    1185            0 :                 undoRemoveElement(c->tremoloTwoChord());
    1186              :             }
    1187              :         }
    1188         3791 :         Tuplet* ltuplet = cr->tuplet();
    1189         3791 :         if (ltuplet != tuplet) {
    1190              :             //
    1191              :             // Current location points to the start of a (nested)tuplet.
    1192              :             // We have to remove the complete tuplet.
    1193              : 
    1194              :             // get the outermost tuplet inside the current one
    1195            4 :             while (ltuplet->tuplet() != tuplet) {
    1196            0 :                 ltuplet = ltuplet->tuplet();
    1197              :             }
    1198              : 
    1199              :             // get last segment of tuplet, drilling down to leaf nodes as necessary
    1200            4 :             Tuplet* t = ltuplet;
    1201            4 :             while (t->elements().back()->isTuplet()) {
    1202            0 :                 t = toTuplet(t->elements().back());
    1203              :             }
    1204            4 :             seg = toChordRest(t->elements().back())->segment();
    1205              : 
    1206              :             // now delete the full tuplet
    1207            4 :             td = ltuplet->ticks();
    1208            4 :             cmdDeleteTuplet(ltuplet, false);
    1209              :         } else {
    1210         3787 :             if (seg != firstSegment || !keepChord) {
    1211         3731 :                 undoRemoveElement(cr);
    1212              :             }
    1213              :             // even if there was a tuplet, we didn't remove it
    1214         3787 :             ltuplet = 0;
    1215              :         }
    1216         3791 :         nextTick += actualTicks(td, tuplet, timeStretch);
    1217         3791 :         if (sd < td) {
    1218              :             //
    1219              :             // we removed too much
    1220              :             //
    1221          284 :             accumulated = _sd;
    1222          284 :             Fraction rd = td - sd;
    1223          284 :             Fraction tick = cr->tick() + actualTicks(sd, tuplet, timeStretch);
    1224              : 
    1225          284 :             std::vector<TDuration> dList;
    1226          284 :             if (tuplet) {
    1227            5 :                 dList = toDurationList(rd, false);
    1228            5 :                 std::reverse(dList.begin(), dList.end());
    1229              :             } else {
    1230          279 :                 TimeSig* timeSig = stf->timeSig(tick);
    1231          279 :                 TimeSigFrac refTimeSig = timeSig ? timeSig->sig() : sigmap()->timesig(tick).nominal();
    1232          279 :                 Fraction rTickStart = (tick - measure->tick()) * stf->timeStretch(tick);
    1233          279 :                 dList = toRhythmicDurationList(rd, true, rTickStart, refTimeSig, measure, 0, stf->timeStretch(tick));
    1234              :             }
    1235          284 :             if (dList.empty()) {
    1236            0 :                 break;
    1237              :             }
    1238              : 
    1239          725 :             for (TDuration d : dList) {
    1240          441 :                 if (ltuplet) {
    1241              :                     // take care not to recreate tuplet we just deleted
    1242            4 :                     Rest* r = setRest(tick, track, d.fraction(), false, tuplet, false);
    1243            4 :                     tick += r->actualTicks();
    1244              :                 } else {
    1245          437 :                     tick += addClone(cr, tick, d)->actualTicks();
    1246              :                 }
    1247              :             }
    1248          284 :             break;
    1249          284 :         }
    1250         3507 :         accumulated += td;
    1251         3507 :         sd          -= td;
    1252         3507 :         if (sd.isZero()) {
    1253         1834 :             break;
    1254              :         }
    1255              :     }
    1256              : 
    1257         2157 :     const Fraction t1 = firstSegmentEnd;
    1258         2157 :     const Fraction t2 = firstSegment->tick() + actualTicks(accumulated, tuplet, stf->timeStretch(firstSegment->tick()));
    1259         2157 :     if (t1 < t2) {
    1260              :         // Delete annotations that require an anchor to the previous segment
    1261         1113 :         Segment* s1 = tick2rightSegment(t1);
    1262         1113 :         Segment* s2 = tick2rightSegment(t2);
    1263         1113 :         const bool segsValid = s1 && s2 && (*s2) > (*s1);
    1264         1113 :         if (segsValid && deleteAnnotations) {
    1265         1279 :             for (Segment* s = s1; s && s != s2; s = s->next1()) {
    1266          917 :                 const auto annotations = s->annotations(); // make a copy since we alter the list
    1267          920 :                 for (EngravingItem* annotation : annotations) {
    1268            3 :                     if (!annotation->systemFlag() && !annotation->allowTimeAnchor() && annotation->track() == track) {
    1269            0 :                         deleteItem(annotation);
    1270              :                     }
    1271              :                 }
    1272          917 :             }
    1273              :         }
    1274              : 
    1275         1113 :         SelectionFilter filter;
    1276         1113 :         deleteSlursFromRange(t1, t2, track, track + 1, filter);
    1277              :     }
    1278              : 
    1279         2157 :     return accumulated;
    1280              : }
    1281              : 
    1282          401 : bool Score::makeGapVoice(Segment* seg, track_idx_t track, Fraction len, const Fraction& tick, bool deleteAnnotations)
    1283              : {
    1284          401 :     ChordRest* cr = 0;
    1285          401 :     cr = toChordRest(seg->element(track));
    1286          401 :     if (!cr) {
    1287              :         // check if we are in the middle of a chord/rest
    1288           44 :         Segment* seg1 = seg->prev(SegmentType::ChordRest);
    1289              :         for (;;) {
    1290           50 :             if (seg1 == 0) {
    1291           30 :                 if (!(track % VOICES)) {
    1292            0 :                     LOGD("no segment before tick %d", tick.ticks());
    1293              :                 }
    1294              :                 // this happens only for voices other than voice 1
    1295           30 :                 expandVoice(seg, track);
    1296           30 :                 return makeGapVoice(seg, track, len, tick, deleteAnnotations);
    1297              :             }
    1298           20 :             if (seg1->element(track)) {
    1299           14 :                 break;
    1300              :             }
    1301            6 :             seg1 = seg1->prev(SegmentType::ChordRest);
    1302              :         }
    1303           14 :         ChordRest* cr1 = toChordRest(seg1->element(track));
    1304           14 :         Fraction srcF = cr1->ticks();
    1305           14 :         Fraction dstF = (tick - cr1->tick()) * cr1->staff()->timeStretch(cr1->tick());
    1306           14 :         std::vector<TDuration> dList = toDurationList(dstF, true);
    1307           14 :         if (dList.empty()) {
    1308            0 :             LOGD("Could not make durations for: %d/%d", dstF.numerator(), dstF.denominator());
    1309            0 :             return false;
    1310              :         }
    1311           14 :         size_t n = dList.size();
    1312           14 :         undoChangeChordRestLen(cr1, TDuration(dList[0]));
    1313           14 :         if (n > 1) {
    1314            1 :             Fraction crtick = cr1->endTick();
    1315            1 :             Measure* measure = tick2measure(crtick);
    1316            1 :             if (cr1->isChord()) {
    1317              :                 // split Chord
    1318            1 :                 Chord* c = toChord(cr1);
    1319            2 :                 for (size_t i = 1; i < n; ++i) {
    1320            1 :                     TDuration d = dList[i];
    1321            1 :                     Chord* c2 = addChord(crtick, d, c, true, c->tuplet());
    1322            1 :                     c = c2;
    1323            1 :                     seg1 = c->segment();
    1324            1 :                     crtick += c->actualTicks();
    1325              :                 }
    1326              :             } else {
    1327              :                 // split Rest
    1328            0 :                 Rest* r       = toRest(cr1);
    1329            0 :                 for (size_t i = 1; i < n; ++i) {
    1330            0 :                     TDuration d = dList[i];
    1331            0 :                     Rest* r2      = toRest(r->clone());
    1332            0 :                     r2->setTicks(d.fraction());
    1333            0 :                     r2->setDurationType(d);
    1334            0 :                     undoAddCR(r2, measure, crtick);
    1335            0 :                     seg1 = r2->segment();
    1336            0 :                     crtick += r2->actualTicks();
    1337              :                 }
    1338              :             }
    1339              :         }
    1340           14 :         setRest(tick, track, srcF - dstF, true, 0);
    1341              :         for (;;) {
    1342           14 :             seg1 = seg1->next1(SegmentType::ChordRest);
    1343           14 :             if (seg1 == 0) {
    1344            0 :                 LOGD("no segment");
    1345            0 :                 return false;
    1346              :             }
    1347           14 :             if (seg1->element(track)) {
    1348           14 :                 cr = toChordRest(seg1->element(track));
    1349           14 :                 break;
    1350              :             }
    1351              :         }
    1352           14 :     }
    1353              : 
    1354              :     for (;;) {
    1355          375 :         if (!cr) {
    1356            0 :             LOGD("cannot make gap");
    1357            0 :             return false;
    1358              :         }
    1359          375 :         Fraction l = makeGap(cr->segment(), cr->track(), len, nullptr, /*keepChord*/ false, deleteAnnotations);
    1360          375 :         if (l.isZero()) {
    1361            0 :             LOGD("returns zero gap");
    1362            0 :             return false;
    1363              :         }
    1364          375 :         len -= l;
    1365          375 :         if (len.isZero()) {
    1366          371 :             break;
    1367              :         }
    1368              :         // go to next cr
    1369            4 :         Measure* m = cr->measure()->nextMeasure();
    1370            4 :         if (!m) {
    1371            0 :             LOGD("EOS reached");
    1372            0 :             InsertMeasureOptions options;
    1373            0 :             options.createMeasureRests = false;
    1374            0 :             insertMeasure(ElementType::MEASURE, nullptr, options);
    1375            0 :             m = cr->measure()->nextMeasure();
    1376            0 :             if (!m) {
    1377            0 :                 LOGD("===EOS reached");
    1378            0 :                 return true;
    1379              :             }
    1380              :         }
    1381              :         // first segment in measure was removed, have to recreate it
    1382            4 :         Segment* s = m->undoGetSegment(SegmentType::ChordRest, m->tick());
    1383            4 :         track_idx_t t = cr->track();
    1384            4 :         cr = toChordRest(s->element(t));
    1385            4 :         if (!cr) {
    1386            2 :             addRest(s, t, TDuration(DurationType::V_MEASURE), 0);
    1387            2 :             cr = toChordRest(s->element(t));
    1388              :         }
    1389            4 :     }
    1390          371 :     return true;
    1391              : }
    1392              : 
    1393              : //---------------------------------------------------------
    1394              : //   splitGapToMeasureBoundaries
    1395              : //    cr  - start of gap
    1396              : //    gap - gap len in local (stretched) time
    1397              : //---------------------------------------------------------
    1398              : 
    1399           28 : std::vector<Fraction> Score::splitGapToMeasureBoundaries(ChordRest* cr, Fraction gap)
    1400              : {
    1401           28 :     std::vector<Fraction> flist;
    1402              : 
    1403           28 :     Tuplet* tuplet = cr->tuplet();
    1404           28 :     if (tuplet) {
    1405            1 :         Fraction rest = tuplet->elementsDuration();
    1406            3 :         for (DurationElement* de : tuplet->elements()) {
    1407            3 :             if (de == cr) {
    1408            1 :                 break;
    1409              :             }
    1410            2 :             rest -= de->ticks();
    1411              :         }
    1412            1 :         if (rest < gap) {
    1413            0 :             LOGD("does not fit in tuplet");
    1414              :         } else {
    1415            1 :             flist.push_back(gap);
    1416              :         }
    1417            1 :         return flist;
    1418              :     }
    1419              : 
    1420           27 :     Segment* s = cr->segment();
    1421           30 :     while (gap > Fraction(0, 1)) {
    1422           30 :         Measure* m    = s->measure();
    1423           30 :         Fraction timeStretch = cr->staff()->timeStretch(s->tick());
    1424           30 :         Fraction rest = (m->ticks() - s->rtick()) * timeStretch;
    1425           30 :         if (rest >= gap) {
    1426           27 :             flist.push_back(gap);
    1427           27 :             return flist;
    1428              :         }
    1429            3 :         flist.push_back(rest);
    1430            3 :         gap -= rest;
    1431            3 :         if (m->nextMeasure() != 0) {
    1432            3 :             m = m->nextMeasure();
    1433              :         }
    1434            3 :         s = m->first(SegmentType::ChordRest);
    1435              :     }
    1436            0 :     return flist;
    1437            0 : }
    1438              : 
    1439              : //---------------------------------------------------------
    1440              : //   changeCRlen
    1441              : //    - dstF is in local (stretched) time
    1442              : //---------------------------------------------------------
    1443              : 
    1444           50 : void Score::changeCRlen(ChordRest* cr, const TDuration& d)
    1445              : {
    1446           50 :     Fraction dstF;
    1447           50 :     if (d.type() == DurationType::V_MEASURE) {
    1448            0 :         dstF = cr->measure()->stretchedLen(cr->staff());
    1449              :     } else {
    1450           50 :         dstF = d.fraction();
    1451              :     }
    1452           50 :     changeCRlen(cr, dstF);
    1453           50 : }
    1454              : 
    1455           55 : void Score::changeCRlen(ChordRest* cr, const Fraction& dstF, bool fillWithRest)
    1456              : {
    1457           55 :     if (cr->isMeasureRepeat()) {
    1458              :         // it is not clear what should this
    1459              :         // operation mean for measure repeats.
    1460           27 :         return;
    1461              :     }
    1462           55 :     Fraction srcF(cr->ticks());
    1463           55 :     if (srcF == dstF) {
    1464            0 :         if (cr->isFullMeasureRest()) {
    1465            0 :             undoChangeChordRestLen(cr, dstF);
    1466              :         }
    1467            0 :         return;
    1468              :     }
    1469              : 
    1470              :     //keep selected element if any
    1471           55 :     EngravingItem* selElement = selection().isSingle() ? getSelectedElement() : 0;
    1472              : 
    1473           55 :     track_idx_t track = cr->track();
    1474           55 :     Tuplet* tuplet = cr->tuplet();
    1475              : 
    1476           55 :     if (srcF > dstF) {
    1477              :         //
    1478              :         // make shorter and fill with rest
    1479              :         //
    1480           27 :         deselectAll();
    1481           27 :         if (cr->isChord()) {
    1482              :             //
    1483              :             // remove ties and tremolo between 2 notes
    1484              :             //
    1485           24 :             Chord* c = toChord(cr);
    1486           24 :             if (c->tremoloTwoChord()) {
    1487            0 :                 undoRemoveElement(c->tremoloTwoChord());
    1488              :             }
    1489           48 :             for (Note* n : c->notes()) {
    1490           24 :                 if (Tie* tie = n->tieFor()) {
    1491            0 :                     if (tie->tieJumpPoints()) {
    1492            0 :                         tie->undoRemoveTiesFromJumpPoints();
    1493              :                     }
    1494            0 :                     undoRemoveElement(tie);
    1495              :                 }
    1496           24 :                 for (Spanner* sp : n->spannerFor()) {
    1497            0 :                     if (sp->isGlissando() || sp->isGuitarBend()) {
    1498            0 :                         undoRemoveElement(sp);
    1499              :                     }
    1500              :                 }
    1501              :             }
    1502              :         }
    1503           27 :         Fraction timeStretch = cr->staff()->timeStretch(cr->tick());
    1504           27 :         std::vector<TDuration> dList = toDurationList(dstF, true);
    1505           27 :         if (dList.empty()) {
    1506            0 :             LOGD("Could not make durations for: %d/%d", dstF.numerator(), dstF.denominator());
    1507            0 :             return;
    1508              :         }
    1509           27 :         undoChangeChordRestLen(cr, dList[0]);
    1510           27 :         Fraction tick2 = cr->tick();
    1511           27 :         for (unsigned i = 1; i < dList.size(); ++i) {
    1512            0 :             tick2 += actualTicks(dList[i - 1].ticks(), tuplet, timeStretch);
    1513            0 :             TDuration d = dList[i];
    1514            0 :             setRest(tick2, track, d.fraction(), (d.dots() > 0), tuplet);
    1515              :         }
    1516           27 :         if (fillWithRest) {
    1517           27 :             setRest(cr->endTick(), track, srcF - dstF, false, tuplet);
    1518              :         }
    1519              : 
    1520           27 :         if (selElement) {
    1521           21 :             select(selElement, SelectType::SINGLE, 0);
    1522              :         }
    1523           27 :         return;
    1524           27 :     }
    1525              : 
    1526              :     //
    1527              :     // make longer
    1528              :     //
    1529              :     // split required len into Measures
    1530           28 :     std::vector<Fraction> flist = splitGapToMeasureBoundaries(cr, dstF);
    1531           28 :     if (flist.empty()) {
    1532            0 :         return;
    1533              :     }
    1534              : 
    1535           28 :     deselectAll();
    1536           28 :     EngravingItem* elementToSelect = nullptr;
    1537              : 
    1538           28 :     Fraction tick  = cr->tick();
    1539           28 :     Fraction f     = dstF;
    1540           28 :     ChordRest* cr1 = cr;
    1541           28 :     Chord* oc      = 0;
    1542           28 :     Segment* s     = cr->segment();
    1543              : 
    1544           28 :     bool first = true;
    1545           57 :     for (const Fraction& f2 : flist) {
    1546           31 :         if (!cr1) {
    1547            0 :             expandVoice(s, track);
    1548            0 :             cr1 = toChordRest(s->element(track));
    1549              :         }
    1550              : 
    1551           31 :         f  -= f2;
    1552           31 :         makeGap(cr1->segment(), cr1->track(), f2, tuplet, first);
    1553              : 
    1554           31 :         if (cr->isRest()) {
    1555            4 :             Fraction timeStretch = cr1->staff()->timeStretch(cr1->tick());
    1556            4 :             Rest* r = toRest(cr);
    1557            4 :             if (first) {
    1558            1 :                 std::vector<TDuration> dList = toDurationList(f2, true);
    1559            1 :                 undoChangeChordRestLen(cr, dList[0]);
    1560            1 :                 Fraction tick2 = cr->tick();
    1561            1 :                 for (unsigned i = 1; i < dList.size(); ++i) {
    1562            0 :                     tick2 += actualTicks(dList[i - 1].ticks(), tuplet, timeStretch);
    1563            0 :                     TDuration d = dList[i];
    1564            0 :                     setRest(tick2, track, d.fraction(), (d.dots() > 0), tuplet);
    1565              :                 }
    1566            1 :             } else {
    1567            3 :                 r = setRest(tick, track, f2, false, tuplet);
    1568              :             }
    1569            4 :             if (first) {
    1570            1 :                 elementToSelect = r;
    1571            1 :                 first = false;
    1572              :             }
    1573            4 :             tick += actualTicks(f2, tuplet, timeStretch);
    1574              :         } else {
    1575           27 :             std::vector<TDuration> dList = toDurationList(f2, true);
    1576           27 :             Measure* measure             = tick2measure(tick);
    1577           27 :             Fraction etick                    = measure->tick();
    1578              : 
    1579           27 :             if (((tick - etick).ticks() % dList[0].ticks().ticks()) == 0) {
    1580           52 :                 for (TDuration du : dList) {
    1581           26 :                     Chord* cc = nullptr;
    1582           26 :                     if (oc) {
    1583            0 :                         cc = oc;
    1584            0 :                         oc = addChord(tick, du, cc, true, tuplet);
    1585              :                     } else {
    1586           26 :                         cc = toChord(cr);
    1587           26 :                         undoChangeChordRestLen(cr, du);
    1588           26 :                         oc = cc;
    1589              :                     }
    1590           26 :                     if (oc && first) {
    1591           26 :                         elementToSelect = selElement ? selElement : oc;
    1592           26 :                         first = false;
    1593              :                     }
    1594           26 :                     if (oc) {
    1595           26 :                         tick += oc->actualTicks();
    1596              :                     }
    1597              :                 }
    1598              :             } else {
    1599            2 :                 for (size_t i = dList.size(); i > 0; --i) {         // loop probably needs to be in this reverse order
    1600              :                     Chord* cc;
    1601            1 :                     if (oc) {
    1602            0 :                         cc = oc;
    1603            0 :                         oc = addChord(tick, dList[i - 1], cc, true, tuplet);
    1604              :                     } else {
    1605            1 :                         cc = toChord(cr);
    1606            1 :                         undoChangeChordRestLen(cr, dList[i - 1]);
    1607            1 :                         oc = cc;
    1608              :                     }
    1609            1 :                     if (first) {
    1610            1 :                         elementToSelect = selElement;
    1611            1 :                         first = false;
    1612              :                     }
    1613            1 :                     tick += oc->actualTicks();
    1614              :                 }
    1615              :             }
    1616           27 :         }
    1617           31 :         const Measure* m  = cr1->measure();
    1618           31 :         if (m->nextMeasure() == 0) {
    1619            2 :             if (f.isNotZero()) {
    1620            0 :                 appendMeasures(1);
    1621              :             }
    1622              :         }
    1623           31 :         const Measure* m1 = m->nextMeasure();
    1624           31 :         if (m1 == 0) {
    1625            2 :             break;
    1626              :         }
    1627           29 :         s = m1->first(SegmentType::ChordRest);
    1628           29 :         cr1 = toChordRest(s->element(track));
    1629              :     }
    1630           28 :     connectTies();
    1631              : 
    1632           28 :     if (elementToSelect) {
    1633           27 :         if (canReselectItem(elementToSelect)) {
    1634           22 :             select(elementToSelect, SelectType::SINGLE, 0);
    1635              :         }
    1636              :     }
    1637           28 : }
    1638              : 
    1639              : //---------------------------------------------------------
    1640              : //   cmdMoveRest
    1641              : //---------------------------------------------------------
    1642              : 
    1643            0 : void Score::cmdMoveRest(Rest* rest, DirectionV dir)
    1644              : {
    1645            0 :     PointF pos(rest->offset());
    1646            0 :     if (dir == DirectionV::UP) {
    1647            0 :         pos.ry() -= rest->spatium() * rest->staffType()->lineDistance().val();
    1648            0 :     } else if (dir == DirectionV::DOWN) {
    1649            0 :         pos.ry() += rest->spatium() * rest->staffType()->lineDistance().val();
    1650              :     }
    1651            0 :     rest->undoChangeProperty(Pid::OFFSET, pos);
    1652            0 : }
    1653              : 
    1654              : //---------------------------------------------------------
    1655              : //   cmdMoveLyrics
    1656              : //---------------------------------------------------------
    1657              : 
    1658            0 : void Score::cmdMoveLyrics(Lyrics* lyrics, DirectionV dir)
    1659              : {
    1660            0 :     int verse = lyrics->verse() + (dir == DirectionV::UP ? -1 : 1);
    1661            0 :     if (verse < 0) {
    1662            0 :         return;
    1663              :     }
    1664            0 :     lyrics->undoChangeProperty(Pid::VERSE, verse);
    1665              : }
    1666              : 
    1667              : //---------------------------------------------------------
    1668              : //   cmdAddGrace
    1669              : ///   adds grace note of specified type to selected notes
    1670              : //---------------------------------------------------------
    1671              : 
    1672            0 : void Score::cmdAddGrace(NoteType graceType, int duration)
    1673              : {
    1674            0 :     const std::vector<EngravingItem*> copyOfElements = selection().elements();
    1675            0 :     for (EngravingItem* e : copyOfElements) {
    1676            0 :         if (e->isNote()) {
    1677            0 :             Note* n = toNote(e);
    1678            0 :             Note* graceNote = setGraceNote(n->chord(), n->pitch(), graceType, duration);
    1679            0 :             select(graceNote, SelectType::SINGLE, 0);
    1680              :         }
    1681              :     }
    1682            0 : }
    1683              : 
    1684              : //---------------------------------------------------------
    1685              : //   cmdRemoveEmptyTrailingMeasures
    1686              : //---------------------------------------------------------
    1687              : 
    1688            0 : void Score::cmdRemoveEmptyTrailingMeasures()
    1689              : {
    1690            0 :     Score* mScore = masterScore();
    1691            0 :     auto beginMeasure = mScore->firstTrailingMeasure();
    1692            0 :     if (beginMeasure) {
    1693            0 :         mScore->deleteMeasures(beginMeasure, mScore->lastMeasure());
    1694              :     }
    1695            0 : }
    1696              : 
    1697              : //---------------------------------------------------------
    1698              : //   cmdToggleLayoutBreak
    1699              : //---------------------------------------------------------
    1700              : 
    1701            0 : void Score::cmdToggleLayoutBreak(LayoutBreakType type)
    1702              : {
    1703              :     // find measure(s)
    1704            0 :     std::vector<MeasureBase*> mbl;
    1705            0 :     bool allNoBreaks = true; // NOBREAK is not removed unless every measure in selection already has one
    1706            0 :     if (selection().isRange()) {
    1707            0 :         MeasureBase* startMeasureBase = nullptr;
    1708            0 :         MeasureBase* endMeasureBase = nullptr;
    1709            0 :         if (!selection().measureBaseRange(&startMeasureBase, &endMeasureBase)) {
    1710            0 :             return;
    1711              :         }
    1712            0 :         if (!startMeasureBase || !endMeasureBase) {
    1713            0 :             return;
    1714              :         }
    1715            0 :         if (type == LayoutBreakType::NOBREAK) {
    1716              :             // add throughout the selection
    1717              :             // or remove if already on every measure
    1718            0 :             if (startMeasureBase == endMeasureBase) {
    1719            0 :                 mbl.push_back(startMeasureBase);
    1720            0 :                 allNoBreaks = startMeasureBase->noBreak();
    1721              :             } else {
    1722            0 :                 for (MeasureBase* m = startMeasureBase; m; m = m->nextMM()) {
    1723            0 :                     if (m == endMeasureBase) {
    1724            0 :                         break;
    1725              :                     }
    1726            0 :                     mbl.push_back(m);
    1727            0 :                     if (!toMeasureBase(m)->noBreak()) {
    1728            0 :                         allNoBreaks = false;
    1729              :                     }
    1730              :                 }
    1731              :             }
    1732              :         } else {
    1733              :             // toggle break on the last measure of the range
    1734            0 :             mbl.push_back(endMeasureBase);
    1735              :             // if more than one measure selected,
    1736              :             // also toggle break *before* the range (to try to fit selection on a single line)
    1737            0 :             if (startMeasureBase != endMeasureBase && startMeasureBase->prev()) {
    1738            0 :                 mbl.push_back(startMeasureBase->prev());
    1739              :             }
    1740              :         }
    1741              :     } else {
    1742            0 :         MeasureBase* mb = nullptr;
    1743            0 :         for (EngravingItem* el : selection().elements()) {
    1744            0 :             switch (el->type()) {
    1745            0 :             case ElementType::HBOX:
    1746              :             case ElementType::VBOX:
    1747              :             case ElementType::TBOX:
    1748              :             case ElementType::FBOX:
    1749            0 :                 mb = toMeasureBase(el);
    1750            0 :                 break;
    1751            0 :             default: {
    1752              :                 // find measure
    1753            0 :                 mb = toMeasure(el->findMeasure());
    1754              :                 // for start repeat, attach break to previous measure
    1755            0 :                 if (mb && el->isBarLine()) {
    1756            0 :                     BarLine* bl = toBarLine(el);
    1757            0 :                     if (bl->barLineType() == BarLineType::START_REPEAT) {
    1758            0 :                         mb = mb->prevMeasure();
    1759              :                     }
    1760              :                 }
    1761              : 
    1762            0 :                 if (mb) {
    1763            0 :                     allNoBreaks = mb->noBreak();
    1764              :                 }
    1765              :             }
    1766              :             }
    1767              :         }
    1768            0 :         if (mb) {
    1769            0 :             mbl.push_back(mb);
    1770              :         }
    1771              :     }
    1772              : 
    1773            0 :     if (type == LayoutBreakType::NOBREAK && !allNoBreaks) {
    1774            0 :         for (MeasureBase* mb : mbl) {
    1775            0 :             if (mb->systemLock()) {
    1776            0 :                 return;
    1777              :             }
    1778              :         }
    1779              :     }
    1780              : 
    1781              :     // toggle the breaks
    1782            0 :     Transaction& tx = transactionManager()->currentOrDummyTransaction();
    1783            0 :     for (MeasureBase* mb: mbl) {
    1784            0 :         bool val = false;
    1785            0 :         switch (type) {
    1786            0 :         case LayoutBreakType::LINE:
    1787            0 :             if (mb->isVBoxBase()) {
    1788            0 :                 break;
    1789              :             }
    1790            0 :             val = !mb->lineBreak();
    1791            0 :             if (val) {
    1792            0 :                 EditSystemLocks::removeSystemLocksOnAddLayoutBreak(tx, this, type, mb);
    1793            0 :                 EditPageLocks::removePageLocksOnAddLayoutBreak(tx, this, type, mb);
    1794              :             }
    1795            0 :             mb->undoSetBreak(val, type);
    1796              :             // remove page break if appropriate
    1797            0 :             if (val && mb->pageBreak()) {
    1798            0 :                 mb->undoSetBreak(false, LayoutBreakType::PAGE);
    1799              :             }
    1800            0 :             break;
    1801            0 :         case LayoutBreakType::PAGE:
    1802            0 :             val = !mb->pageBreak();
    1803            0 :             if (val) {
    1804            0 :                 EditSystemLocks::removeSystemLocksOnAddLayoutBreak(tx, this, type, mb);
    1805            0 :                 EditPageLocks::removePageLocksOnAddLayoutBreak(tx, this, type, mb);
    1806              :             }
    1807            0 :             mb->undoSetBreak(val, type);
    1808              :             // remove line break if appropriate
    1809            0 :             if (val && mb->lineBreak()) {
    1810            0 :                 mb->undoSetBreak(false, LayoutBreakType::LINE);
    1811              :             }
    1812            0 :             break;
    1813            0 :         case LayoutBreakType::SECTION:
    1814            0 :             val = !mb->sectionBreak();
    1815            0 :             if (val) {
    1816            0 :                 EditSystemLocks::removeSystemLocksOnAddLayoutBreak(tx, this, type, mb);
    1817            0 :                 EditPageLocks::removePageLocksOnAddLayoutBreak(tx, this, type, mb);
    1818              :             }
    1819            0 :             mb->undoSetBreak(val, type);
    1820            0 :             break;
    1821            0 :         case LayoutBreakType::NOBREAK:
    1822            0 :             mb->undoSetBreak(!allNoBreaks, type);
    1823              :             // remove other breaks if appropriate
    1824            0 :             if (!mb->noBreak()) {
    1825            0 :                 if (mb->pageBreak()) {
    1826            0 :                     mb->undoSetBreak(false, LayoutBreakType::PAGE);
    1827            0 :                 } else if (mb->lineBreak()) {
    1828            0 :                     mb->undoSetBreak(false, LayoutBreakType::LINE);
    1829            0 :                 } else if (mb->sectionBreak()) {
    1830            0 :                     mb->undoSetBreak(false, LayoutBreakType::SECTION);
    1831              :                 }
    1832              :             }
    1833            0 :             break;
    1834            0 :         default:
    1835            0 :             break;
    1836              :         }
    1837              :     }
    1838            0 : }
    1839              : 
    1840              : //---------------------------------------------------------
    1841              : //   cmdToggleMmrest
    1842              : //---------------------------------------------------------
    1843              : 
    1844            0 : void Score::cmdToggleMmrest()
    1845              : {
    1846            0 :     bool val = !style().styleB(Sid::createMultiMeasureRests);
    1847            0 :     deselectAll();
    1848            0 :     undoChangeStyleVal(Sid::createMultiMeasureRests, val);
    1849            0 : }
    1850              : 
    1851              : //---------------------------------------------------------
    1852              : //   cmdToggleHideEmpty
    1853              : //---------------------------------------------------------
    1854              : 
    1855            0 : void Score::cmdToggleHideEmpty()
    1856              : {
    1857            0 :     bool val = !style().styleB(Sid::hideEmptyStaves);
    1858            0 :     deselectAll();
    1859            0 :     undoChangeStyleVal(Sid::hideEmptyStaves, val);
    1860            0 : }
    1861              : 
    1862            0 : void Score::cmdSetHideStaffIfEmptyOverride(staff_idx_t staffIdx, System* system, engraving::AutoOnOff value)
    1863              : {
    1864            0 :     for (MeasureBase* mb : system->measures()) {
    1865            0 :         if (!mb->isMeasure()) {
    1866            0 :             continue;
    1867              :         }
    1868            0 :         undo(new ChangeMStaffHideIfEmpty(engraving::toMeasure(mb), staffIdx, value));
    1869              :     }
    1870            0 : }
    1871              : 
    1872              : //---------------------------------------------------------
    1873              : //   cmdToggleAutoplace
    1874              : //---------------------------------------------------------
    1875              : 
    1876            0 : void Score::cmdToggleAutoplace(bool all)
    1877              : {
    1878            0 :     if (all) {
    1879            0 :         bool val = !style().styleB(Sid::autoplaceEnabled);
    1880            0 :         undoChangeStyleVal(Sid::autoplaceEnabled, val);
    1881            0 :         setLayoutAll();
    1882              :     } else {
    1883            0 :         std::set<EngravingItem*> spanners;
    1884            0 :         for (EngravingItem* e : selection().elements()) {
    1885            0 :             if (e->isSpannerSegment()) {
    1886            0 :                 if (EngravingObject* ee = e->propertyDelegate(Pid::AUTOPLACE)) {
    1887            0 :                     e = toEngravingItem(ee);
    1888              :                 }
    1889              :                 // spanner segments may each have their own autoplace setting
    1890              :                 // but if they delegate to spanner, only toggle once
    1891            0 :                 if (e->isSpanner()) {
    1892            0 :                     if (muse::contains(spanners, e)) {
    1893            0 :                         continue;
    1894              :                     }
    1895            0 :                     spanners.insert(e);
    1896              :                 }
    1897              :             }
    1898            0 :             PropertyFlags pf = e->propertyFlags(Pid::AUTOPLACE);
    1899            0 :             if (pf == PropertyFlags::STYLED) {
    1900            0 :                 pf = PropertyFlags::UNSTYLED;
    1901              :             }
    1902            0 :             e->undoChangeProperty(Pid::AUTOPLACE, !e->getProperty(Pid::AUTOPLACE).toBool(), pf);
    1903              :         }
    1904            0 :     }
    1905            0 : }
    1906              : }
        

Generated by: LCOV version 2.0-1