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 : #include "notationparts.h"
23 :
24 : #include "translation.h"
25 :
26 : #include "engraving/dom/barline.h"
27 : #include "engraving/dom/excerpt.h"
28 : #include "engraving/dom/factory.h"
29 : #include "engraving/dom/instrument.h"
30 : #include "engraving/dom/page.h"
31 : #include "engraving/editing/addremoveelement.h"
32 : #include "engraving/editing/editexcerpt.h"
33 : #include "engraving/editing/editpagelocks.h"
34 : #include "engraving/editing/editpart.h"
35 : #include "engraving/editing/editstaff.h"
36 : #include "engraving/editing/editstavesharing.h"
37 : #include "engraving/editing/editsystemlocks.h"
38 : #include "engraving/editing/transaction/transaction.h"
39 : #include "engraving/editing/transpose.h"
40 :
41 : #include "igetscore.h"
42 : #include "inotationnoteinput.h" // IWYU pragma: keep
43 :
44 : #include "log.h"
45 :
46 : using namespace muse;
47 : using namespace muse::async;
48 : using namespace mu::notation;
49 : using namespace mu::engraving;
50 :
51 : static const mu::engraving::Fraction DEFAULT_TICK = mu::engraving::Fraction(0, 1);
52 :
53 0 : NotationParts::NotationParts(IGetScore* getScore, INotationInteractionPtr interaction, INotationUndoStackPtr undoStack,
54 0 : INotationStylePtr style)
55 0 : : m_getScore(getScore), m_undoStack(undoStack), m_interaction(interaction), m_style(style)
56 : {
57 0 : m_getScore->scoreInited().onNotify(this, [this]() {
58 0 : listenUndoStackChanges();
59 0 : listenStyleChanges();
60 0 : });
61 0 : }
62 :
63 0 : NotifyList<const Part*> NotationParts::partList() const
64 : {
65 0 : NotifyList<const Part*> result;
66 0 : result.setNotify(m_partChangedNotifier.notify());
67 :
68 0 : if (Score* _score = score()) {
69 0 : for (const Part* part: _score->parts()) {
70 0 : result.push_back(part);
71 : }
72 : }
73 :
74 0 : return result;
75 0 : }
76 :
77 0 : NotifyList<const Staff*> NotationParts::staffList(const ID& partId) const
78 : {
79 0 : NotifyList<const Staff*> result;
80 0 : ChangedNotifier<const Staff*>& notifier = m_staffChangedNotifierMap[partId];
81 0 : result.setNotify(notifier.notify());
82 :
83 0 : const Part* part = this->part(partId);
84 0 : if (!part) {
85 0 : return result;
86 : }
87 :
88 0 : for (const Staff* staff: part->staves()) {
89 0 : result.push_back(staff);
90 : }
91 :
92 0 : return result;
93 0 : }
94 :
95 0 : bool NotationParts::hasParts() const
96 : {
97 0 : const Score* score = this->score();
98 0 : if (!score) {
99 0 : return false;
100 : }
101 :
102 0 : return !score->parts().empty();
103 : }
104 :
105 0 : const Part* NotationParts::part(const ID& partId) const
106 : {
107 0 : return partModifiable(partId);
108 : }
109 :
110 0 : bool NotationParts::partExists(const ID& partId) const
111 : {
112 0 : return part(partId) != nullptr;
113 : }
114 :
115 0 : const Staff* NotationParts::staff(const ID& staffId) const
116 : {
117 0 : return staffModifiable(staffId);
118 : }
119 :
120 0 : bool NotationParts::staffExists(const ID& staffId) const
121 : {
122 0 : return staff(staffId) != nullptr;
123 : }
124 :
125 0 : StaffConfig NotationParts::staffConfig(const ID& staffId, Fraction tick) const
126 : {
127 0 : StaffConfig config;
128 0 : Staff* staff = staffModifiable(staffId);
129 0 : if (!staff) {
130 0 : return config;
131 : }
132 :
133 0 : mu::engraving::StaffType* staffType = staff->staffType(tick);
134 0 : if (!staffType) {
135 0 : return config;
136 : }
137 :
138 0 : config.visible = staff->visible();
139 0 : config.userDistance = staff->userDist();
140 0 : config.cutaway = staff->cutaway();
141 0 : config.hideSystemBarline = staff->hideSystemBarLine();
142 0 : config.mergeMatchingRests = staff->mergeMatchingRests();
143 0 : config.clefTypeList = staff->defaultClefType();
144 0 : config.reflectTranspositionInLinkedTab = staff->reflectTranspositionInLinkedTab();
145 :
146 0 : config.staffType = *staffType;
147 :
148 0 : return config;
149 0 : }
150 :
151 0 : ScoreOrder NotationParts::scoreOrder() const
152 : {
153 0 : return score()->scoreOrder();
154 : }
155 :
156 0 : Part* NotationParts::partModifiable(const ID& partId) const
157 : {
158 0 : return score()->partById(partId.toUint64());
159 : }
160 :
161 0 : Staff* NotationParts::staffModifiable(const ID& staffId) const
162 : {
163 0 : return score()->staffById(staffId.toUint64());
164 : }
165 :
166 0 : std::vector<Staff*> NotationParts::staves(const IDList& stavesIds) const
167 : {
168 0 : std::vector<Staff*> staves;
169 :
170 0 : if (stavesIds.empty()) {
171 0 : return staves;
172 : }
173 :
174 0 : for (Staff* staff : score()->staves()) {
175 0 : if (muse::contains(stavesIds, staff->id())) {
176 0 : staves.push_back(staff);
177 : }
178 : }
179 :
180 0 : return staves;
181 0 : }
182 :
183 0 : std::vector<Part*> NotationParts::parts(const IDList& partsIds) const
184 : {
185 0 : std::vector<Part*> parts;
186 :
187 0 : for (Part* part : score()->parts()) {
188 0 : if (std::find(partsIds.cbegin(), partsIds.cend(), part->id()) != partsIds.cend()) {
189 0 : parts.push_back(part);
190 : }
191 : }
192 :
193 0 : return parts;
194 0 : }
195 :
196 0 : void NotationParts::setParts(const PartInstrumentList& parts, const ScoreOrder& order)
197 : {
198 0 : TRACEFUNC;
199 :
200 0 : endInteractionWithScore();
201 0 : startEdit(TranslatableString("undoableAction", "Add/remove instruments"));
202 :
203 0 : doSetScoreOrder(order);
204 0 : removeMissingParts(parts);
205 0 : insertNewParts(parts);
206 0 : updateSoloist(parts);
207 0 : sortParts(parts);
208 0 : setBracketsAndBarlines();
209 0 : updatePartsAndSystemObjectStaves();
210 :
211 0 : apply();
212 0 : }
213 :
214 0 : void NotationParts::setScoreOrder(const ScoreOrder& order)
215 : {
216 0 : if (score()->scoreOrder() == order) {
217 0 : return;
218 : }
219 :
220 0 : startEdit(TranslatableString("undoableAction", "Set score order"));
221 :
222 0 : doSetScoreOrder(order);
223 0 : setBracketsAndBarlines();
224 :
225 0 : apply();
226 : }
227 :
228 0 : void NotationParts::setPartVisible(const ID& partId, bool visible)
229 : {
230 0 : TRACEFUNC;
231 :
232 0 : Part* part = partModifiable(partId);
233 0 : if (!part) {
234 0 : return;
235 : }
236 :
237 0 : if (part->show() == visible) {
238 0 : return;
239 : }
240 :
241 : const TranslatableString actionName = visible
242 0 : ? TranslatableString("undoableAction", "Show instrument")
243 0 : : TranslatableString("undoableAction", "Hide instrument");
244 :
245 0 : startEdit(actionName);
246 :
247 0 : mu::engraving::EditPart::setPartVisible(score(), part, visible);
248 :
249 0 : if (visible) {
250 0 : engraving::Transaction& tx = score()->transactionManager()->currentOrDummyTransaction();
251 0 : EditPageLocks::removePageLocksContainingMMRests(tx, score());
252 0 : EditSystemLocks::removeSystemLocksContainingMMRests(tx, score());
253 : }
254 :
255 0 : apply();
256 :
257 0 : notifyAboutPartChanged(part);
258 0 : }
259 :
260 0 : void NotationParts::setPartSharpFlat(const ID& partId, const SharpFlat& sharpFlat)
261 : {
262 0 : TRACEFUNC;
263 :
264 0 : Part* part = partModifiable(partId);
265 0 : if (!part) {
266 0 : return;
267 : }
268 :
269 0 : int shartFlatInt = static_cast<int>(sharpFlat);
270 0 : if (part->getProperty(mu::engraving::Pid::PREFER_SHARP_FLAT) == shartFlatInt) {
271 0 : return;
272 : }
273 :
274 0 : auto calcActionName = [](const SharpFlat& sharpFlat) -> TranslatableString {
275 0 : switch (sharpFlat) {
276 0 : case SharpFlat::NONE: return TranslatableString("undoableAction", "Set sharps/flats no preference");
277 0 : case SharpFlat::FLATS: return TranslatableString("undoableAction", "Set prefer flats");
278 0 : case SharpFlat::SHARPS: return TranslatableString("undoableAction", "Set prefer sharps");
279 0 : case SharpFlat::AUTO: return TranslatableString("undoableAction", "Set sharps/flats automatic");
280 : }
281 0 : return TranslatableString("undoableAction", "Set sharps/flats preference");
282 : };
283 :
284 0 : startEdit(calcActionName(sharpFlat));
285 :
286 0 : mu::engraving::EditPart::setPartSharpFlat(score(), part, sharpFlat);
287 :
288 0 : apply();
289 :
290 0 : notifyAboutPartChanged(part);
291 0 : }
292 :
293 0 : void NotationParts::listenUndoStackChanges()
294 : {
295 0 : if (!score()) {
296 0 : return;
297 : }
298 :
299 0 : updatePartsAndSystemObjectStaves();
300 :
301 0 : m_undoStack->changesChannel().onReceive(this, [this](const ScoreChanges& changes) {
302 0 : if (changes.isTextEditing || changes.changedTypes.empty() || m_ignoreUndoStackChanges) {
303 0 : return;
304 : }
305 :
306 : static const ElementTypeSet TYPES_TO_CHECK {
307 : ElementType::SCORE,
308 : ElementType::STAFF,
309 : ElementType::PART,
310 : ElementType::SHARED_PART,
311 0 : };
312 :
313 0 : for (ElementType type : TYPES_TO_CHECK) {
314 0 : if (muse::contains(changes.changedTypes, type)) {
315 0 : updatePartsAndSystemObjectStaves(changes);
316 0 : return;
317 : }
318 : }
319 : });
320 0 : }
321 :
322 0 : void NotationParts::updatePartsAndSystemObjectStaves(const mu::engraving::ScoreChanges& changes)
323 : {
324 0 : const bool partsChanged = m_parts != score()->parts();
325 0 : m_parts = score()->parts();
326 :
327 0 : std::vector<Staff*> newSystemObjectStaves = score()->systemObjectStavesWithTopStaff();
328 0 : const bool systemObjectStavesChanged = m_systemObjectStaves != newSystemObjectStaves;
329 0 : m_systemObjectStaves = std::move(newSystemObjectStaves);
330 :
331 0 : if (partsChanged) {
332 0 : m_partChangedNotifier.changed();
333 : }
334 :
335 0 : if (systemObjectStavesChanged) {
336 0 : m_systemObjectStavesChanged.notify();
337 : }
338 :
339 0 : if (muse::contains(changes.changedTypes, ElementType::SHARED_PART)) {
340 0 : m_sharedPartsChanged.notify();
341 : }
342 :
343 0 : std::vector<Staff*> removedStaves;
344 0 : std::vector<Staff*> addedStaves;
345 :
346 0 : bool stavesSorted = false;
347 :
348 0 : for (auto& pair : changes.changedObjects) {
349 0 : if (muse::contains(pair.second, CommandType::SortStaves)) {
350 0 : stavesSorted = true;
351 0 : break;
352 : }
353 :
354 0 : if (!pair.first->isStaff()) {
355 0 : continue;
356 : }
357 :
358 0 : Staff* staff = toStaff(pair.first);
359 :
360 0 : if (muse::contains(pair.second, CommandType::RemoveStaff)) {
361 0 : removedStaves.push_back(staff);
362 0 : } else if (muse::contains(pair.second, CommandType::InsertStaff)) {
363 0 : addedStaves.push_back(staff);
364 : }
365 : }
366 :
367 0 : if (stavesSorted && !partsChanged) {
368 0 : m_partChangedNotifier.changed();
369 0 : return;
370 : }
371 :
372 0 : for (Staff* staff : removedStaves) {
373 0 : notifyAboutStaffRemoved(staff);
374 : }
375 :
376 0 : for (Staff* staff: addedStaves) {
377 0 : notifyAboutStaffAdded(staff);
378 : }
379 0 : }
380 :
381 0 : void NotationParts::listenStyleChanges()
382 : {
383 0 : if (!score()) {
384 0 : return;
385 : }
386 :
387 0 : m_style->styleChanged().onNotify(this, [this]{ m_sharedPartsChanged.notify(); });
388 : }
389 :
390 0 : void NotationParts::doSetScoreOrder(const ScoreOrder& order)
391 : {
392 0 : EditPart::setScoreOrder(score(), order);
393 :
394 0 : m_scoreOrderChanged.notify();
395 0 : }
396 :
397 0 : void NotationParts::setInstrumentName(const InstrumentKey& instrumentKey, const QString& name)
398 : {
399 0 : TRACEFUNC;
400 :
401 0 : Part* part = partModifiable(instrumentKey.partId);
402 0 : if (!part) {
403 0 : return;
404 : }
405 :
406 0 : const mu::engraving::Instrument* instrument = part->instrument(instrumentKey.tick);
407 0 : if (!instrument) {
408 0 : return;
409 : }
410 :
411 0 : String newName = String::fromQString(name);
412 0 : if (instrument->longName() == newName) {
413 0 : return;
414 : }
415 :
416 0 : startEdit(TranslatableString("undoableAction", "Set instrument name"));
417 :
418 0 : mu::engraving::EditPart::setInstrumentName(score(), part, instrumentKey.tick, name);
419 :
420 0 : apply();
421 :
422 0 : notifyAboutPartChanged(part);
423 0 : }
424 :
425 0 : void NotationParts::setInstrumentAbbreviature(const InstrumentKey& instrumentKey, const QString& abbreviature)
426 : {
427 0 : TRACEFUNC;
428 :
429 0 : Part* part = partModifiable(instrumentKey.partId);
430 0 : if (!part) {
431 0 : return;
432 : }
433 :
434 0 : const mu::engraving::Instrument* instrument = part->instrument(instrumentKey.tick);
435 0 : if (!instrument) {
436 0 : return;
437 : }
438 :
439 0 : if (instrument->abbreviatureAsPlainText() == abbreviature) {
440 0 : return;
441 : }
442 :
443 0 : startEdit(TranslatableString("undoableAction", "Set abbreviated instrument name"));
444 :
445 0 : mu::engraving::EditPart::setInstrumentAbbreviature(score(), part, instrumentKey.tick, abbreviature);
446 :
447 0 : apply();
448 :
449 0 : notifyAboutPartChanged(part);
450 0 : }
451 :
452 0 : void NotationParts::setInstrumentGroupNameOptions(const std::vector<InstrumentKey>& instruments, bool useCustom, const QString& name,
453 : const QString& shortName)
454 : {
455 0 : TRACEFUNC;
456 :
457 0 : startEdit(TranslatableString("undoableAction", "Set instrument custom group name"));
458 :
459 0 : std::vector<Part*> changedParts;
460 :
461 0 : for (const InstrumentKey& key : instruments) {
462 0 : Part* part = partModifiable(key.partId);
463 0 : if (!part) {
464 0 : continue;
465 : }
466 :
467 0 : const mu::engraving::Instrument* instrument = part->instrument(key.tick);
468 0 : if (!instrument) {
469 0 : continue;
470 : }
471 :
472 0 : const InstrumentLabel& instrLabel = instrument->instrumentLabel();
473 0 : if (instrLabel.useCustomGroupName() == useCustom && instrLabel.customNameLongGroup() == name
474 0 : && instrLabel.customNameShortGroup() == shortName) {
475 0 : continue;
476 : }
477 :
478 0 : mu::engraving::EditPart::setInstrumentGroupNameOptions(score(), part, key.tick, useCustom, name, shortName);
479 :
480 0 : changedParts.push_back(part);
481 : }
482 :
483 0 : apply();
484 :
485 0 : for (Part* p : changedParts) {
486 0 : notifyAboutPartChanged(p);
487 : }
488 0 : }
489 :
490 0 : void NotationParts::setInstrumentNumber(const InstrumentKey& instrumentKey, int v)
491 : {
492 0 : TRACEFUNC;
493 :
494 0 : Part* part = partModifiable(instrumentKey.partId);
495 0 : if (!part) {
496 0 : return;
497 : }
498 :
499 0 : const mu::engraving::Instrument* instrument = part->instrument(instrumentKey.tick);
500 0 : if (!instrument) {
501 0 : return;
502 : }
503 :
504 0 : if (instrument->number() == v) {
505 0 : return;
506 : }
507 :
508 0 : startEdit(TranslatableString("undoableAction", "Set instrument number"));
509 :
510 0 : score()->undo(new mu::engraving::ChangeInstrumentNumber(instrumentKey.tick, part, v));
511 :
512 0 : apply();
513 :
514 0 : notifyAboutPartChanged(part);
515 0 : }
516 :
517 0 : bool NotationParts::setVoiceVisible(const ID& staffId, int voiceIndex, bool visible)
518 : {
519 0 : TRACEFUNC;
520 :
521 0 : Staff* staff = staffModifiable(staffId);
522 0 : if (!staff) {
523 0 : return false;
524 : }
525 :
526 : const TranslatableString actionName = visible
527 0 : ? TranslatableString("undoableAction", "Show voice %1").arg(voiceIndex + 1)
528 0 : : TranslatableString("undoableAction", "Hide voice %1").arg(voiceIndex + 1);
529 :
530 0 : startEdit(actionName);
531 :
532 0 : bool result = EditPart::setVoiceVisible(score(), staff, voiceIndex, visible);
533 0 : if (!result) {
534 0 : rollback();
535 0 : return false;
536 : }
537 :
538 : //! HACK: Excerpt::setVoiceVisible recreates the staff,
539 : //! so later in listenUndoStackChanges() we will call notifyAboutStaffRemoved() and notifyAboutStaffAdded(),
540 : //! which will result in the wrong UI state in the Layout panel.
541 : //! We should not recreate the staff here, only update it
542 0 : m_ignoreUndoStackChanges = true;
543 0 : apply();
544 0 : m_ignoreUndoStackChanges = false;
545 :
546 0 : Staff* newStaff = staffModifiable(staffId);
547 0 : notifyAboutStaffChanged(newStaff);
548 :
549 0 : return true;
550 0 : }
551 :
552 0 : void NotationParts::setStaffVisible(const ID& staffId, bool visible)
553 : {
554 0 : TRACEFUNC;
555 :
556 0 : Staff* staff = staffModifiable(staffId);
557 0 : if (!staff) {
558 0 : return;
559 : }
560 :
561 0 : StaffConfig config = staffConfig(staffId);
562 0 : if (config.visible == visible) {
563 0 : return;
564 : }
565 :
566 : const TranslatableString actionName = visible
567 0 : ? TranslatableString("undoableAction", "Show staff")
568 0 : : TranslatableString("undoableAction", "Hide staff");
569 :
570 0 : startEdit(actionName);
571 :
572 0 : config.visible = visible;
573 0 : doSetStaffConfig(staff, config);
574 :
575 0 : if (visible) {
576 0 : engraving::Transaction& tx = score()->transactionManager()->currentOrDummyTransaction();
577 0 : EditPageLocks::removePageLocksContainingMMRests(tx, score());
578 0 : EditSystemLocks::removeSystemLocksContainingMMRests(tx, score());
579 : }
580 :
581 0 : apply();
582 :
583 0 : notifyAboutStaffChanged(staff);
584 0 : }
585 :
586 0 : void NotationParts::setStaffType(const ID& staffId, StaffTypeId type)
587 : {
588 0 : TRACEFUNC;
589 :
590 0 : Staff* staff = staffModifiable(staffId);
591 0 : const mu::engraving::StaffType* staffType = mu::engraving::StaffType::preset(type);
592 :
593 0 : if (!staff || !staffType) {
594 0 : return;
595 : }
596 :
597 0 : if (staff->staffType(DEFAULT_TICK) == staffType) {
598 0 : return;
599 : }
600 :
601 0 : startEdit(TranslatableString("undoableAction", "Set staff type"));
602 :
603 0 : mu::engraving::EditPart::setStaffType(score(), staff, type);
604 :
605 0 : apply();
606 :
607 0 : notifyAboutStaffChanged(staff);
608 0 : }
609 :
610 0 : void NotationParts::setStaffConfig(const ID& staffId, const StaffConfig& config, Fraction tick)
611 : {
612 0 : TRACEFUNC;
613 :
614 0 : Staff* staff = staffModifiable(staffId);
615 0 : if (!staff) {
616 0 : return;
617 : }
618 :
619 0 : if (staffConfig(staffId) == config) {
620 0 : return;
621 : }
622 :
623 0 : startEdit(TranslatableString("undoableAction", "Edit staff properties"));
624 :
625 0 : doSetStaffConfig(staff, config, tick);
626 :
627 0 : apply();
628 :
629 0 : notifyAboutStaffChanged(staff);
630 0 : }
631 :
632 0 : void NotationParts::setSharedPartEnabled(const muse::ID& partId, bool enable)
633 : {
634 0 : TRACEFUNC;
635 :
636 0 : Part* part = partModifiable(partId);
637 0 : if (!part) {
638 0 : return;
639 : }
640 :
641 0 : if (part->getProperty(Pid::SHARED_PART_ENABLED).toBool() == enable) {
642 0 : return;
643 : }
644 :
645 : const TranslatableString actionName = enable
646 0 : ? TranslatableString("undoableAction", "Enable shared staff")
647 0 : : TranslatableString("undoableAction", "Disable shared staff");
648 :
649 0 : startEdit(actionName);
650 :
651 0 : part->undoChangeProperty(Pid::SHARED_PART_ENABLED, enable);
652 :
653 0 : apply();
654 :
655 0 : notifyAboutPartChanged(part);
656 0 : }
657 :
658 0 : bool NotationParts::appendStaff(Staff* staff, const ID& destinationPartId)
659 : {
660 0 : TRACEFUNC;
661 :
662 0 : IF_ASSERT_FAILED(staff) {
663 0 : return false;
664 : }
665 :
666 0 : Part* destinationPart = partModifiable(destinationPartId);
667 0 : if (!destinationPart) {
668 0 : return false;
669 : }
670 :
671 0 : startEdit(TranslatableString("undoableAction", "Add staff"));
672 0 : doAppendStaff(staff, destinationPart);
673 0 : apply();
674 :
675 0 : return true;
676 0 : }
677 :
678 0 : bool NotationParts::appendStaffLinkedToMaster(Staff* staff, Staff* masterSourceStaff, const muse::ID& destinationPartId)
679 : {
680 0 : TRACEFUNC;
681 :
682 0 : IF_ASSERT_FAILED(staff && masterSourceStaff) {
683 0 : return false;
684 : }
685 :
686 0 : Part* destinationPart = partModifiable(destinationPartId);
687 0 : if (!destinationPart) {
688 0 : return false;
689 : }
690 :
691 0 : startEdit(TranslatableString("undoableAction", "Add staff"));
692 :
693 0 : doAppendStaff(staff, destinationPart, /*createRests*/ false);
694 0 : score()->undo(new mu::engraving::Link(staff, masterSourceStaff));
695 :
696 0 : mu::engraving::Excerpt::cloneStaff2(masterSourceStaff, staff, Fraction(0, 1), score()->endTick());
697 :
698 0 : return true;
699 0 : }
700 :
701 0 : bool NotationParts::appendLinkedStaff(Staff* staff, const muse::ID& sourceStaffId, const muse::ID& destinationPartId)
702 : {
703 0 : TRACEFUNC;
704 :
705 0 : IF_ASSERT_FAILED(staff) {
706 0 : return false;
707 : }
708 :
709 0 : Staff* sourceStaff = staffModifiable(sourceStaffId);
710 0 : Part* destinationPart = partModifiable(destinationPartId);
711 0 : if (!sourceStaff || !destinationPart) {
712 0 : return false;
713 : }
714 :
715 0 : startEdit(TranslatableString("undoableAction", "Add linked staff"));
716 :
717 0 : doAppendStaff(staff, destinationPart, false);
718 :
719 : ///! NOTE: need to unlink before linking
720 0 : staff->setLinks(nullptr);
721 0 : mu::engraving::Excerpt::cloneStaff(sourceStaff, staff);
722 :
723 0 : apply();
724 :
725 0 : return true;
726 0 : }
727 :
728 0 : void NotationParts::insertPart(Part* part, size_t index)
729 : {
730 0 : TRACEFUNC;
731 :
732 0 : if (!part) {
733 0 : return;
734 : }
735 :
736 0 : startEdit(TranslatableString("undoableAction", "Add instrument"));
737 :
738 0 : engraving::Transaction& tx = score()->transactionManager()->currentOrDummyTransaction();
739 0 : EditPageLocks::removePageLocksContainingMMRests(tx, score());
740 0 : EditSystemLocks::removeSystemLocksContainingMMRests(tx, score());
741 :
742 0 : doInsertPart(part, index);
743 :
744 0 : apply();
745 :
746 0 : notifyAboutPartAdded(part);
747 0 : }
748 :
749 0 : void NotationParts::replacePart(const ID& partId, Part* newPart)
750 : {
751 0 : TRACEFUNC;
752 :
753 0 : Part* part = partModifiable(partId);
754 0 : if (!part || !newPart) {
755 0 : return;
756 : }
757 :
758 0 : startEdit(TranslatableString("undoableAction", "Reset instrument formatting"));
759 :
760 0 : size_t partIndex = muse::indexOf(score()->parts(), part);
761 0 : score()->cmdRemovePart(part);
762 0 : doInsertPart(newPart, partIndex);
763 :
764 0 : apply();
765 :
766 0 : notifyAboutPartReplaced(part, newPart);
767 0 : }
768 :
769 0 : void NotationParts::replaceInstrument(const InstrumentKey& instrumentKey, const Instrument& newInstrument, const StaffType* newStaffType)
770 : {
771 0 : TRACEFUNC;
772 :
773 0 : Part* part = partModifiable(instrumentKey.partId);
774 0 : if (!part) {
775 0 : return;
776 : }
777 :
778 0 : startEdit(TranslatableString("undoableAction", "Replace instrument"));
779 :
780 0 : if (isMainInstrumentForPart(instrumentKey, part)) {
781 0 : mu::engraving::EditPart::replacePartInstrument(score(), part, newInstrument, newStaffType);
782 : } else {
783 0 : if (!mu::engraving::EditPart::replaceInstrumentAtTick(score(), part, instrumentKey.tick, newInstrument)) {
784 0 : rollback();
785 0 : return;
786 : }
787 : }
788 :
789 0 : apply();
790 :
791 0 : notifyAboutPartChanged(part);
792 0 : }
793 :
794 0 : void NotationParts::replaceDrumset(const InstrumentKey& instrumentKey, const Drumset& newDrumset, bool undoable)
795 : {
796 0 : Part* part = partModifiable(instrumentKey.partId);
797 0 : if (!part) {
798 0 : return;
799 : }
800 :
801 0 : if (undoable) {
802 0 : startEdit(TranslatableString("undoableAction", "Edit drumset"));
803 :
804 0 : for (auto pair : part->instruments()) {
805 0 : Instrument* instrument = pair.second;
806 0 : if (instrument && instrument->drumset() && instrument->id() == instrumentKey.instrumentId) {
807 0 : EditPart::replaceDrumset(score(), part, Fraction::fromTicks(pair.first), newDrumset);
808 : }
809 : }
810 :
811 0 : apply();
812 : } else {
813 0 : for (auto pair : part->instruments()) {
814 0 : Instrument* instrument = pair.second;
815 0 : if (instrument && instrument->drumset() && instrument->id() == instrumentKey.instrumentId) {
816 0 : instrument->setDrumset(&newDrumset);
817 : }
818 : }
819 : }
820 :
821 0 : notifyAboutPartChanged(part);
822 0 : m_interaction->noteInput()->stateChanged().notify();
823 : }
824 :
825 0 : const std::vector<Staff*>& NotationParts::systemObjectStaves() const
826 : {
827 0 : return m_systemObjectStaves;
828 : }
829 :
830 0 : muse::async::Notification NotationParts::systemObjectStavesChanged() const
831 : {
832 0 : return m_systemObjectStavesChanged;
833 : }
834 :
835 0 : void NotationParts::addSystemObjects(const muse::IDList& stavesIds)
836 : {
837 0 : std::vector<Staff*> staves = this->staves(stavesIds);
838 0 : if (staves.empty()) {
839 0 : return;
840 : }
841 :
842 0 : startEdit(TranslatableString("undoableAction", "Add system markings"));
843 :
844 0 : EditPart::addSystemObjects(score(), staves);
845 :
846 0 : apply();
847 0 : }
848 :
849 0 : void NotationParts::removeSystemObjects(const IDList& stavesIds)
850 : {
851 0 : std::vector<Staff*> staves = this->staves(stavesIds);
852 0 : if (staves.empty()) {
853 0 : return;
854 : }
855 :
856 0 : startEdit(TranslatableString("undoableAction", "Remove system markings"));
857 :
858 0 : EditPart::removeSystemObjects(score(), staves);
859 :
860 0 : apply();
861 0 : }
862 :
863 0 : void NotationParts::moveSystemObjects(const ID& sourceStaffId, const ID& destinationStaffId)
864 : {
865 0 : Staff* srcStaff = staffModifiable(sourceStaffId);
866 0 : if (!srcStaff || !srcStaff->isSystemObjectStaff()) {
867 0 : return;
868 : }
869 :
870 0 : Staff* dstStaff = staffModifiable(destinationStaffId);
871 0 : if (!dstStaff) {
872 0 : return;
873 : }
874 :
875 0 : startEdit(TranslatableString("undoableAction", "Move system markings"));
876 :
877 0 : EditPart::moveSystemObjects(score(), srcStaff, dstStaff);
878 :
879 0 : apply();
880 : }
881 :
882 0 : void NotationParts::moveSystemObjectLayerBelowBottomStaff()
883 : {
884 0 : startEdit(TranslatableString("undoableAction", "Add system object layer below the bottom staff"));
885 :
886 0 : score()->undoChangeStyleVal(Sid::systemObjectsBelowBottomStaff, true);
887 :
888 0 : apply();
889 0 : }
890 :
891 0 : void NotationParts::moveSystemObjectLayerAboveBottomStaff()
892 : {
893 0 : startEdit(TranslatableString("undoableAction", "Remove system object layer below the bottom staff"));
894 :
895 0 : score()->undoChangeStyleVal(Sid::systemObjectsBelowBottomStaff, false);
896 :
897 0 : apply();
898 0 : }
899 :
900 0 : void NotationParts::toggleStaveSharing(bool on)
901 : {
902 0 : undoStack()->transaction(TranslatableString("undoableAction", "Toggle stave sharing"), [&](engraving::Transaction& tx) {
903 0 : EditStaveSharing::toggleStaveSharing(tx, score(), on);
904 0 : });
905 0 : m_partsChanged.notify();
906 0 : }
907 :
908 0 : Notification NotationParts::sharedPartsChanged() const
909 : {
910 0 : return m_sharedPartsChanged;
911 : }
912 :
913 0 : Notification NotationParts::partsChanged() const
914 : {
915 0 : return m_partsChanged;
916 : }
917 :
918 0 : Notification NotationParts::scoreOrderChanged() const
919 : {
920 0 : return m_scoreOrderChanged;
921 : }
922 :
923 0 : mu::engraving::Score* NotationParts::score() const
924 : {
925 0 : return m_getScore->score();
926 : }
927 :
928 0 : INotationUndoStackPtr NotationParts::undoStack() const
929 : {
930 0 : return m_undoStack;
931 : }
932 :
933 0 : void NotationParts::startEdit(const muse::TranslatableString& actionName)
934 : {
935 0 : undoStack()->prepareChanges(actionName);
936 0 : }
937 :
938 0 : void NotationParts::apply()
939 : {
940 0 : undoStack()->commitChanges();
941 :
942 0 : m_partsChanged.notify();
943 0 : }
944 :
945 0 : void NotationParts::rollback()
946 : {
947 0 : undoStack()->rollbackChanges();
948 0 : }
949 :
950 0 : void NotationParts::removeParts(const IDList& partsIds)
951 : {
952 0 : TRACEFUNC;
953 :
954 0 : std::vector<Part*> partsToRemove = parts(partsIds);
955 0 : if (partsToRemove.empty()) {
956 0 : return;
957 : }
958 :
959 0 : endInteractionWithScore();
960 0 : startEdit(TranslatableString("undoableAction", "Remove instruments"));
961 :
962 0 : doRemoveParts(partsToRemove);
963 :
964 0 : PartInstrumentList parts;
965 0 : for (mu::engraving::Part* part: score()->parts()) {
966 0 : PartInstrument pi;
967 0 : pi.isExistingPart = true;
968 0 : pi.partId = part->id();
969 0 : parts << pi;
970 0 : }
971 :
972 0 : sortParts(parts);
973 :
974 0 : setBracketsAndBarlines();
975 :
976 0 : apply();
977 :
978 0 : for (const Part* part : partsToRemove) {
979 0 : notifyAboutPartRemoved(part);
980 : }
981 0 : }
982 :
983 0 : void NotationParts::doRemoveParts(const std::vector<Part*>& parts)
984 : {
985 0 : TRACEFUNC;
986 :
987 0 : EditPart::removeParts(score(), parts);
988 :
989 0 : onPartsRemoved(parts);
990 0 : }
991 :
992 0 : void NotationParts::onPartsRemoved(const std::vector<Part*>&)
993 : {
994 0 : }
995 :
996 0 : void NotationParts::doAppendStaff(Staff* staff, Part* destinationPart, bool createRests)
997 : {
998 0 : staff_idx_t staffLocalIndex = destinationPart->nstaves();
999 0 : mu::engraving::KeyList keyList = *destinationPart->staff(staffLocalIndex - 1)->keyList();
1000 :
1001 0 : staff->setScore(score());
1002 0 : staff->setPart(destinationPart);
1003 :
1004 0 : insertStaff(staff, staffLocalIndex, createRests);
1005 :
1006 0 : staff_idx_t staffGlobalIndex = staff->idx();
1007 0 : score()->adjustKeySigs(staffGlobalIndex, staffGlobalIndex + 1, keyList);
1008 :
1009 0 : score()->updateBracesAndBarlines(destinationPart, staffLocalIndex);
1010 0 : setBracketsAndBarlines();
1011 :
1012 0 : destinationPart->instrument()->setClefType(staffLocalIndex, staff->defaultClefType());
1013 0 : }
1014 :
1015 0 : void NotationParts::doSetStaffConfig(Staff* staff, const StaffConfig& config, Fraction tick)
1016 : {
1017 0 : mu::engraving::StaffType* staffType = staff->staffType(tick);
1018 0 : if (!staffType) {
1019 0 : return;
1020 : }
1021 :
1022 0 : score()->undo(new mu::engraving::ChangeStaff(staff, config.visible, config.clefTypeList, config.userDistance, config.cutaway,
1023 0 : config.hideSystemBarline, config.mergeMatchingRests,
1024 0 : config.reflectTranspositionInLinkedTab));
1025 :
1026 0 : score()->undo(new mu::engraving::ChangeStaffType(staff, config.staffType, tick));
1027 : }
1028 :
1029 0 : void NotationParts::doInsertPart(Part* part, size_t index)
1030 : {
1031 0 : TRACEFUNC;
1032 :
1033 0 : std::vector<Staff*> stavesCopy(part->staves());
1034 0 : part->clearStaves();
1035 :
1036 0 : mu::engraving::InstrumentList instrumentsCopy = part->instruments();
1037 0 : part->setInstruments({});
1038 :
1039 0 : if (Excerpt* excerpt = score()->excerpt()) {
1040 0 : score()->undo(new AddPartToExcerpt(excerpt, part, index));
1041 : } else {
1042 0 : score()->undoInsertPart(part, index);
1043 : }
1044 :
1045 0 : for (auto it = instrumentsCopy.cbegin(); it != instrumentsCopy.cend(); ++it) {
1046 0 : part->setInstrument(new Instrument(*it->second), it->first);
1047 : }
1048 :
1049 0 : mu::engraving::Fraction startTick = score()->firstMeasure()->tick();
1050 0 : mu::engraving::Fraction endTick = score()->lastMeasure()->endTick();
1051 :
1052 0 : for (size_t staffIndex = 0; staffIndex < stavesCopy.size(); ++staffIndex) {
1053 0 : Staff* staff = stavesCopy[staffIndex];
1054 :
1055 0 : Staff* staffCopy = engraving::Factory::createStaff(part);
1056 0 : staffCopy->setId(staff->id());
1057 0 : staffCopy->setScore(score());
1058 0 : staffCopy->setPart(part);
1059 0 : staffCopy->init(staff);
1060 :
1061 0 : insertStaff(staffCopy, static_cast<int>(staffIndex), /* createRests = */ false);
1062 0 : score()->undo(new mu::engraving::Link(staffCopy, staff));
1063 :
1064 0 : mu::engraving::Excerpt::cloneStaff2(staff, staffCopy, startTick, endTick);
1065 : }
1066 :
1067 0 : score()->remapBracketsAndBarlines();
1068 0 : }
1069 :
1070 0 : void NotationParts::removeStaves(const IDList& stavesIds)
1071 : {
1072 0 : TRACEFUNC;
1073 :
1074 0 : std::vector<Staff*> stavesToRemove = staves(stavesIds);
1075 0 : if (stavesToRemove.empty()) {
1076 0 : return;
1077 : }
1078 :
1079 0 : endInteractionWithScore();
1080 0 : startEdit(TranslatableString("undoableAction", "Remove staves"));
1081 :
1082 0 : EditPart::removeStaves(score(), stavesToRemove);
1083 :
1084 0 : apply();
1085 0 : }
1086 :
1087 0 : void NotationParts::moveParts(const IDList& sourcePartsIds, const ID& destinationPartId, InsertMode mode)
1088 : {
1089 0 : TRACEFUNC;
1090 :
1091 0 : std::vector<Part*> sourceParts = parts(sourcePartsIds);
1092 0 : if (sourceParts.empty()) {
1093 0 : return;
1094 : }
1095 :
1096 0 : Part* destinationPart = partModifiable(destinationPartId);
1097 0 : if (!destinationPart) {
1098 0 : return;
1099 : }
1100 :
1101 0 : endInteractionWithScore();
1102 0 : startEdit(TranslatableString("undoableAction", "Move instruments"));
1103 :
1104 0 : EditPart::moveParts(score(), sourceParts, destinationPart, mode == InsertMode::After);
1105 :
1106 0 : apply();
1107 0 : }
1108 :
1109 0 : void NotationParts::moveStaves(const IDList& sourceStavesIds, const ID& destinationStaffId, InsertMode mode)
1110 : {
1111 0 : TRACEFUNC;
1112 :
1113 0 : Staff* destinationStaff = staffModifiable(destinationStaffId);
1114 0 : if (!destinationStaff) {
1115 0 : return;
1116 : }
1117 :
1118 0 : std::vector<Staff*> staves = this->staves(sourceStavesIds);
1119 0 : if (staves.empty()) {
1120 0 : return;
1121 : }
1122 :
1123 0 : for (const Staff* staff : staves) {
1124 0 : IF_ASSERT_FAILED_X(staff->part() == destinationStaff->part(), "All staves must have the same part!") {
1125 0 : return;
1126 : }
1127 : }
1128 :
1129 0 : endInteractionWithScore();
1130 0 : startEdit(TranslatableString("undoableAction", "Move staves"));
1131 :
1132 0 : EditPart::moveStaves(score(), staves, destinationStaff, mode == InsertMode::After);
1133 :
1134 0 : apply();
1135 0 : }
1136 :
1137 0 : void NotationParts::appendStaves(Part* part, const InstrumentTemplate& templ, const mu::engraving::KeyList& keyList)
1138 : {
1139 0 : TRACEFUNC;
1140 :
1141 0 : IF_ASSERT_FAILED(part) {
1142 0 : return;
1143 : }
1144 :
1145 0 : for (staff_idx_t staffIndex = 0; staffIndex < templ.staffCount; ++staffIndex) {
1146 0 : Staff* staff = engraving::Factory::createStaff(part);
1147 0 : const mu::engraving::StaffType* staffType = templ.staffTypePreset;
1148 0 : if (!staffType) {
1149 0 : staffType = mu::engraving::StaffType::preset(StaffTypeId::STANDARD);
1150 : }
1151 :
1152 0 : insertStaff(staff, staffIndex);
1153 0 : staff->init(&templ, staffType, staffIndex);
1154 : }
1155 :
1156 0 : if (!part->nstaves()) {
1157 0 : return;
1158 : }
1159 :
1160 0 : staff_idx_t firstStaffIndex = part->staff(0)->idx();
1161 0 : staff_idx_t endStaffIndex = firstStaffIndex + part->nstaves();
1162 0 : score()->adjustKeySigs(firstStaffIndex, endStaffIndex, keyList);
1163 0 : }
1164 :
1165 0 : void NotationParts::insertStaff(Staff* staff, staff_idx_t destinationStaffIndex, bool createRest)
1166 : {
1167 0 : TRACEFUNC;
1168 :
1169 0 : score()->undoInsertStaff(staff, destinationStaffIndex, createRest);
1170 0 : }
1171 :
1172 0 : void NotationParts::removeMissingParts(const PartInstrumentList& newParts)
1173 : {
1174 0 : TRACEFUNC;
1175 :
1176 0 : auto needRemove = [&newParts](const Part* part) {
1177 0 : for (const PartInstrument& pi : newParts) {
1178 0 : if (pi.partId == part->id()) {
1179 0 : return false;
1180 : }
1181 : }
1182 :
1183 0 : return true;
1184 0 : };
1185 :
1186 0 : std::vector<Part*> partsToRemove;
1187 :
1188 0 : for (Part* part: score()->parts()) {
1189 0 : if (needRemove(part)) {
1190 0 : partsToRemove.push_back(part);
1191 : }
1192 : }
1193 :
1194 0 : doRemoveParts(partsToRemove);
1195 0 : }
1196 :
1197 0 : void NotationParts::insertNewParts(const PartInstrumentList& parts, const mu::engraving::KeyList& keyList)
1198 : {
1199 0 : TRACEFUNC;
1200 :
1201 0 : size_t partIdx = 0;
1202 :
1203 0 : for (const PartInstrument& pi: parts) {
1204 0 : if (pi.isExistingPart) {
1205 : // Update numbering for existing parts of instruments being added
1206 0 : if (isNewInstrument(pi.instrumentTemplate, parts)) {
1207 0 : int instrumentNumber = resolveExistingInstrumentNumber(pi.partId, pi.instrumentTemplate);
1208 0 : if (Part* existingPart = score()->partById(pi.partId)) {
1209 0 : score()->undo(new mu::engraving::ChangeInstrumentNumber(Fraction(-1, 1), existingPart, instrumentNumber));
1210 : }
1211 : }
1212 0 : ++partIdx;
1213 0 : continue;
1214 0 : }
1215 :
1216 0 : Instrument instrument = Instrument::fromTemplate(&pi.instrumentTemplate);
1217 0 : const String& longN = instrument.longName();
1218 0 : const String& shortN = instrument.shortName();
1219 :
1220 0 : InstrumentTrait trait = instrument.trait();
1221 0 : const String& transp = trait.type == TraitType::Transposition && !trait.isHiddenOnScore ? trait.name : String();
1222 :
1223 0 : Part* part = new Part(score());
1224 0 : part->setSoloist(pi.isSoloist);
1225 0 : part->setInstrument(instrument);
1226 :
1227 0 : part->setLongName(muse::qtrc("notation", longN));
1228 0 : part->setShortName(muse::qtrc("notation", shortN));
1229 0 : part->setTransposition(muse::qtrc("notation", transp));
1230 :
1231 0 : int instrumentNumber = resolveNewInstrumentNumber(pi.instrumentTemplate, parts);
1232 0 : part->setNumber(instrumentNumber);
1233 :
1234 0 : if (Excerpt* excerpt = score()->excerpt()) {
1235 0 : score()->undo(new AddPartToExcerpt(excerpt, part, partIdx));
1236 : } else {
1237 0 : score()->undoInsertPart(part, partIdx);
1238 : }
1239 0 : appendStaves(part, pi.instrumentTemplate, keyList);
1240 0 : ++partIdx;
1241 :
1242 0 : m_partChangedNotifier.itemAdded(part);
1243 0 : }
1244 0 : }
1245 :
1246 0 : void NotationParts::insertNewParts(const PartInstrumentList& parts)
1247 : {
1248 0 : TRACEFUNC;
1249 0 : insertNewParts(parts, score()->keyList());
1250 0 : }
1251 :
1252 0 : void NotationParts::updateSoloist(const PartInstrumentList& parts)
1253 : {
1254 0 : TRACEFUNC;
1255 :
1256 0 : for (const PartInstrument& pi: parts) {
1257 0 : Part* part = partModifiable(pi.partId);
1258 :
1259 0 : if (pi.isExistingPart && (pi.isSoloist != part->soloist())) {
1260 0 : score()->undo(new mu::engraving::SetSoloist(part, pi.isSoloist));
1261 : }
1262 : }
1263 0 : }
1264 :
1265 0 : void NotationParts::sortParts(const PartInstrumentList& parts)
1266 : {
1267 0 : TRACEFUNC;
1268 :
1269 0 : std::vector<mu::engraving::staff_idx_t> staffMapping;
1270 :
1271 0 : int partIndex = 0;
1272 0 : for (const PartInstrument& pi: parts) {
1273 0 : mu::engraving::Part* currentPart = pi.isExistingPart ? partModifiable(pi.partId) : score()->parts()[partIndex];
1274 :
1275 0 : for (mu::engraving::Staff* staff : currentPart->staves()) {
1276 0 : mu::engraving::staff_idx_t actualStaffIndex = muse::indexOf(score()->staves(), staff);
1277 0 : staffMapping.push_back(actualStaffIndex);
1278 : }
1279 0 : ++partIndex;
1280 : }
1281 :
1282 0 : score()->undo(new mu::engraving::SortStaves(score(), staffMapping));
1283 0 : }
1284 :
1285 0 : int NotationParts::countExistingInstruments(const InstrumentTemplate& instrument) const
1286 : {
1287 0 : int count = 0;
1288 :
1289 0 : for (const Part* part : score()->parts()) {
1290 0 : const Instrument* partInstrument = part->instrument();
1291 :
1292 0 : if (partInstrument->id() == instrument.id) {
1293 0 : ++count;
1294 : }
1295 : }
1296 :
1297 0 : return count;
1298 : }
1299 :
1300 0 : bool NotationParts::isNewInstrument(const InstrumentTemplate& instrument, const PartInstrumentList& parts) const
1301 : {
1302 0 : for (const PartInstrument& pi : parts) {
1303 0 : if (!pi.isExistingPart && pi.instrumentTemplate.id == instrument.id) {
1304 0 : return true;
1305 : }
1306 : }
1307 :
1308 0 : return false;
1309 : }
1310 :
1311 0 : int NotationParts::resolveExistingInstrumentNumber(const muse::ID& partId, const InstrumentTemplate& instrument) const
1312 : {
1313 0 : int count = countExistingInstruments(instrument);
1314 :
1315 0 : int ordinal = 0;
1316 :
1317 0 : for (const Part* part : score()->parts()) {
1318 0 : const Instrument* partInstrument = part->instrument();
1319 :
1320 0 : if (partInstrument->id() != instrument.id) {
1321 0 : continue;
1322 : }
1323 :
1324 0 : ++ordinal;
1325 :
1326 0 : if (part->id() == partId) {
1327 0 : return ordinal;
1328 : }
1329 : }
1330 :
1331 0 : return count;
1332 : }
1333 :
1334 0 : int NotationParts::resolveNewInstrumentNumber(const InstrumentTemplate& instrument,
1335 : const PartInstrumentList& allNewInstruments) const
1336 : {
1337 0 : int count = countExistingInstruments(instrument);
1338 :
1339 0 : if (count > 0) {
1340 0 : return count + 1;
1341 : }
1342 :
1343 0 : for (const PartInstrument& partInstrument: allNewInstruments) {
1344 0 : const InstrumentTemplate& templ = partInstrument.instrumentTemplate;
1345 :
1346 0 : if (templ.id == instrument.id) {
1347 0 : ++count;
1348 : }
1349 : }
1350 :
1351 0 : return count > 1 ? 1 : 0;
1352 : }
1353 :
1354 0 : void NotationParts::setBracketsAndBarlines()
1355 : {
1356 0 : score()->setBracketsAndBarlines();
1357 0 : }
1358 :
1359 0 : void NotationParts::endInteractionWithScore()
1360 : {
1361 0 : m_interaction->clearSelection();
1362 0 : m_interaction->noteInput()->endNoteInput(/*resetState=*/ true);
1363 0 : }
1364 :
1365 0 : void NotationParts::notifyAboutPartChanged(const Part* part) const
1366 : {
1367 0 : IF_ASSERT_FAILED(part) {
1368 0 : return;
1369 : }
1370 :
1371 0 : m_partChangedNotifier.itemChanged(part);
1372 : }
1373 :
1374 0 : void NotationParts::notifyAboutPartAdded(const Part* part) const
1375 : {
1376 0 : IF_ASSERT_FAILED(part) {
1377 0 : return;
1378 : }
1379 :
1380 0 : m_partChangedNotifier.itemAdded(part);
1381 : }
1382 :
1383 0 : void NotationParts::notifyAboutPartRemoved(const Part* part) const
1384 : {
1385 0 : IF_ASSERT_FAILED(part) {
1386 0 : return;
1387 : }
1388 :
1389 0 : m_partChangedNotifier.itemRemoved(part);
1390 : }
1391 :
1392 0 : void NotationParts::notifyAboutPartReplaced(const Part* oldPart, const Part* newPart) const
1393 : {
1394 0 : IF_ASSERT_FAILED(oldPart && newPart) {
1395 0 : return;
1396 : }
1397 :
1398 0 : m_partChangedNotifier.itemReplaced(oldPart, newPart);
1399 : }
1400 :
1401 0 : void NotationParts::notifyAboutStaffChanged(const Staff* staff) const
1402 : {
1403 0 : IF_ASSERT_FAILED(staff && staff->part()) {
1404 0 : return;
1405 : }
1406 :
1407 0 : ChangedNotifier<const Staff*>& notifier = m_staffChangedNotifierMap[staff->part()->id()];
1408 0 : notifier.itemChanged(staff);
1409 : }
1410 :
1411 0 : void NotationParts::notifyAboutStaffAdded(const Staff* staff) const
1412 : {
1413 0 : IF_ASSERT_FAILED(staff && staff->part()) {
1414 0 : return;
1415 : }
1416 :
1417 0 : ChangedNotifier<const Staff*>& notifier = m_staffChangedNotifierMap[staff->part()->id()];
1418 0 : notifier.itemAdded(staff);
1419 : }
1420 :
1421 0 : void NotationParts::notifyAboutStaffRemoved(const Staff* staff) const
1422 : {
1423 0 : IF_ASSERT_FAILED(staff && staff->part()) {
1424 0 : return;
1425 : }
1426 :
1427 0 : ChangedNotifier<const Staff*>& notifier = m_staffChangedNotifierMap[staff->part()->id()];
1428 0 : notifier.itemRemoved(staff);
1429 : }
|