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 "edittimesig.h"
24 :
25 : #include <map>
26 : #include <tuple>
27 : #include <vector>
28 :
29 : #include "global/containers.h"
30 :
31 : #include "../dom/excerpt.h"
32 : #include "../dom/factory.h"
33 : #include "../dom/layoutbreak.h"
34 : #include "../dom/masterscore.h"
35 : #include "../dom/measure.h"
36 : #include "../dom/measurerepeat.h"
37 : #include "../dom/range.h"
38 : #include "../dom/score.h"
39 : #include "../dom/segment.h"
40 : #include "../dom/sig.h"
41 : #include "../dom/slur.h"
42 : #include "../dom/staff.h"
43 : #include "../dom/system.h"
44 : #include "../dom/timesig.h"
45 : #include "../dom/tremolotwochord.h"
46 :
47 : #include "addremoveelement.h"
48 : #include "editmeasures.h"
49 : #include "editspanner.h"
50 : #include "edittremolo.h"
51 : #include "transaction/transaction.h"
52 : #include "transaction/undostack.h"
53 :
54 : #include "log.h"
55 :
56 : using namespace mu;
57 : using namespace mu::engraving;
58 :
59 : //---------------------------------------------------------
60 : // rewriteMeasures
61 : // rewrite all measures from startMeasure to endMeasure (including)
62 : // If staffIdx is valid (>= 0), then rewrite a local
63 : // timesig change.
64 : //---------------------------------------------------------
65 :
66 21 : bool EditTimeSig::rewriteMeasures(Transaction& tx, Score* score, Measure* startMeasure, Measure* endMeasure, const Fraction& newTimeSig,
67 : staff_idx_t staffIdx)
68 : {
69 21 : if (staffIdx != muse::nidx) {
70 : // local timesig
71 : // don't actually rewrite, just update measure rest durations
72 : // abort if there is anything other than measure rests in range
73 3 : track_idx_t strack = staffIdx * VOICES;
74 3 : track_idx_t etrack = strack + VOICES;
75 62 : for (Measure* m = startMeasure;; m = m->nextMeasure()) {
76 124 : for (Segment* s = m->first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
77 310 : for (track_idx_t track = strack; track < etrack; ++track) {
78 248 : ChordRest* cr = toChordRest(s->element(track));
79 248 : if (!cr) {
80 186 : continue;
81 : }
82 62 : if (cr->isRest() && cr->durationType() == DurationType::V_MEASURE) {
83 62 : cr->undoChangeProperty(Pid::DURATION, newTimeSig);
84 : } else {
85 0 : return false;
86 : }
87 : }
88 : }
89 62 : if (m == endMeasure) {
90 3 : break;
91 : }
92 59 : }
93 3 : return true;
94 : }
95 :
96 18 : bool fmr = true;
97 :
98 : // Format: chord 1 tick, chord 2 tick, tremolo, track
99 18 : std::vector<std::tuple<Fraction, Fraction, TremoloTwoChord*, track_idx_t> > tremoloChordTicks;
100 :
101 : track_idx_t strack, etrack;
102 18 : if (staffIdx == muse::nidx) {
103 18 : strack = 0;
104 18 : etrack = score->ntracks();
105 : } else {
106 0 : strack = staffIdx * VOICES;
107 0 : etrack = strack + VOICES;
108 : }
109 :
110 18 : std::vector<Segment*> endOfMeasureTimeSigsToRemove;
111 :
112 55 : for (Measure* m = startMeasure; m; m = m->nextMeasure()) {
113 55 : if (!m->isFullMeasureRest()) {
114 20 : fmr = false;
115 : }
116 :
117 284 : for (Segment* s = m->first(); s; s = s->next()) {
118 229 : if (!s->isChordRestType() && !(s->isTimeSigType() && s->endOfMeasureChange())) {
119 94 : continue;
120 : }
121 :
122 135 : if (s->isTimeSigType()) {
123 0 : endOfMeasureTimeSigsToRemove.push_back(s);
124 0 : continue;
125 : }
126 :
127 935 : for (track_idx_t track = strack; track < etrack; ++track) {
128 800 : ChordRest* cr = toChordRest(s->element(track));
129 800 : if (cr && cr->isChord()) {
130 104 : Chord* chord = toChord(cr);
131 104 : if (chord->tremoloTwoChord()) {
132 4 : TremoloTwoChord* trem = chord->tremoloTwoChord();
133 :
134 : // Don't add same chord twice
135 4 : if (trem->chord2() == chord) {
136 2 : continue;
137 : }
138 :
139 : std::tuple<Fraction, Fraction, TremoloTwoChord*, track_idx_t> newP
140 2 : = { cr->tick(), trem->chord2()->segment()->tick(), trem, track };
141 2 : tremoloChordTicks.push_back(newP);
142 : }
143 : }
144 : }
145 : }
146 :
147 55 : if (m == endMeasure) {
148 18 : break;
149 : }
150 : }
151 :
152 18 : if (!fmr) {
153 : // check for local time signatures
154 64 : for (Measure* m = startMeasure; m; m = m->nextMeasure()) {
155 101 : for (size_t si = 0; si < score->nstaves(); ++si) {
156 61 : if (score->staff(si)->timeStretch(m->tick()) != Fraction(1, 1)) {
157 : // we cannot change a staff with a local time signature
158 0 : return false;
159 : }
160 61 : if (m == endMeasure) {
161 12 : break;
162 : }
163 : }
164 : }
165 : }
166 :
167 18 : for (Segment* seg : endOfMeasureTimeSigsToRemove) {
168 0 : score->doUndoRemoveElement(seg);
169 : }
170 :
171 18 : ScoreRange range;
172 18 : Measure* nextMeasure = endMeasure->nextMeasure();
173 18 : Segment* finalSeg = endMeasure->last();
174 18 : if (nextMeasure) {
175 3 : finalSeg = nextMeasure->first();
176 : }
177 18 : range.read(startMeasure->first(), finalSeg);
178 :
179 : //
180 : // calculate number of required measures = newMeasures
181 : //
182 18 : Fraction ticks = range.ticks().isNotZero() ? range.ticks() : score->endTick() - startMeasure->first()->tick();
183 18 : Fraction k = ticks / newTimeSig;
184 18 : int newMeasures = (k.numerator() + k.denominator() - 1) / k.denominator();
185 :
186 18 : Fraction newDuration = newTimeSig * Fraction(newMeasures, 1);
187 :
188 : // evtl. we have to fill the last measure
189 18 : Fraction fill = newDuration - ticks;
190 18 : range.fill(fill);
191 :
192 36 : for (Score* s : score->scoreList()) {
193 18 : Measure* m1 = s->tick2measure(startMeasure->tick());
194 18 : Measure* m2 = s->tick2measure(endMeasure->tick());
195 :
196 18 : Fraction tick1 = m1->tick();
197 18 : Fraction tick2 = m2->endTick();
198 18 : auto spanners = s->spannerMap().findOverlapping(tick1.ticks(), tick2.ticks());
199 22 : for (auto i : spanners) {
200 4 : if (i.value->tick() >= tick1 && i.value->tick() < tick2) {
201 4 : score->doUndoRemoveElement(i.value);
202 : }
203 : }
204 18 : s->undoRemoveMeasures(m1, m2, true);
205 :
206 18 : Measure* newFirstMeasure = nullptr;
207 18 : Measure* newLastMeasure = nullptr;
208 18 : Fraction tick = startMeasure->tick();
209 93 : for (int i = 0; i < newMeasures; ++i) {
210 75 : Measure* m = Factory::createMeasure(s);
211 75 : m->setPrev(newLastMeasure);
212 75 : if (newLastMeasure) {
213 57 : newLastMeasure->setNext(m);
214 : }
215 75 : m->setTimesig(newTimeSig);
216 75 : m->setTicks(newTimeSig);
217 75 : m->setTick(tick);
218 75 : tick += m->ticks();
219 75 : newLastMeasure = m;
220 75 : if (newFirstMeasure == 0) {
221 18 : newFirstMeasure = m;
222 : }
223 : }
224 : //
225 : // insert new calculated measures
226 : //
227 18 : newFirstMeasure->setPrev(m1->prev());
228 18 : newLastMeasure->setNext(m2->next());
229 18 : tx.push(new InsertMeasures(newFirstMeasure, newLastMeasure));
230 36 : }
231 18 : if (!fill.isZero()) {
232 12 : score->undoInsertTime(endMeasure->endTick(), fill);
233 : }
234 :
235 18 : if (!range.write(score->masterScore(), startMeasure->tick())) {
236 1 : return false;
237 : }
238 :
239 34 : for (Score* s : score->scoreList()) {
240 17 : s->connectTies(true);
241 17 : }
242 :
243 : // reset start and end elements for slurs that overlap the rewritten measures
244 20 : for (auto spanner : score->spannerMap().findOverlapping(startMeasure->tick().ticks(), endMeasure->tick().ticks())) {
245 3 : Slur* slur = (spanner.value->isSlur() ? toSlur(spanner.value) : nullptr);
246 3 : if (slur) {
247 1 : EngravingItem* startEl = slur->startElement();
248 1 : EngravingItem* endEl = slur->endElement();
249 1 : if (!startEl || !endEl) {
250 0 : continue;
251 : }
252 1 : tx.push(new ChangeStartEndSpanner(spanner.value, slur->findStartCR(), slur->findEndCR()));
253 : }
254 : }
255 : // Attempt to move tremolos to correct chords
256 19 : for (auto tremPair : tremoloChordTicks) {
257 2 : Fraction chord1Tick = std::get<0>(tremPair);
258 2 : Fraction chord2Tick = std::get<1>(tremPair);
259 2 : TremoloTwoChord* trem = std::get<2>(tremPair);
260 2 : track_idx_t track = std::get<3>(tremPair);
261 :
262 2 : tx.push(new MoveTremolo(trem->score(), chord1Tick, chord2Tick, trem, track));
263 : }
264 :
265 17 : if (score->noteEntryMode()) {
266 : // set input cursor to possibly re-written segment
267 0 : Fraction icTick = score->inputState().tick();
268 0 : Segment* icSegment = score->tick2segment(icTick, false, SegmentType::ChordRest);
269 0 : if (!icSegment) {
270 : // this can happen if cursor was on a rest
271 : // and in the rewriting it got subsumed into a full measure rest
272 0 : Measure* icMeasure = score->tick2measure(icTick);
273 0 : if (!icMeasure) { // shouldn't happen, but just in case
274 0 : icMeasure = score->firstMeasure();
275 : }
276 0 : icSegment = icMeasure->first(SegmentType::ChordRest);
277 : }
278 0 : score->inputState().setSegment(icSegment);
279 : }
280 :
281 17 : return true;
282 18 : }
283 :
284 : //---------------------------------------------------------
285 : // rewriteMeasures
286 : // rewrite all measures up to the next time signature or section break
287 : //---------------------------------------------------------
288 :
289 21 : bool EditTimeSig::rewriteMeasures(Transaction& tx, Score* score, Measure* fm, const Fraction& ns, staff_idx_t staffIdx)
290 : {
291 21 : Measure* lm = fm;
292 21 : Measure* fm1 = fm;
293 21 : Measure* nm = nullptr;
294 21 : LayoutBreak* sectionBreak = nullptr;
295 21 : Segment* endOfMeasureTimeSigSeg = nullptr;
296 :
297 : //
298 : // split into Measure segments fm-lm
299 : //
300 138 : auto foundLM = [fm, staffIdx, &endOfMeasureTimeSigSeg](MeasureBase* curMeas, Measure* curLM) {
301 138 : if (!curMeas || !curMeas->isMeasure() || curLM->sectionBreak()) {
302 17 : return true;
303 : }
304 121 : Segment* timeSigSeg = toMeasure(curMeas)->findSegmentR(SegmentType::TimeSig, Fraction(0, 1));
305 121 : if (!timeSigSeg && curMeas->prevMeasure()) {
306 : // find time sig at end of previous measure
307 97 : Measure* prevMeasure = curMeas->prevMeasure();
308 97 : timeSigSeg = prevMeasure->findSegmentR(SegmentType::TimeSig, prevMeasure->ticks());
309 97 : if (timeSigSeg) {
310 0 : endOfMeasureTimeSigSeg = timeSigSeg;
311 : }
312 : }
313 121 : if (timeSigSeg && curMeas != fm) {
314 5 : return staffIdx == muse::nidx || timeSigSeg->element(staff2track(staffIdx));
315 : }
316 116 : return false;
317 21 : };
318 138 : for (MeasureBase* measure = fm;; measure = measure->next()) {
319 138 : if (foundLM(measure, lm)) {
320 : // save section break to reinstate after rewrite
321 21 : LayoutBreak* layoutBreak = lm->sectionBreakElement();
322 :
323 21 : if (lm && layoutBreak) {
324 0 : sectionBreak = Factory::copyLayoutBreak(*layoutBreak);
325 : }
326 :
327 21 : if (!rewriteMeasures(tx, score, fm1, lm, ns, staffIdx)) {
328 1 : if (staffIdx != muse::nidx) {
329 0 : MScore::setError(MsError::CANNOT_CHANGE_LOCAL_TIMESIG_MEASURE_NOT_EMPTY);
330 : // restore measure rests that were prematurely modified
331 0 : Fraction fr(score->staff(staffIdx)->timeSig(fm->tick())->sig());
332 0 : for (Measure* m = fm1; m; m = m->nextMeasure()) {
333 0 : ChordRest* cr = m->findChordRest(m->tick(), staffIdx * VOICES);
334 0 : if (cr && cr->isRest() && cr->durationType() == DurationType::V_MEASURE) {
335 0 : cr->undoChangeProperty(Pid::DURATION, fr);
336 : } else {
337 0 : break;
338 : }
339 : }
340 : }
341 1 : for (Measure* m = fm1; m; m = m->nextMeasure()) {
342 1 : if (m->first(SegmentType::TimeSig)) {
343 1 : break;
344 : }
345 0 : Fraction fr(ns);
346 0 : m->undoChangeProperty(Pid::TIMESIG_NOMINAL, fr);
347 : }
348 1 : return false;
349 : }
350 :
351 : // after rewrite, lm is not necessarily valid
352 : // m is first MeasureBase after rewritten range
353 : // m->prevMeasure () is new last measure of range
354 : // set nm to first true Measure after rewritten range
355 : // we may use this to reinstate time signatures
356 20 : if (measure && measure->prevMeasure()) {
357 4 : nm = measure->prevMeasure()->nextMeasure();
358 : } else {
359 16 : nm = nullptr;
360 : }
361 :
362 20 : if (sectionBreak) {
363 : // reinstate section break, then stop rewriting
364 0 : if (measure && measure->prevMeasure()) {
365 0 : sectionBreak->setOwnershipParent(measure->prevMeasure());
366 0 : score->undoAddElement(sectionBreak);
367 0 : } else if (!measure) {
368 0 : sectionBreak->setOwnershipParent(score->lastMeasure());
369 0 : score->undoAddElement(sectionBreak);
370 : } else {
371 0 : LOGD("unable to restore section break");
372 0 : nm = nullptr;
373 0 : sectionBreak = nullptr;
374 : }
375 0 : break;
376 : }
377 :
378 : // stop rewriting at end of score
379 : // or at a measure (which means we found a time signature segment)
380 20 : if (!measure || measure->isMeasure()) {
381 20 : break;
382 : }
383 :
384 : // skip frames
385 0 : while (!measure->isMeasure()) {
386 0 : LayoutBreak* layoutBreak2 = measure->sectionBreakElement();
387 :
388 0 : if (layoutBreak2) {
389 : // frame has a section break; we can stop skipping ahead
390 0 : sectionBreak = layoutBreak2;
391 0 : break;
392 : }
393 0 : measure = measure->next();
394 0 : if (!measure) {
395 0 : break;
396 : }
397 : }
398 : // stop rewriting if we encountered a section break on a frame
399 : // or if there is a time signature on first measure after the frame
400 0 : if (sectionBreak || (measure && toMeasure(measure)->first(SegmentType::TimeSig))) {
401 0 : break;
402 : }
403 :
404 : // set up for next range to rewrite
405 0 : fm1 = toMeasure(measure);
406 0 : if (fm1 == 0) {
407 0 : break;
408 : }
409 : }
410 :
411 : // if we didn't break the loop already,
412 : // we must have an ordinary measure
413 : // add measure to range to rewrite
414 117 : lm = toMeasure(measure);
415 117 : }
416 :
417 : // if any staves don't have time signatures at the point where we stopped,
418 : // we need to reinstate their previous time signatures
419 20 : if (!nm) {
420 16 : return true;
421 : }
422 4 : Segment* timeSigSeg = nm->undoGetSegment(SegmentType::TimeSig, nm->tick());
423 :
424 11 : for (size_t i = 0; i < score->nstaves(); ++i) {
425 7 : if (staffIdx != muse::nidx && i != staffIdx) {
426 3 : continue;
427 : }
428 4 : if (!timeSigSeg->element(staff2track(i))) {
429 1 : TimeSig* ots = endOfMeasureTimeSigSeg
430 1 : ? toTimeSig(endOfMeasureTimeSigSeg->element(staff2track(i))) : score->staff(i)->timeSig(nm->tick());
431 1 : if (ots) {
432 0 : TimeSig* nts = Factory::copyTimeSig(*ots);
433 0 : nts->setOwnershipParent(timeSigSeg);
434 0 : if (sectionBreak) {
435 0 : nts->setGenerated(false);
436 0 : nts->setShowCourtesySig(false);
437 : }
438 0 : score->undoAddElement(nts);
439 : }
440 : }
441 : }
442 :
443 4 : return true;
444 : }
445 :
446 : //---------------------------------------------------------
447 : // addTimeSig
448 : //
449 : // Add or change time signature at measure in response
450 : // to gui command (drop timesig on measure or timesig)
451 : //---------------------------------------------------------
452 :
453 21 : void EditTimeSig::addTimeSig(Transaction& tx, Score* score, Measure* fm, staff_idx_t staffIdx, TimeSig* ts, bool local)
454 : {
455 21 : score->deselectAll();
456 :
457 21 : if (fm->isMMRest()) {
458 0 : fm = fm->mmRestFirst();
459 : }
460 :
461 21 : Fraction ns = ts->sig();
462 21 : Fraction tick = fm->tick();
463 21 : if (local) {
464 3 : Fraction stretch = (ns / fm->timesig()).reduced();
465 3 : ts->setStretch(stretch);
466 : }
467 :
468 21 : track_idx_t track = staffIdx * VOICES;
469 21 : Segment* seg = fm->findSegment(SegmentType::TimeSig, tick);
470 21 : TimeSig* ots = seg ? toTimeSig(seg->element(track)) : nullptr;
471 :
472 : // Check same tick at end of previous measure
473 21 : if (!ots) {
474 10 : Measure* prevMeasure = fm->prevMeasure();
475 10 : seg = prevMeasure ? prevMeasure->findSegmentR(SegmentType::TimeSig, prevMeasure->ticks()) : seg;
476 10 : ots = seg ? toTimeSig(seg->element(track)) : nullptr;
477 : }
478 :
479 21 : if (ots && (*ots == *ts)) {
480 : //
481 : // ignore if there is already a timesig
482 : // with same values
483 : //
484 0 : delete ts;
485 1 : return;
486 : }
487 :
488 21 : seg = fm->undoGetSegment(SegmentType::TimeSig, tick);
489 :
490 51 : auto getStaffIdxRange = [score, local, staffIdx](const Score* lscore) -> std::pair<staff_idx_t /*start*/, staff_idx_t /*end*/> {
491 : staff_idx_t startStaffIdx, endStaffIdx;
492 51 : if (local) {
493 18 : if (lscore == score) {
494 6 : startStaffIdx = staffIdx;
495 6 : endStaffIdx = startStaffIdx + 1;
496 : } else {
497 12 : const Staff* thisStaff = score->staff(staffIdx);
498 12 : const Staff* linkedStaff = thisStaff->findLinkedInScore(lscore);
499 12 : if (linkedStaff) {
500 3 : startStaffIdx = linkedStaff->idx();
501 3 : endStaffIdx = startStaffIdx + 1;
502 : } else {
503 9 : startStaffIdx = 0;
504 9 : endStaffIdx = 0;
505 : }
506 : }
507 : } else {
508 33 : startStaffIdx = 0;
509 33 : endStaffIdx = lscore->nstaves();
510 : }
511 102 : return std::make_pair(startStaffIdx, endStaffIdx);
512 21 : };
513 :
514 21 : if (ots && ots->sig() == ns && ots->stretch() == ts->stretch()) {
515 : //
516 : // the measure duration does not change,
517 : // so its ok to just update the time signatures
518 : //
519 2 : TimeSig* nts = score->staff(staffIdx)->nextTimeSig(tick + Fraction::eps());
520 2 : const Fraction lmTick = nts ? nts->segment()->tick() : Fraction(-1, 1);
521 4 : for (Score* lscore : score->scoreList()) {
522 2 : Measure* mf = lscore->tick2measure(tick);
523 2 : Measure* lm = (lmTick != Fraction(-1, 1)) ? lscore->tick2measure(lmTick) : nullptr;
524 5 : for (Measure* m = mf; m != lm; m = m->nextMeasure()) {
525 3 : bool changeActual = m->ticks() == m->timesig();
526 3 : m->undoChangeProperty(Pid::TIMESIG_NOMINAL, ns);
527 3 : if (changeActual) {
528 3 : m->undoChangeProperty(Pid::TIMESIG_ACTUAL, ns);
529 : }
530 : }
531 2 : std::pair<staff_idx_t, staff_idx_t> staffIdxRange = getStaffIdxRange(lscore);
532 4 : for (staff_idx_t si = staffIdxRange.first; si < staffIdxRange.second; ++si) {
533 2 : TimeSig* nsig = toTimeSig(seg->element(si * VOICES));
534 2 : if (!nsig) {
535 0 : continue;
536 : }
537 2 : nsig->undoChangeProperty(Pid::SHOW_COURTESY, ts->showCourtesySig());
538 2 : nsig->undoChangeProperty(Pid::TIMESIG, ts->sig());
539 2 : nsig->undoChangeProperty(Pid::TIMESIG_TYPE, int(ts->timeSigType()));
540 2 : nsig->undoChangeProperty(Pid::NUMERATOR_STRING, ts->numeratorString());
541 2 : nsig->undoChangeProperty(Pid::DENOMINATOR_STRING, ts->denominatorString());
542 2 : nsig->undoChangeProperty(Pid::TIMESIG_STRETCH, ts->stretch());
543 2 : nsig->undoChangeProperty(Pid::GROUP_NODES, ts->groups().nodes());
544 2 : nsig->setSelected(false);
545 2 : nsig->setDropTarget(false);
546 : }
547 2 : }
548 : } else {
549 19 : Score* mScore = score->masterScore();
550 19 : Measure* mf = mScore->tick2measure(tick);
551 :
552 : //
553 : // rewrite all measures up to the next time signature
554 : //
555 19 : if (mf == mScore->firstMeasure() && mf->nextMeasure() && (mf->ticks() != mf->timesig())) {
556 : // handle upbeat
557 0 : mf->undoChangeProperty(Pid::TIMESIG_NOMINAL, ns);
558 0 : Measure* m = mf->nextMeasure();
559 0 : Segment* s = m->findSegment(SegmentType::TimeSig, m->tick());
560 0 : mf = s ? 0 : mf->nextMeasure();
561 : } else {
562 19 : if (score->sigmap()->timesig(seg->tick().ticks()).nominal().identical(ns)) {
563 : // no change to global time signature,
564 : // but we need to rewrite any staves with local time signatures
565 0 : for (size_t i = 0; i < score->nstaves(); ++i) {
566 0 : if (score->staff(i)->timeSig(tick) && score->staff(i)->timeSig(tick)->isLocal()) {
567 0 : if (!rewriteMeasures(tx, mScore, mf, ns, i)) {
568 0 : score->undoStack()->activeTransaction()->unwind();
569 1 : return;
570 : }
571 : }
572 : }
573 0 : mf = 0;
574 : }
575 : }
576 :
577 : // try to rewrite the measures first
578 : // we will only add time signatures if this succeeds
579 : // this means, however, that the rewrite cannot depend on the time signatures being in place
580 19 : if (mf) {
581 19 : auto staffIdxRangeOnMaster = getStaffIdxRange(mScore);
582 38 : if (staffIdxRangeOnMaster.second != staffIdxRangeOnMaster.first
583 19 : && !rewriteMeasures(tx, mScore, mf, ns, local ? staffIdxRangeOnMaster.first : muse::nidx)) {
584 1 : score->undoStack()->activeTransaction()->unwind();
585 1 : return;
586 : }
587 : }
588 : // add the time signatures
589 18 : std::map<track_idx_t, TimeSig*> masterTimeSigs;
590 48 : for (Score* lscore : score->scoreList()) {
591 30 : Measure* nfm = lscore->tick2measure(tick);
592 30 : seg = nfm->undoGetSegment(SegmentType::TimeSig, nfm->tick());
593 30 : std::pair<staff_idx_t, staff_idx_t> staffIdxRange = getStaffIdxRange(lscore);
594 56 : for (staff_idx_t si = staffIdxRange.first; si < staffIdxRange.second; ++si) {
595 26 : if (fm->isMeasureRepeatGroup(si)) {
596 0 : score->deleteItem(fm->measureRepeatElement(si));
597 : }
598 26 : TimeSig* nsig = toTimeSig(seg->element(si * VOICES));
599 26 : if (nsig == 0) {
600 14 : nsig = Factory::copyTimeSig(*ts);
601 14 : nsig->setScore(lscore);
602 14 : nsig->setTrack(si * VOICES);
603 14 : nsig->setOwnershipParent(seg);
604 14 : nsig->styleChanged();
605 14 : score->undoAddElement(nsig);
606 14 : if (lscore->excerpt()) {
607 3 : const track_idx_t masterTrack = muse::key(lscore->excerpt()->tracksMapping(), nsig->track());
608 3 : TimeSig* masterTimeSig = masterTimeSigs[masterTrack];
609 3 : if (masterTimeSig) {
610 3 : tx.push(new Link(masterTimeSig, nsig));
611 : }
612 : }
613 : } else {
614 12 : nsig->undoChangeProperty(Pid::SHOW_COURTESY, ts->showCourtesySig());
615 12 : nsig->undoChangeProperty(Pid::TIMESIG_TYPE, int(ts->timeSigType()));
616 12 : nsig->undoChangeProperty(Pid::TIMESIG, ts->sig());
617 12 : nsig->undoChangeProperty(Pid::NUMERATOR_STRING, ts->numeratorString());
618 12 : nsig->undoChangeProperty(Pid::DENOMINATOR_STRING, ts->denominatorString());
619 :
620 : // HACK do it twice to accommodate undo
621 12 : nsig->undoChangeProperty(Pid::TIMESIG_TYPE, int(ts->timeSigType()));
622 12 : nsig->undoChangeProperty(Pid::TIMESIG_STRETCH, ts->stretch());
623 12 : nsig->undoChangeProperty(Pid::GROUP_NODES, ts->groups().nodes());
624 12 : nsig->setSelected(false);
625 12 : nsig->setDropTarget(false); // DEBUG
626 : }
627 :
628 26 : if (lscore->isMaster()) {
629 23 : masterTimeSigs[nsig->track()] = nsig;
630 : }
631 : }
632 18 : }
633 18 : }
634 20 : delete ts;
635 : }
636 :
637 : //---------------------------------------------------------
638 : // removeTimeSig
639 : //---------------------------------------------------------
640 :
641 2 : void EditTimeSig::removeTimeSig(Transaction& tx, Score* score, TimeSig* ts)
642 : {
643 2 : Measure* m = ts->measure();
644 2 : Segment* s = ts->segment();
645 :
646 : //
647 : // we cannot remove a courtesy time signature
648 : //
649 2 : if (s->isCourtesySegment()) {
650 0 : return;
651 : }
652 2 : Fraction tick = m->tick();
653 :
654 : // if we remove all time sigs from segment, segment will be already removed by now
655 : // but this would leave us no means of detecting that we have measures in a local timesig
656 : // in cases where we try deleting the local time sig
657 : // known bug: this means we do not correctly detect non-empty measures when deleting global timesig change after a local one
658 : // see http://musescore.org/en/node/51596
659 : // Delete the time sig segment from the root score, we will rewriteMeasures from it
660 : // since it contains all the music while the part doesn't
661 2 : Score* rScore = score->masterScore();
662 2 : Measure* rm = rScore->tick2measure(m->tick());
663 2 : Segment* rs = rm->findSegment(SegmentType::TimeSig, s->tick());
664 2 : if (rs) {
665 2 : rScore->undoRemoveElement(rs);
666 : }
667 : // Measure can contain mmRest that can have its own timesig. We need to delete it too
668 2 : if (rm->mmRest()) {
669 2 : Segment* mmRestTimesig = rm->mmRest()->findSegment(SegmentType::TimeSig, s->tick());
670 2 : if (mmRestTimesig) {
671 2 : rScore->undoRemoveElement(mmRestTimesig);
672 : }
673 : }
674 :
675 2 : Measure* pm = m->prevMeasure();
676 2 : Fraction ns(pm ? pm->timesig() : Fraction(4, 4));
677 :
678 2 : if (!rewriteMeasures(tx, rScore, rm, ns, muse::nidx)) {
679 0 : score->undoStack()->activeTransaction()->unwind();
680 : } else {
681 2 : m = score->tick2measure(tick); // old m may have been replaced
682 : // hack: fix measure rest durations for staves with local time signatures
683 : // if a time signature was deleted to reveal a previous local one,
684 : // then rewriteMeasures() got the measure rest durations wrong
685 : // (if we fixed it to work for delete, it would fail for add)
686 : // so we will fix measure rest durations here
687 : // TODO: fix rewriteMeasures() to get this right
688 4 : for (size_t i = 0; i < score->nstaves(); ++i) {
689 2 : TimeSig* tsig = score->staff(i)->timeSig(tick);
690 2 : if (tsig && tsig->isLocal()) {
691 0 : for (Measure* nm = m; nm; nm = nm->nextMeasure()) {
692 : // stop when time signature changes
693 0 : if (score->staff(i)->timeSig(nm->tick()) != tsig) {
694 0 : break;
695 : }
696 : // fix measure rest duration
697 0 : ChordRest* cr = nm->findChordRest(nm->tick(), i * VOICES);
698 0 : if (cr && cr->isRest() && cr->durationType() == DurationType::V_MEASURE) {
699 0 : cr->undoChangeProperty(Pid::DURATION, nm->stretchedLen(score->staff(i)));
700 : }
701 : //cr->setTicks(nm->stretchedLen(staff(i)));
702 : }
703 : }
704 : }
705 : }
706 : }
|