diff --git a/src/engraving/api/v1/cursor.cpp b/src/engraving/api/v1/cursor.cpp index 501e7fead85aa..e7d0738572ce2 100644 --- a/src/engraving/api/v1/cursor.cpp +++ b/src/engraving/api/v1/cursor.cpp @@ -363,7 +363,8 @@ void Cursor::add(EngravingItem* wrapped) s->setParent(curElement); m_score->undoAddElement(s); } - } // FALLTHROUGH + [[fallthrough]]; + } case ElementType::FINGERING: case ElementType::BEND: case ElementType::NOTEHEAD: { diff --git a/src/engraving/data/harmony_to_diagram.xml b/src/engraving/data/harmony_to_diagram.xml index b44eaaa3d3036..65e7a2745a4a2 100644 --- a/src/engraving/data/harmony_to_diagram.xml +++ b/src/engraving/data/harmony_to_diagram.xml @@ -33782,14 +33782,14 @@ circle - circle + normal circle - O[2-O][2-O]OOO + O[2-O][2-O]O[2-O]O c#m7b5/e diff --git a/src/engraving/dom/anchors.cpp b/src/engraving/dom/anchors.cpp index 3886ec099ac24..8b72e731ee27f 100644 --- a/src/engraving/dom/anchors.cpp +++ b/src/engraving/dom/anchors.cpp @@ -158,18 +158,17 @@ void MoveElementAnchors::moveElementAnchors(EngravingItem* element, KeyboardKey return; } - bool leftRightKey = key == Key_Left || key == Key_Right; bool altMod = mod & AltModifier; - bool shiftMod = mod & ShiftModifier; - bool changeAnchorType = shiftMod && altMod && leftRightKey && canAnchorToEndOfPrevious(element); bool anchorToEndOfPrevious = element->getProperty(Pid::ANCHOR_TO_END_OF_PREVIOUS).toBool(); - if (changeAnchorType) { + bool changeAnchorType = altMod && canAnchorToEndOfPrevious(element); + bool resetAnchorType = !altMod && anchorToEndOfPrevious; + if (changeAnchorType || resetAnchorType) { element->undoChangeProperty(Pid::ANCHOR_TO_END_OF_PREVIOUS, !anchorToEndOfPrevious, element->propertyFlags(Pid::ANCHOR_TO_END_OF_PREVIOUS)); - bool doesntNeedMoveSeg = ((key == Key_Left && anchorToEndOfPrevious) || (key == Key_Right && !anchorToEndOfPrevious)); - if (doesntNeedMoveSeg) { + bool needMoveSeg = (key == Key_Left && anchorToEndOfPrevious) || (key == Key_Right && !anchorToEndOfPrevious); + if (!needMoveSeg) { checkMeasureBoundariesAndMoveIfNeed(element); return; } @@ -199,7 +198,7 @@ void MoveElementAnchors::checkMeasureBoundariesAndMoveIfNeed(EngravingItem* elem Measure* curMeasure = curSeg->measure(); Measure* prevMeasure = curMeasure->prevMeasure(); bool anchorToEndOfPrevious = element->getProperty(Pid::ANCHOR_TO_END_OF_PREVIOUS).toBool(); - bool needMoveToNext = curTick == curMeasure->endTick() && anchorToEndOfPrevious; + bool needMoveToNext = curTick == curMeasure->endTick() && !anchorToEndOfPrevious; bool needMoveToPrevious = curSeg->rtick().isZero() && anchorToEndOfPrevious && prevMeasure; if (!needMoveToPrevious && !needMoveToNext) { @@ -251,14 +250,6 @@ void MoveElementAnchors::moveElementAnchorsOnDrag(EngravingItem* element, EditDa void MoveElementAnchors::moveSegment(EngravingItem* element, bool forward) { - if (canAnchorToEndOfPrevious(element)) { - bool cachedAnchorToEndOfPrevious = element->getProperty(Pid::ANCHOR_TO_END_OF_PREVIOUS).toBool(); - element->undoResetProperty(Pid::ANCHOR_TO_END_OF_PREVIOUS); - if (cachedAnchorToEndOfPrevious && forward) { - return; - } - } - Segment* curSeg = toSegment(element->parentItem()); Segment* newSeg = getNewSegment(element, curSeg, forward); diff --git a/src/engraving/dom/box.cpp b/src/engraving/dom/box.cpp index fbdea04f6c8ec..5a636f6be4361 100644 --- a/src/engraving/dom/box.cpp +++ b/src/engraving/dom/box.cpp @@ -706,6 +706,9 @@ FBox::FBox(System* parent) resetProperty(Pid::BOTTOM_MARGIN); resetProperty(Pid::TOP_GAP); resetProperty(Pid::BOTTOM_GAP); + resetProperty(Pid::EXCLUDE_FROM_OTHER_PARTS); + resetProperty(Pid::APPEARANCE_LINKED_TO_MASTER); + resetProperty(Pid::POSITION_LINKED_TO_MASTER); } void FBox::init() @@ -722,7 +725,7 @@ void FBox::init() StringList diagramsNamesInScore; std::vector harmonyOrDiagramsInScore; - for (mu::engraving::Segment* segment = masterScore()->firstSegment(mu::engraving::SegmentType::ChordRest); segment; + for (mu::engraving::Segment* segment = score()->firstSegment(mu::engraving::SegmentType::ChordRest); segment; segment = segment->next1(mu::engraving::SegmentType::ChordRest)) { for (EngravingItem* item : segment->annotations()) { if (!item || !item->part()) { @@ -749,6 +752,8 @@ void FBox::init() } } + m_diagramsOrderInScore = diagramsNamesInScore; + for (size_t i = 0; i < oldDiagramsNames.size(); ++i) { String oldName = oldDiagramsNames[i]; if (!muse::contains(diagramsNamesInScore, oldName)) { @@ -766,19 +771,6 @@ void FBox::init() score()->undo(new AddFretDiagramToFretBox(newDiagram, idx)); } } - - StringList currentDiagrams; - for (EngravingItem* item : el()) { - currentDiagrams.push_back(toFretDiagram(item)->harmonyText().toLower()); - } - - if (!m_invisibleDiagrams.empty()) { - updateInvisibleDiagrams(currentDiagrams); - } - - if (!m_diagramsOrder.empty()) { - updateDiagramsOrder(currentDiagrams); - } } void FBox::add(EngravingItem* e) @@ -848,9 +840,7 @@ PropertyValue FBox::getProperty(Pid propertyId) const case Pid::RIGHT_MARGIN: return m_contentAlignmentH == AlignH::RIGHT ? VBox::getProperty(propertyId) : PropertyValue(); case Pid::FRET_FRAME_DIAGRAMS_ORDER: - return !m_diagramsOrder.empty() ? m_diagramsOrder.join(FRET_BOX_DIAGRAMS_SEPARATOR) : PropertyValue(); - case Pid::FRET_FRAME_INVISIBLE_DIAGRAMS: - return !m_invisibleDiagrams.empty() ? m_invisibleDiagrams.join(FRET_BOX_DIAGRAMS_SEPARATOR) : PropertyValue(); + return diagramsOrder().join(FRET_BOX_DIAGRAMS_SEPARATOR); default: return VBox::getProperty(propertyId); } @@ -880,10 +870,7 @@ bool FBox::setProperty(Pid propertyId, const PropertyValue& val) resetProperty(Pid::RIGHT_MARGIN); break; case Pid::FRET_FRAME_DIAGRAMS_ORDER: - m_diagramsOrder = val.value().split(FRET_BOX_DIAGRAMS_SEPARATOR); - break; - case Pid::FRET_FRAME_INVISIBLE_DIAGRAMS: - m_invisibleDiagrams = val.value().split(FRET_BOX_DIAGRAMS_SEPARATOR); + reorderElements(val.value().split(FRET_BOX_DIAGRAMS_SEPARATOR)); break; default: return VBox::setProperty(propertyId, val); @@ -907,9 +894,12 @@ PropertyValue FBox::propertyDefault(Pid propertyId) const case Pid::FRET_FRAME_H_ALIGN: return static_cast(AlignH::HCENTER); case Pid::FRET_FRAME_DIAGRAMS_ORDER: - return PropertyValue(); - case Pid::FRET_FRAME_INVISIBLE_DIAGRAMS: - return PropertyValue(); + return m_diagramsOrderInScore.join(FRET_BOX_DIAGRAMS_SEPARATOR); + case Pid::EXCLUDE_FROM_OTHER_PARTS: + return true; + case Pid::APPEARANCE_LINKED_TO_MASTER: + case Pid::POSITION_LINKED_TO_MASTER: + return false; default: return VBox::propertyDefault(propertyId); } @@ -946,81 +936,25 @@ void FBox::undoReorderElements(const StringList& newOrder) triggerLayout(); } -ElementList FBox::orderedElements(bool includeInvisible) const +void FBox::reorderElements(const StringList& newOrder) { - ElementList elements = el(); - const StringList& diagramsOrder = this->diagramsOrder(); - if (diagramsOrder.empty()) { - return elements; - } - - std::sort(elements.begin(), elements.end(), [&diagramsOrder](const EngravingItem* a, const EngravingItem* b) { - const FretDiagram* diagramA = toFretDiagram(a); - const String diagramAHarmonyName = diagramA->harmonyText().toLower(); - - const FretDiagram* diagramB = toFretDiagram(b); - const String diagramBHarmonyName = diagramB->harmonyText().toLower(); - - auto itA = std::find(diagramsOrder.begin(), diagramsOrder.end(), diagramAHarmonyName); - auto itB = std::find(diagramsOrder.begin(), diagramsOrder.end(), diagramBHarmonyName); - - return itA < itB; + std::sort(m_el.begin(), m_el.end(), [&](EngravingItem* a, EngravingItem* b) { + String nameA = toFretDiagram(a)->harmonyText().toLower(); + String nameB = toFretDiagram(b)->harmonyText().toLower(); + auto iterA = std::find(newOrder.begin(), newOrder.end(), nameA); + auto iterB = std::find(newOrder.begin(), newOrder.end(), nameB); + return iterA < iterB; }); - - if (!includeInvisible) { - const StringList& invisibleDiagrams = this->invisibleDiagrams(); - - muse::remove_if(elements, [&invisibleDiagrams](const EngravingItem* element){ - const FretDiagram* diagram = toFretDiagram(element); - const String diagramHarmonyName = diagram->harmonyText().toLower(); - return muse::contains(invisibleDiagrams, diagramHarmonyName); - }); - } - - return elements; } -void FBox::undoSetInvisibleDiagrams(const StringList& invisibleDiagrams) +StringList FBox::diagramsOrder() const { - StringList diagrams; - for (const String& harmonyName : invisibleDiagrams) { - diagrams.push_back(harmonyName.toLower()); - } - - undoChangeProperty(Pid::FRET_FRAME_INVISIBLE_DIAGRAMS, diagrams.join(FRET_BOX_DIAGRAMS_SEPARATOR)); - triggerLayout(); -} - -void FBox::updateDiagramsOrder(const StringList& currentDiagrams) -{ - if (currentDiagrams == m_diagramsOrder) { - return; - } - - m_diagramsOrder.erase(std::remove_if(m_diagramsOrder.begin(), m_diagramsOrder.end(), - [&](const String& harmonyName) { return !muse::contains(currentDiagrams, harmonyName); }), - m_diagramsOrder.end()); - - String previousHarmonyName; - for (const String& harmonyName : currentDiagrams) { - if (!muse::contains(m_diagramsOrder, harmonyName)) { - size_t index = 0; - if (!previousHarmonyName.empty()) { - index = std::find(m_diagramsOrder.begin(), m_diagramsOrder.end(), previousHarmonyName) - m_diagramsOrder.begin() + 1; - } - - m_diagramsOrder.insert(m_diagramsOrder.begin() + index, harmonyName); - } - - previousHarmonyName = harmonyName; + StringList result; + for (EngravingItem* item : m_el) { + result.push_back(toFretDiagram(item)->harmonyText().toLower()); } -} -void FBox::updateInvisibleDiagrams(const StringList& currentDiagrams) -{ - m_invisibleDiagrams.erase(std::remove_if(m_invisibleDiagrams.begin(), m_invisibleDiagrams.end(), - [&](const String& harmonyName) { return !muse::contains(currentDiagrams, harmonyName); }), - m_invisibleDiagrams.end()); + return result; } //--------------------------------------------------------- diff --git a/src/engraving/dom/box.h b/src/engraving/dom/box.h index 56c31ef8c540e..e6a6ed1ab5758 100644 --- a/src/engraving/dom/box.h +++ b/src/engraving/dom/box.h @@ -208,19 +208,14 @@ class FBox : public VBox std::vector gripsPositions(const EditData&) const override; void undoReorderElements(const StringList& newOrder); - const StringList& diagramsOrder() const { return m_diagramsOrder; } - - void undoSetInvisibleDiagrams(const StringList& invisibleDiagrams); - const StringList& invisibleDiagrams() const { return m_invisibleDiagrams; } - - ElementList orderedElements(bool includeInvisible = false) const; + void reorderElements(const StringList& newOrder); + StringList diagramsOrder() const; bool needsRebuild() const { return m_needsRebuild; } void setNeedsRebuild(bool v) { m_needsRebuild = v; } private: - void updateDiagramsOrder(const StringList& currentDiagrams); void updateInvisibleDiagrams(const StringList& currentDiagrams); size_t computeInsertionIdx(const String& nameOfDiagramBeforeThis); @@ -234,8 +229,7 @@ class FBox : public VBox AlignH m_contentAlignmentH = AlignH::HCENTER; - StringList /*harmonyNames*/ m_diagramsOrder; - StringList /*harmonyNames*/ m_invisibleDiagrams; + StringList m_diagramsOrderInScore; }; //--------------------------------------------------------- diff --git a/src/engraving/dom/chordlist.cpp b/src/engraving/dom/chordlist.cpp index 9b6be0905f77c..cb40bb3129ded 100644 --- a/src/engraving/dom/chordlist.cpp +++ b/src/engraving/dom/chordlist.cpp @@ -1707,7 +1707,6 @@ const std::list& ParsedChord::renderList(const ChordList* cl, // Jazz superscript if (superScriptModifier) { // Set scale - LOGI() << "SCALE: " << cl->stackedModifierMag(); m_renderList.emplace_back(new RenderActionScale(cl->stackedModifierMag())); // Move to x-height m_renderList.emplace_back(new RenderActionPush()); diff --git a/src/engraving/dom/edit.cpp b/src/engraving/dom/edit.cpp index 9e17cdebddf4f..1a06a598789c3 100644 --- a/src/engraving/dom/edit.cpp +++ b/src/engraving/dom/edit.cpp @@ -3886,37 +3886,6 @@ void Score::cmdDeleteSelection() // so we don't try to delete them twice if they are also in selection std::set deletedSpanners; - // diagrams in the fret box must be processed first than diagrams outside the box, - // which causes the fret box to be rebuilt and all elements inside to be deleted - std::sort(el.begin(), el.end(), [&](const EngravingItem* item1, const EngravingItem* item2) { - bool inBox1 = isElementInFretBox(item1); - bool inBox2 = isElementInFretBox(item2); - - return inBox1 == inBox2 ? false : inBox1; - }); - - // if there is harmony or fret diagram outside the fret box - // then the fret box will be rebuilt and we should exclude fret box's elements - bool hasHarmonyOrFretDiagramsInFretBox = false; - bool hasHarmonyOrFretDiagramsOutFretBox = false; - for (const EngravingItem* element : el) { - if (!element->isFretDiagram() && !element->isHarmony()) { - continue; - } - - if (isElementInFretBox(element)) { - hasHarmonyOrFretDiagramsInFretBox = true; - } else { - hasHarmonyOrFretDiagramsOutFretBox = true; - } - - if (hasHarmonyOrFretDiagramsInFretBox && hasHarmonyOrFretDiagramsOutFretBox) { - break; - } - } - - bool willFretBoxBeRebuilded = hasHarmonyOrFretDiagramsInFretBox && hasHarmonyOrFretDiagramsOutFretBox; - auto selectCRAtTickAndTrack = [this, &crsSelectedAfterDeletion](Fraction tick, track_idx_t track) { ChordRest* cr = findCR(tick, track); if (cr) { @@ -3988,64 +3957,26 @@ void Score::cmdDeleteSelection() continue; } - // We can't delete elements inside fret box, instead we hide them if (e->isFretDiagram() || e->isHarmony()) { - auto excludeElementFromSelectionInfo = [this](EngravingItem* element) { - // we need to exclude elements ftom undo stack selection info, - // so that when trying to return the selection there is no crash - UndoMacro* activeCommand = undoStack()->activeCommand(); - activeCommand->excludeElementFromSelectionInfo(element); - }; - - if (willFretBoxBeRebuilded && isElementInFretBox(e)) { - excludeElementFromSelectionInfo(e); - continue; - } - - auto hideDiagramInFretBox = [&excludeElementFromSelectionInfo](FBox* fbox, EngravingItem* element){ - StringList invisibleDiagrams = fbox->invisibleDiagrams(); - const String harmonyName = element->isFretDiagram() ? toFretDiagram(element)->harmony()->harmonyName().toLower() - : toHarmony(element)->harmonyName().toLower(); - - invisibleDiagrams.push_back(harmonyName); - fbox->undoSetInvisibleDiagrams(invisibleDiagrams); - - excludeElementFromSelectionInfo(element); - }; - - if (e->isFretDiagram() && toFretDiagram(e)->isInFretBox()) { - FBox* fbox = toFBox(e->explicitParent()); - hideDiagramInFretBox(fbox, toFretDiagram(e)); - elSelectedAfterDeletion = fbox; - continue; - } else if (e->isHarmony()) { - EngravingObject* parent = toHarmony(e)->explicitParent(); - FretDiagram* fretDiagram = parent->isFretDiagram() ? toFretDiagram(parent) : nullptr; - - if (fretDiagram && fretDiagram->isInFretBox()) { - FBox* fbox = toFBox(fretDiagram->explicitParent()); - hideDiagramInFretBox(fbox, fretDiagram); - elSelectedAfterDeletion = fbox; + FBox* fbox = toFBox(e->findAncestor(ElementType::FBOX)); + if (fbox) { + // We can't delete elements inside fret box, instead we hide them + if (FretDiagram* fretDiagram = e->isFretDiagram() ? toFretDiagram(e) : toFretDiagram(e->parent())) { + fretDiagram->undoChangeProperty(Pid::VISIBLE, false); continue; } - } - } - - if (e->isFretDiagram()) { - FretDiagram* fretDiagram = toFretDiagram(e); - Harmony* harmony = fretDiagram->harmony(); - if (harmony) { - undo(new FretLinkHarmony(fretDiagram, harmony, true /* unlink */)); - elSelectedAfterDeletion = fretDiagram->segment()->findAnnotation(ElementType::HARMONY, - fretDiagram->track(), - fretDiagram->track()); - } - } - - if (e->isHarmony()) { - Harmony* harmony = toHarmony(e); - if (harmony->parentItem()->isFretDiagram()) { - elSelectedAfterDeletion = harmony->parentItem(); + } else if (e->isFretDiagram()) { + FretDiagram* fretDiagram = toFretDiagram(e); + Harmony* harmony = fretDiagram->harmony(); + if (harmony) { + undo(new FretLinkHarmony(fretDiagram, harmony, true /* unlink */)); + elSelectedAfterDeletion = harmony; + } + } else if (e->isHarmony()) { + Harmony* harmony = toHarmony(e); + if (harmony->parentItem()->isFretDiagram()) { + elSelectedAfterDeletion = harmony->parentItem(); + } } } @@ -7047,7 +6978,7 @@ void Score::undoAddElement(EngravingItem* element, bool addToLinkedStaves, bool // make harmony child of fret diagram if possible if (ne->isHarmony()) { for (EngravingItem* segel : segment->annotations()) { - if (segel && segel->isFretDiagram() && segel->track() == linkedTrack) { + if (segel && segel->isFretDiagram() && segel->track() == linkedTrack && !toFretDiagram(segel)->harmony()) { segel->add(ne); break; } diff --git a/src/engraving/dom/harmony.cpp b/src/engraving/dom/harmony.cpp index c9c050a160710..d1341862ef5d3 100644 --- a/src/engraving/dom/harmony.cpp +++ b/src/engraving/dom/harmony.cpp @@ -1527,6 +1527,7 @@ bool Harmony::setProperty(Pid pid, const PropertyValue& v) } score()->rebuildFretBox(); } + break; } default: return TextBase::setProperty(pid, v); diff --git a/src/engraving/dom/measure.cpp b/src/engraving/dom/measure.cpp index 8c4f9a4755958..e5b1935feefa6 100644 --- a/src/engraving/dom/measure.cpp +++ b/src/engraving/dom/measure.cpp @@ -1766,8 +1766,12 @@ EngravingItem* Measure::drop(EditData& data) score()->insertBox(ElementType::TBOX, this); break; case ActionIconType::FFRAME: - score()->insertBox(ElementType::FBOX, this); + { + Score::InsertMeasureOptions options; + options.cloneBoxToAllParts = false; + score()->insertBox(ElementType::FBOX, this, options); break; + } case ActionIconType::MEASURE: score()->insertMeasure(ElementType::MEASURE, this); break; diff --git a/src/engraving/dom/page.cpp b/src/engraving/dom/page.cpp index 00a1aefe4b857..72d5e2127be2c 100644 --- a/src/engraving/dom/page.cpp +++ b/src/engraving/dom/page.cpp @@ -394,12 +394,12 @@ TextBlock Page::replaceTextMacros(const TextBlock& tb) const if (!m_no) { break; } - // FALLTHROUGH + [[fallthrough]]; case 'N': // on page 1 only if there are multiple pages if ((score()->npages() + score()->pageNumberOffset()) <= 1) { break; } - // FALLTHROUGH + [[fallthrough]]; case 'P': // on all pages { int no = static_cast(m_no) + 1 + score()->pageNumberOffset(); @@ -426,7 +426,7 @@ TextBlock Page::replaceTextMacros(const TextBlock& tb) const if (!m_no) { break; } - // FALLTHROUGH + [[fallthrough]]; case 'I': newFragments.back().text += score()->metaTag(u"partName"); break; @@ -471,7 +471,7 @@ TextBlock Page::replaceTextMacros(const TextBlock& tb) const if (m_no) { break; } - // FALLTHROUGH + [[fallthrough]]; case 'c': { const String copyrightString = score()->metaTag(u"copyright"); diff --git a/src/engraving/dom/pitchspelling.cpp b/src/engraving/dom/pitchspelling.cpp index 17ef621d522fc..e1dfbac5cbe5a 100644 --- a/src/engraving/dom/pitchspelling.cpp +++ b/src/engraving/dom/pitchspelling.cpp @@ -747,11 +747,11 @@ void Score::spellNotelist(std::vector& notes) case 3: k = end - start - 3; changeAllTpcs(notes[end - 3], tab[(notes[end - 3]->pitch() % 12) * 2 + ((opt & (1 << k)) >> k)]); - // FALLTHROUGH + [[fallthrough]]; case 2: k = end - start - 2; changeAllTpcs(notes[end - 2], tab[(notes[end - 2]->pitch() % 12) * 2 + ((opt & (1 << k)) >> k)]); - // FALLTHROUGH + [[fallthrough]]; case 1: k = end - start - 1; changeAllTpcs(notes[end - 1], tab[(notes[end - 1]->pitch() % 12) * 2 + ((opt & (1 << k)) >> k)]); diff --git a/src/engraving/dom/property.cpp b/src/engraving/dom/property.cpp index 6c3ee312a59c4..db00184e1fb20 100644 --- a/src/engraving/dom/property.cpp +++ b/src/engraving/dom/property.cpp @@ -147,8 +147,7 @@ static constexpr PropertyMetaData propertyList[] = { { Pid::FRET_FRAME_ROW_GAP, false, "fretFrameRowGap", P_TYPE::SPATIUM, PropertyGroup::APPEARANCE, DUMMY_QT_TR_NOOP("propertyName", "row gap") }, { Pid::FRET_FRAME_CHORDS_PER_ROW, false, "fretFrameChordsPerRow", P_TYPE::INT, PropertyGroup::APPEARANCE, DUMMY_QT_TR_NOOP("propertyName", "chords per row") }, { Pid::FRET_FRAME_H_ALIGN, false, "fretFrameHorizontalAlign", P_TYPE::INT, PropertyGroup::APPEARANCE, DUMMY_QT_TR_NOOP("propertyName", "horizontal alignment") }, - { Pid::FRET_FRAME_DIAGRAMS_ORDER, false, "fretFrameDiagramsOrder", P_TYPE::STRING, PropertyGroup::APPEARANCE, DUMMY_QT_TR_NOOP("propertyName", "diagrams order") }, - { Pid::FRET_FRAME_INVISIBLE_DIAGRAMS, false, "fretFrameInvisibleDiagrams", P_TYPE::STRING, PropertyGroup::APPEARANCE, DUMMY_QT_TR_NOOP("propertyName", "invisible diagrams") }, + { Pid::FRET_FRAME_DIAGRAMS_ORDER, false, "fretFrameDiagramsOrder", P_TYPE::STRING, PropertyGroup::NONE, DUMMY_QT_TR_NOOP("propertyName", "diagrams order") }, { Pid::SCALE, false, "scale", P_TYPE::SCALE, PropertyGroup::APPEARANCE, DUMMY_QT_TR_NOOP("propertyName", "scale") }, { Pid::LOCK_ASPECT_RATIO, false, "lockAspectRatio", P_TYPE::BOOL, PropertyGroup::APPEARANCE, DUMMY_QT_TR_NOOP("propertyName", "aspect ratio locked") }, diff --git a/src/engraving/dom/property.h b/src/engraving/dom/property.h index 4a33f3bba0350..2d238d3303231 100644 --- a/src/engraving/dom/property.h +++ b/src/engraving/dom/property.h @@ -155,7 +155,6 @@ enum class Pid { FRET_FRAME_CHORDS_PER_ROW, FRET_FRAME_H_ALIGN, FRET_FRAME_DIAGRAMS_ORDER, - FRET_FRAME_INVISIBLE_DIAGRAMS, SCALE, LOCK_ASPECT_RATIO, diff --git a/src/engraving/dom/realizedharmony.cpp b/src/engraving/dom/realizedharmony.cpp index bdf734b67ac07..3e5a66e9e1d29 100644 --- a/src/engraving/dom/realizedharmony.cpp +++ b/src/engraving/dom/realizedharmony.cpp @@ -124,7 +124,7 @@ const RealizedHarmony::PitchMap RealizedHarmony::generateNotes(int rootTpc, int if (!m_harmony->parsedForm()->understandable()) { break; } - // FALLTHROUGH + [[fallthrough]]; case Voicing::CLOSE: //Voices notes in close position in the first octave above middle C { notes.insert({ rootPitch + DEFAULT_OCTAVE * PITCH_DELTA_OCTAVE, rootTpc }); @@ -439,7 +439,7 @@ RealizedHarmony::PitchMap RealizedHarmony::getIntervals(int rootTpc, bool litera ret.insert({ 9 + RANK_MULT * RANK_ADD, tpcInterval(rootTpc, 13, 0) }); //maj13 omit |= 1 << 13; } - // FALLTHROUGH + [[fallthrough]]; case 11: if (!(omit & (1 << 11))) { if (quality == "minor") { @@ -449,13 +449,13 @@ RealizedHarmony::PitchMap RealizedHarmony::getIntervals(int rootTpc, bool litera } omit |= 1 << 11; } - // FALLTHROUGH + [[fallthrough]]; case 9: if (!(omit & (1 << 9))) { ret.insert({ 2 + RANK_MULT * RANK_9TH, tpcInterval(rootTpc, 9, 0) }); //maj9 omit |= 1 << 9; } - // FALLTHROUGH + [[fallthrough]]; case 7: if (!(omit & (1 << 7))) { if (quality == "major") { diff --git a/src/engraving/dom/spanner.cpp b/src/engraving/dom/spanner.cpp index ce0be57a65def..3f469acdcb5aa 100644 --- a/src/engraving/dom/spanner.cpp +++ b/src/engraving/dom/spanner.cpp @@ -1621,12 +1621,18 @@ String SpannerSegment::formatBarsAndBeats() const endSegment = score()->lastSegment()->prev1MM(SegmentType::ChordRest); } - if (endSegment->tick() != score()->lastSegment()->prev1MM(SegmentType::ChordRest)->tick() + if (endSegment->tick() > Fraction(0, 1) + && endSegment->tick() != score()->lastSegment()->prev1MM(SegmentType::ChordRest)->tick() && spanner->type() != ElementType::SLUR + && spanner->type() != ElementType::HAMMER_ON_PULL_OFF && spanner->type() != ElementType::TIE) { endSegment = endSegment->prev1MM(SegmentType::ChordRest); } + IF_ASSERT_FAILED(endSegment) { + return EngravingItem::formatBarsAndBeats(); + } + return formatStartBarsAndBeats(spanner->startSegment()) + u' ' + formatEndBarsAndBeats(endSegment); } diff --git a/src/engraving/dom/staff.cpp b/src/engraving/dom/staff.cpp index 0fb94e6a092ec..059d48a6f58e0 100644 --- a/src/engraving/dom/staff.cpp +++ b/src/engraving/dom/staff.cpp @@ -249,6 +249,11 @@ bool Staff::shouldShowMeasureNumbers() const return false; } +bool Staff::isLastOfScore() const +{ + return score()->staves().empty() ? false : score()->staves().back() == this; +} + bool Staff::isSystemObjectStaff() const { return score() && muse::contains(score()->systemObjectStaves(), const_cast(this)); @@ -256,7 +261,7 @@ bool Staff::isSystemObjectStaff() const bool Staff::hasSystemObjectsBelowBottomStaff() const { - return isSystemObjectStaff() && score()->staves().back() == this && style().styleB(Sid::systemObjectsBelowBottomStaff); + return isSystemObjectStaff() && isLastOfScore() && style().styleB(Sid::systemObjectsBelowBottomStaff); } //--------------------------------------------------------- diff --git a/src/engraving/dom/staff.h b/src/engraving/dom/staff.h index 3cbae73a04382..f884c745e082a 100644 --- a/src/engraving/dom/staff.h +++ b/src/engraving/dom/staff.h @@ -264,6 +264,7 @@ class Staff final : public EngravingItem void undoSetShowMeasureNumbers(bool show); bool shouldShowMeasureNumbers() const; + bool isLastOfScore() const; bool isSystemObjectStaff() const; bool hasSystemObjectsBelowBottomStaff() const; diff --git a/src/engraving/rendering/score/harmonylayout.cpp b/src/engraving/rendering/score/harmonylayout.cpp index 3872820cfcafa..1c4add953bc13 100644 --- a/src/engraving/rendering/score/harmonylayout.cpp +++ b/src/engraving/rendering/score/harmonylayout.cpp @@ -500,7 +500,7 @@ void HarmonyLayout::renderSingleHarmony(Harmony* item, Harmony::LayoutData* ldat // render bass if (tpcIsValid(info->bassTpc())) { - std::list& bassNoteChordList + std::list bassNoteChordList = style.styleB(Sid::chordBassNoteStagger) ? chordList->renderListBassOffset : chordList->renderListBass; static const std::wregex PATTERN_69 = std::wregex(L"6[,/]?9"); diff --git a/src/engraving/rendering/score/horizontalspacing.cpp b/src/engraving/rendering/score/horizontalspacing.cpp index c25536877f934..b926a5b62c46b 100644 --- a/src/engraving/rendering/score/horizontalspacing.cpp +++ b/src/engraving/rendering/score/horizontalspacing.cpp @@ -309,7 +309,7 @@ std::vector HorizontalSpacing::spaceSegments } if (ctx.systemIsFull) { - checkLyricsAgainstRightMargin(placedSegments); + checkLyricsAgainstRightMargin(placedSegments, ctx); checkLargeTimeSigAgainstRightMargin(placedSegments); } @@ -473,13 +473,20 @@ void HorizontalSpacing::checkLyricsAgainstLeftMargin(Segment* segment, double& x } } -void HorizontalSpacing::checkLyricsAgainstRightMargin(std::vector& segPositions) +void HorizontalSpacing::checkLyricsAgainstRightMargin(std::vector& segPositions, const HorizontalSpacingContext& ctx) { + const double systemEdge = segPositions.back().xPosInSystemCoords + segPositions.back().segment->minRight(); + const MStyle& style = ctx.system->style(); + const bool lyricsDashForce = style.styleB(Sid::lyricsDashForce); + const bool lyricsMelismaForce = style.styleB(Sid::lyricsMelismaForce); + const double minSpaceForDash = lyricsDashForce ? style.styleMM(Sid::lyricsDashPad) + style.styleMM(Sid::lyricsDashMinLength) + + style.styleMM(Sid::lineEndToBarlineDistance) + segPositions.back().segment->minRight() : 0.0; + const double minSpaceForMelisma = lyricsMelismaForce ? style.styleMM(Sid::lyricsMelismaPad) + style.styleMM(Sid::lyricsMelismaMinLength) + + style.styleMM(Sid::lineEndToBarlineDistance) + segPositions.back().segment->minRight() : 0.0; + int chordRestSegmentsCount = 0; for (size_t i = segPositions.size(); i > 1; --i) { - double systemEdge = segPositions.back().xPosInSystemCoords + segPositions.back().segment->minRight(); - SegmentPosition& segPos = segPositions[i - 1]; double x = segPos.xPosInSystemCoords; Segment* seg = segPos.segment; @@ -495,7 +502,16 @@ void HorizontalSpacing::checkLyricsAgainstRightMargin(std::vectorshapes()) { for (const ShapeElement& shapeEl : shape.elements()) { if (shapeEl.item() && shapeEl.item()->isLyrics()) { - xMaxLyrics = std::max(xMaxLyrics, x + shapeEl.right()); + const Lyrics* lyrics = toLyrics(shapeEl.item()); + bool hasMelisma = lyrics->separator() && lyrics->separator()->isEndMelisma(); + bool hasDash = lyrics->syllabic() == LyricsSyllabic::BEGIN || lyrics->syllabic() == LyricsSyllabic::MIDDLE; + double rightEdge = x + shapeEl.right(); + if (hasDash) { + rightEdge += minSpaceForDash; + } else if (hasMelisma) { + rightEdge += minSpaceForMelisma; + } + xMaxLyrics = std::max(xMaxLyrics, rightEdge); } } } diff --git a/src/engraving/rendering/score/horizontalspacing.h b/src/engraving/rendering/score/horizontalspacing.h index b556a57206b65..14a7a4222ec00 100644 --- a/src/engraving/rendering/score/horizontalspacing.h +++ b/src/engraving/rendering/score/horizontalspacing.h @@ -112,7 +112,7 @@ class HorizontalSpacing HorizontalSpacingContext& ctx); static bool stopCheckingPreviousSegments(const SegmentPosition& prev, const SegmentPosition& curSegPos); static void checkLyricsAgainstLeftMargin(Segment* segment, double& x, HorizontalSpacingContext& ctx); - static void checkLyricsAgainstRightMargin(std::vector& segPositions); + static void checkLyricsAgainstRightMargin(std::vector& segPositions, const HorizontalSpacingContext& ctx); static double spaceLyricsAgainstBarlines(Segment* firstSeg, Segment* secondSeg, const HorizontalSpacingContext& ctx); static void checkLargeTimeSigAgainstRightMargin(std::vector& segPositions); static void moveRightAlignedSegments(std::vector& placedSegments, const HorizontalSpacingContext& ctx); diff --git a/src/engraving/rendering/score/restlayout.cpp b/src/engraving/rendering/score/restlayout.cpp index 33f4e7ab3c9be..94e4933d48d72 100644 --- a/src/engraving/rendering/score/restlayout.cpp +++ b/src/engraving/rendering/score/restlayout.cpp @@ -602,7 +602,7 @@ void RestLayout::checkFullMeasureRestCollisions(const System* system, LayoutCont } measureShape.remove_if([fullMeasureRest] (const ShapeElement& shapeEl) { const EngravingItem* shapeItem = shapeEl.item(); - return shapeItem && (shapeItem == fullMeasureRest || shapeItem->isBarLine()); + return shapeItem && (shapeItem == fullMeasureRest || shapeItem->isBarLine() || shapeItem->isAccidental()); }); const double spatium = fullMeasureRest->spatium(); diff --git a/src/engraving/rendering/score/tlayout.cpp b/src/engraving/rendering/score/tlayout.cpp index 656e54db09b2a..9a159e3b75078 100644 --- a/src/engraving/rendering/score/tlayout.cpp +++ b/src/engraving/rendering/score/tlayout.cpp @@ -1497,17 +1497,14 @@ void TLayout::layoutFBox(const FBox* item, FBox::LayoutData* ldata, const Layout ldata->setPos(PointF()); - const ElementList& elements = item->orderedElements(true /*includeInvisible*/); - const StringList& invisibleDiagrams = item->invisibleDiagrams(); - std::vector fretDiagrams; - for (EngravingItem* element : elements) { + for (EngravingItem* element : item->el()) { if (!element || !element->isFretDiagram() || !element->visible()) { continue; } FretDiagram* diagram = toFretDiagram(element); - if (muse::contains(invisibleDiagrams, diagram->harmony()->harmonyName().toLower())) { + if (!diagram->visible()) { //! NOTE: We need to layout the diagrams to get the harmony names to show in the UI layoutItem(diagram, const_cast(ctx)); @@ -2823,8 +2820,8 @@ void TLayout::layoutFretDiagram(const FretDiagram* item, FretDiagram::LayoutData int endString = barre.endString != -1 ? barre.endString : item->strings() - 1; int fret = i.first; bool slurStyleBarre = ctx.conf().styleB(Sid::barreAppearanceSlur); - for (int string = 0; string < item->strings(); ++string) { - if (slurStyleBarre && string >= startString && string <= endString) { + for (int string = startString; string <= endString; ++string) { + if (slurStyleBarre) { const_cast(item)->addDotForDotStyleBarre(string, fret); } else { const_cast(item)->removeDotForDotStyleBarre(string, fret); diff --git a/src/engraving/rw/read460/read460.cpp b/src/engraving/rw/read460/read460.cpp index a2091417eb477..172283a3c4bba 100644 --- a/src/engraving/rw/read460/read460.cpp +++ b/src/engraving/rw/read460/read460.cpp @@ -617,7 +617,9 @@ bool Read460::pasteStaff(XmlReader& e, Segment* dst, staff_idx_t dstStaff, Fract Fraction tick = doScale ? (ctx.tick() - dstTick) * scale + dstTick : ctx.tick(); Measure* m = score->tick2measure(tick); - Segment* seg = m->undoGetChordRestOrTimeTickSegment(tick); + Segment* seg = el->isFretDiagram() + ? m->undoGetSegment(SegmentType::ChordRest, tick) + : m->undoGetChordRestOrTimeTickSegment(tick); el->setParent(seg); // be sure to paste the element in the destination track; diff --git a/src/engraving/rw/read460/tread.cpp b/src/engraving/rw/read460/tread.cpp index 0a0ae71b391c4..430a3b1dde837 100644 --- a/src/engraving/rw/read460/tread.cpp +++ b/src/engraving/rw/read460/tread.cpp @@ -2113,8 +2113,6 @@ void TRead::read(FBox* b, XmlReader& xml, ReadContext& ctx) } else if (readProperty(b, tag, xml, ctx, Pid::FRET_FRAME_ROW_GAP)) { } else if (readProperty(b, tag, xml, ctx, Pid::FRET_FRAME_CHORDS_PER_ROW)) { } else if (readProperty(b, tag, xml, ctx, Pid::FRET_FRAME_H_ALIGN)) { - } else if (readProperty(b, tag, xml, ctx, Pid::FRET_FRAME_DIAGRAMS_ORDER)) { - } else if (readProperty(b, tag, xml, ctx, Pid::FRET_FRAME_INVISIBLE_DIAGRAMS)) { } else if (TRead::readProperties(static_cast(b), xml, ctx)) { } else { xml.unknown(); @@ -4346,9 +4344,6 @@ bool TRead::readProperties(TextBase* t, XmlReader& e, ReadContext& ctx) { const AsciiStringView tag(e.name()); for (Pid i : TextBasePropertyId) { - if (i == Pid::POSITION && tag == propertyName(i)) { - LOGI() << "read pos"; - } if (TRead::readProperty(t, tag, e, ctx, i)) { return true; } diff --git a/src/engraving/rw/write/twrite.cpp b/src/engraving/rw/write/twrite.cpp index 299af42c6b25c..71373eb2291c8 100644 --- a/src/engraving/rw/write/twrite.cpp +++ b/src/engraving/rw/write/twrite.cpp @@ -822,8 +822,6 @@ void TWrite::write(const FBox* item, XmlWriter& xml, WriteContext& ctx) writeProperty(item, xml, Pid::FRET_FRAME_ROW_GAP); writeProperty(item, xml, Pid::FRET_FRAME_CHORDS_PER_ROW); writeProperty(item, xml, Pid::FRET_FRAME_H_ALIGN); - writeProperty(item, xml, Pid::FRET_FRAME_DIAGRAMS_ORDER); - writeProperty(item, xml, Pid::FRET_FRAME_INVISIBLE_DIAGRAMS); writeProperties(static_cast(item), xml, ctx); diff --git a/src/engraving/tests/fretbox_tests.cpp b/src/engraving/tests/fretbox_tests.cpp index 13a097fe28b1e..245f7efa1763d 100644 --- a/src/engraving/tests/fretbox_tests.cpp +++ b/src/engraving/tests/fretbox_tests.cpp @@ -161,7 +161,12 @@ class Engraving_FretBoxTests : public ::testing::Test FBox* fretBox = toFBox(score->measure(0)); ASSERT_TRUE(fretBox); - const ElementList& elements = fretBox->orderedElements(false /*includeInvisible*/); + ElementList elements; + for (EngravingItem* item : fretBox->el()) { + if (item->visible()) { + elements.push_back(item); + } + } EXPECT_EQ(elements.size(), chords.size()); for (size_t i = 0; i < chords.size(); ++i) { diff --git a/src/inspector/models/notation/frames/fretframe/fretframechordlistmodel.cpp b/src/inspector/models/notation/frames/fretframe/fretframechordlistmodel.cpp index 2ea2574cd8b51..e7a49f71e7fc5 100644 --- a/src/inspector/models/notation/frames/fretframe/fretframechordlistmodel.cpp +++ b/src/inspector/models/notation/frames/fretframe/fretframechordlistmodel.cpp @@ -57,15 +57,13 @@ void FretFrameChordListModel::load() return name; }; - const StringList& invisibleDiagrams = m_fretBox->invisibleDiagrams(); - - for (EngravingItem* element : m_fretBox->orderedElements(true /*includeInvisible*/)) { + for (EngravingItem* element : m_fretBox->el()) { FretDiagram* diagram = toFretDiagram(element); auto chordItem = new FretFrameChordItem(this); chordItem->setTitle(harmonyName(diagram->harmony())); chordItem->setPlainText(diagram->harmonyText()); - chordItem->setIsVisible(!muse::contains(invisibleDiagrams, diagram->harmony()->harmonyName().toLower())); + chordItem->setIsVisible(diagram->visible()); items << chordItem; } @@ -107,7 +105,7 @@ void FretFrameChordListModel::setChordVisible(int index, bool visible) return; } - ElementList diagrams = m_fretBox->orderedElements(true /*includeInvisible*/); + ElementList diagrams = m_fretBox->el(); if (index < 0 || index >= static_cast(diagrams.size())) { return; } @@ -124,16 +122,8 @@ void FretFrameChordListModel::setChordVisible(int index, bool visible) notation->undoStack()->prepareChanges(actionName); FretDiagram* diagram = toFretDiagram(diagrams[index]); - String harmonyName = diagram->harmony()->harmonyName().toLower(); - - StringList invisibleDiagrams = m_fretBox->invisibleDiagrams(); - if (visible) { - invisibleDiagrams.erase(std::remove(invisibleDiagrams.begin(), invisibleDiagrams.end(), harmonyName)); - } else { - invisibleDiagrams.push_back(harmonyName); - } - m_fretBox->undoSetInvisibleDiagrams(invisibleDiagrams); + diagram->undoChangeProperty(Pid::VISIBLE, visible); FretFrameChordItem* item = modelIndexToItem(this->index(index)); item->setIsVisible(visible); diff --git a/src/inspector/models/parts/partssettingsmodel.cpp b/src/inspector/models/parts/partssettingsmodel.cpp index 240bb5159386f..5f7a7d3c8f641 100644 --- a/src/inspector/models/parts/partssettingsmodel.cpp +++ b/src/inspector/models/parts/partssettingsmodel.cpp @@ -65,7 +65,7 @@ void PartsSettingsModel::requestElements() m_elementsForTextLinkingOption.clear(); for (EngravingItem* item : m_elementList) { - if (!item->score()->isMaster() && !item->isLayoutBreak()) { + if (!item->score()->isMaster() && !item->isLayoutBreak() && !item->isFBox()) { m_elementsForPartLinkingOption.push_back(item); } if (item->canBeExcludedFromOtherParts()) { @@ -101,6 +101,13 @@ void PartsSettingsModel::onNotationChanged(const PropertyIdSet&, const StyleIdSe loadProperties(); } +void PartsSettingsModel::onCurrentNotationChanged() +{ + emit isMasterScoreChanged(isMasterNotation()); + + AbstractInspectorModel::onCurrentNotationChanged(); +} + PropertyItem* PartsSettingsModel::positionLinkedToMaster() const { return m_positionLinkedToMaster; @@ -136,6 +143,11 @@ bool PartsSettingsModel::showTextLinkingOption() const return m_showTextLinkingOption; } +bool PartsSettingsModel::isMasterScore() const +{ + return isMasterNotation(); +} + void PartsSettingsModel::updateShowPartLinkingOption() { bool showPartLinking = !m_elementsForPartLinkingOption.empty(); diff --git a/src/inspector/models/parts/partssettingsmodel.h b/src/inspector/models/parts/partssettingsmodel.h index 4026c4dee7592..c853ac777675c 100644 --- a/src/inspector/models/parts/partssettingsmodel.h +++ b/src/inspector/models/parts/partssettingsmodel.h @@ -36,6 +36,7 @@ class PartsSettingsModel : public AbstractInspectorModel Q_PROPERTY(bool showPartLinkingOption READ showPartLinkingOption NOTIFY showPartLinkingOptionChanged) Q_PROPERTY(bool showExcludeOption READ showExcludeOption NOTIFY showExcludeOptionChanged) Q_PROPERTY(bool showTextLinkingOption READ showTextLinkingOption NOTIFY showTextLinkingOptionChanged) + Q_PROPERTY(bool isMasterScore READ isMasterScore NOTIFY isMasterScoreChanged) public: explicit PartsSettingsModel(QObject* parent, IElementRepositoryService* repository); @@ -48,11 +49,13 @@ class PartsSettingsModel : public AbstractInspectorModel bool showPartLinkingOption() const; bool showExcludeOption() const; bool showTextLinkingOption() const; + bool isMasterScore() const; signals: void showPartLinkingOptionChanged(bool showPartsOption); void showExcludeOptionChanged(bool excludeOption); void showTextLinkingOptionChanged(bool showTextLink); + void isMasterScoreChanged(bool isMasterScore); private: void createProperties() override; @@ -60,6 +63,7 @@ class PartsSettingsModel : public AbstractInspectorModel void loadProperties() override; void resetProperties() override; void onNotationChanged(const mu::engraving::PropertyIdSet&, const mu::engraving::StyleIdSet&) override; + void onCurrentNotationChanged() override; void updateShowPartLinkingOption(); void updateShowExcludeOption(); diff --git a/src/inspector/view/qml/MuseScore/Inspector/parts/PartsSettings.qml b/src/inspector/view/qml/MuseScore/Inspector/parts/PartsSettings.qml index 45bbe4a7f5a21..6bd8b0a27ca08 100644 --- a/src/inspector/view/qml/MuseScore/Inspector/parts/PartsSettings.qml +++ b/src/inspector/view/qml/MuseScore/Inspector/parts/PartsSettings.qml @@ -106,9 +106,9 @@ InspectorSectionView { navigation.panel: root.navigationPanel navigation.row: root.navigationRowStart - text: root.model && root.model.showPartLinkingOption - ? qsTrc("inspector", "Exclude from score") - : qsTrc("inspector", "Exclude from parts") + text: root.model && root.model.isMasterScore + ? qsTrc("inspector", "Exclude from parts") + : qsTrc("inspector", "Exclude from score") } } } diff --git a/src/instrumentsscene/view/roottreeitem.cpp b/src/instrumentsscene/view/roottreeitem.cpp index 6c0454f415852..f36d048dc952c 100644 --- a/src/instrumentsscene/view/roottreeitem.cpp +++ b/src/instrumentsscene/view/roottreeitem.cpp @@ -218,7 +218,7 @@ MoveParams RootTreeItem::buildSystemObjectsMoveParams(int sourceRow, int count, if ((sourceIsSystemObjectLayer && moveUp) || (sourceIsPartLayer && moveDown)) { moveParams.moveSysObjAboveBottomStaff = true; } - } else if (sourceIsPartLayer && moveUp) { + } else if (sourceIsPartLayer && srcStaff->isLastOfScore() && moveUp) { moveParams.moveSysObjBelowBottomStaff = true; } diff --git a/src/notation/inotationparts.h b/src/notation/inotationparts.h index e1f2ca62439c9..33305404bbe03 100644 --- a/src/notation/inotationparts.h +++ b/src/notation/inotationparts.h @@ -72,6 +72,7 @@ class INotationParts InsertMode mode = InsertMode::Before) = 0; virtual bool appendStaff(Staff* staff, const muse::ID& destinationPartId) = 0; + virtual bool appendStaffLinkedToMaster(Staff* staff, Staff* masterSourceStaff, const muse::ID& destinationPartId) = 0; virtual bool appendLinkedStaff(Staff* staff, const muse::ID& sourceStaffId, const muse::ID& destinationPartId) = 0; virtual void insertPart(Part* part, size_t index) = 0; diff --git a/src/notation/internal/masternotationparts.cpp b/src/notation/internal/masternotationparts.cpp index c8313a9954b85..2839f29c4dded 100644 --- a/src/notation/internal/masternotationparts.cpp +++ b/src/notation/internal/masternotationparts.cpp @@ -145,9 +145,8 @@ bool MasterNotationParts::appendStaff(Staff* staff, const ID& destinationPartId) for (const INotationPartsPtr& parts : excerptsParts()) { Staff* excerptStaff = staff->clone(); - if (parts->appendStaff(excerptStaff, destinationPartId)) { - excerptStaff->linkTo(staff); - } else { + if (!parts->appendStaffLinkedToMaster(excerptStaff, staff, destinationPartId)) { + excerptStaff->undoUnlink(); delete excerptStaff; } } diff --git a/src/notation/internal/masternotationparts.h b/src/notation/internal/masternotationparts.h index bdf080d5d2cf4..f0eec2953bc11 100644 --- a/src/notation/internal/masternotationparts.h +++ b/src/notation/internal/masternotationparts.h @@ -40,6 +40,7 @@ class MasterNotationParts : public NotationParts void removeStaves(const muse::IDList& stavesIds) override; bool appendStaff(Staff* staff, const muse::ID& destinationPartId) override; + bool appendStaffLinkedToMaster(Staff*, Staff*, const muse::ID&) override { return false; } bool appendLinkedStaff(Staff* staff, const muse::ID& sourceStaffId, const muse::ID& destinationPartId) override; void replaceInstrument(const InstrumentKey& instrumentKey, const Instrument& newInstrument, diff --git a/src/notation/internal/notationinteraction.cpp b/src/notation/internal/notationinteraction.cpp index 770dfe3d0b916..403f65236dd37 100644 --- a/src/notation/internal/notationinteraction.cpp +++ b/src/notation/internal/notationinteraction.cpp @@ -5089,6 +5089,7 @@ void NotationInteraction::addBoxes(BoxType boxType, int count, int beforeBoxInde options.createEmptyMeasures = false; options.moveSignaturesClef = moveSignaturesClef; options.needDeselectAll = false; + options.cloneBoxToAllParts = boxType != BoxType::Fret; for (int i = 0; i < count; ++i) { score()->insertMeasure(elementType, beforeBox, options); diff --git a/src/notation/internal/notationparts.cpp b/src/notation/internal/notationparts.cpp index 64b55f9060711..7cd0c8968054a 100644 --- a/src/notation/internal/notationparts.cpp +++ b/src/notation/internal/notationparts.cpp @@ -600,6 +600,29 @@ bool NotationParts::appendStaff(Staff* staff, const ID& destinationPartId) return true; } +bool NotationParts::appendStaffLinkedToMaster(Staff* staff, Staff* masterSourceStaff, const muse::ID& destinationPartId) +{ + TRACEFUNC; + + IF_ASSERT_FAILED(staff && masterSourceStaff) { + return false; + } + + Part* destinationPart = partModifiable(destinationPartId); + if (!destinationPart) { + return false; + } + + startEdit(TranslatableString("undoableAction", "Add staff")); + + doAppendStaff(staff, destinationPart, /*createRests*/ false); + score()->undo(new mu::engraving::Link(staff, masterSourceStaff)); + + mu::engraving::Excerpt::cloneStaff2(masterSourceStaff, staff, Fraction(0, 1), score()->endTick()); + + return true; +} + bool NotationParts::appendLinkedStaff(Staff* staff, const muse::ID& sourceStaffId, const muse::ID& destinationPartId) { TRACEFUNC; diff --git a/src/notation/internal/notationparts.h b/src/notation/internal/notationparts.h index 34e244cae241f..2a704c486e784 100644 --- a/src/notation/internal/notationparts.h +++ b/src/notation/internal/notationparts.h @@ -66,6 +66,7 @@ class NotationParts : public INotationParts, public muse::async::Asyncable void moveStaves(const muse::IDList& sourceStavesIds, const muse::ID& destinationStaffId, InsertMode mode = InsertMode::Before) override; bool appendStaff(Staff* staff, const muse::ID& destinationPartId) override; + bool appendStaffLinkedToMaster(Staff* staff, Staff* masterSourceStaff, const muse::ID& destinationPartId) override; bool appendLinkedStaff(Staff* staff, const muse::ID& sourceStaffId, const muse::ID& destinationPartId) override; void insertPart(Part* part, size_t index) override; diff --git a/vtest/scores/lyrics-30.mscz b/vtest/scores/lyrics-30.mscz new file mode 100644 index 0000000000000..cd936b1795173 Binary files /dev/null and b/vtest/scores/lyrics-30.mscz differ