From 381527115df9764aae00fd41bd3925cca1a2e356 Mon Sep 17 00:00:00 2001 From: Ash-86 <108089527+Ash-86@users.noreply.github.com> Date: Wed, 19 Nov 2025 16:35:10 -0300 Subject: [PATCH 1/7] allow T on list sel --- src/engraving/editing/edit.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/engraving/editing/edit.cpp b/src/engraving/editing/edit.cpp index 0814e7b306922..2c1c1430708ec 100644 --- a/src/engraving/editing/edit.cpp +++ b/src/engraving/editing/edit.cpp @@ -2062,7 +2062,7 @@ void Score::cmdAddTie(bool addToChord) } } - if (noteEntryMode()) { + if (noteEntryMode() || selection().isList()) { ChordRest* cr = nullptr; Chord* c = note->chord(); int staffMove = c->staffMove(); @@ -2168,7 +2168,9 @@ Tie* Score::cmdToggleTie() bool canAddTies = false; const size_t notes = noteList.size(); std::vector tieNoteList(notes); - const bool shouldTieListSelection = notes >= 2; + + bool sameChord = std::all_of(noteList.begin(), noteList.end(), [&](const Note* n) { return n->chord() == noteList[0]->chord(); }); + const bool shouldTieListSelection = !sameChord; for (size_t i = 0; i < notes; ++i) { Note* n = noteList[i]; @@ -2180,6 +2182,16 @@ Tie* Score::cmdToggleTie() if (tieNote) { canAddTies = true; } + if (!tieNote && selection().isList() && sameChord) { + cmdAddTie(); + if (notes >= 2) { + Chord* c = n->chord()->next(); + for (Note* cn : c->notes()) { + score()->select(cn, SelectType::ADD); + } + } + return nullptr; + } } } From b198efe51d7146e3d4baca13c9b9093f647bc098 Mon Sep 17 00:00:00 2001 From: Ash-86 <108089527+Ash-86@users.noreply.github.com> Date: Wed, 19 Nov 2025 16:44:56 -0300 Subject: [PATCH 2/7] move block outside loop --- src/engraving/editing/edit.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/engraving/editing/edit.cpp b/src/engraving/editing/edit.cpp index 2c1c1430708ec..66bb477d56d9e 100644 --- a/src/engraving/editing/edit.cpp +++ b/src/engraving/editing/edit.cpp @@ -2168,31 +2168,34 @@ Tie* Score::cmdToggleTie() bool canAddTies = false; const size_t notes = noteList.size(); std::vector tieNoteList(notes); + Note* tieNote = nullptr; + Note* n = nullptr; bool sameChord = std::all_of(noteList.begin(), noteList.end(), [&](const Note* n) { return n->chord() == noteList[0]->chord(); }); const bool shouldTieListSelection = !sameChord; for (size_t i = 0; i < notes; ++i) { - Note* n = noteList[i]; + n = noteList[i]; if (n->tieFor()) { tieNoteList[i] = nullptr; } else { - Note* tieNote = searchTieNote(n); + tieNote = searchTieNote(n); tieNoteList[i] = tieNote; if (tieNote) { canAddTies = true; } - if (!tieNote && selection().isList() && sameChord) { - cmdAddTie(); - if (notes >= 2) { - Chord* c = n->chord()->next(); - for (Note* cn : c->notes()) { - score()->select(cn, SelectType::ADD); - } - } - return nullptr; + } + } + + if (!tieNote && selection().isList() && sameChord) { + cmdAddTie(); + if (notes >= 2) { + Chord* c = n->chord()->next(); + for (Note* cn : c->notes()) { + score()->select(cn, SelectType::ADD); } } + return nullptr; } const TranslatableString actionName = canAddTies From eb42366caedf8d83b18b6ba4044df92e92195891 Mon Sep 17 00:00:00 2001 From: Ash-86 <108089527+Ash-86@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:16:22 -0300 Subject: [PATCH 3/7] requested changes --- src/engraving/editing/edit.cpp | 43 ++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/engraving/editing/edit.cpp b/src/engraving/editing/edit.cpp index 66bb477d56d9e..210758a4bd22a 100644 --- a/src/engraving/editing/edit.cpp +++ b/src/engraving/editing/edit.cpp @@ -2165,32 +2165,35 @@ Tie* Score::cmdToggleTie() return nullptr; } - bool canAddTies = false; - const size_t notes = noteList.size(); - std::vector tieNoteList(notes); - Note* tieNote = nullptr; - Note* n = nullptr; + std::vector tieNoteList(noteList.size()); + Chord* chord = noteList.front()->chord(); + bool someHaveExistingNextNoteToTieTo = false; + bool allHaveExistingNextNoteToTieTo = true; - bool sameChord = std::all_of(noteList.begin(), noteList.end(), [&](const Note* n) { return n->chord() == noteList[0]->chord(); }); - const bool shouldTieListSelection = !sameChord; - - for (size_t i = 0; i < notes; ++i) { - n = noteList[i]; + for (size_t i = 0; i < noteList.size(); ++i) { + Note* n = noteList[i]; + if (chord && n->chord() != chord) { + chord = nullptr; + } if (n->tieFor()) { tieNoteList[i] = nullptr; } else { - tieNote = searchTieNote(n); + Note* tieNote = searchTieNote(n); tieNoteList[i] = tieNote; if (tieNote) { - canAddTies = true; + someHaveExistingNextNoteToTieTo = true; + } else { + allHaveExistingNextNoteToTieTo = false; } } } - if (!tieNote && selection().isList() && sameChord) { + const bool shouldTieListSelection = noteList.size() >= 2 && !chord; + + if (chord /* i.e. all notes are in the same chord */ && !allHaveExistingNextNoteToTieTo) { cmdAddTie(); - if (notes >= 2) { - Chord* c = n->chord()->next(); + if (noteList.size() >= 2) { + Chord* c = chord->next(); for (Note* cn : c->notes()) { score()->select(cn, SelectType::ADD); } @@ -2198,7 +2201,7 @@ Tie* Score::cmdToggleTie() return nullptr; } - const TranslatableString actionName = canAddTies + const TranslatableString actionName = someHaveExistingNextNoteToTieTo ? TranslatableString("undoableAction", "Add tie") : TranslatableString("undoableAction", "Remove tie"); @@ -2206,7 +2209,7 @@ Tie* Score::cmdToggleTie() Tie* tie = nullptr; - for (size_t i = 0; i < notes; ++i) { + for (size_t i = 0; i < noteList.size(); ++i) { Note* note = noteList[i]; Note* tieToNote = tieNoteList[i]; @@ -2215,7 +2218,7 @@ Tie* Score::cmdToggleTie() } // Tie to adjacent unselected note - if (canAddTies && tieToNote) { + if (someHaveExistingNextNoteToTieTo && tieToNote) { Note* startNote = note->tick() <= tieToNote->tick() ? note : tieToNote; Note* endNote = startNote == tieToNote ? note : tieToNote; tie = createAndAddTie(startNote, endNote); @@ -2239,14 +2242,14 @@ Tie* Score::cmdToggleTie() continue; } - if (!shouldTieListSelection || i > notes - 2) { + if (!shouldTieListSelection || i > noteList.size() - 2) { continue; } // Tie to next appropriate note in selection Note* note2 = nullptr; - for (size_t j = i + 1; j < notes; ++j) { + for (size_t j = i + 1; j < noteList.size(); ++j) { Note* candidateNote = noteList[j]; if (!candidateNote) { continue; From fe8d2aa6b99f3d51b1bc7b66061593ea05145f8f Mon Sep 17 00:00:00 2001 From: Ash-86 <108089527+Ash-86@users.noreply.github.com> Date: Fri, 21 Nov 2025 11:40:13 -0300 Subject: [PATCH 4/7] fix crash on single chord range sel --- src/engraving/editing/edit.cpp | 133 ++++++++++++++++----------------- 1 file changed, 63 insertions(+), 70 deletions(-) diff --git a/src/engraving/editing/edit.cpp b/src/engraving/editing/edit.cpp index 210758a4bd22a..bd3ae8f7617be 100644 --- a/src/engraving/editing/edit.cpp +++ b/src/engraving/editing/edit.cpp @@ -2062,87 +2062,80 @@ void Score::cmdAddTie(bool addToChord) } } - if (noteEntryMode() || selection().isList()) { - ChordRest* cr = nullptr; - Chord* c = note->chord(); - int staffMove = c->staffMove(); - - // set cursor at position after note - if (c->isGraceBefore()) { - // tie grace note before to main note - cr = toChord(c->explicitParent()); - addToChord = true; - } else { - m_is.setSegment(note->chord()->segment()); - m_is.moveToNextInputPos(); - m_is.setLastSegment(m_is.segment()); + ChordRest* cr = nullptr; + Chord* c = note->chord(); + int staffMove = c->staffMove(); + + // set cursor at position after note + if (c->isGraceBefore()) { + // tie grace note before to main note + cr = toChord(c->explicitParent()); + addToChord = true; + } else { + m_is.setSegment(note->chord()->segment()); + m_is.moveToNextInputPos(); + m_is.setLastSegment(m_is.segment()); - if (!m_is.cr()) { - expandVoice(); - } - cr = m_is.cr(); - } - if (!cr) { - break; + if (!m_is.cr()) { + expandVoice(); } + cr = m_is.cr(); + } + if (!cr) { + break; + } - bool addFlag = lastAddedChord != nullptr; + bool addFlag = lastAddedChord != nullptr; - // try to re-use existing note or chord - Note* n = nullptr; - if (addToChord && cr->isChord()) { - Chord* chord = toChord(cr); - Note* nn = chord->findNote(note->pitch()); - if (nn && nn->tpc() == note->tpc()) { - n = nn; // re-use note - } else { - addFlag = true; // re-use chord - } + // try to re-use existing note or chord + Note* n = nullptr; + if (addToChord && cr->isChord()) { + Chord* chord = toChord(cr); + Note* nn = chord->findNote(note->pitch()); + if (nn && nn->tpc() == note->tpc()) { + n = nn; // re-use note + } else { + addFlag = true; // re-use chord } + } - // if no note to re-use, create one - NoteVal nval(note->noteVal()); - if (!n) { - n = addPitch(nval, addFlag); - if (staffMove != 0) { - undo(new ChangeChordStaffMove(n->chord(), staffMove)); - } - } else { - select(n); + // if no note to re-use, create one + NoteVal nval(note->noteVal()); + if (!n) { + n = addPitch(nval, addFlag); + if (staffMove != 0) { + undo(new ChangeChordStaffMove(n->chord(), staffMove)); } + } else { + select(n); + } - if (n) { - if (!lastAddedChord) { - lastAddedChord = n->chord(); - } - // n is not necessarily next note if duration span over measure - Note* nnote = searchTieNote(note); - while (nnote) { - // DEBUG: if duration spans over measure - // this does not set line for intermediate notes - // tpc was set correctly already - //n->setLine(note->line()); - //n->setTpc(note->tpc()); - createAndAddTie(note, nnote); + if (n) { + if (!lastAddedChord) { + lastAddedChord = n->chord(); + } + // n is not necessarily next note if duration span over measure + Note* nnote = searchTieNote(note); + while (nnote) { + // DEBUG: if duration spans over measure + // this does not set line for intermediate notes + // tpc was set correctly already + //n->setLine(note->line()); + //n->setTpc(note->tpc()); + createAndAddTie(note, nnote); - if (!addFlag || nnote->chord()->tick() >= lastAddedChord->tick() || nnote->chord()->isGrace()) { - break; - } else { - note = nnote; - m_is.setLastSegment(m_is.segment()); - nnote = addPitch(nval, true); - } - } - if (staffMove != 0) { - for (Note* tiedNote : n->tiedNotes()) { - undo(new ChangeChordStaffMove(tiedNote->chord(), staffMove)); - } + if (!addFlag || nnote->chord()->tick() >= lastAddedChord->tick() || nnote->chord()->isGrace()) { + break; + } else { + note = nnote; + m_is.setLastSegment(m_is.segment()); + nnote = addPitch(nval, true); } } - } else { - Note* note2 = searchTieNote(note); - if (note2) { - createAndAddTie(note, note2); + if (staffMove != 0) { + for (Note* tiedNote : n->tiedNotes()) { + undo(new ChangeChordStaffMove(tiedNote->chord(), staffMove)); + } } } } From f5272953a62e6c9c41329e48575c6e1bdaef12fc Mon Sep 17 00:00:00 2001 From: Ash-86 <108089527+Ash-86@users.noreply.github.com> Date: Fri, 21 Nov 2025 12:06:35 -0300 Subject: [PATCH 5/7] if the next beat contains something already --- src/engraving/editing/edit.cpp | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/engraving/editing/edit.cpp b/src/engraving/editing/edit.cpp index bd3ae8f7617be..411e29775ca64 100644 --- a/src/engraving/editing/edit.cpp +++ b/src/engraving/editing/edit.cpp @@ -2043,7 +2043,7 @@ static Tie* createAndAddTie(Note* startNote, Note* endNote) void Score::cmdAddTie(bool addToChord) { const std::vector noteList = cmdTieNoteList(selection(), noteEntryMode()); - + std::vector toSelect; if (noteList.empty()) { LOGD("no notes selected"); return; @@ -2138,10 +2138,12 @@ void Score::cmdAddTie(bool addToChord) } } } + toSelect.push_back(n); } if (lastAddedChord) { nextInputPos(lastAddedChord, false); } + score()->select(toSelect, SelectType::ADD); endCmd(); } @@ -2161,7 +2163,6 @@ Tie* Score::cmdToggleTie() std::vector tieNoteList(noteList.size()); Chord* chord = noteList.front()->chord(); bool someHaveExistingNextNoteToTieTo = false; - bool allHaveExistingNextNoteToTieTo = true; for (size_t i = 0; i < noteList.size(); ++i) { Note* n = noteList[i]; @@ -2175,22 +2176,14 @@ Tie* Score::cmdToggleTie() tieNoteList[i] = tieNote; if (tieNote) { someHaveExistingNextNoteToTieTo = true; - } else { - allHaveExistingNextNoteToTieTo = false; } } } const bool shouldTieListSelection = noteList.size() >= 2 && !chord; - if (chord /* i.e. all notes are in the same chord */ && !allHaveExistingNextNoteToTieTo) { - cmdAddTie(); - if (noteList.size() >= 2) { - Chord* c = chord->next(); - for (Note* cn : c->notes()) { - score()->select(cn, SelectType::ADD); - } - } + if (chord) { /* i.e. all notes are in the same chord */ + cmdAddTie(true); return nullptr; } From 0605fba89f9413818436d2b97b35d1f7767244ec Mon Sep 17 00:00:00 2001 From: Ash-86 <108089527+Ash-86@users.noreply.github.com> Date: Fri, 21 Nov 2025 15:39:14 -0300 Subject: [PATCH 6/7] multi staff support --- src/engraving/editing/edit.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/engraving/editing/edit.cpp b/src/engraving/editing/edit.cpp index 411e29775ca64..84281d85df54b 100644 --- a/src/engraving/editing/edit.cpp +++ b/src/engraving/editing/edit.cpp @@ -2044,6 +2044,7 @@ void Score::cmdAddTie(bool addToChord) { const std::vector noteList = cmdTieNoteList(selection(), noteEntryMode()); std::vector toSelect; + if (noteList.empty()) { LOGD("no notes selected"); return; @@ -2072,6 +2073,7 @@ void Score::cmdAddTie(bool addToChord) cr = toChord(c->explicitParent()); addToChord = true; } else { + m_is.setTrack(note->chord()->track()); m_is.setSegment(note->chord()->segment()); m_is.moveToNextInputPos(); m_is.setLastSegment(m_is.segment()); @@ -2097,6 +2099,8 @@ void Score::cmdAddTie(bool addToChord) } else { addFlag = true; // re-use chord } + } else if (!noteEntryMode()) { + addFlag = false; } // if no note to re-use, create one @@ -2161,13 +2165,13 @@ Tie* Score::cmdToggleTie() } std::vector tieNoteList(noteList.size()); - Chord* chord = noteList.front()->chord(); + bool singleTick = true; bool someHaveExistingNextNoteToTieTo = false; - + bool allHaveExistingNextNoteToTieTo = true; for (size_t i = 0; i < noteList.size(); ++i) { Note* n = noteList[i]; - if (chord && n->chord() != chord) { - chord = nullptr; + if (n->chord()->tick() != noteList.front()->tick()) { + singleTick = false; } if (n->tieFor()) { tieNoteList[i] = nullptr; @@ -2176,13 +2180,15 @@ Tie* Score::cmdToggleTie() tieNoteList[i] = tieNote; if (tieNote) { someHaveExistingNextNoteToTieTo = true; + } else { + allHaveExistingNextNoteToTieTo = false; } } } - const bool shouldTieListSelection = noteList.size() >= 2 && !chord; + const bool shouldTieListSelection = noteList.size() >= 2 && !singleTick; - if (chord) { /* i.e. all notes are in the same chord */ + if (singleTick /* i.e. all notes are in the same tick */ && !allHaveExistingNextNoteToTieTo) { cmdAddTie(true); return nullptr; } From 66d88f9c37c22b2ecd695c3a3b8f5a5df76c4eb4 Mon Sep 17 00:00:00 2001 From: Ash-86 <108089527+Ash-86@users.noreply.github.com> Date: Wed, 26 Nov 2025 08:54:27 -0300 Subject: [PATCH 7/7] overwrite notes --- src/engraving/editing/edit.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/engraving/editing/edit.cpp b/src/engraving/editing/edit.cpp index 84281d85df54b..aea25fb62639f 100644 --- a/src/engraving/editing/edit.cpp +++ b/src/engraving/editing/edit.cpp @@ -2042,8 +2042,10 @@ static Tie* createAndAddTie(Note* startNote, Note* endNote) void Score::cmdAddTie(bool addToChord) { - const std::vector noteList = cmdTieNoteList(selection(), noteEntryMode()); + std::vector noteList = cmdTieNoteList(selection(), noteEntryMode()); std::vector toSelect; + std::sort(noteList.begin(), noteList.end(), [](const Note* a, const Note* b) { return a->track() < b->track(); }); + track_idx_t track = noteList[0]->chord()->track(); if (noteList.empty()) { LOGD("no notes selected"); @@ -2088,7 +2090,10 @@ void Score::cmdAddTie(bool addToChord) } bool addFlag = lastAddedChord != nullptr; - + if (c->track() != track) { + addFlag = false; + track = c->track(); + } // try to re-use existing note or chord Note* n = nullptr; if (addToChord && cr->isChord()) { @@ -2099,8 +2104,6 @@ void Score::cmdAddTie(bool addToChord) } else { addFlag = true; // re-use chord } - } else if (!noteEntryMode()) { - addFlag = false; } // if no note to re-use, create one @@ -2189,7 +2192,7 @@ Tie* Score::cmdToggleTie() const bool shouldTieListSelection = noteList.size() >= 2 && !singleTick; if (singleTick /* i.e. all notes are in the same tick */ && !allHaveExistingNextNoteToTieTo) { - cmdAddTie(true); + cmdAddTie(); return nullptr; }