LCOV - code coverage report
Current view: top level - src/engraving/editing - editmeasurerepeat.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 80.0 % 65 52
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
       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 "editmeasurerepeat.h"
      24              : 
      25              : #include <vector>
      26              : 
      27              : #include "translation.h"
      28              : 
      29              : #include "../dom/barline.h"
      30              : #include "../dom/factory.h"
      31              : #include "../dom/measure.h"
      32              : #include "../dom/measurerepeat.h"
      33              : #include "../dom/score.h"
      34              : #include "../dom/segment.h"
      35              : #include "../dom/select.h"
      36              : #include "../dom/staff.h"
      37              : #include "../dom/utils.h"
      38              : 
      39              : #include "../infrastructure/messagebox.h"
      40              : 
      41              : using namespace mu;
      42              : using namespace mu::engraving;
      43              : 
      44              : //---------------------------------------------------------
      45              : //   addMeasureRepeat
      46              : //---------------------------------------------------------
      47              : 
      48            3 : void EditMeasureRepeat::addMeasureRepeat(Transaction& tx, Score* score, Measure* firstMeasure, int numMeasures, staff_idx_t staffIdx)
      49              : {
      50              :     //
      51              :     // make measures into group
      52              :     //
      53            3 :     if (!makeMeasureRepeatGroup(tx, score, firstMeasure, numMeasures, staffIdx)) {
      54            0 :         return;
      55              :     }
      56              : 
      57              :     //
      58              :     // add MeasureRepeat element
      59              :     //
      60              :     int measureWithElementNo;
      61            3 :     if (numMeasures % 2) {
      62              :         // odd number, element anchored to center measure of group
      63            0 :         measureWithElementNo = numMeasures / 2 + 1;
      64              :     } else {
      65              :         // even number, element anchored to last measure in first half of group
      66            3 :         measureWithElementNo = numMeasures / 2;
      67              :     }
      68            3 :     Measure* measureWithElement = firstMeasure;
      69            6 :     for (int i = 1; i < measureWithElementNo; ++i) {
      70            3 :         measureWithElement = measureWithElement->nextMeasure();
      71              :     }
      72              :     // MeasureRepeat element will be positioned appropriately (in center of measure / on following barline)
      73              :     // when stretchMeasure() is called on measureWithElement
      74            3 :     MeasureRepeat* mr = addMeasureRepeat(tx, score, measureWithElement->tick(), staff2track(staffIdx), numMeasures);
      75            3 :     score->select(mr, SelectType::SINGLE, 0);
      76              : }
      77              : 
      78              : //---------------------------------------------------------
      79              : //   makeMeasureRepeatGroup
      80              : ///   clear measures, apply noBreak, set measureRepeatCount
      81              : ///   returns false if these measures won't work or user aborted
      82              : //---------------------------------------------------------
      83              : 
      84            3 : bool EditMeasureRepeat::makeMeasureRepeatGroup(Transaction&, Score* score, Measure* firstMeasure, int numMeasures,
      85              :                                                staff_idx_t staffIdx)
      86              : {
      87              :     //
      88              :     // check that sufficient measures exist, with equal durations
      89              :     //
      90            3 :     std::vector<Measure*> measures;
      91            3 :     Measure* measure = firstMeasure;
      92           15 :     for (int i = 1; i <= numMeasures; ++i) {
      93           12 :         if (!measure || measure->ticks() != firstMeasure->ticks()) {
      94            0 :             MScore::setError(MsError::INSUFFICIENT_MEASURES);
      95            0 :             return false;
      96              :         }
      97           12 :         measures.push_back(measure);
      98           12 :         measure = measure->nextMeasure();
      99              :     }
     100              : 
     101              :     //
     102              :     // warn user if things will have to be deleted to make room for measure repeat
     103              :     //
     104            3 :     bool empty = true;
     105           15 :     for (auto m : measures) {
     106           12 :         if (m != measures.back()) {
     107            9 :             if (m->endBarLineType() != BarLineType::NORMAL) {
     108            0 :                 empty = false;
     109            0 :                 break;
     110              :             }
     111              :         }
     112           36 :         for (auto seg = m->first(); seg && empty; seg = seg->next()) {
     113           24 :             if (seg->segmentType() & SegmentType::ChordRest) {
     114           12 :                 track_idx_t strack = staffIdx * VOICES;
     115           12 :                 track_idx_t etrack = strack + VOICES;
     116           60 :                 for (track_idx_t track = strack; track < etrack; ++track) {
     117           48 :                     EngravingItem* e = seg->element(track);
     118           48 :                     if (e && !e->generated() && !e->isRest()) {
     119            0 :                         empty = false;
     120            0 :                         break;
     121              :                     }
     122              :                 }
     123              :             }
     124              :         }
     125              :     }
     126              : 
     127            3 :     if (!empty) {
     128            0 :         auto b = MessageBox(score->iocContext()).warning(muse::trc("engraving", "Current contents of measures will be replaced"),
     129            0 :                                                          muse::trc("engraving", "Continue with inserting measure repeat?"));
     130            0 :         if (b == MessageBox::Button::Cancel) {
     131            0 :             return false;
     132              :         }
     133              :     }
     134              : 
     135              :     //
     136              :     // group measures and clear current contents
     137              :     //
     138              : 
     139            3 :     score->deselectAll();
     140            3 :     int i = 1;
     141           15 :     for (auto m : measures) {
     142           12 :         score->select(m, SelectType::RANGE, staffIdx);
     143           12 :         if (m->isMeasureRepeatGroup(staffIdx)) {
     144            0 :             score->deleteItem(m->measureRepeatElement(staffIdx)); // reset measures related to an earlier MeasureRepeat
     145              :         }
     146           12 :         score->undoChangeMeasureRepeatCount(m, i++, staffIdx);
     147           12 :         if (m != measures.front()) {
     148            9 :             m->undoChangeProperty(Pid::REPEAT_START, false);
     149              :         }
     150           12 :         if (m != measures.back()) {
     151            9 :             m->undoSetNoBreak(true);
     152            9 :             Segment* seg = m->findSegmentR(SegmentType::EndBarLine, m->ticks());
     153            9 :             BarLine* endBarLine = toBarLine(seg->element(staff2track(staffIdx)));
     154            9 :             score->deleteItem(endBarLine); // also takes care of Pid::REPEAT_END
     155              :         }
     156              :     }
     157            3 :     score->cmdDeleteSelection();
     158            3 :     return true;
     159            3 : }
     160              : 
     161              : //---------------------------------------------------------
     162              : //   addMeasureRepeat
     163              : //    create one MeasureRepeat at tick of subtype numMeasures
     164              : //    create segment if necessary
     165              : //    does NOT set measureRepeatCount or do anything else with measure(s)!
     166              : //---------------------------------------------------------
     167              : 
     168           28 : MeasureRepeat* EditMeasureRepeat::addMeasureRepeat(Transaction&, Score* score, const Fraction& tick, track_idx_t track,
     169              :                                                    int numMeasures)
     170              : {
     171           28 :     Measure* measure = score->tick2measure(tick);
     172           28 :     MeasureRepeat* mr = Factory::createMeasureRepeat(score->dummy());
     173           28 :     mr->setNumMeasures(numMeasures);
     174           28 :     mr->setTicks(measure->stretchedLen(score->staff(track2staff(track))));
     175           28 :     mr->setTrack(track);
     176           28 :     score->undoAddCR(mr, measure, tick);
     177           28 :     return mr;
     178              : }
        

Generated by: LCOV version 2.0-1