diff --git a/src/engraving/dom/chordrest.cpp b/src/engraving/dom/chordrest.cpp index a55e6a08d127e..c635ef97b405f 100644 --- a/src/engraving/dom/chordrest.cpp +++ b/src/engraving/dom/chordrest.cpp @@ -41,6 +41,7 @@ #include "keysig.h" #include "lyrics.h" #include "marker.h" +#include "mcursor.h" #include "measure.h" #include "navigate.h" #include "note.h" @@ -1421,4 +1422,65 @@ bool ChordRest::hasPrecedingJumpItem() const return false; } + +//---------------------------------------------------------------- +// getNotesAtPosition - Get all notes corresponding +// to the vertical segment of a ChordRest input. Only one +// instrument - the ChordRest's instrument - is taken into +// account if onlyOne = true, else the entire score - +// currently not implemented for pianoview +//---------------------------------------------------------------- + +void ChordRest::getNotesAtPosition(std::vector& notesAtPosition, bool onlyOne) +{ + auto part = staff()->part(); + auto firstTrackOfPart = onlyOne ? part->startTrack() : 0; + auto lastTrackOfPart = onlyOne ? part->endTrack() : score()->ntracks(); + auto currentPosition = tick(); + + MCursor c; + c.setScore(score()->masterScore()); + if (!notesAtPosition.empty()) { + notesAtPosition.clear(); + } + for (track_idx_t track = firstTrackOfPart; track < lastTrackOfPart; track++) { + c.move(static_cast(track), currentPosition); + if (auto e = c.currentElement()) { + if (e->isChord()) { + auto notes = toChord(e)->notes(); + for (auto note : notes) { + notesAtPosition.emplace_back(note); + } + } + } + } +} + +//---------------------------------------------------------------- +// getChordRestsAtPosition - same as getNotesAtPosition except +// now stores ChordRests to include rests for the purposes +// of vertical movement of selection of the same tick +//---------------------------------------------------------------- + +void ChordRest::getChordRestsAtPosition(std::vector& chordRestsAtPosition, bool onlyOne) +{ + auto part = staff()->part(); + auto firstTrackOfPart = onlyOne ? part->startTrack() : 0; + auto lastTrackOfPart = onlyOne ? part->endTrack() : score()->ntracks(); + auto currentPosition = tick(); + + MCursor c; + c.setScore(score()->masterScore()); + if (!chordRestsAtPosition.empty()) { + chordRestsAtPosition.clear(); + } + for (track_idx_t track = firstTrackOfPart; track < lastTrackOfPart; track++) { + c.move(static_cast(track), currentPosition); + if (auto e = c.currentElement()) { + if (e->isChord() || e->isRest()) { + chordRestsAtPosition.emplace_back(toChordRest(e)); + } + } + } +} } diff --git a/src/engraving/dom/chordrest.h b/src/engraving/dom/chordrest.h index 6cb32ad7f9cd4..8cec06a158f50 100644 --- a/src/engraving/dom/chordrest.h +++ b/src/engraving/dom/chordrest.h @@ -144,6 +144,9 @@ class ChordRest : public DurationElement void removeDeleteBeam(bool beamed); void replaceBeam(Beam* newBeam); + void getNotesAtPosition(std::vector&, bool onlyOne = true); + void getChordRestsAtPosition(std::vector& chordRestsAtPosition, bool onlyOne = true); + const ElementList& el() const { return m_el; } //! TODO Look like a hack, see using diff --git a/src/engraving/dom/cmd.cpp b/src/engraving/dom/cmd.cpp index 37f4c05554a6e..b1a7491e9a2a1 100644 --- a/src/engraving/dom/cmd.cpp +++ b/src/engraving/dom/cmd.cpp @@ -2871,8 +2871,8 @@ EngravingItem* Score::move(const String& cmd) // to catch up with the cursor and not move the selection by 2 positions cr = selection().cr(); if (cr && (cr->isGrace() || cmd == u"next-chord" || cmd == u"prev-chord")) { - } else { - cr = inputState().cr(); + } else if (auto is = inputState().cr()) { + cr = is; } } else if (selection().activeCR()) { cr = selection().activeCR(); @@ -3023,8 +3023,11 @@ EngravingItem* Score::move(const String& cmd) // selection "cursor" // find previous chordrest, which might be a grace note // this may override note input cursor - el = prevChordRest(cr); - + if (auto pcr = prevChordRest(cr)) { + if (prevChordRest(cr)->isGrace()) { + el = pcr; + } + } // Skip gap rests if we're not in note entry mode... while (!noteEntryMode() && el && el->isRest() && toRest(el)->isGap()) { el = prevChordRest(toChordRest(el)); diff --git a/src/engraving/dom/mcursor.cpp b/src/engraving/dom/mcursor.cpp index 6d07c55909cec..c502bfe36b2e8 100644 --- a/src/engraving/dom/mcursor.cpp +++ b/src/engraving/dom/mcursor.cpp @@ -182,4 +182,17 @@ void MCursor::addPart(const String& instrument) m_score->appendPart(part); m_score->insertStaff(staff, 0); } + +//--------------------------------------------------------- +// currentElement +// returns the element @ cursor position if +// a valid track & tick were set +//--------------------------------------------------------- + +EngravingItem* MCursor::currentElement() const +{ + auto measure = m_score->tick2measure(m_tick); + auto seg = measure->getSegment(SegmentType::ChordRest, m_tick); + return seg && seg->element(m_track) ? seg->element(m_track) : nullptr; +} } diff --git a/src/engraving/dom/mcursor.h b/src/engraving/dom/mcursor.h index 2dd0886ecf0a7..97f9244f23efa 100644 --- a/src/engraving/dom/mcursor.h +++ b/src/engraving/dom/mcursor.h @@ -23,6 +23,7 @@ #ifndef MU_ENGRAVING_MCURSOR_H #define MU_ENGRAVING_MCURSOR_H +#include "engravingitem.h" #include "types/string.h" #include "../types/fraction.h" @@ -55,6 +56,8 @@ class MCursor void setScore(MasterScore* s) { m_score = s; } void setTimeSig(Fraction f) { m_sig = f; } + EngravingItem* currentElement() const; + private: void createMeasures(); diff --git a/src/engraving/dom/navigate.cpp b/src/engraving/dom/navigate.cpp index 2f91a9ceb053e..92b0789e0a67a 100644 --- a/src/engraving/dom/navigate.cpp +++ b/src/engraving/dom/navigate.cpp @@ -35,6 +35,7 @@ #include "staff.h" #include "soundflag.h" #include "guitarbend.h" +#include "notation/notationtypes.h" using namespace mu; @@ -380,6 +381,87 @@ Note* Score::downAltCtrl(Note* note) const return note->chord()->downNote(); } +//--------------------------------------------------------- +// moveAlt - Updated upAlt/downAlt to let tick (beat) take +// precedence - facilitate up/down traveling in a +// vertical time domain +// +// element: Note() or Rest() +// return: Note() or Rest() +// +// return next higher/lower pitched note in chord +// or top/bottom of next/previous track's chord if at wit's end +//--------------------------------------------------------- + +EngravingItem* Score::moveAlt(EngravingItem* element, notation::MoveDirection direction) +{ + EngravingItem* result = nullptr; + ChordRest* cr = nullptr; + auto originalTrack = element->track(); + bool moveUp = (direction == notation::MoveDirection::Up); + bool isNote = element->isNote(); + bool isRest = element->isRest(); + + if (isNote) { + cr = toChordRest(element->parent()); + auto note = toNote(element); + auto chord = note->chord(); + const std::vector& notes = chord->notes(); + auto it = std::find(notes.begin(), notes.end(), note); + // Traverse notes within same ChordRest until at extremum + auto condition = moveUp ? notes.end() : notes.begin(); + if (moveUp) { + ++it; + } + if (it != condition) { + if (!moveUp) { + --it; + } + result = *it; + } + } else if (isRest) { + cr = toChordRest(element); + } + + if (!result) { + // Traverse same-beat tracks + std::vector chordRestsOfBeat; + if (!cr) { + return nullptr; + } + + cr->getChordRestsAtPosition(chordRestsOfBeat, false); + if (moveUp) { + std::reverse(chordRestsOfBeat.begin(), chordRestsOfBeat.end()); + } + + for (auto it : chordRestsOfBeat) { + auto targetCR = it; + auto targetTrack = targetCR->track(); + if (moveUp && (targetTrack >= originalTrack)) { + continue; + } else if (!moveUp && (targetTrack <= originalTrack)) { + continue; + } + if (targetCR) { + result = targetCR; + } + break; + } + + if (result && (result->track() == originalTrack)) { + result = element; + } + } + + if (result && result->isChord()) { + auto chord = toChord(result); + result = moveUp ? chord->downNote() : chord->upNote(); + } + + return result; +} + //--------------------------------------------------------- // firstElement //--------------------------------------------------------- diff --git a/src/engraving/dom/score.h b/src/engraving/dom/score.h index a996e3e3cd1c7..e3d94f95c8053 100644 --- a/src/engraving/dom/score.h +++ b/src/engraving/dom/score.h @@ -90,6 +90,10 @@ namespace mu::engraving::compat { class WriteScoreHook; } +namespace mu::notation { +enum class MoveDirection; +} + namespace mu::engraving { class Articulation; class Audio; @@ -941,6 +945,7 @@ class Score : public EngravingObject, public muse::Injectable Note* upAltCtrl(Note*) const; EngravingItem* downAlt(EngravingItem*); Note* downAltCtrl(Note*) const; + EngravingItem* moveAlt(EngravingItem*, notation::MoveDirection); EngravingItem* firstElement(bool frame = true); EngravingItem* lastElement(bool frame = true); diff --git a/src/instrumentsscene/internal/selectinstrumentscenario.cpp b/src/instrumentsscene/internal/selectinstrumentscenario.cpp index 57ffcd5d7c76e..41ca84d3d5b66 100644 --- a/src/instrumentsscene/internal/selectinstrumentscenario.cpp +++ b/src/instrumentsscene/internal/selectinstrumentscenario.cpp @@ -48,7 +48,7 @@ RetVal SelectInstrumentsScenario::selectInstrument(const Ins return selectedInstruments.ret; } - const InstrumentTemplate& templ = selectedInstruments.val.instruments.first().instrumentTemplate; + const InstrumentTemplate& templ = selectedInstruments.val.instruments.front().instrumentTemplate; return RetVal::make_ok(templ); } @@ -81,7 +81,7 @@ RetVal SelectInstrumentsScenario::selectInstrument String instrumentId = String::fromStdString(map["instrumentId"].toString()); pi.instrumentTemplate = instrumentsRepository()->instrumentTemplate(instrumentId); - result.instruments << pi; + result.instruments.push_back(pi); } ValMap order = content["scoreOrder"].toMap(); diff --git a/src/instrumentsscene/view/staffsettingsmodel.cpp b/src/instrumentsscene/view/staffsettingsmodel.cpp index 38289fc64b9d4..802ac8fd11602 100644 --- a/src/instrumentsscene/view/staffsettingsmodel.cpp +++ b/src/instrumentsscene/view/staffsettingsmodel.cpp @@ -110,7 +110,7 @@ QVariantList StaffSettingsModel::allStaffTypes() const if (isTypeAllowed(type)) { QVariantMap obj; - obj["text"] = staffTypeToString(type.type()); + obj["text"] = staffTypeToString(type.type()).toQString(); obj["value"] = static_cast(type.type()); result << obj; diff --git a/src/notation/internal/masternotationparts.cpp b/src/notation/internal/masternotationparts.cpp index ae106278546c9..be5f75bf6617e 100644 --- a/src/notation/internal/masternotationparts.cpp +++ b/src/notation/internal/masternotationparts.cpp @@ -86,7 +86,7 @@ void MasterNotationParts::setParts(const PartInstrumentList& partList, const Sco PartInstrument pi; pi.isExistingPart = true; pi.partId = part->id(); - excerptPartList << pi; + excerptPartList.push_back(pi); } impl->sortParts(excerptPartList); diff --git a/src/notation/internal/notationinteraction.cpp b/src/notation/internal/notationinteraction.cpp index 19a2c5687fba1..624030c4bddc0 100644 --- a/src/notation/internal/notationinteraction.cpp +++ b/src/notation/internal/notationinteraction.cpp @@ -714,24 +714,77 @@ void NotationInteraction::moveChordNoteSelection(MoveDirection d) return; } - EngravingItem* current = selection()->element(); - if (!current || !(current->isNote() || current->isRest())) { - return; - } + auto removeDuplicates = [](std::vector& elements) { + std::sort(elements.begin(), elements.end()); + auto it = std::unique(elements.begin(), elements.end()); + elements.erase(it, elements.end()); + }; - EngravingItem* chordElem; - if (d == MoveDirection::Up) { - chordElem = score()->upAlt(current); - } else { - chordElem = score()->downAlt(current); + auto& _score = *score(); + auto& selection = _score.selection(); + EngravingItem* currentSingle = selection.element(); + EngravingItem* oldSingle = currentSingle; + bool isRange = selection.isRange(); + std::vector notes; + + // Single traverse: + if (currentSingle && (currentSingle->isNote() || currentSingle->isRest())) { + EngravingItem* newSingle = _score.moveAlt(currentSingle, d); + if (newSingle == currentSingle) { + return; + } + while (newSingle && newSingle->isRest() && toRest(newSingle)->isGap()) { + newSingle = _score.moveAlt(newSingle, d); + if (newSingle == oldSingle) { + break; + } + } + if (newSingle) { + select({ newSingle }, SelectType::SINGLE, newSingle->staffIdx()); + showItem(newSingle); + } + return; + } + // Range/List traverse: + else { + for (auto item : selection.elements()) { + if (item->isNote()) { + auto selectedNote = toNote(item); + if (isRange) { + auto newSelection = (d == MoveDirection::Down) + ? selectedNote->chord()->downNote() + : selectedNote->chord()->upNote(); + notes.emplace_back(newSelection); + } else { // List + auto newSelection = _score.moveAlt(selectedNote, d); + bool keepSelection = !newSelection; + if (newSelection) { + if (newSelection->isNote()) { + auto newNote = toNote(newSelection); + bool sameChord = (newNote->chord() == selectedNote->chord()); + if (!sameChord) { + keepSelection = true; + } + } else if (newSelection->isRest()) { + keepSelection = true; + } + } + notes.emplace_back(keepSelection ? selectedNote : newSelection); + } + } + removeDuplicates(notes); + } } - if (chordElem == current) { + if (!notes.empty()) { + selection.clear(); + for (auto& note : notes) { + select({ note }, SelectType::ADD, note->staffIdx()); + showItem(note); + } + // score.update(); return; } - - select({ chordElem }, SelectType::SINGLE, chordElem->staffIdx()); - showItem(chordElem); } void NotationInteraction::moveSegmentSelection(MoveDirection d) diff --git a/src/notation/internal/notationparts.cpp b/src/notation/internal/notationparts.cpp index 022212fd9e088..df82afe5efe77 100644 --- a/src/notation/internal/notationparts.cpp +++ b/src/notation/internal/notationparts.cpp @@ -707,7 +707,7 @@ void NotationParts::removeParts(const IDList& partsIds) PartInstrument pi; pi.isExistingPart = true; pi.partId = part->id(); - parts << pi; + parts.push_back(pi); } sortParts(parts); @@ -873,7 +873,7 @@ void NotationParts::moveParts(const IDList& sourcePartsIds, const ID& destinatio PartInstrument pi; pi.isExistingPart = true; pi.partId = partId; - parts << pi; + parts.push_back(pi); } endInteractionWithScore(); diff --git a/src/notation/internal/searchcommandsparser.cpp b/src/notation/internal/searchcommandsparser.cpp index 32fd002ea2641..fafdbedf3b4f3 100644 --- a/src/notation/internal/searchcommandsparser.cpp +++ b/src/notation/internal/searchcommandsparser.cpp @@ -37,8 +37,8 @@ static const std::string PAGE_CODE("p"); SearchCommands SearchCommandsParser::availableCommands() { SearchCommands commands; - commands << SearchCommand(ElementType::REHEARSAL_MARK, REHEARSAL_MARK_CODE, muse::trc("notation", "Rehearsal marks")) - << SearchCommand(ElementType::PAGE, PAGE_CODE, muse::trc("notation", "Pages")); + commands.push_back(SearchCommand(ElementType::REHEARSAL_MARK, REHEARSAL_MARK_CODE, muse::trc("notation", "Rehearsal marks"))); + commands.push_back(SearchCommand(ElementType::PAGE, PAGE_CODE, muse::trc("notation", "Pages"))); return commands; } diff --git a/src/notation/notationtypes.h b/src/notation/notationtypes.h index de115d954a30d..d9b2c12f4e833 100644 --- a/src/notation/notationtypes.h +++ b/src/notation/notationtypes.h @@ -22,8 +22,6 @@ #ifndef MU_NOTATION_NOTATIONTYPES_H #define MU_NOTATION_NOTATIONTYPES_H -#include -#include #include #include "translation.h" @@ -150,7 +148,7 @@ using engraving::LoopBoundaryType; using Pid = mu::engraving::Pid; using VoiceAssignment = mu::engraving::VoiceAssignment; -static const muse::String COMMON_GENRE_ID("common"); +static const muse::String COMMON_GENRE_ID(u"common"); enum class DragMode { @@ -305,57 +303,57 @@ inline bool isMainInstrumentForPart(const InstrumentKey& instrumentKey, const Pa return instrumentKey.instrumentId == part->instrumentId() && instrumentKey.tick == Part::MAIN_INSTRUMENT_TICK; } -inline QString formatInstrumentTitle(const QString& instrumentName, const InstrumentTrait& trait) +inline muse::String formatInstrumentTitle(const muse::String& instrumentName, const InstrumentTrait& trait) { // Comments for translators start with //: switch (trait.type) { case TraitType::Tuning: //: %1=tuning ("D"), %2=name ("Tin Whistle"). Example: "D Tin Whistle" - return muse::qtrc("notation", "%1 %2", "Tuned instrument displayed in the UI") + return muse::mtrc("notation", "%1 %2", "Tuned instrument displayed in the UI") .arg(trait.name, instrumentName); case TraitType::Transposition: //: %1=name ("Horn"), %2=transposition ("C alto"). Example: "Horn in C alto" - return muse::qtrc("notation", "%1 in %2", "Transposing instrument displayed in the UI") + return muse::mtrc("notation", "%1 in %2", "Transposing instrument displayed in the UI") .arg(instrumentName, trait.name); case TraitType::Course: //: %1=name ("Tenor Lute"), %2=course/strings ("7-course"). Example: "Tenor Lute (7-course)" - return muse::qtrc("notation", "%1 (%2)", "String instrument displayed in the UI") + return muse::mtrc("notation", "%1 (%2)", "String instrument displayed in the UI") .arg(instrumentName, trait.name); case TraitType::Unknown: + default: return instrumentName; // Example: "Flute" } - Q_UNREACHABLE(); } -inline QString formatInstrumentTitle(const QString& instrumentName, const InstrumentTrait& trait, int instrumentNumber) +inline muse::String formatInstrumentTitle(const muse::String& instrumentName, const InstrumentTrait& trait, int instrumentNumber) { if (instrumentNumber == 0) { // Only one instance of this instrument in the score return formatInstrumentTitle(instrumentName, trait); } - QString number = QString::number(instrumentNumber); + muse::String number = muse::String::number(instrumentNumber); // Comments for translators start with //: switch (trait.type) { case TraitType::Tuning: //: %1=tuning ("D"), %2=name ("Tin Whistle"), %3=number ("2"). Example: "D Tin Whistle 2" - return muse::qtrc("notation", "%1 %2 %3", "One of several tuned instruments displayed in the UI") + return muse::mtrc("notation", "%1 %2 %3", "One of several tuned instruments displayed in the UI") .arg(trait.name, instrumentName, number); case TraitType::Transposition: //: %1=name ("Horn"), %2=transposition ("C alto"), %3=number ("2"). Example: "Horn in C alto 2" - return muse::qtrc("notation", "%1 in %2 %3", "One of several transposing instruments displayed in the UI") + return muse::mtrc("notation", "%1 in %2 %3", "One of several transposing instruments displayed in the UI") .arg(instrumentName, trait.name, number); case TraitType::Course: //: %1=name ("Tenor Lute"), %2=course/strings ("7-course"), %3=number ("2"). Example: "Tenor Lute (7-course) 2" - return muse::qtrc("notation", "%1 (%2) %3", "One of several string instruments displayed in the UI") + return muse::mtrc("notation", "%1 (%2) %3", "One of several string instruments displayed in the UI") .arg(instrumentName, trait.name, number); case TraitType::Unknown: + default: //: %1=name ("Flute"), %2=number ("2"). Example: "Flute 2" - return muse::qtrc("notation", "%1 %2", "One of several instruments displayed in the UI") + return muse::mtrc("notation", "%1 %2", "One of several instruments displayed in the UI") .arg(instrumentName, number); } - Q_UNREACHABLE(); } struct PartInstrument @@ -367,7 +365,7 @@ struct PartInstrument bool isSoloist = false; }; -using PartInstrumentList = QList; +using PartInstrumentList = std::list; struct PartInstrumentListScoreOrder { @@ -384,7 +382,7 @@ struct SearchCommand SearchCommand(const ElementType& searchElementType, const std::string& code, const std::string& description) : searchElementType(searchElementType), code(code), description(description) {} }; -using SearchCommands = QList; +using SearchCommands = std::list; struct FilterElementsOptions { @@ -421,7 +419,7 @@ struct FilterNotesOptions : FilterElementsOptions struct StaffConfig { bool visible = false; - qreal userDistance = 0.0; + double userDistance = 0.0; bool cutaway = false; bool showIfEmpty = false; bool hideSystemBarline = false; @@ -532,10 +530,10 @@ struct ScoreConfig } }; -inline QString staffTypeToString(StaffTypeId type) +inline muse::String staffTypeToString(StaffTypeId type) { const StaffType* preset = StaffType::preset(type); - return preset ? preset->name().toQString() : QString(); + return preset ? preset->name() : muse::String(); } struct MeasureBeat @@ -576,7 +574,7 @@ struct ScoreCreateOptions inline const ScoreOrder& customOrder() { static ScoreOrder order; - order.id = "custom"; + order.id = u"custom"; order.name = muse::TranslatableString("engraving/scoreorder", "Custom"); return order; diff --git a/src/project/view/newscoremodel.cpp b/src/project/view/newscoremodel.cpp index 4e4d16674c516..6e653e426da74 100644 --- a/src/project/view/newscoremodel.cpp +++ b/src/project/view/newscoremodel.cpp @@ -112,7 +112,7 @@ ProjectCreateOptions NewScoreModel::parseOptions(const QVariantMap& info) const pi.isExistingPart = objMap["isExistingPart"].toBool(); pi.isSoloist = objMap["isSoloist"].toBool(); - scoreOptions.parts << pi; + scoreOptions.parts.push_back(pi); } QVariantMap orderMap = info["scoreOrder"].toMap();