/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-Studio-CLA-applies
*
* MuseScore Studio
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore Limited and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "notationactioncontroller.h"
#include "global/io/file.h"
#include "global/translation.h"
#include "global/types/ret.h"
#include "engraving/dom/harmony.h"
#include "engraving/dom/masterscore.h"
#include "engraving/dom/note.h"
#include "engraving/dom/chord.h"
#include "engraving/dom/text.h"
#include "engraving/dom/sig.h"
#include "engraving/editing/noteinput.h"
#include "notation/imasternotation.h"
#include "notation/inotation.h"
#include "notation/inotationautomation.h" // IWYU pragma: keep
#include "notation/inotationelements.h"
#include "notation/inotationmidiinput.h"
#include "notation/inotationnoteinput.h"
#include "notation/inotationplayback.h" // IWYU pragma: keep
#include "notation/inotationselection.h"
#include "notation/inotationstyle.h"
#include "notation/inotationundostack.h"
#include "notation/inotationinteraction.h"
#include "project/inotationproject.h"
#include "qml/MuseScore/NotationScene/abstractelementpopupmodel.h"
#include "qml/MuseScore/NotationScene/notationviewinputcontroller.h"
#include "../notationcommands.h"
#include "../notationcommandsenums.h"
#include "playback/playbackcommands.h"
#include "log.h"
using namespace mu;
using namespace muse;
using namespace muse::io;
using namespace muse::rcommand;
using namespace mu::notation;
using namespace mu::engraving;
static constexpr qreal STRETCH_STEP = 0.1;
static constexpr bool NEAR_NOTE_OR_REST = true;
static constexpr bool DONT_PLAY_CHORD = false;
static const muse::Uri NOTATION_REVIEW_PAGE_URI("musescore://notation/review");
using EngravingDebuggingOptions = engraving::IEngravingConfiguration::DebuggingOptions;
static const std::map s_debuggingCommands = {
{ SHOW_ELEMENT_BOUNDING_RECTS_COMMAND, &EngravingDebuggingOptions::showElementBoundingRects },
{ COLOR_ELEMENT_SHAPES_COMMAND, &EngravingDebuggingOptions::colorElementShapes },
{ SHOW_SEGMENT_SHAPES_COMMAND, &EngravingDebuggingOptions::showSegmentShapes },
{ COLOR_SEGMENT_SHAPES_COMMAND, &EngravingDebuggingOptions::colorSegmentShapes },
{ SHOW_SKYLINES_COMMAND, &EngravingDebuggingOptions::showSkylines },
{ SHOW_SYSTEM_BOUNDING_RECTS_COMMAND, &EngravingDebuggingOptions::showSystemBoundingRects },
{ SHOW_ELEMENT_MASKS_COMMAND, &EngravingDebuggingOptions::showElementMasks },
{ SHOW_LINE_ATTACH_POINTS_COMMAND, &EngravingDebuggingOptions::showLineAttachPoints },
{ MARK_EMPTY_STAFF_COMMAND, &EngravingDebuggingOptions::markEmptyStaffVisibilityOverrides },
{ MARK_CORRUPTED_MEASURES_COMMAND, &EngravingDebuggingOptions::markCorruptedMeasures },
{ SHOW_GAP_RESTS_COMMAND, &EngravingDebuggingOptions::showGapRests },
{ SHOW_ORIGIN_AND_COMBINED_COMMAND, &EngravingDebuggingOptions::showOriginAndCombinedStaves },
};
//! NOTE Just for more readable
using Controller = NotationActionController;
using Interaction = INotationInteraction;
using ViewController = INotationViewController;
void NotationActionController::init()
{
TRACEFUNC;
// global commands
registerCommand(CANCEL_COMMAND, &Controller::resetState);
registerCommand(UNDO_COMMAND, &Interaction::undo);
registerCommand(REDO_COMMAND, &Interaction::redo);
// navigation and selection commands
registerCommandWithParams(SELECT_COMMAND, &Controller::select);
registerCommand(GOTO_FIRST_ELEMENT_COMMAND, [this]() { select(SelectionTarget::FirstItem, PlayMode::PlayChord); });
registerCommand(GOTO_LAST_ELEMENT_COMMAND, [this]() { select(SelectionTarget::LastItem, PlayMode::PlayChord); });
registerCommand(GOTO_NEXT_ELEMENT_COMMAND, [this]() { select(SelectionTarget::NextItem, PlayMode::PlayNote); });
registerCommand(GOTO_PREV_ELEMENT_COMMAND, [this]() { select(SelectionTarget::PrevItem, PlayMode::PlayNote); });
registerCommand(GOTO_NEXT_SEGMENT_ELEMENT_COMMAND, [this]() { select(SelectionTarget::NextSegmentItem, PlayMode::PlayNote); });
registerCommand(GOTO_PREV_SEGMENT_ELEMENT_COMMAND, [this]() { select(SelectionTarget::PrevSegmentItem, PlayMode::PlayNote); });
registerCommand(GOTO_NEXT_TRACK_COMMAND, [this]() { select(SelectionTarget::NextTrack, PlayMode::PlayChord); });
registerCommand(GOTO_PREV_TRACK_COMMAND, [this]() { select(SelectionTarget::PrevTrack, PlayMode::PlayChord); });
registerCommand(GOTO_NEXT_FRAME_COMMAND, [this]() { select(SelectionTarget::NextFrame); });
registerCommand(GOTO_PREV_FRAME_COMMAND, [this]() { select(SelectionTarget::PrevFrame); });
registerCommand(GOTO_NEXT_SYSTEM_COMMAND, [this]() { select(SelectionTarget::NextSystem); });
registerCommand(GOTO_PREV_SYSTEM_COMMAND, [this]() { select(SelectionTarget::PrevSystem); });
registerCommand(GOTO_UPNOTE_IN_CHORD_COMMAND, [this]() { select(SelectionTarget::UpNoteInChord); });
registerCommand(GOTO_DOWNNOTE_IN_CHORD_COMMAND, [this]() { select(SelectionTarget::DownNoteInChord); });
registerCommand(GOTO_TOPNOTE_IN_CHORD_COMMAND, [this]() { select(SelectionTarget::TopNoteInChord); });
registerCommand(GOTO_BOTTOMNOTE_IN_CHORD_COMMAND, [this]() { select(SelectionTarget::BottomNoteInChord); });
registerCommand(GOTO_TOP_STAFF_COMMAND, [this]() { select(SelectionTarget::TopStaff, PlayMode::PlayChord); });
registerCommand(GOTO_EMPTY_TRAILING_MEASURE_COMMAND, [this]() { select(SelectionTarget::EmptyTrailingMeasure); });
registerCommand(SELECT_SIMILAR_COMMAND, [this]() { select(SelectionTarget::Similar); });
registerCommand(SELECT_SIMILAR_IN_STAFF_COMMAND, [this]() { select(SelectionTarget::SimilarInStaff); });
registerCommand(SELECT_SIMILAR_IN_RANGE_COMMAND, [this]() { select(SelectionTarget::SimilarInRange); });
registerCommand(SELECT_NOTES_IN_CHORD_COMMAND, [this]() { select(SelectionTarget::NotesInChord); });
registerCommand(SELECT_ALL_COMMAND, [this]() { select(SelectionTarget::All); });
registerCommand(SELECT_SECTION_COMMAND, [this]() { select(SelectionTarget::Section); });
registerCommand(GET_LOCATION_COMMAND, &Interaction::getLocation);
registerCommand(ADD_TO_SELECTION_NEXT_CHORD_COMMAND, &Interaction::addToSelection, SelectionTarget::NextChord);
registerCommand(ADD_TO_SELECTION_PREV_CHORD_COMMAND, &Interaction::addToSelection, SelectionTarget::PrevChord);
registerCommand(ADD_TO_SELECTION_NEXT_MEASURE_COMMAND, &Interaction::addToSelection, SelectionTarget::NextMeasure);
registerCommand(ADD_TO_SELECTION_PREV_MEASURE_COMMAND, &Interaction::addToSelection, SelectionTarget::PrevMeasure);
registerCommand(ADD_TO_SELECTION_ABOVE_STAFF_COMMAND, &Interaction::addToSelection, SelectionTarget::AboveStaff);
registerCommand(ADD_TO_SELECTION_BELOW_STAFF_COMMAND, &Interaction::addToSelection, SelectionTarget::BelowStaff);
registerCommand(ADD_TO_SELECTION_BEGIN_SYSTEM_COMMAND, &Interaction::expandSelection, ExpandSelectionMode::BeginSystem);
registerCommand(ADD_TO_SELECTION_END_SYSTEM_COMMAND, &Interaction::expandSelection, ExpandSelectionMode::EndSystem);
registerCommand(ADD_TO_SELECTION_BEGIN_SCORE_COMMAND, &Interaction::expandSelection, ExpandSelectionMode::BeginScore);
registerCommand(ADD_TO_SELECTION_END_SCORE_COMMAND, &Interaction::expandSelection, ExpandSelectionMode::EndScore);
registerCommand(OPEN_SELECTION_OPTIONS_COMMAND, &Controller::openSelectionMoreOptions);
// text navigation commands
registerCommand(EDITTEXT_NEXT_WORD_COMMAND, [this]() { nextWord(); });
registerCommand(EDITTEXT_NEXT_ELEMENT_COMMAND, [this]() { nextTextElement(); });
registerCommand(EDITTEXT_PREV_ELEMENT_COMMAND, [this]() { prevTextElement(); });
registerCommand(EDITTEXT_NEXT_BEAT_COMMAND, &Controller::nextBeatTextElement, &Controller::textNavigationByBeatsAvailable);
registerCommand(EDITTEXT_PREV_BEAT_COMMAND, &Controller::prevBeatTextElement, &Controller::textNavigationByBeatsAvailable);
registerNavigationByFractionCommand(EDITTEXT_ADVANCE_LONGA_COMMAND, Fraction(4, 1));
registerNavigationByFractionCommand(EDITTEXT_ADVANCE_BREVE_COMMAND, Fraction(2, 1));
registerNavigationByFractionCommand(EDITTEXT_ADVANCE_1_COMMAND, Fraction(1, 1));
registerNavigationByFractionCommand(EDITTEXT_ADVANCE_2_COMMAND, Fraction(1, 2));
registerNavigationByFractionCommand(EDITTEXT_ADVANCE_4_COMMAND, Fraction(1, 4));
registerNavigationByFractionCommand(EDITTEXT_ADVANCE_8_COMMAND, Fraction(1, 8));
registerNavigationByFractionCommand(EDITTEXT_ADVANCE_16_COMMAND, Fraction(1, 16));
registerNavigationByFractionCommand(EDITTEXT_ADVANCE_32_COMMAND, Fraction(1, 32));
registerNavigationByFractionCommand(EDITTEXT_ADVANCE_64_COMMAND, Fraction(1, 64));
// lyrics editing commands
registerCommand(EDITLYRIC_NEXT_VERSE_COMMAND, &Interaction::navigateToLyricsVerse, MoveDirection::Down);
registerCommand(EDITLYRIC_PREV_VERSE_COMMAND, &Interaction::navigateToLyricsVerse, MoveDirection::Up);
registerCommand(EDITLYRIC_NEXT_SYLLABLE_COMMAND, &Interaction::navigateToNextSyllable);
registerCommand(EDITLYRIC_ADD_MELISMA_COMMAND, &Interaction::addMelisma);
registerCommand(EDITLYRIC_ADD_VERSE_COMMAND, &Interaction::addLyricsVerse);
// text editing commands
registerCommand(EDITTEXT_TOGGLE_BOLD_COMMAND, &Interaction::toggleBold);
registerCommand(EDITTEXT_TOGGLE_ITALIC_COMMAND, &Interaction::toggleItalic);
registerCommand(EDITTEXT_TOGGLE_UNDERLINE_COMMAND, &Interaction::toggleUnderline);
registerCommand(EDITTEXT_TOGGLE_STRIKE_COMMAND, &Interaction::toggleStrike);
registerCommand(EDITTEXT_TOGGLE_SUBSCRIPT_COMMAND, &Interaction::toggleSubScript);
registerCommand(EDITTEXT_TOGGLE_SUPERSCRIPT_COMMAND, &Interaction::toggleSuperScript);
// note input commands
registerNoteInputCommand(TOGGLE_NOTE_INPUT_COMMAND, NoteInputMethod::UNKNOWN /*default*/);
registerNoteInputCommand(TOGGLE_NOTE_INPUT_BY_NOTE_NAME_COMMAND, NoteInputMethod::BY_NOTE_NAME);
registerNoteInputCommand(TOGGLE_NOTE_INPUT_BY_DURATION_COMMAND, NoteInputMethod::BY_DURATION);
registerNoteInputCommand(TOGGLE_NOTE_INPUT_RHYTHM_COMMAND, NoteInputMethod::RHYTHM);
registerNoteInputCommand(TOGGLE_NOTE_INPUT_REPITCH_COMMAND, NoteInputMethod::REPITCH);
registerNoteInputCommand(TOGGLE_NOTE_INPUT_REALTIME_AUTO_COMMAND, NoteInputMethod::REALTIME_AUTO);
registerNoteInputCommand(TOGGLE_NOTE_INPUT_REALTIME_MANUAL_COMMAND, NoteInputMethod::REALTIME_MANUAL);
registerNoteInputCommand(TOGGLE_NOTE_INPUT_TIMEWISE_COMMAND, NoteInputMethod::TIMEWISE);
registerCommand(TOGGLE_INSERT_MODE_COMMAND, [this]() { toggleNoteInputInsert(); }, &NotationActionController::isNotEditingElement);
registerCommand(REALTIME_ADVANCE_COMMAND, &Controller::realtimeAdvance);
registerGetCommand(GET_NOTEINPUT_STATE_COMMAND, &NotationActionController::requestNoteInputState);
registerCommand(SET_DURATION_LONGA_COMMAND, [this]() { setDuration(DurationType::V_LONG); });
registerCommand(SET_DURATION_BREVE_COMMAND, [this]() { setDuration(DurationType::V_BREVE); });
registerCommand(SET_DURATION_WHOLE_COMMAND, [this]() { setDuration(DurationType::V_WHOLE); });
registerCommand(SET_DURATION_HALF_COMMAND, [this]() { setDuration(DurationType::V_HALF); });
registerCommand(SET_DURATION_QUARTER_COMMAND, [this]() { setDuration(DurationType::V_QUARTER); });
registerCommand(SET_DURATION_EIGHTH_COMMAND, [this]() { setDuration(DurationType::V_EIGHTH); });
registerCommand(SET_DURATION_16TH_COMMAND, [this]() { setDuration(DurationType::V_16TH); });
registerCommand(SET_DURATION_32ND_COMMAND, [this]() { setDuration(DurationType::V_32ND); });
registerCommand(SET_DURATION_64TH_COMMAND, [this]() { setDuration(DurationType::V_64TH); });
registerCommand(SET_DURATION_128TH_COMMAND, [this]() { setDuration(DurationType::V_128TH); });
registerCommand(SET_DURATION_256TH_COMMAND, [this]() { setDuration(DurationType::V_256TH); });
registerCommand(SET_DURATION_512TH_COMMAND, [this]() { setDuration(DurationType::V_512TH); });
registerCommand(SET_DURATION_1024TH_COMMAND, [this]() { setDuration(DurationType::V_1024TH); });
registerCommand(SET_DOUBLE_DURATION_COMMAND, [this]() { increaseDecreaseDuration(-1, false); });
registerCommand(SET_HALVE_DURATION_COMMAND, [this]() { increaseDecreaseDuration(1, false); });
registerCommand(SET_DOUBLE_DURATION_DOTTED_COMMAND, [this]() { increaseDecreaseDuration(-1, true); });
registerCommand(SET_HALVE_DURATION_DOTTED_COMMAND, [this]() { increaseDecreaseDuration(1, true); });
registerCommand(EXTEND_TO_NEXT_NOTE_COMMAND, &Interaction::extendToNextNote);
registerCommand(TOGGLE_DOT_COMMAND, [this]() { toggleDots(1); });
registerCommand(TOGGLE_DOT2_COMMAND, [this]() { toggleDots(2); });
registerCommand(TOGGLE_DOT3_COMMAND, [this]() { toggleDots(3); });
registerCommand(TOGGLE_DOT4_COMMAND, [this]() { toggleDots(4); });
registerCommand(TOGGLE_REST_COMMAND, [this]() { toggleRest(); });
registerCommand(TOGGLE_FLAT2_COMMAND, [this]() { toggleAccidental(AccidentalType::FLAT2); });
registerCommand(TOGGLE_FLAT_COMMAND, [this]() { toggleAccidental(AccidentalType::FLAT); });
registerCommand(TOGGLE_NAT_COMMAND, [this]() { toggleAccidental(AccidentalType::NATURAL); });
registerCommand(TOGGLE_SHARP_COMMAND, [this]() { toggleAccidental(AccidentalType::SHARP); });
registerCommand(TOGGLE_SHARP2_COMMAND, [this]() { toggleAccidental(AccidentalType::SHARP2); });
registerCommand(ADD_SHARP2_COMMAND, &Interaction::changeAccidental, mu::engraving::AccidentalType::SHARP2, PlayMode::PlayNote);
registerCommand(ADD_SHARP_COMMAND, &Interaction::changeAccidental, mu::engraving::AccidentalType::SHARP, PlayMode::PlayNote);
registerCommand(ADD_NAT_COMMAND, &Interaction::changeAccidental, mu::engraving::AccidentalType::NATURAL, PlayMode::PlayNote);
registerCommand(ADD_FLAT_COMMAND, &Interaction::changeAccidental, mu::engraving::AccidentalType::FLAT, PlayMode::PlayNote);
registerCommand(ADD_FLAT2_COMMAND, &Interaction::changeAccidental, mu::engraving::AccidentalType::FLAT2, PlayMode::PlayNote);
registerCommand(TOGGLE_TIE_COMMAND, &Controller::addTie);
registerCommand(ADD_SLUR_COMMAND, &Controller::addSlur);
registerCommand(TOGGLE_LV_COMMAND, &Controller::addLaissezVib);
registerCommand(TOGGLE_MARCATO_COMMAND, [this]() { toggleArticulation(SymbolId::articMarcatoAbove); });
registerCommand(TOGGLE_SFORZATO_COMMAND, [this]() { toggleArticulation(SymbolId::articAccentAbove); });
registerCommand(TOGGLE_TENUTO_COMMAND, [this]() { toggleArticulation(SymbolId::articTenutoAbove); });
registerCommand(TOGGLE_STACCATO_COMMAND, [this]() { toggleArticulation(SymbolId::articStaccatoAbove); });
registerCommand(USE_VOICE_1_COMMAND, [this]() { changeVoice(0); });
registerCommand(USE_VOICE_2_COMMAND, [this]() { changeVoice(1); });
registerCommand(USE_VOICE_3_COMMAND, [this]() { changeVoice(2); });
registerCommand(USE_VOICE_4_COMMAND, [this]() { changeVoice(3); });
registerCommand(SWAP_VOICE_X12_COMMAND, [this]() { swapVoices(0, 1); });
registerCommand(SWAP_VOICE_X13_COMMAND, [this]() { swapVoices(0, 2); });
registerCommand(SWAP_VOICE_X14_COMMAND, [this]() { swapVoices(0, 3); });
registerCommand(SWAP_VOICE_X23_COMMAND, [this]() { swapVoices(1, 2); });
registerCommand(SWAP_VOICE_X24_COMMAND, [this]() { swapVoices(1, 3); });
registerCommand(SWAP_VOICE_X34_COMMAND, [this]() { swapVoices(2, 3); });
registerCommand(FLIP_COMMAND, &Interaction::flipSelection);
registerCommand(FLIP_HORIZONTALLY_COMMAND, &Interaction::flipSelectionHorizontally);
registerCommandWithParams(ADD_NOTE_COMMAND, &Controller::addNote);
registerCommandWithParams(ADD_DRUM_NOTE_COMMAND, &Controller::addDrumNote);
registerNoteCommand(ENTER_NOTE_C_COMMAND, NoteName::C);
registerNoteCommand(ENTER_NOTE_D_COMMAND, NoteName::D);
registerNoteCommand(ENTER_NOTE_E_COMMAND, NoteName::E);
registerNoteCommand(ENTER_NOTE_F_COMMAND, NoteName::F);
registerNoteCommand(ENTER_NOTE_G_COMMAND, NoteName::G);
registerNoteCommand(ENTER_NOTE_A_COMMAND, NoteName::A);
registerNoteCommand(ENTER_NOTE_B_COMMAND, NoteName::B);
registerNoteCommand(ADD_NOTE_C_COMMAND, NoteName::C, NoteAddingMode::CurrentChord);
registerNoteCommand(ADD_NOTE_D_COMMAND, NoteName::D, NoteAddingMode::CurrentChord);
registerNoteCommand(ADD_NOTE_E_COMMAND, NoteName::E, NoteAddingMode::CurrentChord);
registerNoteCommand(ADD_NOTE_F_COMMAND, NoteName::F, NoteAddingMode::CurrentChord);
registerNoteCommand(ADD_NOTE_G_COMMAND, NoteName::G, NoteAddingMode::CurrentChord);
registerNoteCommand(ADD_NOTE_A_COMMAND, NoteName::A, NoteAddingMode::CurrentChord);
registerNoteCommand(ADD_NOTE_B_COMMAND, NoteName::B, NoteAddingMode::CurrentChord);
registerNoteCommand(INSERT_NOTE_C_COMMAND, NoteName::C, NoteAddingMode::InsertChord);
registerNoteCommand(INSERT_NOTE_D_COMMAND, NoteName::D, NoteAddingMode::InsertChord);
registerNoteCommand(INSERT_NOTE_E_COMMAND, NoteName::E, NoteAddingMode::InsertChord);
registerNoteCommand(INSERT_NOTE_F_COMMAND, NoteName::F, NoteAddingMode::InsertChord);
registerNoteCommand(INSERT_NOTE_G_COMMAND, NoteName::G, NoteAddingMode::InsertChord);
registerNoteCommand(INSERT_NOTE_A_COMMAND, NoteName::A, NoteAddingMode::InsertChord);
registerNoteCommand(INSERT_NOTE_B_COMMAND, NoteName::B, NoteAddingMode::InsertChord);
registerCommand(ENTER_REST_COMMAND, &Interaction::putRestToSelection);
registerCommand(OPEN_TUPLET_CONFIGURE_COMMAND, [this]() { openTupletOtherDialog(); });
registerCommandWithParams(ADD_TUPLET_COMMAND, &Controller::putTuplet);
registerCommand(ADD_DUPLET_COMMAND, [this]() { putTuplet(2); });
registerCommand(ADD_TRIPLET_COMMAND, [this]() { putTuplet(3); });
registerCommand(ADD_QUADRUPLET_COMMAND, [this]() { putTuplet(4); });
registerCommand(ADD_QUINTUPLET_COMMAND, [this]() { putTuplet(5); });
registerCommand(ADD_SEXTUPLET_COMMAND, [this]() { putTuplet(6); });
registerCommand(ADD_SEPTUPLET_COMMAND, [this]() { putTuplet(7); });
registerCommand(ADD_OCTUPLET_COMMAND, [this]() { putTuplet(8); });
registerCommand(ADD_NONUPLET_COMMAND, [this]() { putTuplet(9); });
registerCommand(INSERT_HBOX_COMMAND, [this]() { addBoxes(BoxType::Horizontal, 1, AddBoxesTarget::BeforeSelection); });
registerCommand(INSERT_VBOX_COMMAND, [this]() { addBoxes(BoxType::Vertical, 1, AddBoxesTarget::BeforeSelection); });
registerCommand(INSERT_TEXTFRAME_COMMAND, [this]() { addBoxes(BoxType::Text, 1, AddBoxesTarget::BeforeSelection); });
registerCommand(INSERT_FRETFRAME_COMMAND, [this]() { addBoxes(BoxType::Fret, 1, AddBoxesTarget::BeforeSelection); });
registerCommand(APPEND_HBOX_COMMAND, [this]() { addBoxes(BoxType::Horizontal, 1, AddBoxesTarget::AtEndOfScore); });
registerCommand(APPEND_VBOX_COMMAND, [this]() { addBoxes(BoxType::Vertical, 1, AddBoxesTarget::AtEndOfScore); });
registerCommand(APPEND_TEXTFRAME_COMMAND, [this]() { addBoxes(BoxType::Text, 1, AddBoxesTarget::AtEndOfScore); });
registerCommand(APPEND_FRETFRAME_COMMAND, [this]() { addBoxes(BoxType::Fret, 1, AddBoxesTarget::AtEndOfScore); });
registerCommand(ADD_FRETBOARD_DIAGRAM_COMMAND, &Controller::addFretboardDiagram);
registerCommand(ADD_OTTAVA_8VA_COMMAND, &Interaction::addOttavaToSelection, OttavaType::OTTAVA_8VA);
registerCommand(ADD_OTTAVA_8VB_COMMAND, &Interaction::addOttavaToSelection, OttavaType::OTTAVA_8VB);
registerCommand(ADD_DYNAMIC_COMMAND, &Interaction::toggleDynamicPopup);
registerCommand(ADD_HAIRPIN_COMMAND, &Interaction::addHairpinsToSelection, HairpinType::CRESC_HAIRPIN);
registerCommand(ADD_HAIRPIN_REVERSE_COMMAND, &Interaction::addHairpinsToSelection, HairpinType::DIM_HAIRPIN);
registerCommand(INCREASE_DYNAMIC_COMMAND, &Interaction::increaseDecreaseSelectedDynamicsValues, /*delta*/ 1);
registerCommand(DECREASE_DYNAMIC_COMMAND, &Interaction::increaseDecreaseSelectedDynamicsValues, /*delta*/ -1);
registerCommand(ADD_NOTELINE_COMMAND, &Interaction::addAnchoredLineToSelectedNotes);
registerCommand(ADD_IMAGE_COMMAND, [this]() { addImage(); });
registerCommand(ADD_UP_BOW_COMMAND, &Interaction::toggleArticulation, mu::engraving::SymId::stringsUpBow);
registerCommand(ADD_DOWN_BOW_COMMAND, &Interaction::toggleArticulation, mu::engraving::SymId::stringsDownBow);
// add text commands
registerCommand(ADD_TITLE_TEXT_COMMAND, [this]() { addText(TextStyleType::TITLE); });
registerCommand(ADD_SUBTITLE_TEXT_COMMAND, [this]() { addText(TextStyleType::SUBTITLE); });
registerCommand(ADD_COMPOSER_TEXT_COMMAND, [this]() { addText(TextStyleType::COMPOSER); });
registerCommand(ADD_LYRICIST_TEXT_COMMAND, [this]() { addText(TextStyleType::LYRICIST); });
registerCommand(ADD_PART_TEXT_COMMAND, [this]() { addText(TextStyleType::INSTRUMENT_EXCERPT); });
registerCommand(ADD_FRAME_TEXT_COMMAND, [this]() { addText(TextStyleType::FRAME); });
registerCommand(ADD_SYSTEM_TEXT_COMMAND, [this]() { addText(TextStyleType::SYSTEM); });
registerCommand(ADD_STAFF_TEXT_COMMAND, [this]() { addText(TextStyleType::STAFF); });
registerCommand(ADD_EXPRESSION_TEXT_COMMAND, [this]() { addText(TextStyleType::EXPRESSION); });
registerCommand(ADD_REHEARSALMARK_TEXT_COMMAND, [this]() { addText(TextStyleType::REHEARSAL_MARK); });
registerCommand(ADD_INSTRUMENT_CHANGE_TEXT_COMMAND, [this]() { addText(TextStyleType::INSTRUMENT_CHANGE); });
registerCommand(ADD_FINGERING_TEXT_COMMAND, [this]() { addText(TextStyleType::FINGERING); });
registerCommand(ADD_STICKING_TEXT_COMMAND, [this]() { addText(TextStyleType::STICKING); });
registerCommand(ADD_CHORD_TEXT_COMMAND, [this]() { addText(TextStyleType::HARMONY_A); });
registerCommand(ADD_ROMAN_NUMERAL_TEXT_COMMAND, [this]() { addText(TextStyleType::HARMONY_ROMAN); });
registerCommand(ADD_NASHVILLE_NUMBER_TEXT_COMMAND, [this]() { addText(TextStyleType::HARMONY_NASHVILLE); });
registerCommand(ADD_LYRICS_COMMAND, [this]() { addText(TextStyleType::LYRICS_ODD); });
registerCommand(ADD_TEMPO_COMMAND, [this]() { addText(TextStyleType::TEMPO); });
registerCommand(ADD_FIGURED_BASS_COMMAND, [this]() { addFiguredBass(); });
// add grace notes commands
registerCommand(ADD_ACCIACCATURA_COMMAND, &Interaction::addGraceNotesToSelectedNotes, GraceNoteType::ACCIACCATURA);
registerCommand(ADD_APPOGGIATURA_COMMAND, &Interaction::addGraceNotesToSelectedNotes, GraceNoteType::APPOGGIATURA);
registerCommand(ADD_GRACE4_COMMAND, &Interaction::addGraceNotesToSelectedNotes, GraceNoteType::GRACE4);
registerCommand(ADD_GRACE16_COMMAND, &Interaction::addGraceNotesToSelectedNotes, GraceNoteType::GRACE16);
registerCommand(ADD_GRACE32_COMMAND, &Interaction::addGraceNotesToSelectedNotes, GraceNoteType::GRACE32);
registerCommand(ADD_GRACE8_AFTER_COMMAND, &Interaction::addGraceNotesToSelectedNotes, GraceNoteType::GRACE8_AFTER);
registerCommand(ADD_GRACE16_AFTER_COMMAND, &Interaction::addGraceNotesToSelectedNotes, GraceNoteType::GRACE16_AFTER);
registerCommand(ADD_GRACE32_AFTER_COMMAND, &Interaction::addGraceNotesToSelectedNotes, GraceNoteType::GRACE32_AFTER);
// add beam commands
registerCommand(ADD_BEAM_AUTO_COMMAND, &Interaction::addBeamToSelectedChordRests, BeamMode::AUTO);
registerCommand(ADD_BEAM_NONE_COMMAND, &Interaction::addBeamToSelectedChordRests, BeamMode::NONE);
registerCommand(ADD_BEAM_BEGIN_COMMAND, &Interaction::addBeamToSelectedChordRests, BeamMode::BEGIN);
registerCommand(ADD_BEAM_BEGIN16_COMMAND, &Interaction::addBeamToSelectedChordRests, BeamMode::BEGIN16);
registerCommand(ADD_BEAM_BEGIN32_COMMAND, &Interaction::addBeamToSelectedChordRests, BeamMode::BEGIN32);
registerCommand(ADD_BEAM_MID_COMMAND, &Interaction::addBeamToSelectedChordRests, BeamMode::MID);
registerCommand(ADD_BEAM_SELECTED_RANGE_COMMAND, &Interaction::beamSelectedRange);
// add brackets commands
registerCommand(ADD_BRACKETS_COMMAND, &Interaction::addBracketsToSelection, BracketsType::Brackets);
registerCommand(ADD_BRACES_COMMAND, &Interaction::addBracketsToSelection, BracketsType::Braces);
registerCommand(ADD_PARENTHESES_COMMAND, &Interaction::addBracketsToSelection, BracketsType::Parentheses);
// add ornament commands
registerCommand(ADD_TURN_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentTurn);
registerCommand(ADD_TURN_INVERTED_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentTurnInverted);
registerCommand(ADD_TURN_SLASH_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentTurnSlash);
registerCommand(ADD_TURN_UP_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentTurnUp);
registerCommand(ADD_TURN_INVERTED_UP_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentTurnUpS);
registerCommand(ADD_TRILL_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentTrill);
registerCommand(ADD_SHORT_TRILL_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentShortTrill);
registerCommand(ADD_MORDENT_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentMordent);
registerCommand(ADD_HAYDN_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentHaydn);
registerCommand(ADD_TREMBLEMENT_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentTremblement);
registerCommand(ADD_PRALL_MORDENT_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentPrallMordent);
registerCommand(ADD_SHAKE_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentShake3);
registerCommand(ADD_SHAKE_MUFFAT_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentShakeMuffat1);
registerCommand(ADD_TREMBLEMENT_COUPERIN_COMMAND, &Interaction::toggleOrnament, mu::engraving::SymId::ornamentTremblementCouperin);
// clef commands
registerCommand(ADD_CLEF_VIOLIN_COMMAND, [this]() { insertClef(mu::engraving::ClefType::G); });
registerCommand(ADD_CLEF_BASS_COMMAND, [this]() { insertClef(mu::engraving::ClefType::F); });
registerCommand(ADD_FULL_MEASURE_REST_COMMAND, &Controller::addFullMeasureRest);
// interval commands
registerCommand(ADD_INTERVAL_PLUS_1_COMMAND, &Interaction::addIntervalToSelectedNotes, 1);
registerCommand(ADD_INTERVAL_PLUS_2_COMMAND, &Interaction::addIntervalToSelectedNotes, 2);
registerCommand(ADD_INTERVAL_PLUS_3_COMMAND, &Interaction::addIntervalToSelectedNotes, 3);
registerCommand(ADD_INTERVAL_PLUS_4_COMMAND, &Interaction::addIntervalToSelectedNotes, 4);
registerCommand(ADD_INTERVAL_PLUS_5_COMMAND, &Interaction::addIntervalToSelectedNotes, 5);
registerCommand(ADD_INTERVAL_PLUS_6_COMMAND, &Interaction::addIntervalToSelectedNotes, 6);
registerCommand(ADD_INTERVAL_PLUS_7_COMMAND, &Interaction::addIntervalToSelectedNotes, 7);
registerCommand(ADD_INTERVAL_PLUS_8_COMMAND, &Interaction::addIntervalToSelectedNotes, 8);
registerCommand(ADD_INTERVAL_PLUS_9_COMMAND, &Interaction::addIntervalToSelectedNotes, 9);
registerCommand(ADD_INTERVAL_PLUS_10_COMMAND, &Interaction::addIntervalToSelectedNotes, 10);
registerCommand(ADD_INTERVAL_MINUS_2_COMMAND, &Interaction::addIntervalToSelectedNotes, -2);
registerCommand(ADD_INTERVAL_MINUS_3_COMMAND, &Interaction::addIntervalToSelectedNotes, -3);
registerCommand(ADD_INTERVAL_MINUS_4_COMMAND, &Interaction::addIntervalToSelectedNotes, -4);
registerCommand(ADD_INTERVAL_MINUS_5_COMMAND, &Interaction::addIntervalToSelectedNotes, -5);
registerCommand(ADD_INTERVAL_MINUS_6_COMMAND, &Interaction::addIntervalToSelectedNotes, -6);
registerCommand(ADD_INTERVAL_MINUS_7_COMMAND, &Interaction::addIntervalToSelectedNotes, -7);
registerCommand(ADD_INTERVAL_MINUS_8_COMMAND, &Interaction::addIntervalToSelectedNotes, -8);
registerCommand(ADD_INTERVAL_MINUS_9_COMMAND, &Interaction::addIntervalToSelectedNotes, -9);
registerCommand(ADD_INTERVAL_MINUS_10_COMMAND, &Interaction::addIntervalToSelectedNotes, -10);
// editing commands
registerCommand(COPY_COMMAND, &Interaction::copySelection);
registerCommand(COPY_PASTE_SWAP_COMMAND, &Interaction::swapSelection);
registerCommand(COPY_LYRICS_COMMAND, &Interaction::copyLyrics);
registerCommand(CUT_COMMAND, &Controller::cutSelection);
registerCommand(PASTE_COMMAND, [this]() { pasteSelection(PastingType::Default); });
registerCommand(PASTE_HALF_COMMAND, [this]() { pasteSelection(PastingType::Half); });
registerCommand(PASTE_DOUBLE_COMMAND, [this]() { pasteSelection(PastingType::Double); });
registerCommand(PASTE_SPECIAL_COMMAND, [this]() { pasteSelection(PastingType::Special); });
registerCommand(DELETE_COMMAND, &Interaction::deleteSelection);
// move commands
registerCommand(MOVE_RIGHT_COMMAND, [this]() { move(MoveDirection::Right, false); });
registerCommand(MOVE_LEFT_COMMAND, [this]() { move(MoveDirection::Left, false); });
registerCommand(MOVE_RIGHT_QUICKLY_COMMAND, [this]() { move(MoveDirection::Right, true); });
registerCommand(MOVE_LEFT_QUICKLY_COMMAND, [this]() { move(MoveDirection::Left, true); });
registerCommand(MOVE_UP_COMMAND, &Interaction::moveChordRestToStaff, MoveDirection::Up);
registerCommand(MOVE_DOWN_COMMAND, &Interaction::moveChordRestToStaff, MoveDirection::Down);
registerCommand(SWAP_LEFT_COMMAND, &Interaction::swapChordRest, MoveDirection::Left);
registerCommand(SWAP_RIGHT_COMMAND, &Interaction::swapChordRest, MoveDirection::Right);
registerCommand(PITCH_UP_COMMAND, [this]() { move(MoveDirection::Up, false); });
registerCommand(PITCH_DOWN_COMMAND, [this]() { move(MoveDirection::Down, false); });
registerCommand(PITCH_UP_OCTAVE_COMMAND, [this]() { move(MoveDirection::Up, true); });
registerCommand(PITCH_DOWN_OCTAVE_COMMAND, [this]() { move(MoveDirection::Down, true); });
registerCommand(PITCH_UP_DIATONIC_COMMAND, [this]() { movePitchDiatonic(MoveDirection::Up, false); });
registerCommand(PITCH_DOWN_DIATONIC_COMMAND, [this]() { movePitchDiatonic(MoveDirection::Down, false); });
registerCommand(PITCH_UP_DIATONIC_ALTERATIONS_COMMAND, &Interaction::transposeDiatonicAlterations,
mu::engraving::TransposeDirection::UP,
PlayMode::PlayNote);
registerCommand(PITCH_DOWN_DIATONIC_ALTERATIONS_COMMAND, &Interaction::transposeDiatonicAlterations,
mu::engraving::TransposeDirection::DOWN,
PlayMode::PlayNote);
// properties commands
registerCommand(TOGGLE_VISIBLE_COMMAND, &Interaction::toggleVisible);
// snap commands
registerCommand(TOGGLE_SNAP_TO_PREV_COMMAND, &Interaction::toggleSnapToPrevious);
registerCommand(TOGGLE_SNAP_TO_NEXT_COMMAND, &Interaction::toggleSnapToNext);
// layout commands
registerCommand(TOGGLE_SYSTEM_BREAK_COMMAND, &Interaction::toggleLayoutBreak, LayoutBreakType::LINE);
registerCommand(TOGGLE_PAGE_BREAK_COMMAND, &Interaction::toggleLayoutBreak, LayoutBreakType::PAGE);
registerCommand(TOGGLE_SECTION_BREAK_COMMAND, &Interaction::toggleLayoutBreak, LayoutBreakType::SECTION);
registerCommand(APPLY_SYSTEM_LOCK_COMMAND, &Interaction::applySystemLock);
registerCommand(TOGGLE_SYSTEM_LOCK_COMMAND, &Interaction::toggleSystemLock);
registerCommand(APPLY_PAGE_LOCK_COMMAND, &Interaction::applyPageLock);
registerCommand(TOGGLE_PAGE_LOCK_COMMAND, &Interaction::togglePageLock);
registerCommand(TOGGLE_SCORE_LOCK_COMMAND, &Interaction::toggleScoreLock);
registerCommand(MAKE_INTO_SYSTEM_COMMAND, &Interaction::makeIntoSystem);
registerCommand(MAKE_INTO_PAGE_COMMAND, &Interaction::makeIntoPage);
registerCommand(MOVE_MEASURE_TO_PREV_SYSTEM_COMMAND, &Interaction::moveMeasureToPrevSystem);
registerCommand(MOVE_MEASURE_TO_NEXT_SYSTEM_COMMAND, &Interaction::moveMeasureToNextSystem);
registerCommand(MOVE_SYSTEM_TO_PREV_PAGE_COMMAND, &Interaction::moveSystemToPrevPage);
registerCommand(MOVE_SYSTEM_TO_NEXT_PAGE_COMMAND, &Interaction::moveSystemToNextPage);
registerCommand(SPLIT_MEASURE_COMMAND, &Interaction::splitSelectedMeasure);
registerCommand(JOIN_MEASURES_COMMAND, &Interaction::joinSelectedMeasures);
registerCommand(INSERT_MEASURE_COMMAND, [this]() { addBoxes(BoxType::Measure, 1, AddBoxesTarget::BeforeSelection); });
registerCommandWithParams(INSERT_MEASURES_COMMAND, [this](const muse::rcommand::Params& params) {
return addMeasures(params, AddBoxesTarget::BeforeSelection);
});
registerCommandWithParams(INSERT_MEASURES_AFTER_SELECTION_COMMAND, [this](const muse::rcommand::Params& params) {
return addMeasures(params, AddBoxesTarget::AfterSelection);
});
registerCommandWithParams(INSERT_MEASURES_AT_START_OF_SCORE_COMMAND, [this](const muse::rcommand::Params& params) {
return addMeasures(params, AddBoxesTarget::AtStartOfScore);
});
registerCommand(APPEND_MEASURE_COMMAND, [this]() {
addBoxes(BoxType::Measure, 1, AddBoxesTarget::AtEndOfScore);
});
registerCommandWithParams(APPEND_MEASURES_COMMAND, [this](const muse::rcommand::Params& params) {
return addMeasures(params, AddBoxesTarget::AtEndOfScore);
});
registerCommand(STRETCH_DECREASE_COMMAND, [this]() { addStretch(-STRETCH_STEP); });
registerCommand(STRETCH_INCREASE_COMMAND, [this]() { addStretch(STRETCH_STEP); });
registerCommand(STRETCH_RESET_COMMAND, &Controller::resetStretch);
// open properties
registerCommand(OPEN_PAGE_SETTINGS_COMMAND, &Controller::openPageSettingsDialog);
registerCommand(OPEN_STAFF_PROPERTIES_COMMAND, &Controller::openStaffProperties);
registerCommand(OPEN_EDIT_STRINGS_COMMAND, &Controller::openEditStringsDialog);
registerCommand(OPEN_BREAKS_COMMAND, &Controller::openBreaksDialog);
registerCommand(OPEN_STAFF_TEXT_PROPERTIES_COMMAND, &Controller::openStaffTextPropertiesDialog);
registerCommand(OPEN_SYSTEM_TEXT_PROPERTIES_COMMAND, &Controller::openStaffTextPropertiesDialog);
registerCommand(OPEN_MEASURE_PROPERTIES_COMMAND, &Controller::openMeasurePropertiesDialog);
registerCommand(OPEN_TRANSPOSE_COMMAND, &Controller::openTransposeDialog);
registerCommand(OPEN_PARTS_COMMAND, &Controller::openPartsDialog);
registerCommand(OPEN_EDITGRIDSIZE_COMMAND, &Controller::openEditGridSizeDialog);
registerCommand(OPEN_REALIZECHORDSYMBOLS_COMMAND, &Controller::openRealizeChordSymbolsDialog);
// style commands
registerCommand(LOAD_STYLE_COMMAND, &Controller::loadStyle);
registerCommand(SAVE_STYLE_COMMAND, &Controller::saveStyle);
registerCommandWithParams(OPEN_EDIT_STYLE_COMMAND, &Controller::openEditStyleDialog);
registerCommand(TOGGLE_CONCERT_PITCH_COMMAND, &Controller::toggleConcertPitch);
// reset commands
registerCommand(RESET_TEXT_STYLE_OVERRIDES_COMMAND, &Interaction::resetTextStyleOverrides);
registerCommand(RESET_BEAMS_COMMAND, &Controller::resetBeamMode);
registerCommand(RESET_SHAPES_AND_POSITIONS_COMMAND, &Interaction::resetShapesAndPosition);
registerCommand(RESET_TO_DEFAULT_LAYOUT_COMMAND, &Interaction::resetToDefaultLayout);
// appearance commands
registerCommand(FREEZE_CURRENT_PLACEMENT_COMMAND, &Interaction::freezeSelectionPosition);
// show commands
registerCommand(SHOW_INVISIBLE_COMMAND, [this]() { toggleScoreConfig(ScoreConfigType::ShowInvisibleElements); });
registerCommand(SHOW_UNPRINTABLE_COMMAND, [this]() { toggleScoreConfig(ScoreConfigType::ShowUnprintableElements); });
registerCommand(SHOW_FRAMES_COMMAND, [this]() { toggleScoreConfig(ScoreConfigType::ShowFrames); });
registerCommand(SHOW_PAGEBORDERS_COMMAND, [this]() { toggleScoreConfig(ScoreConfigType::ShowPageMargins); });
registerCommand(SHOW_SOUNDFLAGS_COMMAND, [this]() { toggleScoreConfig(ScoreConfigType::ShowSoundFlags); });
registerCommand(SHOW_IRREGULAR_COMMAND, [this]() { toggleScoreConfig(ScoreConfigType::MarkIrregularMeasures); });
// staff commands
registerCommand(STAFF_EXPLODE_COMMAND, &Interaction::explodeSelectedStaff);
registerCommand(STAFF_IMPLODE_COMMAND, &Interaction::implodeSelectedStaff);
// remove commands
registerCommand(REMOVE_SELECTED_RANGE_COMMAND, &Interaction::removeSelectedRange);
registerCommand(REMOVE_EMPTY_TRAILING_MEASURES_COMMAND, &Interaction::removeEmptyTrailingMeasures);
// slash commands
registerCommand(SLASH_FILL_COMMAND, &Interaction::fillSelectionWithSlashes);
registerCommand(SLASH_RHYTHM_COMMAND, &Interaction::replaceSelectedNotesWithSlashes);
// spelling commands
registerCommand(PITCH_SPELL_COMMAND, &Interaction::spellPitches);
registerCommand(PITCH_SPELL_SHARPS_COMMAND, &Interaction::spellPitchesWithSharps);
registerCommand(PITCH_SPELL_FLATS_COMMAND, &Interaction::spellPitchesWithFlats);
registerCommand(ENHARMONIC_SPELL_BOTH_COMMAND, &Interaction::changeEnharmonicSpelling, true);
registerCommand(ENHARMONIC_SPELL_CURRENT_COMMAND, &Interaction::changeEnharmonicSpelling, false);
// screen commands
registerCommandWithParams(SCREEN_PUT_NOTE_COMMAND, &Controller::putNote);
registerCommandWithParams(SCREEN_REMOVE_NOTE_COMMAND, &Controller::removeNote);
registerCommandWithParams(SCREEN_EDIT_TEXT_COMMAND, &Controller::startEditSelectedText);
registerCommandWithParams(SCREEN_EDIT_ELEMENT_COMMAND, &Controller::startEditSelectedElement);
// others commands
registerCommand(REGROUP_RHYTHMS_COMMAND, &Interaction::regroupNotesAndRests);
registerCommand(RESEQUENCE_REHEARSAL_MARKS_COMMAND, &Interaction::resequenceRehearsalMarks);
registerCommand(UNROLL_REPEATS_COMMAND, &Controller::unrollRepeats);
registerCommand(REPEAT_SELECTION_COMMAND, &Controller::repeatSelection);
registerCommand(TRANSPOSE_UP_COMMAND, &Interaction::transposeSemitone, 1, PlayMode::PlayNote);
registerCommand(TRANSPOSE_DOWN_COMMAND, &Interaction::transposeSemitone, -1, PlayMode::PlayNote);
registerCommand(TOGGLE_MMREST_COMMAND, &Controller::toggleMmrest);
registerCommand(TOGGLE_HIDE_EMPTY_COMMAND, &Controller::toggleHideEmpty);
registerCommand(MIRROR_NOTEHEAD_COMMAND, &Interaction::mirrorNotes);
registerCommand(SET_VISIBLE_COMMAND, &Interaction::setSelectionVisible, true);
registerCommand(UNSET_VISIBLE_COMMAND, &Interaction::setSelectionVisible, false);
registerCommand(TOGGLE_AUTOPLACE_COMMAND, &Interaction::toggleAutoplace, false);
registerCommand(AUTOPLACE_ENABLED_COMMAND, &Interaction::toggleAutoplace, true);
registerCommand(VOICE_ASSIGNMENT_ALL_IN_INSTR_COMMAND, &Interaction::changeSelectedElementsVoiceAssignment,
VoiceAssignment::ALL_VOICE_IN_INSTRUMENT);
registerCommand(VOICE_ASSIGNMENT_ALL_IN_STAFF_COMMAND, &Interaction::changeSelectedElementsVoiceAssignment,
VoiceAssignment::ALL_VOICE_IN_STAFF);
registerCommand(TOGGLE_AUTOMATION_COMMAND, &Controller::toggleAutomation);
registerCommandWithParams(SELECT_AUTOMATION_TYPE_COMMAND, &Controller::selectAutomationType);
// TAB
registerCommand(SET_DURATION_WHOLE_TAB_COMMAND, [this]() { setDuration(DurationType::V_WHOLE); });
registerCommand(SET_DURATION_HALF_TAB_COMMAND, [this]() { setDuration(DurationType::V_HALF); });
registerCommand(SET_DURATION_QUARTER_TAB_COMMAND, [this]() { setDuration(DurationType::V_QUARTER); });
registerCommand(SET_DURATION_EIGHTH_TAB_COMMAND, [this]() { setDuration(DurationType::V_EIGHTH); });
registerCommand(SET_DURATION_16TH_TAB_COMMAND, [this]() { setDuration(DurationType::V_16TH); });
registerCommand(SET_DURATION_32ND_TAB_COMMAND, [this]() { setDuration(DurationType::V_32ND); });
registerCommand(SET_DURATION_64TH_TAB_COMMAND, [this]() { setDuration(DurationType::V_64TH); });
registerCommand(SET_DURATION_128TH_TAB_COMMAND, [this]() { setDuration(DurationType::V_128TH); });
registerCommand(SET_DURATION_256TH_TAB_COMMAND, [this]() { setDuration(DurationType::V_256TH); });
registerCommand(SET_DURATION_512TH_TAB_COMMAND, [this]() { setDuration(DurationType::V_512TH); });
registerCommand(SET_DURATION_1024TH_TAB_COMMAND, [this]() { setDuration(DurationType::V_1024TH); });
registerCommand(ENTER_FRET_0_COMMAND, [this]() { addFret(0); });
registerCommand(ENTER_FRET_1_COMMAND, [this]() { addFret(1); });
registerCommand(ENTER_FRET_2_COMMAND, [this]() { addFret(2); });
registerCommand(ENTER_FRET_3_COMMAND, [this]() { addFret(3); });
registerCommand(ENTER_FRET_4_COMMAND, [this]() { addFret(4); });
registerCommand(ENTER_FRET_5_COMMAND, [this]() { addFret(5); });
registerCommand(ENTER_FRET_6_COMMAND, [this]() { addFret(6); });
registerCommand(ENTER_FRET_7_COMMAND, [this]() { addFret(7); });
registerCommand(ENTER_FRET_8_COMMAND, [this]() { addFret(8); });
registerCommand(ENTER_FRET_9_COMMAND, [this]() { addFret(9); });
registerCommand(ENTER_FRET_10_COMMAND, [this]() { addFret(10); });
registerCommand(ENTER_FRET_11_COMMAND, [this]() { addFret(11); });
registerCommand(ENTER_FRET_12_COMMAND, [this]() { addFret(12); });
registerCommand(ENTER_FRET_13_COMMAND, [this]() { addFret(13); });
registerCommand(ENTER_FRET_14_COMMAND, [this]() { addFret(14); });
registerCommand(ENTER_REST_TAB_COMMAND, &Interaction::putRestToSelection);
registerCommand(ADD_STANDARD_BEND_COMMAND, [this]() { addGuitarBend(GuitarBendType::BEND); });
registerCommand(ADD_PRE_BEND_COMMAND, [this]() { addGuitarBend(GuitarBendType::PRE_BEND); });
registerCommand(ADD_GRACE_NOTE_BEND_COMMAND, [this]() { addGuitarBend(GuitarBendType::GRACE_NOTE_BEND); });
registerCommand(ADD_SLIGHT_BEND_COMMAND, [this]() { addGuitarBend(GuitarBendType::SLIGHT_BEND); });
registerCommand(ADD_DIVE_COMMAND, [this]() { addGuitarBend(GuitarBendType::DIVE); });
registerCommand(ADD_PRE_DIVE_COMMAND, [this]() { addGuitarBend(GuitarBendType::PRE_DIVE); });
registerCommand(ADD_DIP_COMMAND, [this]() { addGuitarBend(GuitarBendType::DIP); });
registerCommand(ADD_SCOOP_COMMAND, [this]() { addGuitarBend(GuitarBendType::SCOOP); });
registerCommand(ADD_HAMMER_ON_PULL_OFF_COMMAND, [this]() { addHammerOnPullOff(); });
registerCommand(GOTO_STRING_ABOVE_COMMAND, [this]() { move(MoveDirection::Up, false); });
registerCommand(GOTO_STRING_BELOW_COMMAND, [this]() { move(MoveDirection::Down, false); });
// view commands
registerViewCommand(ZOOM_IN_COMMAND, &ViewController::zoomIn);
registerViewCommand(ZOOM_OUT_COMMAND, &ViewController::zoomOut);
registerViewCommand(ZOOM_TO_PAGE_WIDTH_COMMAND, &ViewController::zoomToPageWidth);
registerViewCommand(ZOOM_TO_WHOLE_PAGE_COMMAND, &ViewController::zoomToWholePage);
registerViewCommand(ZOOM_TO_TWO_PAGES_COMMAND, &ViewController::zoomToTwoPages);
registerViewCommand(ZOOM_TO_100_COMMAND, &ViewController::setZoom, 100);
registerCommandWithParams(ZOOM_TO_PERCENT_COMMAND, &Controller::zoomToPercent);
registerViewCommand(VIEW_MODE_PAGE_COMMAND, &ViewController::setViewMode, ViewMode::PAGE);
registerViewCommand(VIEW_MODE_FLOAT_COMMAND, &ViewController::setViewMode, ViewMode::FLOAT);
registerViewCommand(VIEW_MODE_CONTINUOUS_COMMAND, &ViewController::setViewMode, ViewMode::LINE);
registerViewCommand(VIEW_MODE_SINGLE_COMMAND, &ViewController::setViewMode, ViewMode::SYSTEM);
registerViewCommand(NEXT_SCREEN_COMMAND, &ViewController::nextScreen);
registerViewCommand(PREV_SCREEN_COMMAND, &ViewController::previousScreen);
registerViewCommand(NEXT_PAGE_COMMAND, &ViewController::nextPage);
registerViewCommand(PREV_PAGE_COMMAND, &ViewController::previousPage);
registerViewCommand(TOP_OF_FIRST_PAGE_COMMAND, &ViewController::startOfScore);
registerViewCommand(BOTTOM_OF_LAST_PAGE_COMMAND, &ViewController::endOfScore);
registerViewCommand(CONTEXT_MENU_OF_SELECTION_COMMAND, &ViewController::openContextMenuOfSelection);
registerViewCommand(SHOW_SEARCH_COMMAND, &ViewController::showSearch);
registerCommandWithParams(PIANO_KEYBOARD_SET_NUMBER_OF_KEYS_COMMAND, &Controller::setPianoKeyboardNumberOfKeys);
// diagnostic commands
registerViewCommand(DIAGNOSTIC_VIEW_REDRAW_COMMAND, &ViewController::redrawView);
registerCommand(CHECK_FOR_SCORE_CORRUPTIONS_COMMAND, &Controller::checkForScoreCorruptions);
// debugging commands
for (auto& [command, member] : s_debuggingCommands) {
registerCommand(command, [this, member = member]() {
EngravingDebuggingOptions options = engravingConfiguration()->debuggingOptions();
options.*member = !(options.*member);
engravingConfiguration()->setDebuggingOptions(options);
});
}
// listen on state changes
projectSession()->currentNotationChanged().onNotify(this, [this]() {
auto notation = projectSession()->currentNotation();
if (notation) {
auto interaction = notation->interaction();
interaction->selectionChanged().onNotify(this, [this]() {
m_selectionChanged.notify();
}, Asyncable::Mode::SetReplace);
interaction->noteInput()->stateChanged().onNotify(this, [this]() {
m_noteInputStateChanged.notify();
}, Asyncable::Mode::SetReplace);
interaction->textEditingStarted().onNotify(this, [this]() {
m_textEditingChanged.send(true);
}, Asyncable::Mode::SetReplace);
interaction->textEditingEnded().onReceive(this, [this](TextBase*) {
m_textEditingChanged.send(false);
}, Asyncable::Mode::SetReplace);
interaction->scoreConfigChanged().onReceive(this, [this](ScoreConfigType configType) {
m_scoreConfigChanged.send(configType);
}, Asyncable::Mode::SetReplace);
auto undoStack = notation->undoStack();
undoStack->stackChanged().onNotify(this, [this]() {
m_stackChanged.notify();
}, Asyncable::Mode::SetReplace);
notation->style()->styleChanged().onNotify(this, [this]() {
m_currentNotationStyleChanged.notify();
}, Asyncable::Mode::SetReplace);
if (const IMasterNotationPtr masterNotation = notation->masterNotation()) {
masterNotation->automation()->automationModeEnabledChanged().onNotify(this, [this]() {
m_automationModeEnabledChanged.notify();
}, Asyncable::Mode::SetReplace);
}
}
m_textEditingChanged.send(isTextEditing());
m_noteInputStateChanged.notify();
m_currentNotationStyleChanged.notify();
});
playbackController()->isPlayingChanged().onReceive(this, [this](bool) {
m_isNoteInputAllowedChanged.send(isNoteInputAllowed());
}, Asyncable::Mode::SetReplace);
}
void NotationActionController::setViewController(INotationViewController* controller)
{
m_viewController = controller;
LOGD() << "view controller changed";
}
INotationViewController* NotationActionController::viewController() const
{
return m_viewController;
}
INotationPtr NotationActionController::currentNotation() const
{
return projectSession()->currentNotation();
}
IMasterNotationPtr NotationActionController::currentMasterNotation() const
{
return projectSession()->masterNotation();
}
INotationInteractionPtr NotationActionController::currentNotationInteraction() const
{
return currentNotation() ? currentNotation()->interaction() : nullptr;
}
INotationSelectionPtr NotationActionController::currentNotationSelection() const
{
return currentNotationInteraction() ? currentNotationInteraction()->selection() : nullptr;
}
INotationElementsPtr NotationActionController::currentNotationElements() const
{
return currentNotation() ? currentNotation()->elements() : nullptr;
}
muse::async::Notification NotationActionController::currentNotationChanged() const
{
return projectSession()->currentNotationChanged();
}
muse::async::Notification NotationActionController::currentMasterNotationChanged() const
{
return projectSession()->masterNotationChanged();
}
INotationNoteInputPtr NotationActionController::currentNotationNoteInput() const
{
auto interaction = currentNotationInteraction();
if (!interaction) {
return nullptr;
}
return interaction->noteInput();
}
INotationUndoStackPtr NotationActionController::currentNotationUndoStack() const
{
auto notation = currentNotation();
if (!notation) {
return nullptr;
}
return notation->undoStack();
}
INotationMidiInputPtr NotationActionController::currentNotationMidiInput() const
{
auto notation = currentNotation();
if (!notation) {
return nullptr;
}
return notation->midiInput();
}
mu::engraving::Score* NotationActionController::currentNotationScore() const
{
return currentNotationElements() ? currentNotationElements()->msScore() : nullptr;
}
INotationStylePtr NotationActionController::notationStyle() const
{
auto notation = currentNotation();
if (!notation) {
return nullptr;
}
return notation->style();
}
muse::async::Notification NotationActionController::notationStyleChanged() const
{
return m_currentNotationStyleChanged;
}
void NotationActionController::resetState()
{
TRACEFUNC;
if (playbackController()->isPlaying()) {
commandDispatcher()->dispatch(playback::STOP_COMMAND);
}
auto noteInput = currentNotationNoteInput();
if (!noteInput) {
return;
}
if (noteInput->isNoteInputMode()) {
noteInput->endNoteInput();
return;
}
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
if (interaction->isGripEditStarted()) {
// Exit grip edit but leave element selected
interaction->endEditElement();
return;
}
if (interaction->isTextEditingStarted()) {
interaction->endEditElement();
return;
} else if (interaction->isEditingElement()) {
interaction->endEditElement();
}
if (!interaction->selection()->isNone()) {
interaction->clearSelection();
}
}
bool NotationActionController::isNoteInputAllowed() const
{
return !playbackController()->isPlaying();
}
muse::async::Channel NotationActionController::isNoteInputAllowedChanged() const
{
return m_isNoteInputAllowedChanged;
}
muse::async::Notification NotationActionController::noteInputStateChanged() const
{
return m_noteInputStateChanged;
}
bool NotationActionController::isNoteInputMode() const
{
auto noteInput = currentNotationNoteInput();
if (!noteInput) {
return false;
}
return noteInput->isNoteInputMode();
}
NoteInputMethod NotationActionController::noteInputMethod() const
{
auto noteInput = currentNotationNoteInput();
if (!noteInput) {
return NoteInputMethod::UNKNOWN;
}
return noteInput->state().noteEntryMethod();
}
void NotationActionController::toggleNoteInput(NoteInputMethod method)
{
TRACEFUNC;
INotationNoteInputPtr noteInput = currentNotationNoteInput();
if (!noteInput) {
return;
}
if (method == NoteInputMethod::UNKNOWN) {
method = configuration()->defaultNoteInputMethod();
}
if (!noteInput->isNoteInputMode()) {
noteInput->startNoteInput(method);
} else if (noteInput->usingNoteInputMethod(method)) {
noteInput->endNoteInput();
} else {
noteInput->setNoteInputMethod(method);
}
}
void NotationActionController::toggleNoteInputInsert()
{
if (!currentNotationNoteInput()->usingNoteInputMethod(NoteInputMethod::TIMEWISE)) {
toggleNoteInput(NoteInputMethod::TIMEWISE);
} else {
toggleNoteInput(NoteInputMethod::BY_NOTE_NAME);
}
}
muse::RetVal NotationActionController::requestNoteInputState() const
{
INotationNoteInputPtr noteInput = currentNotationNoteInput();
if (!noteInput) {
return RetVal::make_ret(Ret::Code::InternalError);
}
if (!noteInput->isNoteInputMode()) {
return RetVal::make_ok(muse::rcommand::Params({ { "noteinputmode", Val("disabled") } }));
}
const NoteInputState& state = noteInput->state();
return RetVal::make_ok(muse::rcommand::Params({
{ "noteinputmode", Val("enabled") },
Contract::param(state.noteEntryMethod())
}));
}
muse::Ret NotationActionController::addNote(const muse::rcommand::Params& params)
{
TRACEFUNC;
NoteName note = Contract::get(params);
NoteAddingMode mode = Contract::get(params);
addNote(note, mode);
return muse::make_ok();
}
void NotationActionController::addNote(NoteName note, NoteAddingMode mode)
{
NoteInputParams params;
const bool addFlag = mode == NoteAddingMode::CurrentChord;
bool ok = mu::engraving::NoteInput::resolveNoteInputParams(currentNotationScore(), static_cast(note), addFlag, params);
if (!ok) {
LOGE() << "Could not resolve note input params, note: " << (int)note << ", addFlag: " << addFlag;
return;
}
doAddNote(params, mode);
}
muse::Ret NotationActionController::addDrumNote(const muse::rcommand::Params& params)
{
TRACEFUNC;
int pitch = params.at("pitch").toInt();
NoteAddingMode mode = Contract::get(params);
NoteInputParams noteInputParams;
noteInputParams.drumPitch = pitch;
doAddNote(noteInputParams, mode);
return muse::make_ok();
}
void NotationActionController::doAddNote(const NoteInputParams& params, const NoteAddingMode& addingMode)
{
TRACEFUNC;
INotationNoteInputPtr noteInput = currentNotationNoteInput();
if (!noteInput) {
return;
}
startNoteInput();
if (addingMode == NoteAddingMode::NextChord) {
if (noteInput->usingNoteInputMethod(NoteInputMethod::BY_DURATION)) {
noteInput->setRestMode(false);
noteInput->setInputNote(params);
if (configuration()->isPlayPreviewNotesInInputByDuration()) {
const NoteInputState& state = noteInput->state();
playbackController()->playNotes(state.notes(), state.staffIdx(), state.segment());
}
return;
}
}
noteInput->addNote(params, addingMode);
seekAndPlaySelectedElement();
}
void NotationActionController::setDuration(DurationType duration)
{
TRACEFUNC;
INotationInteractionPtr interaction = currentNotationInteraction();
if (!interaction) {
return;
}
INotationNoteInputPtr noteInput = interaction->noteInput();
if (interaction->selection()->isNone()) {
if (!noteInput->isNoteInputMode() && !toggleNoteInputAllowed()) {
return;
}
startNoteInput();
}
if (noteInput->isNoteInputMode() && !noteInput->state().isValid()) {
return;
}
noteInput->setDuration(duration);
if (noteInput->usingNoteInputMethod(NoteInputMethod::BY_DURATION)
|| noteInput->usingNoteInputMethod(NoteInputMethod::RHYTHM)) {
seekAndPlaySelectedElement();
}
}
void NotationActionController::toggleRest()
{
TRACEFUNC;
INotationInteractionPtr interaction = currentNotationInteraction();
if (!interaction) {
return;
}
INotationNoteInputPtr noteInput = interaction->noteInput();
if (interaction->selection()->isNone()) {
if (!noteInput->isNoteInputMode() && !toggleNoteInputAllowed()) {
return;
}
startNoteInput();
}
noteInput->toggleRest();
if (noteInput->usingNoteInputMethod(NoteInputMethod::BY_DURATION)
|| noteInput->usingNoteInputMethod(NoteInputMethod::RHYTHM)) {
seekSelectedElement();
}
}
void NotationActionController::toggleDots(int dots)
{
TRACEFUNC;
INotationInteractionPtr interaction = currentNotationInteraction();
if (!interaction) {
return;
}
INotationNoteInputPtr noteInput = interaction->noteInput();
if (interaction->selection()->isNone()) {
if (!noteInput->isNoteInputMode() && !toggleNoteInputAllowed()) {
return;
}
startNoteInput();
}
if (!noteInput->isNoteInputMode() || !configuration()->addAccidentalDotsArticulationsToNextNoteEntered()) {
interaction->toggleDotsForSelection(dots);
return;
}
noteInput->toggleDots(dots);
if (noteInput->usingNoteInputMethod(NoteInputMethod::BY_DURATION)
|| noteInput->usingNoteInputMethod(NoteInputMethod::RHYTHM)) {
seekAndPlaySelectedElement();
}
}
DurationType NotationActionController::currentDurationType() const
{
constexpr DurationType INVALID_DURATION_TYPE = DurationType::V_INVALID;
INotationInteractionPtr interaction = currentNotationInteraction();
if (!interaction) {
return INVALID_DURATION_TYPE;
}
INotationNoteInputPtr noteInput = interaction->noteInput();
if (!noteInput) {
return INVALID_DURATION_TYPE;
}
INotationSelectionPtr selection = interaction->selection();
if (!selection) {
return INVALID_DURATION_TYPE;
}
if (noteInput->isNoteInputMode()) {
return noteInput->state().duration().type();
}
if (selection->isNone() || selection->isRange()) {
return INVALID_DURATION_TYPE;
}
const std::vector& selectedElements = selection->elements();
if (selectedElements.empty()) {
return INVALID_DURATION_TYPE;
}
auto elementToChordRest = [](const EngravingItem* element) -> const ChordRest* {
if (!element) {
return nullptr;
}
if (element->isChordRest()) {
return toChordRest(element);
}
if (element->isNote()) {
return toNote(element)->chord();
}
if (element->isStem()) {
return toStem(element)->chord();
}
if (element->isHook()) {
return toHook(element)->chord();
}
return nullptr;
};
DurationType result = INVALID_DURATION_TYPE;
bool isFirstElement = true;
for (const EngravingItem* element: selectedElements) {
const ChordRest* chordRest = elementToChordRest(element);
if (!chordRest) {
continue;
}
if (isFirstElement) {
result = chordRest->durationType().type();
isFirstElement = false;
} else if (result != chordRest->durationType().type()) {
return INVALID_DURATION_TYPE;
}
}
return result;
}
int NotationActionController::currentDotCount() const
{
INotationInteractionPtr interaction = currentNotationInteraction();
if (!interaction) {
return 0;
}
return interaction->noteInput()->state().duration().dots();
}
bool NotationActionController::currentIsRest() const
{
INotationInteractionPtr interaction = currentNotationInteraction();
if (!interaction) {
return false;
}
INotationNoteInputPtr noteInput = interaction->noteInput();
if (!noteInput) {
return false;
}
if (noteInput->isNoteInputMode()) {
return noteInput->state().rest();
}
INotationSelectionPtr selection = interaction->selection();
if (!selection) {
return false;
}
if (selection->isNone() || selection->isRange()) {
return false;
}
for (const EngravingItem* element: selection->elements()) {
if (!element->isRest()) {
return false;
}
}
return true;
}
AccidentalType NotationActionController::currentAccidentalType() const
{
INotationInteractionPtr interaction = currentNotationInteraction();
if (!interaction) {
return AccidentalType::NONE;
}
return interaction->noteInput()->state().accidentalType();
}
std::set NotationActionController::currentArticulations() const
{
INotationInteractionPtr interaction = currentNotationInteraction();
if (!interaction) {
return {};
}
INotationNoteInputPtr noteInput = interaction->noteInput();
if (!noteInput) {
return {};
}
if (noteInput->isNoteInputMode()) {
return mu::engraving::splitArticulations(noteInput->state().articulationIds());
}
INotationSelectionPtr selection = interaction->selection();
if (!selection) {
return {};
}
if (selection->isNone()) {
return {};
}
auto chordArticulations = [](const Chord* chord) {
std::set result;
for (Articulation* articulation: chord->articulations()) {
result.insert(articulation->symId());
}
result = mu::engraving::flipArticulations(result, mu::engraving::PlacementV::ABOVE);
return mu::engraving::splitArticulations(result);
};
std::set result;
bool isFirstNote = true;
for (const EngravingItem* element: selection->elements()) {
if (!element->isNote()) {
continue;
}
const Note* note = toNote(element);
if (isFirstNote) {
result = chordArticulations(note->chord());
isFirstNote = false;
} else {
std::set currentNoteArticulations = chordArticulations(note->chord());
for (auto it = result.begin(); it != result.end();) {
if (std::find(currentNoteArticulations.begin(), currentNoteArticulations.end(),
*it) == currentNoteArticulations.end()) {
it = result.erase(it);
} else {
++it;
}
}
}
}
return result;
}
voice_idx_t NotationActionController::currentVoice() const
{
constexpr voice_idx_t INVALID_VOICE = muse::nidx;
INotationInteractionPtr interaction = currentNotationInteraction();
if (!interaction) {
return INVALID_VOICE;
}
INotationNoteInputPtr noteInput = interaction->noteInput();
if (!noteInput) {
return INVALID_VOICE;
}
if (noteInput->isNoteInputMode()) {
return noteInput->state().voice();
}
INotationSelectionPtr selection = interaction->selection();
if (!selection) {
return INVALID_VOICE;
}
if (selection->isNone()) {
return INVALID_VOICE;
}
const std::vector& selectedElements = selection->elements();
if (selectedElements.empty()) {
return INVALID_VOICE;
}
voice_idx_t voice = INVALID_VOICE;
for (const EngravingItem* element : selectedElements) {
if (element->hasVoiceAssignmentProperties()) {
VoiceAssignment voiceAssignment = element->getProperty(Pid::VOICE_ASSIGNMENT).value();
if (voiceAssignment == VoiceAssignment::ALL_VOICE_IN_INSTRUMENT || voiceAssignment == VoiceAssignment::ALL_VOICE_IN_STAFF) {
return INVALID_VOICE;
}
}
voice_idx_t elementVoice = element->voice();
if (elementVoice != voice && voice != INVALID_VOICE) {
return INVALID_VOICE;
}
voice = elementVoice;
}
return voice;
}
muse::async::Notification NotationActionController::selectionChanged() const
{
return m_selectionChanged;
}
bool NotationActionController::selectionHasTie() const
{
INotationInteractionPtr interaction = currentNotationInteraction();
if (!interaction) {
return false;
}
std::vector tiedNotes = interaction->selection()->notes(NoteFilter::WithTie);
if (tiedNotes.empty()) {
return false;
}
bool hasTie = true;
for (const Note* note: tiedNotes) {
if (!note->tieFor()) {
hasTie = false;
break;
}
if (note->laissezVib()) {
hasTie = false;
break;
}
}
return hasTie;
}
bool NotationActionController::selectionHasLaissezVib() const
{
INotationInteractionPtr interaction = currentNotationInteraction();
if (!interaction) {
return false;
}
std::vector tiedNotes = interaction->selection()->notes(NoteFilter::WithTie);
if (tiedNotes.empty()) {
return false;
}
bool hasLaissezVib = true;
for (const Note* note: tiedNotes) {
if (!note->laissezVib()) {
hasLaissezVib = false;
break;
}
}
return hasLaissezVib;
}
bool NotationActionController::selectionHasSlur() const
{
INotationInteractionPtr interaction = currentNotationInteraction();
if (!interaction) {
return false;
}
bool hasSlur = interaction->noteInput()->state().slur() != nullptr;
return hasSlur;
}
muse::Ret NotationActionController::putNote(const muse::rcommand::Params& params)
{
TRACEFUNC;
INotationNoteInputPtr noteInput = currentNotationNoteInput();
if (!noteInput) {
return muse::make_ret(Ret::Code::NotSupported);
}
IF_ASSERT_FAILED(params.contains("pos_x") && params.contains("pos_y")) {
return muse::make_ret(Ret::Code::BadArgs);
}
float posX = params.at("pos_x").toFloat();
float posY = params.at("pos_y").toFloat();
bool replace = params.at("replace", Val(false)).toBool();
bool insert = params.at("insert", Val(false)).toBool();
PointF pos = PointF(posX, posY);
Ret ret = noteInput->putNote(pos, replace, insert);
if (ret) {
seekAndPlaySelectedElement();
}
return muse::make_ok();
}
muse::Ret NotationActionController::removeNote(const muse::rcommand::Params& params)
{
TRACEFUNC;
INotationNoteInputPtr noteInput = currentNotationNoteInput();
if (!noteInput) {
return muse::make_ret(Ret::Code::NotSupported);
}
IF_ASSERT_FAILED(params.contains("pos_x") && params.contains("pos_y")) {
return muse::make_ret(Ret::Code::BadArgs);
}
float posX = params.at("pos_x").toFloat();
float posY = params.at("pos_y").toFloat();
PointF pos = PointF(posX, posY);
noteInput->removeNote(pos);
seekSelectedElement();
return muse::make_ok();
}
void NotationActionController::toggleAccidental(AccidentalType type)
{
TRACEFUNC;
INotationInteractionPtr interaction = currentNotationInteraction();
if (!interaction) {
return;
}
INotationNoteInputPtr noteInput = interaction->noteInput();
if (!noteInput) {
return;
}
if (interaction->selection()->isNone()) {
if (!noteInput->isNoteInputMode() && !toggleNoteInputAllowed()) {
return;
}
startNoteInput();
}
if (noteInput->isNoteInputMode() && configuration()->addAccidentalDotsArticulationsToNextNoteEntered()) {
noteInput->setAccidental(type);
} else {
interaction->toggleAccidentalForSelection(type);
seekAndPlaySelectedElement();
}
}
void NotationActionController::toggleArticulation(SymbolId articulationSymbolId)
{
TRACEFUNC;
INotationInteractionPtr interaction = currentNotationInteraction();
if (!interaction) {
return;
}
INotationNoteInputPtr noteInput = interaction->noteInput();
if (!noteInput) {
return;
}
if (interaction->selection()->isNone()) {
if (!noteInput->isNoteInputMode() && !toggleNoteInputAllowed()) {
return;
}
startNoteInput();
}
if (noteInput->isNoteInputMode() && configuration()->addAccidentalDotsArticulationsToNextNoteEntered()) {
noteInput->setArticulation(articulationSymbolId);
} else {
interaction->toggleArticulationForSelection(articulationSymbolId);
}
}
muse::Ret NotationActionController::putTuplet(const muse::rcommand::Params& params)
{
TupletOptions options;
options.ratio = engraving::Fraction::fromString(muse::String::fromStdString(params.at("ratio").toString()));
options.numberType = Contract::get(params);
options.bracketType = Contract::get(params);
options.autoBaseLen = params.at("auto-baselen", Val(false)).toBool();
putTuplet(options);
return muse::make_ok();
}
void NotationActionController::putTuplet(const TupletOptions& options)
{
TRACEFUNC;
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
auto noteInput = interaction->noteInput();
if (!noteInput) {
return;
}
if (!interaction->canAddTupletToSelectedChordRests()) {
interactive()->error(muse::trc("notation", "Cannot create tuplet"), muse::trc("notation", "Note value is too short"));
return;
}
if (noteInput->isNoteInputMode()) {
noteInput->addTuplet(options);
} else {
interaction->addTupletToSelectedChordRests(options);
}
}
void NotationActionController::putTuplet(int tupletCount)
{
TRACEFUNC;
TupletOptions options;
options.ratio.setNumerator(tupletCount);
options.ratio.setDenominator(2);
options.autoBaseLen = true;
// get the bracket type from score style settings
if (INotationStylePtr style = notationStyle()) {
int bracketType = style->styleValue(StyleId::tupletBracketType).toInt();
options.bracketType = static_cast(bracketType);
int numberType = style->styleValue(StyleId::tupletNumberType).toInt();
options.numberType = static_cast(numberType);
}
putTuplet(options);
}
void NotationActionController::increaseDecreaseDuration(int steps, bool stepByDots)
{
TRACEFUNC;
INotationInteractionPtr interaction = currentNotationInteraction();
if (!interaction) {
return;
}
INotationNoteInputPtr noteInput = interaction->noteInput();
if (noteInput->isNoteInputMode()) {
if (steps > 0) {
noteInput->halveNoteInputDuration();
} else {
noteInput->doubleNoteInputDuration();
}
} else {
interaction->increaseDecreaseDuration(steps, stepByDots);
}
}
void NotationActionController::realtimeAdvance()
{
TRACEFUNC;
INotationMidiInputPtr midiInput = currentNotationMidiInput();
if (!midiInput) {
return;
}
midiInput->onRealtimeAdvance();
}
bool NotationActionController::isMoveSelectionAvailable(MoveSelectionType type) const
{
auto interaction = currentNotationInteraction();
return interaction && interaction->moveSelectionAvailable(type);
}
bool NotationActionController::isToggleLayoutBreakAvailable() const
{
auto interaction = currentNotationInteraction();
return interaction && interaction->toggleLayoutBreakAvailable();
}
ScoreConfig NotationActionController::scoreConfig() const
{
auto interaction = currentNotationInteraction();
if (!interaction) {
return ScoreConfig();
}
return interaction->scoreConfig();
}
muse::async::Channel NotationActionController::scoreConfigChanged() const
{
return m_scoreConfigChanged;
}
muse::Ret NotationActionController::moveWithRet(MoveDirection direction, bool quickly)
{
TRACEFUNC;
auto interaction = currentNotationInteraction();
if (!interaction) {
return muse::make_ret(muse::Ret::Code::InternalError);
}
const NoteInputState& state = interaction->noteInput()->state();
const bool previousSelectionExists = currentNotationScore() && currentNotationScore()->selection().currentCR();
if (interaction->selection()->isNone() && previousSelectionExists && !state.beyondScore()) {
// Try to restore the previous selection...
interaction->moveSelectionDeprecated(direction, MoveSelectionType::EngravingItem);
seekAndPlaySelectedElement(true);
return muse::make_ok();
}
const EngravingItem* selectedElement = interaction->selection()->element();
INotationNoteInputPtr noteInput = interaction->noteInput();
bool playChord = false;
switch (direction) {
case MoveDirection::Up:
case MoveDirection::Down:
if (!quickly && selectedElement && selectedElement->isLyrics()) {
interaction->moveLyrics(direction);
} else if (selectedElement && (selectedElement->isTextBase() || selectedElement->isArticulationFamily())) {
interaction->nudge(direction, quickly);
} else if (selectedElement && selectedElement->hasGrips() && interaction->isGripEditStarted()) {
interaction->nudgeAnchors(direction);
} else if (state.isValid() && noteInput->isNoteInputMode() && noteInput->usingNoteInputMethod(NoteInputMethod::BY_DURATION)) {
moveInputNotes(direction == MoveDirection::Up, quickly ? PitchMode::OCTAVE : PitchMode::DIATONIC);
return muse::make_ok();
} else if (noteInput->isNoteInputMode() && noteInput->state().staffGroup() == mu::engraving::StaffGroup::TAB) {
if (quickly) {
interaction->movePitch(direction, PitchMode::OCTAVE);
}
interaction->moveSelectionDeprecated(direction, MoveSelectionType::String);
return muse::make_ok();
} else if (interaction->selection()->isNone() && !state.beyondScore()) {
interaction->select(SelectionTarget::FirstItem);
} else {
interaction->movePitch(direction, quickly ? PitchMode::OCTAVE : PitchMode::CHROMATIC);
}
break;
case MoveDirection::Right:
case MoveDirection::Left:
if (playbackController()->isPlaying()) {
engraving::MeasureBeat beat = playbackController()->currentBeat();
int targetBeatIdx = static_cast(beat.beat);
int targetMeasureIdx = beat.measureIndex;
int increment = (direction == MoveDirection::Right ? 1 : -1);
if (quickly) {
targetBeatIdx = 0;
targetMeasureIdx += increment;
if (targetMeasureIdx > beat.maxMeasureIndex) {
targetMeasureIdx = beat.maxMeasureIndex;
} else if (targetMeasureIdx < 0) {
targetMeasureIdx = 0;
}
} else {
targetBeatIdx += increment;
if (targetBeatIdx > beat.maxBeatIndex) {
targetBeatIdx = 0;
targetMeasureIdx += 1;
} else if (targetBeatIdx < 0) {
targetMeasureIdx -= 1;
// Set target beat to max beat of previous bar
engraving::TimeSigMap* timeSigMap = currentMasterNotation()->masterScore()->sigmap();
int targetBarStartTick = timeSigMap->bar2tick(targetMeasureIdx, 0);
targetBeatIdx = timeSigMap->timesig(Fraction::fromTicks(targetBarStartTick)).timesig().numerator() - 1;
}
}
playbackController()->seekBeat(targetMeasureIdx, targetBeatIdx);
return muse::make_ok();
}
if (interaction->isTextEditingStarted() && textNavigationAvailable()) {
navigateToTextElementInNearMeasure(direction);
return muse::make_ok();
}
if (selectedElement && selectedElement->isTextBase()) {
interaction->nudge(direction, quickly);
} else if (selectedElement && selectedElement->hasGrips() && interaction->isGripEditStarted()) {
interaction->nudgeAnchors(direction);
} else {
if (interaction->selection()->isNone() && !state.beyondScore()) {
interaction->select(SelectionTarget::FirstItem);
}
interaction->moveSelectionDeprecated(direction, quickly ? MoveSelectionType::Measure : MoveSelectionType::Chord);
playChord = true;
}
break;
case MoveDirection::Undefined:
break;
}
seekAndPlaySelectedElement(playChord);
return muse::make_ok();
}
void NotationActionController::move(MoveDirection direction, bool quickly)
{
moveWithRet(direction, quickly);
}
void NotationActionController::moveInputNotes(bool up, PitchMode mode)
{
INotationInteractionPtr interaction = currentNotationInteraction();
if (!interaction) {
return;
}
interaction->noteInput()->setRestMode(false);
interaction->noteInput()->moveInputNotes(up, mode);
if (configuration()->isPlayPreviewNotesInInputByDuration()) {
const NoteInputState& state = interaction->noteInput()->state();
playbackController()->playNotes(state.notes(), state.staffIdx(), state.segment());
}
}
void NotationActionController::movePitchDiatonic(MoveDirection direction, bool)
{
INotationInteractionPtr interaction = currentNotationInteraction();
if (!interaction) {
return;
}
INotationNoteInputPtr noteInput = interaction->noteInput();
if (noteInput->state().isValid() && noteInput->isNoteInputMode() && noteInput->usingNoteInputMethod(NoteInputMethod::BY_DURATION)) {
moveInputNotes(direction == MoveDirection::Up, PitchMode::DIATONIC);
return;
}
interaction->movePitch(direction, PitchMode::DIATONIC);
seekAndPlaySelectedElement(true);
}
void NotationActionController::changeVoice(voice_idx_t voiceIndex)
{
TRACEFUNC;
INotationInteractionPtr interaction = currentNotationInteraction();
if (!interaction) {
return;
}
INotationNoteInputPtr noteInput = interaction->noteInput();
if (!noteInput) {
return;
}
if (interaction->selection()->isNone()) {
if (!noteInput->isNoteInputMode() && !toggleNoteInputAllowed()) {
return;
}
startNoteInput();
}
noteInput->setCurrentVoice(voiceIndex);
if (!noteInput->isNoteInputMode()) {
interaction->changeSelectedElementsVoice(voiceIndex);
}
}
void NotationActionController::swapVoices(voice_idx_t voiceIndex1, voice_idx_t voiceIndex2)
{
TRACEFUNC;
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
interaction->swapVoices(voiceIndex1, voiceIndex2);
}
void NotationActionController::cutSelection()
{
TRACEFUNC;
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
interaction->copySelection();
interaction->deleteSelection();
}
void NotationActionController::repeatSelection()
{
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
interaction->repeatSelection();
seekAndPlaySelectedElement(true);
}
void NotationActionController::pasteSelection(PastingType type)
{
TRACEFUNC;
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
Fraction scale = resolvePastingScale(interaction, type);
interaction->pasteSelection(scale);
seekAndPlaySelectedElement(DONT_PLAY_CHORD);
}
Fraction NotationActionController::resolvePastingScale(const INotationInteractionPtr& interaction, PastingType type) const
{
TRACEFUNC;
const Fraction DEFAULT_SCALE(1, 1);
switch (type) {
case PastingType::Default: return DEFAULT_SCALE;
case PastingType::Half: return Fraction(1, 2);
case PastingType::Double: return Fraction(2, 1);
case PastingType::Special:
Fraction scale = DEFAULT_SCALE;
Fraction duration = interaction->noteInput()->state().duration().fraction();
if (duration.isValid() && !duration.isZero()) {
scale = duration * 4;
scale.reduce();
}
return scale;
}
return DEFAULT_SCALE;
}
void NotationActionController::addTie()
{
TRACEFUNC;
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
auto noteInput = interaction->noteInput();
if (!noteInput) {
return;
}
if (noteInput->isNoteInputMode()) {
noteInput->addTie();
seekAndPlaySelectedElement(true);
} else {
interaction->toggleTieForSelection();
}
}
void NotationActionController::addLaissezVib()
{
TRACEFUNC;
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
auto noteInput = interaction->noteInput();
if (!noteInput) {
return;
}
if (noteInput->isNoteInputMode()) {
noteInput->addLaissezVib();
seekAndPlaySelectedElement(true);
} else {
interaction->addLaissezVibToSelection();
}
}
void NotationActionController::addSlur()
{
TRACEFUNC;
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
auto noteInput = interaction->noteInput();
if (!noteInput) {
return;
}
if (noteInput->isNoteInputMode() && noteInput->state().slur()) {
noteInput->resetSlur();
} else {
interaction->addSlurToSelection();
}
}
void NotationActionController::addHammerOnPullOff()
{
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
interaction->addHammerOnPullOffToSelection();
}
void NotationActionController::addFret(int num)
{
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
interaction->addFret(num);
seekAndPlaySelectedElement(currentNotationScore()->playChord());
}
void NotationActionController::insertClef(mu::engraving::ClefType type)
{
INotationInteractionPtr interaction = currentNotationInteraction();
if (!interaction || !interaction->canInsertClef(type)) {
return;
}
interaction->insertClef(type);
}
void NotationActionController::addText(TextStyleType type)
{
TRACEFUNC;
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
EngravingItem* item = nullptr;
const INotationSelectionPtr sel = interaction->selection();
if (sel->isRange()) {
const INotationSelectionRangePtr range = sel->range();
item = range->rangeStartSegment()->firstElementForNavigation(range->startStaffIndex());
} else {
item = interaction->contextItem();
}
if (isVerticalBoxTextStyle(type)) {
if (!item || !item->isVBox()) {
interaction->addTextToTopFrame(type);
return;
}
}
interaction->addTextToItem(type, item);
}
void NotationActionController::addImage()
{
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
EngravingItem* item = interaction->contextItem();
if (!interaction->canAddImageToItem(item)) {
return;
}
std::vector filter
= { muse::trc("notation", "All Supported Files") + " (*.svg *.svgz *.jpg *.jpeg *.png *.bmp *.tif *.tiff)",
muse::trc("notation", "Scalable Vector Graphics") + " (*.svg *.svgz)",
muse::trc("notation", "JPEG") + " (*.jpg *.jpeg)",
muse::trc("notation", "PNG Bitmap Graphic") + " (*.png)",
muse::trc("notation", "Bitmap") + " (*.bmp)",
muse::trc("notation", "TIFF") + " (*.tif *.tiff)",
muse::trc("notation", "All") + " (*)" };
muse::io::path_t path = interactive()->selectOpeningFileSync(muse::trc("notation", "Insert Image"), "", filter);
interaction->addImageToItem(path, item);
}
void NotationActionController::addFiguredBass()
{
TRACEFUNC;
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
interaction->addFiguredBass();
}
void NotationActionController::addGuitarBend(GuitarBendType bendType)
{
TRACEFUNC;
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
interaction->addGuitarBend(bendType);
}
void NotationActionController::addFretboardDiagram()
{
TRACEFUNC;
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
interaction->addFretboardDiagram();
}
void NotationActionController::openSelectionMoreOptions()
{
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
auto item = interaction->contextItem();
if (!item) {
return;
}
bool noteSelected = item->isNote();
if (noteSelected) {
interactive()->open("musescore://notation/selectnote");
} else {
interactive()->open("musescore://notation/selectelement");
}
}
muse::Ret NotationActionController::startEditSelectedElement(const muse::rcommand::Params& params)
{
auto interaction = currentNotationInteraction();
if (!interaction) {
return muse::make_ret(Ret::Code::NotSupported);
}
auto selection = interaction->selection();
if (!selection) {
return muse::make_ret(Ret::Code::BadArgs);
}
mu::engraving::EngravingItem* element = selection->element();
if (!element) {
return muse::make_ret(Ret::Code::BadArgs);
}
if (element->isInstrumentName()) {
openStaffProperties();
return muse::make_ok();
}
if (elementHasPopup(element) && !interaction->textEditingAllowed(element)) {
IF_ASSERT_FAILED(m_viewController) {
return muse::make_ret(Ret::Code::NotSupported);
}
m_viewController->togglePopupForItemIfSupports(element);
return muse::make_ok();
}
if (element->isText()) {
TextStyleType styleType = mu::engraving::toText(element)->textStyleType();
if (styleType == mu::engraving::TextStyleType::HEADER || styleType == mu::engraving::TextStyleType::FOOTER) {
openEditStyleDialog({ { "page_code", Val("header-and-footer") } });
return muse::make_ok();
}
}
if (interaction->textEditingAllowed(element)) {
float posX = params.at("pos_x").toFloat();
float posY = params.at("pos_y").toFloat();
PointF cursorPos = PointF(posX, posY);
interaction->startEditText(element, cursorPos);
} else {
interaction->startEditElement(element);
}
return muse::make_ok();
}
muse::Ret NotationActionController::startEditSelectedText(const muse::rcommand::Params& params)
{
auto interaction = currentNotationInteraction();
if (!interaction) {
return muse::make_ret(Ret::Code::NotSupported);
}
auto selection = interaction->selection();
if (!selection) {
return muse::make_ret(Ret::Code::BadArgs);
}
mu::engraving::EngravingItem* element = selection->element();
if (interaction->textEditingAllowed(element)) {
float posX = params.at("pos_x").toFloat();
float posY = params.at("pos_y").toFloat();
PointF cursorPos = PointF(posX, posY);
interaction->startEditText(element, cursorPos);
}
return muse::make_ok();
}
muse::Ret NotationActionController::addMeasures(const muse::rcommand::Params& params, AddBoxesTarget target)
{
if (params.contains("count")) {
int count = params.at("count").toInt();
addBoxes(BoxType::Measure, count, target);
} else {
interactive()->open("musescore://notation/selectmeasurescount")
.onResolve(this, [this, target](const Val& v) {
int count = v.toInt();
addBoxes(BoxType::Measure, count, target);
});
}
return muse::make_ok();
}
void NotationActionController::addBoxes(BoxType boxType, int count, AddBoxesTarget target)
{
TRACEFUNC;
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
interaction->addBoxes(boxType, count, target);
}
void NotationActionController::unrollRepeats()
{
NOT_IMPLEMENTED;
// TODO: https://github.com/musescore/MuseScore/issues/9670
}
void NotationActionController::toggleMmrest()
{
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
interaction->execute(&mu::engraving::Score::cmdToggleMmrest, TranslatableString("undoableAction", "Toggle multimeasure rests"));
}
void NotationActionController::toggleHideEmpty()
{
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
interaction->execute(&mu::engraving::Score::cmdToggleHideEmpty, TranslatableString("undoableAction", "Toggle empty staves"));
}
void NotationActionController::addFullMeasureRest()
{
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
interaction->execute(&mu::engraving::Score::cmdFullMeasureRest, TranslatableString("undoableAction", "Enter full measure rest"));
}
void NotationActionController::addStretch(qreal value)
{
TRACEFUNC;
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
auto selection = currentNotationSelection();
if (!selection) {
return;
}
if (!selection->isRange()) {
return;
}
interaction->addStretch(value);
}
void NotationActionController::resetStretch()
{
TRACEFUNC;
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
auto selection = currentNotationSelection();
if (!selection) {
return;
}
if (!selection->isRange()) {
return;
}
interaction->resetStretch();
}
void NotationActionController::resetBeamMode()
{
TRACEFUNC;
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
auto selection = currentNotationSelection();
if (!selection) {
return;
}
if (selection->isNone() || selection->isRange()) {
interaction->resetBeamMode();
}
}
muse::Ret NotationActionController::openEditStyleDialog(const muse::rcommand::Params& params)
{
UriQuery uri("musescore://notation/style");
if (params.contains("page_code")) {
uri.addParam("currentPageCode", Val(params.at("page_code").toString()));
if (params.contains("sub_page_code")) {
uri.addParam("currentSubPageCode", Val(params.at("sub_page_code").toString()));
}
}
interactive()->open(uri);
return muse::make_ok();
}
void NotationActionController::openPageSettingsDialog()
{
interactive()->open("musescore://notation/pagesettings");
}
void NotationActionController::openStaffProperties()
{
interactive()->open("musescore://notation/staffproperties");
}
void NotationActionController::openEditStringsDialog()
{
interactive()->open("musescore://notation/editstrings");
}
void NotationActionController::openBreaksDialog()
{
interactive()->open("musescore://notation/breaks");
}
void NotationActionController::openTransposeDialog()
{
interactive()->open("musescore://notation/transpose").onResolve(this, [this](const Val&) {
currentNotationInteraction()->checkAndShowError();
});
}
void NotationActionController::openPartsDialog()
{
interactive()->open("musescore://notation/parts");
}
muse::io::path_t NotationActionController::selectStyleFile(bool forLoad)
{
muse::io::path_t dir = configuration()->userStylesPath();
std::string filterName = forLoad
? muse::trc("notation", "MuseScore style files")
: muse::trc("notation", "MuseScore style file");
std::vector filter = { filterName + " (*.mss)" };
return forLoad
? interactive()->selectOpeningFileSync(muse::trc("notation", "Load style"), dir, filter)
: interactive()->selectSavingFileSync(muse::trc("notation", "Save style"), dir, filter);
}
void NotationActionController::loadStyle()
{
TRACEFUNC;
auto path = selectStyleFile(true);
if (!path.empty()) {
File f(path.toQString());
if (!f.open(IODevice::ReadOnly) || !mu::engraving::MStyle::isValid(&f)) {
interactive()->error(muse::trc("notation", "The style file could not be loaded."),
f.errorString());
return;
}
if (!notationStyle()->loadStyle(path.toQString(), false)) {
auto promise = interactive()->warning(
muse::trc("notation",
"Since this style file is from a different version of MuseScore Studio, your score is not guaranteed to display correctly."),
muse::trc("notation", "Click OK to load anyway."), { IInteractive::Button::Ok, IInteractive::Button::Cancel },
IInteractive::Button::Ok);
promise.onResolve(this, [this, path](const IInteractive::Result& res) {
if (res.isButton(IInteractive::Button::Ok)) {
notationStyle()->loadStyle(path.toQString(), true);
}
});
}
}
}
void NotationActionController::saveStyle()
{
TRACEFUNC;
auto path = selectStyleFile(false);
if (!path.empty()) {
if (!notationStyle()->saveStyle(path)) {
interactive()->error(muse::trc("notation", "The style file could not be saved."),
muse::trc("notation", "An error occurred."));
}
}
}
bool NotationActionController::isTextEditing() const
{
auto interaction = currentNotationInteraction();
if (!interaction) {
return false;
}
return interaction->isTextEditingStarted();
}
bool NotationActionController::isLyricsEditing() const
{
auto interaction = currentNotationInteraction();
if (!interaction) {
return false;
}
return interaction->isTextEditingStarted()
&& interaction->selection()->element()
&& interaction->selection()->element()->isLyrics();
}
muse::async::Channel NotationActionController::textEditingChanged() const
{
return m_textEditingChanged;
}
bool NotationActionController::textNavigationAvailable() const
{
return resolveTextNavigationAvailable(TextNavigationType::NearNoteOrRest);
}
bool NotationActionController::textNavigationByBeatsAvailable() const
{
return resolveTextNavigationAvailable(TextNavigationType::NearBeat);
}
bool NotationActionController::textNavigationByFractionAvailable() const
{
return resolveTextNavigationAvailable(TextNavigationType::Fraction);
}
bool NotationActionController::resolveTextNavigationAvailable(TextNavigationType type) const
{
if (!isEditingText()) {
return false;
}
static const QList allowedElementsForTextNavigation {
mu::engraving::ElementType::LYRICS,
mu::engraving::ElementType::HARMONY,
mu::engraving::ElementType::FIGURED_BASS,
mu::engraving::ElementType::STICKING,
mu::engraving::ElementType::FINGERING
};
static const QList allowedElementsForBeatsNavigation {
mu::engraving::ElementType::HARMONY
};
static const QList allowedElementsForFractionNavigation {
mu::engraving::ElementType::HARMONY,
mu::engraving::ElementType::FIGURED_BASS
};
const mu::engraving::EngravingItem* element = selectedElement();
mu::engraving::ElementType elementType = element ? element->type() : mu::engraving::ElementType::INVALID;
switch (type) {
case TextNavigationType::NearNoteOrRest:
return allowedElementsForTextNavigation.contains(elementType);
case TextNavigationType::NearBeat:
return allowedElementsForBeatsNavigation.contains(elementType);
case TextNavigationType::Fraction:
return allowedElementsForFractionNavigation.contains(elementType);
}
return false;
}
muse::Ret NotationActionController::nextTextElement()
{
navigateToTextElement(MoveDirection::Right, NEAR_NOTE_OR_REST);
return muse::make_ok();
}
muse::Ret NotationActionController::prevTextElement()
{
navigateToTextElement(MoveDirection::Left, NEAR_NOTE_OR_REST);
return muse::make_ok();
}
muse::Ret NotationActionController::nextWord()
{
if (!textNavigationAvailable()) {
return muse::make_ret(Ret::Code::NotSupported);
}
navigateToTextElement(MoveDirection::Right, NEAR_NOTE_OR_REST, false);
return muse::make_ok();
}
void NotationActionController::nextBeatTextElement()
{
navigateToTextElement(MoveDirection::Right);
}
void NotationActionController::prevBeatTextElement()
{
navigateToTextElement(MoveDirection::Left);
}
void NotationActionController::navigateToTextElement(MoveDirection direction, bool nearNoteOrRest, bool moveOnly)
{
const mu::engraving::EngravingItem* element = selectedElement();
if (!element) {
return;
}
if (element->isLyrics()) {
currentNotationInteraction()->navigateToLyrics(direction, moveOnly);
} else if (element->isHarmony()) {
const engraving::Harmony* chordSymbol = editedChordSymbol();
// otherwise, chord symbol will be deleted when navigating away from it
const bool canPlay = chordSymbol && !chordSymbol->harmonyName().empty();
currentNotationInteraction()->navigateToNearHarmony(direction, nearNoteOrRest);
if (canPlay) {
playbackController()->playElements({ chordSymbol });
}
} else if (element->isFiguredBass()) {
currentNotationInteraction()->navigateToNearFiguredBass(direction);
} else {
currentNotationInteraction()->navigateToNearText(direction);
}
}
void NotationActionController::navigateToTextItemByFraction(const Fraction& fraction)
{
const mu::engraving::EngravingItem* element = selectedElement();
if (!element) {
return;
}
if (element->isHarmony()) {
const engraving::Harmony* chordSymbol = editedChordSymbol();
// otherwise, chord symbol will be deleted when navigating away from it
const bool canPlay = chordSymbol && !chordSymbol->harmonyName().empty();
currentNotationInteraction()->navigateToHarmony(fraction);
if (canPlay) {
playbackController()->playElements({ chordSymbol });
}
} else if (element->isFiguredBass()) {
currentNotationInteraction()->navigateToFiguredBass(fraction);
}
}
void NotationActionController::navigateToTextElementInNearMeasure(MoveDirection direction)
{
const mu::engraving::EngravingItem* element = selectedElement();
if (!element) {
return;
}
if (element->isHarmony()) {
const engraving::Harmony* chordSymbol = editedChordSymbol();
// otherwise, chord symbol will be deleted when navigating away from it
const bool canPlay = chordSymbol && !chordSymbol->harmonyName().empty();
currentNotationInteraction()->navigateToHarmonyInNearMeasure(direction);
if (canPlay) {
playbackController()->playElements({ chordSymbol });
}
} else if (element->isFiguredBass()) {
currentNotationInteraction()->navigateToFiguredBassInNearMeasure(direction);
}
}
bool NotationActionController::isEditingText() const
{
auto interaction = currentNotationInteraction();
if (!interaction) {
return false;
}
return interaction->isTextEditingStarted();
}
void NotationActionController::openTupletOtherDialog()
{
interactive()->open("musescore://notation/othertupletdialog");
}
void NotationActionController::openStaffTextPropertiesDialog()
{
interactive()->open("musescore://notation/stafftextproperties");
}
void NotationActionController::openMeasurePropertiesDialog()
{
if (currentNotationInteraction()->selectedMeasure() != nullptr) {
interactive()->open("musescore://notation/measureproperties");
}
}
void NotationActionController::openEditGridSizeDialog()
{
interactive()->open("musescore://notation/editgridsize");
}
void NotationActionController::openRealizeChordSymbolsDialog()
{
interactive()->open("musescore://notation/realizechordsymbols");
}
void NotationActionController::toggleScoreConfig(ScoreConfigType configType)
{
TRACEFUNC;
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
ScoreConfig config = interaction->scoreConfig();
bool isShow = config.isShown(configType);
config.setShown(configType, !isShow);
interaction->setScoreConfig(config);
}
void NotationActionController::toggleConcertPitch()
{
TRACEFUNC;
INotationStylePtr style = notationStyle();
if (!style) {
return;
}
bool toggle = !style->styleValue(StyleId::concertPitch).toBool();
const TranslatableString actionName = toggle
? TranslatableString("undoableAction", "Display concert pitch")
: TranslatableString("undoableAction", "Display transposed");
currentNotationUndoStack()->prepareChanges(actionName);
style->setStyleValue(StyleId::concertPitch, toggle);
currentNotationUndoStack()->commitChanges();
}
void NotationActionController::seekAndPlaySelectedElement(bool playChord)
{
seekSelectedElement();
playSelectedElement(playChord);
}
void NotationActionController::seekSelectedElement()
{
const IMasterNotationPtr master = currentMasterNotation();
if (!master || master->playback()->isLoopEnabled()) {
return;
}
const EngravingItem* element = selectedElement();
if (!element) {
return;
}
playbackController()->seekElement(element);
}
void NotationActionController::playSelectedElement(bool playChord)
{
TRACEFUNC;
const EngravingItem* element = selectedElement();
if (!element) {
return;
}
if (playChord) {
element = element->elementBase();
}
playbackController()->playElements({ element });
currentNotationScore()->setPlayChord(false);
currentNotationScore()->setPlayNote(false);
}
bool NotationActionController::toggleNoteInputAllowed() const
{
if (!isNoteInputAllowed()) {
return false;
}
//! NOTE: We're more strict about starting note input mode than exiting it.
if (!isNoteInputMode() && isEditingElement()) {
return false;
}
return true;
}
void NotationActionController::startNoteInput()
{
INotationNoteInputPtr noteInput = currentNotationNoteInput();
if (noteInput) {
noteInput->startNoteInput(configuration()->defaultNoteInputMethod());
}
}
bool NotationActionController::hasSelection() const
{
return currentNotationSelection() ? !currentNotationSelection()->isNone() : false;
}
bool NotationActionController::isSingleSelection() const
{
return currentNotationSelection() ? currentNotationSelection()->isSingle() : false;
}
bool NotationActionController::isRangeSelection() const
{
return currentNotationSelection() ? currentNotationSelection()->isRange() : false;
}
mu::engraving::EngravingItem* NotationActionController::selectedElement() const
{
auto selection = currentNotationSelection();
return selection ? selection->element() : nullptr;
}
bool NotationActionController::isNoteOrRestSelected() const
{
if (isNoteInputMode()) {
return true;
}
INotationSelectionPtr selection = currentNotationInteraction() ? currentNotationInteraction()->selection() : nullptr;
return selection && selection->elementsSelected(NOTE_REST_TYPES);
}
bool NotationActionController::isHairpinSelected() const
{
INotationSelectionPtr selection = currentNotationInteraction() ? currentNotationInteraction()->selection() : nullptr;
return selection && selection->element() && selection->element()->isHairpinSegment();
}
const mu::engraving::Harmony* NotationActionController::editedChordSymbol() const
{
INotationInteractionPtr interaction = currentNotationInteraction();
if (!interaction) {
return nullptr;
}
const TextBase* text = interaction->editedText();
if (!text || !text->isHarmony()) {
return nullptr;
}
return toHarmony(text);
}
bool NotationActionController::elementHasPopup(const EngravingItem* e) const
{
return AbstractElementPopupModel::hasElementEditPopup(e);
}
bool NotationActionController::canUndo() const
{
return currentNotationUndoStack() ? currentNotationUndoStack()->canUndo() : false;
}
bool NotationActionController::canRedo() const
{
return currentNotationUndoStack() ? currentNotationUndoStack()->canRedo() : false;
}
muse::async::Notification NotationActionController::stackChanged() const
{
return m_stackChanged;
}
bool NotationActionController::isTablatureStaff() const
{
return isNotEditingElement() && currentNotationScore()->inputState().staffGroup() == mu::engraving::StaffGroup::TAB;
}
bool NotationActionController::isAutomationModeEnabled() const
{
return currentMasterNotation() ? currentMasterNotation()->automation()->isAutomationModeEnabled() : false;
}
muse::async::Notification NotationActionController::automationModeEnabledChanged() const
{
return m_automationModeEnabledChanged;
}
bool NotationActionController::isDebuggingCommandEnabled(const muse::rcommand::Command& command) const
{
auto it = s_debuggingCommands.find(command);
if (it != s_debuggingCommands.cend()) {
return engravingConfiguration()->debuggingOptions().*(it->second);
}
return false;
}
muse::async::Notification NotationActionController::debuggingOptionsChanged() const
{
return engravingConfiguration()->debuggingOptionsChanged();
}
bool NotationActionController::isEditingElement() const
{
auto interaction = currentNotationInteraction();
if (interaction) {
return interaction->isEditingElement() || interaction->isDragStarted();
}
return false;
}
bool NotationActionController::isNotEditingElement() const
{
return !isEditingElement();
}
void NotationActionController::checkForScoreCorruptions()
{
project::INotationProjectPtr project = projectSession()->project();
if (!project) {
return;
}
String fileName = io::filename(project->path()).toString();
Ret ret = project->masterNotation()->masterScore()->sanityCheck();
if (ret) {
std::string title = muse::mtrc("project", "File “%1” seems not corrupted").arg(fileName).toStdString();
std::string body = muse::trc("project", "This file does not seem to contain errors.");
interactive()->info(title, body);
} else {
std::string title = muse::mtrc("project", "File “%1” is corrupted").arg(fileName).toStdString();
IInteractive::Text text;
text.text = muse::trc("project", "This file contains errors that could cause MuseScore Studio to malfunction. "
"Please fix those at the earliest, to prevent crashes and further corruptions.");
text.detailedText = ret.text();
interactive()->warning(title, text);
}
}
void NotationActionController::toggleAutomation()
{
TRACEFUNC;
IMasterNotationPtr masterNotation = currentMasterNotation();
if (!masterNotation) {
return;
}
const bool isEnabled = masterNotation->automation()->isAutomationModeEnabled();
masterNotation->automation()->setAutomationModeEnabled(!isEnabled);
}
muse::Ret NotationActionController::selectAutomationType(const muse::rcommand::Params& params)
{
const std::string type = params.at("type").toString();
mu::engraving::AutomationType automationType = mu::engraving::AutomationType::Dynamics;
if (type == "tempo") {
automationType = mu::engraving::AutomationType::Tempo;
} else if (type == "volume") {
automationType = mu::engraving::AutomationType::Volume;
} else if (type == "pan") {
automationType = mu::engraving::AutomationType::Pan;
}
configuration()->setCurrentAutomationType(automationType);
return muse::make_ok();
}
bool NotationActionController::isNoteInputActionAllowed() const
{
if (!isNoteInputMode() && !toggleNoteInputAllowed()) {
return false;
}
return !isTablatureStaff();
}
muse::Ret NotationActionController::select(const muse::rcommand::Params& params)
{
SelectionTarget target = Contract::get(params);
if (target == SelectionTarget::Undefined) {
return muse::make_ret(Ret::Code::BadArgs);
}
PlayMode playMode = Contract::get(params);
select(target, playMode);
return muse::make_ok();
}
void NotationActionController::select(SelectionTarget target, PlayMode playMode)
{
auto interaction = currentNotationInteraction();
if (!interaction) {
return;
}
interaction->select(target);
seekSelectedElement();
if (playMode != PlayMode::NoPlay) {
playSelectedElement(playMode == PlayMode::PlayChord);
}
}
// COMMANDS
void NotationActionController::registerCommand(const muse::rcommand::Command& command, std::function handler)
{
registerCommand(command, handler, nullptr);
}
void NotationActionController::registerCommand(const muse::rcommand::Command& command,
std::function handler,
bool (NotationActionController::* enabler)() const)
{
commandDispatcher()->onRequest(this, command, [this, command, handler, enabler]() {
if (!commandsState()->commandState(command).enabled) {
return muse::make_ret(Ret::Code::NotSupported);
}
if (enabler && !(this->*enabler)()) {
return muse::make_ret(Ret::Code::NotSupported);
}
handler();
return muse::make_ok();
});
}
void NotationActionController::registerCommandWithParams(const muse::rcommand::Command& command,
std::function handler)
{
commandDispatcher()->onRequest(this, command, [this, command, handler](const rcommand::Request& request) {
if (!commandsState()->commandState(command).enabled) {
return rcommand::make_response(request, muse::make_ret(Ret::Code::NotSupported));
}
muse::Ret ret = handler(request.params);
return rcommand::make_response(request, ret);
});
}
void NotationActionController::registerCommand(const muse::rcommand::Command& command, void (NotationActionController::* handler)())
{
registerCommand(command, handler, nullptr);
}
void NotationActionController::registerCommand(const muse::rcommand::Command& command,
void (NotationActionController::* handler)(),
bool (NotationActionController::* enabler)() const)
{
commandDispatcher()->onRequest(this, command, [this, command, handler, enabler]() {
if (!commandsState()->commandState(command).enabled) {
return muse::make_ret(Ret::Code::NotSupported);
}
if (enabler && !(this->*enabler)()) {
return muse::make_ret(Ret::Code::NotSupported);
}
(this->*handler)();
return muse::make_ok();
});
}
void NotationActionController::registerCommandWithParams(const muse::rcommand::Command& command,
muse::Ret (NotationActionController::* handler)(const muse::rcommand::Params&))
{
commandDispatcher()->onRequest(this, command, [this, command, handler](const rcommand::Request& request) {
if (!commandsState()->commandState(command).enabled) {
return rcommand::make_response(request, muse::make_ret(Ret::Code::NotSupported));
}
muse::Ret ret = (this->*handler)(request.params);
return rcommand::make_response(request, ret);
});
}
void NotationActionController::registerCommand(const muse::rcommand::Command& command,
void (INotationInteraction::* handler)(),
PlayMode playMode,
bool (NotationActionController::* enabler)() const)
{
registerCommand(command, [this, handler, playMode, enabler]()
{
if (enabler && !(this->*enabler)()) {
return muse::make_ret(Ret::Code::NotSupported);
}
INotationPtr notation = currentNotation();
if (notation) {
(notation->interaction().get()->*handler)();
seekSelectedElement();
if (playMode != PlayMode::NoPlay) {
playSelectedElement(playMode == PlayMode::PlayChord);
}
}
return muse::make_ok();
});
}
template
void NotationActionController::registerCommand(const muse::rcommand::Command& command,
void (INotationInteraction::* handler)(P1), P1 param1,
PlayMode playMode,
bool (NotationActionController::* enabler)() const)
{
registerCommand(command, [this, handler, param1, playMode]()
{
INotationPtr notation = currentNotation();
if (notation) {
(notation->interaction().get()->*handler)(param1);
seekSelectedElement();
if (playMode != PlayMode::NoPlay) {
playSelectedElement(playMode == PlayMode::PlayChord);
}
}
}, enabler);
}
void NotationActionController::registerNoteInputCommand(const muse::rcommand::Command& command, NoteInputMethod method)
{
registerCommand(command, [this, method]() { toggleNoteInput(method); }, &NotationActionController::toggleNoteInputAllowed);
}
void NotationActionController::registerNoteCommand(const muse::rcommand::Command& command,
NoteName noteName,
NoteAddingMode addingMode)
{
registerCommand(command, [this, noteName, addingMode]()
{
addNote(noteName, addingMode);
});
}
void NotationActionController::registerNavigationByFractionCommand(const muse::rcommand::Command& command, const Fraction& fraction)
{
registerCommand(command, [this, fraction]() {
navigateToTextItemByFraction(
fraction);
}, &NotationActionController::textNavigationByFractionAvailable);
}
void NotationActionController::registerGetCommand(const muse::rcommand::Command& command,
std::function()> handler)
{
commandDispatcher()->onRequest(this, command, [this, command, handler](const rcommand::Request& request) {
if (!commandsState()->commandState(command).enabled) {
return rcommand::make_response(request, muse::make_ret(Ret::Code::NotSupported));
}
muse::RetVal rv = handler();
return rcommand::make_response(request, rv.ret, rv.val);
});
}
void NotationActionController::registerGetCommand(const muse::rcommand::Command& command,
muse::RetVal(NotationActionController::* handler)() const)
{
commandDispatcher()->onRequest(this, command, [this, command, handler](const rcommand::Request& request) {
if (!commandsState()->commandState(command).enabled) {
return rcommand::make_response(request, muse::make_ret(Ret::Code::NotSupported));
}
muse::RetVal rv = (this->*handler)();
return rcommand::make_response(request, rv.ret, rv.val);
});
}
void NotationActionController::registerViewCommand(const muse::rcommand::Command& command, void (INotationViewController::* handler)())
{
commandDispatcher()->onRequest(this, command, [this, command, handler]() {
if (!commandsState()->commandState(command).enabled) {
return muse::make_ret(Ret::Code::NotSupported);
}
if (!m_viewController) {
LOGE() << "command: " << command.isValid() << " NotSupported, viewController is null";
return muse::make_ret(Ret::Code::NotSupported);
}
(m_viewController->*handler)();
return muse::make_ok();
});
}
template
void NotationActionController::registerViewCommand(const muse::rcommand::Command& command,
void (INotationViewController::* handler)(P1),
P1 p1)
{
commandDispatcher()->onRequest(this, command, [this, command, handler, p1]() {
if (!commandsState()->commandState(command).enabled) {
return muse::make_ret(Ret::Code::NotSupported);
}
if (!m_viewController) {
LOGE() << "command: " << command.isValid() << " NotSupported, viewController is null";
return muse::make_ret(Ret::Code::NotSupported);
}
(m_viewController->*handler)(p1);
return muse::make_ok();
});
}
muse::Ret NotationActionController::zoomToPercent(const muse::rcommand::Params& params)
{
if (!params.contains("percent")) {
return muse::make_ret(Ret::Code::BadArgs);
}
if (!m_viewController) {
LOGE() << "command: " << ZOOM_TO_PERCENT_COMMAND.toString() << " NotSupported, viewController is null";
return muse::make_ret(Ret::Code::NotSupported);
}
int zoomPercentage = params.at("percent").toInt();
m_viewController->setZoom(zoomPercentage);
return muse::make_ok();
}
muse::Ret NotationActionController::setPianoKeyboardNumberOfKeys(const muse::rcommand::Params& params)
{
if (!params.contains("keys")) {
return muse::make_ret(Ret::Code::BadArgs);
}
int numberOfKeys = params.at("keys").toInt();
sceneConfiguration()->setPianoKeyboardNumberOfKeys(numberOfKeys);
return muse::make_ok();
}