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) 2025 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 "splitjoinmeasure.h"
24 :
25 : #include "dom/chordrest.h"
26 : #include "dom/factory.h"
27 : #include "dom/masterscore.h"
28 : #include "dom/measure.h"
29 : #include "dom/range.h"
30 : #include "dom/score.h"
31 : #include "dom/segment.h"
32 : #include "dom/spanner.h"
33 : #include "dom/staff.h"
34 : #include "dom/stafftypechange.h"
35 : #include "dom/timesig.h"
36 :
37 : #include "addremoveelement.h"
38 : #include "editspanner.h"
39 : #include "transaction/transaction.h"
40 :
41 : using namespace mu::engraving;
42 :
43 : //---------------------------------------------------------
44 : // splitMeasure
45 : //---------------------------------------------------------
46 :
47 22 : void SplitJoinMeasure::splitMeasure(Transaction& tx, MasterScore* masterScore, const Fraction& tick)
48 : {
49 22 : Segment* segment = masterScore->tick2segment(tick, false, SegmentType::ChordRest);
50 :
51 22 : if (segment->rtick().isZero()) {
52 0 : MScore::setError(MsError::CANNOT_SPLIT_MEASURE_FIRST_BEAT);
53 0 : return;
54 : }
55 22 : if (segment->splitsTuplet()) {
56 0 : MScore::setError(MsError::CANNOT_SPLIT_MEASURE_TUPLET);
57 0 : return;
58 : }
59 :
60 22 : Measure* measure = masterScore->tick2measure(tick);
61 :
62 46 : for (size_t staffIdx = 0; staffIdx < masterScore->nstaves(); ++staffIdx) {
63 24 : if (measure->isMeasureRepeatGroup(static_cast<int>(staffIdx))) {
64 0 : MScore::setError(MsError::CANNOT_SPLIT_MEASURE_REPEAT);
65 0 : return;
66 : }
67 : }
68 :
69 22 : ScoreRange range;
70 22 : range.read(measure->first(), measure->last());
71 :
72 22 : Fraction stick = measure->tick();
73 22 : Fraction etick = measure->endTick();
74 :
75 22 : std::vector<std::tuple<Spanner*, Fraction, Fraction> > spanners;
76 34 : for (auto i : masterScore->spanner()) {
77 12 : Spanner* s = i.second;
78 12 : EngravingItem* start = s->startElement();
79 12 : EngravingItem* end = s->endElement();
80 12 : if (s->tick() >= stick && s->tick() < etick) {
81 6 : start = nullptr;
82 : }
83 12 : if (s->tick2() >= stick && s->tick2() < etick) {
84 4 : end = nullptr;
85 : }
86 12 : if (start != s->startElement() || end != s->endElement()) {
87 8 : spanners.push_back(std::make_tuple(s, s->tick(), s->ticks()));
88 8 : tx.push(new ChangeStartEndSpanner(s, start, end));
89 : }
90 12 : if (s->tick() < stick && s->tick2() > stick) {
91 1 : spanners.push_back(std::make_tuple(s, s->tick(), s->ticks()));
92 : }
93 : }
94 :
95 22 : MeasureBase* nm = measure->next();
96 :
97 : // create empty measures:
98 22 : Score::InsertMeasureOptions options;
99 22 : options.createMeasureRests = true;
100 22 : options.moveSignaturesClef = false;
101 22 : options.moveStaffTypeChanges = false;
102 22 : options.ignoreBarLines = true;
103 :
104 22 : masterScore->insertMeasure(nm, options);
105 22 : Measure* m2 = toMeasure(nm ? nm->prev() : masterScore->lastMeasure());
106 22 : masterScore->insertMeasure(m2, options);
107 22 : Measure* m1 = toMeasure(m2->prev());
108 :
109 46 : for (Score* s : masterScore->scoreList()) {
110 24 : Measure* m = s->tick2measure(tick);
111 24 : s->undoRemoveMeasures(m, m, true, false);
112 22 : }
113 22 : masterScore->undoInsertTime(measure->tick(), -measure->ticks());
114 :
115 22 : m1->setTick(measure->tick());
116 22 : m2->setTick(tick);
117 :
118 22 : Fraction ticks1 = tick - measure->tick();
119 22 : Fraction ticks2 = measure->ticks() - ticks1;
120 :
121 22 : m1->setTimesig(measure->timesig());
122 22 : m2->setTimesig(measure->timesig());
123 :
124 22 : ticks1.reduce();
125 22 : ticks2.reduce();
126 : // Now make sure this reduction doesn't go 'beyond' the original measure's
127 : // actual denominator for both resultant measures.
128 22 : if (ticks1.denominator() < measure->ticks().denominator()) {
129 14 : if (measure->ticks().denominator() % m1->timesig().denominator() == 0) {
130 13 : int mult = measure->ticks().denominator() / ticks1.denominator();
131 : // *= operator automatically reduces via GCD, so rather do literal multiplication:
132 13 : ticks1.setDenominator(ticks1.denominator() * mult);
133 13 : ticks1.setNumerator(ticks1.numerator() * mult);
134 : }
135 : }
136 22 : if (ticks2.denominator() < measure->ticks().denominator()) {
137 11 : if (measure->ticks().denominator() % m2->timesig().denominator() == 0) {
138 11 : int mult = measure->ticks().denominator() / ticks2.denominator();
139 11 : ticks2.setDenominator(ticks2.denominator() * mult);
140 11 : ticks2.setNumerator(ticks2.numerator() * mult);
141 : }
142 : }
143 22 : if (ticks1.denominator() > 128 || ticks2.denominator() > 128) {
144 0 : MScore::setError(MsError::CANNOT_SPLIT_MEASURE_TOO_SHORT);
145 0 : return;
146 : }
147 :
148 22 : m1->adjustToLen(ticks1, false);
149 22 : m2->adjustToLen(ticks2, false);
150 :
151 : // set correct barline types if needed
152 46 : for (Score* linkedScore : masterScore->scoreList()) {
153 24 : Measure* sM1 = linkedScore->tick2measure(m1->tick());
154 24 : Measure* sM2 = linkedScore->tick2measure(m2->tick());
155 : // TODO: handle other end barline types; they may differ per staff
156 24 : if (measure->endBarLineType() == BarLineType::END_REPEAT) {
157 0 : sM2->undoChangeProperty(Pid::REPEAT_END, true);
158 : }
159 24 : if (measure->getProperty(Pid::REPEAT_START).toBool()) {
160 0 : sM1->undoChangeProperty(Pid::REPEAT_START, true);
161 : }
162 22 : }
163 :
164 22 : range.write(masterScore, m1->tick());
165 :
166 31 : for (const auto& [s, t, ticks] : spanners) {
167 9 : if (s->tick() != t) {
168 1 : s->undoChangeProperty(Pid::SPANNER_TICK, t);
169 : }
170 9 : if (s->ticks() != ticks) {
171 1 : s->undoChangeProperty(Pid::SPANNER_TICKS, ticks);
172 : }
173 : }
174 :
175 46 : for (size_t staffIdx = 0; staffIdx < masterScore->nstaves(); ++staffIdx) {
176 24 : Staff* staff = masterScore->staves().at(staffIdx);
177 24 : if (staff->isStaffTypeStartFrom(stick)) {
178 0 : StaffTypeChange* stc = Factory::createStaffTypeChange(m1);
179 0 : stc->setTrack(staffIdx * VOICES);
180 0 : masterScore->addElement(stc);
181 : }
182 : }
183 22 : }
184 :
185 : //---------------------------------------------------------
186 : // joinMeasure
187 : //---------------------------------------------------------
188 :
189 11 : void SplitJoinMeasure::joinMeasures(Transaction& tx, MasterScore* masterScore, const Fraction& tick1, const Fraction& tick2)
190 : {
191 11 : Measure* m1 = masterScore->tick2measure(tick1);
192 11 : Measure* m2 = masterScore->tick2measure(tick2);
193 :
194 11 : if (!m1 || !m2) {
195 0 : return;
196 : }
197 :
198 22 : for (size_t staffIdx = 0; staffIdx < masterScore->nstaves(); ++staffIdx) {
199 11 : if (m1->isMeasureRepeatGroupWithPrevM(static_cast<int>(staffIdx))
200 11 : || m2->isMeasureRepeatGroupWithNextM(static_cast<int>(staffIdx))) {
201 0 : MScore::setError(MsError::CANNOT_SPLIT_MEASURE_REPEAT);
202 0 : return;
203 : }
204 11 : if (masterScore->staves().at(staffIdx)->staffTypeRange(tick2).first > tick1.ticks()) {
205 0 : MScore::setError(MsError::CANNOT_JOIN_MEASURE_STAFFTYPE_CHANGE);
206 0 : return;
207 : }
208 : }
209 :
210 11 : if (m1->isMMRest()) {
211 0 : m1 = m1->mmRestFirst();
212 : }
213 11 : if (m2->isMMRest()) {
214 0 : m2 = m2->mmRestLast();
215 : }
216 :
217 11 : masterScore->deselectAll();
218 :
219 11 : ScoreRange range;
220 11 : range.read(m1->first(), m2->last());
221 :
222 11 : Fraction startTick = m1->tick();
223 11 : Fraction endTick = m2->endTick();
224 :
225 11 : auto spanners = masterScore->spannerMap().findContained(startTick.ticks(), endTick.ticks());
226 22 : for (auto i : spanners) {
227 11 : masterScore->doUndoRemoveElement(i.value);
228 : }
229 :
230 11 : std::map<Spanner*, Fraction> spannersEndingInRange;
231 11 : std::multimap<int, Spanner*> spannerMap = masterScore->spanner();
232 23 : for (std::pair<int, Spanner*> i : spannerMap) {
233 12 : Spanner* s = i.second;
234 12 : if (s->tick() >= startTick && s->tick() < endTick) {
235 4 : s->setStartElement(0);
236 : }
237 12 : if (s->tick2() >= startTick && s->tick2() < endTick) {
238 3 : s->setEndElement(0);
239 : }
240 : // Maintain length of spanners starting outside and ending within join range
241 12 : if (s->tick() < startTick && s->tick2() < endTick) {
242 3 : spannersEndingInRange.emplace(s, s->tick2());
243 : }
244 : // Remove spanners starting within join range and ending outside
245 : // These will be replaced on range.write
246 12 : if (s->tick() > startTick && s->tick() < endTick && s->tick2() > endTick) {
247 4 : masterScore->doUndoRemoveElement(s);
248 : }
249 : }
250 :
251 11 : MeasureBase* next = m2->next();
252 11 : masterScore->deleteMeasures(m1, m2, true);
253 :
254 11 : Score::InsertMeasureOptions options;
255 11 : options.createMeasureRests = true;
256 11 : options.moveSignaturesClef = false;
257 11 : options.moveStaffTypeChanges = false;
258 11 : options.ignoreBarLines = true;
259 11 : Measure* joinedMeasure = toMeasure(masterScore->insertMeasure(next, options));
260 :
261 14 : for (auto spannerToFixup : spannersEndingInRange) {
262 3 : Spanner* sp = spannerToFixup.first;
263 3 : Fraction spEndTick = spannerToFixup.second;
264 :
265 3 : sp->setTick2(spEndTick);
266 : }
267 :
268 22 : for (size_t staffIdx = 0; staffIdx < masterScore->nstaves(); ++staffIdx) {
269 11 : Staff* staff = masterScore->staves().at(staffIdx);
270 11 : if (staff->isStaffTypeStartFrom(tick1)) {
271 0 : StaffTypeChange* stc = engraving::Factory::createStaffTypeChange(joinedMeasure);
272 0 : stc->setTrack(staffIdx * VOICES);
273 0 : masterScore->addElement(stc);
274 : }
275 : }
276 :
277 11 : const Fraction newTimesig = m1->timesig();
278 11 : Fraction newLen;
279 22 : for (Measure* mm = m1; mm; mm = mm->nextMeasure()) {
280 22 : newLen += mm->ticks();
281 22 : if (mm == m2) {
282 11 : break;
283 : }
284 : }
285 :
286 : // The loop since measures are not currently linked in MuseScore
287 : // change nominal time sig, as inserted measure took it from next measure
288 22 : for (Score* linkedScore : masterScore->scoreList()) {
289 11 : Measure* ins = linkedScore->tick2measure(startTick);
290 11 : ins->undoChangeProperty(Pid::TIMESIG_NOMINAL, newTimesig);
291 : // set correct barline types if needed
292 : // TODO: handle other end barline types; they may differ per staff
293 11 : if (m2->endBarLineType() == BarLineType::END_REPEAT) {
294 0 : ins->undoChangeProperty(Pid::REPEAT_END, true);
295 : }
296 11 : if (m1->getProperty(Pid::REPEAT_START).toBool()) {
297 0 : ins->undoChangeProperty(Pid::REPEAT_START, true);
298 : }
299 11 : }
300 11 : Measure* inserted = (next ? next->prevMeasure() : masterScore->lastMeasure());
301 11 : inserted->adjustToLen(newLen, /* appendRests... */ false);
302 :
303 11 : range.write(masterScore, m1->tick());
304 :
305 : // if there are some Time Signatures in joined measures, move last one to the next measure (if there is not one already)
306 11 : Segment* ts = inserted->last()->prev(SegmentType::TimeSig);
307 11 : if (ts && ts->rtick().isNotZero()) {
308 0 : for (Segment* insMSeg = inserted->last()->prev(SegmentType::TimeSig); insMSeg && insMSeg->rtick().isNotZero();
309 0 : insMSeg = insMSeg->prev(SegmentType::TimeSig)) {
310 0 : for (staff_idx_t staffIdx = 0; staffIdx < masterScore->nstaves(); ++staffIdx) {
311 0 : if (TimeSig* insTimeSig = toTimeSig(insMSeg->element(staffIdx * VOICES))) {
312 0 : TimeSig* lts = nullptr;
313 0 : for (EngravingObject* l : insTimeSig->linkList()) {
314 0 : Score* linkedScore = l->score();
315 0 : TimeSig* timeSig = toTimeSig(l);
316 0 : Segment* tSeg = timeSig->segment();
317 0 : track_idx_t track = timeSig->track();
318 0 : Measure* sNext = next ? linkedScore->tick2measure(next->tick()) : nullptr;
319 0 : Segment* nextTSeg = sNext ? sNext->undoGetSegmentR(SegmentType::TimeSig, Fraction(0, 1)) : nullptr;
320 0 : if (sNext && !nextTSeg->element(track)) {
321 0 : TimeSig* nsig = Factory::copyTimeSig(*timeSig);
322 0 : nsig->setScore(linkedScore);
323 0 : nsig->setTrack(track);
324 0 : nsig->setOwnershipParent(nextTSeg);
325 :
326 0 : linkedScore->doUndoAddElement(nsig);
327 :
328 0 : if (!lts) {
329 0 : lts = nsig;
330 : } else {
331 0 : tx.push(new Link(lts, nsig));
332 : }
333 : }
334 0 : linkedScore->doUndoRemoveElement(timeSig);
335 0 : if (tSeg->empty()) {
336 0 : linkedScore->doUndoRemoveElement(tSeg);
337 : }
338 0 : }
339 : }
340 : }
341 : }
342 : }
343 11 : }
|