LCOV - code coverage report
Current view: top level - src/engraving/rendering/score - mmrestlayout.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 82.6 % 430 355
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) 2026 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 "mmrestlayout.h"
      23              : 
      24              : #include "dom/barline.h"
      25              : #include "dom/factory.h"
      26              : #include "dom/layoutbreak.h"
      27              : #include "dom/marker.h"
      28              : #include "dom/measure.h"
      29              : #include "dom/mmrest.h"
      30              : #include "dom/mmrestrange.h"
      31              : #include "dom/score.h"
      32              : #include "dom/segment.h"
      33              : #include "dom/staff.h"
      34              : 
      35              : #include "editing/editmeasures.h"
      36              : 
      37              : #include "measurelayout.h"
      38              : #include "tlayout.h"
      39              : 
      40              : using namespace mu::engraving;
      41              : using namespace mu::engraving::rendering::score;
      42              : 
      43              : static const std::unordered_set<ElementType> BREAK_TYPES {
      44              :     ElementType::TEMPO_TEXT,
      45              :     ElementType::REHEARSAL_MARK,
      46              :     ElementType::HARMONY,
      47              :     ElementType::STAFF_TEXT,
      48              :     ElementType::EXPRESSION,
      49              :     ElementType::DYNAMIC,
      50              :     ElementType::SYSTEM_TEXT,
      51              :     ElementType::TRIPLET_FEEL,
      52              :     ElementType::PLAYTECH_ANNOTATION,
      53              :     ElementType::CAPO,
      54              :     ElementType::INSTRUMENT_CHANGE,
      55              :     ElementType::STRING_TUNINGS,
      56              :     ElementType::SYMBOL,
      57              :     ElementType::FRET_DIAGRAM,
      58              :     ElementType::HARP_DIAGRAM,
      59              :     ElementType::PLAY_COUNT_TEXT,
      60              :     ElementType::FERMATA,
      61              : };
      62              : 
      63              : static const std::unordered_set<ElementType> ALWAYS_BREAK_TYPES {
      64              :     ElementType::TEMPO_TEXT,
      65              :     ElementType::REHEARSAL_MARK
      66              : };
      67              : 
      68              : static const std::unordered_set<ElementType> CONDITIONAL_BREAK_TYPES {
      69              :     ElementType::HARMONY,
      70              :     ElementType::STAFF_TEXT,
      71              :     ElementType::EXPRESSION,
      72              :     ElementType::DYNAMIC,
      73              :     ElementType::SYSTEM_TEXT,
      74              :     ElementType::TRIPLET_FEEL,
      75              :     ElementType::PLAYTECH_ANNOTATION,
      76              :     ElementType::CAPO,
      77              :     ElementType::INSTRUMENT_CHANGE,
      78              :     ElementType::STRING_TUNINGS,
      79              :     ElementType::SYMBOL,
      80              :     ElementType::FRET_DIAGRAM,
      81              :     ElementType::HARP_DIAGRAM,
      82              : };
      83              : 
      84              : /*
      85              :     MMRests are measures which overlay existing notation
      86              :     When they are created, any item which should be visible is moved from the underlying measure to the MMRest measure.
      87              :     Items which are invisible remain in the underlying measure.
      88              :     This means that when deciding where in the score to break MMRests, we must check both the underlying measures and any MMRests connected
      89              :     to them
      90              : */
      91              : 
      92          128 : void MMRestLayout::reuseExistingMMRest(LayoutContext& ctx, Measure* mmrMeasure, Measure* lastMeasure, Fraction len)
      93              : {
      94          128 :     if (mmrMeasure->ticks() == len) {
      95          118 :         MeasureLayout::removeSystemTrailer(mmrMeasure, ctx);
      96          118 :         return;
      97              :     }
      98              : 
      99           10 :     Fraction remainingMMRDuration = std::min(mmrMeasure->ticks() - len, ctx.dom().lastMeasure()->endTick() - len);
     100              : 
     101           10 :     Segment* barLineSeg = mmrMeasure->findSegmentR(SegmentType::EndBarLine, mmrMeasure->ticks());
     102           10 :     Segment* clefSeg = mmrMeasure->findSegmentR(SegmentType::Clef, mmrMeasure->ticks());
     103           10 :     Segment* breathSeg = mmrMeasure->findSegmentR(SegmentType::Breath, mmrMeasure->ticks());
     104              : 
     105           10 :     if (!remainingMMRDuration.negative() && remainingMMRDuration.isNotZero()) {
     106              :         // Time remaining. Create another mmrest to fill the time
     107            4 :         Measure* nextFirstMeasure = ctx.mutDom().tick2measure(lastMeasure->endTick());
     108            4 :         if (!nextFirstMeasure->mmRest()) {
     109            3 :             Measure* nextLastMeasure = ctx.mutDom().tick2measure(lastMeasure->endTick() + remainingMMRDuration - Fraction::eps());
     110            3 :             nextLastMeasure = nextLastMeasure ? nextLastMeasure : ctx.mutDom().lastMeasure();
     111            3 :             const int numMeasuresInNewMMRest = nextLastMeasure->measureIndex() - nextFirstMeasure->measureIndex() + 1;
     112              : 
     113            3 :             Measure* nextMMRMeasure = Factory::createMeasure(ctx.mutDom().score());
     114            3 :             nextMMRMeasure->setTicks(remainingMMRDuration);
     115            3 :             nextMMRMeasure->setTick(nextFirstMeasure->tick());
     116            3 :             ctx.mutDom().undo(new ChangeMMRest(nextFirstMeasure, nextMMRMeasure));
     117              : 
     118            3 :             nextMMRMeasure->setTimesig(nextFirstMeasure->timesig());
     119            3 :             nextMMRMeasure->setRepeatCount(nextLastMeasure->repeatCount());
     120            3 :             nextMMRMeasure->setMMRestCount(numMeasuresInNewMMRest);
     121            3 :             nextMMRMeasure->setMeasureNumber(nextFirstMeasure->measureNumber());
     122              : 
     123            3 :             ctx.mutDom().updateLocksOnCreateMMRest(nextFirstMeasure, nextLastMeasure);
     124              : 
     125            3 :             nextMMRMeasure->setRepeatStart(nextFirstMeasure->repeatStart() || nextLastMeasure->repeatStart());
     126            3 :             nextMMRMeasure->setRepeatEnd(nextFirstMeasure->repeatEnd() || nextLastMeasure->repeatEnd());
     127              : 
     128            3 :             if (barLineSeg) {
     129            3 :                 changeElementsParent(barLineSeg, nextMMRMeasure, remainingMMRDuration, ctx, false);
     130              :             }
     131            3 :             if (clefSeg) {
     132            0 :                 changeElementsParent(clefSeg, nextMMRMeasure, remainingMMRDuration, ctx, false);
     133              :             }
     134            3 :             if (breathSeg) {
     135            0 :                 changeElementsParent(breathSeg, nextMMRMeasure, remainingMMRDuration, ctx, false);
     136              :             }
     137              :         }
     138              :     }
     139              : 
     140              :     // adjust length
     141           10 :     mmrMeasure->setTicks(len);
     142              :     // move existing end barline, clef and breath
     143           10 :     if (barLineSeg) {
     144           10 :         barLineSeg->setRtick(len);
     145              :     }
     146           10 :     if (clefSeg) {
     147            0 :         clefSeg->setRtick(len);
     148              :     }
     149           10 :     if (breathSeg) {
     150            0 :         breathSeg->setRtick(len);
     151              :     }
     152           10 :     MeasureLayout::removeSystemTrailer(mmrMeasure, ctx);
     153              : }
     154              : 
     155              : //---------------------------------------------------------
     156              : //   createMMRest
     157              : //    create a multimeasure rest
     158              : //    from firstMeasure to lastMeasure (inclusive)
     159              : //---------------------------------------------------------
     160              : 
     161          163 : void MMRestLayout::createMMRest(LayoutContext& ctx, Measure* firstMeasure, Measure* lastMeasure, const Fraction& len)
     162              : {
     163          163 :     int numMeasuresInMMRest = 1;
     164          163 :     if (firstMeasure != lastMeasure) {
     165          453 :         for (Measure* m = firstMeasure->nextMeasure(); m; m = m->nextMeasure()) {
     166          453 :             ++numMeasuresInMMRest;
     167          453 :             m->setMMRestCount(0);
     168          453 :             if (Measure* oldMMRest = m->mmRest()) {
     169            2 :                 ctx.mutDom().undo(new ChangeMMRest(m, nullptr));
     170              :                 // These MMRests are being removed. Move all elements back to underlying measures.
     171              :                 // Any elements that need moving back to the MMRest (in lastUnderlyingMeasure) will be handled by changeMeasureElParents below
     172            2 :                 Measure* lastUnderlyingMeasure = oldMMRest->mmRestLast();
     173            2 :                 restoreMeasureElParents(m, lastUnderlyingMeasure, oldMMRest, ctx);
     174              :             }
     175          453 :             if (m == lastMeasure) {
     176          132 :                 break;
     177              :             }
     178              :         }
     179              :     }
     180              : 
     181              :     // mmrMeasure coexists with n undisplayed measures of rests
     182          163 :     Measure* mmrMeasure = firstMeasure->mmRest();
     183          163 :     if (mmrMeasure) {
     184          128 :         reuseExistingMMRest(ctx, mmrMeasure, lastMeasure, len);
     185              :     } else {
     186           35 :         mmrMeasure = Factory::createMeasure(ctx.mutDom().score());
     187           35 :         mmrMeasure->setTicks(len);
     188           35 :         mmrMeasure->setTick(firstMeasure->tick());
     189           35 :         ctx.mutDom().undo(new ChangeMMRest(firstMeasure, mmrMeasure));
     190              :     }
     191          163 :     mmrMeasure->setTimesig(firstMeasure->timesig());
     192          163 :     mmrMeasure->setRepeatCount(lastMeasure->repeatCount());
     193          163 :     mmrMeasure->setMMRestCount(numMeasuresInMMRest);
     194          163 :     mmrMeasure->setMeasureNumber(firstMeasure->measureNumber());
     195              : 
     196          163 :     ctx.mutDom().updateLocksOnCreateMMRest(firstMeasure, lastMeasure);
     197              : 
     198          163 :     mmrMeasure->setRepeatStart(firstMeasure->repeatStart() || lastMeasure->repeatStart());
     199          163 :     mmrMeasure->setRepeatEnd(firstMeasure->repeatEnd() || lastMeasure->repeatEnd());
     200              : 
     201          163 :     Segment* chordRestSeg = mmrMeasure->undoGetSegmentR(SegmentType::ChordRest, Fraction(0, 1));
     202          344 :     for (size_t staffIdx = 0; staffIdx < ctx.dom().nstaves(); ++staffIdx) {
     203          181 :         track_idx_t track = staffIdx * VOICES;
     204          181 :         EngravingItem* existing = chordRestSeg->element(track);
     205          181 :         if (!existing) {
     206           45 :             MMRest* mmr = Factory::createMMRest(chordRestSeg);
     207           45 :             mmr->setDurationType(DurationType::V_MEASURE);
     208           45 :             mmr->setTicks(mmrMeasure->ticks());
     209           45 :             mmr->setTrack(track);
     210           45 :             ctx.mutDom().doUndoAddElement(mmr);
     211          136 :         } else if (existing->isMMRest()) {
     212              :             // a reused mmrMeasure may have changed length, so keep its MM rests in sync
     213          136 :             toMMRest(existing)->setTicks(mmrMeasure->ticks());
     214              :         }
     215              :     }
     216              : 
     217          163 :     changeMeasureElParents(firstMeasure, lastMeasure, mmrMeasure, ctx);
     218              : 
     219          163 :     MeasureBase* nm = ctx.conf().isShowVBox() ? lastMeasure->next() : lastMeasure->nextMeasure();
     220          163 :     ctx.mutDom().undo(new ChangeMMRestNext(mmrMeasure, nm));
     221          163 :     ctx.mutDom().undo(new ChangeMMRestPrev(mmrMeasure, firstMeasure->prev()));
     222          163 : }
     223              : 
     224          163 : void MMRestLayout::changeMeasureElParents(Measure* firstMeasure, Measure* lastMeasure, Measure* mmrMeasure, LayoutContext& ctx)
     225              : {
     226              :     // copy markers & jumps to mmrMeasure
     227              :     // jumps come from lastMeasure, markers come from firstMeasure
     228          163 :     ElementList newList = lastMeasure->el();
     229          166 :     for (EngravingItem* e : firstMeasure->el()) {
     230            3 :         if (e->isMarker() && firstMeasure != lastMeasure) {
     231            0 :             newList.push_back(e);
     232              :         }
     233              :     }
     234          176 :     for (EngravingItem* e : newList) {
     235           13 :         ctx.mutDom().undoChangeParent(e, mmrMeasure, e->staffIdx(), false);
     236              :     }
     237              : 
     238              :     // set mmrMeasure with same barline as last underlying measure
     239          163 :     Segment* lastMeasureEndBarlineSeg = lastMeasure->findSegmentR(SegmentType::EndBarLine, lastMeasure->ticks());
     240          163 :     changeElementsParent(lastMeasureEndBarlineSeg, mmrMeasure, mmrMeasure->ticks(), ctx, false);
     241              : 
     242              :     // if last underlying measure ends with clef change, show same at end of mmrest
     243          163 :     Segment* lastMeasureClefSeg = lastMeasure->findSegmentR(SegmentType::Clef | SegmentType::HeaderClef,
     244          163 :                                                             lastMeasure->ticks());
     245          163 :     changeElementsParent(lastMeasureClefSeg, mmrMeasure, mmrMeasure->ticks(), ctx, false);
     246              : 
     247              :     // further check for clefs
     248          163 :     Segment* underlyingClefSeg = lastMeasure->findSegmentR(SegmentType::Clef, lastMeasure->ticks());
     249          163 :     Segment* mmrClefSeg = changeElementsParent(underlyingClefSeg, mmrMeasure, mmrMeasure->ticks(), ctx, false);
     250              : 
     251          163 :     if (underlyingClefSeg && mmrClefSeg) {
     252            1 :         mmrClefSeg->setEnabled(underlyingClefSeg->enabled());
     253            1 :         mmrClefSeg->setTrailer(underlyingClefSeg->trailer());
     254              :     }
     255              : 
     256              :     // check for time signature
     257          163 :     Segment* underlyingTimeSigSeg = firstMeasure->findSegmentR(SegmentType::TimeSig, Fraction(0, 1));
     258          163 :     Segment* mmrTimeSigSeg = changeElementsParent(underlyingTimeSigSeg, mmrMeasure, Fraction(0, 1), ctx, false);
     259              : 
     260          163 :     if (underlyingTimeSigSeg && mmrTimeSigSeg) {
     261           75 :         mmrTimeSigSeg->setEnabled(underlyingTimeSigSeg->enabled());
     262           75 :         mmrTimeSigSeg->setHeader(underlyingTimeSigSeg->header());
     263              :     }
     264              : 
     265              :     // check for end of measure time signature
     266          163 :     Segment* underlyingEndTimeSigSeg = lastMeasure->findSegmentR(SegmentType::TimeSig, lastMeasure->ticks());
     267          163 :     Segment* mmrEndTimeSigSeg = changeElementsParent(underlyingEndTimeSigSeg, mmrMeasure, mmrMeasure->ticks(), ctx, false);
     268              : 
     269          163 :     if (underlyingEndTimeSigSeg && mmrEndTimeSigSeg) {
     270            4 :         mmrEndTimeSigSeg->setEnabled(underlyingEndTimeSigSeg->enabled());
     271            4 :         mmrEndTimeSigSeg->setHeader(underlyingEndTimeSigSeg->header());
     272            4 :         mmrEndTimeSigSeg->setEndOfMeasureChange(underlyingEndTimeSigSeg->endOfMeasureChange());
     273              :     }
     274              : 
     275              :     // check for end of measure breaths
     276          163 :     Segment* underlyingBreathSeg = lastMeasure->findSegmentR(SegmentType::Breath, lastMeasure->ticks());
     277          163 :     changeElementsParent(underlyingBreathSeg, mmrMeasure, mmrMeasure->ticks(), ctx, false);
     278              : 
     279              :     // check for ambitus
     280          163 :     Segment* underlyingAmbitusSeg = firstMeasure->findSegmentR(SegmentType::Ambitus, Fraction(0, 1));
     281          163 :     changeElementsParent(underlyingAmbitusSeg, mmrMeasure, Fraction(0, 1), ctx, false);
     282              : 
     283              :     // check for key signature
     284          163 :     Segment* underlyingKeySigSeg = firstMeasure->findSegmentR(SegmentType::KeySig, Fraction(0, 1));
     285          163 :     Segment* mmrKeySigSeg = changeElementsParent(underlyingKeySigSeg, mmrMeasure, Fraction(0, 1), ctx, false);
     286              : 
     287          163 :     if (underlyingKeySigSeg && mmrKeySigSeg) {
     288           36 :         mmrKeySigSeg->setEnabled(underlyingKeySigSeg->enabled());
     289           36 :         mmrKeySigSeg->setHeader(underlyingKeySigSeg->header());
     290              :     }
     291              : 
     292          163 :     mmrMeasure->checkHeader();
     293          163 :     mmrMeasure->checkTrailer();
     294              : 
     295              :     // check for rehearsal mark etc.
     296          163 :     Segment* mmrChordRestSeg = mmrMeasure->undoGetSegmentR(SegmentType::ChordRest, Fraction(0, 1));
     297          163 :     Segment* underlyingCRSeg = firstMeasure->findSegmentR(SegmentType::ChordRest, Fraction(0, 1));
     298          163 :     changeAnnotationsParent(underlyingCRSeg, mmrChordRestSeg, ctx, false);
     299          163 : }
     300              : 
     301           10 : void MMRestLayout::restoreMeasureElParents(Measure* firstMeasure, Measure* lastMeasure, Measure* mmrMeasure, LayoutContext& ctx)
     302              : {
     303              :     // copy markers & jumps back to underlying measures
     304           10 :     ElementList mmrMeasureEls = mmrMeasure->el();
     305           12 :     for (EngravingItem* e : mmrMeasureEls) {
     306            2 :         Measure* newMeasure = e->isMarker() ? firstMeasure : lastMeasure;
     307            2 :         ctx.mutDom().undoChangeParent(e, newMeasure, e->staffIdx(), false);
     308              :     }
     309              : 
     310              :     // restore the barline from mmrMeasure to the last underlying measure
     311           10 :     Segment* mmrEndBarlineSeg = mmrMeasure->findSegmentR(SegmentType::EndBarLine, mmrMeasure->ticks());
     312           10 :     changeElementsParent(mmrEndBarlineSeg, lastMeasure, lastMeasure->ticks(), ctx, false);
     313              : 
     314              :     // restore clef segments from mmrMeasure to the last underlying measure
     315           10 :     Segment* mmrClefOrHeaderSeg = mmrMeasure->findSegmentR(SegmentType::Clef | SegmentType::HeaderClef, mmrMeasure->ticks());
     316           10 :     changeElementsParent(mmrClefOrHeaderSeg, lastMeasure, lastMeasure->ticks(), ctx, false);
     317              : 
     318           10 :     Segment* mmrClefSeg = mmrMeasure->findSegmentR(SegmentType::Clef, mmrMeasure->ticks());
     319           10 :     Segment* lastMeasureClefSeg = changeElementsParent(mmrClefSeg, lastMeasure, lastMeasure->ticks(), ctx, false);
     320              : 
     321           10 :     if (mmrClefSeg && lastMeasureClefSeg) {
     322            0 :         lastMeasureClefSeg->setEnabled(mmrClefSeg->enabled());
     323            0 :         lastMeasureClefSeg->setTrailer(mmrClefSeg->trailer());
     324              :     }
     325              : 
     326              :     // restore time signature segments from mmrMeasure to the first underlying measure
     327           10 :     Segment* mmrTimeSigSeg = mmrMeasure->findSegmentR(SegmentType::TimeSig, Fraction(0, 1));
     328           10 :     Segment* firstMeasureTimeSigSeg = changeElementsParent(mmrTimeSigSeg, firstMeasure, Fraction(0, 1), ctx, false);
     329              : 
     330           10 :     if (mmrTimeSigSeg && firstMeasureTimeSigSeg) {
     331            3 :         firstMeasureTimeSigSeg->setEnabled(mmrTimeSigSeg->enabled());
     332            3 :         firstMeasureTimeSigSeg->setHeader(mmrTimeSigSeg->header());
     333              :     }
     334              : 
     335              :     // restore end-of-measure time signature segment from mmrMeasure to the last underlying measure
     336           10 :     Segment* mmrEndTimeSigSeg = mmrMeasure->findSegmentR(SegmentType::TimeSig, mmrMeasure->ticks());
     337           10 :     Segment* lastMeasureEndTimeSigSeg = changeElementsParent(mmrEndTimeSigSeg, lastMeasure, lastMeasure->ticks(), ctx, false);
     338              : 
     339           10 :     if (mmrEndTimeSigSeg && lastMeasureEndTimeSigSeg) {
     340            0 :         lastMeasureEndTimeSigSeg->setEnabled(mmrEndTimeSigSeg->enabled());
     341            0 :         lastMeasureEndTimeSigSeg->setHeader(mmrEndTimeSigSeg->header());
     342            0 :         lastMeasureEndTimeSigSeg->setEndOfMeasureChange(mmrEndTimeSigSeg->endOfMeasureChange());
     343              :     }
     344              : 
     345              :     // restore breath segment from mmrMeasure to the last underlying measure
     346           10 :     Segment* mmrBreathSeg = mmrMeasure->findSegmentR(SegmentType::Breath, mmrMeasure->ticks());
     347           10 :     changeElementsParent(mmrBreathSeg, lastMeasure, lastMeasure->ticks(), ctx, false);
     348              : 
     349              :     // restore ambitus segment from mmrMeasure to the first underlying measure
     350           10 :     Segment* mmrAmbitusSeg = mmrMeasure->findSegmentR(SegmentType::Ambitus, Fraction(0, 1));
     351           10 :     changeElementsParent(mmrAmbitusSeg, firstMeasure, Fraction(0, 1), ctx, false);
     352              : 
     353              :     // restore key signature segment from mmrMeasure to the first underlying measure
     354           10 :     Segment* mmrKeySigSeg = mmrMeasure->findSegmentR(SegmentType::KeySig, Fraction(0, 1));
     355           10 :     Segment* firstMeasureKeySigSeg = changeElementsParent(mmrKeySigSeg, firstMeasure, Fraction(0, 1), ctx, false);
     356              : 
     357           10 :     if (mmrKeySigSeg && firstMeasureKeySigSeg) {
     358            5 :         firstMeasureKeySigSeg->setEnabled(mmrKeySigSeg->enabled());
     359            5 :         firstMeasureKeySigSeg->setHeader(mmrKeySigSeg->header());
     360              :     }
     361              : 
     362           10 :     firstMeasure->checkHeader();
     363           10 :     lastMeasure->checkTrailer();
     364              : 
     365              :     // restore annotations from the mmr chord/rest segment back to the first underlying measure
     366           10 :     Segment* mmrChordRestSeg = mmrMeasure->undoGetSegmentR(SegmentType::ChordRest, Fraction(0, 1));
     367           10 :     Segment* underlyingCRSeg = firstMeasure->findSegmentR(SegmentType::ChordRest, Fraction(0, 1));
     368           10 :     changeAnnotationsParent(mmrChordRestSeg, underlyingCRSeg, ctx, false);
     369           10 : }
     370              : 
     371          356 : void MMRestLayout::changeAnnotationsParent(Segment* oldParent, Segment* newParent, const LayoutContext& ctx, bool checkVisibility)
     372              : {
     373          356 :     if (!oldParent || !newParent) {
     374            0 :         return;
     375              :     }
     376          356 :     std::vector<EngravingItem*> annotations = oldParent->annotations();
     377          367 :     for (EngravingItem* e : annotations) {
     378              :         // look at elements in underlying measure
     379           11 :         if (!muse::contains(BREAK_TYPES, e->type())) {
     380            0 :             continue;
     381              :         }
     382           11 :         if (checkVisibility && (!e->visible() || !ctx.dom().staff(e->staffIdx())->show())) {
     383            0 :             continue;
     384              :         }
     385              : 
     386           11 :         if (e->isInstrumentChange()) {
     387              :             /* InstrumentChange items register their Instrument in Part::instruments(), keyed by the tick of
     388              :              * the segment they belong to (see Segment::add/remove). Re-parenting one to a segment at a different
     389              :              * tick would re-key that entry, making the instrument change take effect at the wrong time: */
     390            0 :             DO_ASSERT_X(oldParent->tick() == newParent->tick(), "Changing InstrumentChange's parent to a different tick");
     391              :         }
     392              : 
     393           11 :         e->score()->undoChangeParent(e, newParent, e->staffIdx(), false);
     394              :     }
     395          356 : }
     396              : 
     397         1387 : Segment* MMRestLayout::changeElementsParent(Segment* oldSeg, Measure* newMeasure, const Fraction& newSegTick,
     398              :                                             LayoutContext& ctx, bool checkVisibility)
     399              : {
     400              :     // Moves elements in oldSeg to newSeg at newSegTick in newMeasure. Creates a new segment if no new seg exists
     401         1387 :     if (!oldSeg) {
     402         1204 :         return nullptr;
     403              :     }
     404          183 :     Segment* newSeg = newMeasure->undoGetSegmentR(oldSeg->segmentType(), newSegTick);
     405          390 :     for (size_t staffIdx = 0; staffIdx < ctx.dom().nstaves(); ++staffIdx) {
     406          207 :         const track_idx_t track = staff2track(staffIdx);
     407          207 :         EngravingItem* el = oldSeg->element(track);
     408          207 :         if (!el || !el->visible()) {
     409           71 :             continue;
     410              :         }
     411          136 :         if (checkVisibility && (!el->visible() || !ctx.dom().staff(staffIdx)->show())) {
     412            0 :             continue;
     413              :         }
     414          136 :         ctx.mutDom().undoChangeParent(el, newSeg, el->staffIdx(), false);
     415              :     }
     416              : 
     417          183 :     changeAnnotationsParent(oldSeg, newSeg, ctx, checkVisibility);
     418              : 
     419          183 :     return newSeg;
     420              : }
     421              : 
     422              : //---------------------------------------------------------
     423              : // validMMRestMeasure
     424              : //    return true if this might be a measure in a
     425              : //    multi measure rest
     426              : //---------------------------------------------------------
     427              : 
     428         1123 : bool MMRestLayout::validMMRestMeasure(const LayoutContext& ctx, const Measure* m)
     429              : {
     430         1123 :     if (m->excludeFromNumbering()) {
     431            0 :         return false;
     432              :     }
     433              : 
     434         1123 :     size_t nstaves = ctx.dom().nstaves();
     435         2448 :     for (staff_idx_t staffIdx = 0; staffIdx < nstaves; ++staffIdx) {
     436         1329 :         if (!ctx.dom().staff(staffIdx)->show()) {
     437           66 :             continue;
     438              :         }
     439              : 
     440         1263 :         if (m->isMeasureRepeatGroup(staffIdx)) {
     441            4 :             return false;
     442              :         }
     443              :     }
     444              : 
     445         1119 :     int n = 0;
     446         2412 :     for (const Segment* s = m->first(); s; s = s->next()) {
     447         1780 :         for (const EngravingItem* e : s->annotations()) {
     448           59 :             if (e->staffIdx() >= nstaves || !e->staff()->show() || !e->visible()) {
     449            4 :                 continue;
     450              :             }
     451           55 :             if (muse::contains(BREAK_TYPES, e->type()) && !s->rtick().isZero()) {
     452              :                 // play count text and fermatas are permitted at the end of a measure
     453            0 :                 if (!e->isPlayCountText() && !e->isFermata()) {
     454            0 :                     return false;
     455              :                 }
     456              :             }
     457              :         }
     458         1721 :         size_t tracks = ctx.dom().ntracks();
     459         1721 :         if (!s->isChordRestType()) {
     460          585 :             continue;
     461              :         }
     462         1136 :         bool restFound = false;
     463         4412 :         for (track_idx_t track = 0; track < tracks; ++track) {
     464         3700 :             if ((track % VOICES) == 0 && !ctx.dom().staff(track / VOICES)->show()) {
     465           60 :                 track += VOICES - 1;
     466           60 :                 continue;
     467              :             }
     468         3640 :             if (!s->element(track)) {
     469         2412 :                 continue;
     470              :             }
     471         1228 :             if (!s->element(track)->isRest()) {
     472          424 :                 return false;
     473              :             } else {
     474          804 :                 bool isNonEmptyIrregular = m->isIrregular() && !toRest(s->element(track))->isFullMeasureRest();
     475          804 :                 if (isNonEmptyIrregular) {
     476            0 :                     return false;
     477              :                 }
     478              :             }
     479          804 :             restFound = true;
     480              :         }
     481          722 :         for (const EngravingItem* e : s->annotations()) {
     482           10 :             if (e->isFermata()) {
     483            0 :                 return false;
     484              :             }
     485              :         }
     486          712 :         if (restFound) {
     487          700 :             ++n;
     488              :         }
     489              :         // measure is not empty if there is more than one rest
     490          712 :         if (n > 1) {
     491            4 :             return false;
     492              :         }
     493              :     }
     494          691 :     return true;
     495              : }
     496              : 
     497          513 : bool MMRestLayout::breakMMRForElement(Measure* measure, Measure* prevMeasure, const LayoutContext& ctx)
     498              : {
     499           10 :     auto breakForAnnotation = [&](EngravingItem* e) {
     500           10 :         if (muse::contains(ALWAYS_BREAK_TYPES, e->type())) {
     501            0 :             return true;
     502              :         }
     503           10 :         bool breakForElement = e->systemFlag() || e->staff()->show();
     504           10 :         if (muse::contains(CONDITIONAL_BREAK_TYPES, e->type()) && breakForElement) {
     505           10 :             return true;
     506              :         }
     507            0 :         return false;
     508              :     };
     509              : 
     510              :     // break for marker in this measure
     511          530 :     for (EngravingItem* e : measure->el()) {
     512           17 :         if (e->isMarker()) {
     513            0 :             Marker* mark = toMarker(e);
     514            0 :             if (!(mark->align() == AlignH::RIGHT)) {
     515            0 :                 return true;
     516              :             }
     517              :         }
     518              :     }
     519              : 
     520         1215 :     for (Segment* s = measure->first(); s; s = s->next()) {
     521          731 :         for (EngravingItem* e : s->annotations()) {
     522           10 :             if (!e->visible()) {
     523            0 :                 continue;
     524              :             }
     525           10 :             if (breakForAnnotation(e)) {
     526           10 :                 return true;
     527              :             }
     528              :         }
     529         1602 :         for (size_t staffIdx = 0; staffIdx < ctx.dom().nstaves(); ++staffIdx) {
     530          900 :             if (!ctx.dom().staff(staffIdx)->show()) {
     531           48 :                 continue;
     532              :             }
     533          852 :             EngravingItem* e = s->element(staffIdx * VOICES);
     534          852 :             if (!e || e->generated()) {
     535          273 :                 continue;
     536              :             }
     537          579 :             if (s->isStartRepeatBarLineType()) {
     538            0 :                 return true;
     539              :             }
     540          579 :             if (s->isType(SegmentType::KeySig | SegmentType::TimeSig) && measure->tick().isNotZero()) {
     541           19 :                 return true;
     542              :             }
     543          560 :             if (s->isClefType()) {
     544            0 :                 if (s->tick() != measure->endTick() && measure->tick().isNotZero()) {
     545            0 :                     return true;
     546              :                 }
     547              :             }
     548              :         }
     549              :     }
     550              : 
     551          484 :     if (!prevMeasure) {
     552            0 :         return false;
     553              :     }
     554              : 
     555              :     // break for marker & jump in previous measure
     556          484 :     for (EngravingItem* e : prevMeasure->el()) {
     557            3 :         if (e->isJump()) {
     558            3 :             return true;
     559            0 :         } else if (e->isMarker()) {
     560            0 :             Marker* mark = toMarker(e);
     561            0 :             if (mark->align() == AlignH::RIGHT) {
     562            0 :                 return true;
     563              :             }
     564              :         }
     565              :     }
     566              : 
     567              :     // Break for breaths and annotations found mid-way into the previous measure
     568         1258 :     for (Segment* s = prevMeasure->first(); s; s = s->next()) {
     569         1756 :         for (size_t staffIdx = 0; staffIdx < ctx.dom().nstaves(); ++staffIdx) {
     570          979 :             if (!ctx.dom().staff(staffIdx)->show()) {
     571           54 :                 continue;
     572              :             }
     573          925 :             EngravingItem* e = s->element(staffIdx * VOICES);
     574          925 :             if (!e || e->generated()) {
     575          318 :                 continue;
     576              :             }
     577          607 :             if (s->isBreathType()) {
     578            2 :                 return true;
     579              :             }
     580              :         }
     581              : 
     582          785 :         for (EngravingItem* e : s->annotations()) {
     583            8 :             if (!e->visible()) {
     584            0 :                 continue;
     585              :             }
     586            8 :             if (e->rtick() == Fraction(0, 1)) {
     587            8 :                 continue;
     588              :             }
     589            0 :             if (breakForAnnotation(e) || e->isFermata()) {
     590            0 :                 return true;
     591              :             }
     592              :         }
     593              :     }
     594              : 
     595              :     // Break for non-normal end barline in previous measure
     596          479 :     Segment* endBarSeg = prevMeasure->findSegmentR(SegmentType::EndBarLine, prevMeasure->ticks());
     597          479 :     if (endBarSeg) {
     598          160 :         for (size_t staffIdx = 0; staffIdx < ctx.dom().nstaves(); ++staffIdx) {
     599          123 :             BarLine* bl = toBarLine(endBarSeg->element(staffIdx * VOICES));
     600          123 :             if (!bl) {
     601           37 :                 continue;
     602              :             }
     603           86 :             BarLineType t = bl->barLineType();
     604           86 :             if (t != BarLineType::NORMAL && t != BarLineType::BROKEN && t != BarLineType::DOTTED && !bl->generated()) {
     605            0 :                 return true;
     606              :             } else {
     607           86 :                 break;
     608              :             }
     609              :         }
     610              :     }
     611              : 
     612              :     // Break for courtesy clefs/sigs at the end of the previous measure,
     613              :     // only if the element is on a visible staff
     614            0 :     auto hasVisibleElement = [&ctx](Segment* seg) -> bool {
     615            0 :         for (size_t staffIdx = 0; staffIdx < ctx.dom().nstaves(); ++staffIdx) {
     616            0 :             if (!ctx.dom().staff(staffIdx)->show()) {
     617            0 :                 continue;
     618              :             }
     619            0 :             EngravingItem* e = seg->element(staffIdx * VOICES);
     620            0 :             if (e && !e->generated()) {
     621            0 :                 return true;
     622              :             }
     623              :         }
     624            0 :         return false;
     625          479 :     };
     626              : 
     627          479 :     if (Segment* clefSeg = prevMeasure->findSegment(SegmentType::Clef, measure->tick())) {
     628            0 :         if (hasVisibleElement(clefSeg)) {
     629            0 :             return true;
     630              :         }
     631              :     }
     632          479 :     if (Segment* tsSeg = prevMeasure->findSegment(SegmentType::TimeSigTypes, measure->tick())) {
     633            0 :         if (hasVisibleElement(tsSeg)) {
     634            0 :             return true;
     635              :         }
     636              :     }
     637          479 :     if (Segment* ksSeg = prevMeasure->findSegment(SegmentType::KeySigTypes, measure->tick())) {
     638            0 :         if (hasVisibleElement(ksSeg)) {
     639            0 :             return true;
     640              :         }
     641              :     }
     642              : 
     643          479 :     return false;
     644              : }
     645              : 
     646              : //---------------------------------------------------------
     647              : //  breakMultiMeasureRest
     648              : //    return true if this measure should start a new
     649              : //    multi measure rest
     650              : //---------------------------------------------------------
     651              : 
     652          512 : bool MMRestLayout::breakMultiMeasureRest(const LayoutContext& ctx, Measure* m)
     653              : {
     654          512 :     if (m->breakMultiMeasureRest()) {
     655           16 :         return true;
     656              :     }
     657              : 
     658          496 :     if (m->repeatStart()
     659          496 :         || (m->prevMeasure() && m->prevMeasure()->repeatEnd())
     660          494 :         || (m->isIrregular())
     661          494 :         || (m->prevMeasure() && m->prevMeasure()->isIrregular())
     662          992 :         || (m->prevMeasure() && (m->prevMeasure()->sectionBreak()))) {
     663            2 :         return true;
     664              :     }
     665              : 
     666              :     static const std::set<ElementType> breakSpannerTypes {
     667              :         ElementType::VOLTA,
     668              :         ElementType::GRADUAL_TEMPO_CHANGE,
     669              :         ElementType::TEXTLINE,
     670          500 :     };
     671              :     // Break for spanners/textLines in this measure
     672          494 :     auto sl = ctx.dom().spannerMap().findOverlapping(m->tick().ticks(), m->endTick().ticks());
     673          506 :     for (auto i : sl) {
     674           20 :         Spanner* s = i.value;
     675           20 :         if (!s->visible()) {
     676            0 :             continue;
     677              :         }
     678           20 :         Fraction spannerStart = s->tick();
     679           20 :         Fraction spannerEnd = s->tick2();
     680           20 :         Fraction measureStart = m->tick();
     681           20 :         Fraction measureEnd = m->endTick();
     682           20 :         bool spannerStartsInside = spannerStart >= measureStart && spannerStart < measureEnd;
     683           20 :         bool spannerEndsInside = spannerEnd >= measureStart && spannerEnd < measureEnd;
     684           20 :         if (muse::contains(breakSpannerTypes, s->type()) && (spannerStartsInside || spannerEndsInside)) {
     685            8 :             return true;
     686              :         }
     687              :     }
     688              :     // Break for spanners/textLines starting or ending mid-way inside the *previous* measure
     689          486 :     Measure* prevMeas = m->prevMeasure();
     690          486 :     if (prevMeas) {
     691          486 :         auto prevMeasSpanners = ctx.dom().spannerMap().findOverlapping(prevMeas->tick().ticks(), prevMeas->endTick().ticks());
     692          500 :         for (auto i : prevMeasSpanners) {
     693           14 :             Spanner* s = i.value;
     694           14 :             if (!s->visible()) {
     695            0 :                 continue;
     696              :             }
     697           14 :             Fraction spannerStart = s->tick();
     698           14 :             Fraction spannerEnd = s->tick2();
     699           14 :             Fraction measureStart = prevMeas->tick();
     700           14 :             Fraction measureEnd = prevMeas->endTick();
     701           14 :             bool spannerStartsInside = spannerStart > measureStart && spannerStart < measureEnd;
     702           14 :             bool spannerEndsInside = spannerEnd > measureStart && spannerEnd < measureEnd;
     703           14 :             if (muse::contains(breakSpannerTypes, s->type()) && (spannerStartsInside || spannerEndsInside)) {
     704            0 :                 return true;
     705              :             }
     706              :         }
     707          486 :     }
     708              : 
     709          486 :     bool breakRest = breakMMRForElement(m, prevMeas, ctx);
     710              : 
     711          486 :     Measure* mmrest = m->mmRest();
     712          486 :     Measure* mmrestPrev = mmrest ? m->mmRest()->prevMeasureMM() : nullptr;
     713              : 
     714              :     // If we are creating MMRests from expanded notation, checking underlying measures is sufficient.
     715              :     // If we are updating MMRests which are already being shown (any layout update) we must also check that the elements dictating their boundaries are still present
     716              :     // This is because elements are moved between underlying and covering measures
     717          486 :     if (mmrest && (mmrest->mmRestFirst() == m || mmrest->mmRestLast() == m)) {
     718           27 :         breakRest |= breakMMRForElement(mmrest, mmrestPrev, ctx);
     719              :     }
     720              : 
     721          486 :     return breakRest;
     722          494 : }
     723              : 
     724        30511 : void MMRestLayout::createMultiMeasureRestsIfNeed(Measure* firstMeasure, LayoutContext& ctx)
     725              : {
     726              :     LAYOUT_CALL() << LAYOUT_ITEM_INFO(firstMeasure);
     727              : 
     728        30511 :     if (ctx.dom().nstaves() == 0) {
     729           96 :         return;
     730              :     }
     731              : 
     732        30415 :     int mn = ctx.state().measureNumber();
     733              : 
     734        30415 :     if (ctx.conf().styleB(Sid::createMultiMeasureRests)) {
     735          546 :         Measure* measureToBeChecked = firstMeasure;
     736          546 :         Measure* lastMeasure = measureToBeChecked;
     737          546 :         int n       = 0;
     738          546 :         Fraction len;
     739              : 
     740         1123 :         while (validMMRestMeasure(ctx, measureToBeChecked)) {
     741          691 :             if (n && breakMultiMeasureRest(ctx, measureToBeChecked)) {
     742           59 :                 break;
     743              :             }
     744          632 :             if (measureToBeChecked != firstMeasure) {
     745          453 :                 int measureNumber = MeasureLayout::adjustMeasureNumber(measureToBeChecked, ctx.state().measureNumber());
     746          453 :                 ctx.mutState().setMeasureNumber(measureNumber);
     747              :             }
     748          632 :             ++n;
     749          632 :             len += measureToBeChecked->ticks();
     750          632 :             lastMeasure = measureToBeChecked;
     751          632 :             MeasureBase* nextMeasureBase = ctx.conf().isShowVBox() ? measureToBeChecked->next() : measureToBeChecked->nextMeasure();
     752          632 :             if (!(nextMeasureBase && nextMeasureBase->isMeasure())) {
     753           55 :                 break;
     754              :             }
     755          577 :             measureToBeChecked = toMeasure(nextMeasureBase);
     756              :         }
     757              : 
     758          546 :         if (n >= ctx.conf().styleI(Sid::minEmptyMeasures)) {
     759          163 :             createMMRest(ctx, firstMeasure, lastMeasure, len);
     760          163 :             ctx.mutState().setCurMeasure(firstMeasure->mmRest());
     761          163 :             ctx.mutState().setNextMeasure(ctx.conf().isShowVBox() ? lastMeasure->next() : lastMeasure->nextMeasure());
     762              :         } else {
     763          383 :             if (firstMeasure->mmRest()) {
     764            3 :                 removeMMRestElements(firstMeasure->mmRest(), ctx);
     765            3 :                 ctx.mutDom().undo(new ChangeMMRest(firstMeasure, nullptr));
     766              :             }
     767          383 :             ctx.mutState().setMeasureNumber(mn);
     768              :         }
     769        29869 :     } else if (firstMeasure->mmRest()) {
     770            5 :         removeMMRestElements(firstMeasure->mmRest(), ctx);
     771              : 
     772            5 :         if (firstMeasure->mmRestCount() > 0) {
     773            0 :             LOGD("mmrest: measureNumber %d += %d", ctx.state().measureNumber(), firstMeasure->mmRestCount());
     774            0 :             int measureNumber = ctx.state().measureNumber() + firstMeasure->mmRestCount() - 1;
     775            0 :             ctx.mutState().setMeasureNumber(measureNumber);
     776              :         }
     777              :     }
     778              : }
     779              : 
     780            8 : void MMRestLayout::removeMMRestElements(Measure* mmrMeasure, LayoutContext& ctx)
     781              : {
     782              :     // Move elements from MMRest measure back to underlying
     783            8 :     Measure* firstMeasure = mmrMeasure->mmRestFirst();
     784            8 :     Measure* lastMeasure = mmrMeasure->mmRestLast();
     785            8 :     IF_ASSERT_FAILED(firstMeasure && lastMeasure) {
     786            0 :         return;
     787              :     }
     788              : 
     789            8 :     restoreMeasureElParents(firstMeasure, lastMeasure, mmrMeasure, ctx);
     790              : }
     791              : 
     792        31790 : void MMRestLayout::layoutMMRestRange(Measure* m, LayoutContext& ctx)
     793              : {
     794        31790 :     if (!m->isMMRest() || !ctx.conf().styleB(Sid::mmRestShowMeasureNumberRange)) {
     795              :         // Remove existing
     796        78630 :         for (unsigned staffIdx = 0; staffIdx < m->mstaves().size(); ++staffIdx) {
     797        46840 :             const MStaff* ms = m->mstaves().at(staffIdx);
     798        46840 :             MMRestRange* rr = ms->mmRangeText();
     799        46840 :             if (rr) {
     800            0 :                 ctx.mutDom().doUndoRemoveElement(rr);
     801              :             }
     802              :         }
     803              : 
     804        31790 :         return;
     805              :     }
     806              : 
     807            0 :     String s;
     808            0 :     if (m->mmRestCount() > 1) {
     809              :         // middle char is an en dash (not em)
     810            0 :         s = String(u"%1–%2").arg(m->measureNumber() + 1).arg(m->measureNumber() + m->mmRestCount());
     811              :     } else {
     812              :         // If the minimum range to create a mmrest is set to 1,
     813              :         // then simply show the measure number as there is no range
     814            0 :         s = String::number(m->measureNumber() + 1);
     815              :     }
     816              : 
     817            0 :     for (unsigned staffIdx = 0; staffIdx < m->mstaves().size(); ++staffIdx) {
     818            0 :         const MStaff* ms = m->mstaves().at(staffIdx);
     819            0 :         MMRestRange* rr = ms->mmRangeText();
     820            0 :         if (!rr) {
     821            0 :             rr = new MMRestRange(m);
     822            0 :             rr->setTrack(staffIdx * VOICES);
     823            0 :             rr->setGenerated(true);
     824            0 :             m->add(rr);
     825              :         }
     826              :         // setXmlText is reimplemented to take care of brackets
     827            0 :         rr->setXmlText(s);
     828            0 :         TLayout::layoutMMRestRange(rr, rr->mutldata(), ctx);
     829              :     }
     830            0 : }
        

Generated by: LCOV version 2.0-1