LCOV - code coverage report
Current view: top level - src/engraving/rendering/score - modifydom.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 79.2 % 298 236
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2023 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : #include "modifydom.h"
      23              : 
      24              : #include "dom/measure.h"
      25              : #include "dom/staff.h"
      26              : #include "dom/spannermap.h"
      27              : #include "dom/timesig.h"
      28              : #include "dom/trill.h"
      29              : #include "dom/ornament.h"
      30              : #include "dom/note.h"
      31              : #include "dom/utils.h"
      32              : #include "dom/chord.h"
      33              : #include "dom/keysig.h"
      34              : #include "dom/hook.h"
      35              : #include "dom/part.h"
      36              : #include "dom/score.h"
      37              : #include "editing/addremoveelement.h"
      38              : #include "rendering/score/chordlayout.h"
      39              : 
      40              : using namespace mu::engraving::rendering::score;
      41              : 
      42        30347 : void ModifyDom::setCrossMeasure(const Measure* measure, LayoutContext& ctx)
      43              : {
      44        30347 :     bool crossMeasure = ctx.conf().styleB(Sid::crossMeasureValues);
      45        30347 :     const DomAccessor& dom = ctx.dom();
      46        74673 :     for (staff_idx_t staffIdx = 0; staffIdx < dom.nstaves(); ++staffIdx) {
      47        44326 :         const Staff* staff = dom.staff(staffIdx);
      48        44326 :         if (!staff->show()) {
      49          667 :             continue;
      50              :         }
      51              : 
      52        43659 :         track_idx_t startTrack = staffIdx * VOICES;
      53        43659 :         track_idx_t endTrack  = startTrack + VOICES;
      54              : 
      55       178072 :         for (const Segment& segment : measure->segments()) {
      56       134413 :             if (!segment.isJustType(SegmentType::ChordRest)) {
      57        46567 :                 continue;
      58              :             }
      59              : 
      60       439230 :             for (track_idx_t t = startTrack; t < endTrack; ++t) {
      61       351384 :                 ChordRest* cr = segment.cr(t);
      62       351384 :                 if (!cr) {
      63       264283 :                     continue;
      64              :                 }
      65        87101 :                 if (cr->isChord()) {
      66        48116 :                     Chord* chord = toChord(cr);
      67        48116 :                     if (!chord->isGrace()) {
      68        48116 :                         ChordLayout::crossMeasureSetup(chord, crossMeasure, ctx);
      69              :                     }
      70              :                 }
      71              :             }
      72              :         }
      73              :     }
      74        30347 : }
      75              : 
      76        30347 : void ModifyDom::connectTremolo(Measure* m)
      77              : {
      78        30347 :     m->connectTremolo();
      79        30347 : }
      80              : 
      81        30347 : void ModifyDom::cmdUpdateNotes(const Measure* measure, const DomAccessor& dom)
      82              : {
      83        74673 :     for (staff_idx_t staffIdx = 0; staffIdx < dom.nstaves(); ++staffIdx) {
      84        44326 :         const Staff* staff = dom.staff(staffIdx);
      85        44326 :         if (!staff->show()) {
      86          667 :             continue;
      87              :         }
      88              : 
      89        43659 :         AccidentalState as;          // list of already set accidentals for this measure
      90              :         // initAccidentalState
      91              :         {
      92        43659 :             as.init(staff->keySigEvent(measure->tick()));
      93              : 
      94              :             // Trills may carry an accidental into this measure that requires a force-restate
      95        43659 :             int ticks = measure->tick().ticks();
      96        43659 :             auto spanners = dom.spannerMap().findOverlapping(ticks, ticks, true);
      97        47400 :             for (auto iter : spanners) {
      98         3741 :                 Spanner* spanner = iter.value;
      99         5947 :                 if (spanner->staffIdx() != staffIdx || !spanner->isTrill()
     100         5947 :                     || spanner->tick() == measure->tick() || spanner->tick2() == measure->tick()) {
     101         3726 :                     continue;
     102              :                 }
     103           15 :                 Ornament* ornament = toTrill(spanner)->ornament();
     104           15 :                 Note* trillNote = ornament ? ornament->noteAbove() : nullptr;
     105           15 :                 if (trillNote && trillNote->accidental() && ornament->showAccidental() == OrnamentShowAccidental::DEFAULT) {
     106            6 :                     int line = absStep(trillNote->tpc(), trillNote->epitch());
     107            6 :                     as.setForceRestateAccidental(line, true);
     108              :                 }
     109              :             }
     110        43659 :         }
     111              : 
     112        43659 :         const TrackRange trackRange = staff->part()->trackRange();
     113        43659 :         track_idx_t mainTrack = staffIdx * VOICES;
     114              : 
     115       178072 :         for (const Segment& segment : measure->segments()) {
     116       134413 :             if (segment.isJustType(SegmentType::KeySig)) {
     117         6699 :                 KeySig* ks = item_cast<KeySig*>(segment.element(mainTrack));
     118         6699 :                 if (ks) {
     119         6443 :                     Fraction tick = segment.tick();
     120         6443 :                     as.init(staff->keySigEvent(tick));
     121              :                 }
     122       127714 :             } else if (segment.isJustType(SegmentType::ChordRest)) {
     123       533850 :                 for (track_idx_t t = trackRange.startTrack; t < trackRange.endTrack; ++t) {
     124       446004 :                     Chord* chord = item_cast<Chord*>(segment.element(t), CastMode::MAYBE_BAD); // maybe Rest
     125       446004 :                     if (chord) {
     126        59965 :                         chord->cmdUpdateNotes(&as, staffIdx);
     127              :                     }
     128              :                 }
     129              :             }
     130              :         }
     131              :     }
     132        30347 : }
     133              : 
     134        30347 : void ModifyDom::createStems(const Measure* measure, LayoutContext& ctx)
     135              : {
     136        30347 :     const DomAccessor& dom = ctx.dom();
     137        74673 :     for (staff_idx_t staffIdx = 0; staffIdx < dom.nstaves(); ++staffIdx) {
     138        44326 :         const Staff* staff = dom.staff(staffIdx);
     139        44326 :         if (!staff->show()) {
     140          667 :             continue;
     141              :         }
     142              : 
     143        43659 :         track_idx_t startTrack = staffIdx * VOICES;
     144        43659 :         track_idx_t endTrack  = startTrack + VOICES;
     145              : 
     146       178072 :         for (const Segment& segment : measure->segments()) {
     147       134413 :             if (!segment.isJustType(SegmentType::ChordRest)) {
     148        46567 :                 continue;
     149              :             }
     150              : 
     151       439230 :             for (track_idx_t t = startTrack; t < endTrack; ++t) {
     152       351384 :                 ChordRest* cr = segment.cr(t);
     153       351384 :                 if (!cr) {
     154       264283 :                     continue;
     155              :                 }
     156              : 
     157        49021 :                 auto createStems = [](Chord* chord) {
     158        49021 :                     if (!chord->shouldHaveStem()) {
     159         5203 :                         chord->removeStem();
     160         5203 :                         return;
     161              :                     }
     162              : 
     163        43818 :                     if (!chord->stem()) {
     164        22727 :                         chord->createStem();
     165              :                     }
     166              :                 };
     167              : 
     168        87101 :                 if (cr->isChord()) {
     169        48116 :                     Chord* chord = toChord(cr);
     170              : 
     171        49021 :                     for (Chord* c : chord->graceNotes()) {
     172          905 :                         createStems(c);
     173              :                     }
     174              : 
     175        48116 :                     createStems(chord);     // create stems needed to calculate spacing
     176              :                     // stem direction can change later during beam processing
     177              :                 }
     178              :             }
     179              :         }
     180              :     }
     181        30347 : }
     182              : 
     183        30347 : void ModifyDom::setTrackForChordGraceNotes(Measure* measure, const DomAccessor& dom)
     184              : {
     185        74673 :     for (staff_idx_t staffIdx = 0; staffIdx < dom.nstaves(); ++staffIdx) {
     186        44326 :         track_idx_t startTrack = staffIdx * VOICES;
     187        44326 :         track_idx_t endTrack  = startTrack + VOICES;
     188              : 
     189       180656 :         for (const Segment& segment : measure->segments()) {
     190       136330 :             if (!segment.isJustType(SegmentType::ChordRest)) {
     191        47636 :                 continue;
     192              :             }
     193              : 
     194       443470 :             for (track_idx_t t = startTrack; t < endTrack; ++t) {
     195       354776 :                 ChordRest* cr = segment.cr(t);
     196       354776 :                 if (!cr) {
     197       266871 :                     continue;
     198              :                 }
     199              : 
     200        87905 :                 if (cr->isChord()) {
     201        48252 :                     Chord* chord = toChord(cr);
     202        49158 :                     for (Chord* c : chord->graceNotes()) {
     203          906 :                         c->setTrack(t);
     204              :                     }
     205              :                 }
     206              :             }
     207              :         }
     208              :     }
     209        30347 : }
     210              : 
     211        30347 : void ModifyDom::sortMeasureSegments(Measure* measure, LayoutContext& ctx)
     212              : {
     213        30347 :     measure = measure->coveringMMRestOrThis();
     214              : 
     215              :     // Move segments between measure which need to move
     216              : 
     217              :     // Move key and time signature segments to the correct measure.
     218              :     // Depending on configuration, this could mean moving segments at the end of this measure to the start of the following measure
     219              :     // or moving segments from the start of the following measure to the end of this measure
     220              : 
     221              :     // Loop through clef, key, time sigs in this bar.  If any are found and shouldn't be in this bar, move to the next
     222              : 
     223           19 :     auto changeAppliesToRepeatAndContinuation = [&](const Segment& seg) -> bool {
     224              :         // Check if the change applies to the beginning of the repeat section as well as the continuation
     225           19 :         const std::vector<Measure*> measures = findFollowingRepeatMeasures(measure);
     226           25 :         for (const Measure* repeatMeasure : measures) {
     227           22 :             if (repeatMeasure == measure->nextMeasureMM()) {
     228            3 :                 continue;
     229              :             }
     230           19 :             const Fraction startTick = repeatMeasure->tick();
     231           22 :             for (track_idx_t track = 0; track < ctx.dom().nstaves(); track += VOICES) {
     232           19 :                 const Staff* staff = ctx.dom().staff(track2staff(track));
     233              : 
     234           19 :                 if (seg.isKeySigType()) {
     235            3 :                     const Key continuationKey = staff->key(seg.tick());
     236            3 :                     const Key repeatKey = staff->key(startTick);
     237            3 :                     const Key keyBeforeContinuation = staff->key(seg.tick() - Fraction::eps());
     238           16 :                     return keyBeforeContinuation != continuationKey && continuationKey == repeatKey;
     239              :                 }
     240              : 
     241           16 :                 if (seg.isTimeSigType()) {
     242           13 :                     const TimeSig* continuationTimeSig = staff->timeSig(seg.tick());
     243           13 :                     const TimeSig* repeatTimeSig = staff->timeSig(startTick);
     244           13 :                     const TimeSig* timeSigBeforeContinuation = staff->timeSig(seg.tick() - Fraction::eps());
     245           13 :                     return continuationTimeSig && repeatTimeSig && timeSigBeforeContinuation
     246           13 :                            && timeSigBeforeContinuation->sig() != continuationTimeSig->sig()
     247           26 :                            && continuationTimeSig->sig() == repeatTimeSig->sig();
     248              :                 }
     249              : 
     250            3 :                 if (seg.isClefType()) {
     251            3 :                     const ClefType continuationClef = staff->clef(seg.tick());
     252            3 :                     const ClefType repeatClef = staff->clef(startTick);
     253            3 :                     const ClefType clefBeforeContinuation = staff->clef(seg.tick() - Fraction::eps());
     254            3 :                     if (clefBeforeContinuation != continuationClef && continuationClef == repeatClef) {
     255            0 :                         return true;
     256              :                     }
     257              :                 }
     258              :             }
     259              :         }
     260            3 :         return false;
     261           19 :     };
     262              : 
     263          228 :     auto clefSegBarlinePosition = [&](const Segment& seg) -> ClefToBarlinePosition
     264              :     {
     265         1476 :         for (EngravingItem* el : seg.elist()) {
     266         1253 :             if (!el || !el->isClef()) {
     267         1018 :                 continue;
     268              :             }
     269          235 :             const Clef* clef = toClef(el);
     270          235 :             if (clef->clefToBarlinePosition() != ClefToBarlinePosition::AUTO) {
     271            5 :                 return clef->clefToBarlinePosition();
     272              :             }
     273              :         }
     274          223 :         return ClefToBarlinePosition::AUTO;
     275              :     };
     276              : 
     277        30347 :     MeasureBase* nextMb = measure ? measure->nextMM() : nullptr;
     278              : 
     279        30347 :     if (!nextMb || !nextMb->isMeasure()) {
     280         3930 :         return;
     281              :     }
     282              : 
     283        26417 :     Measure* nextMeasure = toMeasure(nextMb);
     284              : 
     285        26739 :     const bool sigsShouldBeInThisMeasure = ((measure->repeatEnd() && ctx.conf().styleB(Sid::changesBeforeBarlineRepeats))
     286        26739 :                                             || (measure->repeatJump() && ctx.conf().styleB(Sid::changesBeforeBarlineOtherJumps)));
     287        26417 :     std::vector<Segment*> segsToRemove;
     288              : 
     289        26417 :     std::vector<Segment*> segsToMoveToNextMeasure;
     290       104555 :     for (Segment& seg : measure->segments()) {
     291        78138 :         if (seg.tick() != measure->endTick() || seg.isChordRestType()) {
     292        64087 :             continue;
     293              :         }
     294              : 
     295              :         // Move clefs at the end of this measure into the next measure
     296        14051 :         if (seg.isClefType()) {
     297          217 :             ClefToBarlinePosition pos = clefSegBarlinePosition(seg);
     298              :             // Clef position explicitly set by user
     299          217 :             if (pos == ClefToBarlinePosition::BEFORE) {
     300            0 :                 continue;
     301          217 :             } else if (pos == ClefToBarlinePosition::AFTER) {
     302            0 :                 segsToMoveToNextMeasure.push_back(&seg);
     303            0 :                 continue;
     304              :             }
     305              : 
     306              :             // AUTO on normal barline
     307          217 :             if (!measure->repeatEnd()) {
     308          217 :                 continue;
     309              :             }
     310              : 
     311              :             // Otherwise, pos is AUTO
     312              :             // Place changes before barline & clefs before repeats style settings
     313            0 :             if ((!sigsShouldBeInThisMeasure && !changeAppliesToRepeatAndContinuation(seg))
     314            0 :                 || (measure->repeatEnd() && !ctx.conf().styleB(Sid::placeClefsBeforeRepeats))) {
     315            0 :                 segsToMoveToNextMeasure.push_back(&seg);
     316            0 :                 continue;
     317              :             }
     318              :         }
     319              : 
     320              :         // Move key sigs and time sigs at the end of this measure into the next measure
     321        27668 :         if ((seg.isKeySigType() || seg.isTimeSigType()) && !seg.header() && !seg.trailer()
     322        27668 :             && (!sigsShouldBeInThisMeasure || !changeAppliesToRepeatAndContinuation(seg))) {
     323            0 :             if (nextMeasure && nextMeasure->findSegmentR(seg.segmentType(), Fraction(0, 1))) {
     324            0 :                 segsToRemove.push_back(&seg);
     325            0 :                 continue;
     326              :             }
     327            0 :             segsToMoveToNextMeasure.push_back(&seg);
     328              :         }
     329              :     }
     330              : 
     331              :     // No next measure, so these segments are useless
     332        26417 :     if (!nextMeasure) {
     333            0 :         for (Segment* seg : segsToMoveToNextMeasure) {
     334            0 :             ctx.mutDom().doUndoRemoveElement(seg);
     335              :         }
     336            0 :         return;
     337              :     }
     338              : 
     339        26417 :     std::vector<Segment*> segsToMoveToThisMeasure;
     340        95454 :     for (Segment& seg : nextMeasure->segments()) {
     341        69037 :         if (seg.tick() != nextMeasure->tick() || seg.isChordRestType()) {
     342        64718 :             continue;
     343              :         }
     344              : 
     345              :         // Move clefs at the beginning of the next measure into this measure
     346         4319 :         if (seg.isClefType()) {
     347           11 :             ClefToBarlinePosition pos = clefSegBarlinePosition(seg);
     348              :             // Clef position explicitly set by user
     349           11 :             if (pos == ClefToBarlinePosition::BEFORE) {
     350            0 :                 segsToMoveToThisMeasure.push_back(&seg);
     351            0 :                 continue;
     352           11 :             } else if (pos == ClefToBarlinePosition::AFTER) {
     353            5 :                 continue;
     354              :             }
     355              : 
     356            6 :             if (!measure->repeatEnd()) {
     357            3 :                 segsToMoveToThisMeasure.push_back(&seg);
     358            3 :                 continue;
     359              :             }
     360              : 
     361              :             // Otherwise, pos is AUTO
     362              :             // Place changes before barline & clefs before repeats style settings
     363            3 :             if ((sigsShouldBeInThisMeasure && changeAppliesToRepeatAndContinuation(seg))
     364            6 :                 || !measure->repeatEnd() || ctx.conf().styleB(Sid::placeClefsBeforeRepeats)) {
     365            1 :                 segsToMoveToThisMeasure.push_back(&seg);
     366              :             }
     367              :         }
     368              : 
     369              :         // Move key sigs and time sigs at the start of the next measure to the end of this measure
     370         8622 :         if ((seg.isTimeSigType() || seg.isKeySigType()) && !seg.header() && !seg.trailer()
     371         8622 :             && sigsShouldBeInThisMeasure && changeAppliesToRepeatAndContinuation(seg)) {
     372            3 :             if (measure->findSegmentR(seg.segmentType(), measure->ticks())) {
     373            0 :                 segsToRemove.push_back(&seg);
     374            0 :                 continue;
     375              :             }
     376            3 :             segsToMoveToThisMeasure.push_back(&seg);
     377              :         }
     378              :     }
     379              : 
     380        26417 :     for (Segment* seg : segsToRemove) {
     381              :         // Don't add duplicate segs to a measure
     382            0 :         ctx.mutDom().doUndoRemoveElement(seg);
     383              : 
     384              :         // Remove from underlying measures
     385            0 :         Measure* segMeasure = seg->measure();
     386            0 :         if (segMeasure && segMeasure->isMMRest()) {
     387            0 :             Measure* target = nullptr;
     388              :             // Segments at the start of the measure -> mmRestFirst, end -> mmRestLast
     389            0 :             if (seg->rtick() == Fraction(0, 1) || seg->tick() == segMeasure->tick()) {
     390            0 :                 target = segMeasure->mmRestFirst();
     391              :             } else {
     392            0 :                 target = segMeasure->mmRestLast();
     393              :             }
     394            0 :             if (target && target != segMeasure) {
     395            0 :                 Segment* counterpart = target->findSegmentR(seg->segmentType(), seg->rtick());
     396            0 :                 if (counterpart) {
     397            0 :                     ctx.mutDom().doUndoRemoveElement(counterpart);
     398              :                 }
     399              :             }
     400              :         }
     401              :     }
     402              : 
     403        26417 :     for (Segment* seg : segsToMoveToNextMeasure) {
     404            0 :         Fraction origRtick = seg->rtick();
     405              : 
     406            0 :         seg->undoChangeProperty(Pid::END_OF_MEASURE_CHANGE, false);
     407            0 :         measure->score()->undo(new ChangeSegmentParent(seg, nextMeasure, Fraction(0, 1)));
     408              : 
     409              :         // Move in underlying segments
     410            0 :         if (measure->isMMRest()) {
     411            0 :             Measure* fromMmLast = measure->mmRestLast();
     412            0 :             if (fromMmLast) {
     413            0 :                 Segment* underlyingSeg = fromMmLast->findSegmentR(seg->segmentType(), origRtick);
     414            0 :                 if (underlyingSeg) {
     415            0 :                     underlyingSeg->undoChangeProperty(Pid::END_OF_MEASURE_CHANGE, false);
     416            0 :                     if (nextMeasure->isMMRest() && nextMeasure->mmRestFirst()) {
     417            0 :                         nextMeasure->score()->undo(new ChangeSegmentParent(underlyingSeg, nextMeasure->mmRestFirst(), Fraction(0, 1)));
     418              :                     } else {
     419            0 :                         ctx.mutDom().doUndoRemoveElement(underlyingSeg);
     420              :                     }
     421              :                 }
     422              :             }
     423              :         }
     424              : 
     425              :         // If the destination is an MMRest but the source is not, create underlying segment
     426            0 :         if (nextMeasure->isMMRest() && !measure->isMMRest()) {
     427            0 :             Measure* mmFirst = nextMeasure->mmRestFirst();
     428            0 :             if (mmFirst && mmFirst != nextMeasure) {
     429            0 :                 Segment* mmSeg = mmFirst->undoGetSegmentR(seg->segmentType(), Fraction(0, 1));
     430            0 :                 mmSeg->undoChangeProperty(Pid::END_OF_MEASURE_CHANGE, false);
     431            0 :                 for (track_idx_t track = 0; track < ctx.dom().ntracks(); track += VOICES) {
     432            0 :                     EngravingItem* e = seg->element(track);
     433            0 :                     if (!e) {
     434            0 :                         continue;
     435              :                     }
     436            0 :                     if (!mmSeg->element(track)) {
     437            0 :                         bool generated = e->generated();
     438            0 :                         EngravingItem* eClone = generated ? e->clone() : e->linkedClone();
     439            0 :                         eClone->setGenerated(generated);
     440            0 :                         eClone->setOwnershipParent(mmSeg);
     441            0 :                         ctx.mutDom().doUndoAddElement(eClone);
     442              :                     }
     443              :                 }
     444              :             }
     445              :         }
     446              :     }
     447              : 
     448        26424 :     for (Segment* seg : segsToMoveToThisMeasure) {
     449            7 :         Fraction origRtick = seg->rtick();
     450              : 
     451            7 :         seg->undoChangeProperty(Pid::END_OF_MEASURE_CHANGE, true);
     452            7 :         measure->score()->undo(new ChangeSegmentParent(seg, measure, measure->ticks()));
     453              : 
     454              :         // Move in underlying segments
     455            7 :         if (nextMeasure->isMMRest()) {
     456            1 :             Measure* fromMmFirst = nextMeasure->mmRestFirst();
     457            1 :             if (fromMmFirst) {
     458            1 :                 Segment* underlyingSeg = fromMmFirst->findSegmentR(seg->segmentType(), origRtick);
     459            1 :                 if (underlyingSeg) {
     460            1 :                     underlyingSeg->undoChangeProperty(Pid::END_OF_MEASURE_CHANGE, true);
     461            1 :                     if (measure->isMMRest() && measure->mmRestLast()) {
     462            2 :                         measure->score()->undo(new ChangeSegmentParent(underlyingSeg, measure->mmRestLast(),
     463            1 :                                                                        measure->mmRestLast()->ticks()));
     464              :                     } else {
     465            0 :                         ctx.mutDom().doUndoRemoveElement(underlyingSeg);
     466              :                     }
     467              :                 }
     468              :             }
     469              :         }
     470              : 
     471              :         // If the destination is an MMRest but the source is not, create underlying segment
     472            7 :         if (measure->isMMRest() && !nextMeasure->isMMRest()) {
     473            1 :             Measure* mmLast = measure->mmRestLast();
     474            1 :             if (mmLast && mmLast != measure) {
     475            1 :                 Segment* mmSeg = mmLast->undoGetSegmentR(seg->segmentType(), measure->ticks());
     476            1 :                 mmSeg->undoChangeProperty(Pid::END_OF_MEASURE_CHANGE, true);
     477              :                 // Clone per-track elements from the moved segment into the underlying mmrest segment
     478            2 :                 for (track_idx_t track = 0; track < ctx.dom().ntracks(); track += VOICES) {
     479            1 :                     EngravingItem* e = seg->element(track);
     480            1 :                     if (!e) {
     481            0 :                         continue;
     482              :                     }
     483            1 :                     if (!mmSeg->element(track)) {
     484            1 :                         bool generated = e->generated();
     485            1 :                         EngravingItem* eClone = generated ? e->clone() : e->linkedClone();
     486            1 :                         eClone->setGenerated(generated);
     487            1 :                         eClone->setOwnershipParent(mmSeg);
     488            1 :                         ctx.mutDom().doUndoAddElement(eClone);
     489              :                     }
     490              :                 }
     491              :             }
     492              :         }
     493              :     }
     494              : 
     495              :     // Check end of measure changes for top and underlying measures
     496        26417 :     measure->checkEndOfMeasureChange();
     497        26417 :     if (measure->isMMRest()) {
     498          112 :         Measure* mmLast = measure->mmRestLast();
     499          112 :         if (mmLast) {
     500          112 :             mmLast->checkEndOfMeasureChange();
     501              :         }
     502              :     }
     503        26417 :     if (nextMeasure->isMMRest()) {
     504           86 :         Measure* mmFirst = nextMeasure->mmRestFirst();
     505           86 :         if (mmFirst) {
     506           86 :             mmFirst->checkEndOfMeasureChange();
     507              :         }
     508              :     }
     509              : 
     510              :     // Sort segments at start of next measure
     511        26417 :     removeAndAddBeginSegments(nextMeasure);
     512        26417 :     if (nextMeasure->isMMRest()) {
     513           86 :         Measure* mmFirst = nextMeasure->mmRestFirst();
     514           86 :         if (mmFirst) {
     515           86 :             removeAndAddBeginSegments(mmFirst);
     516              :         }
     517              :     }
     518              : 
     519              :     // Sort segments at start of first measure
     520        26417 :     Measure* prevMeasure = measure->prevMeasure();
     521        26417 :     if (prevMeasure && prevMeasure == ctx.dom().firstMeasure()) {
     522         2312 :         removeAndAddBeginSegments(prevMeasure);
     523         2312 :         if (prevMeasure->isMMRest()) {
     524            0 :             Measure* mmFirst = prevMeasure->mmRestFirst();
     525            0 :             if (mmFirst) {
     526            0 :                 removeAndAddBeginSegments(mmFirst);
     527              :             }
     528              :         }
     529              :     }
     530        26417 : }
     531              : 
     532        28815 : void ModifyDom::removeAndAddBeginSegments(Measure* measure)
     533              : {
     534        28815 :     std::vector<Segment*> segsToSort;
     535       115282 :     for (Segment& seg : measure->segments()) {
     536        86467 :         if (seg.rtick() != Fraction(0, 1) || seg.isChordRestType() || seg.isEndBarLineType()) {
     537        74533 :             continue;
     538              :         }
     539              : 
     540        11934 :         segsToSort.push_back(&seg);
     541              :     }
     542              : 
     543              :     // Re-add the segments. They will be placed in their correct positions
     544        40749 :     for (Segment* seg : segsToSort) {
     545        11934 :         measure->segments().remove(seg);
     546              :     }
     547        40749 :     for (Segment* seg : segsToSort) {
     548        11934 :         measure->add(seg);
     549              :     }
     550        28815 : }
        

Generated by: LCOV version 2.0-1