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 : }
|