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 "editpart.h"
24 : #include "editscoreproperties.h"
25 : #include "editstaff.h"
26 : #include "transaction/transaction.h"
27 : #include "transpose.h"
28 :
29 : #include "../dom/excerpt.h"
30 : #include "../dom/factory.h"
31 : #include "../dom/instrchange.h"
32 : #include "../dom/instrtemplate.h"
33 : #include "../dom/scoreorder.h"
34 : #include "../dom/masterscore.h"
35 : #include "../dom/mscore.h"
36 : #include "../dom/part.h"
37 : #include "../dom/score.h"
38 : #include "../dom/segment.h"
39 : #include "../dom/staff.h"
40 : #include "../dom/stafftype.h"
41 : #include "../dom/stringtunings.h"
42 : #include "../dom/utils.h"
43 :
44 : using namespace mu::engraving;
45 :
46 : //---------------------------------------------------------
47 : // InsertPart
48 : //---------------------------------------------------------
49 :
50 14 : InsertPart::InsertPart(Part* p, size_t targetPartIdx)
51 : {
52 14 : m_part = p;
53 14 : m_targetPartIdx = targetPartIdx;
54 14 : }
55 :
56 6 : void InsertPart::undo()
57 : {
58 6 : m_part->score()->removePart(m_part);
59 6 : }
60 :
61 15 : void InsertPart::redo()
62 : {
63 15 : m_part->score()->insertPart(m_part, m_targetPartIdx);
64 15 : }
65 :
66 11 : void InsertPart::cleanup(bool wasDone)
67 : {
68 11 : if (!wasDone) {
69 5 : delete m_part;
70 5 : m_part = nullptr;
71 : }
72 11 : }
73 :
74 : //---------------------------------------------------------
75 : // RemovePart
76 : //---------------------------------------------------------
77 :
78 8 : RemovePart::RemovePart(Part* p, size_t partIdx)
79 : {
80 8 : m_part = p;
81 8 : m_partIdx = partIdx;
82 :
83 8 : if (m_partIdx == muse::nidx) {
84 0 : m_partIdx = muse::indexOf(m_part->score()->parts(), m_part);
85 : }
86 8 : }
87 :
88 3 : void RemovePart::undo()
89 : {
90 3 : m_part->score()->insertPart(m_part, m_partIdx);
91 3 : }
92 :
93 8 : void RemovePart::redo()
94 : {
95 8 : m_part->score()->removePart(m_part);
96 8 : }
97 :
98 3 : void RemovePart::cleanup(bool wasDone)
99 : {
100 3 : if (wasDone) {
101 0 : delete m_part;
102 0 : m_part = nullptr;
103 : }
104 3 : }
105 :
106 : //---------------------------------------------------------
107 : // SetSoloist
108 : //---------------------------------------------------------
109 :
110 0 : SetSoloist::SetSoloist(Part* p, bool b)
111 : {
112 0 : part = p;
113 0 : soloist = b;
114 0 : }
115 :
116 0 : void SetSoloist::undo()
117 : {
118 0 : part->setSoloist(!soloist);
119 0 : }
120 :
121 0 : void SetSoloist::redo()
122 : {
123 0 : part->setSoloist(soloist);
124 0 : }
125 :
126 : //---------------------------------------------------------
127 : // ChangePart
128 : //---------------------------------------------------------
129 :
130 4 : ChangePart::ChangePart(Part* _part, Instrument* i)
131 : {
132 4 : instrument = i;
133 4 : part = _part;
134 4 : }
135 :
136 7 : void ChangePart::flip()
137 : {
138 7 : Instrument* oi = part->instrument(); //tick?
139 7 : part->setInstrument(instrument);
140 :
141 7 : part->updateHarmonyChannels(false);
142 :
143 7 : Score* score = part->score();
144 7 : score->masterScore()->rebuildMidiMapping();
145 7 : score->setInstrumentsChanged(true);
146 :
147 : // check if notes need to be updated
148 : // true if changing into or away from TAB or from one TAB type to another
149 :
150 7 : score->setLayoutAll();
151 :
152 7 : instrument = oi;
153 7 : }
154 :
155 3 : void ChangePart::cleanup(bool)
156 : {
157 3 : delete instrument;
158 3 : instrument = nullptr;
159 3 : }
160 :
161 : //---------------------------------------------------------
162 : // ChangeInstrumentLong
163 : //---------------------------------------------------------
164 :
165 2 : ChangeInstrumentLong::ChangeInstrumentLong(const Fraction& _tick, Part* p, const String& t)
166 2 : : part(p), tick(_tick), longName(t)
167 : {
168 2 : }
169 :
170 3 : void ChangeInstrumentLong::flip()
171 : {
172 3 : String s = part->longName(tick);
173 3 : part->setLongName(longName, tick);
174 3 : longName = s;
175 3 : part->score()->setLayoutAll();
176 3 : }
177 :
178 : //---------------------------------------------------------
179 : // ChangeInstrumentShort
180 : //---------------------------------------------------------
181 :
182 1 : ChangeInstrumentShort::ChangeInstrumentShort(const Fraction& _tick, Part* p, const String& t)
183 1 : : part(p), tick(_tick), shortName(t)
184 : {
185 1 : }
186 :
187 2 : void ChangeInstrumentShort::flip()
188 : {
189 2 : String s = part->shortName(tick);
190 2 : part->setShortName(shortName, tick);
191 2 : shortName = s;
192 2 : part->score()->setLayoutAll();
193 2 : }
194 :
195 : //---------------------------------------------------------
196 : // ChangeInstrumentLong
197 : //---------------------------------------------------------
198 :
199 0 : ChangeInstrumentGroupOptions::ChangeInstrumentGroupOptions(const Fraction& _tick, Part* p, bool useCustom, const String& longName,
200 0 : const String& shortName)
201 0 : : part(p), tick(_tick), useCustom(useCustom), longName(longName), shortName(shortName)
202 : {
203 0 : }
204 :
205 0 : void ChangeInstrumentGroupOptions::flip()
206 : {
207 0 : InstrumentLabel& label = part->instrument(tick)->instrumentLabel();
208 :
209 0 : bool curUseCustom = label.useCustomGroupName();
210 0 : const String& curLong = label.customNameLongGroup();
211 0 : const String& curShort = label.customNameShortGroup();
212 :
213 0 : label.setUseCustomGroupName(useCustom);
214 0 : label.setCustomNameLongGroup(longName);
215 0 : label.setCustomNameShortGroup(shortName);
216 :
217 0 : useCustom = curUseCustom;
218 0 : longName = curLong;
219 0 : shortName = curShort;
220 :
221 0 : part->score()->setLayoutAll();
222 0 : }
223 :
224 0 : ChangeInstrumentNumber::ChangeInstrumentNumber(const Fraction& _tick, Part* p, int v)
225 0 : : part(p), tick(_tick), number(v)
226 : {
227 0 : }
228 :
229 0 : void ChangeInstrumentNumber::flip()
230 : {
231 0 : int v = part->number(tick);
232 0 : part->setNumber(number, tick);
233 0 : number = v;
234 0 : part->score()->setLayoutAll();
235 0 : }
236 :
237 : //---------------------------------------------------------
238 : // ChangeDrumset
239 : //---------------------------------------------------------
240 :
241 3 : void ChangeDrumset::flip()
242 : {
243 3 : Drumset d = instrument->drumset() ? *instrument->drumset() : Drumset();
244 3 : instrument->setDrumset(&drumset);
245 3 : drumset = d;
246 :
247 3 : if (part->staves().size() > 0) {
248 3 : part->score()->setLayout(Fraction(0, 1), part->score()->endTick(), part->staves().front()->idx(), part->staves().back()->idx());
249 : }
250 3 : }
251 :
252 : //---------------------------------------------------------
253 : // ChangeStringData
254 : //---------------------------------------------------------
255 :
256 0 : void ChangeStringData::flip()
257 : {
258 0 : const StringData* stringData = m_stringTunings ? m_stringTunings->stringData() : m_instrument->stringData();
259 0 : int frets = stringData->frets();
260 0 : std::vector<instrString> stringList = stringData->stringList();
261 :
262 0 : if (m_stringTunings) {
263 0 : m_stringTunings->setStringData(m_stringData);
264 : } else {
265 0 : m_instrument->setStringData(m_stringData);
266 : }
267 :
268 0 : m_stringData.set(StringData(frets, stringList));
269 0 : }
270 :
271 : //---------------------------------------------------------
272 : // ChangePatch
273 : //---------------------------------------------------------
274 :
275 1 : void ChangePatch::flip()
276 : {
277 1 : MidiPatch op;
278 1 : op.prog = channel->program();
279 1 : op.bank = channel->bank();
280 1 : op.synti = channel->synti();
281 :
282 1 : channel->setProgram(patch.prog);
283 1 : channel->setBank(patch.bank);
284 1 : channel->setSynti(patch.synti);
285 :
286 1 : patch = op;
287 :
288 : //! TODO Needs porting for MU4
289 : // if (MScore::seq == 0) {
290 : // LOGW("no seq");
291 : // return;
292 : // }
293 : //
294 : // NPlayEvent event;
295 : // event.setType(ME_CONTROLLER);
296 : // event.setChannel(channel->channel());
297 : //
298 : // int hbank = (channel->bank() >> 7) & 0x7f;
299 : // int lbank = channel->bank() & 0x7f;
300 : //
301 : // event.setController(CTRL_HBANK);
302 : // event.setValue(hbank);
303 : // MScore::seq->sendEvent(event);
304 : //
305 : // event.setController(CTRL_LBANK);
306 : // event.setValue(lbank);
307 : // MScore::seq->sendEvent(event);
308 : //
309 : // event.setController(CTRL_PROGRAM);
310 : // event.setValue(channel->program());
311 : //
312 1 : score->setInstrumentsChanged(true);
313 : //
314 : // MScore::seq->sendEvent(event);
315 1 : }
316 :
317 : //---------------------------------------------------------
318 : // SetUserBankController
319 : //---------------------------------------------------------
320 :
321 0 : void SetUserBankController::flip()
322 : {
323 0 : bool oldVal = channel->userBankController();
324 0 : channel->setUserBankController(val);
325 0 : val = oldVal;
326 0 : }
327 :
328 : //---------------------------------------------------------
329 : // findInstrumentChange
330 : // Helper function to find InstrumentChange element at a given tick
331 : //---------------------------------------------------------
332 :
333 0 : static InstrumentChange* findInstrumentChange(Score* score, const Part* part, const Fraction& tick)
334 : {
335 0 : const Segment* segment = score->tick2segment(tick, true, SegmentType::ChordRest);
336 0 : if (!segment) {
337 0 : return nullptr;
338 : }
339 :
340 0 : const TrackRange trackRange = part->trackRange();
341 0 : EngravingItem* item = segment->findAnnotation(ElementType::INSTRUMENT_CHANGE, trackRange.startTrack, trackRange.endTrack - 1);
342 0 : return item ? toInstrumentChange(item) : nullptr;
343 : }
344 :
345 1 : void EditPart::replacePartInstrument(Score* score, Part* part, const Instrument& newInstrument,
346 : const StaffType* newStaffType)
347 : {
348 1 : if (!score || !part) {
349 0 : return;
350 : }
351 :
352 1 : score->undo(new ChangePart(part, new Instrument(newInstrument)));
353 :
354 : // Update clefs and staff type for all staves in the part
355 2 : for (staff_idx_t staffIdx = 0; staffIdx < part->nstaves(); ++staffIdx) {
356 1 : Staff* staff = part->staves().at(staffIdx);
357 1 : if (!staff) {
358 0 : continue;
359 : }
360 :
361 : // Get current staff properties
362 1 : bool visible = staff->visible();
363 1 : ClefTypeList currentClefType = staff->defaultClefType();
364 1 : Spatium userDist = staff->userDist();
365 1 : bool cutaway = staff->cutaway();
366 1 : bool hideSystemBarLine = staff->hideSystemBarLine();
367 1 : AutoOnOff mergeMatchingRests = staff->mergeMatchingRests();
368 1 : bool reflectTransposition = staff->reflectTranspositionInLinkedTab();
369 :
370 : // Get new clef type from the new instrument
371 1 : ClefTypeList newClefType = newInstrument.clefType(staffIdx);
372 :
373 : // Only update staff if clef type changes
374 1 : if (currentClefType != newClefType) {
375 0 : score->undo(new ChangeStaff(staff, visible, newClefType, userDist, cutaway,
376 0 : hideSystemBarLine, mergeMatchingRests, reflectTransposition));
377 : }
378 :
379 : // Apply new staff type if provided
380 1 : if (newStaffType) {
381 0 : score->undo(new ChangeStaffType(staff, *newStaffType));
382 : }
383 : }
384 : }
385 :
386 0 : bool EditPart::replaceInstrumentAtTick(Score* score, Part* part, const Fraction& tick,
387 : const Instrument& newInstrument)
388 : {
389 0 : if (!score || !part) {
390 0 : return false;
391 : }
392 :
393 0 : InstrumentChange* instrumentChange = findInstrumentChange(score, part, tick);
394 0 : if (!instrumentChange) {
395 0 : return false;
396 : }
397 :
398 0 : instrumentChange->setInit(true);
399 0 : instrumentChange->setupInstrument(&newInstrument);
400 0 : return true;
401 : }
402 :
403 9 : void EditPart::setPartVisible(Score* score, Part* part, bool visible)
404 : {
405 9 : if (!score || !part) {
406 0 : return;
407 : }
408 :
409 9 : part->undoChangeProperty(Pid::VISIBLE, visible);
410 : }
411 :
412 1 : void EditPart::setStaffVisible(Score* score, Staff* staff, bool visible)
413 : {
414 1 : if (!score || !staff) {
415 0 : return;
416 : }
417 :
418 1 : staff->undoChangeProperty(Pid::VISIBLE, visible);
419 : }
420 :
421 1 : void EditPart::setPartSharpFlat(Score* score, Part* part, PreferSharpFlat sharpFlat)
422 : {
423 1 : if (!score || !part) {
424 0 : return;
425 : }
426 :
427 1 : Interval oldTransposition = part->staff(0)->transpose(Fraction(0, 1));
428 :
429 1 : part->undoChangeProperty(Pid::PREFER_SHARP_FLAT, int(sharpFlat));
430 :
431 1 : Transaction& tx = score->transactionManager()->currentOrDummyTransaction();
432 1 : Transpose::transpositionChanged(tx, score, part, oldTransposition);
433 : }
434 :
435 2 : void EditPart::setInstrumentName(Score* score, Part* part, const Fraction& tick, const String& name)
436 : {
437 2 : if (!score || !part) {
438 0 : return;
439 : }
440 :
441 2 : score->undo(new ChangeInstrumentLong(tick, part, name));
442 : }
443 :
444 1 : void EditPart::setInstrumentAbbreviature(Score* score, Part* part, const Fraction& tick, const String& abbreviature)
445 : {
446 1 : if (!score || !part) {
447 0 : return;
448 : }
449 :
450 1 : score->undo(new ChangeInstrumentShort(tick, part, abbreviature));
451 : }
452 :
453 0 : void EditPart::setInstrumentGroupNameOptions(Score* score, Part* part, const Fraction& tick, bool useCustom, const String& longName,
454 : const String& shortName)
455 : {
456 0 : if (!score || !part) {
457 0 : return;
458 : }
459 :
460 0 : score->undo(new ChangeInstrumentGroupOptions(tick, part, useCustom, longName, shortName));
461 : }
462 :
463 2 : void EditPart::setStaffType(Score* score, Staff* staff, StaffTypes typeId)
464 : {
465 2 : if (!score || !staff) {
466 0 : return;
467 : }
468 :
469 2 : const StaffType* staffType = StaffType::preset(typeId);
470 2 : if (!staffType) {
471 0 : return;
472 : }
473 :
474 2 : score->undo(new ChangeStaffType(staff, *staffType));
475 : }
476 :
477 2 : void EditPart::removeParts(Score* score, const std::vector<Part*>& parts)
478 : {
479 2 : if (!score || parts.empty()) {
480 0 : return;
481 : }
482 :
483 4 : for (Part* part : parts) {
484 2 : score->cmdRemovePart(part);
485 : }
486 :
487 2 : score->setBracketsAndBarlines();
488 : }
489 :
490 1 : void EditPart::removeStaves(Score* score, const std::vector<Staff*>& staves)
491 : {
492 1 : if (!score || staves.empty()) {
493 0 : return;
494 : }
495 :
496 2 : for (Staff* staff : staves) {
497 1 : score->cmdRemoveStaff(staff->idx());
498 : }
499 :
500 1 : score->setBracketsAndBarlines();
501 : }
502 :
503 2 : void EditPart::moveParts(Score* score, const std::vector<Part*>& sourceParts, Part* destinationPart, bool insertAfter)
504 : {
505 2 : if (!score || sourceParts.empty() || !destinationPart) {
506 0 : return;
507 : }
508 :
509 : // Build new part order: remove source parts, then insert at destination
510 2 : std::vector<Part*> allParts(score->parts().begin(), score->parts().end());
511 :
512 : // Remove source parts from the list
513 4 : for (Part* srcPart : sourceParts) {
514 2 : allParts.erase(std::remove(allParts.begin(), allParts.end(), srcPart), allParts.end());
515 : }
516 :
517 : // Find destination index
518 2 : auto dstIt = std::find(allParts.begin(), allParts.end(), destinationPart);
519 2 : if (dstIt == allParts.end()) {
520 0 : return;
521 : }
522 :
523 2 : size_t dstIndex = std::distance(allParts.begin(), dstIt);
524 2 : if (insertAfter) {
525 0 : dstIndex++;
526 : }
527 :
528 : // Insert source parts at destination
529 4 : for (size_t i = 0; i < sourceParts.size(); ++i) {
530 2 : allParts.insert(allParts.begin() + dstIndex + i, sourceParts[i]);
531 : }
532 :
533 : // Build staff index mapping from the new part order
534 2 : std::vector<staff_idx_t> staffMapping;
535 2 : staffMapping.reserve(score->nstaves());
536 7 : for (Part* part : allParts) {
537 10 : for (Staff* staff : part->staves()) {
538 5 : staffMapping.push_back(staff->idx());
539 : }
540 : }
541 :
542 2 : score->undo(new SortStaves(score, staffMapping));
543 2 : score->setBracketsAndBarlines();
544 2 : }
545 :
546 1 : void EditPart::moveStaves(Score* score, const std::vector<Staff*>& sourceStaves, Staff* destinationStaff, bool insertAfter)
547 : {
548 1 : if (!score || sourceStaves.empty() || !destinationStaff) {
549 0 : return;
550 : }
551 :
552 : // Build new staff order: remove source staves, then insert at destination
553 1 : std::vector<Staff*> allStaves(score->staves().begin(), score->staves().end());
554 :
555 : // Remove source staves from the list
556 2 : for (Staff* srcStaff : sourceStaves) {
557 1 : allStaves.erase(std::remove(allStaves.begin(), allStaves.end(), srcStaff), allStaves.end());
558 : }
559 :
560 : // Find destination index
561 1 : auto dstIt = std::find(allStaves.begin(), allStaves.end(), destinationStaff);
562 1 : if (dstIt == allStaves.end()) {
563 0 : return;
564 : }
565 :
566 1 : size_t dstIndex = std::distance(allStaves.begin(), dstIt);
567 1 : if (insertAfter) {
568 0 : dstIndex++;
569 : }
570 :
571 : // Insert source staves at destination
572 2 : for (size_t i = 0; i < sourceStaves.size(); ++i) {
573 1 : allStaves.insert(allStaves.begin() + dstIndex + i, sourceStaves[i]);
574 : }
575 :
576 : // Build index mapping
577 1 : std::vector<staff_idx_t> sortedIndexes;
578 1 : sortedIndexes.reserve(allStaves.size());
579 3 : for (const Staff* staff : allStaves) {
580 2 : sortedIndexes.push_back(staff->idx());
581 : }
582 :
583 1 : score->undo(new SortStaves(score, sortedIndexes));
584 1 : score->setBracketsAndBarlines();
585 1 : }
586 :
587 0 : void EditPart::addSystemObjects(Score* score, const std::vector<Staff*>& staves)
588 : {
589 0 : if (!score || staves.empty()) {
590 0 : return;
591 : }
592 :
593 0 : std::vector<EngravingItem*> topSystemObjects = collectSystemObjects(score);
594 :
595 0 : for (Staff* staff : staves) {
596 0 : if (staff->isSystemObjectStaff()) {
597 0 : continue;
598 : }
599 :
600 0 : score->undo(new AddSystemObjectStaff(staff));
601 :
602 0 : const staff_idx_t staffIdx = staff->idx();
603 0 : for (EngravingItem* obj : topSystemObjects) {
604 0 : if (obj->isTimeSig()) {
605 0 : obj->triggerLayout();
606 0 : continue;
607 : }
608 0 : EngravingItem* copy = obj->linkedClone();
609 0 : copy->setStaffIdx(staffIdx);
610 0 : score->undoAddElement(copy, false /*addToLinkedStaves*/);
611 : }
612 : }
613 0 : }
614 :
615 0 : void EditPart::removeSystemObjects(Score* score, const std::vector<Staff*>& staves)
616 : {
617 0 : if (!score || staves.empty()) {
618 0 : return;
619 : }
620 :
621 0 : std::vector<EngravingItem*> systemObjects = collectSystemObjects(score, staves);
622 :
623 0 : for (Staff* staff : staves) {
624 0 : if (staff->isSystemObjectStaff()) {
625 0 : score->undo(new RemoveSystemObjectStaff(staff));
626 0 : if (staff->hasSystemObjectsBelowBottomStaff()) {
627 0 : score->undoChangeStyleVal(Sid::systemObjectsBelowBottomStaff, false);
628 : }
629 : }
630 : }
631 :
632 0 : for (EngravingItem* obj : systemObjects) {
633 0 : if (obj->isTimeSig()) {
634 0 : obj->triggerLayout();
635 0 : continue;
636 : }
637 0 : obj->undoUnlink();
638 0 : score->undoRemoveElement(obj, false /*removeLinked*/);
639 : }
640 0 : }
641 :
642 0 : void EditPart::moveSystemObjects(Score* score, Staff* sourceStaff, Staff* destinationStaff)
643 : {
644 0 : if (!score || !sourceStaff || !destinationStaff) {
645 0 : return;
646 : }
647 :
648 0 : if (!sourceStaff->isSystemObjectStaff()) {
649 0 : return;
650 : }
651 :
652 0 : const std::vector<EngravingItem*> systemObjects = collectSystemObjects(score, { sourceStaff, destinationStaff });
653 0 : const staff_idx_t dstStaffIdx = destinationStaff->idx();
654 :
655 0 : score->undo(new RemoveSystemObjectStaff(sourceStaff));
656 0 : if (!destinationStaff->isSystemObjectStaff() && dstStaffIdx != 0) {
657 0 : score->undo(new AddSystemObjectStaff(destinationStaff));
658 : } else {
659 0 : score->undoChangeStyleVal(Sid::systemObjectsBelowBottomStaff, false);
660 : }
661 :
662 0 : AutoOnOff showMeasNumOnSrcStaff = sourceStaff->getProperty(Pid::SHOW_MEASURE_NUMBERS).value<AutoOnOff>();
663 0 : if (showMeasNumOnSrcStaff != AutoOnOff::AUTO) {
664 0 : destinationStaff->undoChangeProperty(Pid::SHOW_MEASURE_NUMBERS, showMeasNumOnSrcStaff);
665 0 : sourceStaff->undoResetProperty(Pid::SHOW_MEASURE_NUMBERS);
666 : }
667 :
668 : // Remove items from destination first
669 0 : for (EngravingItem* item : systemObjects) {
670 0 : if (item->isTimeSig()) {
671 0 : item->triggerLayout();
672 0 : continue;
673 : }
674 :
675 0 : if (item->staff() == sourceStaff) {
676 0 : continue;
677 : }
678 0 : item->undoUnlink();
679 0 : score->undoRemoveElement(item, false /*removeLinked*/);
680 : }
681 :
682 : // Move items from source to destination
683 0 : for (EngravingItem* item : systemObjects) {
684 0 : if (item->isTimeSig()) {
685 0 : continue;
686 : }
687 :
688 0 : if (item->staff() == sourceStaff) {
689 0 : item->undoChangeProperty(Pid::TRACK, staff2track(dstStaffIdx, item->voice()));
690 : }
691 : }
692 0 : }
693 :
694 4 : void EditPart::doAppendStaff(Score* score, Staff* staff, Part* destinationPart, bool createRests)
695 : {
696 4 : if (!score || !staff || !destinationPart) {
697 0 : return;
698 : }
699 :
700 4 : staff_idx_t staffLocalIndex = destinationPart->nstaves();
701 4 : KeyList keyList = *destinationPart->staff(staffLocalIndex - 1)->keyList();
702 :
703 4 : staff->setScore(score);
704 4 : staff->setPart(destinationPart);
705 :
706 4 : score->undoInsertStaff(staff, staffLocalIndex, createRests);
707 :
708 4 : staff_idx_t staffGlobalIndex = staff->idx();
709 4 : score->adjustKeySigs(staffGlobalIndex, staffGlobalIndex + 1, keyList);
710 :
711 4 : score->updateBracesAndBarlines(destinationPart, staffLocalIndex);
712 :
713 4 : destinationPart->instrument()->setClefType(staffLocalIndex, staff->defaultClefType());
714 4 : }
715 :
716 2 : Staff* EditPart::appendStaff(Score* score, Part* destinationPart)
717 : {
718 2 : if (!score || !destinationPart) {
719 0 : return nullptr;
720 : }
721 :
722 2 : Staff* staff = Factory::createStaff(destinationPart);
723 2 : doAppendStaff(score, staff, destinationPart);
724 2 : return staff;
725 : }
726 :
727 2 : Staff* EditPart::appendLinkedStaff(Score* score, Staff* sourceStaff, Part* destinationPart)
728 : {
729 2 : if (!score || !sourceStaff || !destinationPart) {
730 0 : return nullptr;
731 : }
732 :
733 2 : Staff* staff = Factory::createStaff(destinationPart);
734 2 : doAppendStaff(score, staff, destinationPart, false);
735 :
736 2 : staff->setLinks(nullptr);
737 2 : Excerpt::cloneStaff(sourceStaff, staff);
738 :
739 2 : return staff;
740 : }
741 :
742 2 : bool EditPart::setVoiceVisible(Score* score, Staff* staff, int voiceIndex, bool visible)
743 : {
744 2 : if (!score || !staff) {
745 0 : return false;
746 : }
747 :
748 2 : if (!score->excerpt()) {
749 2 : return false;
750 : }
751 :
752 0 : if (!visible && !staff->canDisableVoice()) {
753 0 : return false;
754 : }
755 :
756 0 : score->excerpt()->setVoiceVisible(staff, voiceIndex, visible);
757 0 : return true;
758 : }
759 :
760 4 : void EditPart::insertPart(Score* score, const InstrumentTemplate* templ, size_t index)
761 : {
762 4 : if (!score || !templ) {
763 0 : return;
764 : }
765 :
766 4 : Part* part = new Part(score);
767 4 : part->initFromInstrTemplate(templ);
768 :
769 8 : for (staff_idx_t i = 0; i < templ->staffCount; ++i) {
770 4 : Staff* staff = Factory::createStaff(part);
771 4 : StaffType* stt = staff->staffType(Fraction(0, 1));
772 4 : score->undoInsertStaff(staff, i);
773 4 : staff->init(templ, stt, int(i));
774 : }
775 :
776 4 : score->undoInsertPart(part, index);
777 4 : score->masterScore()->rebuildMidiMapping();
778 : }
779 :
780 2 : void EditPart::replacePart(Score* score, Part* oldPart, const InstrumentTemplate* templ)
781 : {
782 2 : if (!score || !oldPart || !templ) {
783 0 : return;
784 : }
785 :
786 2 : size_t partIndex = muse::indexOf(score->parts(), oldPart);
787 2 : score->cmdRemovePart(oldPart);
788 2 : insertPart(score, templ, partIndex);
789 : }
790 :
791 2 : void EditPart::replaceDrumset(Score* score, Part* part, const Fraction& tick, const Drumset& newDrumset)
792 : {
793 2 : if (!score || !part) {
794 0 : return;
795 : }
796 :
797 2 : Instrument* instrument = part->instrument(tick);
798 2 : if (instrument) {
799 2 : score->undo(new ChangeDrumset(instrument, newDrumset, part));
800 : }
801 : }
802 :
803 2 : void EditPart::setScoreOrder(Score* score, const ScoreOrder& order)
804 : {
805 2 : if (!score) {
806 0 : return;
807 : }
808 :
809 2 : score->undo(new ChangeScoreOrder(score, order));
810 2 : ScoreOrder mutableOrder = order;
811 2 : mutableOrder.setBracketsAndBarlines(score);
812 2 : }
|