LCOV - code coverage report
Current view: top level - src/engraving/editing - navigation.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 11.8 % 927 109
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include "navigation.h"
      24              : 
      25              : #include "../dom/box.h"
      26              : #include "../dom/chord.h"
      27              : #include "../dom/engravingitem.h"
      28              : #include "../dom/fret.h"
      29              : #include "../dom/guitarbend.h"
      30              : #include "../dom/hammeronpulloff.h"
      31              : #include "../dom/harmony.h"
      32              : #include "../dom/lyrics.h"
      33              : #include "../dom/measure.h"
      34              : #include "../dom/measurerepeat.h"
      35              : #include "../dom/mscore.h"
      36              : #include "../dom/note.h"
      37              : #include "../dom/pagelockindicator.h"
      38              : #include "../dom/rest.h"
      39              : #include "../dom/score.h"
      40              : #include "../dom/segment.h"
      41              : #include "../dom/soundflag.h"
      42              : #include "../dom/spanner.h"
      43              : #include "../dom/staff.h"
      44              : #include "../dom/system.h"
      45              : #include "../dom/systemlockindicator.h"
      46              : #include "../dom/tapping.h"
      47              : #include "../style/style.h"
      48              : 
      49              : #include "mscoreview.h"
      50              : 
      51              : using namespace mu;
      52              : 
      53              : namespace mu::engraving {
      54              : using ElementPair = std::pair<EngravingItem*, int>; // element and its index as a child
      55              : 
      56              : //---------------------------------------------------------
      57              : //   isChild1beforeChild2
      58              : //    compares if two children are order correctly
      59              : //    from top-left to bottom-right
      60              : //---------------------------------------------------------
      61              : 
      62            0 : static bool isChild1beforeChild2(const ElementPair& child1, const ElementPair& child2)
      63              : {
      64            0 :     assert(child1.first->parent() == child2.first->parent());
      65              : 
      66            0 :     PointF p1 = child1.first->pos() + child1.first->ldata()->bbox().center();
      67            0 :     PointF p2 = child2.first->pos() + child2.first->ldata()->bbox().center();
      68              : 
      69              :     // If one child is *clearly* above the other then visit the higher one first
      70            0 :     double verticalSeparation = p2.y() - p1.y();
      71            0 :     if (verticalSeparation > 10) {
      72            0 :         return true;
      73            0 :     } else if (verticalSeparation < -10) {
      74            0 :         return false;
      75              :     }
      76              : 
      77              :     // Children are roughly aligned vertically. Visit the one on the left first
      78            0 :     double horizontalSeparation = p2.x() - p1.x();
      79            0 :     if (horizontalSeparation > 0.0) {
      80            0 :         return true;
      81            0 :     } else if (horizontalSeparation < 0.0) {
      82            0 :         return false;
      83              :     }
      84              : 
      85              :     // Children are aligned horizontally so check exact vertical position.
      86            0 :     if (verticalSeparation > 0.0) {
      87            0 :         return true;
      88            0 :     } else if (verticalSeparation < 0.0) {
      89            0 :         return false;
      90              :     }
      91              : 
      92              :     // Elements are at exact same position so fall back to using child index to determine order.
      93            0 :     if (child1.second < child2.second) {
      94            0 :         return true;
      95              :     }
      96              : 
      97            0 :     return false;
      98              : }
      99              : 
     100              : //---------------------------------------------------------
     101              : //   toChildPairsSet
     102              : //    return a set of children ordered
     103              : //    on the basis of position
     104              : //---------------------------------------------------------
     105              : 
     106            0 : static std::set<ElementPair, decltype(& isChild1beforeChild2)> toChildPairsSet(const EngravingItem* element)
     107              : {
     108            0 :     std::set<ElementPair, decltype(& isChild1beforeChild2)> children(&isChild1beforeChild2);
     109            0 :     int index = 0;
     110              : 
     111            0 :     for (EngravingObject* object : element->children()) {
     112            0 :         EngravingItem* engravingItem = toEngravingItem(object);
     113              : 
     114            0 :         if (!engravingItem) {
     115            0 :             continue;
     116              :         }
     117              : 
     118            0 :         children.insert(std::pair<EngravingItem*, int>(engravingItem, index));
     119            0 :         index++;
     120              :     }
     121              : 
     122            0 :     return children;
     123            0 : }
     124              : 
     125            0 : static EngravingItem* nextElementForSpannerSegment(const SpannerSegment* spannerSegment)
     126              : {
     127            0 :     Spanner* spanner = spannerSegment->spanner();
     128            0 :     EngravingItem* elSt = spanner->startElement();
     129            0 :     IF_ASSERT_FAILED(elSt->isNote()) {
     130            0 :         return nullptr;
     131              :     }
     132              : 
     133            0 :     Note* note = toNote(elSt);
     134            0 :     return note->nextElement();
     135              : }
     136              : 
     137            0 : static EngravingItem* prevElementForSpannerSegment(const SpannerSegment* spannerSegment)
     138              : {
     139            0 :     Spanner* spanner = spannerSegment->spanner();
     140            0 :     EngravingItem* elSt = spanner->startElement();
     141            0 :     IF_ASSERT_FAILED(elSt->isNote()) {
     142            0 :         return nullptr;
     143              :     }
     144              : 
     145            0 :     Note* note = toNote(elSt);
     146            0 :     return note->prevElement();
     147              : }
     148              : 
     149              : //---------------------------------------------------------
     150              : //   nextChordRest
     151              : //    return next Chord or Rest
     152              : //---------------------------------------------------------
     153              : 
     154          810 : ChordRest* Navigation::nextChordRest(const ChordRest* cr, const ChordRestNavigateOptions& options)
     155              : {
     156          810 :     if (!cr) {
     157            0 :         return nullptr;
     158              :     }
     159              : 
     160          810 :     if (cr->isGrace()) {
     161           13 :         const Chord* c  = toChord(cr);
     162           13 :         Chord* pc = toChord(cr->ownershipParent());
     163              : 
     164           13 :         if (options.skipGrace) {
     165            0 :             cr = toChordRest(cr->ownershipParent());
     166           13 :         } else if (cr->isGraceBefore()) {
     167            5 :             const GraceNotesGroup& group = pc->graceNotesBefore();
     168            5 :             auto i = std::find(group.begin(), group.end(), c);
     169            5 :             if (i == group.end()) {
     170            0 :                 return nullptr;           // unable to find self?
     171              :             }
     172            5 :             ++i;
     173            5 :             if (i != group.end()) {
     174            0 :                 return *i;
     175              :             }
     176              :             // if this was last grace note before, return parent
     177            5 :             return pc;
     178              :         } else {
     179            8 :             const GraceNotesGroup& group = pc->graceNotesAfter();
     180            8 :             auto i = std::find(group.begin(), group.end(), c);
     181            8 :             if (i == group.end()) {
     182            3 :                 return nullptr;           // unable to find self?
     183              :             }
     184            8 :             ++i;
     185            8 :             if (i != group.end()) {
     186            3 :                 return *i;
     187              :             }
     188              :             // if this was last grace note after, fall through to find next main note
     189            5 :             cr = pc;
     190              :         }
     191              :     } else { // cr is not a grace note
     192          797 :         if (cr->isChord() && !options.skipGrace) {
     193          468 :             const Chord* c = toChord(cr);
     194          468 :             if (!c->graceNotes().empty()) {
     195           21 :                 const GraceNotesGroup& group = c->graceNotesAfter();
     196           21 :                 if (!group.empty()) {
     197            4 :                     return group.front();
     198              :                 }
     199              :             }
     200              :         }
     201              :     }
     202              : 
     203          798 :     track_idx_t track = cr->track();
     204          798 :     SegmentType st = SegmentType::ChordRest;
     205          798 :     Segment* curSeg = cr->segment();
     206          820 :     for (Segment* seg = curSeg->next1MM(st); seg; seg = seg->next1MM(st)) {
     207          787 :         if (options.disableOverRepeats && !segmentsAreAdjacent(curSeg, seg)) {
     208            0 :             return nullptr;
     209              :         }
     210          787 :         ChordRest* e = toChordRest(seg->element(track));
     211          787 :         if (e) {
     212          766 :             if (options.skipMeasureRepeatRests && e->isRest() && e->measure()->isMeasureRepeatGroup(track2staff(track))) {
     213            1 :                 continue; // these rests are not shown, skip them
     214              :             }
     215          765 :             if (e->isChord() && !options.skipGrace) {
     216          272 :                 Chord* c = toChord(e);
     217          272 :                 if (!c->graceNotes().empty()) {
     218           16 :                     const GraceNotesGroup& group = c->graceNotesBefore();
     219           16 :                     if (!group.empty()) {
     220           12 :                         return group.front();
     221              :                     }
     222              :                 }
     223              :             }
     224          753 :             return e;
     225              :         }
     226              :     }
     227              : 
     228           33 :     return nullptr;
     229              : }
     230              : 
     231              : //---------------------------------------------------------
     232              : //   prevChordRest
     233              : //    return previous Chord or Rest
     234              : //    if grace is true, include grace notes
     235              : //---------------------------------------------------------
     236              : 
     237          869 : ChordRest* Navigation::prevChordRest(const ChordRest* cr, const ChordRestNavigateOptions& options)
     238              : {
     239          869 :     if (!cr) {
     240            0 :         return nullptr;
     241              :     }
     242              : 
     243          869 :     if (cr->isGrace()) {
     244          563 :         const Chord* c  = toChord(cr);
     245          563 :         Chord* pc = toChord(cr->ownershipParent());
     246              : 
     247          563 :         if (options.skipGrace) {
     248            0 :             cr = toChordRest(cr->ownershipParent());
     249          563 :         } else if (cr->isGraceBefore()) {
     250          563 :             const GraceNotesGroup& group = pc->graceNotesBefore();
     251          563 :             auto i = std::find(group.begin(), group.end(), c);
     252          563 :             if (i == group.end()) {
     253            0 :                 return nullptr;           // unable to find self?
     254              :             }
     255          563 :             if (i != group.begin()) {
     256            0 :                 return *--i;
     257              :             }
     258              :             // if this was first grace note before, fall through to find previous main note
     259          563 :             cr = pc;
     260              :         } else {
     261            0 :             const GraceNotesGroup& group = pc->graceNotesAfter();
     262            0 :             auto i = std::find(group.begin(), group.end(), c);
     263            0 :             if (i == group.end()) {
     264            0 :                 return nullptr;           // unable to find self?
     265              :             }
     266            0 :             if (i != group.begin()) {
     267            0 :                 return *--i;
     268              :             }
     269              :             // if this was first grace note after, return parent
     270            0 :             return pc;
     271              :         }
     272              :     } else {
     273              :         //
     274              :         // cr is not a grace note
     275          306 :         if (cr->isChord() && !options.skipGrace) {
     276          306 :             const Chord* c = toChord(cr);
     277          306 :             const GraceNotesGroup& group = c->graceNotesBefore();
     278          306 :             if (!group.empty()) {
     279            0 :                 return group.back();
     280              :             }
     281              :         }
     282              :     }
     283              : 
     284          869 :     track_idx_t track = cr->track();
     285          869 :     SegmentType st = SegmentType::ChordRest;
     286          869 :     Segment* curSeg = cr->segment();
     287          882 :     for (Segment* seg = cr->segment()->prev1MM(st); seg; seg = seg->prev1MM(st)) {
     288          679 :         if (options.disableOverRepeats && !segmentsAreAdjacent(curSeg, seg)) {
     289            0 :             return nullptr;
     290              :         }
     291              : 
     292          679 :         ChordRest* e = toChordRest(seg->element(track));
     293          679 :         if (e) {
     294          666 :             if (options.skipMeasureRepeatRests && e->isRest() && e->measure()->isMeasureRepeatGroup(track2staff(track))) {
     295            0 :                 continue; // these rests are not shown, skip them
     296              :             }
     297          666 :             if (e->isChord() && !options.skipGrace) {
     298          543 :                 const GraceNotesGroup& group = toChord(e)->graceNotesAfter();
     299          543 :                 if (!group.empty()) {
     300          117 :                     return group.back();
     301              :                 }
     302              :             }
     303          549 :             return e;
     304              :         }
     305              :     }
     306              : 
     307          203 :     return nullptr;
     308              : }
     309              : 
     310              : //---------------------------------------------------------
     311              : //   chordNoteAbove
     312              : //    element: Note, Rest, MMRest, or MeasureRepeat
     313              : //    return: Note, Rest, MMRest, or MeasureRepeat
     314              : //
     315              : //    return next higher pitched note in chord
     316              : //    move to previous track if at top of chord
     317              : //---------------------------------------------------------
     318              : 
     319            0 : EngravingItem* Navigation::chordNoteAbove(Score* score, EngravingItem* element)
     320              : {
     321            0 :     EngravingItem* re = 0;
     322            0 :     if (element->isRestFamily()) {
     323            0 :         re = score->prevTrack(toRest(element));
     324            0 :     } else if (element->isNote()) {
     325            0 :         Note* note = toNote(element);
     326            0 :         Chord* chord = note->chord();
     327            0 :         const std::vector<Note*>& notes = chord->notes();
     328            0 :         auto i = std::find(notes.begin(), notes.end(), note);
     329            0 :         ++i;
     330            0 :         if (i != notes.end()) {
     331            0 :             re = *i;
     332              :         } else {
     333            0 :             re = score->prevTrack(chord);
     334            0 :             if (re->track() == chord->track()) {
     335            0 :                 re = element;
     336              :             }
     337              :         }
     338              :     }
     339            0 :     if (re == 0) {
     340            0 :         return 0;
     341              :     }
     342            0 :     if (re->isChord()) {
     343            0 :         re = toChord(re)->notes().front();
     344              :     }
     345            0 :     return re;
     346              : }
     347              : 
     348              : //---------------------------------------------------------
     349              : //   topNoteInChord
     350              : //---------------------------------------------------------
     351              : 
     352            0 : Note* Navigation::topNoteInChord(Note* note)
     353              : {
     354            0 :     return note->chord()->upNote();
     355              : }
     356              : 
     357              : //---------------------------------------------------------
     358              : //   chordNoteBelow
     359              : //    return next lower pitched note in chord
     360              : //    move to next track if at bottom of chord
     361              : //---------------------------------------------------------
     362              : 
     363            0 : EngravingItem* Navigation::chordNoteBelow(Score* score, EngravingItem* element)
     364              : {
     365            0 :     EngravingItem* re = 0;
     366            0 :     if (element->isRestFamily()) {
     367            0 :         re = score->nextTrack(toRest(element));
     368            0 :     } else if (element->isNote()) {
     369            0 :         Note* note   = toNote(element);
     370            0 :         Chord* chord = note->chord();
     371            0 :         const std::vector<Note*>& notes = chord->notes();
     372            0 :         auto i = std::find(notes.begin(), notes.end(), note);
     373            0 :         if (i != notes.begin()) {
     374            0 :             --i;
     375            0 :             re = *i;
     376              :         } else {
     377            0 :             re = score->nextTrack(chord);
     378            0 :             if (re->track() == chord->track()) {
     379            0 :                 re = element;
     380              :             }
     381              :         }
     382              :     }
     383            0 :     if (re == 0) {
     384            0 :         return 0;
     385              :     }
     386            0 :     if (re->isChord()) {
     387            0 :         re = toChord(re)->notes().back();
     388              :     }
     389            0 :     return re;
     390              : }
     391              : 
     392              : //---------------------------------------------------------
     393              : //   bottomNoteInChord
     394              : //---------------------------------------------------------
     395              : 
     396            0 : Note* Navigation::bottomNoteInChord(Note* note)
     397              : {
     398            0 :     return note->chord()->downNote();
     399              : }
     400              : 
     401              : //---------------------------------------------------------
     402              : //   firstElement
     403              : //---------------------------------------------------------
     404              : 
     405            0 : EngravingItem* Navigation::firstElement(Score* score, bool frame)
     406              : {
     407            0 :     if (frame) {
     408            0 :         MeasureBase* mb = score->measures()->first();
     409            0 :         if (mb && mb->isBox()) {
     410            0 :             return mb;
     411              :         }
     412              :     }
     413            0 :     Segment* s = score->firstSegmentMM(SegmentType::All);
     414            0 :     return s ? s->element(0) : nullptr;
     415              : }
     416              : 
     417              : //---------------------------------------------------------
     418              : //   lastElement
     419              : //---------------------------------------------------------
     420              : 
     421            4 : EngravingItem* Navigation::lastElement(Score* score, bool frame)
     422              : {
     423            4 :     if (frame) {
     424            4 :         MeasureBase* mb = score->measures()->last();
     425            4 :         if (mb && mb->isBox()) {
     426            0 :             auto boxChildren = toChildPairsSet(mb);
     427            0 :             if (!boxChildren.empty()) {
     428            0 :                 return boxChildren.rbegin()->first;
     429              :             }
     430            0 :             return mb;
     431            0 :         }
     432              :     }
     433            4 :     EngravingItem* re = 0;
     434            4 :     Segment* seg = score->lastSegmentMM();
     435            4 :     if (!seg) {
     436            0 :         return nullptr;
     437              :     }
     438              :     while (true) {
     439           20 :         for (size_t i = (score->staves().size() - 1) * VOICES; i < score->staves().size() * VOICES; i++) {
     440           16 :             if (seg->element(i)) {
     441            4 :                 re = seg->element(i);
     442              :             }
     443              :         }
     444            4 :         if (re) {
     445            4 :             if (re->isChord()) {
     446            0 :                 return toChord(re)->notes().front();
     447              :             }
     448            4 :             return re;
     449              :         }
     450            0 :         seg = seg->prev1MM(SegmentType::All);
     451            0 :     }
     452              : }
     453              : 
     454              : //---------------------------------------------------------
     455              : //   upStaff
     456              : //---------------------------------------------------------
     457              : 
     458            0 : ChordRest* Navigation::upStaff(ChordRest* cr)
     459              : {
     460            0 :     Segment* segment = cr->segment();
     461              : 
     462            0 :     if (cr->staffIdx() == 0) {
     463            0 :         return cr;
     464              :     }
     465              : 
     466            0 :     for (int track = (static_cast<int>(cr->staffIdx()) - 1) * VOICES; track >= 0; --track) {
     467            0 :         EngravingItem* el = segment->element(track);
     468            0 :         if (!el) {
     469            0 :             continue;
     470              :         }
     471            0 :         if (el->isNote()) {
     472            0 :             el = toNote(el)->chord();
     473              :         }
     474            0 :         if (el->isChordRest()) {
     475            0 :             return toChordRest(el);
     476              :         }
     477              :     }
     478            0 :     return 0;
     479              : }
     480              : 
     481              : //---------------------------------------------------------
     482              : //   downStaff
     483              : //---------------------------------------------------------
     484              : 
     485            0 : ChordRest* Navigation::downStaff(Score* score, ChordRest* cr)
     486              : {
     487            0 :     Segment* segment = cr->segment();
     488            0 :     track_idx_t tracks = score->nstaves() * VOICES;
     489              : 
     490            0 :     if (cr->staffIdx() == score->nstaves() - 1) {
     491            0 :         return cr;
     492              :     }
     493              : 
     494            0 :     for (track_idx_t track = (cr->staffIdx() + 1) * VOICES; track < tracks; --track) {
     495            0 :         EngravingItem* el = segment->element(track);
     496            0 :         if (!el) {
     497            0 :             continue;
     498              :         }
     499            0 :         if (el->isNote()) {
     500            0 :             el = toNote(el)->chord();
     501              :         }
     502            0 :         if (el->isChordRest()) {
     503            0 :             return toChordRest(el);
     504              :         }
     505              :     }
     506            0 :     return 0;
     507              : }
     508              : 
     509              : //---------------------------------------------------------
     510              : //   topStaff
     511              : //---------------------------------------------------------
     512              : 
     513            0 : ChordRest* Navigation::topStaff(Score* score, ChordRest* cr)
     514              : {
     515              :     // Go to top-most staff of current or first measure depending upon active selection
     516            0 :     const auto* destinationMeasure = cr ? cr->measure() : score->firstMeasure();
     517            0 :     if (destinationMeasure) {
     518              :         // Accommodate for MMRest
     519            0 :         if (score->style().styleB(Sid::createMultiMeasureRests) && destinationMeasure->hasMMRest()) {
     520            0 :             destinationMeasure = destinationMeasure->coveringMMRestOrThis();
     521              :         }
     522              :         // Get first ChordRest of top staff
     523            0 :         cr = destinationMeasure->first()->nextChordRest(0, false);
     524              :     }
     525            0 :     return cr;
     526              : }
     527              : 
     528              : //---------------------------------------------------------
     529              : //   nextTrack
     530              : //    returns note at or just before current (cr) position
     531              : //    in next track for this measure
     532              : //    that contains such an element
     533              : //---------------------------------------------------------
     534              : 
     535            0 : ChordRest* Score::nextTrack(ChordRest* cr, bool skipMeasureRepeatRests)
     536              : {
     537            0 :     if (!cr) {
     538            0 :         return 0;
     539              :     }
     540              : 
     541            0 :     ChordRest* el = nullptr;
     542            0 :     Measure* measure = cr->measure();
     543            0 :     track_idx_t track = cr->track();
     544            0 :     size_t tracks = nstaves() * VOICES;
     545              : 
     546            0 :     while (!el) {
     547              :         // find next non-empty track
     548            0 :         while (++track < tracks) {
     549            0 :             if (measure->hasVoice(track)) {
     550            0 :                 break;
     551              :             }
     552              :         }
     553              :         // no more tracks, return original element
     554            0 :         if (track == tracks) {
     555            0 :             return cr;
     556              :         }
     557              :         // treat MeasureRepeat as if it starts in the first measure of its group even if internally it's farther along
     558            0 :         if (cr->isMeasureRepeat()) {
     559            0 :             measure = measure->firstOfMeasureRepeatGroup(cr->staffIdx());
     560            0 :             return toChordRest(measure->first(SegmentType::ChordRest)->element(track));
     561              :         }
     562              :         // find element at same or previous segment within this track
     563            0 :         for (Segment* segment = cr->segment(); segment; segment = segment->prev(SegmentType::ChordRest)) {
     564            0 :             el = toChordRest(segment->element(track));
     565            0 :             if (el) {
     566            0 :                 break;
     567              :             }
     568              :         }
     569              :     }
     570            0 :     if (skipMeasureRepeatRests && el->isRest() && measure->isMeasureRepeatGroup(track2staff(track))) {
     571            0 :         el = measure->measureRepeatElement(track2staff(track));
     572              :     }
     573            0 :     return el;
     574              : }
     575              : 
     576              : //---------------------------------------------------------
     577              : //   prevTrack
     578              : //    returns ChordRest at or just before current (cr) position
     579              : //    in previous track for this measure
     580              : //    that contains such an element
     581              : //---------------------------------------------------------
     582              : 
     583            0 : ChordRest* Score::prevTrack(ChordRest* cr, bool skipMeasureRepeatRests)
     584              : {
     585            0 :     if (!cr) {
     586            0 :         return 0;
     587              :     }
     588              : 
     589            0 :     ChordRest* el = nullptr;
     590            0 :     Measure* measure = cr->measure();
     591            0 :     int track = static_cast<int>(cr->track());
     592              : 
     593            0 :     while (!el) {
     594              :         // find next non-empty track
     595            0 :         while (--track >= 0) {
     596            0 :             if (measure->hasVoice(track)) {
     597            0 :                 break;
     598              :             }
     599              :         }
     600              :         // no more tracks, return original element
     601            0 :         if (track < 0) {
     602            0 :             return cr;
     603              :         }
     604              :         // treat MeasureRepeat as if it starts in the first measure of its group even if internally it's farther along
     605            0 :         if (cr->isMeasureRepeat()) {
     606            0 :             measure = measure->firstOfMeasureRepeatGroup(cr->staffIdx());
     607            0 :             return toChordRest(measure->first(SegmentType::ChordRest)->element(track));
     608              :         }
     609              :         // find element at same or previous segment within this track
     610            0 :         for (Segment* segment = cr->segment(); segment; segment = segment->prev(SegmentType::ChordRest)) {
     611            0 :             el = toChordRest(segment->element(track));
     612            0 :             if (el) {
     613            0 :                 break;
     614              :             }
     615              :         }
     616              :     }
     617            0 :     if (skipMeasureRepeatRests && el->isRest() && measure->isMeasureRepeatGroup(track2staff(track))) {
     618            0 :         el = measure->measureRepeatElement(track2staff(track));
     619              :     }
     620            0 :     return el;
     621              : }
     622              : 
     623              : //---------------------------------------------------------
     624              : //   nextMeasure
     625              : //---------------------------------------------------------
     626              : 
     627            0 : ChordRest* Navigation::nextMeasure(Score* score, ChordRest* element, bool selectBehavior, bool mmRest)
     628              : {
     629            0 :     if (!element) {
     630            0 :         return nullptr;
     631              :     }
     632              : 
     633            0 :     Measure* measure = nullptr;
     634            0 :     if (mmRest) {
     635            0 :         measure = element->measure()->nextMeasureMM();
     636              :     } else {
     637            0 :         measure = element->measure()->nextMeasure();
     638              :     }
     639              : 
     640            0 :     Fraction endTick = element->measure()->last()->nextChordRest(element->track(), true)->tick();
     641            0 :     bool last = false;
     642              : 
     643            0 :     if (score->selection().isRange()) {
     644            0 :         if (element->tick() != endTick && score->selection().tickEnd() <= endTick) {
     645            0 :             measure = element->measure();
     646            0 :             last = true;
     647            0 :         } else if (element->tick() == endTick && score->selection().isEndActive()) {
     648            0 :             last = true;
     649              :         }
     650            0 :     } else if (element->tick() != endTick && selectBehavior) {
     651            0 :         measure = element->measure();
     652            0 :         last = true;
     653              :     }
     654            0 :     if (!measure) {
     655            0 :         measure = element->measure();
     656            0 :         last = true;
     657              :     }
     658            0 :     staff_idx_t staff = element->staffIdx();
     659              : 
     660            0 :     Segment* startSeg = last ? measure->last() : measure->first();
     661            0 :     for (Segment* seg = startSeg; seg; seg = last ? seg->prev() : seg->next()) {
     662            0 :         track_idx_t etrack = (staff + 1) * VOICES;
     663            0 :         for (track_idx_t track = staff * VOICES; track < etrack; ++track) {
     664            0 :             EngravingItem* pel = seg->element(track);
     665              : 
     666            0 :             if (pel && pel->isChordRest()) {
     667            0 :                 return toChordRest(pel);
     668              :             }
     669              :         }
     670              :     }
     671            0 :     return nullptr;
     672              : }
     673              : 
     674              : //---------------------------------------------------------
     675              : //   prevMeasure
     676              : //---------------------------------------------------------
     677              : 
     678            0 : ChordRest* Navigation::prevMeasure(Score* score, ChordRest* element, bool mmRest)
     679              : {
     680            0 :     if (!element) {
     681            0 :         return nullptr;
     682              :     }
     683              : 
     684            0 :     Measure* measure = nullptr;
     685            0 :     if (mmRest) {
     686            0 :         measure = element->measure()->prevMeasureMM();
     687              :     } else {
     688            0 :         measure = element->measure()->prevMeasure();
     689              :     }
     690              : 
     691            0 :     Fraction startTick = element->measure()->first()->nextChordRest(element->track())->tick();
     692            0 :     bool last = false;
     693              : 
     694            0 :     if (score->selection().isRange() && score->selection().isEndActive() && score->selection().startSegment()->tick() <= startTick) {
     695            0 :         last = true;
     696            0 :     } else if (element->tick() != startTick) {
     697            0 :         measure = element->measure();
     698              :     }
     699            0 :     if (!measure) {
     700            0 :         measure = element->measure();
     701            0 :         last = false;
     702              :     }
     703              : 
     704            0 :     staff_idx_t staff = element->staffIdx();
     705              : 
     706            0 :     Segment* startSeg = last ? measure->last() : measure->first();
     707            0 :     for (Segment* seg = startSeg; seg; seg = last ? seg->prev() : seg->next()) {
     708            0 :         track_idx_t etrack = (staff + 1) * VOICES;
     709            0 :         for (track_idx_t track = staff * VOICES; track < etrack; ++track) {
     710            0 :             EngravingItem* pel = seg->element(track);
     711              : 
     712            0 :             if (pel && pel->isChordRest()) {
     713            0 :                 return toChordRest(pel);
     714              :             }
     715              :         }
     716              :     }
     717            0 :     return nullptr;
     718              : }
     719              : 
     720              : //---------------------------------------------------------
     721              : //   nextElement
     722              : //---------------------------------------------------------
     723              : 
     724            0 : EngravingItem* Score::nextElement()
     725              : {
     726            0 :     EngravingItem* e = getSelectedElement();
     727            0 :     if (!e) {
     728            0 :         return nullptr;
     729              :     }
     730            0 :     staff_idx_t staffId = e->staffIdx();
     731            0 :     while (e) {
     732            0 :         switch (e->type()) {
     733            0 :         case ElementType::NOTE:
     734              :         case ElementType::REST:
     735              :         case ElementType::MMREST:
     736              :         case ElementType::CHORD:
     737              :         case ElementType::TUPLET: {
     738            0 :             EngravingItem* next = e->nextElement();
     739            0 :             if (next) {
     740            0 :                 return next;
     741              :             } else {
     742            0 :                 break;
     743              :             }
     744              :         }
     745            0 :         case ElementType::SEGMENT: {
     746            0 :             Segment* s = toSegment(e);
     747            0 :             EngravingItem* next = s->nextElement(staffId);
     748            0 :             if (next) {
     749            0 :                 return next;
     750              :             } else {
     751            0 :                 break;
     752              :             }
     753              :         }
     754            0 :         case ElementType::MEASURE: {
     755            0 :             Measure* m = toMeasure(e);
     756            0 :             EngravingItem* next = m->nextElementStaff(staffId);
     757            0 :             if (next) {
     758            0 :                 return next;
     759              :             } else {
     760            0 :                 break;
     761              :             }
     762              :         }
     763            0 :         case ElementType::CLEF:
     764              :         case ElementType::KEYSIG:
     765              :         case ElementType::TIMESIG:
     766              :         case ElementType::BAR_LINE: {
     767            0 :             for (; e && !e->isSegment(); e = e->layoutParent()) {
     768              :             }
     769            0 :             Segment* s = toSegment(e);
     770            0 :             EngravingItem* next = s->nextElement(staffId);
     771            0 :             if (next) {
     772            0 :                 return next;
     773              :             } else {
     774            0 :                 return Navigation::firstElement(score());
     775              :             }
     776              :         }
     777            0 :         case ElementType::TAPPING:
     778              :         {
     779            0 :             TappingHalfSlur* halfSlurAbove = toTapping(e)->halfSlurAbove();
     780            0 :             EngravingItem* selEl = getSelectedElement();
     781            0 :             if (halfSlurAbove && !(selEl && selEl->isTappingHalfSlurSegment())) {
     782            0 :                 return halfSlurAbove->frontSegment();
     783              :             }
     784            0 :             break;
     785              :         }
     786            0 :         case ElementType::HAMMER_ON_PULL_OFF_TEXT:
     787            0 :             return toHammerOnPullOffText(e)->endChord()->upNote();
     788            0 :         case ElementType::HAMMER_ON_PULL_OFF_SEGMENT:
     789              :         {
     790            0 :             HammerOnPullOffSegment* hopoSeg = toHammerOnPullOffSegment(e);
     791            0 :             if (!hopoSeg->hopoText().empty()) {
     792            0 :                 return hopoSeg->hopoText().front();
     793              :             }
     794              :             [[fallthrough]];
     795              :         }
     796              :         case ElementType::TAPPING_HALF_SLUR_SEGMENT:
     797              :         {
     798            0 :             TappingHalfSlur* halfSlur = toTappingHalfSlurSegment(e)->tappingHalfSlur();
     799            0 :             Tapping* tapping = halfSlur->tapping();
     800            0 :             if (halfSlur->isHalfSlurAbove() && tapping->halfSlurBelow()) {
     801            0 :                 return tapping->halfSlurBelow()->frontSegment();
     802              :             }
     803              :             [[fallthrough]];
     804              :         }
     805              :         case ElementType::VOLTA_SEGMENT:
     806              :         case ElementType::SLUR_SEGMENT:
     807              :         case ElementType::TEXTLINE_SEGMENT:
     808              :         case ElementType::HAIRPIN_SEGMENT:
     809              :         case ElementType::OTTAVA_SEGMENT:
     810              :         case ElementType::TRILL_SEGMENT:
     811              :         case ElementType::VIBRATO_SEGMENT:
     812              :         case ElementType::LET_RING_SEGMENT:
     813              :         case ElementType::GRADUAL_TEMPO_CHANGE_SEGMENT:
     814              :         case ElementType::PALM_MUTE_SEGMENT:
     815              :         case ElementType::WHAMMY_BAR_SEGMENT:
     816              :         case ElementType::RASGUEADO_SEGMENT:
     817              :         case ElementType::HARMONIC_MARK_SEGMENT:
     818              :         case ElementType::PICK_SCRAPE_SEGMENT:
     819              :         case ElementType::PEDAL_SEGMENT: {
     820            0 :             SpannerSegment* s = toSpannerSegment(e);
     821            0 :             Spanner* sp = s->spanner();
     822            0 :             Spanner* nextSp = sp->nextSpanner(sp, staffId);
     823            0 :             if (nextSp) {
     824            0 :                 return nextSp->spannerSegments().front();
     825              :             }
     826              : 
     827            0 :             Segment* seg = tick2segment(sp->tick());
     828            0 :             if (seg) {
     829            0 :                 Segment* nextSegment = seg->next1();
     830            0 :                 while (nextSegment) {
     831            0 :                     if (nextSegment->isTimeTickType()) {
     832            0 :                         if (EngravingItem* annotation = nextSegment->firstAnnotation(staffId)) {
     833            0 :                             return annotation;
     834              :                         }
     835            0 :                         if (Spanner* spanner = nextSegment->firstSpanner(staffId)) {
     836            0 :                             return spanner->spannerSegments().front();
     837              :                         }
     838            0 :                     } else if (EngravingItem* nextEl = nextSegment->firstElementOfSegment(staffId)) {
     839            0 :                         return nextEl;
     840              :                     }
     841            0 :                     nextSegment = nextSegment->next1MM();
     842              :                 }
     843              :             }
     844            0 :             break;
     845              :         }
     846            0 :         case ElementType::GUITAR_BEND_SEGMENT:
     847              :         case ElementType::GUITAR_BEND_HOLD_SEGMENT: {
     848              :             GuitarBend* bend
     849            0 :                 = e->isGuitarBendSegment() ? toGuitarBendSegment(e)->guitarBend() : toGuitarBendHoldSegment(e)->guitarBendHold()->
     850            0 :                   guitarBend();
     851            0 :             if (bend->bendType() != GuitarBendType::SLIGHT_BEND) {
     852            0 :                 return bend->endNote();
     853              :             } else {
     854            0 :                 EngravingItem* next = nextElementForSpannerSegment(toSpannerSegment(e));
     855            0 :                 if (next) {
     856            0 :                     return next;
     857              :                 }
     858              :             }
     859              : 
     860            0 :             break;
     861              :         }
     862            0 :         case ElementType::GLISSANDO_SEGMENT:
     863              :         case ElementType::NOTELINE_SEGMENT:
     864              :         case ElementType::LAISSEZ_VIB_SEGMENT:
     865              :         case ElementType::PARTIAL_TIE_SEGMENT:
     866              :         case ElementType::TIE_SEGMENT: {
     867            0 :             EngravingItem* next = nextElementForSpannerSegment(toSpannerSegment(e));
     868            0 :             if (next) {
     869            0 :                 return next;
     870              :             } else {
     871            0 :                 break;
     872              :             }
     873              :         }
     874            0 :         case ElementType::VBOX:
     875              :         case ElementType::HBOX:
     876              :         case ElementType::TBOX:
     877              :         case ElementType::FBOX: {
     878            0 :             auto boxChildren = toChildPairsSet(e);
     879              : 
     880            0 :             EngravingItem* selectedElement = getSelectedElement();
     881              : 
     882            0 :             if ((selectedElement->isVBox()
     883            0 :                  || selectedElement->isHBox()
     884            0 :                  || selectedElement->isTBox()) && !boxChildren.empty()) {
     885            0 :                 return boxChildren.begin()->first;
     886              :             }
     887              : 
     888            0 :             if (selectedElement->isFBox()) {
     889            0 :                 for (EngravingItem* child : toFBox(selectedElement)->el()) {
     890            0 :                     if (child->isFretDiagram() && child->visible()) {
     891            0 :                         return toFretDiagram(child)->harmony();
     892              :                     }
     893              :                 }
     894              :             }
     895              : 
     896            0 :             for (auto child = boxChildren.begin(); child != boxChildren.end(); child++) {
     897            0 :                 if (selectedElement != child->first) {
     898            0 :                     continue;
     899              :                 }
     900              : 
     901            0 :                 auto targetElement = std::next(child);
     902              : 
     903            0 :                 if (targetElement != boxChildren.end()) {
     904            0 :                     return targetElement->first;
     905              :                 }
     906              :             }
     907              : 
     908            0 :             MeasureBase* mb = toMeasureBase(e)->nextMM();
     909            0 :             if (!mb) {
     910            0 :                 break;
     911            0 :             } else if (mb->isMeasure()) {
     912            0 :                 ChordRest* cr = selection().currentCR();
     913            0 :                 staff_idx_t si = cr ? cr->staffIdx() : 0;
     914            0 :                 return toMeasure(mb)->nextElementStaff(si);
     915              :             } else {
     916            0 :                 return mb;
     917              :             }
     918            0 :         }
     919            0 :         case ElementType::LAYOUT_BREAK: {
     920            0 :             staffId = 0;             // otherwise it will equal -1, which breaks the navigation
     921            0 :             break;
     922              :         }
     923              :         // TODO: case ElementType::STAFF_VISIBILITY_INDICATOR:
     924            0 :         case ElementType::SYSTEM_LOCK_INDICATOR:
     925              :         {
     926            0 :             staffId = 0;
     927            0 :             SystemLockIndicator* sli = toSystemLockIndicator(e);
     928            0 :             System* system = sli->system();
     929            0 :             if (system && system->pageLockIndicator()) {
     930            0 :                 return system->pageLockIndicator();
     931              :             } else {
     932            0 :                 e = toSystemLockIndicator(e)->systemLock()->endMB();
     933              :             }
     934            0 :             continue;
     935            0 :         }
     936            0 :         case ElementType::PAGE_LOCK_INDICATOR:
     937              :         {
     938            0 :             staffId = 0;
     939            0 :             e = toPageLockIndicator(e)->pageLock()->endMB();
     940            0 :             continue;
     941              :         }
     942            0 :         case ElementType::SOUND_FLAG:
     943            0 :             if (EngravingItem* parent = toSoundFlag(e)->ownershipParentItem()) {
     944            0 :                 return parent;
     945              :             }
     946            0 :             break;
     947            0 :         case ElementType::HARMONY: {
     948            0 :             if (EngravingItem* parent = toHarmony(e)->ownershipParentItem()) {
     949            0 :                 if (parent->isFretDiagram()) {
     950            0 :                     return parent;
     951              :                 }
     952              :             }
     953            0 :             break;
     954              :         }
     955            0 :         case ElementType::FRET_DIAGRAM: {
     956            0 :             FretDiagram* fretDiagram = toFretDiagram(e);
     957            0 :             if (fretDiagram->isInFretBox()) {
     958            0 :                 const ElementList& diagrams = toFBox(fretDiagram->ownershipParent())->el();
     959              : 
     960            0 :                 size_t index = muse::indexOf(diagrams, fretDiagram);
     961            0 :                 if (index != muse::nidx) {
     962            0 :                     while (++index < diagrams.size()) {
     963            0 :                         FretDiagram* fretDiagramI = toFretDiagram(diagrams[index]);
     964            0 :                         if (fretDiagramI->visible()) {
     965            0 :                             return fretDiagramI->harmony();
     966              :                         }
     967              :                     }
     968              :                 }
     969              :             }
     970            0 :             break;
     971              :         }
     972            0 :         default:
     973            0 :             break;
     974            0 :         }
     975            0 :         e = e->layoutParent();
     976              :     }
     977            0 :     return Navigation::lastElement(score());
     978              : }
     979              : 
     980              : //---------------------------------------------------------
     981              : //   prevElement
     982              : //---------------------------------------------------------
     983              : 
     984            0 : EngravingItem* Score::prevElement()
     985              : {
     986            0 :     EngravingItem* e = getSelectedElement();
     987            0 :     if (!e) {
     988            0 :         return nullptr;
     989              :     }
     990            0 :     staff_idx_t staffId = e->staffIdx();
     991            0 :     while (e) {
     992            0 :         switch (e->type()) {
     993            0 :         case ElementType::NOTE:
     994              :         case ElementType::REST:
     995              :         case ElementType::MMREST:
     996              :         case ElementType::CHORD:
     997              :         case ElementType::TUPLET: {
     998            0 :             EngravingItem* prev = e->prevElement();
     999            0 :             if (prev) {
    1000            0 :                 return prev;
    1001              :             } else {
    1002            0 :                 break;
    1003              :             }
    1004              :         }
    1005            0 :         case ElementType::SEGMENT: {
    1006            0 :             Segment* s = toSegment(e);
    1007            0 :             EngravingItem* prev = s->prevElement(staffId);
    1008            0 :             if (prev) {
    1009            0 :                 return prev;
    1010              :             } else {
    1011            0 :                 break;
    1012              :             }
    1013              :         }
    1014            0 :         case ElementType::MEASURE: {
    1015            0 :             Measure* m = toMeasure(e);
    1016            0 :             return m->prevElementStaff(staffId);
    1017              :         }
    1018            0 :         case ElementType::CLEF:
    1019              :         case ElementType::KEYSIG:
    1020              :         case ElementType::TIMESIG:
    1021              :         case ElementType::BAR_LINE: {
    1022            0 :             for (; e && !e->isSegment(); e = e->layoutParent()) {
    1023              :             }
    1024            0 :             EngravingItem* previousElement = toSegment(e)->prevElement(staffId);
    1025              : 
    1026            0 :             if (!previousElement->isBox()) {
    1027            0 :                 return previousElement;
    1028              :             }
    1029              : 
    1030            0 :             if (previousElement->isFretDiagram()) {
    1031            0 :                 return previousElement;
    1032              :             }
    1033              : 
    1034            0 :             auto boxChildren = toChildPairsSet(previousElement);
    1035              : 
    1036            0 :             if (boxChildren.size() > 0) {
    1037            0 :                 return boxChildren.rbegin()->first;
    1038              :             }
    1039              : 
    1040            0 :             return previousElement;
    1041            0 :         }
    1042            0 :         case ElementType::HAMMER_ON_PULL_OFF_TEXT:
    1043              :         {
    1044            0 :             HammerOnPullOffText* hopoText = toHammerOnPullOffText(e);
    1045            0 :             HammerOnPullOffSegment* hopoSegment = toHammerOnPullOffSegment(hopoText->parent());
    1046            0 :             DO_ASSERT(hopoSegment);
    1047            0 :             if (hopoSegment->hopoText().size() > 0 && hopoSegment->hopoText().front() == hopoText) {
    1048            0 :                 return hopoSegment;
    1049              :             } else {
    1050            0 :                 return hopoText->startChord()->downNote();
    1051              :             }
    1052              :         }
    1053            0 :         case ElementType::TAPPING_HALF_SLUR_SEGMENT:
    1054              :         {
    1055            0 :             TappingHalfSlur* halfSlur = toTappingHalfSlurSegment(e)->tappingHalfSlur();
    1056            0 :             Tapping* tapping = halfSlur->tapping();
    1057            0 :             if (!halfSlur->isHalfSlurAbove()) {
    1058            0 :                 IF_ASSERT_FAILED(tapping->halfSlurAbove()) {
    1059            0 :                     return tapping;
    1060              :                 }
    1061            0 :                 return tapping->halfSlurAbove()->frontSegment();
    1062              :             } else {
    1063            0 :                 return tapping;
    1064              :             }
    1065              :         }
    1066            0 :         case ElementType::VOLTA_SEGMENT:
    1067              :         case ElementType::SLUR_SEGMENT:
    1068              :         case ElementType::HAMMER_ON_PULL_OFF_SEGMENT:
    1069              :         case ElementType::TEXTLINE_SEGMENT:
    1070              :         case ElementType::HAIRPIN_SEGMENT:
    1071              :         case ElementType::OTTAVA_SEGMENT:
    1072              :         case ElementType::TRILL_SEGMENT:
    1073              :         case ElementType::VIBRATO_SEGMENT:
    1074              :         case ElementType::PEDAL_SEGMENT: {
    1075            0 :             SpannerSegment* s = toSpannerSegment(e);
    1076            0 :             Spanner* sp = s->spanner();
    1077            0 :             EngravingItem* stEl = sp->startElement();
    1078            0 :             Spanner* prevSp = sp->prevSpanner(sp, staffId);
    1079            0 :             if (prevSp) {
    1080            0 :                 return prevSp->spannerSegments().front();
    1081              :             } else {
    1082            0 :                 Segment* startSeg = sp->startSegment();
    1083            0 :                 if (EngravingItem* annotation = startSeg->lastAnnotation(staffId)) {
    1084            0 :                     return annotation;
    1085              :                 }
    1086            0 :                 if (startSeg->isTimeTickType()) {
    1087            0 :                     startSeg = startSeg->prev1MMenabled();
    1088            0 :                     for (; startSeg && startSeg->isTimeTickType(); startSeg = startSeg->prev1MMenabled()) {
    1089            0 :                         if (Spanner* spanner = startSeg->lastSpanner(staffId)) {
    1090            0 :                             return spanner->spannerSegments().front();
    1091              :                         }
    1092            0 :                         if (EngravingItem* annotation = startSeg->lastAnnotation(staffId)) {
    1093            0 :                             return annotation;
    1094              :                         }
    1095              :                     }
    1096            0 :                     if (!startSeg) {
    1097            0 :                         break;
    1098              :                     }
    1099              :                     // Also check first non-timeTick segment encountered.
    1100            0 :                     if (Spanner* spanner = startSeg->lastSpanner(staffId)) {
    1101            0 :                         return spanner->spannerSegments().front();
    1102              :                     }
    1103            0 :                     if (EngravingItem* annotation = startSeg->lastAnnotation(staffId)) {
    1104            0 :                         return annotation;
    1105              :                     }
    1106              :                 }
    1107            0 :                 EngravingItem* el = startSeg->lastElementOfSegment(staffId);
    1108            0 :                 if (stEl->isChordRest() || stEl->isNote()) {
    1109            0 :                     ChordRest* cr = startSeg->cr(stEl->track());
    1110            0 :                     if (cr) {
    1111            0 :                         EngravingItem* elCr = cr->lastElementBeforeSegment();
    1112            0 :                         if (elCr) {
    1113            0 :                             return elCr;
    1114              :                         }
    1115              :                     }
    1116              :                 }
    1117            0 :                 if (el->isChord()) {
    1118            0 :                     return toChord(el)->lastElementBeforeSegment();
    1119            0 :                 } else if (el->isNote()) {
    1120            0 :                     Chord* c = toNote(el)->chord();
    1121            0 :                     return c->lastElementBeforeSegment();
    1122              :                 } else {
    1123            0 :                     return el;
    1124              :                 }
    1125              :             }
    1126              :         }
    1127            0 :         case ElementType::GUITAR_BEND_SEGMENT:
    1128              :         case ElementType::GUITAR_BEND_HOLD_SEGMENT: {
    1129            0 :             GuitarBend* bend = e->isGuitarBendSegment() ? toGuitarBendSegment(e)->guitarBend()
    1130            0 :                                : toGuitarBendHoldSegment(e)->guitarBendHold()->guitarBend();
    1131            0 :             return bend->startNote();
    1132              :         }
    1133            0 :         case ElementType::GLISSANDO_SEGMENT:
    1134              :         case ElementType::NOTELINE_SEGMENT:
    1135              :         case ElementType::LAISSEZ_VIB_SEGMENT:
    1136              :         case ElementType::PARTIAL_TIE_SEGMENT:
    1137              :         case ElementType::TIE_SEGMENT: {
    1138            0 :             EngravingItem* prev = prevElementForSpannerSegment(toSpannerSegment(e));
    1139            0 :             if (prev) {
    1140            0 :                 return prev;
    1141              :             }
    1142              : 
    1143            0 :             break;
    1144              :         }
    1145            0 :         case ElementType::VBOX:
    1146              :         case ElementType::HBOX:
    1147              :         case ElementType::TBOX:
    1148              :         case ElementType::FBOX: {
    1149            0 :             auto boxChildren = toChildPairsSet(e);
    1150              : 
    1151            0 :             EngravingItem* selectedElement = getSelectedElement();
    1152              : 
    1153            0 :             for (auto child = boxChildren.rbegin(); child != boxChildren.rend(); child++) {
    1154            0 :                 if (selectedElement != child->first) {
    1155            0 :                     continue;
    1156              :                 }
    1157              : 
    1158            0 :                 auto targetElement = std::next(child);
    1159              : 
    1160            0 :                 if (targetElement != boxChildren.rend()) {
    1161            0 :                     return targetElement->first;
    1162              :                 }
    1163              :             }
    1164              : 
    1165            0 :             MeasureBase* mb = toMeasureBase(e)->prevMM();
    1166            0 :             if (!mb) {
    1167            0 :                 break;
    1168            0 :             } else if (mb->isMeasure()) {
    1169            0 :                 ChordRest* cr = selection().currentCR();
    1170            0 :                 staff_idx_t si = cr ? cr->staffIdx() : 0;
    1171            0 :                 Segment* s = toMeasure(mb)->last();
    1172            0 :                 if (s) {
    1173            0 :                     return s->lastElementForNavigation(si);
    1174              :                 }
    1175              :             } else {
    1176            0 :                 return mb;
    1177              :             }
    1178            0 :         }
    1179            0 :         break;
    1180            0 :         case ElementType::LAYOUT_BREAK: {
    1181            0 :             staffId = 0;             // otherwise it will equal -1, which breaks the navigation
    1182            0 :             break;
    1183              :         }
    1184              :         // TODO: case ElementType::STAFF_VISIBILITY_INDICATOR:
    1185            0 :         case ElementType::SYSTEM_LOCK_INDICATOR:
    1186              :         {
    1187            0 :             staffId = 0;
    1188            0 :             e = toSystemLockIndicator(e)->systemLock()->endMB();
    1189            0 :             continue;
    1190              :         }
    1191            0 :         case ElementType::PAGE_LOCK_INDICATOR:
    1192              :         {
    1193            0 :             staffId = 0;
    1194            0 :             PageLockIndicator* pli = toPageLockIndicator(e);
    1195            0 :             System* system = pli->system();
    1196            0 :             if (system && !system->systemLockIndicators().empty()) {
    1197            0 :                 return system->systemLockIndicators().front();
    1198              :             } else {
    1199            0 :                 e = toPageLockIndicator(e)->pageLock()->endMB();
    1200              :             }
    1201            0 :             continue;
    1202            0 :         }
    1203            0 :         case ElementType::HARMONY: {
    1204            0 :             Harmony* harmony = toHarmony(e);
    1205            0 :             if (harmony->isInFretBox()) {
    1206            0 :                 FretDiagram* fretDiagram = toFretDiagram(harmony->ownershipParent());
    1207            0 :                 FBox* fretBox = toFBox(fretDiagram->ownershipParent());
    1208            0 :                 const ElementList& diagrams = fretBox->el();
    1209              : 
    1210            0 :                 size_t index = muse::indexOf(diagrams, fretDiagram);
    1211            0 :                 while (--index != muse::nidx) {
    1212            0 :                     FretDiagram* fretDiagramI = toFretDiagram(diagrams[index]);
    1213            0 :                     if (fretDiagramI->visible()) {
    1214            0 :                         return fretDiagramI;
    1215              :                     }
    1216              :                 }
    1217              : 
    1218            0 :                 return fretBox;
    1219            0 :             } else if (harmony->ownershipParent()->isFretDiagram()) {
    1220            0 :                 EngravingItem* prev = harmony->getParentSeg()->prevAnnotation(toFretDiagram(harmony->ownershipParent()));
    1221            0 :                 if (prev) {
    1222            0 :                     return prev;
    1223              :                 }
    1224              : 
    1225            0 :                 e = toFretDiagram(harmony->ownershipParent());
    1226              :             }
    1227            0 :             break;
    1228              :         }
    1229            0 :         case ElementType::FRET_DIAGRAM: {
    1230            0 :             FretDiagram* fretDiagram = toFretDiagram(e);
    1231            0 :             EngravingItem* harmony = fretDiagram->harmony();
    1232            0 :             if (harmony) {
    1233            0 :                 return harmony;
    1234              :             }
    1235            0 :             break;
    1236              :         }
    1237            0 :         default:
    1238            0 :             break;
    1239            0 :         }
    1240            0 :         e = e->layoutParent();
    1241              :     }
    1242            0 :     return Navigation::firstElement(score());
    1243              : }
    1244              : 
    1245              : //---------------------------------------------------------
    1246              : //   prevLyrics
    1247              : //    - find the lyric (if any) before this one (not including lines)
    1248              : //    - currently used to determine the first lyric of a melisma
    1249              : //---------------------------------------------------------
    1250              : 
    1251            9 : Lyrics* Navigation::lastLyricsInMeasure(const Segment* seg, const staff_idx_t staffIdx, const int no, const PlacementV& placement)
    1252              : {
    1253           15 :     while (seg) {
    1254            9 :         const track_idx_t strack = staffIdx * VOICES;
    1255            9 :         const track_idx_t etrack = strack + VOICES;
    1256           34 :         for (track_idx_t track = strack; track < etrack; ++track) {
    1257           28 :             EngravingItem* el = seg->element(track);
    1258           28 :             Lyrics* prevLyrics = el && el->isChord() ? toChordRest(el)->lyrics(no, placement) : nullptr;
    1259           28 :             if (prevLyrics) {
    1260            3 :                 return prevLyrics;
    1261              :             }
    1262              :         }
    1263            6 :         seg = seg->prev1(mu::engraving::SegmentType::ChordRest);
    1264              :     }
    1265            6 :     return nullptr;
    1266              : }
    1267              : 
    1268           48 : Lyrics* Navigation::prevLyrics(const Lyrics* lyrics)
    1269              : {
    1270           48 :     Segment* seg = lyrics->ownershipParent() ? lyrics->segment() : nullptr;
    1271           48 :     if (!seg) {
    1272            0 :         return nullptr;
    1273              :     }
    1274          101 :     while ((seg = seg->prev1(mu::engraving::SegmentType::ChordRest))) {
    1275           53 :         const track_idx_t strack = lyrics->staffIdx() * VOICES;
    1276           53 :         const track_idx_t etrack = strack + VOICES;
    1277          265 :         for (track_idx_t track = strack; track < etrack; ++track) {
    1278          212 :             EngravingItem* el = seg->element(track);
    1279          212 :             Lyrics* prevLyrics = el && el->isChord() ? toChordRest(el)->lyrics(lyrics->verse(), lyrics->placement()) : nullptr;
    1280          212 :             if (prevLyrics) {
    1281            0 :                 return prevLyrics;
    1282              :             }
    1283              :         }
    1284              :     }
    1285           48 :     return nullptr;
    1286              : }
    1287              : 
    1288            0 : Lyrics* Navigation::nextLyrics(const Lyrics* lyrics)
    1289              : {
    1290            0 :     Segment* seg = lyrics->ownershipParent() ? lyrics->segment() : nullptr;
    1291            0 :     if (!seg) {
    1292            0 :         return nullptr;
    1293              :     }
    1294            0 :     Segment* nextSegment = seg;
    1295            0 :     while ((nextSegment = nextSegment->next1(mu::engraving::SegmentType::ChordRest))) {
    1296            0 :         const track_idx_t strack = lyrics->staffIdx() * VOICES;
    1297            0 :         const track_idx_t etrack = strack + VOICES;
    1298            0 :         for (track_idx_t track = strack; track < etrack; ++track) {
    1299            0 :             EngravingItem* el = nextSegment->element(track);
    1300            0 :             Lyrics* nextLyrics = el && el->isChord() ? toChordRest(el)->lyrics(lyrics->verse(), lyrics->placement()) : nullptr;
    1301            0 :             if (nextLyrics) {
    1302            0 :                 return nextLyrics;
    1303              :             }
    1304              :         }
    1305              :     }
    1306            0 :     return nullptr;
    1307              : }
    1308              : 
    1309              : //---------------------------------------------------------
    1310              : //   move
    1311              : //    move current selection
    1312              : //---------------------------------------------------------
    1313              : 
    1314            0 : EngravingItem* Navigation::move(Score* score, const String& cmd)
    1315              : {
    1316            0 :     ChordRest* cr { nullptr };
    1317            0 :     Box* box { nullptr };
    1318            0 :     if (score->noteEntryMode()) {
    1319              :         // if selection exists and is grace note, use it
    1320              :         // otherwise use chord/rest at input position
    1321              :         // also use it if we are moving to next chord
    1322              :         // to catch up with the cursor and not move the selection by 2 positions
    1323            0 :         cr = score->selection().cr();
    1324            0 :         if (cr && (cr->isGrace() || cmd == u"next-chord" || cmd == u"prev-chord")) {
    1325              :         } else {
    1326            0 :             cr = score->inputState().cr();
    1327              :         }
    1328            0 :     } else if (score->selection().activeCR()) {
    1329            0 :         cr = score->selection().activeCR();
    1330              :     } else {
    1331            0 :         cr = score->selection().lastChordRest();
    1332              :     }
    1333              : 
    1334              :     // no chord/rest found? look for another type of element,
    1335              :     // but commands [empty-trailing-measure] and [top-staff] don't
    1336              :     // necessarily need an active selection for appropriate functioning
    1337            0 :     if (!cr && cmd != u"empty-trailing-measure" && cmd != u"top-staff") {
    1338            0 :         if (score->selection().elements().empty()) {
    1339            0 :             return 0;
    1340              :         }
    1341              :         // retrieve last element of section list
    1342            0 :         EngravingItem* el = score->selection().elements().back();
    1343            0 :         EngravingItem* trg = nullptr;
    1344              : 
    1345              :         // get parent of element and process accordingly:
    1346              :         // trg is the element to select on "next-chord" cmd
    1347              :         // cr is the ChordRest to move from on other cmd's
    1348            0 :         track_idx_t track = el->track();                // keep note of element track
    1349            0 :         if (!el->isBox()) {
    1350            0 :             el = el->ownershipParentItem();
    1351              :         }
    1352              :         // element with no parent (eg, a newly-added line) - no way to find context
    1353            0 :         if (!el) {
    1354            0 :             return 0;
    1355              :         }
    1356            0 :         switch (el->type()) {
    1357            0 :         case ElementType::NOTE:                     // a note is a valid target
    1358            0 :             trg = el;
    1359            0 :             cr  = toNote(el)->chord();
    1360            0 :             break;
    1361            0 :         case ElementType::CHORD:                    // a chord or a rest are valid targets
    1362              :         case ElementType::REST:
    1363              :         case ElementType::MMREST:
    1364            0 :             trg = el;
    1365            0 :             cr  = toChordRest(trg);
    1366            0 :             break;
    1367            0 :         case ElementType::SEGMENT: {                // from segment go to top chordrest in segment
    1368            0 :             Segment* seg  = toSegment(el);
    1369              :             // if segment is not chord/rest or grace, move to next chord/rest or grace segment
    1370            0 :             if (!seg->isChordRest()) {
    1371            0 :                 seg = seg->next1(SegmentType::ChordRest);
    1372            0 :                 if (!seg) {                 // if none found, return failure
    1373            0 :                     return 0;
    1374              :                 }
    1375              :             }
    1376              :             // segment for sure contains chords/rests,
    1377            0 :             size_t size = seg->elist().size();
    1378              :             // if segment has a chord/rest in original element track, use it
    1379            0 :             if (track < size && seg->element(track)) {
    1380            0 :                 trg  = seg->element(track);
    1381            0 :                 cr = toChordRest(trg);
    1382            0 :                 break;
    1383              :             }
    1384              :             // if not, get topmost chord/rest
    1385            0 :             for (size_t i = 0; i < size; i++) {
    1386            0 :                 if (seg->element(i)) {
    1387            0 :                     trg  = seg->element(i);
    1388            0 :                     cr = toChordRest(trg);
    1389            0 :                     break;
    1390              :                 }
    1391              :             }
    1392            0 :             break;
    1393              :         }
    1394            0 :         case ElementType::HBOX:           // fallthrough
    1395              :         case ElementType::VBOX:           // fallthrough
    1396              :         case ElementType::TBOX:
    1397              :         case ElementType::FBOX:
    1398            0 :             box = toBox(el);
    1399            0 :             break;
    1400            0 :         default:                                // on anything else, return failure
    1401            0 :             return 0;
    1402              :         }
    1403              : 
    1404              :         // if something found and command is forward, the element found is the destination
    1405            0 :         if (trg && cmd == u"next-chord") {
    1406              :             // if chord, go to topmost note
    1407            0 :             if (trg->isChord()) {
    1408            0 :                 trg = toChord(trg)->upNote();
    1409              :             }
    1410            0 :             score->setPlayNote(true);
    1411            0 :             score->select(trg, SelectType::SINGLE, 0);
    1412            0 :             return trg;
    1413              :         }
    1414              :         // if no chordrest and no box (frame) found, do nothing
    1415            0 :         if (!cr && !box) {
    1416            0 :             return 0;
    1417              :         }
    1418              :         // if some chordrest found, continue with default processing
    1419              :     }
    1420              : 
    1421            0 :     EngravingItem* el = nullptr;
    1422            0 :     Segment* ois = score->noteEntryMode() ? score->inputState().segment() : nullptr;
    1423            0 :     Measure* oim = ois ? ois->measure() : nullptr;
    1424              : 
    1425            0 :     if (cmd == u"next-chord" && cr) {
    1426              :         // note input cursor
    1427            0 :         if (score->noteEntryMode()) {
    1428            0 :             score->inputState().moveToNextInputPos();
    1429              :         }
    1430              : 
    1431              :         // selection "cursor"
    1432              :         // find next chordrest, which might be a grace note
    1433              :         // this may override note input cursor
    1434            0 :         el = Navigation::nextChordRest(cr);
    1435              : 
    1436              :         // Skip gap rests if we're not in note entry mode...
    1437            0 :         while (!score->noteEntryMode() && el && el->isRest() && toRest(el)->isGap()) {
    1438            0 :             el = Navigation::nextChordRest(toChordRest(el));
    1439              :         }
    1440            0 :         if (el && score->noteEntryMode()) {
    1441              :             // do not use if not in original or new measure (don't skip measures)
    1442            0 :             Measure* m = toChordRest(el)->measure();
    1443            0 :             Segment* nis = score->inputState().segment();
    1444            0 :             Measure* nim = nis ? nis->measure() : nullptr;
    1445            0 :             if (m != oim && m != nim) {
    1446            0 :                 el = cr;
    1447              :             }
    1448              :             // do not use if new input segment is current cr
    1449              :             // this means input cursor just caught up to current selection
    1450            0 :             else if (cr && nis == cr->segment()) {
    1451            0 :                 el = cr;
    1452              :             }
    1453            0 :         } else if (!el) {
    1454            0 :             if (score->noteEntryMode()) {
    1455            0 :                 score->inputState().setBeyondScore(true);
    1456              :             }
    1457            0 :             el = cr;
    1458              :         }
    1459            0 :     } else if (cmd == u"prev-chord" && cr) {
    1460              :         // note input cursor
    1461            0 :         if (score->noteEntryMode()) {
    1462            0 :             if (score->inputState().beyondScore()) {
    1463            0 :                 score->inputState().setBeyondScore(false);
    1464            0 :             } else if (score->inputState().segment()) {
    1465            0 :                 Measure* m = score->inputState().segment()->measure();
    1466            0 :                 Segment* s = score->inputState().segment()->prev1(SegmentType::ChordRest);
    1467            0 :                 track_idx_t track = score->inputState().track();
    1468            0 :                 for (; s; s = s->prev1(SegmentType::ChordRest)) {
    1469            0 :                     if (s->element(track) || (s->measure() != m && s->rtick().isZero())) {
    1470            0 :                         if (s->element(track)) {
    1471            0 :                             if (s->element(track)->isRest() && toRest(s->element(track))->isGap()) {
    1472            0 :                                 continue;
    1473              :                             }
    1474              :                         }
    1475            0 :                         break;
    1476              :                     }
    1477              :                 }
    1478            0 :                 score->inputState().moveInputPos(s);
    1479              :             }
    1480              :         }
    1481              : 
    1482              :         // selection "cursor"
    1483              :         // find previous chordrest, which might be a grace note
    1484              :         // this may override note input cursor
    1485            0 :         el = Navigation::prevChordRest(cr);
    1486              : 
    1487              :         // Skip gap rests if we're not in note entry mode...
    1488            0 :         while (!score->noteEntryMode() && el && el->isRest() && toRest(el)->isGap()) {
    1489            0 :             el = Navigation::prevChordRest(toChordRest(el));
    1490              :         }
    1491            0 :         if (el && score->noteEntryMode()) {
    1492              :             // do not use if not in original or new measure (don't skip measures)
    1493            0 :             Measure* m = toChordRest(el)->measure();
    1494            0 :             Segment* nis = score->inputState().segment();
    1495            0 :             Measure* nim = nis ? nis->measure() : nullptr;
    1496            0 :             if (m != oim && m != nim) {
    1497            0 :                 el = cr;
    1498              :             }
    1499              :             // do not use if new input segment is current cr
    1500              :             // this means input cursor just caught up to current selection
    1501            0 :             else if (cr && nis == cr->segment()) {
    1502            0 :                 el = cr;
    1503              :             }
    1504            0 :         } else if (!el) {
    1505            0 :             el = cr;
    1506              :         }
    1507            0 :     } else if (cmd == u"next-measure") {
    1508            0 :         if (box && box->nextMeasure() && box->nextMeasure()->first()) {
    1509            0 :             el = box->nextMeasure()->first()->nextChordRest(0, false);
    1510              :         }
    1511            0 :         if (cr) {
    1512            0 :             if (score->noteEntryMode() && cr->measure() == score->lastMeasure()) {
    1513            0 :                 score->inputState().setBeyondScore(true);
    1514            0 :                 el = score->lastMeasure()->lastChordRest(cr->track());
    1515              :             } else {
    1516            0 :                 el = Navigation::nextMeasure(score, cr);
    1517              :             }
    1518              :         }
    1519            0 :         if (el && score->noteEntryMode()) {
    1520            0 :             score->inputState().moveInputPos(el);
    1521              :         }
    1522            0 :     } else if (cmd == u"prev-measure") {
    1523            0 :         if (box && box->prevMeasure() && box->prevMeasure()->first()) {
    1524            0 :             el = box->prevMeasure()->first()->nextChordRest(0, false);
    1525              :         }
    1526            0 :         if (score->noteEntryMode() && score->inputState().beyondScore()) {
    1527            0 :             score->inputState().setBeyondScore(false);
    1528            0 :             el = score->lastMeasure()->first()->nextChordRest(score->inputState().track(), false);
    1529            0 :         } else if (cr) {
    1530            0 :             el = Navigation::prevMeasure(score, cr);
    1531              :         }
    1532            0 :         if (el && score->noteEntryMode()) {
    1533            0 :             score->inputState().moveInputPos(el);
    1534              :         }
    1535            0 :     } else if (cmd == u"next-system" && cr) {
    1536            0 :         if (score->noteEntryMode() && cr->measure()->system()->endTick() == score->endTick()) {
    1537            0 :             score->inputState().setBeyondScore(true);
    1538            0 :             el = score->lastMeasure()->lastChordRest(cr->track());
    1539              :         } else {
    1540            0 :             el = score->cmdNextPrevSystem(cr, true);
    1541              :         }
    1542            0 :         if (el && score->noteEntryMode()) {
    1543            0 :             score->inputState().moveInputPos(el);
    1544              :         }
    1545            0 :     } else if (cmd == u"prev-system" && cr) {
    1546            0 :         if (score->noteEntryMode() && score->inputState().beyondScore()) {
    1547            0 :             score->inputState().setBeyondScore(false);
    1548              :         }
    1549            0 :         el = score->cmdNextPrevSystem(cr, false);
    1550            0 :         if (score->noteEntryMode()) {
    1551            0 :             score->inputState().moveInputPos(el);
    1552              :         }
    1553            0 :     } else if (cmd == u"next-frame") {
    1554            0 :         auto measureBase = cr ? cr->measure()->findMeasureBase() : box->findMeasureBase();
    1555            0 :         el = measureBase ? score->cmdNextPrevFrame(measureBase, true) : nullptr;
    1556            0 :     } else if (cmd == u"prev-frame") {
    1557            0 :         auto measureBase = cr ? cr->measure()->findMeasureBase() : box->findMeasureBase();
    1558            0 :         el = measureBase ? score->cmdNextPrevFrame(measureBase, false) : nullptr;
    1559            0 :     } else if (cmd == u"next-section") {
    1560            0 :         if (!(el = box)) {
    1561            0 :             el = cr;
    1562              :         }
    1563            0 :         el = score->cmdNextPrevSection(el, true);
    1564            0 :     } else if (cmd == u"prev-section") {
    1565            0 :         if (!(el = box)) {
    1566            0 :             el = cr;
    1567              :         }
    1568            0 :         el = score->cmdNextPrevSection(el, false);
    1569            0 :     } else if (cmd == u"next-track" && cr) {
    1570            0 :         el = score->nextTrack(cr);
    1571            0 :         if (score->noteEntryMode()) {
    1572            0 :             score->inputState().moveInputPos(el);
    1573              :         }
    1574            0 :     } else if (cmd == u"prev-track" && cr) {
    1575            0 :         el = score->prevTrack(cr);
    1576            0 :         if (score->noteEntryMode()) {
    1577            0 :             score->inputState().moveInputPos(el);
    1578              :         }
    1579            0 :     } else if (cmd == u"top-staff") {
    1580            0 :         el = cr ? Navigation::topStaff(score, cr) : Navigation::topStaff(score);
    1581            0 :         if (score->noteEntryMode()) {
    1582            0 :             score->inputState().moveInputPos(el);
    1583              :         }
    1584            0 :     } else if (cmd == u"empty-trailing-measure") {
    1585            0 :         const Measure* ftm = nullptr;
    1586            0 :         if (!cr) {
    1587            0 :             ftm = score->firstTrailingMeasure() ? score->firstTrailingMeasure() : score->lastMeasure();
    1588              :         } else {
    1589            0 :             ftm = score->firstTrailingMeasure(&cr) ? score->firstTrailingMeasure(&cr) : score->lastMeasure();
    1590              :         }
    1591            0 :         if (ftm) {
    1592            0 :             if (score->style().styleB(Sid::createMultiMeasureRests) && ftm->hasMMRest()) {
    1593            0 :                 ftm = ftm->coveringMMRestOrThis();
    1594              :             }
    1595            0 :             el = !cr ? ftm->first()->nextChordRest(0, false) : ftm->first()->nextChordRest(trackZeroVoice(cr->track()), false);
    1596              :         }
    1597              :         // Note: Due to the nature of this command as being preparatory for input,
    1598              :         // Note-Entry is activated from within ScoreView::cmd()
    1599            0 :         score->inputState().moveInputPos(el);
    1600              :     }
    1601              : 
    1602            0 :     if (el) {
    1603            0 :         if (el->isChord()) {
    1604            0 :             el = toChord(el)->upNote();             // originally downNote
    1605              :         }
    1606            0 :         score->setPlayNote(true);
    1607            0 :         if (score->noteEntryMode()) {
    1608            0 :             if (score->inputState().beyondScore()) {
    1609              :                 // don't select el when cursor is beyond score
    1610            0 :                 score->deselectAll();
    1611            0 :             } else if (score->inputState().cr() || !el->selected()) {
    1612              :                 // if cursor moved into a gap, selection cannot follow
    1613              :                 // only select & play el if it was not already selected (does not normally happen)
    1614            0 :                 score->select(el, SelectType::SINGLE, 0);
    1615              :             } else {
    1616            0 :                 score->setPlayNote(false);
    1617              :             }
    1618            0 :             for (MuseScoreView* view : score->getViewer()) {
    1619            0 :                 view->moveCursor();
    1620              :             }
    1621              :         } else {
    1622            0 :             score->select(el, SelectType::SINGLE, 0);
    1623              :         }
    1624              :     }
    1625            0 :     return el;
    1626              : }
    1627              : 
    1628              : //---------------------------------------------------------
    1629              : //   selectMove
    1630              : //---------------------------------------------------------
    1631              : 
    1632            0 : EngravingItem* Navigation::selectMove(Score* score, const String& cmd)
    1633              : {
    1634              :     ChordRest* cr;
    1635            0 :     if (score->selection().activeCR()) {
    1636            0 :         cr = score->selection().activeCR();
    1637              :     } else {
    1638            0 :         cr = score->selection().lastChordRest();
    1639              :     }
    1640            0 :     if (!cr && score->noteEntryMode()) {
    1641            0 :         cr = score->inputState().cr();
    1642              :     }
    1643            0 :     if (!cr) {
    1644            0 :         return 0;
    1645              :     }
    1646              : 
    1647            0 :     ChordRest* el = nullptr;
    1648            0 :     ChordRestNavigateOptions options;
    1649            0 :     options.skipGrace = true;
    1650            0 :     options.skipMeasureRepeatRests = false;
    1651            0 :     if (cmd == u"select-next-chord") {
    1652            0 :         el = Navigation::nextChordRest(cr, options);
    1653            0 :     } else if (cmd == u"select-prev-chord") {
    1654            0 :         el = Navigation::prevChordRest(cr, options);
    1655            0 :     } else if (cmd == u"select-next-measure") {
    1656            0 :         el = Navigation::nextMeasure(score, cr, true, true);
    1657            0 :     } else if (cmd == u"select-prev-measure") {
    1658            0 :         el = Navigation::prevMeasure(score, cr, true);
    1659            0 :     } else if (cmd == u"select-begin-line") {
    1660            0 :         Measure* measure = cr->segment()->measure()->system()->firstMeasure();
    1661            0 :         if (!measure) {
    1662            0 :             return 0;
    1663              :         }
    1664            0 :         el = measure->first()->nextChordRest(cr->track());
    1665            0 :     } else if (cmd == u"select-end-line") {
    1666            0 :         Measure* measure = cr->segment()->measure()->system()->lastMeasure();
    1667            0 :         if (!measure) {
    1668            0 :             return 0;
    1669              :         }
    1670            0 :         el = measure->last()->nextChordRest(cr->track(), true);
    1671            0 :     } else if (cmd == u"select-begin-score") {
    1672            0 :         Measure* measure = score->firstMeasureMM();
    1673            0 :         if (!measure) {
    1674            0 :             return 0;
    1675              :         }
    1676            0 :         el = measure->first()->nextChordRest(cr->track());
    1677            0 :     } else if (cmd == u"select-end-score") {
    1678            0 :         Measure* measure = score->lastMeasureMM();
    1679            0 :         if (!measure) {
    1680            0 :             return 0;
    1681              :         }
    1682            0 :         el = measure->last()->nextChordRest(cr->track(), true);
    1683            0 :     } else if (cmd == u"select-staff-above") {
    1684            0 :         el = Navigation::upStaff(cr);
    1685            0 :     } else if (cmd == u"select-staff-below") {
    1686            0 :         el = Navigation::downStaff(score, cr);
    1687              :     }
    1688            0 :     if (el) {
    1689            0 :         score->select(el, SelectType::RANGE, el->staffIdx());
    1690              :     }
    1691            0 :     return el;
    1692              : }
    1693              : }
        

Generated by: LCOV version 2.0-1