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 "editsystemlocks.h"
24 : #include "editpagelocks.h"
25 :
26 : #include "transaction/transaction.h"
27 : #include "transaction/undoablecommand.h"
28 :
29 : #include "../dom/measure.h"
30 : #include "../dom/measurebase.h"
31 : #include "../dom/rangelock.h"
32 : #include "../dom/score.h"
33 : #include "../dom/system.h"
34 : #include "../dom/systemlockindicator.h"
35 :
36 : using namespace mu::engraving;
37 :
38 : //---------------------------------------------------------
39 : // AddSystemLock
40 : //---------------------------------------------------------
41 :
42 : class AddSystemLock : public UndoableCommand
43 : {
44 56 : OBJECT_ALLOCATOR(engraving, AddSystemLock)
45 :
46 : const RangeLock* m_systemLock;
47 : public:
48 28 : AddSystemLock(const RangeLock* systemLock)
49 28 : : m_systemLock(systemLock) {}
50 :
51 0 : void undo() override
52 : {
53 0 : Score* score = m_systemLock->startMB()->score();
54 0 : score->removeSystemLock(m_systemLock);
55 0 : }
56 :
57 28 : void redo() override
58 : {
59 28 : Score* score = m_systemLock->startMB()->score();
60 28 : score->addSystemLock(m_systemLock);
61 28 : }
62 :
63 28 : void cleanup(bool wasDone) override
64 : {
65 28 : if (!wasDone) {
66 0 : delete m_systemLock;
67 0 : m_systemLock = nullptr;
68 : }
69 28 : }
70 :
71 0 : UNDO_NAME("AddSystemLock")
72 :
73 28 : std::vector<EngravingObject*> objectItems() const override
74 : {
75 84 : return { m_systemLock->startMB(), m_systemLock->endMB() };
76 : }
77 : };
78 :
79 : //---------------------------------------------------------
80 : // RemoveSystemLock
81 : //---------------------------------------------------------
82 :
83 : class RemoveSystemLock : public UndoableCommand
84 : {
85 54 : OBJECT_ALLOCATOR(engraving, RemoveSystemLock)
86 :
87 : const RangeLock* m_systemLock;
88 : public:
89 27 : RemoveSystemLock(const RangeLock* systemLock)
90 27 : : m_systemLock(systemLock) {}
91 :
92 0 : void undo() override
93 : {
94 0 : Score* score = m_systemLock->startMB()->score();
95 0 : score->addSystemLock(m_systemLock);
96 0 : }
97 :
98 27 : void redo() override
99 : {
100 27 : Score* score = m_systemLock->startMB()->score();
101 27 : score->removeSystemLock(m_systemLock);
102 27 : }
103 :
104 27 : void cleanup(bool wasDone) override
105 : {
106 27 : if (wasDone) {
107 27 : delete m_systemLock;
108 27 : m_systemLock = nullptr;
109 : }
110 27 : }
111 :
112 0 : UNDO_NAME("RemoveSystemLock")
113 27 : std::vector<EngravingObject*> objectItems() const override
114 : {
115 81 : return { m_systemLock->startMB(), m_systemLock->endMB() };
116 : }
117 : };
118 :
119 : //---------------------------------------------------------
120 : // EditSystemLocks
121 : //---------------------------------------------------------
122 :
123 28 : void EditSystemLocks::undoAddSystemLock(Transaction& tx, const RangeLock* lock)
124 : {
125 28 : updateLayoutBreaksOnAddSystemLock(tx, lock);
126 28 : tx.push(new AddSystemLock(lock));
127 28 : }
128 :
129 27 : void EditSystemLocks::undoRemoveSystemLock(Transaction& tx, const RangeLock* lock)
130 : {
131 27 : tx.push(new RemoveSystemLock(lock));
132 27 : }
133 :
134 0 : void EditSystemLocks::undoRemoveAllLocks(Transaction& tx, Score* score)
135 : {
136 0 : std::vector<const RangeLock*> allLocks = score->systemLocks()->allLocks(); // copy
137 0 : for (const RangeLock* lock : allLocks) {
138 0 : undoRemoveSystemLock(tx, lock);
139 : }
140 0 : }
141 :
142 4 : void EditSystemLocks::toggleSystemLock(Transaction& tx, Score* score, const std::vector<System*>& systems)
143 : {
144 4 : bool unlockAll = true;
145 5 : for (const System* system : systems) {
146 4 : if (!system->isLocked()) {
147 3 : unlockAll = false;
148 3 : break;
149 : }
150 : }
151 :
152 8 : for (System* system : systems) {
153 4 : MeasureBase* startMeas = system->first();
154 4 : const RangeLock* currentLock = score->systemLocks()->lockStartingAt(startMeas);
155 4 : if (currentLock && unlockAll) {
156 1 : undoRemoveSystemLock(tx, currentLock);
157 1 : continue;
158 3 : } else if (!currentLock && !unlockAll) {
159 3 : RangeLock* newSystemLock = new RangeLock(startMeas, system->last());
160 3 : undoAddSystemLock(tx, newSystemLock);
161 : }
162 : }
163 4 : }
164 :
165 2 : void EditSystemLocks::toggleScoreLock(Transaction& tx, Score* score)
166 : {
167 2 : bool unlockAll = true;
168 10 : for (const System* system : score->systems()) {
169 9 : const MeasureBase* first = system->first();
170 9 : if (!(first->isMeasure() || first->isHBox())) {
171 0 : continue;
172 : }
173 9 : if (!system->isLocked()) {
174 1 : unlockAll = false;
175 1 : break;
176 : }
177 : }
178 :
179 14 : for (System* system : score->systems()) {
180 12 : MeasureBase* startMeas = system->first();
181 12 : if (!(startMeas->isMeasure() || startMeas->isHBox())) {
182 0 : continue;
183 : }
184 12 : const RangeLock* currentLock = score->systemLocks()->lockStartingAt(startMeas);
185 12 : if (currentLock && unlockAll) {
186 8 : undoRemoveSystemLock(tx, currentLock);
187 8 : continue;
188 4 : } else if (!currentLock && !unlockAll) {
189 4 : RangeLock* newSystemLock = new RangeLock(startMeas, system->last());
190 4 : undoAddSystemLock(tx, newSystemLock);
191 : }
192 : }
193 2 : }
194 :
195 3 : void EditSystemLocks::addRemoveSystemLocks(Transaction& tx, Score* score, int interval, bool lock)
196 : {
197 3 : MeasureBase* startMeasure = score->selection().startMeasureBase();
198 3 : MeasureBase* endMeasure = score->selection().endMeasureBase();
199 3 : if (!endMeasure) {
200 0 : endMeasure = score->lastMeasureMM();
201 : }
202 :
203 3 : if (!startMeasure || !endMeasure) {
204 2 : return;
205 : }
206 :
207 3 : if (lock) {
208 5 : for (const System* system : score->systems()) {
209 4 : if (system->last()->isBefore(startMeasure)) {
210 0 : continue;
211 : }
212 4 : if (system->first()->isAfter(endMeasure)) {
213 0 : break;
214 : }
215 4 : if (!system->isLocked()) {
216 4 : undoAddSystemLock(tx, new RangeLock(system->first(), system->last()));
217 : }
218 : }
219 1 : return;
220 : }
221 :
222 2 : std::vector<const RangeLock*> currentLocks = score->systemLocks()->locksContainedInRange(startMeasure, endMeasure);
223 14 : for (const RangeLock* l : currentLocks) {
224 12 : undoRemoveSystemLock(tx, l);
225 : }
226 :
227 2 : if (interval == 0) {
228 1 : return;
229 : }
230 :
231 1 : int count = 0;
232 1 : MeasureBase* lockStart = nullptr;
233 32 : for (MeasureBase* mb = startMeasure; mb; mb = mb->nextMM()) {
234 32 : if (count == 0) {
235 8 : lockStart = mb;
236 : }
237 32 : count++;
238 32 : if (count == interval || mb == endMeasure) {
239 8 : undoAddSystemLock(tx, new RangeLock(lockStart, mb));
240 8 : lockStart = nullptr;
241 8 : count = 0;
242 : }
243 32 : if (mb == endMeasure) {
244 1 : break;
245 : }
246 : }
247 2 : }
248 :
249 4 : void EditSystemLocks::makeIntoSystem(Transaction& tx, Score* score, MeasureBase* first, MeasureBase* last)
250 : {
251 4 : const RangeLock* lockContainingfirst = score->systemLocks()->lockContaining(first);
252 4 : const RangeLock* lockContaininglast = score->systemLocks()->lockContaining(last);
253 :
254 4 : if (lockContainingfirst) {
255 3 : undoRemoveSystemLock(tx, lockContainingfirst);
256 3 : if (lockContainingfirst->startMB()->isBefore(first)) {
257 2 : MeasureBase* oneBeforeFirst = first->prevMM();
258 2 : RangeLock* newLockBefore = new RangeLock(lockContainingfirst->startMB(), oneBeforeFirst);
259 2 : undoAddSystemLock(tx, newLockBefore);
260 : }
261 : }
262 :
263 4 : if (lockContaininglast) {
264 3 : if (lockContaininglast != lockContainingfirst) {
265 3 : undoRemoveSystemLock(tx, lockContaininglast);
266 : }
267 3 : if (last->isBefore(lockContaininglast->endMB())) {
268 2 : MeasureBase* oneAfterLast = last->nextMM();
269 2 : RangeLock* newLockAfter = new RangeLock(oneAfterLast, lockContaininglast->endMB());
270 2 : undoAddSystemLock(tx, newLockAfter);
271 : }
272 : }
273 :
274 4 : std::vector<const RangeLock*> locksContainedInRange = score->systemLocks()->locksContainedInRange(first, last);
275 4 : for (const RangeLock* lock : locksContainedInRange) {
276 0 : if (lock != lockContainingfirst && lock != lockContaininglast) {
277 0 : undoRemoveSystemLock(tx, lock);
278 : }
279 : }
280 :
281 4 : MeasureBase* firstMB = first;
282 4 : MeasureBase* lastMB = last;
283 :
284 : // If a section break is within this range, create a new system lock after it
285 29 : for (MeasureBase* mb = first; mb && mb->isBeforeOrEqual(last); mb = mb->nextMM()) {
286 25 : if (!firstMB) {
287 0 : firstMB = mb;
288 : }
289 25 : lastMB = mb;
290 :
291 25 : if (mb->sectionBreak()) {
292 0 : RangeLock* newLock = new RangeLock(firstMB, lastMB);
293 0 : undoAddSystemLock(tx, newLock);
294 :
295 0 : firstMB = nullptr;
296 0 : lastMB = nullptr;
297 : }
298 : }
299 4 : if (!firstMB || !lastMB) {
300 0 : return;
301 : }
302 :
303 4 : RangeLock* newLock = new RangeLock(firstMB, lastMB);
304 4 : undoAddSystemLock(tx, newLock);
305 4 : }
306 :
307 1 : void EditSystemLocks::moveMeasureToPrevSystem(Transaction& tx, Score* score, MeasureBase* m)
308 : {
309 1 : const System* prevSystem = m->prevNonVBoxSystem();
310 1 : if (!prevSystem) {
311 0 : return;
312 : }
313 :
314 1 : MeasureBase* prevSystemFirstMeas = prevSystem->first();
315 :
316 1 : makeIntoSystem(tx, score, prevSystemFirstMeas, m);
317 : }
318 :
319 1 : void EditSystemLocks::moveMeasureToNextSystem(Transaction& tx, Score* score, MeasureBase* m)
320 : {
321 1 : const System* curSystem = m->system();
322 1 : MeasureBase* startMeas = curSystem->first();
323 1 : MeasureBase* systemCurEndMeasure = curSystem->last();
324 :
325 1 : const System* nextSystem = m->nextNonVBoxSystem();
326 1 : if (!nextSystem) {
327 0 : return;
328 : }
329 :
330 1 : const RangeLock* curLock = score->systemLocks()->lockStartingAt(startMeas);
331 1 : const RangeLock* nextSysLock = score->systemLocks()->lockStartingAt(nextSystem->first());
332 :
333 1 : MeasureBase* prevMeas = m->prevMM();
334 :
335 1 : if (!prevMeas) {
336 0 : return;
337 : }
338 :
339 1 : const bool pageBreak = systemCurEndMeasure->pageBreak();
340 1 : const bool endOfPageLock = systemCurEndMeasure->isEndOfPageLock();
341 1 : if (pageBreak) {
342 : // Move page break to measure preceding selection
343 0 : systemCurEndMeasure->undoSetBreak(false, LayoutBreakType::PAGE);
344 0 : prevMeas->undoSetBreak(true, LayoutBreakType::PAGE);
345 : }
346 :
347 1 : if (endOfPageLock) {
348 : // Move page lock to end at measure preceding selection
349 0 : const RangeLock* pageLock = systemCurEndMeasure->pageLock();
350 0 : MeasureBase* pageLockStartMb = pageLock->startMB();
351 :
352 0 : EditPageLocks::undoRemovePageLock(tx, pageLock);
353 :
354 0 : if (pageLockStartMb->isBeforeOrEqual(prevMeas)) {
355 0 : RangeLock* newPageLock = new RangeLock(pageLockStartMb, prevMeas);
356 0 : EditPageLocks::undoAddPageLock(tx, newPageLock);
357 : }
358 : }
359 :
360 1 : if (!curLock && !nextSysLock) {
361 0 : if (!pageBreak && !endOfPageLock && !prevMeas->systemLock()) {
362 : // Add a system break
363 0 : prevMeas->undoSetBreak(true, LayoutBreakType::LINE);
364 : }
365 0 : return;
366 : }
367 :
368 1 : if (curLock && !nextSysLock) {
369 0 : undoRemoveSystemLock(tx, curLock);
370 :
371 0 : MeasureBase* firstMeasure = curSystem->first();
372 0 : MeasureBase* lastMeasure = m->prev();
373 :
374 0 : undoAddSystemLock(tx, new RangeLock(firstMeasure, lastMeasure));
375 0 : return;
376 : }
377 :
378 1 : MeasureBase* lastMeasure = nextSystem->last();
379 1 : makeIntoSystem(tx, score, m, lastMeasure);
380 : }
381 :
382 1 : void EditSystemLocks::applyLockToSelection(Transaction& tx, Score* score)
383 : {
384 1 : MeasureBase* first = nullptr;
385 1 : MeasureBase* last = nullptr;
386 :
387 1 : if (score->selection().isRange()) {
388 1 : first = score->selection().startMeasureBase();
389 1 : last = score->selection().endMeasureBase();
390 : } else {
391 0 : for (EngravingItem* el : score->selection().elements()) {
392 0 : if (el->isSystemLockIndicator()) {
393 0 : const RangeLock* lock = toSystemLockIndicator(el)->systemLock();
394 0 : first = lock->startMB();
395 0 : last = lock->endMB();
396 0 : break;
397 : }
398 0 : MeasureBase* mb = el->findMeasureBase();
399 0 : if (!mb) {
400 0 : continue;
401 : }
402 0 : if (!first || mb->isBefore(first)) {
403 0 : first = mb;
404 : }
405 0 : if (!last || mb->isAfter(last)) {
406 0 : last = mb;
407 : }
408 : }
409 : }
410 :
411 1 : if (!first || !last) {
412 0 : return;
413 : }
414 :
415 1 : const RangeLock* lockOnLast = score->systemLocks()->lockContaining(last);
416 1 : if (lockOnLast && lockOnLast->endMB() == last) {
417 0 : undoRemoveSystemLock(tx, lockOnLast);
418 1 : } else if (first != last) {
419 1 : makeIntoSystem(tx, score, first, last);
420 : } else {
421 0 : makeIntoSystem(tx, score, first->system()->first(), last);
422 : }
423 : }
424 :
425 0 : void EditSystemLocks::removeSystemLocksOnAddLayoutBreak(Transaction& tx, Score* score, LayoutBreakType breakType,
426 : const MeasureBase* measure)
427 : {
428 0 : IF_ASSERT_FAILED(breakType != LayoutBreakType::NOBREAK) {
429 0 : return; // NOBREAK not allowed on locked measures
430 : }
431 :
432 0 : const RangeLock* lock = score->systemLocks()->lockContaining(measure);
433 0 : MeasureBase* lockEndMeasure = lock ? lock->endMB() : nullptr;
434 0 : if (lock && (breakType == LayoutBreakType::LINE || measure != lockEndMeasure)) {
435 0 : undoRemoveSystemLock(tx, lock);
436 :
437 0 : if (measure != lockEndMeasure && measure->next()) {
438 0 : undoAddSystemLock(tx, new RangeLock(measure->next(), lockEndMeasure));
439 : }
440 : }
441 : }
442 :
443 28 : void EditSystemLocks::updateLayoutBreaksOnAddSystemLock(Transaction& tx, const RangeLock* lock)
444 : {
445 28 : bool moveSectionBreak = false;
446 28 : bool movePageBreak = false;
447 28 : MeasureBase* pageLockStartMB = nullptr;
448 170 : for (MeasureBase* mb = lock->startMB(); mb && mb->isBeforeOrEqual(lock->endMB()); mb = mb->nextMM()) {
449 142 : mb->undoSetBreak(false, LayoutBreakType::LINE);
450 142 : if (mb != lock->endMB()) {
451 : // Move existing page breaks and section breaks to the end of the new range
452 114 : moveSectionBreak |= mb->sectionBreak();
453 114 : movePageBreak |= mb->pageBreak();
454 114 : mb->undoSetBreak(false, LayoutBreakType::SECTION);
455 114 : mb->undoSetBreak(false, LayoutBreakType::PAGE);
456 : } else {
457 : // End of system lock
458 : // System lock and no-break would conflict
459 28 : mb->undoSetBreak(false, LayoutBreakType::NOBREAK);
460 : }
461 :
462 142 : if (mb != lock->startMB() && mb->isStartOfPageLock()) {
463 : // Page lock starting within range of new system lock
464 : // Move start of page lock to measure following end of system lock
465 : // (a page lock starting together with the system lock does not conflict with it)
466 0 : const RangeLock* pageLock = mb->pageLock();
467 0 : MeasureBase* pageLockEndMB = pageLock->endMB();
468 :
469 0 : if (pageLockEndMB->isAfter(lock->endMB()) && lock->endMB()->next()) {
470 0 : EditPageLocks::undoRemovePageLock(tx, pageLock);
471 0 : RangeLock* newPageLock = new RangeLock(lock->endMB()->next(), pageLockEndMB);
472 0 : EditPageLocks::undoAddPageLock(tx, newPageLock);
473 : }
474 : }
475 :
476 142 : if (mb->isEndOfPageLock()) {
477 : // Create an updated page lock which extends to the end of the new range
478 1 : const RangeLock* pageLock = mb->pageLock();
479 1 : if (!pageLockStartMB) {
480 1 : pageLockStartMB = pageLock->startMB();
481 : }
482 1 : EditPageLocks::undoRemovePageLock(tx, pageLock);
483 : }
484 : }
485 :
486 28 : if (pageLockStartMB) {
487 1 : RangeLock* newPageLock = new RangeLock(pageLockStartMB, lock->endMB());
488 1 : EditPageLocks::undoAddPageLock(tx, newPageLock);
489 : }
490 28 : if (moveSectionBreak) {
491 0 : lock->endMB()->undoSetBreak(true, LayoutBreakType::SECTION);
492 : }
493 28 : if (movePageBreak) {
494 0 : lock->endMB()->undoSetBreak(true, LayoutBreakType::PAGE);
495 : }
496 28 : }
497 :
498 62 : void EditSystemLocks::removeSystemLocksOnRemoveMeasures(Transaction& tx, Score* score, const MeasureBase* m1, const MeasureBase* m2)
499 : {
500 62 : std::vector<const RangeLock*> allSysLocks = score->systemLocks()->allLocks();
501 62 : for (const RangeLock* lock : allSysLocks) {
502 0 : MeasureBase* lockStart = lock->startMB();
503 0 : MeasureBase* lockEnd = lock->endMB();
504 0 : bool lockStartIsInRange = lockStart->isAfterOrEqual(m1) && lockStart->isBeforeOrEqual(m2);
505 0 : bool lockEndIsInRange = lockEnd->isAfterOrEqual(m1) && lockEnd->isBeforeOrEqual(m2);
506 0 : if (lockStartIsInRange || lockEndIsInRange) {
507 0 : undoRemoveSystemLock(tx, lock);
508 : }
509 0 : if (lockStartIsInRange && !lockEndIsInRange) {
510 0 : MeasureBase* newLockStart = m2->nextMeasure();
511 0 : if (newLockStart) {
512 0 : undoAddSystemLock(tx, new RangeLock(newLockStart, lockEnd));
513 : }
514 0 : } else if (!lockStartIsInRange && lockEndIsInRange) {
515 0 : MeasureBase* newLockEnd = m1->prevMeasure();
516 0 : if (newLockEnd) {
517 0 : undoAddSystemLock(tx, new RangeLock(lockStart, newLockEnd));
518 : }
519 : }
520 : }
521 62 : }
522 :
523 2 : void EditSystemLocks::removeSystemLocksContainingMMRests(Transaction& tx, Score* score)
524 : {
525 2 : std::vector<const RangeLock*> allLocks = score->systemLocks()->allLocks(); // copy
526 2 : for (const RangeLock* lock : allLocks) {
527 0 : for (MeasureBase* mb = lock->startMB(); mb; mb = mb->next()) {
528 0 : if (mb->isMeasure() && toMeasure(mb)->mmRest()) {
529 0 : undoRemoveSystemLock(tx, lock);
530 0 : break;
531 : }
532 0 : if (mb->isAfter(lock->endMB())) {
533 0 : break;
534 : }
535 : }
536 : }
537 2 : }
538 :
539 166 : void EditSystemLocks::updateSystemLocksOnCreateMMRests(Transaction& tx, Score* score, Measure* first, Measure* last)
540 : {
541 : // NOTE: this must be done during layout as the mmRests get created.
542 :
543 166 : for (const RangeLock* lock : score->systemLocks()->locksContainedInRange(first, last)) {
544 : // These locks are inside the range of the mmRest so remove them
545 0 : undoRemoveSystemLock(tx, lock);
546 166 : }
547 :
548 166 : const RangeLock* lockOnFirst = score->systemLocks()->lockContaining(first);
549 166 : const RangeLock* lockOnLast = score->systemLocks()->lockContaining(last);
550 :
551 166 : if (lockOnFirst) {
552 0 : MeasureBase* startMB = lockOnFirst->startMB();
553 0 : MeasureBase* endMB = lockOnFirst->endMB();
554 :
555 0 : if (startMB->isBefore(first)) {
556 0 : if (endMB->isBeforeOrEqual(last)) {
557 0 : endMB = first->mmRest();
558 : } else {
559 0 : return;
560 : }
561 : } else {
562 0 : startMB = first->mmRest();
563 : }
564 :
565 0 : if (startMB != lockOnFirst->startMB() || endMB != lockOnFirst->endMB()) {
566 0 : undoRemoveSystemLock(tx, lockOnFirst);
567 0 : undoAddSystemLock(tx, new RangeLock(startMB, endMB));
568 : }
569 : }
570 :
571 166 : if (!lockOnLast || lockOnLast == lockOnFirst) {
572 166 : return;
573 : }
574 :
575 0 : MeasureBase* startMB = lockOnLast->startMB();
576 0 : MeasureBase* endMB = lockOnLast->endMB();
577 0 : assert(startMB->isAfter(first) && endMB->isAfter(last));
578 :
579 0 : undoRemoveSystemLock(tx, lockOnLast);
580 0 : startMB = last->nextMM();
581 0 : undoAddSystemLock(tx, new RangeLock(startMB, endMB));
582 : }
|