LCOV - code coverage report
Current view: top level - src/notation/internal - notationselection.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 547 0
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              : #include "notationselection.h"
      23              : 
      24              : #include <QMimeData>
      25              : 
      26              : #include "engraving/dom/score.h"
      27              : #include "engraving/dom/segment.h"
      28              : #include "engraving/dom/measure.h"
      29              : #include "engraving/dom/keysig.h"
      30              : #include "engraving/dom/timesig.h"
      31              : #include "engraving/dom/box.h"
      32              : #include "engraving/dom/staff.h"
      33              : #include "engraving/dom/part.h"
      34              : #include "engraving/dom/stringdata.h"
      35              : #include "engraving/dom/stafftype.h"
      36              : 
      37              : #include "engraving/editing/edittie.h"
      38              : #include "engraving/editing/navigation.h"
      39              : 
      40              : #include "notationselectionrange.h"
      41              : #include "notationerrors.h"
      42              : 
      43              : #include "log.h"
      44              : #include "thirdparty/kors_logger/src/log_base.h"
      45              : 
      46              : using namespace muse;
      47              : using namespace mu::notation;
      48              : using namespace mu::engraving;
      49              : 
      50            0 : NotationSelection::NotationSelection(IGetScore* getScore, IInteractionForSelection* interaction)
      51            0 :     : m_getScore(getScore), m_interaction(interaction)
      52              : {
      53            0 :     m_range = std::make_shared<NotationSelectionRange>(getScore);
      54            0 : }
      55              : 
      56            0 : bool NotationSelection::isNone() const
      57              : {
      58            0 :     return score()->selection().isNone();
      59              : }
      60              : 
      61            0 : bool NotationSelection::isSingle() const
      62              : {
      63            0 :     return score()->selection().isSingle();
      64              : }
      65              : 
      66            0 : bool NotationSelection::isRange() const
      67              : {
      68            0 :     return score()->selection().isRange();
      69              : }
      70              : 
      71            0 : SelectionState NotationSelection::state() const
      72              : {
      73            0 :     return score()->selection().state();
      74              : }
      75              : 
      76            0 : Ret NotationSelection::canCopy() const
      77              : {
      78            0 :     if (isNone()) {
      79            0 :         return make_ret(Err::EmptySelection);
      80              :     }
      81              : 
      82            0 :     if (!score()->selection().canCopy()) {
      83            0 :         return make_ret(Err::SelectCompleteTupletOrTremolo);
      84              :     }
      85              : 
      86            0 :     return muse::make_ok();
      87              : }
      88              : 
      89            0 : muse::ByteArray NotationSelection::mimeData() const
      90              : {
      91            0 :     return score()->selection().mimeData();
      92              : }
      93              : 
      94            0 : QMimeData* NotationSelection::qMimeData() const
      95              : {
      96            0 :     QString mimeType = score()->selection().mimeType();
      97            0 :     if (mimeType.isEmpty()) {
      98            0 :         return nullptr;
      99              :     }
     100              : 
     101            0 :     QMimeData* mimeData = new QMimeData();
     102            0 :     mimeData->setData(mimeType, score()->selection().mimeData().toQByteArray());
     103              : 
     104            0 :     return mimeData;
     105            0 : }
     106              : 
     107            0 : EngravingItem* NotationSelection::element() const
     108              : {
     109            0 :     return score()->selection().element();
     110              : }
     111              : 
     112            0 : const std::vector<EngravingItem*>& NotationSelection::elements() const
     113              : {
     114            0 :     return score()->selection().elements();
     115              : }
     116              : 
     117            0 : std::vector<Note*> NotationSelection::notes(NoteFilter filter) const
     118              : {
     119            0 :     switch (filter) {
     120            0 :     case NoteFilter::All: return score()->selection().noteList();
     121            0 :     case NoteFilter::WithTie: return mu::engraving::EditTie::cmdTieNoteList(score()->selection(), false);
     122            0 :     case NoteFilter::WithSlur: {
     123            0 :         NOT_IMPLEMENTED;
     124            0 :         return {};
     125              :     }
     126              :     }
     127              : 
     128            0 :     return {};
     129              : }
     130              : 
     131            0 : muse::RectF NotationSelection::canvasBoundingRect() const
     132              : {
     133            0 :     if (isNone()) {
     134            0 :         return RectF();
     135              :     }
     136              : 
     137            0 :     if (const mu::engraving::EngravingItem* element = score()->selection().element()) {
     138            0 :         return element->canvasBoundingRect();
     139              :     }
     140              : 
     141            0 :     RectF result;
     142              : 
     143            0 :     for (const RectF& rect : range()->boundingArea()) {
     144            0 :         result = result.united(rect);
     145            0 :     }
     146              : 
     147            0 :     return result;
     148              : }
     149              : 
     150            0 : INotationSelectionRangePtr NotationSelection::range() const
     151              : {
     152            0 :     return m_range;
     153              : }
     154              : 
     155            0 : Score* NotationSelection::score() const
     156              : {
     157            0 :     return m_getScore->score();
     158              : }
     159              : 
     160            0 : void NotationSelection::onElementHit(EngravingItem* el)
     161              : {
     162            0 :     m_lastElementHit = el;
     163            0 : }
     164              : 
     165            0 : MeasureBase* NotationSelection::startMeasureBase() const
     166              : {
     167            0 :     return score()->selection().startMeasureBase();
     168              : }
     169              : 
     170            0 : MeasureBase* NotationSelection::endMeasureBase() const
     171              : {
     172            0 :     return score()->selection().endMeasureBase();
     173              : }
     174              : 
     175            0 : std::vector<MeasureBase*> NotationSelection::measureBasesInSelection() const
     176              : {
     177            0 :     return score()->selection().measureBasesInSelection();
     178              : }
     179              : 
     180            0 : std::vector<System*> NotationSelection::selectedSystems() const
     181              : {
     182            0 :     return score()->selection().selectedSystems();
     183              : }
     184              : 
     185            0 : std::vector<mu::engraving::Page*> NotationSelection::pagesContainingSelection() const
     186              : {
     187            0 :     return score()->selection().pagesContainingSelection();
     188              : }
     189              : 
     190            0 : EngravingItem* NotationSelection::lastElementHit() const
     191              : {
     192            0 :     return m_lastElementHit;
     193              : }
     194              : 
     195            0 : bool NotationSelection::elementsSelected(const mu::engraving::ElementTypeSet& types) const
     196              : {
     197            0 :     return score()->selection().elementsSelected(types);
     198              : }
     199              : 
     200            0 : void NotationSelection::select(SelectionTarget target)
     201              : {
     202              :     //! TODO It's better to change the implementation,
     203              :     // instead of calling different methods, it's better to do this:
     204              :     // 1. get target elements
     205              :     // 2. call select method with the elements
     206            0 :     switch (target) {
     207            0 :     case SelectionTarget::Undefined:
     208            0 :         break;
     209            0 :     case SelectionTarget::FirstItem:
     210            0 :         selectFirstElement();
     211            0 :         break;
     212            0 :     case SelectionTarget::LastItem:
     213            0 :         selectLastElement();
     214            0 :         break;
     215            0 :     case SelectionTarget::NextItem:
     216            0 :         moveSelection(MoveDirection::Right, MoveSelectionType::EngravingItem);
     217            0 :         break;
     218            0 :     case SelectionTarget::PrevItem:
     219            0 :         moveSelection(MoveDirection::Left, MoveSelectionType::EngravingItem);
     220            0 :         break;
     221            0 :     case SelectionTarget::NextSegmentItem:
     222            0 :         moveSegmentSelection(MoveDirection::Right);
     223            0 :         break;
     224            0 :     case SelectionTarget::PrevSegmentItem:
     225            0 :         moveSegmentSelection(MoveDirection::Left);
     226            0 :         break;
     227            0 :     case SelectionTarget::NextChord:
     228            0 :         moveSelection(MoveDirection::Right, MoveSelectionType::Chord);
     229            0 :         break;
     230            0 :     case SelectionTarget::PrevChord:
     231            0 :         moveSelection(MoveDirection::Left, MoveSelectionType::Chord);
     232            0 :         break;
     233            0 :     case SelectionTarget::NextMeasure:
     234            0 :         moveSelection(MoveDirection::Right, MoveSelectionType::Measure);
     235            0 :         break;
     236            0 :     case SelectionTarget::PrevMeasure:
     237            0 :         moveSelection(MoveDirection::Left, MoveSelectionType::Measure);
     238            0 :         break;
     239            0 :     case SelectionTarget::NextTrack:
     240            0 :         moveSelection(MoveDirection::Right, MoveSelectionType::Track);
     241            0 :         break;
     242            0 :     case SelectionTarget::PrevTrack:
     243            0 :         moveSelection(MoveDirection::Left, MoveSelectionType::Track);
     244            0 :         break;
     245            0 :     case SelectionTarget::NextFrame:
     246            0 :         moveSelection(MoveDirection::Right, MoveSelectionType::Frame);
     247            0 :         break;
     248            0 :     case SelectionTarget::PrevFrame:
     249            0 :         moveSelection(MoveDirection::Left, MoveSelectionType::Frame);
     250            0 :         break;
     251            0 :     case SelectionTarget::NextSystem:
     252            0 :         moveSelection(MoveDirection::Right, MoveSelectionType::System);
     253            0 :         break;
     254            0 :     case SelectionTarget::PrevSystem:
     255            0 :         moveSelection(MoveDirection::Left, MoveSelectionType::System);
     256            0 :         break;
     257            0 :     case SelectionTarget::AboveStaff:
     258            0 :         moveSelection(MoveDirection::Up, MoveSelectionType::Track);
     259            0 :         break;
     260            0 :     case SelectionTarget::BelowStaff:
     261            0 :         moveSelection(MoveDirection::Down, MoveSelectionType::Track);
     262            0 :         break;
     263            0 :     case SelectionTarget::UpNoteInChord:
     264            0 :         moveChordNoteSelection(MoveDirection::Up);
     265            0 :         break;
     266            0 :     case SelectionTarget::DownNoteInChord:
     267            0 :         moveChordNoteSelection(MoveDirection::Down);
     268            0 :         break;
     269            0 :     case SelectionTarget::TopNoteInChord:
     270            0 :         selectTopOrBottomOfChord(MoveDirection::Up);
     271            0 :         break;
     272            0 :     case SelectionTarget::BottomNoteInChord:
     273            0 :         selectTopOrBottomOfChord(MoveDirection::Down);
     274            0 :         break;
     275            0 :     case SelectionTarget::TopStaff:
     276            0 :         selectTopStaff();
     277            0 :         break;
     278            0 :     case SelectionTarget::EmptyTrailingMeasure:
     279            0 :         selectEmptyTrailingMeasure();
     280            0 :         break;
     281            0 :     case SelectionTarget::Similar:
     282            0 :         selectAllSimilarElements();
     283            0 :         break;
     284            0 :     case SelectionTarget::SimilarInStaff:
     285            0 :         selectAllSimilarElementsInStaff();
     286            0 :         break;
     287            0 :     case SelectionTarget::SimilarInRange:
     288            0 :         selectAllSimilarElementsInRange();
     289            0 :         break;
     290            0 :     case SelectionTarget::NotesInChord:
     291            0 :         selectAllNotesInChord();
     292            0 :         break;
     293            0 :     case SelectionTarget::All:
     294            0 :         selectAll();
     295            0 :         break;
     296            0 :     case SelectionTarget::Section:
     297            0 :         selectSection();
     298            0 :         break;
     299              :     }
     300            0 : }
     301              : 
     302            0 : void NotationSelection::addToSelection(SelectionTarget target)
     303              : {
     304            0 :     switch (target) {
     305            0 :     case SelectionTarget::NextChord:
     306            0 :         addToSelection(MoveDirection::Right, MoveSelectionType::Chord);
     307            0 :         break;
     308            0 :     case SelectionTarget::PrevChord:
     309            0 :         addToSelection(MoveDirection::Left, MoveSelectionType::Chord);
     310            0 :         break;
     311            0 :     case SelectionTarget::NextMeasure:
     312            0 :         addToSelection(MoveDirection::Right, MoveSelectionType::Measure);
     313            0 :         break;
     314            0 :     case SelectionTarget::PrevMeasure:
     315            0 :         addToSelection(MoveDirection::Left, MoveSelectionType::Measure);
     316            0 :         break;
     317            0 :     case SelectionTarget::AboveStaff:
     318            0 :         addToSelection(MoveDirection::Up, MoveSelectionType::Track);
     319            0 :         break;
     320            0 :     case SelectionTarget::BelowStaff:
     321            0 :         addToSelection(MoveDirection::Down, MoveSelectionType::Track);
     322            0 :         break;
     323            0 :     default:
     324            0 :         NOT_SUPPORTED << "SelectionTarget: " << (int)target;
     325            0 :         break;
     326              :     }
     327            0 : }
     328              : 
     329            0 : void NotationSelection::select(const std::vector<EngravingItem*>& elements, SelectType type, staff_idx_t staffIndex)
     330              : {
     331            0 :     TRACEFUNC;
     332              : 
     333            0 :     const mu::engraving::Selection& selection = score()->selection();
     334            0 :     const std::vector<EngravingItem*> oldSelectedElements = selection.elements();
     335            0 :     const mu::engraving::SelState oldSelectionState = selection.state();
     336              : 
     337            0 :     const Fraction oldStartTick = selection.tickStart();
     338            0 :     const Fraction oldEndTick = selection.tickEnd();
     339              : 
     340            0 :     const staff_idx_t oldStartStaff = selection.staffStart();
     341            0 :     const staff_idx_t oldEndStaff = selection.staffEnd();
     342              : 
     343            0 :     const Box* oldStartBox = selection.startBox();
     344            0 :     const Box* oldEndBox = selection.endBox();
     345              : 
     346            0 :     doSelect(elements, type, staffIndex);
     347              : 
     348            0 :     bool rangeChanged = false;
     349            0 :     if (selection.isRange()) {
     350            0 :         const bool ticksChanged = oldStartTick != selection.tickStart() || oldEndTick != selection.tickEnd();
     351            0 :         const bool stavesChanged = oldStartStaff != selection.staffStart() || oldEndStaff != selection.staffEnd();
     352            0 :         const bool boxesChanged = oldStartBox != selection.startBox() || oldEndBox != selection.endBox();
     353            0 :         rangeChanged = ticksChanged || stavesChanged || boxesChanged;
     354              :     }
     355              : 
     356            0 :     if (rangeChanged || oldSelectionState != selection.state() || oldSelectedElements != selection.elements()) {
     357            0 :         notifyAboutSelectionChangedIfNeed();
     358              :     } else {
     359            0 :         score()->setSelectionChanged(false);
     360              :     }
     361            0 : }
     362              : 
     363            0 : void NotationSelection::doSelect(const std::vector<EngravingItem*>& elements, SelectType type, staff_idx_t staffIndex)
     364              : {
     365            0 :     TRACEFUNC;
     366              : 
     367            0 :     if (elements.size() == 1 && type == SelectType::ADD && QGuiApplication::keyboardModifiers() == Qt::KeyboardModifier::ControlModifier) {
     368            0 :         if (score()->selection().isRange()) {
     369            0 :             score()->selection().setState(mu::engraving::SelState::LIST);
     370            0 :             score()->setUpdateAll();
     371              :         }
     372              : 
     373            0 :         if (elements.front()->selected()) {
     374            0 :             score()->deselect(elements.front());
     375            0 :             return;
     376              :         }
     377              :     }
     378              : 
     379            0 :     if (type == SelectType::REPLACE) {
     380            0 :         score()->deselectAll();
     381            0 :         type = SelectType::ADD;
     382              :     }
     383              : 
     384            0 :     if (type == SelectType::SINGLE && elements.size() == 1) {
     385            0 :         const mu::engraving::EngravingItem* element = elements.front();
     386            0 :         mu::engraving::Segment* segment = nullptr;
     387              : 
     388            0 :         if (element->isKeySig()) {
     389            0 :             segment = mu::engraving::toKeySig(element)->segment();
     390            0 :         } else if (element->isTimeSig()) {
     391            0 :             segment = mu::engraving::toTimeSig(element)->segment();
     392              :         }
     393              : 
     394            0 :         if (segment) {
     395            0 :             selectElementsWithSameTypeOnSegment(element->type(), segment);
     396            0 :             return;
     397              :         }
     398              :     }
     399              : 
     400            0 :     score()->select(elements, type, staffIndex);
     401            0 : }
     402              : 
     403            0 : void NotationSelection::clearSelection()
     404              : {
     405            0 :     score()->deselectAll();
     406              : 
     407            0 :     notifyAboutSelectionChangedIfNeed();
     408            0 : }
     409              : 
     410            0 : void NotationSelection::notifyAboutSelectionChangedIfNeed()
     411              : {
     412            0 :     if (!score()->selectionChanged()) {
     413            0 :         return;
     414              :     }
     415              : 
     416            0 :     TRACEFUNC;
     417              : 
     418            0 :     score()->setSelectionChanged(false);
     419              : 
     420            0 :     m_selectionChanged.notify();
     421            0 : }
     422              : 
     423            0 : muse::async::Notification NotationSelection::selectionChanged() const
     424              : {
     425            0 :     return m_selectionChanged;
     426              : }
     427              : 
     428            0 : void NotationSelection::selectElementsWithSameTypeOnSegment(mu::engraving::ElementType elementType, mu::engraving::Segment* segment)
     429              : {
     430            0 :     TRACEFUNC;
     431              : 
     432            0 :     IF_ASSERT_FAILED(segment) {
     433            0 :         return;
     434              :     }
     435              : 
     436            0 :     score()->deselectAll();
     437              : 
     438            0 :     std::vector<EngravingItem*> elementsToSelect;
     439              : 
     440            0 :     for (track_idx_t track = 0; track < score()->ntracks(); track += VOICES) {
     441            0 :         EngravingItem* element = segment->element(track);
     442            0 :         if (element && element->type() == elementType) {
     443            0 :             elementsToSelect.push_back(element);
     444              :         }
     445              :     }
     446              : 
     447            0 :     score()->select(elementsToSelect, SelectType::ADD);
     448            0 : }
     449              : 
     450            0 : void NotationSelection::selectFirstElement(bool frame)
     451              : {
     452            0 :     if (EngravingItem* element = Navigation::firstElement(score(), frame)) {
     453            0 :         select({ element }, SelectType::SINGLE, element->staffIdx());
     454            0 :         m_interaction->showItem(element);
     455              :     }
     456            0 : }
     457              : 
     458            0 : void NotationSelection::selectLastElement()
     459              : {
     460            0 :     if (EngravingItem* element = Navigation::lastElement(score())) {
     461            0 :         select({ element }, SelectType::SINGLE, element->staffIdx());
     462            0 :         m_interaction->showItem(element);
     463              :     }
     464            0 : }
     465              : 
     466            0 : void NotationSelection::moveSelection(MoveDirection d, MoveSelectionType type)
     467              : {
     468            0 :     IF_ASSERT_FAILED(MoveSelectionType::Undefined != type) {
     469            0 :         return;
     470              :     }
     471              : 
     472            0 :     if (type != MoveSelectionType::String) {
     473            0 :         IF_ASSERT_FAILED(MoveDirection::Left == d || MoveDirection::Right == d) {
     474            0 :             return;
     475              :         }
     476              :     } else {
     477            0 :         IF_ASSERT_FAILED(MoveDirection::Up == d || MoveDirection::Down == d) {
     478            0 :             return;
     479              :         }
     480              :     }
     481              : 
     482            0 :     if (MoveSelectionType::EngravingItem == type) {
     483            0 :         moveElementSelection(d);
     484            0 :         return;
     485              :     }
     486              : 
     487            0 :     if (MoveSelectionType::String == type) {
     488            0 :         moveStringSelection(d);
     489            0 :         return;
     490              :     }
     491              : 
     492              :     // TODO: rewrite, Score::move( ) logic needs to be here
     493              : 
     494            0 :     auto typeToString = [](MoveSelectionType type) {
     495            0 :         switch (type) {
     496            0 :         case MoveSelectionType::Undefined: return QString();
     497            0 :         case MoveSelectionType::EngravingItem:   return QString();
     498            0 :         case MoveSelectionType::Chord:     return QString("chord");
     499            0 :         case MoveSelectionType::Measure:   return QString("measure");
     500            0 :         case MoveSelectionType::Track:     return QString("track");
     501            0 :         case MoveSelectionType::Frame:     return QString("frame");
     502            0 :         case MoveSelectionType::System:    return QString("system");
     503            0 :         case MoveSelectionType::String:   return QString();
     504              :         }
     505            0 :         return QString();
     506              :     };
     507              : 
     508            0 :     QString cmd;
     509            0 :     if (MoveDirection::Left == d) {
     510            0 :         cmd = "prev-";
     511            0 :     } else if (MoveDirection::Right == d) {
     512            0 :         cmd = "next-";
     513              :     }
     514              : 
     515            0 :     cmd += typeToString(type);
     516              : 
     517            0 :     mu::engraving::EngravingItem* item = Navigation::move(score(), cmd);
     518            0 :     m_interaction->resetHitElementContext();
     519              : 
     520            0 :     notifyAboutSelectionChangedIfNeed();
     521            0 :     m_interaction->showItem(item);
     522            0 : }
     523              : 
     524            0 : ChordRest* NotationSelection::activeCr(const mu::engraving::Score* score) const
     525              : {
     526            0 :     ChordRest* cr = score->selection().activeCR();
     527            0 :     if (!cr) {
     528            0 :         cr = score->selection().lastChordRest();
     529            0 :         if (!cr && score->noteEntryMode()) {
     530            0 :             cr = score->inputState().cr();
     531              :         }
     532              :     }
     533            0 :     return cr;
     534              : }
     535              : 
     536            0 : void NotationSelection::addToSelection(MoveDirection d, MoveSelectionType type)
     537              : {
     538            0 :     ChordRest* cr = activeCr(score());
     539            0 :     if (!cr) {
     540            0 :         return;
     541              :     }
     542            0 :     ChordRest* el = 0;
     543            0 :     switch (type) {
     544            0 :     case MoveSelectionType::Chord: {
     545            0 :         ChordRestNavigateOptions options;
     546            0 :         options.skipGrace = true;
     547            0 :         if (d == MoveDirection::Right) {
     548            0 :             el = mu::engraving::Navigation::nextChordRest(cr, options);
     549              :         } else {
     550            0 :             el = mu::engraving::Navigation::prevChordRest(cr, options);
     551              :         }
     552            0 :         break;
     553              :     }
     554            0 :     case MoveSelectionType::Measure:
     555            0 :         if (d == MoveDirection::Right) {
     556            0 :             el = mu::engraving::Navigation::nextMeasure(score(), cr, true, true);
     557              :         } else {
     558            0 :             el = mu::engraving::Navigation::prevMeasure(score(), cr, true);
     559              :         }
     560            0 :         break;
     561            0 :     case MoveSelectionType::Track:
     562            0 :         if (d == MoveDirection::Up) {
     563            0 :             el = mu::engraving::Navigation::upStaff(cr);
     564              :         } else {
     565            0 :             el = mu::engraving::Navigation::downStaff(score(), cr);
     566              :         }
     567              :     case MoveSelectionType::EngravingItem:
     568              :     case MoveSelectionType::Frame:
     569              :     case MoveSelectionType::System:
     570              :     case MoveSelectionType::String:
     571              :     case MoveSelectionType::Undefined:
     572            0 :         break;
     573              :     }
     574              : 
     575            0 :     if (el) {
     576            0 :         select({ el }, SelectType::RANGE, el->staffIdx());
     577            0 :         m_interaction->showItem(el);
     578            0 :         m_interaction->resetHitElementContext();
     579              :     }
     580              : }
     581              : 
     582            0 : void NotationSelection::moveElementSelection(MoveDirection d)
     583              : {
     584            0 :     EngravingItem* el = score()->selection().element();
     585            0 :     if (!el && !score()->selection().elements().empty()) {
     586            0 :         el = score()->selection().elements().back();
     587              :     }
     588              : 
     589            0 :     if (m_interaction->isTextEditingStarted() && el && el->isTextBase()) {
     590            0 :         m_interaction->navigateToNearText(d);
     591            0 :         return;
     592              :     }
     593              : 
     594            0 :     const bool isLeftDirection = MoveDirection::Left == d;
     595            0 :     const bool isHorizontalLayout = score()->isLayoutMode(LayoutMode::LINE) || score()->isLayoutMode(LayoutMode::HORIZONTAL_FIXED);
     596              : 
     597              :     // VBoxes are not included in horizontal layouts - skip over them (and their contents) when moving selections...
     598            0 :     const auto nextNonVBox = [this, isLeftDirection](EngravingItem* currElem) -> EngravingItem* {
     599            0 :         IF_ASSERT_FAILED(currElem) {
     600            0 :             return nullptr;
     601              :         }
     602              : 
     603            0 :         while (const EngravingItem* vBox = currElem->findAncestor(ElementType::VBOX)) {
     604            0 :             currElem = isLeftDirection ? toVBox(vBox)->prevMM() : toVBox(vBox)->nextMM();
     605            0 :             if (currElem && currElem->isMeasure()) {
     606            0 :                 const ChordRest* cr = score()->selection().currentCR();
     607            0 :                 const staff_idx_t si = cr ? cr->staffIdx() : 0;
     608            0 :                 Measure* mb = toMeasure(currElem);
     609            0 :                 currElem = isLeftDirection ? mb->prevElementStaff(si, currElem) : mb->nextElementStaff(si, currElem);
     610              :             }
     611              : 
     612            0 :             if (!currElem) {
     613            0 :                 break;
     614              :             }
     615            0 :         }
     616              : 
     617            0 :         return currElem;
     618            0 :     };
     619              : 
     620            0 :     EngravingItem* toEl = nullptr;
     621              : 
     622            0 :     if (el) {
     623            0 :         toEl = isLeftDirection ? score()->prevElement() : score()->nextElement();
     624            0 :         if (isHorizontalLayout) {
     625            0 :             toEl = nextNonVBox(toEl);
     626              :         }
     627              :     } else {
     628              :         // Nothing currently selected (e.g. because user pressed Esc or clicked on
     629              :         // an empty region of the page). Try to restore previous selection.
     630            0 :         if (ChordRest* cr = score()->selection().currentCR()) {
     631            0 :             el = cr->isChord() ? toChord(cr)->upNote() : toEngravingItem(cr);
     632              :         }
     633            0 :         if (el) {
     634            0 :             toEl = el;     // Restoring previous selection.
     635              :         } else {
     636            0 :             toEl = isLeftDirection ? Navigation::lastElement(score()) : Navigation::firstElement(score());
     637            0 :             if (isHorizontalLayout) {
     638            0 :                 toEl = nextNonVBox(toEl);
     639              :             }
     640              :         }
     641              :     }
     642              : 
     643            0 :     if (!toEl) {
     644            0 :         return;
     645              :     }
     646              : 
     647            0 :     if (m_interaction->isEditingElement()) {
     648            0 :         m_interaction->endEditElement();
     649              :     }
     650              : 
     651            0 :     select({ toEl }, SelectType::REPLACE);
     652            0 :     m_interaction->resetHitElementContext();
     653            0 :     m_interaction->showItem(toEl);
     654              : 
     655            0 :     if (toEl->isNote() || toEl->isHarmony()) {
     656            0 :         score()->setPlayNote(true);
     657              :     }
     658              : 
     659            0 :     if (toEl->needStartEditingAfterSelecting()) {
     660            0 :         m_interaction->startEditElement(toEl);
     661              :     }
     662              : }
     663              : 
     664            0 : void NotationSelection::moveStringSelection(MoveDirection d)
     665              : {
     666            0 :     mu::engraving::InputState& is = score()->inputState();
     667            0 :     mu::engraving::Staff* staff = score()->staff(track2staff(is.track()));
     668            0 :     int instrStrgs = static_cast<int>(staff->part()->stringData(is.tick(), staff->idx())->strings());
     669            0 :     int delta = (staff->staffType(is.tick())->upsideDown() ? -1 : 1);
     670              : 
     671            0 :     if (MoveDirection::Up == d) {
     672            0 :         delta = -delta;
     673              :     }
     674              : 
     675            0 :     int strg = is.string() + delta;
     676            0 :     if (strg >= 0 && strg < instrStrgs && strg != is.string()) {
     677            0 :         is.setString(strg);
     678              : 
     679            0 :         const ChordRest* chordRest = is.cr();
     680            0 :         if (chordRest && chordRest->isChord()) {
     681            0 :             const Chord* chord = toChord(chordRest);
     682              : 
     683            0 :             for (Note* note : chord->notes()) {
     684            0 :                 if (note->string() == strg) {
     685            0 :                     select({ note }, SelectType::SINGLE);
     686              :                 }
     687              :             }
     688              :         }
     689              :     }
     690            0 : }
     691              : 
     692            0 : void NotationSelection::moveSegmentSelection(MoveDirection d)
     693              : {
     694            0 :     IF_ASSERT_FAILED(MoveDirection::Left == d || MoveDirection::Right == d) {
     695            0 :         return;
     696              :     }
     697              : 
     698            0 :     EngravingItem* e = this->element();
     699            0 :     if (!e && !this->elements().empty()) {
     700            0 :         e = d == MoveDirection::Left ? this->elements().front() : this->elements().back();
     701              :     }
     702              : 
     703            0 :     if (!e || (e = d == MoveDirection::Left ? e->prevSegmentElement() : e->nextSegmentElement()) == nullptr) {
     704            0 :         e = d == MoveDirection::Left ? Navigation::firstElement(score()) : Navigation::lastElement(score());
     705              :     }
     706              : 
     707            0 :     select({ e }, SelectType::SINGLE);
     708            0 :     m_interaction->showItem(e);
     709              : }
     710              : 
     711            0 : void NotationSelection::moveChordNoteSelection(MoveDirection d)
     712              : {
     713            0 :     IF_ASSERT_FAILED(MoveDirection::Up == d || MoveDirection::Down == d) {
     714            0 :         return;
     715              :     }
     716              : 
     717            0 :     EngravingItem* current = this->element();
     718            0 :     if (!current || !(current->isNote() || current->isRest())) {
     719            0 :         return;
     720              :     }
     721              : 
     722              :     EngravingItem* chordElem;
     723            0 :     if (d == MoveDirection::Up) {
     724            0 :         chordElem = Navigation::chordNoteAbove(score(), current);
     725              :     } else {
     726            0 :         chordElem = Navigation::chordNoteBelow(score(), current);
     727              :     }
     728              : 
     729            0 :     if (chordElem == current) {
     730            0 :         return;
     731              :     }
     732              : 
     733            0 :     select({ chordElem }, SelectType::SINGLE, chordElem->staffIdx());
     734            0 :     m_interaction->showItem(chordElem);
     735              : }
     736              : 
     737            0 : void NotationSelection::selectTopOrBottomOfChord(MoveDirection d)
     738              : {
     739            0 :     IF_ASSERT_FAILED(MoveDirection::Up == d || MoveDirection::Down == d) {
     740            0 :         return;
     741              :     }
     742              : 
     743            0 :     EngravingItem* current = this->element();
     744            0 :     if (!current || !current->isNote()) {
     745            0 :         return;
     746              :     }
     747              : 
     748              :     EngravingItem* target = d == MoveDirection::Up
     749            0 :                             ? Navigation::topNoteInChord(toNote(current)) : Navigation::bottomNoteInChord(toNote(current));
     750              : 
     751            0 :     if (target == current) {
     752            0 :         return;
     753              :     }
     754              : 
     755            0 :     select({ target }, SelectType::SINGLE);
     756            0 :     m_interaction->showItem(target);
     757              : }
     758              : 
     759            0 : void NotationSelection::selectTopStaff()
     760              : {
     761            0 :     EngravingItem* el = Navigation::topStaff(score(), activeCr(score()));
     762            0 :     if (score()->noteEntryMode()) {
     763            0 :         score()->inputState().moveInputPos(el);
     764              :     }
     765              : 
     766            0 :     if (el->isChord()) {
     767            0 :         el = mu::engraving::toChord(el)->upNote();
     768              :     }
     769              : 
     770            0 :     select({ el }, SelectType::SINGLE, 0);
     771            0 :     m_interaction->showItem(el);
     772            0 :     m_interaction->resetHitElementContext();
     773            0 : }
     774              : 
     775            0 : void NotationSelection::selectEmptyTrailingMeasure()
     776              : {
     777            0 :     ChordRest* cr = activeCr(score());
     778            0 :     const Measure* ftm = score()->firstTrailingMeasure(cr ? &cr : nullptr);
     779            0 :     if (!ftm) {
     780            0 :         ftm = score()->lastMeasure();
     781              :     }
     782            0 :     if (ftm) {
     783            0 :         if (score()->style().styleB(mu::engraving::Sid::createMultiMeasureRests) && ftm->hasMMRest()) {
     784            0 :             ftm = ftm->coveringMMRestOrThis();
     785              :         }
     786              :         EngravingItem* el
     787            0 :             = !cr ? ftm->first()->nextChordRest(0, false) : ftm->first()->nextChordRest(mu::engraving::trackZeroVoice(cr->track()), false);
     788            0 :         score()->inputState().moveInputPos(el);
     789            0 :         select({ el }, SelectType::SINGLE);
     790            0 :         m_interaction->resetHitElementContext();
     791              :     }
     792            0 : }
     793              : 
     794            0 : FilterElementsOptions NotationSelection::elementsFilterOptions(const EngravingItem* element) const
     795              : {
     796            0 :     TRACEFUNC;
     797            0 :     FilterElementsOptions options;
     798            0 :     options.elementType = element->type();
     799              : 
     800            0 :     if (element->isNote()) {
     801            0 :         const mu::engraving::Note* note = dynamic_cast<const mu::engraving::Note*>(element);
     802            0 :         if (note->chord()->isGrace()) {
     803            0 :             options.subtype = -1;
     804              :         } else {
     805            0 :             options.subtype = element->subtype();
     806              :         }
     807            0 :     } else if (element->isHairpinSegment() || element->isHarmony()) {
     808            0 :         options.subtype = element->subtype();
     809            0 :         options.bySubtype = true;
     810              :     }
     811              : 
     812            0 :     return options;
     813            0 : }
     814              : 
     815            0 : void NotationSelection::selectAllSimilarElements()
     816              : {
     817            0 :     TRACEFUNC;
     818            0 :     auto notationElements = m_interaction->elements();
     819            0 :     if (!notationElements) {
     820            0 :         return;
     821              :     }
     822              : 
     823            0 :     EngravingItem* selectedElement = this->element();
     824            0 :     if (!selectedElement) {
     825            0 :         return;
     826              :     }
     827              : 
     828            0 :     FilterElementsOptions options = elementsFilterOptions(selectedElement);
     829            0 :     std::vector<EngravingItem*> elements = notationElements->elements(options);
     830            0 :     if (elements.empty()) {
     831            0 :         return;
     832              :     }
     833              : 
     834            0 :     clearSelection();
     835              : 
     836            0 :     select(elements, SelectType::ADD);
     837            0 : }
     838              : 
     839            0 : void NotationSelection::selectAllSimilarElementsInStaff()
     840              : {
     841            0 :     TRACEFUNC;
     842            0 :     auto notationElements = m_interaction->elements();
     843            0 :     if (!notationElements) {
     844            0 :         return;
     845              :     }
     846              : 
     847            0 :     EngravingItem* selectedElement = this->element();
     848            0 :     if (!selectedElement) {
     849            0 :         return;
     850              :     }
     851              : 
     852            0 :     FilterElementsOptions options = elementsFilterOptions(selectedElement);
     853            0 :     options.staffStart = static_cast<int>(selectedElement->staffIdx());
     854            0 :     options.staffEnd = options.staffStart + 1;
     855              : 
     856            0 :     std::vector<EngravingItem*> elements = notationElements->elements(options);
     857            0 :     if (elements.empty()) {
     858            0 :         return;
     859              :     }
     860              : 
     861            0 :     clearSelection();
     862              : 
     863            0 :     select(elements, SelectType::ADD);
     864            0 : }
     865              : 
     866            0 : void NotationSelection::selectAllSimilarElementsInRange()
     867              : {
     868            0 :     auto elements = m_interaction->elements();
     869            0 :     if (!elements) {
     870            0 :         return;
     871              :     }
     872              : 
     873            0 :     mu::engraving::EngravingItem* lastHit = this->lastElementHit();
     874            0 :     if (!lastHit) {
     875            0 :         return;
     876              :     }
     877              : 
     878            0 :     mu::engraving::Score* score = elements->msScore();
     879            0 :     score->selectSimilarInRange(lastHit);
     880            0 :     notifyAboutSelectionChangedIfNeed();
     881            0 : }
     882              : 
     883            0 : void NotationSelection::selectAllNotesInChord()
     884              : {
     885            0 :     TRACEFUNC;
     886              : 
     887            0 :     const std::vector<EngravingItem*>& selectedElements = this->elements();
     888            0 :     if (selectedElements.empty()) {
     889            0 :         return;
     890              :     }
     891              : 
     892            0 :     std::set<const Chord*> chords;
     893            0 :     for (const EngravingItem* item : selectedElements) {
     894            0 :         if (item->isNote()) {
     895            0 :             chords.insert(toNote(item)->chord());
     896              :         }
     897              :     }
     898              : 
     899            0 :     if (chords.empty()) {
     900            0 :         return;
     901              :     }
     902              : 
     903            0 :     std::vector<EngravingItem*> allNotes;
     904            0 :     for (const Chord* chord : chords) {
     905            0 :         for (Note* note : chord->notes()) {
     906            0 :             allNotes.push_back(note);
     907              :         }
     908              :     }
     909              : 
     910            0 :     clearSelection();
     911            0 :     select(allNotes, SelectType::ADD);
     912            0 : }
     913              : 
     914            0 : void NotationSelection::selectAll()
     915              : {
     916            0 :     if (m_interaction->isTextEditingStarted()) {
     917            0 :         m_interaction->selectAllText();
     918              :     } else {
     919            0 :         score()->cmdSelectAll();
     920              :     }
     921              : 
     922            0 :     notifyAboutSelectionChangedIfNeed();
     923            0 : }
     924              : 
     925            0 : void NotationSelection::selectSection()
     926              : {
     927            0 :     score()->cmdSelectSection();
     928              : 
     929            0 :     notifyAboutSelectionChangedIfNeed();
     930            0 : }
        

Generated by: LCOV version 2.0-1