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 : }
|