LCOV - code coverage report
Current view: top level - src/engraving/editing - editmeasures.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 83.4 % 217 181
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) 2021 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              : 
      23              : #include "editmeasures.h"
      24              : 
      25              : #include "../dom/chord.h"
      26              : #include "../dom/chordrest.h"
      27              : #include "../dom/clef.h"
      28              : #include "../dom/engravingitem.h"
      29              : #include "../dom/instrchange.h"
      30              : #include "../dom/key.h"
      31              : #include "../dom/keysig.h"
      32              : #include "../dom/measure.h"
      33              : #include "../dom/note.h"
      34              : #include "../dom/page.h"
      35              : #include "../dom/part.h"
      36              : #include "../dom/repeatlist.h"
      37              : #include "../dom/score.h"
      38              : #include "../dom/segment.h"
      39              : #include "../dom/spanner.h"
      40              : #include "../dom/staff.h"
      41              : #include "../dom/stafftypechange.h"
      42              : #include "../dom/system.h"
      43              : #include "../dom/tie.h"
      44              : #include "../dom/utils.h"
      45              : 
      46              : #include "log.h"
      47              : 
      48              : using namespace muse;
      49              : using namespace mu::engraving;
      50              : 
      51              : //---------------------------------------------------------
      52              : //   getCourtesyClefs
      53              : //    remember clefs at the end of previous measure
      54              : //---------------------------------------------------------
      55              : 
      56          396 : std::vector<Clef*> InsertRemoveMeasures::getCourtesyClefs(Measure* m)
      57              : {
      58          396 :     Score* score = m->score();
      59          396 :     std::vector<Clef*> startClefs;
      60          396 :     if (m->prev() && m->prev()->isMeasure()) {
      61          288 :         Measure* prevMeasure = toMeasure(m->prev());
      62          288 :         const Segment* clefSeg = prevMeasure->findSegmentR(SegmentType::Clef | SegmentType::HeaderClef, prevMeasure->ticks());
      63          288 :         if (clefSeg) {
      64            8 :             for (size_t st = 0; st < score->nstaves(); ++st) {
      65            4 :                 EngravingItem* clef = clefSeg->element(staff2track(static_cast<int>(st)));
      66            4 :                 if (clef && clef->isClef()) {
      67            4 :                     startClefs.push_back(toClef(clef));
      68              :                 }
      69              :             }
      70              :         }
      71              :     }
      72          396 :     return startClefs;
      73            0 : }
      74              : 
      75              : //---------------------------------------------------------
      76              : //   insertMeasures
      77              : //---------------------------------------------------------
      78              : 
      79          382 : void InsertRemoveMeasures::insertMeasures()
      80              : {
      81          382 :     Score* score = fm->score();
      82              : 
      83          382 :     std::vector<Clef*> clefs;
      84          382 :     std::vector<Clef*> prevMeasureClefs;
      85          382 :     std::vector<KeySig*> keys;
      86          382 :     Segment* fs = nullptr;
      87          382 :     Segment* ls = nullptr;
      88          382 :     std::vector<RepeatSegmentInfo> oldSegments;
      89          382 :     if (fm->isMeasure()) {
      90              :         // Snapshot before the structural edit below invalidates it
      91          316 :         oldSegments = score->repeatSegmentInfoList(/*expandRepeats*/ true);
      92              : 
      93          316 :         score->invalidateRepeatList();
      94          316 :         fs = toMeasure(fm)->first();
      95          316 :         ls = toMeasure(lm)->last();
      96          378 :         for (Segment* s = fs; s && s != ls; s = s->next1()) {
      97           62 :             if (!s->enabled() || !(s->segmentType() & (SegmentType::Clef | SegmentType::HeaderClef | SegmentType::KeySig))) {
      98           54 :                 continue;
      99              :             }
     100           18 :             for (track_idx_t track = 0; track < score->ntracks(); track += VOICES) {
     101           10 :                 EngravingItem* e = s->element(track);
     102           10 :                 if (!e || e->generated()) {
     103            5 :                     continue;
     104              :                 }
     105            5 :                 if (e->isClef()) {
     106            4 :                     clefs.push_back(toClef(e));
     107            1 :                 } else if (e->isKeySig()) {
     108            1 :                     keys.push_back(toKeySig(e));
     109              :                 }
     110              :             }
     111              :         }
     112          316 :         prevMeasureClefs = getCourtesyClefs(toMeasure(fm));
     113              :     }
     114          382 :     score->measures()->insert(fm, lm);
     115              : 
     116          382 :     if (fm->isMeasure()) {
     117          316 :         score->updateTicksAndTimeSigMap();
     118          316 :         score->insertTime(fm->tick(), lm->endTick() - fm->tick(), oldSegments);
     119              : 
     120              :         // move ownership of Instrument back to part
     121          378 :         for (Segment* s = fs; s && s != ls; s = s->next1()) {
     122           62 :             for (EngravingItem* e : s->annotations()) {
     123            0 :                 if (e->isInstrumentChange()) {
     124            0 :                     e->part()->setInstrument(toInstrumentChange(e)->instrument(), s->tick());
     125              :                 }
     126              :             }
     127              :         }
     128          318 :         for (Clef* clef : prevMeasureClefs) {
     129            2 :             clef->staff()->setClef(clef);
     130              :         }
     131          320 :         for (Clef* clef : clefs) {
     132            4 :             clef->staff()->setClef(clef);
     133              :         }
     134          317 :         for (KeySig* key : keys) {
     135            1 :             key->staff()->setKey(key->segment()->tick(), key->keySigEvent());
     136              :         }
     137              :     }
     138              : 
     139          382 :     score->setLayoutAll();
     140              : 
     141              :     // move subsequent StaffTypeChanges
     142          382 :     Fraction tickStart = fm->tick();
     143          382 :     Fraction tickEnd = lm->endTick();
     144          382 :     if (moveStc && tickEnd > tickStart) {
     145          574 :         for (Staff* staff : score->staves()) {
     146              :             // loop backwards until the insert point
     147          318 :             auto stRange = staff->staffTypeRange(score->lastMeasure()->tick());
     148          318 :             int moveTick = stRange.first;
     149              : 
     150          318 :             while (moveTick >= tickStart.ticks() && moveTick > 0) {
     151            0 :                 Fraction tick = Fraction::fromTicks(moveTick);
     152            0 :                 Fraction tickNew = tick + tickEnd - tickStart;
     153              : 
     154              :                 // measures were inserted already so icon is at differnt place, as staffTypeChange itslef
     155            0 :                 Measure* measure = score->tick2measure(tickNew);
     156            0 :                 bool stIcon = false;
     157              : 
     158            0 :                 staff->moveStaffType(tick, tickNew);
     159              : 
     160            0 :                 for (EngravingItem* el : measure->el()) {
     161            0 :                     if (el && el->isStaffTypeChange() && el->track() == staff->idx() * VOICES) {
     162            0 :                         StaffTypeChange* stc = toStaffTypeChange(el);
     163            0 :                         stc->setStaffType(staff->staffType(tickNew), false);
     164            0 :                         stIcon = true;
     165            0 :                         break;
     166              :                     }
     167              :                 }
     168              : 
     169            0 :                 if (!stIcon) {
     170            0 :                     LOGD() << "StaffTypeChange icon is missing in measure " << measure->measureNumber();
     171              :                 }
     172              : 
     173            0 :                 stRange = staff->staffTypeRange(tick);
     174            0 :                 moveTick = stRange.first;
     175              :             }
     176              :         }
     177              :     }
     178              : 
     179              :     //
     180              :     // connect ties
     181              :     //
     182              : 
     183          382 :     if (!fm->isMeasure() || !fm->prevMeasure()) {
     184          130 :         return;
     185              :     }
     186          252 :     Measure* m = fm->prevMeasure();
     187         1789 :     for (Segment* seg = m->first(); seg; seg = seg->next()) {
     188         8149 :         for (track_idx_t track = 0; track < score->ntracks(); ++track) {
     189         6612 :             EngravingItem* e = seg->element(track);
     190         6612 :             if (!e || !e->isChord()) {
     191         5495 :                 continue;
     192              :             }
     193         1117 :             Chord* chord = toChord(e);
     194         2244 :             for (Note* n : chord->notes()) {
     195         1127 :                 Tie* tie = n->tieForNonPartial();
     196         1127 :                 if (!tie) {
     197         1107 :                     continue;
     198              :                 }
     199           20 :                 if (!tie->endNote() || tie->endNote()->chord()->segment()->measure() != m) {
     200           10 :                     Note* nn = searchTieNote(n);
     201           10 :                     if (nn) {
     202            0 :                         tie->setEndNote(nn);
     203            0 :                         nn->setTieBack(tie);
     204              :                     }
     205              :                 }
     206              :             }
     207              :         }
     208              :     }
     209          772 : }
     210              : 
     211              : //---------------------------------------------------------
     212              : //   removeMeasures
     213              : //---------------------------------------------------------
     214              : 
     215           80 : void InsertRemoveMeasures::removeMeasures()
     216              : {
     217           80 :     Score* score = fm->score();
     218              : 
     219           80 :     Fraction tick1 = fm->tick();
     220           80 :     Fraction tick2 = lm->endTick();
     221              : 
     222              :     // Snapshot before the structural edit below invalidates it
     223           80 :     std::vector<RepeatSegmentInfo> oldSegments;
     224           80 :     if (fm->isMeasure()) {
     225           80 :         oldSegments = score->repeatSegmentInfoList(/*expandRepeats*/ true);
     226              :     }
     227              : 
     228           80 :     if (fm->isMeasure() && lm->isMeasure()) {
     229              :         // remove beams from chordrests in affected area, they will be rebuilt later but we need
     230              :         // to avoid situations where notes from deleted measures remain in beams
     231              :         // when undoing, we need to check the previous measure as well as there could be notes in there
     232              :         // that need to have their beams recalculated (esp. when adding time signature)
     233           80 :         MeasureBase* prev = fm->prev();
     234           80 :         Segment* first = toMeasure(prev && prev->isMeasure() ? prev : fm)->first();
     235          831 :         for (Segment* s = first; s && s != toMeasure(lm)->last(); s = s->next1()) {
     236          751 :             if (!s) {
     237            0 :                 break;
     238              :             }
     239          751 :             if (!s->isChordRestType()) {
     240          306 :                 continue;
     241              :             }
     242              : 
     243         2545 :             for (track_idx_t track = 0; track < score->ntracks(); ++track) {
     244         2100 :                 EngravingItem* e = s->element(track);
     245         2100 :                 if (e && e->isChordRest()) {
     246          493 :                     toChordRest(e)->removeDeleteBeam(false);
     247              :                 }
     248              :             }
     249              :         }
     250              :     }
     251              : 
     252           80 :     std::vector<System*> systemList;
     253          139 :     for (MeasureBase* mb = lm;; mb = mb->prev()) {
     254          139 :         System* system = mb->system();
     255          139 :         if (system) {
     256          127 :             if (!muse::contains(systemList, system)) {
     257           73 :                 systemList.push_back(system);
     258              :             }
     259          127 :             system->removeMeasure(mb);
     260              :         }
     261          139 :         if (mb == fm) {
     262           80 :             break;
     263              :         }
     264           59 :     }
     265              : 
     266              :     // move subsequent StaffTypeChanges
     267           80 :     if (moveStc) {
     268          115 :         for (Staff* staff : score->staves()) {
     269           62 :             Fraction tickStart = tick1;
     270           62 :             Fraction tickEnd = tick2;
     271              : 
     272              :             // loop trhu, until the last one
     273           62 :             auto stRange = staff->staffTypeRange(tickEnd);
     274           62 :             int moveTick = stRange.first == tickEnd.ticks() ? stRange.first : stRange.second;
     275              : 
     276           62 :             while (moveTick != -1) {
     277            0 :                 Fraction tick = Fraction::fromTicks(moveTick);
     278            0 :                 Fraction newTick = tick + tickStart - tickEnd;
     279              : 
     280            0 :                 Measure* measure = score->tick2measure(tick);
     281            0 :                 bool stIcon = false;
     282              : 
     283            0 :                 staff->moveStaffType(tick, newTick);
     284              : 
     285            0 :                 for (EngravingItem* el : measure->el()) {
     286            0 :                     if (el && el->isStaffTypeChange() && el->track() == staff->idx() * VOICES) {
     287            0 :                         StaffTypeChange* stc = toStaffTypeChange(el);
     288            0 :                         stc->setStaffType(staff->staffType(newTick), false);
     289            0 :                         stIcon = true;
     290            0 :                         break;
     291              :                     }
     292              :                 }
     293              : 
     294            0 :                 if (!stIcon) {
     295            0 :                     LOGD() << "StaffTypeChange icon is missing in measure " << measure->measureNumber();
     296              :                 }
     297              : 
     298            0 :                 stRange = staff->staffTypeRange(tick);
     299            0 :                 moveTick = stRange.second;
     300              :             }
     301              :         }
     302              :     }
     303              : 
     304           80 :     score->measures()->remove(fm, lm);
     305              : 
     306           80 :     if (fm->isMeasure()) {
     307           80 :         score->updateTicksAndTimeSigMap();
     308           80 :         score->invalidateRepeatList();
     309              : 
     310              :         // check if there is a clef at the end of last measure
     311              :         // remove clef from staff cleflist
     312              : 
     313           80 :         if (lm->isMeasure()) {
     314           80 :             Measure* m = toMeasure(lm);
     315           80 :             Segment* s = m->findSegment(SegmentType::Clef, tick2);
     316           80 :             if (s) {
     317            5 :                 for (EngravingItem* e : s->elist()) {
     318            4 :                     Clef* clef = toClef(e);
     319            4 :                     if (clef) {
     320            1 :                         score->staff(clef->staffIdx())->removeClef(clef);
     321              :                     }
     322              :                 }
     323              :             }
     324              :         }
     325              : 
     326              :         // remember clefs at the end of previous measure
     327           80 :         const auto clefs = getCourtesyClefs(toMeasure(fm));
     328              : 
     329           80 :         score->insertTime(tick1, -(tick2 - tick1), oldSegments);
     330              : 
     331              :         // Restore clefs that were backed up. Events for them could be lost
     332              :         // as a result of the recent insertTime() call.
     333           82 :         for (Clef* clef : clefs) {
     334            2 :             clef->staff()->setClef(clef);
     335              :         }
     336              : 
     337           80 :         std::set<Spanner*> spannersCopy = score->unmanagedSpanners();
     338           84 :         for (Spanner* sp : spannersCopy) {
     339            4 :             if ((sp->tick() >= tick1 && sp->tick() < tick2) || (sp->tick2() >= tick1 && sp->tick2() < tick2)) {
     340            3 :                 sp->removeUnmanaged();
     341              :             }
     342              :         }
     343           80 :     }
     344              : 
     345              :     // remove empty systems
     346              : 
     347          153 :     for (System* s : systemList) {
     348           73 :         if (s->measures().empty()) {
     349           24 :             Page* page = s->page();
     350           24 :             if (page) {
     351              :                 // erase system from page
     352           24 :                 muse::remove(page->systems(), s);
     353           24 :                 s->setPage(nullptr);
     354              :             }
     355              :             // erase system from score
     356           24 :             muse::remove(score->systems(), s);
     357              :             // finally delete system
     358           24 :             s->deleteLater();
     359              : 
     360           24 :             if (page && page->systems().empty()) {
     361              :                 // if page is empty, delete it as well
     362           10 :                 muse::remove(score->pages(), page);
     363           10 :                 page->deleteLater();
     364              :             }
     365              :         }
     366              :     }
     367              : 
     368           80 :     score->setLayoutAll();
     369           80 : }
     370              : 
     371              : //---------------------------------------------------------
     372              : //   ChangeMeasureLen
     373              : //---------------------------------------------------------
     374              : 
     375           69 : ChangeMeasureLen::ChangeMeasureLen(Measure* m, Fraction l)
     376              : {
     377           69 :     measure     = m;
     378           69 :     len         = l;
     379           69 : }
     380              : 
     381           79 : void ChangeMeasureLen::flip()
     382              : {
     383           79 :     Fraction oLen = measure->ticks();
     384              : 
     385              :     //
     386              :     // move EndBarLine and TimeSigAnnounce
     387              :     // to end of measure:
     388              :     //
     389              : 
     390          261 :     for (Segment* s = measure->first(); s; s = s->next()) {
     391          182 :         if (!s->isEndBarLineType() && !s->isTimeSigAnnounceType()) {
     392          163 :             continue;
     393              :         }
     394           19 :         s->setRtick(len);
     395           19 :         measure->remove(s);
     396              :     }
     397           79 :     measure->setTicks(len);
     398           79 :     measure->score()->updateTicksAndTimeSigMap();
     399           79 :     measure->score()->invalidateRepeatList();
     400           79 :     len = oLen;
     401           79 : }
     402              : 
     403              : //---------------------------------------------------------
     404              : //   ChangeMMRest
     405              : //---------------------------------------------------------
     406              : 
     407           44 : void ChangeMMRest::flip()
     408              : {
     409           44 :     Measure* mmr = m->mmRest();
     410           44 :     m->setMMRest(mmrest);
     411           44 :     mmrest = mmr;
     412           44 : }
     413              : 
     414              : //---------------------------------------------------------
     415              : //   ChangeMeasureRepeatCount
     416              : //---------------------------------------------------------
     417              : 
     418           72 : void ChangeMeasureRepeatCount::flip()
     419              : {
     420           72 :     int oldCount = m->measureRepeatCount(staffIdx);
     421           72 :     m->setMeasureRepeatCount(count, staffIdx);
     422           72 :     count = oldCount;
     423           72 : }
     424              : 
     425          168 : void ChangeMMRestNext::flip()
     426              : {
     427          168 :     assert(m_mmrest->isMMRest());
     428          168 :     MeasureBase* oldNext = m_mmrest->next();
     429              : 
     430          168 :     m_mmrest->setNext(m_next);
     431          168 :     m_next = oldNext;
     432          168 : }
     433              : 
     434          168 : void ChangeMMRestPrev::flip()
     435              : {
     436          168 :     assert(m_mmrest->isMMRest());
     437          168 :     MeasureBase* oldPrev = m_mmrest->prev();
     438              : 
     439          168 :     m_mmrest->setPrev(m_prev);
     440          168 :     m_prev = oldPrev;
     441          168 : }
        

Generated by: LCOV version 2.0-1