LCOV - code coverage report
Current view: top level - src/notationscene/internal - notationactioncontroller.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 1926 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              : 
      23              : #include "notationactioncontroller.h"
      24              : 
      25              : #include "global/io/file.h"
      26              : #include "global/translation.h"
      27              : #include "global/types/ret.h"
      28              : 
      29              : #include "engraving/dom/harmony.h"
      30              : #include "engraving/dom/masterscore.h"
      31              : #include "engraving/dom/note.h"
      32              : #include "engraving/dom/chord.h"
      33              : #include "engraving/dom/text.h"
      34              : #include "engraving/dom/sig.h"
      35              : #include "engraving/editing/noteinput.h"
      36              : 
      37              : #include "notation/imasternotation.h"
      38              : #include "notation/inotation.h"
      39              : #include "notation/inotationautomation.h" // IWYU pragma: keep
      40              : #include "notation/inotationelements.h"
      41              : #include "notation/inotationmidiinput.h"
      42              : #include "notation/inotationnoteinput.h"
      43              : #include "notation/inotationplayback.h" // IWYU pragma: keep
      44              : #include "notation/inotationselection.h"
      45              : #include "notation/inotationstyle.h"
      46              : #include "notation/inotationundostack.h"
      47              : #include "notation/inotationinteraction.h"
      48              : 
      49              : #include "project/inotationproject.h"
      50              : 
      51              : #include "qml/MuseScore/NotationScene/abstractelementpopupmodel.h"
      52              : #include "qml/MuseScore/NotationScene/notationviewinputcontroller.h"
      53              : 
      54              : #include "../notationcommands.h"
      55              : #include "../notationcommandsenums.h"
      56              : #include "playback/playbackcommands.h"
      57              : 
      58              : #include "log.h"
      59              : 
      60              : using namespace mu;
      61              : using namespace muse;
      62              : using namespace muse::io;
      63              : using namespace muse::rcommand;
      64              : using namespace mu::notation;
      65              : using namespace mu::engraving;
      66              : 
      67              : static constexpr qreal STRETCH_STEP = 0.1;
      68              : static constexpr bool NEAR_NOTE_OR_REST = true;
      69              : 
      70              : static constexpr bool DONT_PLAY_CHORD = false;
      71              : 
      72              : static const muse::Uri NOTATION_REVIEW_PAGE_URI("musescore://notation/review");
      73              : 
      74              : using EngravingDebuggingOptions = engraving::IEngravingConfiguration::DebuggingOptions;
      75              : static const std::map<muse::rcommand::Command, bool EngravingDebuggingOptions::*> s_debuggingCommands = {
      76              :     { SHOW_ELEMENT_BOUNDING_RECTS_COMMAND, &EngravingDebuggingOptions::showElementBoundingRects },
      77              :     { COLOR_ELEMENT_SHAPES_COMMAND, &EngravingDebuggingOptions::colorElementShapes },
      78              :     { SHOW_SEGMENT_SHAPES_COMMAND, &EngravingDebuggingOptions::showSegmentShapes },
      79              :     { COLOR_SEGMENT_SHAPES_COMMAND, &EngravingDebuggingOptions::colorSegmentShapes },
      80              :     { SHOW_SKYLINES_COMMAND, &EngravingDebuggingOptions::showSkylines },
      81              :     { SHOW_SYSTEM_BOUNDING_RECTS_COMMAND, &EngravingDebuggingOptions::showSystemBoundingRects },
      82              :     { SHOW_ELEMENT_MASKS_COMMAND, &EngravingDebuggingOptions::showElementMasks },
      83              :     { SHOW_LINE_ATTACH_POINTS_COMMAND, &EngravingDebuggingOptions::showLineAttachPoints },
      84              :     { MARK_EMPTY_STAFF_COMMAND, &EngravingDebuggingOptions::markEmptyStaffVisibilityOverrides },
      85              :     { MARK_CORRUPTED_MEASURES_COMMAND, &EngravingDebuggingOptions::markCorruptedMeasures },
      86              :     { SHOW_GAP_RESTS_COMMAND, &EngravingDebuggingOptions::showGapRests },
      87              :     { SHOW_ORIGIN_AND_COMBINED_COMMAND, &EngravingDebuggingOptions::showOriginAndCombinedStaves },
      88              : };
      89              : 
      90              : //! NOTE Just for more readable
      91              : using Controller = NotationActionController;
      92              : using Interaction = INotationInteraction;
      93              : using ViewController = INotationViewController;
      94              : 
      95            0 : void NotationActionController::init()
      96              : {
      97            0 :     TRACEFUNC;
      98              : 
      99              :     // global commands
     100            0 :     registerCommand(CANCEL_COMMAND, &Controller::resetState);
     101            0 :     registerCommand(UNDO_COMMAND, &Interaction::undo);
     102            0 :     registerCommand(REDO_COMMAND, &Interaction::redo);
     103              : 
     104              :     // navigation and selection commands
     105              : 
     106            0 :     registerCommandWithParams(SELECT_COMMAND, &Controller::select);
     107            0 :     registerCommand(GOTO_FIRST_ELEMENT_COMMAND, [this]() { select(SelectionTarget::FirstItem, PlayMode::PlayChord); });
     108            0 :     registerCommand(GOTO_LAST_ELEMENT_COMMAND, [this]() { select(SelectionTarget::LastItem, PlayMode::PlayChord); });
     109            0 :     registerCommand(GOTO_NEXT_ELEMENT_COMMAND, [this]() { select(SelectionTarget::NextItem, PlayMode::PlayNote); });
     110            0 :     registerCommand(GOTO_PREV_ELEMENT_COMMAND, [this]() { select(SelectionTarget::PrevItem, PlayMode::PlayNote); });
     111            0 :     registerCommand(GOTO_NEXT_SEGMENT_ELEMENT_COMMAND, [this]() { select(SelectionTarget::NextSegmentItem, PlayMode::PlayNote); });
     112            0 :     registerCommand(GOTO_PREV_SEGMENT_ELEMENT_COMMAND, [this]() { select(SelectionTarget::PrevSegmentItem, PlayMode::PlayNote); });
     113            0 :     registerCommand(GOTO_NEXT_TRACK_COMMAND, [this]() { select(SelectionTarget::NextTrack, PlayMode::PlayChord); });
     114            0 :     registerCommand(GOTO_PREV_TRACK_COMMAND, [this]() { select(SelectionTarget::PrevTrack, PlayMode::PlayChord); });
     115            0 :     registerCommand(GOTO_NEXT_FRAME_COMMAND, [this]() { select(SelectionTarget::NextFrame); });
     116            0 :     registerCommand(GOTO_PREV_FRAME_COMMAND, [this]() { select(SelectionTarget::PrevFrame); });
     117            0 :     registerCommand(GOTO_NEXT_SYSTEM_COMMAND, [this]() { select(SelectionTarget::NextSystem); });
     118            0 :     registerCommand(GOTO_PREV_SYSTEM_COMMAND, [this]() { select(SelectionTarget::PrevSystem); });
     119            0 :     registerCommand(GOTO_UPNOTE_IN_CHORD_COMMAND, [this]() { select(SelectionTarget::UpNoteInChord); });
     120            0 :     registerCommand(GOTO_DOWNNOTE_IN_CHORD_COMMAND, [this]() { select(SelectionTarget::DownNoteInChord); });
     121            0 :     registerCommand(GOTO_TOPNOTE_IN_CHORD_COMMAND, [this]() { select(SelectionTarget::TopNoteInChord); });
     122            0 :     registerCommand(GOTO_BOTTOMNOTE_IN_CHORD_COMMAND, [this]() { select(SelectionTarget::BottomNoteInChord); });
     123            0 :     registerCommand(GOTO_TOP_STAFF_COMMAND, [this]() { select(SelectionTarget::TopStaff, PlayMode::PlayChord); });
     124            0 :     registerCommand(GOTO_EMPTY_TRAILING_MEASURE_COMMAND, [this]() { select(SelectionTarget::EmptyTrailingMeasure); });
     125            0 :     registerCommand(SELECT_SIMILAR_COMMAND, [this]() { select(SelectionTarget::Similar); });
     126            0 :     registerCommand(SELECT_SIMILAR_IN_STAFF_COMMAND, [this]() { select(SelectionTarget::SimilarInStaff); });
     127            0 :     registerCommand(SELECT_SIMILAR_IN_RANGE_COMMAND, [this]() { select(SelectionTarget::SimilarInRange); });
     128            0 :     registerCommand(SELECT_NOTES_IN_CHORD_COMMAND, [this]() { select(SelectionTarget::NotesInChord); });
     129            0 :     registerCommand(SELECT_ALL_COMMAND, [this]() { select(SelectionTarget::All); });
     130            0 :     registerCommand(SELECT_SECTION_COMMAND, [this]() { select(SelectionTarget::Section); });
     131              : 
     132            0 :     registerCommand(GET_LOCATION_COMMAND, &Interaction::getLocation);
     133              : 
     134            0 :     registerCommand(ADD_TO_SELECTION_NEXT_CHORD_COMMAND, &Interaction::addToSelection, SelectionTarget::NextChord);
     135            0 :     registerCommand(ADD_TO_SELECTION_PREV_CHORD_COMMAND, &Interaction::addToSelection, SelectionTarget::PrevChord);
     136            0 :     registerCommand(ADD_TO_SELECTION_NEXT_MEASURE_COMMAND, &Interaction::addToSelection, SelectionTarget::NextMeasure);
     137            0 :     registerCommand(ADD_TO_SELECTION_PREV_MEASURE_COMMAND, &Interaction::addToSelection, SelectionTarget::PrevMeasure);
     138            0 :     registerCommand(ADD_TO_SELECTION_ABOVE_STAFF_COMMAND, &Interaction::addToSelection, SelectionTarget::AboveStaff);
     139            0 :     registerCommand(ADD_TO_SELECTION_BELOW_STAFF_COMMAND, &Interaction::addToSelection, SelectionTarget::BelowStaff);
     140            0 :     registerCommand(ADD_TO_SELECTION_BEGIN_SYSTEM_COMMAND, &Interaction::expandSelection, ExpandSelectionMode::BeginSystem);
     141            0 :     registerCommand(ADD_TO_SELECTION_END_SYSTEM_COMMAND, &Interaction::expandSelection, ExpandSelectionMode::EndSystem);
     142            0 :     registerCommand(ADD_TO_SELECTION_BEGIN_SCORE_COMMAND, &Interaction::expandSelection, ExpandSelectionMode::BeginScore);
     143            0 :     registerCommand(ADD_TO_SELECTION_END_SCORE_COMMAND, &Interaction::expandSelection, ExpandSelectionMode::EndScore);
     144              : 
     145            0 :     registerCommand(OPEN_SELECTION_OPTIONS_COMMAND, &Controller::openSelectionMoreOptions);
     146              : 
     147              :     // text navigation commands
     148            0 :     registerCommand(EDITTEXT_NEXT_WORD_COMMAND, [this]() { nextWord(); });
     149            0 :     registerCommand(EDITTEXT_NEXT_ELEMENT_COMMAND, [this]() { nextTextElement(); });
     150            0 :     registerCommand(EDITTEXT_PREV_ELEMENT_COMMAND, [this]() { prevTextElement(); });
     151            0 :     registerCommand(EDITTEXT_NEXT_BEAT_COMMAND, &Controller::nextBeatTextElement, &Controller::textNavigationByBeatsAvailable);
     152            0 :     registerCommand(EDITTEXT_PREV_BEAT_COMMAND, &Controller::prevBeatTextElement, &Controller::textNavigationByBeatsAvailable);
     153              : 
     154            0 :     registerNavigationByFractionCommand(EDITTEXT_ADVANCE_LONGA_COMMAND, Fraction(4, 1));
     155            0 :     registerNavigationByFractionCommand(EDITTEXT_ADVANCE_BREVE_COMMAND, Fraction(2, 1));
     156            0 :     registerNavigationByFractionCommand(EDITTEXT_ADVANCE_1_COMMAND, Fraction(1, 1));
     157            0 :     registerNavigationByFractionCommand(EDITTEXT_ADVANCE_2_COMMAND, Fraction(1, 2));
     158            0 :     registerNavigationByFractionCommand(EDITTEXT_ADVANCE_4_COMMAND, Fraction(1, 4));
     159            0 :     registerNavigationByFractionCommand(EDITTEXT_ADVANCE_8_COMMAND, Fraction(1, 8));
     160            0 :     registerNavigationByFractionCommand(EDITTEXT_ADVANCE_16_COMMAND, Fraction(1, 16));
     161            0 :     registerNavigationByFractionCommand(EDITTEXT_ADVANCE_32_COMMAND, Fraction(1, 32));
     162            0 :     registerNavigationByFractionCommand(EDITTEXT_ADVANCE_64_COMMAND, Fraction(1, 64));
     163              : 
     164              :     // lyrics editing commands
     165            0 :     registerCommand(EDITLYRIC_NEXT_VERSE_COMMAND, &Interaction::navigateToLyricsVerse, MoveDirection::Down);
     166            0 :     registerCommand(EDITLYRIC_PREV_VERSE_COMMAND, &Interaction::navigateToLyricsVerse, MoveDirection::Up);
     167            0 :     registerCommand(EDITLYRIC_NEXT_SYLLABLE_COMMAND, &Interaction::navigateToNextSyllable);
     168            0 :     registerCommand(EDITLYRIC_ADD_MELISMA_COMMAND, &Interaction::addMelisma);
     169            0 :     registerCommand(EDITLYRIC_ADD_VERSE_COMMAND, &Interaction::addLyricsVerse);
     170              : 
     171              :     // text editing commands
     172            0 :     registerCommand(EDITTEXT_TOGGLE_BOLD_COMMAND, &Interaction::toggleBold);
     173            0 :     registerCommand(EDITTEXT_TOGGLE_ITALIC_COMMAND, &Interaction::toggleItalic);
     174            0 :     registerCommand(EDITTEXT_TOGGLE_UNDERLINE_COMMAND, &Interaction::toggleUnderline);
     175            0 :     registerCommand(EDITTEXT_TOGGLE_STRIKE_COMMAND, &Interaction::toggleStrike);
     176            0 :     registerCommand(EDITTEXT_TOGGLE_SUBSCRIPT_COMMAND, &Interaction::toggleSubScript);
     177            0 :     registerCommand(EDITTEXT_TOGGLE_SUPERSCRIPT_COMMAND, &Interaction::toggleSuperScript);
     178              : 
     179              :     // note input commands
     180            0 :     registerNoteInputCommand(TOGGLE_NOTE_INPUT_COMMAND, NoteInputMethod::UNKNOWN /*default*/);
     181            0 :     registerNoteInputCommand(TOGGLE_NOTE_INPUT_BY_NOTE_NAME_COMMAND, NoteInputMethod::BY_NOTE_NAME);
     182            0 :     registerNoteInputCommand(TOGGLE_NOTE_INPUT_BY_DURATION_COMMAND, NoteInputMethod::BY_DURATION);
     183            0 :     registerNoteInputCommand(TOGGLE_NOTE_INPUT_RHYTHM_COMMAND, NoteInputMethod::RHYTHM);
     184            0 :     registerNoteInputCommand(TOGGLE_NOTE_INPUT_REPITCH_COMMAND, NoteInputMethod::REPITCH);
     185            0 :     registerNoteInputCommand(TOGGLE_NOTE_INPUT_REALTIME_AUTO_COMMAND, NoteInputMethod::REALTIME_AUTO);
     186            0 :     registerNoteInputCommand(TOGGLE_NOTE_INPUT_REALTIME_MANUAL_COMMAND, NoteInputMethod::REALTIME_MANUAL);
     187            0 :     registerNoteInputCommand(TOGGLE_NOTE_INPUT_TIMEWISE_COMMAND, NoteInputMethod::TIMEWISE);
     188            0 :     registerCommand(TOGGLE_INSERT_MODE_COMMAND, [this]() { toggleNoteInputInsert(); }, &NotationActionController::isNotEditingElement);
     189              : 
     190            0 :     registerCommand(REALTIME_ADVANCE_COMMAND, &Controller::realtimeAdvance);
     191              : 
     192            0 :     registerGetCommand<GetNoteInputStateCommand>(&NotationActionController::getNoteInputState);
     193              : 
     194            0 :     registerCommand(SET_DURATION_LONGA_COMMAND, [this]() { setDuration(DurationType::V_LONG); });
     195            0 :     registerCommand(SET_DURATION_BREVE_COMMAND, [this]() { setDuration(DurationType::V_BREVE); });
     196            0 :     registerCommand(SET_DURATION_WHOLE_COMMAND, [this]() { setDuration(DurationType::V_WHOLE); });
     197            0 :     registerCommand(SET_DURATION_HALF_COMMAND, [this]() { setDuration(DurationType::V_HALF); });
     198            0 :     registerCommand(SET_DURATION_QUARTER_COMMAND, [this]() { setDuration(DurationType::V_QUARTER); });
     199            0 :     registerCommand(SET_DURATION_EIGHTH_COMMAND, [this]() { setDuration(DurationType::V_EIGHTH); });
     200            0 :     registerCommand(SET_DURATION_16TH_COMMAND, [this]() { setDuration(DurationType::V_16TH); });
     201            0 :     registerCommand(SET_DURATION_32ND_COMMAND, [this]() { setDuration(DurationType::V_32ND); });
     202            0 :     registerCommand(SET_DURATION_64TH_COMMAND, [this]() { setDuration(DurationType::V_64TH); });
     203            0 :     registerCommand(SET_DURATION_128TH_COMMAND, [this]() { setDuration(DurationType::V_128TH); });
     204            0 :     registerCommand(SET_DURATION_256TH_COMMAND, [this]() { setDuration(DurationType::V_256TH); });
     205            0 :     registerCommand(SET_DURATION_512TH_COMMAND, [this]() { setDuration(DurationType::V_512TH); });
     206            0 :     registerCommand(SET_DURATION_1024TH_COMMAND, [this]() { setDuration(DurationType::V_1024TH); });
     207              : 
     208            0 :     registerCommand(SET_DOUBLE_DURATION_COMMAND, [this]() { increaseDecreaseDuration(-1, false); });
     209            0 :     registerCommand(SET_HALVE_DURATION_COMMAND, [this]() { increaseDecreaseDuration(1, false); });
     210            0 :     registerCommand(SET_DOUBLE_DURATION_DOTTED_COMMAND, [this]() { increaseDecreaseDuration(-1, true); });
     211            0 :     registerCommand(SET_HALVE_DURATION_DOTTED_COMMAND, [this]() { increaseDecreaseDuration(1, true); });
     212              : 
     213            0 :     registerCommand(EXTEND_TO_NEXT_NOTE_COMMAND, &Interaction::extendToNextNote);
     214              : 
     215            0 :     registerCommand(TOGGLE_DOT_COMMAND, [this]() { toggleDots(1); });
     216            0 :     registerCommand(TOGGLE_DOT2_COMMAND, [this]() { toggleDots(2); });
     217            0 :     registerCommand(TOGGLE_DOT3_COMMAND, [this]() { toggleDots(3); });
     218            0 :     registerCommand(TOGGLE_DOT4_COMMAND, [this]() { toggleDots(4); });
     219              : 
     220            0 :     registerCommand(TOGGLE_REST_COMMAND, [this]() { toggleRest(); });
     221              : 
     222            0 :     registerCommand(TOGGLE_FLAT2_COMMAND, [this]() { toggleAccidental(AccidentalType::FLAT2); });
     223            0 :     registerCommand(TOGGLE_FLAT_COMMAND, [this]() { toggleAccidental(AccidentalType::FLAT); });
     224            0 :     registerCommand(TOGGLE_NAT_COMMAND, [this]() { toggleAccidental(AccidentalType::NATURAL); });
     225            0 :     registerCommand(TOGGLE_SHARP_COMMAND, [this]() { toggleAccidental(AccidentalType::SHARP); });
     226            0 :     registerCommand(TOGGLE_SHARP2_COMMAND, [this]() { toggleAccidental(AccidentalType::SHARP2); });
     227              : 
     228            0 :     registerCommand(ADD_SHARP2_COMMAND, &Interaction::changeAccidental, mu::engraving::AccidentalType::SHARP2, PlayMode::PlayNote);
     229            0 :     registerCommand(ADD_SHARP_COMMAND, &Interaction::changeAccidental, mu::engraving::AccidentalType::SHARP, PlayMode::PlayNote);
     230            0 :     registerCommand(ADD_NAT_COMMAND, &Interaction::changeAccidental, mu::engraving::AccidentalType::NATURAL, PlayMode::PlayNote);
     231            0 :     registerCommand(ADD_FLAT_COMMAND, &Interaction::changeAccidental, mu::engraving::AccidentalType::FLAT, PlayMode::PlayNote);
     232            0 :     registerCommand(ADD_FLAT2_COMMAND, &Interaction::changeAccidental, mu::engraving::AccidentalType::FLAT2, PlayMode::PlayNote);
     233              : 
     234            0 :     registerCommand(TOGGLE_TIE_COMMAND, &Controller::addTie);
     235            0 :     registerCommand(ADD_SLUR_COMMAND, &Controller::addSlur);
     236            0 :     registerCommand(TOGGLE_LV_COMMAND, &Controller::addLaissezVib);
     237            0 :     registerCommand(TOGGLE_MARCATO_COMMAND, [this]() { toggleArticulation(SymbolId::articMarcatoAbove); });
     238            0 :     registerCommand(TOGGLE_SFORZATO_COMMAND, [this]() { toggleArticulation(SymbolId::articAccentAbove); });
     239            0 :     registerCommand(TOGGLE_TENUTO_COMMAND, [this]() { toggleArticulation(SymbolId::articTenutoAbove); });
     240            0 :     registerCommand(TOGGLE_STACCATO_COMMAND, [this]() { toggleArticulation(SymbolId::articStaccatoAbove); });
     241              : 
     242            0 :     registerCommand(USE_VOICE_1_COMMAND, [this]() { changeVoice(0); });
     243            0 :     registerCommand(USE_VOICE_2_COMMAND, [this]() { changeVoice(1); });
     244            0 :     registerCommand(USE_VOICE_3_COMMAND, [this]() { changeVoice(2); });
     245            0 :     registerCommand(USE_VOICE_4_COMMAND, [this]() { changeVoice(3); });
     246            0 :     registerCommand(SWAP_VOICE_X12_COMMAND, [this]() { swapVoices(0, 1); });
     247            0 :     registerCommand(SWAP_VOICE_X13_COMMAND, [this]() { swapVoices(0, 2); });
     248            0 :     registerCommand(SWAP_VOICE_X14_COMMAND, [this]() { swapVoices(0, 3); });
     249            0 :     registerCommand(SWAP_VOICE_X23_COMMAND, [this]() { swapVoices(1, 2); });
     250            0 :     registerCommand(SWAP_VOICE_X24_COMMAND, [this]() { swapVoices(1, 3); });
     251            0 :     registerCommand(SWAP_VOICE_X34_COMMAND, [this]() { swapVoices(2, 3); });
     252              : 
     253            0 :     registerCommand<FlipCommand>(&Controller::flipSelection);
     254            0 :     registerCommand(FLIP_HORIZONTALLY_COMMAND, &Interaction::flipSelectionHorizontally);
     255              : 
     256            0 :     registerCommand<AddNoteCommand>(&Controller::addNote);
     257            0 :     registerCommandWithParams(ADD_DRUM_NOTE_COMMAND, &Controller::addDrumNote);
     258              : 
     259            0 :     registerNoteCommand(ENTER_NOTE_C_COMMAND, NoteName::C);
     260            0 :     registerNoteCommand(ENTER_NOTE_D_COMMAND, NoteName::D);
     261            0 :     registerNoteCommand(ENTER_NOTE_E_COMMAND, NoteName::E);
     262            0 :     registerNoteCommand(ENTER_NOTE_F_COMMAND, NoteName::F);
     263            0 :     registerNoteCommand(ENTER_NOTE_G_COMMAND, NoteName::G);
     264            0 :     registerNoteCommand(ENTER_NOTE_A_COMMAND, NoteName::A);
     265            0 :     registerNoteCommand(ENTER_NOTE_B_COMMAND, NoteName::B);
     266            0 :     registerNoteCommand(ADD_NOTE_C_COMMAND, NoteName::C, NoteAddingMode::CurrentChord);
     267            0 :     registerNoteCommand(ADD_NOTE_D_COMMAND, NoteName::D, NoteAddingMode::CurrentChord);
     268            0 :     registerNoteCommand(ADD_NOTE_E_COMMAND, NoteName::E, NoteAddingMode::CurrentChord);
     269            0 :     registerNoteCommand(ADD_NOTE_F_COMMAND, NoteName::F, NoteAddingMode::CurrentChord);
     270            0 :     registerNoteCommand(ADD_NOTE_G_COMMAND, NoteName::G, NoteAddingMode::CurrentChord);
     271            0 :     registerNoteCommand(ADD_NOTE_A_COMMAND, NoteName::A, NoteAddingMode::CurrentChord);
     272            0 :     registerNoteCommand(ADD_NOTE_B_COMMAND, NoteName::B, NoteAddingMode::CurrentChord);
     273            0 :     registerNoteCommand(INSERT_NOTE_C_COMMAND, NoteName::C, NoteAddingMode::InsertChord);
     274            0 :     registerNoteCommand(INSERT_NOTE_D_COMMAND, NoteName::D, NoteAddingMode::InsertChord);
     275            0 :     registerNoteCommand(INSERT_NOTE_E_COMMAND, NoteName::E, NoteAddingMode::InsertChord);
     276            0 :     registerNoteCommand(INSERT_NOTE_F_COMMAND, NoteName::F, NoteAddingMode::InsertChord);
     277            0 :     registerNoteCommand(INSERT_NOTE_G_COMMAND, NoteName::G, NoteAddingMode::InsertChord);
     278            0 :     registerNoteCommand(INSERT_NOTE_A_COMMAND, NoteName::A, NoteAddingMode::InsertChord);
     279            0 :     registerNoteCommand(INSERT_NOTE_B_COMMAND, NoteName::B, NoteAddingMode::InsertChord);
     280              : 
     281            0 :     registerCommand(ENTER_REST_COMMAND, &Interaction::putRestToSelection);
     282              : 
     283            0 :     registerCommand(OPEN_TUPLET_CONFIGURE_COMMAND, [this]() { openTupletOtherDialog(); });
     284            0 :     registerCommandWithParams(ADD_TUPLET_COMMAND, &Controller::putTuplet);
     285            0 :     registerCommand(ADD_DUPLET_COMMAND, [this]() { putTuplet(2); });
     286            0 :     registerCommand(ADD_TRIPLET_COMMAND, [this]() { putTuplet(3); });
     287            0 :     registerCommand(ADD_QUADRUPLET_COMMAND, [this]() { putTuplet(4); });
     288            0 :     registerCommand(ADD_QUINTUPLET_COMMAND, [this]() { putTuplet(5); });
     289            0 :     registerCommand(ADD_SEXTUPLET_COMMAND, [this]() { putTuplet(6); });
     290            0 :     registerCommand(ADD_SEPTUPLET_COMMAND, [this]() { putTuplet(7); });
     291            0 :     registerCommand(ADD_OCTUPLET_COMMAND, [this]() { putTuplet(8); });
     292            0 :     registerCommand(ADD_NONUPLET_COMMAND, [this]() { putTuplet(9); });
     293              : 
     294            0 :     registerCommand(INSERT_HBOX_COMMAND, [this]() { addBoxes(BoxType::Horizontal, 1, AddBoxesTarget::BeforeSelection); });
     295            0 :     registerCommand(INSERT_VBOX_COMMAND, [this]() { addBoxes(BoxType::Vertical, 1, AddBoxesTarget::BeforeSelection); });
     296            0 :     registerCommand(INSERT_TEXTFRAME_COMMAND, [this]() { addBoxes(BoxType::Text, 1, AddBoxesTarget::BeforeSelection); });
     297            0 :     registerCommand(INSERT_FRETFRAME_COMMAND, [this]() { addBoxes(BoxType::Fret, 1, AddBoxesTarget::BeforeSelection); });
     298            0 :     registerCommand(APPEND_HBOX_COMMAND, [this]() { addBoxes(BoxType::Horizontal, 1, AddBoxesTarget::AtEndOfScore); });
     299            0 :     registerCommand(APPEND_VBOX_COMMAND, [this]() { addBoxes(BoxType::Vertical, 1, AddBoxesTarget::AtEndOfScore); });
     300            0 :     registerCommand(APPEND_TEXTFRAME_COMMAND, [this]() { addBoxes(BoxType::Text, 1, AddBoxesTarget::AtEndOfScore); });
     301            0 :     registerCommand(APPEND_FRETFRAME_COMMAND, [this]() { addBoxes(BoxType::Fret, 1, AddBoxesTarget::AtEndOfScore); });
     302              : 
     303            0 :     registerCommand(ADD_FRETBOARD_DIAGRAM_COMMAND, &Controller::addFretboardDiagram);
     304              : 
     305            0 :     registerCommand(ADD_OTTAVA_8VA_COMMAND, &Interaction::addOttavaToSelection, OttavaType::OTTAVA_8VA);
     306            0 :     registerCommand(ADD_OTTAVA_8VB_COMMAND, &Interaction::addOttavaToSelection, OttavaType::OTTAVA_8VB);
     307              : 
     308            0 :     registerCommand(ADD_DYNAMIC_COMMAND, &Interaction::toggleDynamicPopup);
     309            0 :     registerCommand(ADD_HAIRPIN_COMMAND, &Interaction::addHairpinsToSelection, HairpinType::CRESC_HAIRPIN);
     310            0 :     registerCommand(ADD_HAIRPIN_REVERSE_COMMAND, &Interaction::addHairpinsToSelection, HairpinType::DIM_HAIRPIN);
     311            0 :     registerCommand(INCREASE_DYNAMIC_COMMAND, &Interaction::increaseDecreaseSelectedDynamicsValues, /*delta*/ 1);
     312            0 :     registerCommand(DECREASE_DYNAMIC_COMMAND, &Interaction::increaseDecreaseSelectedDynamicsValues, /*delta*/ -1);
     313            0 :     registerCommand(ADD_NOTELINE_COMMAND, &Interaction::addAnchoredLineToSelectedNotes);
     314              : 
     315            0 :     registerCommand(ADD_IMAGE_COMMAND, [this]() { addImage(); });
     316              : 
     317            0 :     registerCommand(ADD_UP_BOW_COMMAND, &Interaction::toggleArticulation, mu::engraving::SymId::stringsUpBow);
     318            0 :     registerCommand(ADD_DOWN_BOW_COMMAND, &Interaction::toggleArticulation, mu::engraving::SymId::stringsDownBow);
     319              : 
     320              :     // add text commands
     321            0 :     registerCommand(ADD_TITLE_TEXT_COMMAND, [this]() { addText(TextStyleType::TITLE); });
     322            0 :     registerCommand(ADD_SUBTITLE_TEXT_COMMAND, [this]() { addText(TextStyleType::SUBTITLE); });
     323            0 :     registerCommand(ADD_COMPOSER_TEXT_COMMAND, [this]() { addText(TextStyleType::COMPOSER); });
     324            0 :     registerCommand(ADD_LYRICIST_TEXT_COMMAND, [this]() { addText(TextStyleType::LYRICIST); });
     325            0 :     registerCommand(ADD_PART_TEXT_COMMAND, [this]() { addText(TextStyleType::INSTRUMENT_EXCERPT); });
     326            0 :     registerCommand(ADD_FRAME_TEXT_COMMAND, [this]() { addText(TextStyleType::FRAME); });
     327            0 :     registerCommand(ADD_SYSTEM_TEXT_COMMAND, [this]() { addText(TextStyleType::SYSTEM); });
     328            0 :     registerCommand(ADD_STAFF_TEXT_COMMAND, [this]() { addText(TextStyleType::STAFF); });
     329            0 :     registerCommand(ADD_EXPRESSION_TEXT_COMMAND, [this]() { addText(TextStyleType::EXPRESSION); });
     330            0 :     registerCommand(ADD_REHEARSALMARK_TEXT_COMMAND, [this]() { addText(TextStyleType::REHEARSAL_MARK); });
     331            0 :     registerCommand(ADD_INSTRUMENT_CHANGE_TEXT_COMMAND, [this]() { addText(TextStyleType::INSTRUMENT_CHANGE); });
     332            0 :     registerCommand(ADD_FINGERING_TEXT_COMMAND, [this]() { addText(TextStyleType::FINGERING); });
     333            0 :     registerCommand(ADD_STICKING_TEXT_COMMAND, [this]() { addText(TextStyleType::STICKING); });
     334            0 :     registerCommand(ADD_CHORD_TEXT_COMMAND, [this]() { addText(TextStyleType::HARMONY_A); });
     335            0 :     registerCommand(ADD_ROMAN_NUMERAL_TEXT_COMMAND, [this]() { addText(TextStyleType::HARMONY_ROMAN); });
     336            0 :     registerCommand(ADD_NASHVILLE_NUMBER_TEXT_COMMAND, [this]() { addText(TextStyleType::HARMONY_NASHVILLE); });
     337            0 :     registerCommand(ADD_LYRICS_COMMAND, [this]() { addText(TextStyleType::LYRICS_ODD); });
     338            0 :     registerCommand(ADD_TEMPO_COMMAND, [this]() { addText(TextStyleType::TEMPO); });
     339            0 :     registerCommand(ADD_FIGURED_BASS_COMMAND, [this]() { addFiguredBass(); });
     340              : 
     341              :     // add grace notes commands
     342            0 :     registerCommand(ADD_ACCIACCATURA_COMMAND, &Interaction::addGraceNotesToSelectedNotes, GraceNoteType::ACCIACCATURA);
     343            0 :     registerCommand(ADD_APPOGGIATURA_COMMAND, &Interaction::addGraceNotesToSelectedNotes, GraceNoteType::APPOGGIATURA);
     344            0 :     registerCommand(ADD_GRACE4_COMMAND, &Interaction::addGraceNotesToSelectedNotes, GraceNoteType::GRACE4);
     345            0 :     registerCommand(ADD_GRACE16_COMMAND, &Interaction::addGraceNotesToSelectedNotes, GraceNoteType::GRACE16);
     346            0 :     registerCommand(ADD_GRACE32_COMMAND, &Interaction::addGraceNotesToSelectedNotes, GraceNoteType::GRACE32);
     347            0 :     registerCommand(ADD_GRACE8_AFTER_COMMAND, &Interaction::addGraceNotesToSelectedNotes, GraceNoteType::GRACE8_AFTER);
     348            0 :     registerCommand(ADD_GRACE16_AFTER_COMMAND, &Interaction::addGraceNotesToSelectedNotes, GraceNoteType::GRACE16_AFTER);
     349            0 :     registerCommand(ADD_GRACE32_AFTER_COMMAND, &Interaction::addGraceNotesToSelectedNotes, GraceNoteType::GRACE32_AFTER);
     350              : 
     351              :     // add beam commands
     352            0 :     registerCommand(ADD_BEAM_AUTO_COMMAND, &Interaction::addBeamToSelectedChordRests, BeamMode::AUTO);
     353            0 :     registerCommand(ADD_BEAM_NONE_COMMAND, &Interaction::addBeamToSelectedChordRests, BeamMode::NONE);
     354            0 :     registerCommand(ADD_BEAM_BEGIN_COMMAND, &Interaction::addBeamToSelectedChordRests, BeamMode::BEGIN);
     355            0 :     registerCommand(ADD_BEAM_BEGIN16_COMMAND, &Interaction::addBeamToSelectedChordRests, BeamMode::BEGIN16);
     356            0 :     registerCommand(ADD_BEAM_BEGIN32_COMMAND, &Interaction::addBeamToSelectedChordRests, BeamMode::BEGIN32);
     357            0 :     registerCommand(ADD_BEAM_MID_COMMAND, &Interaction::addBeamToSelectedChordRests, BeamMode::MID);
     358            0 :     registerCommand(ADD_BEAM_SELECTED_RANGE_COMMAND, &Interaction::beamSelectedRange);
     359              : 
     360              :     // add brackets commands
     361            0 :     registerCommand(ADD_BRACKETS_COMMAND, &Interaction::addBracketsToSelection, BracketsType::Brackets);
     362            0 :     registerCommand(ADD_BRACES_COMMAND, &Interaction::addBracketsToSelection, BracketsType::Braces);
     363            0 :     registerCommand(ADD_PARENTHESES_COMMAND, &Interaction::addBracketsToSelection, BracketsType::Parentheses);
     364              : 
     365              :     // add ornament commands
     366            0 :     registerCommand(ADD_TURN_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentTurn);
     367            0 :     registerCommand(ADD_TURN_INVERTED_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentTurnInverted);
     368            0 :     registerCommand(ADD_TURN_SLASH_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentTurnSlash);
     369            0 :     registerCommand(ADD_TURN_UP_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentTurnUp);
     370            0 :     registerCommand(ADD_TURN_INVERTED_UP_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentTurnUpS);
     371            0 :     registerCommand(ADD_TRILL_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentTrill);
     372            0 :     registerCommand(ADD_SHORT_TRILL_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentShortTrill);
     373            0 :     registerCommand(ADD_MORDENT_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentMordent);
     374            0 :     registerCommand(ADD_HAYDN_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentHaydn);
     375            0 :     registerCommand(ADD_TREMBLEMENT_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentTremblement);
     376            0 :     registerCommand(ADD_PRALL_MORDENT_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentPrallMordent);
     377            0 :     registerCommand(ADD_SHAKE_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentShake3);
     378            0 :     registerCommand(ADD_SHAKE_MUFFAT_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentShakeMuffat1);
     379            0 :     registerCommand(ADD_TREMBLEMENT_COUPERIN_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentTremblementCouperin);
     380              : 
     381              :     // clef commands
     382            0 :     registerCommand(ADD_CLEF_VIOLIN_COMMAND, [this]() { insertClef(mu::engraving::ClefType::G); });
     383            0 :     registerCommand(ADD_CLEF_BASS_COMMAND, [this]() { insertClef(mu::engraving::ClefType::F); });
     384              : 
     385            0 :     registerCommand(ADD_FULL_MEASURE_REST_COMMAND, &Controller::addFullMeasureRest);
     386              : 
     387              :     // interval commands
     388            0 :     registerCommand(ADD_INTERVAL_PLUS_1_COMMAND, &Interaction::addIntervalToSelectedNotes, 1);
     389            0 :     registerCommand(ADD_INTERVAL_PLUS_2_COMMAND, &Interaction::addIntervalToSelectedNotes, 2);
     390            0 :     registerCommand(ADD_INTERVAL_PLUS_3_COMMAND, &Interaction::addIntervalToSelectedNotes, 3);
     391            0 :     registerCommand(ADD_INTERVAL_PLUS_4_COMMAND, &Interaction::addIntervalToSelectedNotes, 4);
     392            0 :     registerCommand(ADD_INTERVAL_PLUS_5_COMMAND, &Interaction::addIntervalToSelectedNotes, 5);
     393            0 :     registerCommand(ADD_INTERVAL_PLUS_6_COMMAND, &Interaction::addIntervalToSelectedNotes, 6);
     394            0 :     registerCommand(ADD_INTERVAL_PLUS_7_COMMAND, &Interaction::addIntervalToSelectedNotes, 7);
     395            0 :     registerCommand(ADD_INTERVAL_PLUS_8_COMMAND, &Interaction::addIntervalToSelectedNotes, 8);
     396            0 :     registerCommand(ADD_INTERVAL_PLUS_9_COMMAND, &Interaction::addIntervalToSelectedNotes, 9);
     397            0 :     registerCommand(ADD_INTERVAL_PLUS_10_COMMAND, &Interaction::addIntervalToSelectedNotes, 10);
     398            0 :     registerCommand(ADD_INTERVAL_MINUS_2_COMMAND, &Interaction::addIntervalToSelectedNotes, -2);
     399            0 :     registerCommand(ADD_INTERVAL_MINUS_3_COMMAND, &Interaction::addIntervalToSelectedNotes, -3);
     400            0 :     registerCommand(ADD_INTERVAL_MINUS_4_COMMAND, &Interaction::addIntervalToSelectedNotes, -4);
     401            0 :     registerCommand(ADD_INTERVAL_MINUS_5_COMMAND, &Interaction::addIntervalToSelectedNotes, -5);
     402            0 :     registerCommand(ADD_INTERVAL_MINUS_6_COMMAND, &Interaction::addIntervalToSelectedNotes, -6);
     403            0 :     registerCommand(ADD_INTERVAL_MINUS_7_COMMAND, &Interaction::addIntervalToSelectedNotes, -7);
     404            0 :     registerCommand(ADD_INTERVAL_MINUS_8_COMMAND, &Interaction::addIntervalToSelectedNotes, -8);
     405            0 :     registerCommand(ADD_INTERVAL_MINUS_9_COMMAND, &Interaction::addIntervalToSelectedNotes, -9);
     406            0 :     registerCommand(ADD_INTERVAL_MINUS_10_COMMAND, &Interaction::addIntervalToSelectedNotes, -10);
     407              : 
     408              :     // editing commands
     409            0 :     registerCommand(COPY_COMMAND, &Interaction::copySelection);
     410            0 :     registerCommand(COPY_PASTE_SWAP_COMMAND, &Interaction::swapSelection);
     411            0 :     registerCommand(COPY_LYRICS_COMMAND, &Interaction::copyLyrics);
     412            0 :     registerCommand(CUT_COMMAND, &Controller::cutSelection);
     413            0 :     registerCommand(PASTE_COMMAND, [this]() { pasteSelection(PastingType::Default); });
     414            0 :     registerCommand(PASTE_HALF_COMMAND, [this]() { pasteSelection(PastingType::Half); });
     415            0 :     registerCommand(PASTE_DOUBLE_COMMAND, [this]() { pasteSelection(PastingType::Double); });
     416            0 :     registerCommand(PASTE_SPECIAL_COMMAND, [this]() { pasteSelection(PastingType::Special); });
     417            0 :     registerCommand(DELETE_COMMAND, &Interaction::deleteSelection);
     418              : 
     419              :     // move commands
     420            0 :     registerCommand(MOVE_RIGHT_COMMAND, [this]() { move(MoveDirection::Right, false); });
     421            0 :     registerCommand(MOVE_LEFT_COMMAND, [this]() { move(MoveDirection::Left, false); });
     422            0 :     registerCommand(MOVE_RIGHT_QUICKLY_COMMAND, [this]() { move(MoveDirection::Right, true); });
     423            0 :     registerCommand(MOVE_LEFT_QUICKLY_COMMAND, [this]() { move(MoveDirection::Left, true); });
     424              : 
     425            0 :     registerCommand(MOVE_UP_COMMAND, &Interaction::moveChordRestToStaff, MoveDirection::Up);
     426            0 :     registerCommand(MOVE_DOWN_COMMAND, &Interaction::moveChordRestToStaff, MoveDirection::Down);
     427            0 :     registerCommand(SWAP_LEFT_COMMAND, &Interaction::swapChordRest, MoveDirection::Left);
     428            0 :     registerCommand(SWAP_RIGHT_COMMAND, &Interaction::swapChordRest, MoveDirection::Right);
     429              : 
     430            0 :     registerCommand(PITCH_UP_COMMAND, [this]() { move(MoveDirection::Up, false); });
     431            0 :     registerCommand(PITCH_DOWN_COMMAND, [this]() { move(MoveDirection::Down, false); });
     432            0 :     registerCommand(PITCH_UP_OCTAVE_COMMAND, [this]() { move(MoveDirection::Up, true); });
     433            0 :     registerCommand(PITCH_DOWN_OCTAVE_COMMAND, [this]() { move(MoveDirection::Down, true); });
     434            0 :     registerCommand(PITCH_UP_DIATONIC_COMMAND, [this]() { movePitchDiatonic(MoveDirection::Up, false); });
     435            0 :     registerCommand(PITCH_DOWN_DIATONIC_COMMAND, [this]() { movePitchDiatonic(MoveDirection::Down, false); });
     436              : 
     437            0 :     registerCommand(PITCH_UP_DIATONIC_ALTERATIONS_COMMAND, &Interaction::transposeDiatonicAlterations,
     438              :                     mu::engraving::TransposeDirection::UP,
     439              :                     PlayMode::PlayNote);
     440            0 :     registerCommand(PITCH_DOWN_DIATONIC_ALTERATIONS_COMMAND, &Interaction::transposeDiatonicAlterations,
     441              :                     mu::engraving::TransposeDirection::DOWN,
     442              :                     PlayMode::PlayNote);
     443              : 
     444              :     // properties commands
     445            0 :     registerCommand(TOGGLE_VISIBLE_COMMAND, &Interaction::toggleVisible);
     446              : 
     447              :     // snap commands
     448            0 :     registerCommand(TOGGLE_SNAP_TO_PREV_COMMAND, &Interaction::toggleSnapToPrevious);
     449            0 :     registerCommand(TOGGLE_SNAP_TO_NEXT_COMMAND, &Interaction::toggleSnapToNext);
     450              : 
     451              :     // layout commands
     452            0 :     registerCommand(TOGGLE_SYSTEM_BREAK_COMMAND, &Interaction::toggleLayoutBreak, LayoutBreakType::LINE);
     453            0 :     registerCommand(TOGGLE_PAGE_BREAK_COMMAND, &Interaction::toggleLayoutBreak, LayoutBreakType::PAGE);
     454            0 :     registerCommand(TOGGLE_SECTION_BREAK_COMMAND, &Interaction::toggleLayoutBreak, LayoutBreakType::SECTION);
     455              : 
     456            0 :     registerCommand(APPLY_SYSTEM_LOCK_COMMAND, &Interaction::applySystemLock);
     457            0 :     registerCommand(TOGGLE_SYSTEM_LOCK_COMMAND, &Interaction::toggleSystemLock);
     458            0 :     registerCommand(APPLY_PAGE_LOCK_COMMAND, &Interaction::applyPageLock);
     459            0 :     registerCommand(TOGGLE_PAGE_LOCK_COMMAND, &Interaction::togglePageLock);
     460            0 :     registerCommand(TOGGLE_SCORE_LOCK_COMMAND, &Interaction::toggleScoreLock);
     461              : 
     462            0 :     registerCommand(MAKE_INTO_SYSTEM_COMMAND, &Interaction::makeIntoSystem);
     463            0 :     registerCommand(MAKE_INTO_PAGE_COMMAND, &Interaction::makeIntoPage);
     464              : 
     465            0 :     registerCommand(MOVE_MEASURE_TO_PREV_SYSTEM_COMMAND, &Interaction::moveMeasureToPrevSystem);
     466            0 :     registerCommand(MOVE_MEASURE_TO_NEXT_SYSTEM_COMMAND, &Interaction::moveMeasureToNextSystem);
     467            0 :     registerCommand(MOVE_SYSTEM_TO_PREV_PAGE_COMMAND, &Interaction::moveSystemToPrevPage);
     468            0 :     registerCommand(MOVE_SYSTEM_TO_NEXT_PAGE_COMMAND, &Interaction::moveSystemToNextPage);
     469              : 
     470            0 :     registerCommand(SPLIT_MEASURE_COMMAND, &Interaction::splitSelectedMeasure);
     471            0 :     registerCommand(JOIN_MEASURES_COMMAND, &Interaction::joinSelectedMeasures);
     472            0 :     registerCommand(INSERT_MEASURE_COMMAND, [this]() { addBoxes(BoxType::Measure, 1, AddBoxesTarget::BeforeSelection); });
     473            0 :     registerCommandWithParams(INSERT_MEASURES_COMMAND, [this](const muse::rcommand::Params& params) {
     474            0 :         return addMeasures(params, AddBoxesTarget::BeforeSelection);
     475              :     });
     476            0 :     registerCommandWithParams(INSERT_MEASURES_AFTER_SELECTION_COMMAND, [this](const muse::rcommand::Params& params) {
     477            0 :         return addMeasures(params, AddBoxesTarget::AfterSelection);
     478              :     });
     479            0 :     registerCommandWithParams(INSERT_MEASURES_AT_START_OF_SCORE_COMMAND, [this](const muse::rcommand::Params& params) {
     480            0 :         return addMeasures(params, AddBoxesTarget::AtStartOfScore);
     481              :     });
     482            0 :     registerCommand(APPEND_MEASURE_COMMAND, [this]() {
     483            0 :         addBoxes(BoxType::Measure, 1, AddBoxesTarget::AtEndOfScore);
     484            0 :     });
     485            0 :     registerCommandWithParams(APPEND_MEASURES_COMMAND, [this](const muse::rcommand::Params& params) {
     486            0 :         return addMeasures(params, AddBoxesTarget::AtEndOfScore);
     487              :     });
     488              : 
     489            0 :     registerCommand(STRETCH_DECREASE_COMMAND, [this]() { addStretch(-STRETCH_STEP); });
     490            0 :     registerCommand(STRETCH_INCREASE_COMMAND, [this]() { addStretch(STRETCH_STEP); });
     491            0 :     registerCommand(STRETCH_RESET_COMMAND, &Controller::resetStretch);
     492              : 
     493              :     // open properties
     494            0 :     registerCommand(OPEN_PAGE_SETTINGS_COMMAND, &Controller::openPageSettingsDialog);
     495            0 :     registerCommand(OPEN_STAFF_PROPERTIES_COMMAND, &Controller::openStaffProperties);
     496            0 :     registerCommand(OPEN_EDIT_STRINGS_COMMAND, &Controller::openEditStringsDialog);
     497            0 :     registerCommand(OPEN_BREAKS_COMMAND, &Controller::openBreaksDialog);
     498            0 :     registerCommand(OPEN_STAFF_TEXT_PROPERTIES_COMMAND, &Controller::openStaffTextPropertiesDialog);
     499            0 :     registerCommand(OPEN_SYSTEM_TEXT_PROPERTIES_COMMAND, &Controller::openStaffTextPropertiesDialog);
     500            0 :     registerCommand(OPEN_MEASURE_PROPERTIES_COMMAND, &Controller::openMeasurePropertiesDialog);
     501            0 :     registerCommand(OPEN_TRANSPOSE_COMMAND, &Controller::openTransposeDialog);
     502            0 :     registerCommand(OPEN_PARTS_COMMAND, &Controller::openPartsDialog);
     503            0 :     registerCommand(OPEN_EDITGRIDSIZE_COMMAND, &Controller::openEditGridSizeDialog);
     504            0 :     registerCommand(OPEN_REALIZECHORDSYMBOLS_COMMAND, &Controller::openRealizeChordSymbolsDialog);
     505              : 
     506              :     // style commands
     507            0 :     registerCommand(LOAD_STYLE_COMMAND, &Controller::loadStyle);
     508            0 :     registerCommand(SAVE_STYLE_COMMAND, &Controller::saveStyle);
     509            0 :     registerCommandWithParams(OPEN_EDIT_STYLE_COMMAND, &Controller::openEditStyleDialog);
     510            0 :     registerCommand(TOGGLE_CONCERT_PITCH_COMMAND, &Controller::toggleConcertPitch);
     511              : 
     512              :     // reset commands
     513            0 :     registerCommand(RESET_TEXT_STYLE_OVERRIDES_COMMAND, &Interaction::resetTextStyleOverrides);
     514            0 :     registerCommand(RESET_BEAMS_COMMAND, &Controller::resetBeamMode);
     515            0 :     registerCommand(RESET_SHAPES_AND_POSITIONS_COMMAND, &Interaction::resetShapesAndPosition);
     516            0 :     registerCommand(RESET_TO_DEFAULT_LAYOUT_COMMAND, &Interaction::resetToDefaultLayout);
     517              : 
     518              :     // appearance commands
     519            0 :     registerCommand(FREEZE_CURRENT_PLACEMENT_COMMAND, &Interaction::freezeSelectionPosition);
     520              : 
     521              :     // show commands
     522            0 :     registerCommand(SHOW_INVISIBLE_COMMAND, [this]() { toggleScoreConfig(ScoreConfigType::ShowInvisibleElements); });
     523            0 :     registerCommand(SHOW_UNPRINTABLE_COMMAND, [this]() { toggleScoreConfig(ScoreConfigType::ShowUnprintableElements); });
     524            0 :     registerCommand(SHOW_FRAMES_COMMAND, [this]() { toggleScoreConfig(ScoreConfigType::ShowFrames); });
     525            0 :     registerCommand(SHOW_PAGEBORDERS_COMMAND, [this]() { toggleScoreConfig(ScoreConfigType::ShowPageMargins); });
     526            0 :     registerCommand(SHOW_SOUNDFLAGS_COMMAND, [this]() { toggleScoreConfig(ScoreConfigType::ShowSoundFlags); });
     527            0 :     registerCommand(SHOW_IRREGULAR_COMMAND, [this]() { toggleScoreConfig(ScoreConfigType::MarkIrregularMeasures); });
     528              : 
     529              :     // staff commands
     530            0 :     registerCommand(STAFF_EXPLODE_COMMAND, &Interaction::explodeSelectedStaff);
     531            0 :     registerCommand(STAFF_IMPLODE_COMMAND, &Interaction::implodeSelectedStaff);
     532              : 
     533              :     // remove commands
     534            0 :     registerCommand(REMOVE_SELECTED_RANGE_COMMAND, &Interaction::removeSelectedRange);
     535            0 :     registerCommand(REMOVE_EMPTY_TRAILING_MEASURES_COMMAND, &Interaction::removeEmptyTrailingMeasures);
     536              : 
     537              :     // slash commands
     538            0 :     registerCommand(SLASH_FILL_COMMAND, &Interaction::fillSelectionWithSlashes);
     539            0 :     registerCommand(SLASH_RHYTHM_COMMAND, &Interaction::replaceSelectedNotesWithSlashes);
     540              : 
     541              :     // spelling commands
     542            0 :     registerCommand(PITCH_SPELL_COMMAND, &Interaction::spellPitches);
     543            0 :     registerCommand(PITCH_SPELL_SHARPS_COMMAND, &Interaction::spellPitchesWithSharps);
     544            0 :     registerCommand(PITCH_SPELL_FLATS_COMMAND, &Interaction::spellPitchesWithFlats);
     545            0 :     registerCommand(ENHARMONIC_SPELL_BOTH_COMMAND, &Interaction::changeEnharmonicSpelling, true);
     546            0 :     registerCommand(ENHARMONIC_SPELL_CURRENT_COMMAND, &Interaction::changeEnharmonicSpelling, false);
     547              : 
     548              :     // screen commands
     549            0 :     registerCommandWithParams(SCREEN_PUT_NOTE_COMMAND, &Controller::putNote);
     550            0 :     registerCommandWithParams(SCREEN_REMOVE_NOTE_COMMAND, &Controller::removeNote);
     551            0 :     registerCommandWithParams(SCREEN_EDIT_TEXT_COMMAND, &Controller::startEditSelectedText);
     552            0 :     registerCommandWithParams(SCREEN_EDIT_ELEMENT_COMMAND, &Controller::startEditSelectedElement);
     553              : 
     554            0 :     registerCommandWithParams(EDIT_ELEMENT_COMMAND, &Controller::startEditSelectedElement); // same as screen edit element command, no params
     555              : 
     556              :     // others commands
     557            0 :     registerCommand(REGROUP_RHYTHMS_COMMAND, &Interaction::regroupNotesAndRests);
     558            0 :     registerCommand(RESEQUENCE_REHEARSAL_MARKS_COMMAND, &Interaction::resequenceRehearsalMarks);
     559            0 :     registerCommand(UNROLL_REPEATS_COMMAND, &Controller::unrollRepeats);
     560            0 :     registerCommand(REPEAT_SELECTION_COMMAND, &Controller::repeatSelection);
     561            0 :     registerCommand(TRANSPOSE_UP_COMMAND, &Interaction::transposeSemitone, 1, PlayMode::PlayNote);
     562            0 :     registerCommand(TRANSPOSE_DOWN_COMMAND, &Interaction::transposeSemitone, -1, PlayMode::PlayNote);
     563            0 :     registerCommand(TOGGLE_MMREST_COMMAND, &Controller::toggleMmrest);
     564            0 :     registerCommand(TOGGLE_HIDE_EMPTY_COMMAND, &Controller::toggleHideEmpty);
     565            0 :     registerCommand(MIRROR_NOTEHEAD_COMMAND, &Interaction::mirrorNotes);
     566            0 :     registerCommand(SET_VISIBLE_COMMAND, &Interaction::setSelectionVisible, true);
     567            0 :     registerCommand(UNSET_VISIBLE_COMMAND, &Interaction::setSelectionVisible, false);
     568            0 :     registerCommand(TOGGLE_AUTOPLACE_COMMAND, &Interaction::toggleAutoplace, false);
     569            0 :     registerCommand(AUTOPLACE_ENABLED_COMMAND, &Interaction::toggleAutoplace, true);
     570              : 
     571            0 :     registerCommand(VOICE_ASSIGNMENT_ALL_IN_INSTR_COMMAND, &Interaction::changeSelectedElementsVoiceAssignment,
     572              :                     VoiceAssignment::ALL_VOICE_IN_INSTRUMENT);
     573            0 :     registerCommand(VOICE_ASSIGNMENT_ALL_IN_STAFF_COMMAND, &Interaction::changeSelectedElementsVoiceAssignment,
     574              :                     VoiceAssignment::ALL_VOICE_IN_STAFF);
     575              : 
     576            0 :     registerCommand(TOGGLE_AUTOMATION_COMMAND, &Controller::toggleAutomation);
     577            0 :     registerCommandWithParams(SELECT_AUTOMATION_TYPE_COMMAND, &Controller::selectAutomationType);
     578              : 
     579              :     // TAB
     580            0 :     registerCommand(SET_DURATION_WHOLE_TAB_COMMAND, [this]() { setDuration(DurationType::V_WHOLE); });
     581            0 :     registerCommand(SET_DURATION_HALF_TAB_COMMAND, [this]() { setDuration(DurationType::V_HALF); });
     582            0 :     registerCommand(SET_DURATION_QUARTER_TAB_COMMAND, [this]() { setDuration(DurationType::V_QUARTER); });
     583            0 :     registerCommand(SET_DURATION_EIGHTH_TAB_COMMAND, [this]() { setDuration(DurationType::V_EIGHTH); });
     584            0 :     registerCommand(SET_DURATION_16TH_TAB_COMMAND, [this]() { setDuration(DurationType::V_16TH); });
     585            0 :     registerCommand(SET_DURATION_32ND_TAB_COMMAND, [this]() { setDuration(DurationType::V_32ND); });
     586            0 :     registerCommand(SET_DURATION_64TH_TAB_COMMAND, [this]() { setDuration(DurationType::V_64TH); });
     587            0 :     registerCommand(SET_DURATION_128TH_TAB_COMMAND, [this]() { setDuration(DurationType::V_128TH); });
     588            0 :     registerCommand(SET_DURATION_256TH_TAB_COMMAND, [this]() { setDuration(DurationType::V_256TH); });
     589            0 :     registerCommand(SET_DURATION_512TH_TAB_COMMAND, [this]() { setDuration(DurationType::V_512TH); });
     590            0 :     registerCommand(SET_DURATION_1024TH_TAB_COMMAND, [this]() { setDuration(DurationType::V_1024TH); });
     591              : 
     592            0 :     registerCommand(ENTER_FRET_0_COMMAND, [this]() { addFret(0); });
     593            0 :     registerCommand(ENTER_FRET_1_COMMAND, [this]() { addFret(1); });
     594            0 :     registerCommand(ENTER_FRET_2_COMMAND, [this]() { addFret(2); });
     595            0 :     registerCommand(ENTER_FRET_3_COMMAND, [this]() { addFret(3); });
     596            0 :     registerCommand(ENTER_FRET_4_COMMAND, [this]() { addFret(4); });
     597            0 :     registerCommand(ENTER_FRET_5_COMMAND, [this]() { addFret(5); });
     598            0 :     registerCommand(ENTER_FRET_6_COMMAND, [this]() { addFret(6); });
     599            0 :     registerCommand(ENTER_FRET_7_COMMAND, [this]() { addFret(7); });
     600            0 :     registerCommand(ENTER_FRET_8_COMMAND, [this]() { addFret(8); });
     601            0 :     registerCommand(ENTER_FRET_9_COMMAND, [this]() { addFret(9); });
     602            0 :     registerCommand(ENTER_FRET_10_COMMAND, [this]() { addFret(10); });
     603            0 :     registerCommand(ENTER_FRET_11_COMMAND, [this]() { addFret(11); });
     604            0 :     registerCommand(ENTER_FRET_12_COMMAND, [this]() { addFret(12); });
     605            0 :     registerCommand(ENTER_FRET_13_COMMAND, [this]() { addFret(13); });
     606            0 :     registerCommand(ENTER_FRET_14_COMMAND, [this]() { addFret(14); });
     607              : 
     608            0 :     registerCommand(ENTER_REST_TAB_COMMAND, &Interaction::putRestToSelection);
     609              : 
     610            0 :     registerCommand(ADD_STANDARD_BEND_COMMAND, [this]() { addGuitarBend(GuitarBendType::BEND); });
     611            0 :     registerCommand(ADD_PRE_BEND_COMMAND, [this]() { addGuitarBend(GuitarBendType::PRE_BEND); });
     612            0 :     registerCommand(ADD_GRACE_NOTE_BEND_COMMAND, [this]() { addGuitarBend(GuitarBendType::GRACE_NOTE_BEND); });
     613            0 :     registerCommand(ADD_SLIGHT_BEND_COMMAND, [this]() { addGuitarBend(GuitarBendType::SLIGHT_BEND); });
     614            0 :     registerCommand(ADD_DIVE_COMMAND, [this]() { addGuitarBend(GuitarBendType::DIVE); });
     615            0 :     registerCommand(ADD_PRE_DIVE_COMMAND, [this]() { addGuitarBend(GuitarBendType::PRE_DIVE); });
     616            0 :     registerCommand(ADD_DIP_COMMAND, [this]() { addGuitarBend(GuitarBendType::DIP); });
     617            0 :     registerCommand(ADD_SCOOP_COMMAND, [this]() { addGuitarBend(GuitarBendType::SCOOP); });
     618              : 
     619            0 :     registerCommand(ADD_HAMMER_ON_PULL_OFF_COMMAND, [this]() { addHammerOnPullOff(); });
     620              : 
     621            0 :     registerCommand(GOTO_STRING_ABOVE_COMMAND, [this]() { move(MoveDirection::Up, false); });
     622            0 :     registerCommand(GOTO_STRING_BELOW_COMMAND, [this]() { move(MoveDirection::Down, false); });
     623              : 
     624              :     // view commands
     625            0 :     registerViewCommand(ZOOM_IN_COMMAND, &ViewController::zoomIn);
     626            0 :     registerViewCommand(ZOOM_OUT_COMMAND, &ViewController::zoomOut);
     627            0 :     registerViewCommand(ZOOM_TO_PAGE_WIDTH_COMMAND, &ViewController::zoomToPageWidth);
     628            0 :     registerViewCommand(ZOOM_TO_WHOLE_PAGE_COMMAND, &ViewController::zoomToWholePage);
     629            0 :     registerViewCommand(ZOOM_TO_TWO_PAGES_COMMAND, &ViewController::zoomToTwoPages);
     630            0 :     registerViewCommand(ZOOM_TO_100_COMMAND, &ViewController::setZoom, 100);
     631            0 :     registerCommandWithParams(ZOOM_TO_PERCENT_COMMAND, &Controller::zoomToPercent);
     632              : 
     633            0 :     registerViewCommand(VIEW_MODE_PAGE_COMMAND, &ViewController::setViewMode, ViewMode::PAGE);
     634            0 :     registerViewCommand(VIEW_MODE_FLOAT_COMMAND, &ViewController::setViewMode, ViewMode::FLOAT);
     635            0 :     registerViewCommand(VIEW_MODE_CONTINUOUS_COMMAND, &ViewController::setViewMode, ViewMode::LINE);
     636            0 :     registerViewCommand(VIEW_MODE_SINGLE_COMMAND, &ViewController::setViewMode, ViewMode::SYSTEM);
     637              : 
     638            0 :     registerViewCommand(NEXT_SCREEN_COMMAND, &ViewController::nextScreen);
     639            0 :     registerViewCommand(PREV_SCREEN_COMMAND, &ViewController::previousScreen);
     640            0 :     registerViewCommand(NEXT_PAGE_COMMAND, &ViewController::nextPage);
     641            0 :     registerViewCommand(PREV_PAGE_COMMAND, &ViewController::previousPage);
     642            0 :     registerViewCommand(TOP_OF_FIRST_PAGE_COMMAND, &ViewController::startOfScore);
     643            0 :     registerViewCommand(BOTTOM_OF_LAST_PAGE_COMMAND, &ViewController::endOfScore);
     644              : 
     645            0 :     registerViewCommand(CONTEXT_MENU_OF_SELECTION_COMMAND, &ViewController::openContextMenuOfSelection);
     646              : 
     647            0 :     registerViewCommand(SHOW_SEARCH_COMMAND, &ViewController::showSearch);
     648              : 
     649            0 :     registerCommandWithParams(PIANO_KEYBOARD_SET_NUMBER_OF_KEYS_COMMAND, &Controller::setPianoKeyboardNumberOfKeys);
     650              : 
     651              :     // diagnostic commands
     652            0 :     registerViewCommand(DIAGNOSTIC_VIEW_REDRAW_COMMAND, &ViewController::redrawView);
     653              : 
     654            0 :     registerCommand(CHECK_FOR_SCORE_CORRUPTIONS_COMMAND, &Controller::checkForScoreCorruptions);
     655              : 
     656              :     // debugging commands
     657            0 :     for (auto& [command, member] : s_debuggingCommands) {
     658            0 :         registerCommand(command, [this, member = member]() {
     659            0 :             EngravingDebuggingOptions options = engravingConfiguration()->debuggingOptions();
     660            0 :             options.*member = !(options.*member);
     661            0 :             engravingConfiguration()->setDebuggingOptions(options);
     662            0 :         });
     663              :     }
     664              : 
     665              :     // listen on state changes
     666            0 :     projectSession()->currentNotationChanged().onNotify(this, [this]() {
     667            0 :         auto notation = projectSession()->currentNotation();
     668            0 :         if (notation) {
     669            0 :             auto interaction = notation->interaction();
     670              : 
     671            0 :             interaction->selectionChanged().onNotify(this, [this]() {
     672            0 :                 m_selectionChanged.notify();
     673            0 :             }, Asyncable::Mode::SetReplace);
     674              : 
     675            0 :             interaction->noteInput()->stateChanged().onNotify(this, [this]() {
     676            0 :                 m_noteInputStateChanged.notify();
     677            0 :             }, Asyncable::Mode::SetReplace);
     678              : 
     679            0 :             interaction->textEditingStarted().onNotify(this, [this]() {
     680            0 :                 m_textEditingChanged.send(true);
     681            0 :             }, Asyncable::Mode::SetReplace);
     682            0 :             interaction->textEditingEnded().onReceive(this, [this](TextBase*) {
     683            0 :                 m_textEditingChanged.send(false);
     684            0 :             }, Asyncable::Mode::SetReplace);
     685              : 
     686            0 :             interaction->scoreConfigChanged().onReceive(this, [this](ScoreConfigType configType) {
     687            0 :                 m_scoreConfigChanged.send(configType);
     688            0 :             }, Asyncable::Mode::SetReplace);
     689              : 
     690            0 :             auto undoStack = notation->undoStack();
     691            0 :             undoStack->stackChanged().onNotify(this, [this]() {
     692            0 :                 m_stackChanged.notify();
     693            0 :             }, Asyncable::Mode::SetReplace);
     694              : 
     695            0 :             notation->style()->styleChanged().onNotify(this, [this]() {
     696            0 :                 m_currentNotationStyleChanged.notify();
     697            0 :             }, Asyncable::Mode::SetReplace);
     698              : 
     699            0 :             if (const IMasterNotationPtr masterNotation = notation->masterNotation()) {
     700            0 :                 masterNotation->automation()->automationModeEnabledChanged().onNotify(this, [this]() {
     701            0 :                     m_automationModeEnabledChanged.notify();
     702            0 :                 }, Asyncable::Mode::SetReplace);
     703            0 :             }
     704            0 :         }
     705              : 
     706            0 :         m_textEditingChanged.send(isTextEditing());
     707            0 :         m_noteInputStateChanged.notify();
     708            0 :         m_currentNotationStyleChanged.notify();
     709            0 :     });
     710              : 
     711            0 :     playbackController()->isPlayingChanged().onReceive(this, [this](bool) {
     712            0 :         m_isNoteInputAllowedChanged.send(isNoteInputAllowed());
     713            0 :     }, Asyncable::Mode::SetReplace);
     714            0 : }
     715              : 
     716            0 : void NotationActionController::setViewController(INotationViewController* controller)
     717              : {
     718            0 :     m_viewController = controller;
     719            0 :     LOGD() << "view controller changed";
     720            0 : }
     721              : 
     722            0 : INotationViewController* NotationActionController::viewController() const
     723              : {
     724            0 :     return m_viewController;
     725              : }
     726              : 
     727            0 : INotationPtr NotationActionController::currentNotation() const
     728              : {
     729            0 :     return projectSession()->currentNotation();
     730              : }
     731              : 
     732            0 : IMasterNotationPtr NotationActionController::currentMasterNotation() const
     733              : {
     734            0 :     return projectSession()->masterNotation();
     735              : }
     736              : 
     737            0 : INotationInteractionPtr NotationActionController::currentNotationInteraction() const
     738              : {
     739            0 :     return currentNotation() ? currentNotation()->interaction() : nullptr;
     740              : }
     741              : 
     742            0 : INotationSelectionPtr NotationActionController::currentNotationSelection() const
     743              : {
     744            0 :     return currentNotationInteraction() ? currentNotationInteraction()->selection() : nullptr;
     745              : }
     746              : 
     747            0 : INotationElementsPtr NotationActionController::currentNotationElements() const
     748              : {
     749            0 :     return currentNotation() ? currentNotation()->elements() : nullptr;
     750              : }
     751              : 
     752            0 : muse::async::Notification NotationActionController::currentNotationChanged() const
     753              : {
     754            0 :     return projectSession()->currentNotationChanged();
     755              : }
     756              : 
     757            0 : muse::async::Notification NotationActionController::currentMasterNotationChanged() const
     758              : {
     759            0 :     return projectSession()->masterNotationChanged();
     760              : }
     761              : 
     762            0 : INotationNoteInputPtr NotationActionController::currentNotationNoteInput() const
     763              : {
     764            0 :     auto interaction = currentNotationInteraction();
     765            0 :     if (!interaction) {
     766            0 :         return nullptr;
     767              :     }
     768              : 
     769            0 :     return interaction->noteInput();
     770            0 : }
     771              : 
     772            0 : INotationUndoStackPtr NotationActionController::currentNotationUndoStack() const
     773              : {
     774            0 :     auto notation = currentNotation();
     775            0 :     if (!notation) {
     776            0 :         return nullptr;
     777              :     }
     778              : 
     779            0 :     return notation->undoStack();
     780            0 : }
     781              : 
     782            0 : INotationMidiInputPtr NotationActionController::currentNotationMidiInput() const
     783              : {
     784            0 :     auto notation = currentNotation();
     785            0 :     if (!notation) {
     786            0 :         return nullptr;
     787              :     }
     788              : 
     789            0 :     return notation->midiInput();
     790            0 : }
     791              : 
     792            0 : mu::engraving::Score* NotationActionController::currentNotationScore() const
     793              : {
     794            0 :     return currentNotationElements() ? currentNotationElements()->msScore() : nullptr;
     795              : }
     796              : 
     797            0 : INotationStylePtr NotationActionController::notationStyle() const
     798              : {
     799            0 :     auto notation = currentNotation();
     800            0 :     if (!notation) {
     801            0 :         return nullptr;
     802              :     }
     803              : 
     804            0 :     return notation->style();
     805            0 : }
     806              : 
     807            0 : muse::async::Notification NotationActionController::notationStyleChanged() const
     808              : {
     809            0 :     return m_currentNotationStyleChanged;
     810              : }
     811              : 
     812            0 : void NotationActionController::resetState()
     813              : {
     814            0 :     TRACEFUNC;
     815              : 
     816            0 :     if (playbackController()->isPlaying()) {
     817            0 :         commandDispatcher()->dispatch(playback::STOP_COMMAND);
     818              :     }
     819              : 
     820            0 :     auto noteInput = currentNotationNoteInput();
     821            0 :     if (!noteInput) {
     822            0 :         return;
     823              :     }
     824              : 
     825            0 :     if (noteInput->isNoteInputMode()) {
     826            0 :         noteInput->endNoteInput();
     827            0 :         return;
     828              :     }
     829              : 
     830            0 :     auto interaction = currentNotationInteraction();
     831            0 :     if (!interaction) {
     832            0 :         return;
     833              :     }
     834              : 
     835            0 :     if (interaction->isGripEditStarted()) {
     836              :         // Exit grip edit but leave element selected
     837            0 :         interaction->endEditElement();
     838            0 :         return;
     839              :     }
     840              : 
     841            0 :     if (interaction->isTextEditingStarted()) {
     842            0 :         interaction->endEditElement();
     843            0 :         return;
     844            0 :     } else if (interaction->isEditingElement()) {
     845            0 :         interaction->endEditElement();
     846              :     }
     847              : 
     848            0 :     if (!interaction->selection()->isNone()) {
     849            0 :         interaction->clearSelection();
     850              :     }
     851              : 
     852            0 :     if (automation()) {
     853            0 :         automation()->resetEditStateRequested().notify();
     854              :     }
     855            0 : }
     856              : 
     857            0 : bool NotationActionController::isNoteInputAllowed() const
     858              : {
     859            0 :     return !playbackController()->isPlaying();
     860              : }
     861              : 
     862            0 : muse::async::Channel<bool> NotationActionController::isNoteInputAllowedChanged() const
     863              : {
     864            0 :     return m_isNoteInputAllowedChanged;
     865              : }
     866              : 
     867            0 : muse::async::Notification NotationActionController::noteInputStateChanged() const
     868              : {
     869            0 :     return m_noteInputStateChanged;
     870              : }
     871              : 
     872            0 : bool NotationActionController::isNoteInputMode() const
     873              : {
     874            0 :     auto noteInput = currentNotationNoteInput();
     875            0 :     if (!noteInput) {
     876            0 :         return false;
     877              :     }
     878              : 
     879            0 :     return noteInput->isNoteInputMode();
     880            0 : }
     881              : 
     882            0 : NoteInputMethod NotationActionController::noteInputMethod() const
     883              : {
     884            0 :     auto noteInput = currentNotationNoteInput();
     885            0 :     if (!noteInput) {
     886            0 :         return NoteInputMethod::UNKNOWN;
     887              :     }
     888              : 
     889            0 :     return noteInput->state().noteEntryMethod();
     890            0 : }
     891              : 
     892            0 : void NotationActionController::toggleNoteInput(NoteInputMethod method)
     893              : {
     894            0 :     TRACEFUNC;
     895              : 
     896            0 :     INotationNoteInputPtr noteInput = currentNotationNoteInput();
     897            0 :     if (!noteInput) {
     898            0 :         return;
     899              :     }
     900              : 
     901            0 :     if (method == NoteInputMethod::UNKNOWN) {
     902            0 :         method = configuration()->defaultNoteInputMethod();
     903              :     }
     904              : 
     905            0 :     if (!noteInput->isNoteInputMode()) {
     906            0 :         noteInput->startNoteInput(method);
     907            0 :     } else if (noteInput->usingNoteInputMethod(method)) {
     908            0 :         noteInput->endNoteInput();
     909              :     } else {
     910            0 :         noteInput->setNoteInputMethod(method);
     911              :     }
     912            0 : }
     913              : 
     914            0 : void NotationActionController::toggleNoteInputInsert()
     915              : {
     916            0 :     if (!currentNotationNoteInput()->usingNoteInputMethod(NoteInputMethod::TIMEWISE)) {
     917            0 :         toggleNoteInput(NoteInputMethod::TIMEWISE);
     918              :     } else {
     919            0 :         toggleNoteInput(NoteInputMethod::BY_NOTE_NAME);
     920              :     }
     921            0 : }
     922              : 
     923            0 : muse::RetVal<GetNoteInputStateCommand::Output> NotationActionController::getNoteInputState() const
     924              : {
     925            0 :     INotationNoteInputPtr noteInput = currentNotationNoteInput();
     926            0 :     if (!noteInput) {
     927            0 :         return muse::RetVal<GetNoteInputStateCommand::Output>::make_ret(Ret::Code::InternalError);
     928              :     }
     929              : 
     930            0 :     GetNoteInputStateCommand::Output output;
     931            0 :     output.enabled = noteInput->isNoteInputMode();
     932            0 :     if (output.enabled) {
     933            0 :         const NoteInputState& state = noteInput->state();
     934            0 :         output.method = state.noteEntryMethod();
     935              :     }
     936              : 
     937            0 :     return muse::RetVal<GetNoteInputStateCommand::Output>::make_ok(output);
     938            0 : }
     939              : 
     940            0 : muse::Ret NotationActionController::flipSelection()
     941              : {
     942            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
     943            0 :     if (!interaction) {
     944            0 :         return muse::make_ret(Ret::Code::InternalError);
     945              :     }
     946              : 
     947            0 :     interaction->flipSelection();
     948            0 :     return muse::make_ok();
     949            0 : }
     950              : 
     951            0 : muse::Ret NotationActionController::addNote(const AddNoteCommand& command)
     952              : {
     953            0 :     addNote(command.note, command.mode);
     954            0 :     return muse::make_ok();
     955              : }
     956              : 
     957            0 : void NotationActionController::addNote(NoteName note, NoteAddingMode mode)
     958              : {
     959            0 :     NoteInputParams params;
     960            0 :     const bool addFlag = mode == NoteAddingMode::CurrentChord;
     961            0 :     bool ok = mu::engraving::NoteInput::resolveNoteInputParams(currentNotationScore(), static_cast<int>(note), addFlag, params);
     962            0 :     if (!ok) {
     963            0 :         LOGE() << "Could not resolve note input params, note: " << (int)note << ", addFlag: " << addFlag;
     964            0 :         return;
     965              :     }
     966              : 
     967            0 :     doAddNote(params, mode);
     968              : }
     969              : 
     970            0 : muse::Ret NotationActionController::addDrumNote(const muse::rcommand::Params& params)
     971              : {
     972            0 :     TRACEFUNC;
     973              : 
     974            0 :     int pitch = params.at("pitch").toInt();
     975            0 :     NoteAddingMode mode = Contract<NoteAddingMode>::get(params);
     976              : 
     977            0 :     NoteInputParams noteInputParams;
     978            0 :     noteInputParams.drumPitch = pitch;
     979              : 
     980            0 :     doAddNote(noteInputParams, mode);
     981              : 
     982            0 :     return muse::make_ok();
     983            0 : }
     984              : 
     985            0 : void NotationActionController::doAddNote(const NoteInputParams& params, const NoteAddingMode& addingMode)
     986              : {
     987            0 :     TRACEFUNC;
     988              : 
     989            0 :     INotationNoteInputPtr noteInput = currentNotationNoteInput();
     990            0 :     if (!noteInput) {
     991            0 :         return;
     992              :     }
     993              : 
     994            0 :     startNoteInput();
     995              : 
     996            0 :     if (addingMode == NoteAddingMode::NextChord) {
     997            0 :         if (noteInput->usingNoteInputMethod(NoteInputMethod::BY_DURATION)) {
     998            0 :             noteInput->setRestMode(false);
     999            0 :             noteInput->setInputNote(params);
    1000              : 
    1001            0 :             if (configuration()->isPlayPreviewNotesInInputByDuration()) {
    1002            0 :                 const NoteInputState& state = noteInput->state();
    1003            0 :                 playbackController()->playNotes(state.notes(), state.staffIdx(), state.segment());
    1004              :             }
    1005            0 :             return;
    1006              :         }
    1007              :     }
    1008              : 
    1009            0 :     noteInput->addNote(params, addingMode);
    1010              : 
    1011            0 :     seekAndPlaySelectedElement();
    1012            0 : }
    1013              : 
    1014            0 : void NotationActionController::setDuration(DurationType duration)
    1015              : {
    1016            0 :     TRACEFUNC;
    1017              : 
    1018            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
    1019            0 :     if (!interaction) {
    1020            0 :         return;
    1021              :     }
    1022              : 
    1023            0 :     INotationNoteInputPtr noteInput = interaction->noteInput();
    1024            0 :     if (interaction->selection()->isNone()) {
    1025            0 :         if (!noteInput->isNoteInputMode() && !toggleNoteInputAllowed()) {
    1026            0 :             return;
    1027              :         }
    1028              : 
    1029            0 :         startNoteInput();
    1030              :     }
    1031            0 :     if (noteInput->isNoteInputMode() && !noteInput->state().isValid()) {
    1032            0 :         return;
    1033              :     }
    1034              : 
    1035            0 :     noteInput->setDuration(duration);
    1036              : 
    1037            0 :     if (noteInput->usingNoteInputMethod(NoteInputMethod::BY_DURATION)
    1038            0 :         || noteInput->usingNoteInputMethod(NoteInputMethod::RHYTHM)) {
    1039            0 :         seekAndPlaySelectedElement();
    1040              :     }
    1041            0 : }
    1042              : 
    1043            0 : void NotationActionController::toggleRest()
    1044              : {
    1045            0 :     TRACEFUNC;
    1046              : 
    1047            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
    1048            0 :     if (!interaction) {
    1049            0 :         return;
    1050              :     }
    1051              : 
    1052            0 :     INotationNoteInputPtr noteInput = interaction->noteInput();
    1053            0 :     if (interaction->selection()->isNone()) {
    1054            0 :         if (!noteInput->isNoteInputMode() && !toggleNoteInputAllowed()) {
    1055            0 :             return;
    1056              :         }
    1057              : 
    1058            0 :         startNoteInput();
    1059              :     }
    1060              : 
    1061            0 :     noteInput->toggleRest();
    1062              : 
    1063            0 :     if (noteInput->usingNoteInputMethod(NoteInputMethod::BY_DURATION)
    1064            0 :         || noteInput->usingNoteInputMethod(NoteInputMethod::RHYTHM)) {
    1065            0 :         seekSelectedElement();
    1066              :     }
    1067            0 : }
    1068              : 
    1069            0 : void NotationActionController::toggleDots(int dots)
    1070              : {
    1071            0 :     TRACEFUNC;
    1072              : 
    1073            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
    1074            0 :     if (!interaction) {
    1075            0 :         return;
    1076              :     }
    1077              : 
    1078            0 :     INotationNoteInputPtr noteInput = interaction->noteInput();
    1079            0 :     if (interaction->selection()->isNone()) {
    1080            0 :         if (!noteInput->isNoteInputMode() && !toggleNoteInputAllowed()) {
    1081            0 :             return;
    1082              :         }
    1083              : 
    1084            0 :         startNoteInput();
    1085              :     }
    1086              : 
    1087            0 :     if (!noteInput->isNoteInputMode() || !configuration()->addAccidentalDotsArticulationsToNextNoteEntered()) {
    1088            0 :         interaction->toggleDotsForSelection(dots);
    1089            0 :         return;
    1090              :     }
    1091              : 
    1092            0 :     noteInput->toggleDots(dots);
    1093              : 
    1094            0 :     if (noteInput->usingNoteInputMethod(NoteInputMethod::BY_DURATION)
    1095            0 :         || noteInput->usingNoteInputMethod(NoteInputMethod::RHYTHM)) {
    1096            0 :         seekAndPlaySelectedElement();
    1097              :     }
    1098            0 : }
    1099              : 
    1100            0 : DurationType NotationActionController::currentDurationType() const
    1101              : {
    1102            0 :     constexpr DurationType INVALID_DURATION_TYPE = DurationType::V_INVALID;
    1103              : 
    1104            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
    1105            0 :     if (!interaction) {
    1106            0 :         return INVALID_DURATION_TYPE;
    1107              :     }
    1108              : 
    1109            0 :     INotationNoteInputPtr noteInput = interaction->noteInput();
    1110            0 :     if (!noteInput) {
    1111            0 :         return INVALID_DURATION_TYPE;
    1112              :     }
    1113              : 
    1114            0 :     INotationSelectionPtr selection = interaction->selection();
    1115            0 :     if (!selection) {
    1116            0 :         return INVALID_DURATION_TYPE;
    1117              :     }
    1118              : 
    1119            0 :     if (noteInput->isNoteInputMode()) {
    1120            0 :         return noteInput->state().duration().type();
    1121              :     }
    1122              : 
    1123            0 :     if (selection->isNone() || selection->isRange()) {
    1124            0 :         return INVALID_DURATION_TYPE;
    1125              :     }
    1126              : 
    1127            0 :     const std::vector<EngravingItem*>& selectedElements = selection->elements();
    1128            0 :     if (selectedElements.empty()) {
    1129            0 :         return INVALID_DURATION_TYPE;
    1130              :     }
    1131              : 
    1132            0 :     auto elementToChordRest = [](const EngravingItem* element) -> const ChordRest* {
    1133            0 :         if (!element) {
    1134            0 :             return nullptr;
    1135              :         }
    1136            0 :         if (element->isChordRest()) {
    1137            0 :             return toChordRest(element);
    1138              :         }
    1139            0 :         if (element->isNote()) {
    1140            0 :             return toNote(element)->chord();
    1141              :         }
    1142            0 :         if (element->isStem()) {
    1143            0 :             return toStem(element)->chord();
    1144              :         }
    1145            0 :         if (element->isHook()) {
    1146            0 :             return toHook(element)->chord();
    1147              :         }
    1148            0 :         return nullptr;
    1149              :     };
    1150              : 
    1151            0 :     DurationType result = INVALID_DURATION_TYPE;
    1152            0 :     bool isFirstElement = true;
    1153            0 :     for (const EngravingItem* element: selectedElements) {
    1154            0 :         const ChordRest* chordRest = elementToChordRest(element);
    1155            0 :         if (!chordRest) {
    1156            0 :             continue;
    1157              :         }
    1158              : 
    1159            0 :         if (isFirstElement) {
    1160            0 :             result = chordRest->durationType().type();
    1161            0 :             isFirstElement = false;
    1162            0 :         } else if (result != chordRest->durationType().type()) {
    1163            0 :             return INVALID_DURATION_TYPE;
    1164              :         }
    1165              :     }
    1166              : 
    1167            0 :     return result;
    1168            0 : }
    1169              : 
    1170            0 : int NotationActionController::currentDotCount() const
    1171              : {
    1172            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
    1173            0 :     if (!interaction) {
    1174            0 :         return 0;
    1175              :     }
    1176              : 
    1177            0 :     return interaction->noteInput()->state().duration().dots();
    1178            0 : }
    1179              : 
    1180            0 : bool NotationActionController::currentIsRest() const
    1181              : {
    1182            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
    1183            0 :     if (!interaction) {
    1184            0 :         return false;
    1185              :     }
    1186              : 
    1187            0 :     INotationNoteInputPtr noteInput = interaction->noteInput();
    1188            0 :     if (!noteInput) {
    1189            0 :         return false;
    1190              :     }
    1191              : 
    1192            0 :     if (noteInput->isNoteInputMode()) {
    1193            0 :         return noteInput->state().rest();
    1194              :     }
    1195              : 
    1196            0 :     INotationSelectionPtr selection = interaction->selection();
    1197            0 :     if (!selection) {
    1198            0 :         return false;
    1199              :     }
    1200              : 
    1201            0 :     if (selection->isNone() || selection->isRange()) {
    1202            0 :         return false;
    1203              :     }
    1204              : 
    1205            0 :     for (const EngravingItem* element: selection->elements()) {
    1206            0 :         if (!element->isRest()) {
    1207            0 :             return false;
    1208              :         }
    1209              :     }
    1210              : 
    1211            0 :     return true;
    1212            0 : }
    1213              : 
    1214            0 : AccidentalType NotationActionController::currentAccidentalType() const
    1215              : {
    1216            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
    1217            0 :     if (!interaction) {
    1218            0 :         return AccidentalType::NONE;
    1219              :     }
    1220              : 
    1221            0 :     return interaction->noteInput()->state().accidentalType();
    1222            0 : }
    1223              : 
    1224            0 : std::set<SymbolId> NotationActionController::currentArticulations() const
    1225              : {
    1226            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
    1227            0 :     if (!interaction) {
    1228            0 :         return {};
    1229              :     }
    1230              : 
    1231            0 :     INotationNoteInputPtr noteInput = interaction->noteInput();
    1232            0 :     if (!noteInput) {
    1233            0 :         return {};
    1234              :     }
    1235              : 
    1236            0 :     if (noteInput->isNoteInputMode()) {
    1237            0 :         return mu::engraving::splitArticulations(noteInput->state().articulationIds());
    1238              :     }
    1239              : 
    1240            0 :     INotationSelectionPtr selection = interaction->selection();
    1241            0 :     if (!selection) {
    1242            0 :         return {};
    1243              :     }
    1244              : 
    1245            0 :     if (selection->isNone()) {
    1246            0 :         return {};
    1247              :     }
    1248              : 
    1249            0 :     auto chordArticulations = [](const Chord* chord) {
    1250            0 :         std::set<SymbolId> result;
    1251            0 :         for (Articulation* articulation: chord->articulations()) {
    1252            0 :             result.insert(articulation->symId());
    1253              :         }
    1254              : 
    1255            0 :         result = mu::engraving::flipArticulations(result, mu::engraving::PlacementV::ABOVE);
    1256            0 :         return mu::engraving::splitArticulations(result);
    1257            0 :     };
    1258              : 
    1259            0 :     std::set<SymbolId> result;
    1260            0 :     bool isFirstNote = true;
    1261            0 :     for (const EngravingItem* element: selection->elements()) {
    1262            0 :         if (!element->isNote()) {
    1263            0 :             continue;
    1264              :         }
    1265              : 
    1266            0 :         const Note* note = toNote(element);
    1267            0 :         if (isFirstNote) {
    1268            0 :             result = chordArticulations(note->chord());
    1269            0 :             isFirstNote = false;
    1270              :         } else {
    1271            0 :             std::set<SymbolId> currentNoteArticulations = chordArticulations(note->chord());
    1272            0 :             for (auto it = result.begin(); it != result.end();) {
    1273            0 :                 if (std::find(currentNoteArticulations.begin(), currentNoteArticulations.end(),
    1274            0 :                               *it) == currentNoteArticulations.end()) {
    1275            0 :                     it = result.erase(it);
    1276              :                 } else {
    1277            0 :                     ++it;
    1278              :                 }
    1279              :             }
    1280            0 :         }
    1281              :     }
    1282              : 
    1283            0 :     return result;
    1284            0 : }
    1285              : 
    1286            0 : voice_idx_t NotationActionController::currentVoice() const
    1287              : {
    1288            0 :     constexpr voice_idx_t INVALID_VOICE = muse::nidx;
    1289              : 
    1290            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
    1291            0 :     if (!interaction) {
    1292            0 :         return INVALID_VOICE;
    1293              :     }
    1294              : 
    1295            0 :     INotationNoteInputPtr noteInput = interaction->noteInput();
    1296            0 :     if (!noteInput) {
    1297            0 :         return INVALID_VOICE;
    1298              :     }
    1299              : 
    1300            0 :     if (noteInput->isNoteInputMode()) {
    1301            0 :         return noteInput->state().voice();
    1302              :     }
    1303              : 
    1304            0 :     INotationSelectionPtr selection = interaction->selection();
    1305            0 :     if (!selection) {
    1306            0 :         return INVALID_VOICE;
    1307              :     }
    1308              : 
    1309            0 :     if (selection->isNone()) {
    1310            0 :         return INVALID_VOICE;
    1311              :     }
    1312              : 
    1313            0 :     const std::vector<EngravingItem*>& selectedElements = selection->elements();
    1314            0 :     if (selectedElements.empty()) {
    1315            0 :         return INVALID_VOICE;
    1316              :     }
    1317              : 
    1318            0 :     voice_idx_t voice = INVALID_VOICE;
    1319            0 :     for (const EngravingItem* element : selectedElements) {
    1320            0 :         if (element->hasVoiceAssignmentProperties()) {
    1321            0 :             VoiceAssignment voiceAssignment = element->getProperty(Pid::VOICE_ASSIGNMENT).value<VoiceAssignment>();
    1322            0 :             if (voiceAssignment == VoiceAssignment::ALL_VOICE_IN_INSTRUMENT || voiceAssignment == VoiceAssignment::ALL_VOICE_IN_STAFF) {
    1323            0 :                 return INVALID_VOICE;
    1324              :             }
    1325              :         }
    1326            0 :         voice_idx_t elementVoice = element->voice();
    1327            0 :         if (elementVoice != voice && voice != INVALID_VOICE) {
    1328            0 :             return INVALID_VOICE;
    1329              :         }
    1330              : 
    1331            0 :         voice = elementVoice;
    1332              :     }
    1333              : 
    1334            0 :     return voice;
    1335            0 : }
    1336              : 
    1337            0 : muse::async::Notification NotationActionController::selectionChanged() const
    1338              : {
    1339            0 :     return m_selectionChanged;
    1340              : }
    1341              : 
    1342            0 : bool NotationActionController::selectionHasTie() const
    1343              : {
    1344            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
    1345            0 :     if (!interaction) {
    1346            0 :         return false;
    1347              :     }
    1348              : 
    1349            0 :     std::vector<Note*> tiedNotes = interaction->selection()->notes(NoteFilter::WithTie);
    1350            0 :     if (tiedNotes.empty()) {
    1351            0 :         return false;
    1352              :     }
    1353              : 
    1354            0 :     bool hasTie = true;
    1355            0 :     for (const Note* note: tiedNotes) {
    1356            0 :         if (!note->tieFor()) {
    1357            0 :             hasTie = false;
    1358            0 :             break;
    1359              :         }
    1360            0 :         if (note->laissezVib()) {
    1361            0 :             hasTie = false;
    1362            0 :             break;
    1363              :         }
    1364              :     }
    1365              : 
    1366            0 :     return hasTie;
    1367            0 : }
    1368              : 
    1369            0 : bool NotationActionController::selectionHasLaissezVib() const
    1370              : {
    1371            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
    1372            0 :     if (!interaction) {
    1373            0 :         return false;
    1374              :     }
    1375              : 
    1376            0 :     std::vector<Note*> tiedNotes = interaction->selection()->notes(NoteFilter::WithTie);
    1377            0 :     if (tiedNotes.empty()) {
    1378            0 :         return false;
    1379              :     }
    1380              : 
    1381            0 :     bool hasLaissezVib = true;
    1382            0 :     for (const Note* note: tiedNotes) {
    1383            0 :         if (!note->laissezVib()) {
    1384            0 :             hasLaissezVib = false;
    1385            0 :             break;
    1386              :         }
    1387              :     }
    1388            0 :     return hasLaissezVib;
    1389            0 : }
    1390              : 
    1391            0 : bool NotationActionController::selectionHasSlur() const
    1392              : {
    1393            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
    1394            0 :     if (!interaction) {
    1395            0 :         return false;
    1396              :     }
    1397              : 
    1398            0 :     bool hasSlur = interaction->noteInput()->state().slur() != nullptr;
    1399            0 :     return hasSlur;
    1400            0 : }
    1401              : 
    1402            0 : muse::Ret NotationActionController::putNote(const muse::rcommand::Params& params)
    1403              : {
    1404            0 :     TRACEFUNC;
    1405              : 
    1406            0 :     INotationNoteInputPtr noteInput = currentNotationNoteInput();
    1407            0 :     if (!noteInput) {
    1408            0 :         return muse::make_ret(Ret::Code::NotSupported);
    1409              :     }
    1410              : 
    1411            0 :     IF_ASSERT_FAILED(params.contains("pos_x") && params.contains("pos_y")) {
    1412            0 :         return muse::make_ret(Ret::Code::BadArgs);
    1413              :     }
    1414              : 
    1415            0 :     float posX = params.at("pos_x").toFloat();
    1416            0 :     float posY = params.at("pos_y").toFloat();
    1417            0 :     bool replace = params.at("replace", Val(false)).toBool();
    1418            0 :     bool insert = params.at("insert", Val(false)).toBool();
    1419              : 
    1420            0 :     PointF pos = PointF(posX, posY);
    1421              : 
    1422            0 :     Ret ret = noteInput->putNote(pos, replace, insert);
    1423            0 :     if (ret) {
    1424            0 :         seekAndPlaySelectedElement();
    1425              :     }
    1426              : 
    1427            0 :     return muse::make_ok();
    1428            0 : }
    1429              : 
    1430            0 : muse::Ret NotationActionController::removeNote(const muse::rcommand::Params& params)
    1431              : {
    1432            0 :     TRACEFUNC;
    1433              : 
    1434            0 :     INotationNoteInputPtr noteInput = currentNotationNoteInput();
    1435            0 :     if (!noteInput) {
    1436            0 :         return muse::make_ret(Ret::Code::NotSupported);
    1437              :     }
    1438              : 
    1439            0 :     IF_ASSERT_FAILED(params.contains("pos_x") && params.contains("pos_y")) {
    1440            0 :         return muse::make_ret(Ret::Code::BadArgs);
    1441              :     }
    1442              : 
    1443            0 :     float posX = params.at("pos_x").toFloat();
    1444            0 :     float posY = params.at("pos_y").toFloat();
    1445              : 
    1446            0 :     PointF pos = PointF(posX, posY);
    1447            0 :     noteInput->removeNote(pos);
    1448            0 :     seekSelectedElement();
    1449              : 
    1450            0 :     return muse::make_ok();
    1451            0 : }
    1452              : 
    1453            0 : void NotationActionController::toggleAccidental(AccidentalType type)
    1454              : {
    1455            0 :     TRACEFUNC;
    1456              : 
    1457            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
    1458            0 :     if (!interaction) {
    1459            0 :         return;
    1460              :     }
    1461              : 
    1462            0 :     INotationNoteInputPtr noteInput = interaction->noteInput();
    1463            0 :     if (!noteInput) {
    1464            0 :         return;
    1465              :     }
    1466              : 
    1467            0 :     if (interaction->selection()->isNone()) {
    1468            0 :         if (!noteInput->isNoteInputMode() && !toggleNoteInputAllowed()) {
    1469            0 :             return;
    1470              :         }
    1471              : 
    1472            0 :         startNoteInput();
    1473              :     }
    1474              : 
    1475            0 :     if (noteInput->isNoteInputMode() && configuration()->addAccidentalDotsArticulationsToNextNoteEntered()) {
    1476            0 :         noteInput->setAccidental(type);
    1477              :     } else {
    1478            0 :         interaction->toggleAccidentalForSelection(type);
    1479            0 :         seekAndPlaySelectedElement();
    1480              :     }
    1481            0 : }
    1482              : 
    1483            0 : void NotationActionController::toggleArticulation(SymbolId articulationSymbolId)
    1484              : {
    1485            0 :     TRACEFUNC;
    1486              : 
    1487            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
    1488            0 :     if (!interaction) {
    1489            0 :         return;
    1490              :     }
    1491              : 
    1492            0 :     INotationNoteInputPtr noteInput = interaction->noteInput();
    1493            0 :     if (!noteInput) {
    1494            0 :         return;
    1495              :     }
    1496              : 
    1497            0 :     if (interaction->selection()->isNone()) {
    1498            0 :         if (!noteInput->isNoteInputMode() && !toggleNoteInputAllowed()) {
    1499            0 :             return;
    1500              :         }
    1501              : 
    1502            0 :         startNoteInput();
    1503              :     }
    1504              : 
    1505            0 :     if (noteInput->isNoteInputMode() && configuration()->addAccidentalDotsArticulationsToNextNoteEntered()) {
    1506            0 :         noteInput->setArticulation(articulationSymbolId);
    1507              :     } else {
    1508            0 :         interaction->toggleArticulationForSelection(articulationSymbolId);
    1509              :     }
    1510            0 : }
    1511              : 
    1512            0 : muse::Ret NotationActionController::putTuplet(const muse::rcommand::Params& params)
    1513              : {
    1514            0 :     TupletOptions options;
    1515            0 :     options.ratio = engraving::Fraction::fromString(muse::String::fromStdString(params.at("ratio").toString()));
    1516            0 :     options.numberType = Contract<TupletNumberType>::get(params);
    1517            0 :     options.bracketType = Contract<TupletBracketType>::get(params);
    1518            0 :     options.autoBaseLen = params.at("auto-baselen", Val(false)).toBool();
    1519            0 :     options.selectedDurationAs = Contract<TupletSelectedDuration>::get(params);
    1520              : 
    1521            0 :     putTuplet(options);
    1522            0 :     return muse::make_ok();
    1523              : }
    1524              : 
    1525            0 : void NotationActionController::putTuplet(const TupletOptions& options)
    1526              : {
    1527            0 :     TRACEFUNC;
    1528            0 :     auto interaction = currentNotationInteraction();
    1529            0 :     if (!interaction) {
    1530            0 :         return;
    1531              :     }
    1532              : 
    1533            0 :     auto noteInput = interaction->noteInput();
    1534            0 :     if (!noteInput) {
    1535            0 :         return;
    1536              :     }
    1537              : 
    1538            0 :     if (noteInput->isNoteInputMode()) {
    1539            0 :         noteInput->addTuplet(options);
    1540              :     } else {
    1541            0 :         interaction->addTupletToSelectedChordRests(options);
    1542              :     }
    1543            0 : }
    1544              : 
    1545            0 : void NotationActionController::putTuplet(int tupletCount)
    1546              : {
    1547            0 :     TRACEFUNC;
    1548              : 
    1549            0 :     TupletOptions options;
    1550            0 :     options.ratio.setNumerator(tupletCount);
    1551            0 :     options.ratio.setDenominator(2);
    1552            0 :     options.autoBaseLen = true;
    1553              :     // get the bracket type from score style settings
    1554            0 :     if (INotationStylePtr style = notationStyle()) {
    1555            0 :         int bracketType = style->styleValue(StyleId::tupletBracketType).toInt();
    1556            0 :         options.bracketType = static_cast<engraving::TupletBracketType>(bracketType);
    1557            0 :         int numberType = style->styleValue(StyleId::tupletNumberType).toInt();
    1558            0 :         options.numberType = static_cast<engraving::TupletNumberType>(numberType);
    1559            0 :     }
    1560              : 
    1561            0 :     putTuplet(options);
    1562            0 : }
    1563              : 
    1564            0 : void NotationActionController::increaseDecreaseDuration(int steps, bool stepByDots)
    1565              : {
    1566            0 :     TRACEFUNC;
    1567              : 
    1568            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
    1569            0 :     if (!interaction) {
    1570            0 :         return;
    1571              :     }
    1572              : 
    1573            0 :     INotationNoteInputPtr noteInput = interaction->noteInput();
    1574              : 
    1575            0 :     if (noteInput->isNoteInputMode()) {
    1576            0 :         if (steps > 0) {
    1577            0 :             noteInput->halveNoteInputDuration();
    1578              :         } else {
    1579            0 :             noteInput->doubleNoteInputDuration();
    1580              :         }
    1581              :     } else {
    1582            0 :         interaction->increaseDecreaseDuration(steps, stepByDots);
    1583              :     }
    1584            0 : }
    1585              : 
    1586            0 : void NotationActionController::realtimeAdvance()
    1587              : {
    1588            0 :     TRACEFUNC;
    1589              : 
    1590            0 :     INotationMidiInputPtr midiInput = currentNotationMidiInput();
    1591            0 :     if (!midiInput) {
    1592            0 :         return;
    1593              :     }
    1594              : 
    1595            0 :     midiInput->onRealtimeAdvance();
    1596            0 : }
    1597              : 
    1598            0 : bool NotationActionController::isMoveSelectionAvailable(MoveSelectionType type) const
    1599              : {
    1600            0 :     auto interaction = currentNotationInteraction();
    1601            0 :     return interaction && interaction->moveSelectionAvailable(type);
    1602            0 : }
    1603              : 
    1604            0 : bool NotationActionController::isToggleLayoutBreakAvailable() const
    1605              : {
    1606            0 :     auto interaction = currentNotationInteraction();
    1607            0 :     return interaction && interaction->toggleLayoutBreakAvailable();
    1608            0 : }
    1609              : 
    1610            0 : ScoreConfig NotationActionController::scoreConfig() const
    1611              : {
    1612            0 :     auto interaction = currentNotationInteraction();
    1613            0 :     if (!interaction) {
    1614            0 :         return ScoreConfig();
    1615              :     }
    1616            0 :     return interaction->scoreConfig();
    1617            0 : }
    1618              : 
    1619            0 : muse::async::Channel<ScoreConfigType> NotationActionController::scoreConfigChanged() const
    1620              : {
    1621            0 :     return m_scoreConfigChanged;
    1622              : }
    1623              : 
    1624            0 : muse::Ret NotationActionController::moveWithRet(MoveDirection direction, bool quickly)
    1625              : {
    1626            0 :     TRACEFUNC;
    1627            0 :     auto interaction = currentNotationInteraction();
    1628            0 :     if (!interaction) {
    1629            0 :         return muse::make_ret(muse::Ret::Code::InternalError);
    1630              :     }
    1631              : 
    1632            0 :     const NoteInputState& state = interaction->noteInput()->state();
    1633            0 :     const bool previousSelectionExists = currentNotationScore() && currentNotationScore()->selection().currentCR();
    1634            0 :     if (interaction->selection()->isNone() && previousSelectionExists && !state.beyondScore()) {
    1635              :         // Try to restore the previous selection...
    1636            0 :         interaction->moveSelectionDeprecated(direction, MoveSelectionType::EngravingItem);
    1637            0 :         seekAndPlaySelectedElement(true);
    1638            0 :         return muse::make_ok();
    1639              :     }
    1640              : 
    1641            0 :     const EngravingItem* selectedElement = interaction->selection()->element();
    1642            0 :     INotationNoteInputPtr noteInput = interaction->noteInput();
    1643            0 :     bool playChord = false;
    1644              : 
    1645            0 :     switch (direction) {
    1646            0 :     case MoveDirection::Up:
    1647              :     case MoveDirection::Down:
    1648            0 :         if (!quickly && selectedElement && selectedElement->isLyrics()) {
    1649            0 :             interaction->moveLyrics(direction);
    1650            0 :         } else if (selectedElement && (selectedElement->isTextBase() || selectedElement->isArticulationFamily())) {
    1651            0 :             interaction->nudge(direction, quickly);
    1652            0 :         } else if (selectedElement && selectedElement->hasGrips() && interaction->isGripEditStarted()) {
    1653            0 :             interaction->nudgeAnchors(direction);
    1654            0 :         } else if (state.isValid() && noteInput->isNoteInputMode() && noteInput->usingNoteInputMethod(NoteInputMethod::BY_DURATION)) {
    1655            0 :             moveInputNotes(direction == MoveDirection::Up, quickly ? PitchMode::OCTAVE : PitchMode::DIATONIC);
    1656            0 :             return muse::make_ok();
    1657            0 :         } else if (noteInput->isNoteInputMode() && noteInput->state().staffGroup() == mu::engraving::StaffGroup::TAB) {
    1658            0 :             if (quickly) {
    1659            0 :                 interaction->movePitch(direction, PitchMode::OCTAVE);
    1660              :             }
    1661            0 :             interaction->moveSelectionDeprecated(direction, MoveSelectionType::String);
    1662            0 :             return muse::make_ok();
    1663            0 :         } else if (interaction->selection()->isNone() && !state.beyondScore()) {
    1664            0 :             interaction->select(SelectionTarget::FirstItem);
    1665              :         } else {
    1666            0 :             interaction->movePitch(direction, quickly ? PitchMode::OCTAVE : PitchMode::CHROMATIC);
    1667              :         }
    1668            0 :         break;
    1669            0 :     case MoveDirection::Right:
    1670              :     case MoveDirection::Left:
    1671            0 :         if (playbackController()->isPlaying()) {
    1672            0 :             engraving::MeasureBeat beat = playbackController()->currentBeat();
    1673            0 :             int targetBeatIdx = static_cast<int>(beat.beat);
    1674            0 :             int targetMeasureIdx = beat.measureIndex;
    1675            0 :             int increment = (direction == MoveDirection::Right ? 1 : -1);
    1676              : 
    1677            0 :             if (quickly) {
    1678            0 :                 targetBeatIdx = 0;
    1679            0 :                 targetMeasureIdx += increment;
    1680            0 :                 if (targetMeasureIdx > beat.maxMeasureIndex) {
    1681            0 :                     targetMeasureIdx = beat.maxMeasureIndex;
    1682            0 :                 } else if (targetMeasureIdx < 0) {
    1683            0 :                     targetMeasureIdx = 0;
    1684              :                 }
    1685              :             } else {
    1686            0 :                 targetBeatIdx += increment;
    1687            0 :                 if (targetBeatIdx > beat.maxBeatIndex) {
    1688            0 :                     targetBeatIdx = 0;
    1689            0 :                     targetMeasureIdx += 1;
    1690            0 :                 } else if (targetBeatIdx < 0) {
    1691            0 :                     targetMeasureIdx -= 1;
    1692              : 
    1693              :                     // Set target beat to max beat of previous bar
    1694            0 :                     engraving::TimeSigMap* timeSigMap = currentMasterNotation()->masterScore()->sigmap();
    1695            0 :                     int targetBarStartTick = timeSigMap->bar2tick(targetMeasureIdx, 0);
    1696            0 :                     targetBeatIdx = timeSigMap->timesig(Fraction::fromTicks(targetBarStartTick)).timesig().numerator() - 1;
    1697              :                 }
    1698              :             }
    1699              : 
    1700            0 :             playbackController()->seekBeat(targetMeasureIdx, targetBeatIdx);
    1701            0 :             return muse::make_ok();
    1702              :         }
    1703              : 
    1704            0 :         if (interaction->isTextEditingStarted() && textNavigationAvailable()) {
    1705            0 :             navigateToTextElementInNearMeasure(direction);
    1706            0 :             return muse::make_ok();
    1707              :         }
    1708              : 
    1709            0 :         if (selectedElement && selectedElement->isTextBase()) {
    1710            0 :             interaction->nudge(direction, quickly);
    1711            0 :         } else if (selectedElement && selectedElement->hasGrips() && interaction->isGripEditStarted()) {
    1712            0 :             interaction->nudgeAnchors(direction);
    1713              :         } else {
    1714            0 :             if (interaction->selection()->isNone() && !state.beyondScore()) {
    1715            0 :                 interaction->select(SelectionTarget::FirstItem);
    1716              :             }
    1717            0 :             interaction->moveSelectionDeprecated(direction, quickly ? MoveSelectionType::Measure : MoveSelectionType::Chord);
    1718            0 :             playChord = true;
    1719              :         }
    1720            0 :         break;
    1721            0 :     case MoveDirection::Undefined:
    1722            0 :         break;
    1723              :     }
    1724              : 
    1725            0 :     seekAndPlaySelectedElement(playChord);
    1726              : 
    1727            0 :     return muse::make_ok();
    1728            0 : }
    1729              : 
    1730            0 : void NotationActionController::move(MoveDirection direction, bool quickly)
    1731              : {
    1732            0 :     moveWithRet(direction, quickly);
    1733            0 : }
    1734              : 
    1735            0 : void NotationActionController::moveInputNotes(bool up, PitchMode mode)
    1736              : {
    1737            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
    1738            0 :     if (!interaction) {
    1739            0 :         return;
    1740              :     }
    1741              : 
    1742            0 :     interaction->noteInput()->setRestMode(false);
    1743            0 :     interaction->noteInput()->moveInputNotes(up, mode);
    1744              : 
    1745            0 :     if (configuration()->isPlayPreviewNotesInInputByDuration()) {
    1746            0 :         const NoteInputState& state = interaction->noteInput()->state();
    1747            0 :         playbackController()->playNotes(state.notes(), state.staffIdx(), state.segment());
    1748              :     }
    1749            0 : }
    1750              : 
    1751            0 : void NotationActionController::movePitchDiatonic(MoveDirection direction, bool)
    1752              : {
    1753            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
    1754            0 :     if (!interaction) {
    1755            0 :         return;
    1756              :     }
    1757              : 
    1758            0 :     INotationNoteInputPtr noteInput = interaction->noteInput();
    1759            0 :     if (noteInput->state().isValid() && noteInput->isNoteInputMode() && noteInput->usingNoteInputMethod(NoteInputMethod::BY_DURATION)) {
    1760            0 :         moveInputNotes(direction == MoveDirection::Up, PitchMode::DIATONIC);
    1761            0 :         return;
    1762              :     }
    1763              : 
    1764            0 :     interaction->movePitch(direction, PitchMode::DIATONIC);
    1765            0 :     seekAndPlaySelectedElement(true);
    1766            0 : }
    1767              : 
    1768            0 : void NotationActionController::changeVoice(voice_idx_t voiceIndex)
    1769              : {
    1770            0 :     TRACEFUNC;
    1771              : 
    1772            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
    1773            0 :     if (!interaction) {
    1774            0 :         return;
    1775              :     }
    1776              : 
    1777            0 :     INotationNoteInputPtr noteInput = interaction->noteInput();
    1778            0 :     if (!noteInput) {
    1779            0 :         return;
    1780              :     }
    1781              : 
    1782            0 :     if (interaction->selection()->isNone()) {
    1783            0 :         if (!noteInput->isNoteInputMode() && !toggleNoteInputAllowed()) {
    1784            0 :             return;
    1785              :         }
    1786              : 
    1787            0 :         startNoteInput();
    1788              :     }
    1789              : 
    1790            0 :     noteInput->setCurrentVoice(voiceIndex);
    1791              : 
    1792            0 :     if (!noteInput->isNoteInputMode()) {
    1793            0 :         interaction->changeSelectedElementsVoice(voiceIndex);
    1794              :     }
    1795            0 : }
    1796              : 
    1797            0 : void NotationActionController::swapVoices(voice_idx_t voiceIndex1, voice_idx_t voiceIndex2)
    1798              : {
    1799            0 :     TRACEFUNC;
    1800            0 :     auto interaction = currentNotationInteraction();
    1801            0 :     if (!interaction) {
    1802            0 :         return;
    1803              :     }
    1804              : 
    1805            0 :     interaction->swapVoices(voiceIndex1, voiceIndex2);
    1806            0 : }
    1807              : 
    1808            0 : void NotationActionController::cutSelection()
    1809              : {
    1810            0 :     TRACEFUNC;
    1811            0 :     auto interaction = currentNotationInteraction();
    1812            0 :     if (!interaction) {
    1813            0 :         return;
    1814              :     }
    1815            0 :     interaction->copySelection();
    1816            0 :     interaction->deleteSelection();
    1817            0 : }
    1818              : 
    1819            0 : void NotationActionController::repeatSelection()
    1820              : {
    1821            0 :     auto interaction = currentNotationInteraction();
    1822            0 :     if (!interaction) {
    1823            0 :         return;
    1824              :     }
    1825            0 :     interaction->repeatSelection();
    1826            0 :     seekAndPlaySelectedElement(true);
    1827            0 : }
    1828              : 
    1829            0 : void NotationActionController::pasteSelection(PastingType type)
    1830              : {
    1831            0 :     TRACEFUNC;
    1832            0 :     auto interaction = currentNotationInteraction();
    1833            0 :     if (!interaction) {
    1834            0 :         return;
    1835              :     }
    1836              : 
    1837            0 :     Fraction scale = resolvePastingScale(interaction, type);
    1838            0 :     interaction->pasteSelection(scale);
    1839              : 
    1840            0 :     seekAndPlaySelectedElement(DONT_PLAY_CHORD);
    1841            0 : }
    1842              : 
    1843            0 : Fraction NotationActionController::resolvePastingScale(const INotationInteractionPtr& interaction, PastingType type) const
    1844              : {
    1845            0 :     TRACEFUNC;
    1846            0 :     const Fraction DEFAULT_SCALE(1, 1);
    1847              : 
    1848            0 :     switch (type) {
    1849            0 :     case PastingType::Default: return DEFAULT_SCALE;
    1850            0 :     case PastingType::Half: return Fraction(1, 2);
    1851            0 :     case PastingType::Double: return Fraction(2, 1);
    1852            0 :     case PastingType::Special:
    1853            0 :         Fraction scale = DEFAULT_SCALE;
    1854            0 :         Fraction duration = interaction->noteInput()->state().duration().fraction();
    1855              : 
    1856            0 :         if (duration.isValid() && !duration.isZero()) {
    1857            0 :             scale = duration * 4;
    1858            0 :             scale.reduce();
    1859              :         }
    1860              : 
    1861            0 :         return scale;
    1862              :     }
    1863              : 
    1864            0 :     return DEFAULT_SCALE;
    1865            0 : }
    1866              : 
    1867            0 : void NotationActionController::addTie()
    1868              : {
    1869            0 :     TRACEFUNC;
    1870            0 :     auto interaction = currentNotationInteraction();
    1871            0 :     if (!interaction) {
    1872            0 :         return;
    1873              :     }
    1874              : 
    1875            0 :     auto noteInput = interaction->noteInput();
    1876            0 :     if (!noteInput) {
    1877            0 :         return;
    1878              :     }
    1879              : 
    1880            0 :     if (noteInput->isNoteInputMode()) {
    1881            0 :         noteInput->addTie();
    1882            0 :         seekAndPlaySelectedElement(true);
    1883              :     } else {
    1884            0 :         interaction->toggleTieForSelection();
    1885              :     }
    1886            0 : }
    1887              : 
    1888            0 : void NotationActionController::addLaissezVib()
    1889              : {
    1890            0 :     TRACEFUNC;
    1891            0 :     auto interaction = currentNotationInteraction();
    1892            0 :     if (!interaction) {
    1893            0 :         return;
    1894              :     }
    1895              : 
    1896            0 :     auto noteInput = interaction->noteInput();
    1897            0 :     if (!noteInput) {
    1898            0 :         return;
    1899              :     }
    1900              : 
    1901            0 :     if (noteInput->isNoteInputMode()) {
    1902            0 :         noteInput->addLaissezVib();
    1903            0 :         seekAndPlaySelectedElement(true);
    1904              :     } else {
    1905            0 :         interaction->addLaissezVibToSelection();
    1906              :     }
    1907            0 : }
    1908              : 
    1909            0 : void NotationActionController::addSlur()
    1910              : {
    1911            0 :     TRACEFUNC;
    1912            0 :     auto interaction = currentNotationInteraction();
    1913            0 :     if (!interaction) {
    1914            0 :         return;
    1915              :     }
    1916              : 
    1917            0 :     auto noteInput = interaction->noteInput();
    1918            0 :     if (!noteInput) {
    1919            0 :         return;
    1920              :     }
    1921              : 
    1922            0 :     if (noteInput->isNoteInputMode() && noteInput->state().slur()) {
    1923            0 :         noteInput->resetSlur();
    1924              :     } else {
    1925            0 :         interaction->addSlurToSelection();
    1926              :     }
    1927            0 : }
    1928              : 
    1929            0 : void NotationActionController::addHammerOnPullOff()
    1930              : {
    1931            0 :     auto interaction = currentNotationInteraction();
    1932            0 :     if (!interaction) {
    1933            0 :         return;
    1934              :     }
    1935              : 
    1936            0 :     interaction->addHammerOnPullOffToSelection();
    1937            0 : }
    1938              : 
    1939            0 : void NotationActionController::addFret(int num)
    1940              : {
    1941            0 :     auto interaction = currentNotationInteraction();
    1942            0 :     if (!interaction) {
    1943            0 :         return;
    1944              :     }
    1945              : 
    1946            0 :     interaction->addFret(num);
    1947            0 :     seekAndPlaySelectedElement(currentNotationScore()->playChord());
    1948            0 : }
    1949              : 
    1950            0 : void NotationActionController::insertClef(mu::engraving::ClefType type)
    1951              : {
    1952            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
    1953            0 :     if (!interaction || !interaction->canInsertClef(type)) {
    1954            0 :         return;
    1955              :     }
    1956              : 
    1957            0 :     interaction->insertClef(type);
    1958            0 : }
    1959              : 
    1960            0 : void NotationActionController::addText(TextStyleType type)
    1961              : {
    1962            0 :     TRACEFUNC;
    1963              : 
    1964            0 :     auto interaction = currentNotationInteraction();
    1965            0 :     if (!interaction) {
    1966            0 :         return;
    1967              :     }
    1968              : 
    1969            0 :     EngravingItem* item = nullptr;
    1970              : 
    1971            0 :     const INotationSelectionPtr sel = interaction->selection();
    1972            0 :     if (sel->isRange()) {
    1973            0 :         const INotationSelectionRangePtr range = sel->range();
    1974            0 :         item = range->rangeStartSegment()->firstElementForNavigation(range->startStaffIndex());
    1975            0 :     } else {
    1976            0 :         item = interaction->contextItem();
    1977              :     }
    1978              : 
    1979            0 :     if (isVerticalBoxTextStyle(type)) {
    1980            0 :         if (!item || !item->isVBox()) {
    1981            0 :             interaction->addTextToTopFrame(type);
    1982            0 :             return;
    1983              :         }
    1984              :     }
    1985              : 
    1986            0 :     interaction->addTextToItem(type, item);
    1987            0 : }
    1988              : 
    1989            0 : void NotationActionController::addImage()
    1990              : {
    1991            0 :     auto interaction = currentNotationInteraction();
    1992            0 :     if (!interaction) {
    1993            0 :         return;
    1994              :     }
    1995              : 
    1996            0 :     EngravingItem* item = interaction->contextItem();
    1997            0 :     if (!interaction->canAddImageToItem(item)) {
    1998            0 :         return;
    1999              :     }
    2000              : 
    2001              :     std::vector<std::string> filter
    2002            0 :         = { muse::trc("notation", "All Supported Files") + " (*.svg *.svgz *.jpg *.jpeg *.png *.bmp *.tif *.tiff)",
    2003            0 :             muse::trc("notation", "Scalable Vector Graphics") + " (*.svg *.svgz)",
    2004            0 :             muse::trc("notation", "JPEG") + " (*.jpg *.jpeg)",
    2005            0 :             muse::trc("notation", "PNG Bitmap Graphic") + " (*.png)",
    2006            0 :             muse::trc("notation", "Bitmap") + " (*.bmp)",
    2007            0 :             muse::trc("notation", "TIFF") + " (*.tif *.tiff)",
    2008            0 :             muse::trc("notation", "All") + " (*)" };
    2009              : 
    2010            0 :     muse::io::path_t path = interactive()->selectOpeningFileSync(muse::trc("notation", "Insert Image"), "", filter);
    2011            0 :     interaction->addImageToItem(path, item);
    2012            0 : }
    2013              : 
    2014            0 : void NotationActionController::addFiguredBass()
    2015              : {
    2016            0 :     TRACEFUNC;
    2017              : 
    2018            0 :     auto interaction = currentNotationInteraction();
    2019            0 :     if (!interaction) {
    2020            0 :         return;
    2021              :     }
    2022              : 
    2023            0 :     interaction->addFiguredBass();
    2024            0 : }
    2025              : 
    2026            0 : void NotationActionController::addGuitarBend(GuitarBendType bendType)
    2027              : {
    2028            0 :     TRACEFUNC;
    2029              : 
    2030            0 :     auto interaction = currentNotationInteraction();
    2031            0 :     if (!interaction) {
    2032            0 :         return;
    2033              :     }
    2034              : 
    2035            0 :     interaction->addGuitarBend(bendType);
    2036            0 : }
    2037              : 
    2038            0 : void NotationActionController::addFretboardDiagram()
    2039              : {
    2040            0 :     TRACEFUNC;
    2041              : 
    2042            0 :     auto interaction = currentNotationInteraction();
    2043            0 :     if (!interaction) {
    2044            0 :         return;
    2045              :     }
    2046              : 
    2047            0 :     interaction->addFretboardDiagram();
    2048            0 : }
    2049              : 
    2050            0 : void NotationActionController::openSelectionMoreOptions()
    2051              : {
    2052            0 :     auto interaction = currentNotationInteraction();
    2053            0 :     if (!interaction) {
    2054            0 :         return;
    2055              :     }
    2056              : 
    2057            0 :     auto item = interaction->contextItem();
    2058            0 :     if (!item) {
    2059            0 :         return;
    2060              :     }
    2061              : 
    2062            0 :     bool noteSelected = item->isNote();
    2063              : 
    2064            0 :     if (noteSelected) {
    2065            0 :         interactive()->open("musescore://notation/selectnote");
    2066              :     } else {
    2067            0 :         interactive()->open("musescore://notation/selectelement");
    2068              :     }
    2069            0 : }
    2070              : 
    2071            0 : muse::Ret NotationActionController::startEditSelectedElement(const muse::rcommand::Params& params)
    2072              : {
    2073            0 :     auto interaction = currentNotationInteraction();
    2074            0 :     if (!interaction) {
    2075            0 :         return muse::make_ret(Ret::Code::NotSupported);
    2076              :     }
    2077              : 
    2078            0 :     auto selection = interaction->selection();
    2079            0 :     if (!selection) {
    2080            0 :         return muse::make_ret(Ret::Code::BadArgs);
    2081              :     }
    2082              : 
    2083            0 :     mu::engraving::EngravingItem* element = selection->element();
    2084            0 :     if (!element) {
    2085            0 :         return muse::make_ret(Ret::Code::BadArgs);
    2086              :     }
    2087              : 
    2088            0 :     if (element->isInstrumentName()) {
    2089            0 :         openStaffProperties();
    2090            0 :         return muse::make_ok();
    2091              :     }
    2092              : 
    2093            0 :     if (elementHasPopup(element) && !interaction->textEditingAllowed(element)) {
    2094            0 :         IF_ASSERT_FAILED(m_viewController) {
    2095            0 :             return muse::make_ret(Ret::Code::NotSupported);
    2096              :         }
    2097            0 :         m_viewController->togglePopupForItemIfSupports(element);
    2098            0 :         return muse::make_ok();
    2099              :     }
    2100              : 
    2101            0 :     if (element->isText()) {
    2102            0 :         TextStyleType styleType = mu::engraving::toText(element)->textStyleType();
    2103              : 
    2104            0 :         if (styleType == mu::engraving::TextStyleType::HEADER || styleType == mu::engraving::TextStyleType::FOOTER) {
    2105            0 :             openEditStyleDialog({ { "page_code", Val("header-and-footer") } });
    2106            0 :             return muse::make_ok();
    2107              :         }
    2108              :     }
    2109              : 
    2110            0 :     if (interaction->textEditingAllowed(element)) {
    2111            0 :         float posX = params.at("pos_x").toFloat();
    2112            0 :         float posY = params.at("pos_y").toFloat();
    2113            0 :         PointF cursorPos = PointF(posX, posY);
    2114            0 :         interaction->startEditText(element, cursorPos);
    2115              :     } else {
    2116            0 :         interaction->startEditElement(element);
    2117              :     }
    2118              : 
    2119            0 :     return muse::make_ok();
    2120            0 : }
    2121              : 
    2122            0 : muse::Ret NotationActionController::startEditSelectedText(const muse::rcommand::Params& params)
    2123              : {
    2124            0 :     auto interaction = currentNotationInteraction();
    2125            0 :     if (!interaction) {
    2126            0 :         return muse::make_ret(Ret::Code::NotSupported);
    2127              :     }
    2128              : 
    2129            0 :     auto selection = interaction->selection();
    2130            0 :     if (!selection) {
    2131            0 :         return muse::make_ret(Ret::Code::BadArgs);
    2132              :     }
    2133              : 
    2134            0 :     mu::engraving::EngravingItem* element = selection->element();
    2135              : 
    2136            0 :     if (interaction->textEditingAllowed(element)) {
    2137            0 :         float posX = params.at("pos_x").toFloat();
    2138            0 :         float posY = params.at("pos_y").toFloat();
    2139            0 :         PointF cursorPos = PointF(posX, posY);
    2140            0 :         interaction->startEditText(element, cursorPos);
    2141              :     }
    2142              : 
    2143            0 :     return muse::make_ok();
    2144            0 : }
    2145              : 
    2146            0 : muse::Ret NotationActionController::addMeasures(const muse::rcommand::Params& params, AddBoxesTarget target)
    2147              : {
    2148            0 :     if (params.contains("count")) {
    2149            0 :         int count = params.at("count").toInt();
    2150            0 :         addBoxes(BoxType::Measure, count, target);
    2151              :     } else {
    2152            0 :         interactive()->open("musescore://notation/selectmeasurescount")
    2153            0 :         .onResolve(this, [this, target](const Val& v) {
    2154            0 :             int count = v.toInt();
    2155            0 :             addBoxes(BoxType::Measure, count, target);
    2156            0 :         });
    2157              :     }
    2158              : 
    2159            0 :     return muse::make_ok();
    2160              : }
    2161              : 
    2162            0 : void NotationActionController::addBoxes(BoxType boxType, int count, AddBoxesTarget target)
    2163              : {
    2164            0 :     TRACEFUNC;
    2165            0 :     auto interaction = currentNotationInteraction();
    2166            0 :     if (!interaction) {
    2167            0 :         return;
    2168              :     }
    2169              : 
    2170            0 :     interaction->addBoxes(boxType, count, target);
    2171            0 : }
    2172              : 
    2173            0 : void NotationActionController::unrollRepeats()
    2174              : {
    2175            0 :     NOT_IMPLEMENTED;
    2176              :     // TODO: https://github.com/musescore/MuseScore/issues/9670
    2177            0 : }
    2178              : 
    2179            0 : void NotationActionController::toggleMmrest()
    2180              : {
    2181            0 :     auto interaction = currentNotationInteraction();
    2182            0 :     if (!interaction) {
    2183            0 :         return;
    2184              :     }
    2185              : 
    2186            0 :     interaction->execute(&mu::engraving::Score::cmdToggleMmrest, TranslatableString("undoableAction", "Toggle multimeasure rests"));
    2187            0 : }
    2188              : 
    2189            0 : void NotationActionController::toggleHideEmpty()
    2190              : {
    2191            0 :     auto interaction = currentNotationInteraction();
    2192            0 :     if (!interaction) {
    2193            0 :         return;
    2194              :     }
    2195              : 
    2196            0 :     interaction->execute(&mu::engraving::Score::cmdToggleHideEmpty, TranslatableString("undoableAction", "Toggle empty staves"));
    2197            0 : }
    2198              : 
    2199            0 : void NotationActionController::addFullMeasureRest()
    2200              : {
    2201            0 :     auto interaction = currentNotationInteraction();
    2202            0 :     if (!interaction) {
    2203            0 :         return;
    2204              :     }
    2205              : 
    2206            0 :     interaction->execute(&mu::engraving::Score::cmdFullMeasureRest, TranslatableString("undoableAction", "Enter full measure rest"));
    2207            0 : }
    2208              : 
    2209            0 : void NotationActionController::addStretch(qreal value)
    2210              : {
    2211            0 :     TRACEFUNC;
    2212            0 :     auto interaction = currentNotationInteraction();
    2213            0 :     if (!interaction) {
    2214            0 :         return;
    2215              :     }
    2216              : 
    2217            0 :     auto selection = currentNotationSelection();
    2218            0 :     if (!selection) {
    2219            0 :         return;
    2220              :     }
    2221              : 
    2222            0 :     if (!selection->isRange()) {
    2223            0 :         return;
    2224              :     }
    2225              : 
    2226            0 :     interaction->addStretch(value);
    2227            0 : }
    2228              : 
    2229            0 : void NotationActionController::resetStretch()
    2230              : {
    2231            0 :     TRACEFUNC;
    2232            0 :     auto interaction = currentNotationInteraction();
    2233            0 :     if (!interaction) {
    2234            0 :         return;
    2235              :     }
    2236              : 
    2237            0 :     auto selection = currentNotationSelection();
    2238            0 :     if (!selection) {
    2239            0 :         return;
    2240              :     }
    2241              : 
    2242            0 :     if (!selection->isRange()) {
    2243            0 :         return;
    2244              :     }
    2245              : 
    2246            0 :     interaction->resetStretch();
    2247            0 : }
    2248              : 
    2249            0 : void NotationActionController::resetBeamMode()
    2250              : {
    2251            0 :     TRACEFUNC;
    2252            0 :     auto interaction = currentNotationInteraction();
    2253            0 :     if (!interaction) {
    2254            0 :         return;
    2255              :     }
    2256              : 
    2257            0 :     auto selection = currentNotationSelection();
    2258            0 :     if (!selection) {
    2259            0 :         return;
    2260              :     }
    2261              : 
    2262            0 :     if (selection->isNone() || selection->isRange()) {
    2263            0 :         interaction->resetBeamMode();
    2264              :     }
    2265            0 : }
    2266              : 
    2267            0 : muse::Ret NotationActionController::openEditStyleDialog(const muse::rcommand::Params& params)
    2268              : {
    2269            0 :     UriQuery uri("musescore://notation/style");
    2270              : 
    2271            0 :     if (params.contains("page_code")) {
    2272            0 :         uri.addParam("currentPageCode", Val(params.at("page_code").toString()));
    2273              : 
    2274            0 :         if (params.contains("sub_page_code")) {
    2275            0 :             uri.addParam("currentSubPageCode", Val(params.at("sub_page_code").toString()));
    2276              :         }
    2277              :     }
    2278              : 
    2279            0 :     interactive()->open(uri);
    2280            0 :     return muse::make_ok();
    2281            0 : }
    2282              : 
    2283            0 : void NotationActionController::openPageSettingsDialog()
    2284              : {
    2285            0 :     interactive()->open("musescore://notation/pagesettings");
    2286            0 : }
    2287              : 
    2288            0 : void NotationActionController::openStaffProperties()
    2289              : {
    2290            0 :     interactive()->open("musescore://notation/staffproperties");
    2291            0 : }
    2292              : 
    2293            0 : void NotationActionController::openEditStringsDialog()
    2294              : {
    2295            0 :     interactive()->open("musescore://notation/editstrings");
    2296            0 : }
    2297              : 
    2298            0 : void NotationActionController::openBreaksDialog()
    2299              : {
    2300            0 :     interactive()->open("musescore://notation/breaks");
    2301            0 : }
    2302              : 
    2303            0 : void NotationActionController::openTransposeDialog()
    2304              : {
    2305            0 :     interactive()->open("musescore://notation/transpose").onResolve(this, [this](const Val&) {
    2306            0 :         currentNotationInteraction()->checkAndShowError();
    2307            0 :     });
    2308            0 : }
    2309              : 
    2310            0 : void NotationActionController::openPartsDialog()
    2311              : {
    2312            0 :     interactive()->open("musescore://notation/parts");
    2313            0 : }
    2314              : 
    2315            0 : muse::io::path_t NotationActionController::selectStyleFile(bool forLoad)
    2316              : {
    2317            0 :     muse::io::path_t dir = configuration()->userStylesPath();
    2318              :     std::string filterName = forLoad
    2319            0 :                              ? muse::trc("notation", "MuseScore style files")
    2320            0 :                              : muse::trc("notation", "MuseScore style file");
    2321            0 :     std::vector<std::string> filter = { filterName + " (*.mss)" };
    2322              :     return forLoad
    2323            0 :            ? interactive()->selectOpeningFileSync(muse::trc("notation", "Load style"), dir, filter)
    2324            0 :            : interactive()->selectSavingFileSync(muse::trc("notation", "Save style"), dir, filter);
    2325            0 : }
    2326              : 
    2327            0 : void NotationActionController::loadStyle()
    2328              : {
    2329            0 :     TRACEFUNC;
    2330            0 :     auto path = selectStyleFile(true);
    2331            0 :     if (!path.empty()) {
    2332            0 :         File f(path.toQString());
    2333            0 :         if (!f.open(IODevice::ReadOnly) || !mu::engraving::MStyle::isValid(&f)) {
    2334            0 :             interactive()->error(muse::trc("notation", "The style file could not be loaded."),
    2335              :                                  f.errorString());
    2336            0 :             return;
    2337              :         }
    2338            0 :         if (!notationStyle()->loadStyle(path.toQString(), false)) {
    2339            0 :             auto promise = interactive()->warning(
    2340            0 :                 muse::trc("notation",
    2341              :                           "Since this style file is from a different version of MuseScore Studio, your score is not guaranteed to display correctly."),
    2342            0 :                 muse::trc("notation", "Click OK to load anyway."), { IInteractive::Button::Ok, IInteractive::Button::Cancel },
    2343            0 :                 IInteractive::Button::Ok);
    2344              : 
    2345            0 :             promise.onResolve(this, [this, path](const IInteractive::Result& res) {
    2346            0 :                 if (res.isButton(IInteractive::Button::Ok)) {
    2347            0 :                     notationStyle()->loadStyle(path.toQString(), true);
    2348              :                 }
    2349            0 :             });
    2350            0 :         }
    2351            0 :     }
    2352            0 : }
    2353              : 
    2354            0 : void NotationActionController::saveStyle()
    2355              : {
    2356            0 :     TRACEFUNC;
    2357            0 :     auto path = selectStyleFile(false);
    2358            0 :     if (!path.empty()) {
    2359            0 :         if (!notationStyle()->saveStyle(path)) {
    2360            0 :             interactive()->error(muse::trc("notation", "The style file could not be saved."),
    2361            0 :                                  muse::trc("notation", "An error occurred."));
    2362              :         }
    2363              :     }
    2364            0 : }
    2365              : 
    2366            0 : bool NotationActionController::isTextEditing() const
    2367              : {
    2368            0 :     auto interaction = currentNotationInteraction();
    2369            0 :     if (!interaction) {
    2370            0 :         return false;
    2371              :     }
    2372              : 
    2373            0 :     return interaction->isTextEditingStarted();
    2374            0 : }
    2375              : 
    2376            0 : bool NotationActionController::isLyricsEditing() const
    2377              : {
    2378            0 :     auto interaction = currentNotationInteraction();
    2379            0 :     if (!interaction) {
    2380            0 :         return false;
    2381              :     }
    2382              : 
    2383            0 :     return interaction->isTextEditingStarted()
    2384            0 :            && interaction->selection()->element()
    2385            0 :            && interaction->selection()->element()->isLyrics();
    2386            0 : }
    2387              : 
    2388            0 : muse::async::Channel<bool> NotationActionController::textEditingChanged() const
    2389              : {
    2390            0 :     return m_textEditingChanged;
    2391              : }
    2392              : 
    2393            0 : bool NotationActionController::textNavigationAvailable() const
    2394              : {
    2395            0 :     return resolveTextNavigationAvailable(TextNavigationType::NearNoteOrRest);
    2396              : }
    2397              : 
    2398            0 : bool NotationActionController::textNavigationByBeatsAvailable() const
    2399              : {
    2400            0 :     return resolveTextNavigationAvailable(TextNavigationType::NearBeat);
    2401              : }
    2402              : 
    2403            0 : bool NotationActionController::textNavigationByFractionAvailable() const
    2404              : {
    2405            0 :     return resolveTextNavigationAvailable(TextNavigationType::Fraction);
    2406              : }
    2407              : 
    2408            0 : bool NotationActionController::resolveTextNavigationAvailable(TextNavigationType type) const
    2409              : {
    2410            0 :     if (!isEditingText()) {
    2411            0 :         return false;
    2412              :     }
    2413              : 
    2414              :     static const QList<mu::engraving::ElementType> allowedElementsForTextNavigation {
    2415              :         mu::engraving::ElementType::LYRICS,
    2416              :         mu::engraving::ElementType::HARMONY,
    2417              :         mu::engraving::ElementType::FIGURED_BASS,
    2418              :         mu::engraving::ElementType::STICKING,
    2419              :         mu::engraving::ElementType::FINGERING
    2420            0 :     };
    2421              : 
    2422              :     static const QList<mu::engraving::ElementType> allowedElementsForBeatsNavigation {
    2423              :         mu::engraving::ElementType::HARMONY
    2424            0 :     };
    2425              : 
    2426              :     static const QList<mu::engraving::ElementType> allowedElementsForFractionNavigation {
    2427              :         mu::engraving::ElementType::HARMONY,
    2428              :         mu::engraving::ElementType::FIGURED_BASS
    2429            0 :     };
    2430              : 
    2431            0 :     const mu::engraving::EngravingItem* element = selectedElement();
    2432            0 :     mu::engraving::ElementType elementType = element ? element->type() : mu::engraving::ElementType::INVALID;
    2433              : 
    2434            0 :     switch (type) {
    2435            0 :     case TextNavigationType::NearNoteOrRest:
    2436            0 :         return allowedElementsForTextNavigation.contains(elementType);
    2437            0 :     case TextNavigationType::NearBeat:
    2438            0 :         return allowedElementsForBeatsNavigation.contains(elementType);
    2439            0 :     case TextNavigationType::Fraction:
    2440            0 :         return allowedElementsForFractionNavigation.contains(elementType);
    2441              :     }
    2442              : 
    2443            0 :     return false;
    2444              : }
    2445              : 
    2446            0 : muse::Ret NotationActionController::nextTextElement()
    2447              : {
    2448            0 :     navigateToTextElement(MoveDirection::Right, NEAR_NOTE_OR_REST);
    2449            0 :     return muse::make_ok();
    2450              : }
    2451              : 
    2452            0 : muse::Ret NotationActionController::prevTextElement()
    2453              : {
    2454            0 :     navigateToTextElement(MoveDirection::Left, NEAR_NOTE_OR_REST);
    2455            0 :     return muse::make_ok();
    2456              : }
    2457              : 
    2458            0 : muse::Ret NotationActionController::nextWord()
    2459              : {
    2460            0 :     if (!textNavigationAvailable()) {
    2461            0 :         return muse::make_ret(Ret::Code::NotSupported);
    2462              :     }
    2463              : 
    2464            0 :     navigateToTextElement(MoveDirection::Right, NEAR_NOTE_OR_REST, false);
    2465            0 :     return muse::make_ok();
    2466              : }
    2467              : 
    2468            0 : void NotationActionController::nextBeatTextElement()
    2469              : {
    2470            0 :     navigateToTextElement(MoveDirection::Right);
    2471            0 : }
    2472              : 
    2473            0 : void NotationActionController::prevBeatTextElement()
    2474              : {
    2475            0 :     navigateToTextElement(MoveDirection::Left);
    2476            0 : }
    2477              : 
    2478            0 : void NotationActionController::navigateToTextElement(MoveDirection direction, bool nearNoteOrRest, bool moveOnly)
    2479              : {
    2480            0 :     const mu::engraving::EngravingItem* element = selectedElement();
    2481            0 :     if (!element) {
    2482            0 :         return;
    2483              :     }
    2484              : 
    2485            0 :     if (element->isLyrics()) {
    2486            0 :         currentNotationInteraction()->navigateToLyrics(direction, moveOnly);
    2487            0 :     } else if (element->isHarmony()) {
    2488            0 :         const engraving::Harmony* chordSymbol = editedChordSymbol();
    2489              : 
    2490              :         // otherwise, chord symbol will be deleted when navigating away from it
    2491            0 :         const bool canPlay = chordSymbol && !chordSymbol->harmonyName().empty();
    2492              : 
    2493            0 :         currentNotationInteraction()->navigateToNearHarmony(direction, nearNoteOrRest);
    2494              : 
    2495            0 :         if (canPlay) {
    2496            0 :             playbackController()->playElements({ chordSymbol });
    2497              :         }
    2498            0 :     } else if (element->isFiguredBass()) {
    2499            0 :         currentNotationInteraction()->navigateToNearFiguredBass(direction);
    2500              :     } else {
    2501            0 :         currentNotationInteraction()->navigateToNearText(direction);
    2502              :     }
    2503              : }
    2504              : 
    2505            0 : void NotationActionController::navigateToTextItemByFraction(const Fraction& fraction)
    2506              : {
    2507            0 :     const mu::engraving::EngravingItem* element = selectedElement();
    2508            0 :     if (!element) {
    2509            0 :         return;
    2510              :     }
    2511              : 
    2512            0 :     if (element->isHarmony()) {
    2513            0 :         const engraving::Harmony* chordSymbol = editedChordSymbol();
    2514              : 
    2515              :         // otherwise, chord symbol will be deleted when navigating away from it
    2516            0 :         const bool canPlay = chordSymbol && !chordSymbol->harmonyName().empty();
    2517              : 
    2518            0 :         currentNotationInteraction()->navigateToHarmony(fraction);
    2519              : 
    2520            0 :         if (canPlay) {
    2521            0 :             playbackController()->playElements({ chordSymbol });
    2522              :         }
    2523            0 :     } else if (element->isFiguredBass()) {
    2524            0 :         currentNotationInteraction()->navigateToFiguredBass(fraction);
    2525              :     }
    2526              : }
    2527              : 
    2528            0 : void NotationActionController::navigateToTextElementInNearMeasure(MoveDirection direction)
    2529              : {
    2530            0 :     const mu::engraving::EngravingItem* element = selectedElement();
    2531            0 :     if (!element) {
    2532            0 :         return;
    2533              :     }
    2534              : 
    2535            0 :     if (element->isHarmony()) {
    2536            0 :         const engraving::Harmony* chordSymbol = editedChordSymbol();
    2537              : 
    2538              :         // otherwise, chord symbol will be deleted when navigating away from it
    2539            0 :         const bool canPlay = chordSymbol && !chordSymbol->harmonyName().empty();
    2540              : 
    2541            0 :         currentNotationInteraction()->navigateToHarmonyInNearMeasure(direction);
    2542              : 
    2543            0 :         if (canPlay) {
    2544            0 :             playbackController()->playElements({ chordSymbol });
    2545              :         }
    2546            0 :     } else if (element->isFiguredBass()) {
    2547            0 :         currentNotationInteraction()->navigateToFiguredBassInNearMeasure(direction);
    2548              :     }
    2549              : }
    2550              : 
    2551            0 : bool NotationActionController::isEditingText() const
    2552              : {
    2553            0 :     auto interaction = currentNotationInteraction();
    2554            0 :     if (!interaction) {
    2555            0 :         return false;
    2556              :     }
    2557              : 
    2558            0 :     return interaction->isTextEditingStarted();
    2559            0 : }
    2560              : 
    2561            0 : void NotationActionController::openTupletOtherDialog()
    2562              : {
    2563            0 :     interactive()->open("musescore://notation/othertupletdialog");
    2564            0 : }
    2565              : 
    2566            0 : void NotationActionController::openStaffTextPropertiesDialog()
    2567              : {
    2568            0 :     interactive()->open("musescore://notation/stafftextproperties");
    2569            0 : }
    2570              : 
    2571            0 : void NotationActionController::openMeasurePropertiesDialog()
    2572              : {
    2573            0 :     if (currentNotationInteraction()->selectedMeasure() != nullptr) {
    2574            0 :         interactive()->open("musescore://notation/measureproperties");
    2575              :     }
    2576            0 : }
    2577              : 
    2578            0 : void NotationActionController::openEditGridSizeDialog()
    2579              : {
    2580            0 :     interactive()->open("musescore://notation/editgridsize");
    2581            0 : }
    2582              : 
    2583            0 : void NotationActionController::openRealizeChordSymbolsDialog()
    2584              : {
    2585            0 :     interactive()->open("musescore://notation/realizechordsymbols");
    2586            0 : }
    2587              : 
    2588            0 : void NotationActionController::toggleScoreConfig(ScoreConfigType configType)
    2589              : {
    2590            0 :     TRACEFUNC;
    2591            0 :     auto interaction = currentNotationInteraction();
    2592            0 :     if (!interaction) {
    2593            0 :         return;
    2594              :     }
    2595              : 
    2596            0 :     ScoreConfig config = interaction->scoreConfig();
    2597              : 
    2598            0 :     bool isShow = config.isShown(configType);
    2599            0 :     config.setShown(configType, !isShow);
    2600              : 
    2601            0 :     interaction->setScoreConfig(config);
    2602            0 : }
    2603              : 
    2604            0 : void NotationActionController::toggleConcertPitch()
    2605              : {
    2606            0 :     TRACEFUNC;
    2607            0 :     INotationStylePtr style = notationStyle();
    2608            0 :     if (!style) {
    2609            0 :         return;
    2610              :     }
    2611              : 
    2612            0 :     bool toggle = !style->styleValue(StyleId::concertPitch).toBool();
    2613              : 
    2614              :     const TranslatableString actionName = toggle
    2615            0 :                                           ? TranslatableString("undoableAction", "Display concert pitch")
    2616            0 :                                           : TranslatableString("undoableAction", "Display transposed");
    2617              : 
    2618            0 :     currentNotationUndoStack()->prepareChanges(actionName);
    2619            0 :     style->setStyleValue(StyleId::concertPitch, toggle);
    2620            0 :     currentNotationUndoStack()->commitChanges();
    2621            0 : }
    2622              : 
    2623            0 : void NotationActionController::seekAndPlaySelectedElement(bool playChord)
    2624              : {
    2625            0 :     seekSelectedElement();
    2626            0 :     playSelectedElement(playChord);
    2627            0 : }
    2628              : 
    2629            0 : void NotationActionController::seekSelectedElement()
    2630              : {
    2631            0 :     const IMasterNotationPtr master = currentMasterNotation();
    2632            0 :     if (!master || master->playback()->isLoopEnabled()) {
    2633            0 :         return;
    2634              :     }
    2635              : 
    2636            0 :     const EngravingItem* element = selectedElement();
    2637            0 :     if (!element) {
    2638            0 :         return;
    2639              :     }
    2640              : 
    2641            0 :     playbackController()->seekElement(element);
    2642            0 : }
    2643              : 
    2644            0 : void NotationActionController::playSelectedElement(bool playChord)
    2645              : {
    2646            0 :     TRACEFUNC;
    2647              : 
    2648            0 :     const EngravingItem* element = selectedElement();
    2649            0 :     if (!element) {
    2650            0 :         return;
    2651              :     }
    2652              : 
    2653            0 :     if (playChord) {
    2654            0 :         element = element->elementBase();
    2655              :     }
    2656              : 
    2657            0 :     playbackController()->playElements({ element });
    2658              : 
    2659            0 :     currentNotationScore()->setPlayChord(false);
    2660            0 :     currentNotationScore()->setPlayNote(false);
    2661            0 : }
    2662              : 
    2663            0 : bool NotationActionController::toggleNoteInputAllowed() const
    2664              : {
    2665            0 :     if (!isNoteInputAllowed()) {
    2666            0 :         return false;
    2667              :     }
    2668              : 
    2669              :     //! NOTE: We're more strict about starting note input mode than exiting it.
    2670            0 :     if (!isNoteInputMode() && isEditingElement()) {
    2671            0 :         return false;
    2672              :     }
    2673              : 
    2674            0 :     return true;
    2675              : }
    2676              : 
    2677            0 : void NotationActionController::startNoteInput()
    2678              : {
    2679            0 :     INotationNoteInputPtr noteInput = currentNotationNoteInput();
    2680            0 :     if (noteInput) {
    2681            0 :         noteInput->startNoteInput(configuration()->defaultNoteInputMethod());
    2682              :     }
    2683            0 : }
    2684              : 
    2685            0 : bool NotationActionController::hasSelection() const
    2686              : {
    2687            0 :     return currentNotationSelection() ? !currentNotationSelection()->isNone() : false;
    2688              : }
    2689              : 
    2690            0 : bool NotationActionController::isSingleSelection() const
    2691              : {
    2692            0 :     return currentNotationSelection() ? currentNotationSelection()->isSingle() : false;
    2693              : }
    2694              : 
    2695            0 : bool NotationActionController::isRangeSelection() const
    2696              : {
    2697            0 :     return currentNotationSelection() ? currentNotationSelection()->isRange() : false;
    2698              : }
    2699              : 
    2700            0 : mu::engraving::EngravingItem* NotationActionController::selectedElement() const
    2701              : {
    2702            0 :     auto selection = currentNotationSelection();
    2703            0 :     return selection ? selection->element() : nullptr;
    2704            0 : }
    2705              : 
    2706            0 : bool NotationActionController::isNoteOrRestSelected() const
    2707              : {
    2708            0 :     if (isNoteInputMode()) {
    2709            0 :         return true;
    2710              :     }
    2711              : 
    2712            0 :     INotationSelectionPtr selection = currentNotationInteraction() ? currentNotationInteraction()->selection() : nullptr;
    2713            0 :     return selection && selection->elementsSelected(NOTE_REST_TYPES);
    2714            0 : }
    2715              : 
    2716            0 : bool NotationActionController::isHairpinSelected() const
    2717              : {
    2718            0 :     INotationSelectionPtr selection = currentNotationInteraction() ? currentNotationInteraction()->selection() : nullptr;
    2719            0 :     return selection && selection->element() && selection->element()->isHairpinSegment();
    2720            0 : }
    2721              : 
    2722            0 : const mu::engraving::Harmony* NotationActionController::editedChordSymbol() const
    2723              : {
    2724            0 :     INotationInteractionPtr interaction = currentNotationInteraction();
    2725            0 :     if (!interaction) {
    2726            0 :         return nullptr;
    2727              :     }
    2728              : 
    2729            0 :     const TextBase* text = interaction->editedText();
    2730            0 :     if (!text || !text->isHarmony()) {
    2731            0 :         return nullptr;
    2732              :     }
    2733              : 
    2734            0 :     return toHarmony(text);
    2735            0 : }
    2736              : 
    2737            0 : bool NotationActionController::elementHasPopup(const EngravingItem* e) const
    2738              : {
    2739            0 :     return AbstractElementPopupModel::hasElementEditPopup(e);
    2740              : }
    2741              : 
    2742            0 : bool NotationActionController::canUndo() const
    2743              : {
    2744            0 :     return currentNotationUndoStack() ? currentNotationUndoStack()->canUndo() : false;
    2745              : }
    2746              : 
    2747            0 : bool NotationActionController::canRedo() const
    2748              : {
    2749            0 :     return currentNotationUndoStack() ? currentNotationUndoStack()->canRedo() : false;
    2750              : }
    2751              : 
    2752            0 : muse::async::Notification NotationActionController::stackChanged() const
    2753              : {
    2754            0 :     return m_stackChanged;
    2755              : }
    2756              : 
    2757            0 : bool NotationActionController::isTablatureStaff() const
    2758              : {
    2759            0 :     return isNotEditingElement() && currentNotationScore()->inputState().staffGroup() == mu::engraving::StaffGroup::TAB;
    2760              : }
    2761              : 
    2762            0 : INotationAutomationPtr NotationActionController::automation() const
    2763              : {
    2764            0 :     return currentMasterNotation() ? currentMasterNotation()->automation() : nullptr;
    2765              : }
    2766              : 
    2767            0 : bool NotationActionController::isAutomationModeEnabled() const
    2768              : {
    2769            0 :     return automation() ? automation()->isAutomationModeEnabled() : false;
    2770              : }
    2771              : 
    2772            0 : muse::async::Notification NotationActionController::automationModeEnabledChanged() const
    2773              : {
    2774            0 :     return m_automationModeEnabledChanged;
    2775              : }
    2776              : 
    2777            0 : bool NotationActionController::isDebuggingCommandEnabled(const muse::rcommand::Command& command) const
    2778              : {
    2779            0 :     auto it = s_debuggingCommands.find(command);
    2780            0 :     if (it != s_debuggingCommands.cend()) {
    2781            0 :         return engravingConfiguration()->debuggingOptions().*(it->second);
    2782              :     }
    2783              : 
    2784            0 :     return false;
    2785              : }
    2786              : 
    2787            0 : muse::async::Notification NotationActionController::debuggingOptionsChanged() const
    2788              : {
    2789            0 :     return engravingConfiguration()->debuggingOptionsChanged();
    2790              : }
    2791              : 
    2792            0 : bool NotationActionController::isEditingElement() const
    2793              : {
    2794            0 :     auto interaction = currentNotationInteraction();
    2795            0 :     if (interaction) {
    2796            0 :         return interaction->isEditingElement() || interaction->isDragStarted();
    2797              :     }
    2798            0 :     return false;
    2799            0 : }
    2800              : 
    2801            0 : bool NotationActionController::isNotEditingElement() const
    2802              : {
    2803            0 :     return !isEditingElement();
    2804              : }
    2805              : 
    2806            0 : void NotationActionController::checkForScoreCorruptions()
    2807              : {
    2808            0 :     project::INotationProjectPtr project = projectSession()->project();
    2809            0 :     if (!project) {
    2810            0 :         return;
    2811              :     }
    2812              : 
    2813            0 :     String fileName = io::filename(project->path()).toString();
    2814              : 
    2815            0 :     Ret ret = project->masterNotation()->masterScore()->sanityCheck();
    2816            0 :     if (ret) {
    2817            0 :         std::string title = muse::mtrc("project", "File “%1” seems not corrupted").arg(fileName).toStdString();
    2818            0 :         std::string body = muse::trc("project", "This file does not seem to contain errors.");
    2819            0 :         interactive()->info(title, body);
    2820            0 :     } else {
    2821            0 :         std::string title = muse::mtrc("project", "File “%1” is corrupted").arg(fileName).toStdString();
    2822            0 :         IInteractive::Text text;
    2823            0 :         text.text = muse::trc("project", "This file contains errors that could cause MuseScore Studio to malfunction. "
    2824            0 :                                          "Please fix those at the earliest, to prevent crashes and further corruptions.");
    2825            0 :         text.detailedText = ret.text();
    2826              : 
    2827            0 :         interactive()->warning(title, text);
    2828            0 :     }
    2829            0 : }
    2830              : 
    2831            0 : void NotationActionController::toggleAutomation()
    2832              : {
    2833            0 :     TRACEFUNC;
    2834              : 
    2835            0 :     IMasterNotationPtr masterNotation = currentMasterNotation();
    2836            0 :     if (!masterNotation) {
    2837            0 :         return;
    2838              :     }
    2839              : 
    2840            0 :     const bool isEnabled = masterNotation->automation()->isAutomationModeEnabled();
    2841            0 :     masterNotation->automation()->setAutomationModeEnabled(!isEnabled);
    2842            0 : }
    2843              : 
    2844            0 : muse::Ret NotationActionController::selectAutomationType(const muse::rcommand::Params& params)
    2845              : {
    2846            0 :     const std::string type = params.at("type").toString();
    2847            0 :     mu::engraving::AutomationType automationType = mu::engraving::AutomationType::Dynamics;
    2848              : 
    2849            0 :     if (type == "tempo") {
    2850            0 :         automationType = mu::engraving::AutomationType::Tempo;
    2851            0 :     } else if (type == "volume") {
    2852            0 :         automationType = mu::engraving::AutomationType::Volume;
    2853            0 :     } else if (type == "pan") {
    2854            0 :         automationType = mu::engraving::AutomationType::Pan;
    2855              :     }
    2856              : 
    2857            0 :     configuration()->setCurrentAutomationType(automationType);
    2858              : 
    2859            0 :     return muse::make_ok();
    2860            0 : }
    2861              : 
    2862            0 : bool NotationActionController::isNoteInputActionAllowed() const
    2863              : {
    2864            0 :     if (!isNoteInputMode() && !toggleNoteInputAllowed()) {
    2865            0 :         return false;
    2866              :     }
    2867              : 
    2868            0 :     return !isTablatureStaff();
    2869              : }
    2870              : 
    2871            0 : muse::Ret NotationActionController::select(const muse::rcommand::Params& params)
    2872              : {
    2873            0 :     SelectionTarget target = Contract<SelectionTarget>::get(params);
    2874            0 :     if (target == SelectionTarget::Undefined) {
    2875            0 :         return muse::make_ret(Ret::Code::BadArgs);
    2876              :     }
    2877              : 
    2878            0 :     PlayMode playMode = Contract<PlayMode>::get(params);
    2879              : 
    2880            0 :     select(target, playMode);
    2881            0 :     return muse::make_ok();
    2882              : }
    2883              : 
    2884            0 : void NotationActionController::select(SelectionTarget target, PlayMode playMode)
    2885              : {
    2886            0 :     auto interaction = currentNotationInteraction();
    2887            0 :     if (!interaction) {
    2888            0 :         return;
    2889              :     }
    2890              : 
    2891            0 :     interaction->select(target);
    2892            0 :     seekSelectedElement();
    2893              : 
    2894            0 :     if (playMode != PlayMode::NoPlay) {
    2895            0 :         playSelectedElement(playMode == PlayMode::PlayChord);
    2896              :     }
    2897            0 : }
    2898              : 
    2899              : // COMMANDS
    2900              : template<muse::rcommand::TypedCommand C>
    2901            0 : void NotationActionController::registerCommand(muse::Ret (NotationActionController::* handler)())
    2902              : {
    2903            0 :     commandDispatcher()->onRequest<C>(this, [this, handler](const C&) {
    2904            0 :         if (!commandsState()->commandState(C::id).enabled) {
    2905            0 :             return muse::make_ret(Ret::Code::NotSupported);
    2906              :         }
    2907            0 :         return (this->*handler)();
    2908              :     });
    2909            0 : }
    2910              : 
    2911              : template<muse::rcommand::TypedCommand C>
    2912            0 : void NotationActionController::registerCommand(muse::Ret (NotationActionController::* handler)(const C&))
    2913              : {
    2914            0 :     commandDispatcher()->onRequest<C>(this, [this, handler](const C& command) {
    2915            0 :         if (!commandsState()->commandState(C::id).enabled) {
    2916            0 :             return muse::make_ret(Ret::Code::NotSupported);
    2917              :         }
    2918              : 
    2919            0 :         muse::Ret ret = (this->*handler)(command);
    2920            0 :         return ret;
    2921            0 :     });
    2922            0 : }
    2923              : 
    2924              : template<muse::rcommand::HasOutput C>
    2925            0 : void NotationActionController::registerGetCommand(muse::RetVal<typename C::Output>(NotationActionController::* handler)() const)
    2926              : {
    2927            0 :     commandDispatcher()->onRequest<C>(this, [this, handler](const C&) {
    2928            0 :         if (!commandsState()->commandState(C::id).enabled) {
    2929            0 :             return muse::RetVal<typename C::Output>::make_ret(Ret::Code::NotSupported);
    2930              :         }
    2931              : 
    2932            0 :         muse::RetVal<typename C::Output> ret = (this->*handler)();
    2933            0 :         return ret;
    2934            0 :     });
    2935            0 : }
    2936              : 
    2937              : template<muse::rcommand::HasOutput C>
    2938              : void NotationActionController::registerGetCommand(muse::RetVal<typename C::Output>(NotationActionController::* handler)(const C&) const)
    2939              : {
    2940              :     commandDispatcher()->onRequest<C>(this, [this, handler](const C& command) {
    2941              :         if (!commandsState()->commandState(C::id).enabled) {
    2942              :             return muse::RetVal<typename C::Output>::make_ret(Ret::Code::NotSupported);
    2943              :         }
    2944              : 
    2945              :         muse::RetVal<typename C::Output> ret = (this->*handler)(command);
    2946              :         return ret;
    2947              :     });
    2948              : }
    2949              : 
    2950            0 : void NotationActionController::registerCommand(const muse::rcommand::Command& command, std::function<void()> handler)
    2951              : {
    2952            0 :     registerCommand(command, handler, nullptr);
    2953            0 : }
    2954              : 
    2955            0 : void NotationActionController::registerCommand(const muse::rcommand::Command& command,
    2956              :                                                std::function<void()> handler,
    2957              :                                                bool (NotationActionController::* enabler)() const)
    2958              : {
    2959            0 :     commandDispatcher()->onRequest(this, command, [this, command, handler, enabler]() {
    2960            0 :         if (!commandsState()->commandState(command).enabled) {
    2961            0 :             return muse::make_ret(Ret::Code::NotSupported);
    2962              :         }
    2963              : 
    2964            0 :         if (enabler && !(this->*enabler)()) {
    2965            0 :             return muse::make_ret(Ret::Code::NotSupported);
    2966              :         }
    2967              : 
    2968            0 :         handler();
    2969            0 :         return muse::make_ok();
    2970              :     });
    2971            0 : }
    2972              : 
    2973            0 : void NotationActionController::registerCommandWithParams(const muse::rcommand::Command& command,
    2974              :                                                          std::function<muse::Ret(const muse::rcommand::Params&)> handler)
    2975              : {
    2976            0 :     commandDispatcher()->onRequest(this, command, [this, command, handler](const rcommand::Request& request) {
    2977            0 :         if (!commandsState()->commandState(command).enabled) {
    2978            0 :             return rcommand::make_response(request, muse::make_ret(Ret::Code::NotSupported));
    2979              :         }
    2980              : 
    2981            0 :         muse::Ret ret = handler(request.params);
    2982            0 :         return rcommand::make_response(request, ret);
    2983            0 :     });
    2984            0 : }
    2985              : 
    2986            0 : void NotationActionController::registerCommand(const muse::rcommand::Command& command, void (NotationActionController::* handler)())
    2987              : {
    2988            0 :     registerCommand(command, handler, nullptr);
    2989            0 : }
    2990              : 
    2991            0 : void NotationActionController::registerCommand(const muse::rcommand::Command& command,
    2992              :                                                void (NotationActionController::* handler)(),
    2993              :                                                bool (NotationActionController::* enabler)() const)
    2994              : {
    2995            0 :     commandDispatcher()->onRequest(this, command, [this, command, handler, enabler]() {
    2996            0 :         if (!commandsState()->commandState(command).enabled) {
    2997            0 :             return muse::make_ret(Ret::Code::NotSupported);
    2998              :         }
    2999              : 
    3000            0 :         if (enabler && !(this->*enabler)()) {
    3001            0 :             return muse::make_ret(Ret::Code::NotSupported);
    3002              :         }
    3003              : 
    3004            0 :         (this->*handler)();
    3005            0 :         return muse::make_ok();
    3006              :     });
    3007            0 : }
    3008              : 
    3009            0 : void NotationActionController::registerCommandWithParams(const muse::rcommand::Command& command,
    3010              :                                                          muse::Ret (NotationActionController::* handler)(const muse::rcommand::Params&))
    3011              : {
    3012            0 :     commandDispatcher()->onRequest(this, command, [this, command, handler](const rcommand::Request& request) {
    3013            0 :         if (!commandsState()->commandState(command).enabled) {
    3014            0 :             return rcommand::make_response(request, muse::make_ret(Ret::Code::NotSupported));
    3015              :         }
    3016              : 
    3017            0 :         muse::Ret ret = (this->*handler)(request.params);
    3018            0 :         return rcommand::make_response(request, ret);
    3019            0 :     });
    3020            0 : }
    3021              : 
    3022            0 : void NotationActionController::registerCommand(const muse::rcommand::Command& command,
    3023              :                                                void (INotationInteraction::* handler)(),
    3024              :                                                PlayMode playMode,
    3025              :                                                bool (NotationActionController::* enabler)() const)
    3026              : {
    3027            0 :     registerCommand(command, [this, handler, playMode, enabler]()
    3028              :     {
    3029            0 :         if (enabler && !(this->*enabler)()) {
    3030            0 :             return muse::make_ret(Ret::Code::NotSupported);
    3031              :         }
    3032              : 
    3033            0 :         INotationPtr notation = currentNotation();
    3034            0 :         if (notation) {
    3035            0 :             (notation->interaction().get()->*handler)();
    3036              : 
    3037            0 :             seekSelectedElement();
    3038              : 
    3039            0 :             if (playMode != PlayMode::NoPlay) {
    3040            0 :                 playSelectedElement(playMode == PlayMode::PlayChord);
    3041              :             }
    3042              :         }
    3043              : 
    3044            0 :         return muse::make_ok();
    3045            0 :     });
    3046            0 : }
    3047              : 
    3048              : template<class P1>
    3049            0 : void NotationActionController::registerCommand(const muse::rcommand::Command& command,
    3050              :                                                void (INotationInteraction::* handler)(P1), P1 param1,
    3051              :                                                PlayMode playMode,
    3052              :                                                bool (NotationActionController::* enabler)() const)
    3053              : {
    3054            0 :     registerCommand(command, [this, handler, param1, playMode]()
    3055              :     {
    3056            0 :         INotationPtr notation = currentNotation();
    3057            0 :         if (notation) {
    3058            0 :             (notation->interaction().get()->*handler)(param1);
    3059              : 
    3060            0 :             seekSelectedElement();
    3061              : 
    3062            0 :             if (playMode != PlayMode::NoPlay) {
    3063            0 :                 playSelectedElement(playMode == PlayMode::PlayChord);
    3064              :             }
    3065              :         }
    3066            0 :     }, enabler);
    3067            0 : }
    3068              : 
    3069            0 : void NotationActionController::registerNoteInputCommand(const muse::rcommand::Command& command, NoteInputMethod method)
    3070              : {
    3071            0 :     registerCommand(command, [this, method]() { toggleNoteInput(method); }, &NotationActionController::toggleNoteInputAllowed);
    3072            0 : }
    3073              : 
    3074            0 : void NotationActionController::registerNoteCommand(const muse::rcommand::Command& command,
    3075              :                                                    NoteName noteName,
    3076              :                                                    NoteAddingMode addingMode)
    3077              : {
    3078            0 :     registerCommand(command, [this, noteName, addingMode]()
    3079              :     {
    3080            0 :         addNote(noteName, addingMode);
    3081            0 :     });
    3082            0 : }
    3083              : 
    3084            0 : void NotationActionController::registerNavigationByFractionCommand(const muse::rcommand::Command& command, const Fraction& fraction)
    3085              : {
    3086            0 :     registerCommand(command, [this, fraction]() {
    3087            0 :         navigateToTextItemByFraction(
    3088            0 :             fraction);
    3089            0 :     }, &NotationActionController::textNavigationByFractionAvailable);
    3090            0 : }
    3091              : 
    3092            0 : void NotationActionController::registerGetCommand(const muse::rcommand::Command& command,
    3093              :                                                   std::function<muse::RetVal<muse::rcommand::Params>()> handler)
    3094              : {
    3095            0 :     commandDispatcher()->onRequest(this, command, [this, command, handler](const rcommand::Request& request) {
    3096            0 :         if (!commandsState()->commandState(command).enabled) {
    3097            0 :             return rcommand::make_response(request, muse::make_ret(Ret::Code::NotSupported));
    3098              :         }
    3099              : 
    3100            0 :         muse::RetVal<muse::rcommand::Params> rv = handler();
    3101            0 :         return rcommand::make_response(request, rv.ret, rv.val);
    3102            0 :     });
    3103            0 : }
    3104              : 
    3105            0 : void NotationActionController::registerGetCommand(const muse::rcommand::Command& command,
    3106              :                                                   muse::RetVal<muse::rcommand::Params>(NotationActionController::* handler)() const)
    3107              : {
    3108            0 :     commandDispatcher()->onRequest(this, command, [this, command, handler](const rcommand::Request& request) {
    3109            0 :         if (!commandsState()->commandState(command).enabled) {
    3110            0 :             return rcommand::make_response(request, muse::make_ret(Ret::Code::NotSupported));
    3111              :         }
    3112              : 
    3113            0 :         muse::RetVal<muse::rcommand::Params> rv = (this->*handler)();
    3114            0 :         return rcommand::make_response(request, rv.ret, rv.val);
    3115            0 :     });
    3116            0 : }
    3117              : 
    3118            0 : void NotationActionController::registerViewCommand(const muse::rcommand::Command& command, void (INotationViewController::* handler)())
    3119              : {
    3120            0 :     commandDispatcher()->onRequest(this, command, [this, command, handler]() {
    3121            0 :         if (!commandsState()->commandState(command).enabled) {
    3122            0 :             return muse::make_ret(Ret::Code::NotSupported);
    3123              :         }
    3124              : 
    3125            0 :         if (!m_viewController) {
    3126            0 :             LOGE() << "command: " << command.isValid() << " NotSupported, viewController is null";
    3127            0 :             return muse::make_ret(Ret::Code::NotSupported);
    3128              :         }
    3129              : 
    3130            0 :         (m_viewController->*handler)();
    3131            0 :         return muse::make_ok();
    3132              :     });
    3133            0 : }
    3134              : 
    3135              : template<typename P1>
    3136            0 : void NotationActionController::registerViewCommand(const muse::rcommand::Command& command,
    3137              :                                                    void (INotationViewController::* handler)(P1),
    3138              :                                                    P1 p1)
    3139              : {
    3140            0 :     commandDispatcher()->onRequest(this, command, [this, command, handler, p1]() {
    3141            0 :         if (!commandsState()->commandState(command).enabled) {
    3142            0 :             return muse::make_ret(Ret::Code::NotSupported);
    3143              :         }
    3144              : 
    3145            0 :         if (!m_viewController) {
    3146            0 :             LOGE() << "command: " << command.isValid() << " NotSupported, viewController is null";
    3147            0 :             return muse::make_ret(Ret::Code::NotSupported);
    3148              :         }
    3149              : 
    3150            0 :         (m_viewController->*handler)(p1);
    3151            0 :         return muse::make_ok();
    3152              :     });
    3153            0 : }
    3154              : 
    3155            0 : muse::Ret NotationActionController::zoomToPercent(const muse::rcommand::Params& params)
    3156              : {
    3157            0 :     if (!params.contains("percent")) {
    3158            0 :         return muse::make_ret(Ret::Code::BadArgs);
    3159              :     }
    3160              : 
    3161            0 :     if (!m_viewController) {
    3162            0 :         LOGE() << "command: " << ZOOM_TO_PERCENT_COMMAND.toString() << " NotSupported, viewController is null";
    3163            0 :         return muse::make_ret(Ret::Code::NotSupported);
    3164              :     }
    3165              : 
    3166            0 :     int zoomPercentage = params.at("percent").toInt();
    3167            0 :     m_viewController->setZoom(zoomPercentage);
    3168            0 :     return muse::make_ok();
    3169              : }
    3170              : 
    3171            0 : muse::Ret NotationActionController::setPianoKeyboardNumberOfKeys(const muse::rcommand::Params& params)
    3172              : {
    3173            0 :     if (!params.contains("keys")) {
    3174            0 :         return muse::make_ret(Ret::Code::BadArgs);
    3175              :     }
    3176              : 
    3177            0 :     int numberOfKeys = params.at("keys").toInt();
    3178            0 :     sceneConfiguration()->setPianoKeyboardNumberOfKeys(numberOfKeys);
    3179            0 :     return muse::make_ok();
    3180              : }
        

Generated by: LCOV version 2.0-1