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 "masternotation.h"
24 :
25 : #include <QFileInfo>
26 :
27 : #include "log.h"
28 : #include "realfn.h"
29 : #include "translation.h"
30 :
31 : #include "engraving/style/defaultstyle.h"
32 : #include "engraving/style/pagestyle.h"
33 :
34 : #include "engraving/dom/factory.h"
35 : #include "engraving/dom/masterscore.h"
36 : #include "engraving/dom/part.h"
37 : #include "engraving/dom/staff.h"
38 : #include "engraving/dom/excerpt.h"
39 : #include "engraving/dom/measure.h"
40 : #include "engraving/dom/box.h"
41 : #include "engraving/dom/keysig.h"
42 : #include "engraving/dom/sig.h"
43 : #include "engraving/dom/tempotext.h"
44 :
45 : #include "engraving/editing/autospatium.h"
46 : #include "engraving/editing/editkeysig.h"
47 : #include "engraving/editing/edittimesig.h"
48 : #include "engraving/editing/transaction/transaction.h"
49 :
50 : #include "inotationelements.h" // IWYU pragma: keep
51 : #include "inotationsolomutestate.h"
52 : #include "excerptnotation.h"
53 : #include "masternotationparts.h"
54 : #include "notationautomation.h"
55 : #include "types/scorecreateoptions.h"
56 :
57 : #ifdef MUE_BUILD_ENGRAVING_PLAYBACK
58 : #include "notationplayback.h"
59 : #else
60 : #include "notationplaybackstub.h"
61 : #endif
62 :
63 : #include "../notationerrors.h"
64 :
65 : using namespace mu::notation;
66 : using namespace mu::engraving;
67 : using namespace muse;
68 : using namespace muse::async;
69 :
70 0 : static ExcerptNotation* get_impl(const IExcerptNotationPtr& excerpt)
71 : {
72 0 : return static_cast<ExcerptNotation*>(excerpt.get());
73 : }
74 :
75 0 : static IExcerptNotationPtr createAndInitExcerptNotation(MasterNotation* master, mu::engraving::Excerpt* excerpt,
76 : const muse::modularity::ContextPtr& iocCtx)
77 : {
78 0 : auto excerptNotation = std::make_shared<ExcerptNotation>(master, excerpt, iocCtx);
79 0 : excerptNotation->init();
80 :
81 0 : return excerptNotation;
82 0 : }
83 :
84 0 : MasterNotation::MasterNotation(project::INotationProject* project, const muse::modularity::ContextPtr& iocCtx)
85 0 : : Notation(this, iocCtx), m_project(project)
86 : {
87 0 : m_parts = std::make_shared<MasterNotationParts>(this, interaction(), undoStack(), style());
88 :
89 : #ifdef MUE_BUILD_ENGRAVING_PLAYBACK
90 0 : m_notationPlayback = std::make_shared<NotationPlayback>(this, m_notationChanged, iocCtx);
91 : #else
92 : m_notationPlayback = std::make_shared<NotationPlaybackStub>();
93 : #endif
94 :
95 0 : m_notationAutomation = std::make_shared<NotationAutomation>(undoStack());
96 :
97 0 : m_parts->partsChanged().onNotify(this, [this]() {
98 0 : notifyAboutNotationChanged();
99 0 : });
100 :
101 0 : async::NotifyList<const Part*> partList = m_parts->partList();
102 :
103 0 : partList.onChanged(this, [this]() {
104 0 : onPartsChanged();
105 0 : });
106 :
107 0 : partList.onItemAdded(this, [this](const Part*) {
108 0 : onPartsChanged();
109 0 : });
110 :
111 0 : partList.onItemRemoved(this, [this](const Part*) {
112 0 : onPartsChanged();
113 0 : });
114 0 : }
115 :
116 0 : MasterNotation::~MasterNotation()
117 : {
118 0 : m_parts = nullptr;
119 :
120 0 : unloadExcerpts(m_potentialExcerpts);
121 0 : }
122 :
123 0 : mu::project::INotationProject* MasterNotation::project() const
124 : {
125 0 : return m_project;
126 : }
127 :
128 0 : int MasterNotation::mscVersion() const
129 : {
130 0 : if (!masterScore()) {
131 0 : return 0;
132 : }
133 :
134 0 : return masterScore()->mscVersion();
135 : }
136 :
137 0 : INotationPtr MasterNotation::notation()
138 : {
139 0 : return shared_from_this();
140 : }
141 :
142 0 : void MasterNotation::initAfterSettingScore(const MasterScore* score, bool disablePlayback)
143 : {
144 0 : IF_ASSERT_FAILED(score) {
145 0 : return;
146 : }
147 :
148 0 : TRACEFUNC;
149 :
150 0 : score->changesChannel().onReceive(this, [this](const ScoreChanges& changes) {
151 0 : if (!changes.isTextEditing) {
152 0 : updateExcerpts();
153 : }
154 0 : });
155 :
156 0 : if (!disablePlayback) {
157 0 : m_notationPlayback->init();
158 : }
159 :
160 0 : initExcerptNotations(score->excerpts());
161 0 : }
162 :
163 0 : void MasterNotation::setMasterScore(mu::engraving::MasterScore* score, bool disablePlayback)
164 : {
165 0 : if (masterScore() == score) {
166 0 : return;
167 : }
168 :
169 0 : TRACEFUNC;
170 :
171 0 : setScore(score);
172 0 : std::static_pointer_cast<NotationAutomation>(m_notationAutomation)->setMasterScore(score);
173 :
174 0 : score->updateSwing();
175 :
176 0 : initAfterSettingScore(score, disablePlayback);
177 0 : }
178 :
179 0 : mu::engraving::MasterScore* MasterNotation::masterScore() const
180 : {
181 0 : return dynamic_cast<mu::engraving::MasterScore*>(score());
182 : }
183 :
184 0 : static void clearMeasures(mu::engraving::MasterScore* masterScore)
185 : {
186 0 : TRACEFUNC;
187 :
188 0 : for (mu::engraving::Score* score : masterScore->scoreList()) {
189 0 : for (Part* part : score->parts()) {
190 0 : part->removeNonPrimaryInstruments();
191 : }
192 :
193 0 : mu::engraving::MeasureBaseList* measures = score->measures();
194 0 : for (mu::engraving::MeasureBase* measure = measures->first(); measure; measure = measure->next()) {
195 0 : measure->deleteLater();
196 : }
197 :
198 0 : auto spanners = score->spanner();
199 0 : for (auto spanner = spanners.begin(); spanner != spanners.end(); spanner = ++spanner) {
200 0 : score->removeSpanner(spanner->second);
201 : }
202 :
203 0 : measures->clear();
204 0 : }
205 :
206 0 : masterScore->invalidateRepeatList();
207 0 : masterScore->updateRepeatList();
208 0 : }
209 :
210 0 : static void createMeasures(MasterScore* masterScore, const ScoreCreateOptions& scoreOptions)
211 : {
212 0 : TRACEFUNC;
213 :
214 0 : masterScore->sigmap()->add(0, scoreOptions.globalTimesig);
215 :
216 0 : KeySigEvent keySigEvent;
217 0 : if (scoreOptions.key == Key::INVALID) {
218 : // Make atonal key signature
219 0 : keySigEvent.setCustom(true);
220 0 : keySigEvent.setMode(KeyMode::NONE);
221 : } else {
222 0 : keySigEvent.setConcertKey(scoreOptions.key);
223 : }
224 :
225 : // Ensure all key sigs that may have been in the template file are cleared
226 0 : for (Staff* staff : masterScore->staves()) {
227 0 : KeyList* keys = staff->keyList();
228 0 : keys->clear();
229 : }
230 :
231 0 : const int totalMeasures = scoreOptions.withPickupMeasure ? scoreOptions.totalMeasures + 1 : scoreOptions.totalMeasures;
232 0 : for (int i = 0; i < totalMeasures; ++i) {
233 0 : MeasureBase* measureBase = masterScore->insertMeasure();
234 0 : Measure* measure = measureBase && measureBase->isMeasure() ? toMeasure(measureBase) : nullptr;
235 0 : IF_ASSERT_FAILED(measure) {
236 0 : break;
237 : }
238 :
239 0 : measure->setTimesig(scoreOptions.globalTimesig);
240 :
241 0 : if (i > 0) {
242 0 : measure->setTicks(scoreOptions.globalTimesig);
243 0 : continue;
244 : }
245 :
246 : // Special handling for first measure (apply pickups, timesigs, and keysigs)...
247 :
248 0 : measure->setExcludeFromNumbering(scoreOptions.withPickupMeasure);
249 0 : measure->adjustToLen(scoreOptions.withPickupMeasure ? scoreOptions.pickupTimesig : scoreOptions.globalTimesig);
250 :
251 : // Add timesigs...
252 0 : TimeSig* timesig = Factory::createTimeSig(masterScore->dummy());
253 0 : timesig->setSig(scoreOptions.globalTimesig, scoreOptions.timesigType);
254 0 : Transaction& tx = masterScore->transactionManager()->currentOrDummyTransaction();
255 0 : EditTimeSig::addTimeSig(tx, masterScore, measure, /*staffIdx*/ 0, timesig, /*local*/ false);
256 :
257 0 : for (Staff* staff : masterScore->staves()) {
258 : // Add keysig for each staff...
259 0 : mu::engraving::EditKeySig::undoChangeKeySig(tx, masterScore, staff, measure->tick(), keySigEvent);
260 : }
261 : }
262 0 : }
263 :
264 : //! NOTE: this method with all of its dependencies was copied from MU3
265 : //! source: file.cpp, MuseScore::getNewFile()
266 0 : Ret MasterNotation::setupNewScore(mu::engraving::MasterScore* score, const ScoreCreateOptions& scoreOptions)
267 : {
268 0 : TRACEFUNC;
269 :
270 0 : setScore(score);
271 0 : std::static_pointer_cast<NotationAutomation>(m_notationAutomation)->setMasterScore(score);
272 :
273 0 : undoStack()->lock();
274 :
275 0 : parts()->setParts(scoreOptions.parts, scoreOptions.order);
276 :
277 0 : score->checkChordList();
278 0 : score->updateSwing();
279 0 : score->updateCapo();
280 :
281 0 : applyOptions(score, scoreOptions);
282 :
283 0 : score->initAutomation();
284 :
285 0 : initAfterSettingScore(score);
286 0 : addExcerptsToMasterScore(score->excerpts());
287 :
288 0 : undoStack()->unlock();
289 :
290 0 : return make_ret(Err::NoError);
291 0 : }
292 :
293 0 : void MasterNotation::applyOptions(mu::engraving::MasterScore* score, const ScoreCreateOptions& scoreOptions, bool createdFromTemplate)
294 : {
295 0 : TRACEFUNC;
296 :
297 0 : mu::engraving::VBox* nvb = nullptr;
298 :
299 0 : if (createdFromTemplate) {
300 0 : mu::engraving::MeasureBase* mb = score->first();
301 0 : if (mb && mb->isVBox()) {
302 0 : mu::engraving::VBox* tvb = toVBox(mb);
303 0 : nvb = Factory::createTitleVBox(score);
304 0 : nvb->setBoxHeight(tvb->boxHeight());
305 0 : nvb->setBoxWidth(tvb->boxWidth());
306 0 : nvb->setTopGap(tvb->topGap());
307 0 : nvb->setBottomGap(tvb->bottomGap());
308 0 : nvb->setTopMargin(tvb->topMargin());
309 0 : nvb->setBottomMargin(tvb->bottomMargin());
310 0 : nvb->setLeftMargin(tvb->leftMargin());
311 0 : nvb->setRightMargin(tvb->rightMargin());
312 0 : nvb->setAutoSizeEnabled(tvb->isAutoSizeEnabled());
313 : }
314 :
315 0 : for (mu::engraving::Score* s : score->scoreList()) {
316 0 : s->clearSystemLocks();
317 0 : }
318 0 : clearMeasures(score);
319 :
320 : // for templates using built-in base page style, set score page style to default (may be user-defined)
321 0 : for (auto st : pageStyles()) {
322 0 : if (st != Sid::pageTwosided && RealIsEqual(score->style().styleD(st), DefaultStyle::baseStyle().styleD(st))) {
323 0 : score->style().set(st, DefaultStyle::defaultStyle().value(st));
324 : }
325 : }
326 : }
327 :
328 0 : score->checkChordList();
329 :
330 0 : createMeasures(score, scoreOptions);
331 :
332 : {
333 0 : const QString title = score->metaTag(u"workTitle");
334 0 : const QString subtitle = score->metaTag(u"subtitle");
335 0 : const QString composer = score->metaTag(u"composer");
336 0 : const QString lyricist = score->metaTag(u"lyricist");
337 :
338 0 : if (!title.isEmpty() || !subtitle.isEmpty() || !composer.isEmpty() || !lyricist.isEmpty()) {
339 0 : mu::engraving::MeasureBase* measure = score->measures()->first();
340 0 : if (!measure->isVBox()) {
341 0 : if (!nvb) {
342 0 : nvb = Factory::createTitleVBox(score);
343 : }
344 0 : nvb->setTick(mu::engraving::Fraction(0, 1));
345 0 : nvb->setNext(measure);
346 0 : score->measures()->add(nvb);
347 0 : } else if (nvb) {
348 0 : delete nvb;
349 0 : nvb = nullptr;
350 : }
351 :
352 0 : auto setText = [score](mu::engraving::TextStyleType textItemId, const QString& text) {
353 0 : mu::engraving::TextBase* textItem = score->getText(textItemId);
354 :
355 0 : if (!textItem) {
356 0 : textItem = score->addText(textItemId);
357 : }
358 :
359 0 : if (textItem) {
360 0 : textItem->setPlainText(text);
361 : }
362 0 : };
363 :
364 0 : if (!title.isEmpty()) {
365 0 : setText(mu::engraving::TextStyleType::TITLE, title);
366 : }
367 0 : if (!subtitle.isEmpty()) {
368 0 : setText(mu::engraving::TextStyleType::SUBTITLE, subtitle);
369 : }
370 0 : if (!composer.isEmpty()) {
371 0 : setText(mu::engraving::TextStyleType::COMPOSER, composer);
372 : }
373 0 : if (!lyricist.isEmpty()) {
374 0 : setText(mu::engraving::TextStyleType::LYRICIST, lyricist);
375 : }
376 0 : } else if (nvb) {
377 0 : delete nvb;
378 0 : nvb = nullptr;
379 : }
380 :
381 0 : if (nvb) {
382 0 : nvb->manageExclusionFromParts(/*exclude*/ false);
383 : }
384 0 : }
385 :
386 0 : if (scoreOptions.withTempo) {
387 0 : String text { u"<sym>metNoteQuarterUp</sym> = %1" };
388 0 : const double bpm = scoreOptions.tempo.valueBpm;
389 0 : double qpm = bpm; // internal tempo value is always expressed in quarter notes
390 :
391 0 : const bool withDot = scoreOptions.tempo.withDot;
392 0 : switch (scoreOptions.tempo.duration) {
393 0 : case DurationType::V_WHOLE:
394 0 : text = "<sym>metNoteWhole</sym> = %1";
395 0 : qpm *= 4.0;
396 0 : break;
397 0 : case DurationType::V_HALF:
398 0 : if (withDot) {
399 0 : text = "<sym>metNoteHalfUp</sym><sym>space</sym><sym>metAugmentationDot</sym> = %1";
400 0 : qpm *= 3.0;
401 : } else {
402 0 : text = "<sym>metNoteHalfUp</sym> = %1";
403 0 : qpm *= 2.0;
404 : }
405 0 : break;
406 0 : case DurationType::V_QUARTER:
407 0 : if (withDot) {
408 0 : text = "<sym>metNoteQuarterUp</sym><sym>space</sym><sym>metAugmentationDot</sym> = %1";
409 0 : qpm *= 1.5;
410 : } else {
411 0 : text = "<sym>metNoteQuarterUp</sym> = %1";
412 : //qpm *= 1.0;
413 : }
414 0 : break;
415 0 : case DurationType::V_EIGHTH:
416 0 : if (withDot) {
417 0 : text = "<sym>metNote8thUp</sym><sym>space</sym><sym>metAugmentationDot</sym> = %1";
418 0 : qpm *= 0.75;
419 : } else {
420 0 : text = "<sym>metNote8thUp</sym> = %1";
421 0 : qpm *= 0.5;
422 : }
423 0 : break;
424 0 : case DurationType::V_16TH:
425 0 : if (withDot) {
426 0 : text = "<sym>metNote16thUp</sym><sym>space</sym><sym>metAugmentationDot</sym> = %1";
427 0 : qpm *= 0.375;
428 : } else {
429 0 : text = "<sym>metNote16thUp</sym> = %1";
430 0 : qpm *= 0.25;
431 : }
432 0 : break;
433 0 : default:
434 0 : break;
435 : }
436 :
437 0 : engraving::Segment* seg = score->firstMeasure()->first(engraving::SegmentType::ChordRest);
438 0 : engraving::TempoText* tt = new engraving::TempoText(seg);
439 0 : tt->setXmlText(text.arg(bpm));
440 0 : tt->setTempo(qpm / 60.0); // qpm -> qps
441 0 : tt->setFollowText(true);
442 0 : tt->setTrack(0);
443 0 : seg->add(tt);
444 0 : for (auto staff : score->systemObjectStaves()) {
445 0 : TempoText* linkedTt = toTempoText(tt->linkedClone());
446 0 : linkedTt->setScore(score);
447 0 : linkedTt->setTrack(staff->idx() * VOICES);
448 0 : seg->add(linkedTt);
449 : }
450 0 : }
451 :
452 0 : score->updateTicksAndTimeSigMap();
453 0 : AutoSpatium::update(score);
454 :
455 : {
456 0 : mu::engraving::ScoreLoad sl;
457 0 : for (mu::engraving::Score* s : score->scoreList()) {
458 0 : s->doLayout();
459 0 : }
460 0 : }
461 :
462 0 : if (m_notationPlayback) {
463 0 : m_notationPlayback->reload();
464 : }
465 0 : }
466 :
467 0 : void MasterNotation::unloadExcerpts(ExcerptNotationList& excerpts)
468 : {
469 0 : for (const IExcerptNotationPtr& ptr : excerpts) {
470 0 : Excerpt* excerpt = get_impl(ptr)->excerpt();
471 0 : if (!excerpt) {
472 0 : continue;
473 : }
474 :
475 0 : if (Score* score = excerpt->excerptScore()) {
476 0 : delete score;
477 0 : excerpt->setExcerptScore(nullptr);
478 : }
479 : }
480 :
481 0 : excerpts.clear();
482 0 : }
483 :
484 0 : void MasterNotation::initExcerpts(const ExcerptNotationList& excerpts)
485 : {
486 0 : for (const IExcerptNotationPtr& excerptNotation : excerpts) {
487 0 : ExcerptNotation* impl = get_impl(excerptNotation);
488 :
489 0 : masterScore()->initExcerpt(impl->excerpt());
490 0 : impl->init();
491 :
492 0 : initNotationSoloMuteState(impl->notation());
493 : }
494 0 : }
495 :
496 0 : void MasterNotation::setExcerpts(const ExcerptNotationList& excerpts)
497 : {
498 0 : TRACEFUNC;
499 :
500 0 : if (m_excerpts == excerpts) {
501 0 : return;
502 : }
503 :
504 0 : MasterScore* score = masterScore();
505 0 : IF_ASSERT_FAILED(score) {
506 0 : return;
507 : }
508 :
509 0 : undoStack()->prepareChanges(TranslatableString("undoableAction", "Add/remove parts"));
510 :
511 : // Delete old excerpts (that are not included in the new list)
512 0 : for (const IExcerptNotationPtr& excerptNotation : m_excerpts) {
513 0 : auto it = std::find(excerpts.begin(), excerpts.end(), excerptNotation);
514 0 : if (it != excerpts.end()) {
515 0 : continue;
516 : }
517 :
518 0 : score->deleteExcerpt(get_impl(excerptNotation)->excerpt());
519 : }
520 :
521 : // Init new excerpts
522 0 : for (size_t i = 0; i < excerpts.size(); ++i) {
523 0 : const IExcerptNotationPtr& excerptNotation = excerpts.at(i);
524 0 : ExcerptNotation* excerptNotationImpl = get_impl(excerptNotation);
525 :
526 0 : auto it = std::find(m_excerpts.cbegin(), m_excerpts.cend(), excerptNotation);
527 0 : if (it != m_excerpts.end()) {
528 0 : std::vector<Excerpt*>& msExcerpts = score->excerpts();
529 0 : muse::moveItem(msExcerpts, muse::indexOf(msExcerpts, excerptNotationImpl->excerpt()), i);
530 0 : continue;
531 0 : }
532 :
533 0 : score->initAndAddExcerpt(excerptNotationImpl->excerpt(), false);
534 0 : excerptNotationImpl->init();
535 :
536 0 : initNotationSoloMuteState(excerptNotationImpl->notation());
537 : }
538 :
539 0 : score->setExcerptsChanged(false);
540 :
541 0 : undoStack()->commitChanges();
542 :
543 0 : doSetExcerpts(excerpts);
544 0 : }
545 :
546 0 : void MasterNotation::resetExcerpt(IExcerptNotationPtr& excerptNotation)
547 : {
548 0 : if (!excerptNotation || !excerptNotation->isInited()) {
549 0 : return;
550 : }
551 :
552 0 : TRACEFUNC;
553 :
554 0 : undoStack()->prepareChanges(TranslatableString("undoableAction", "Reset part"));
555 :
556 0 : mu::engraving::Excerpt* oldExcerpt = get_impl(excerptNotation)->excerpt();
557 0 : masterScore()->deleteExcerpt(oldExcerpt);
558 :
559 0 : mu::engraving::Excerpt* newExcerpt = new mu::engraving::Excerpt(*oldExcerpt, false);
560 0 : masterScore()->initAndAddExcerpt(newExcerpt, false);
561 :
562 0 : newExcerpt->excerptScore()->setIsOpen(oldExcerpt->excerptScore()->isOpen());
563 :
564 0 : get_impl(excerptNotation)->reinit(newExcerpt);
565 :
566 0 : masterScore()->setExcerptsChanged(false);
567 :
568 0 : undoStack()->commitChanges();
569 0 : }
570 :
571 0 : void MasterNotation::sortExcerpts(ExcerptNotationList& excerpts)
572 : {
573 0 : TRACEFUNC;
574 :
575 0 : std::vector<ID> partIdList;
576 0 : for (const Part* part : masterScore()->parts()) {
577 0 : partIdList.push_back(part->id());
578 : }
579 :
580 0 : std::sort(excerpts.begin(), excerpts.end(), [&partIdList](const IExcerptNotationPtr& f, const IExcerptNotationPtr& s) {
581 : //! NOTE: excertps created by users are always at the end of the list in alphabetical order
582 0 : if (f->isCustom() && !s->isCustom()) {
583 0 : return false;
584 0 : } else if (!f->isCustom() && s->isCustom()) {
585 0 : return true;
586 0 : } else if (f->isCustom() && s->isCustom()) {
587 0 : return f->name() < s->name();
588 : }
589 :
590 : //! NOTE: sort standard excerpts in the order of their initial parts in the main score
591 0 : const ID& initialPart1 = get_impl(f)->excerpt()->initialPartId();
592 0 : const ID& initialPart2 = get_impl(s)->excerpt()->initialPartId();
593 :
594 0 : size_t index1 = muse::indexOf(partIdList, initialPart1);
595 0 : size_t index2 = muse::indexOf(partIdList, initialPart2);
596 :
597 0 : return index1 < index2;
598 : });
599 0 : }
600 :
601 0 : void MasterNotation::setExcerptIsOpen(const INotationPtr excerptNotation, bool open)
602 : {
603 0 : if (excerptNotation->isOpen() == open) {
604 0 : return;
605 : }
606 :
607 0 : excerptNotation->setIsOpen(open);
608 :
609 0 : if (open) {
610 0 : excerptNotation->elements()->msScore()->doLayout();
611 : }
612 : }
613 :
614 0 : void MasterNotation::doSetExcerpts(const ExcerptNotationList& excerpts)
615 : {
616 0 : TRACEFUNC;
617 :
618 0 : m_excerpts = excerpts;
619 0 : m_excerptsChanged.notify();
620 :
621 0 : static_cast<MasterNotationParts*>(m_parts.get())->setExcerpts(excerpts);
622 :
623 0 : updatePotentialExcerpts();
624 0 : }
625 :
626 0 : void MasterNotation::updateExcerpts()
627 : {
628 0 : if (!masterScore()->excerptsChanged()) {
629 0 : return;
630 : }
631 :
632 0 : TRACEFUNC;
633 :
634 0 : ExcerptNotationList updatedExcerpts;
635 :
636 0 : const std::vector<mu::engraving::Excerpt*>& excerpts = masterScore()->excerpts();
637 :
638 : // exclude notations for old excerpts
639 0 : for (const IExcerptNotationPtr& excerptNotation : m_excerpts) {
640 0 : ExcerptNotation* impl = get_impl(excerptNotation);
641 :
642 0 : if (muse::contains(excerpts, impl->excerpt())) {
643 0 : updatedExcerpts.push_back(excerptNotation);
644 0 : continue;
645 : }
646 : }
647 :
648 : // create notations for new excerpts
649 0 : for (mu::engraving::Excerpt* excerpt : excerpts) {
650 0 : if (containsExcerpt(excerpt)) {
651 0 : continue;
652 : }
653 :
654 0 : IExcerptNotationPtr excerptNotation = createAndInitExcerptNotation(this, excerpt, iocContext());
655 0 : bool open = excerpt->excerptScore()->isOpen();
656 0 : if (open) {
657 0 : excerptNotation->notation()->elements()->msScore()->doLayout();
658 : }
659 :
660 0 : initNotationSoloMuteState(excerptNotation->notation());
661 :
662 0 : updatedExcerpts.push_back(excerptNotation);
663 0 : }
664 :
665 0 : doSetExcerpts(updatedExcerpts);
666 :
667 0 : masterScore()->setExcerptsChanged(false);
668 0 : }
669 :
670 0 : void MasterNotation::updatePotentialExcerpts() const
671 : {
672 0 : TRACEFUNC;
673 :
674 0 : auto findExcerptByPart = [](const ExcerptNotationList& excerpts, const Part* part) {
675 0 : return std::find_if(excerpts.cbegin(), excerpts.cend(), [part](const IExcerptNotationPtr& notation) {
676 0 : return get_impl(notation)->excerpt()->initialPartId() == part->id();
677 0 : });
678 : };
679 :
680 0 : ExcerptNotationList potentialExcerpts;
681 0 : std::vector<Part*> partsWithoutExcerpt;
682 :
683 0 : for (Part* part : score()->parts()) {
684 0 : if (part->isSharedPart() || findExcerptByPart(m_excerpts, part) != m_excerpts.end()) {
685 0 : continue;
686 : }
687 :
688 0 : if (!m_potentialExcerptsForcedDirty) {
689 0 : auto it = findExcerptByPart(m_potentialExcerpts, part);
690 0 : if (it != m_potentialExcerpts.cend()) {
691 0 : potentialExcerpts.push_back(*it);
692 0 : continue;
693 : }
694 : }
695 :
696 0 : partsWithoutExcerpt.push_back(part);
697 : }
698 :
699 0 : std::vector<mu::engraving::Excerpt*> excerpts = mu::engraving::Excerpt::createExcerptsFromParts(partsWithoutExcerpt, masterScore());
700 :
701 0 : for (mu::engraving::Excerpt* excerpt : excerpts) {
702 0 : auto excerptNotation = std::make_shared<ExcerptNotation>(const_cast<MasterNotation*>(this), excerpt, iocContext());
703 0 : potentialExcerpts.push_back(excerptNotation);
704 0 : }
705 :
706 0 : m_potentialExcerpts = std::move(potentialExcerpts);
707 0 : m_potentialExcerptsForcedDirty = false;
708 0 : }
709 :
710 0 : bool MasterNotation::containsExcerpt(const mu::engraving::Excerpt* excerpt) const
711 : {
712 0 : for (const IExcerptNotationPtr& excerptNotation : m_excerpts) {
713 0 : if (get_impl(excerptNotation)->excerpt() == excerpt) {
714 0 : return true;
715 : }
716 : }
717 :
718 0 : return false;
719 : }
720 :
721 0 : void MasterNotation::onPartsChanged()
722 : {
723 0 : m_hasPartsChanged.notify();
724 0 : m_potentialExcerptsForcedDirty = true;
725 0 : }
726 :
727 0 : IExcerptNotationPtr MasterNotation::createEmptyExcerpt(const QString& name) const
728 : {
729 : auto excerptNotation
730 0 : = std::make_shared<ExcerptNotation>(const_cast<MasterNotation*>(this), new mu::engraving::Excerpt(masterScore()), iocContext());
731 0 : excerptNotation->setName(name);
732 :
733 0 : return excerptNotation;
734 0 : }
735 :
736 0 : const ExcerptNotationList& MasterNotation::excerpts() const
737 : {
738 0 : return m_excerpts;
739 : }
740 :
741 0 : muse::async::Notification MasterNotation::excerptsChanged() const
742 : {
743 0 : return m_excerptsChanged;
744 : }
745 :
746 0 : INotationPartsPtr MasterNotation::parts() const
747 : {
748 0 : return m_parts;
749 : }
750 :
751 0 : bool MasterNotation::hasParts() const
752 : {
753 0 : return m_parts && m_parts->hasParts();
754 : }
755 :
756 0 : Notification MasterNotation::hasPartsChanged() const
757 : {
758 0 : return m_hasPartsChanged;
759 : }
760 :
761 0 : INotationPlaybackPtr MasterNotation::playback() const
762 : {
763 0 : return m_notationPlayback;
764 : }
765 :
766 0 : INotationAutomationPtr MasterNotation::automation() const
767 : {
768 0 : return m_notationAutomation;
769 : }
770 :
771 0 : void MasterNotation::initNotationSoloMuteState(const INotationPtr notation)
772 : {
773 0 : IF_ASSERT_FAILED(notation) {
774 0 : return;
775 : }
776 :
777 0 : const INotationSoloMuteStatePtr excerptSoloMuteState = notation->soloMuteState();
778 :
779 : // Iterate over all parts (not just the excerpt parts) - tracks that do exist in the master notation but
780 : // not in the excerpt should be muted in the excerpt...
781 0 : for (const Part* masterPart : parts()->partList()) {
782 0 : const Part* excerptPart = notation->parts()->part(masterPart->id());
783 0 : const bool shouldMute = !excerptPart || !excerptPart->isVisible();
784 0 : const INotationSoloMuteState::SoloMuteState state = { shouldMute, /*solo*/ false };
785 0 : for (const InstrumentTrackId& trackId : masterPart->instrumentTrackIdSet()) {
786 0 : excerptSoloMuteState->setTrackSoloMuteState(trackId, state);
787 0 : }
788 0 : if (masterPart->hasChordSymbol()) {
789 0 : const InstrumentTrackId& chordsId = playback()->chordSymbolsTrackId(masterPart->id());
790 0 : excerptSoloMuteState->setTrackSoloMuteState(chordsId, state);
791 0 : }
792 0 : }
793 0 : }
794 :
795 0 : const ExcerptNotationList& MasterNotation::potentialExcerpts() const
796 : {
797 0 : updatePotentialExcerpts();
798 :
799 0 : return m_potentialExcerpts;
800 : }
801 :
802 0 : void MasterNotation::initExcerptNotations(const std::vector<mu::engraving::Excerpt*>& excerpts)
803 : {
804 0 : TRACEFUNC;
805 :
806 0 : ExcerptNotationList notationExcerpts;
807 :
808 0 : for (mu::engraving::Excerpt* excerpt : excerpts) {
809 0 : if (excerpt->isEmpty()) {
810 0 : masterScore()->initEmptyExcerpt(excerpt);
811 : }
812 :
813 0 : IExcerptNotationPtr excerptNotation = createAndInitExcerptNotation(this, excerpt, iocContext());
814 0 : notationExcerpts.push_back(excerptNotation);
815 0 : }
816 :
817 0 : masterScore()->setExcerptsChanged(false);
818 :
819 0 : doSetExcerpts(notationExcerpts);
820 0 : }
821 :
822 0 : void MasterNotation::addExcerptsToMasterScore(const std::vector<mu::engraving::Excerpt*>& excerpts)
823 : {
824 0 : TRACEFUNC;
825 :
826 0 : for (mu::engraving::Excerpt* excerpt : excerpts) {
827 0 : masterScore()->initAndAddExcerpt(excerpt, false);
828 : }
829 :
830 0 : masterScore()->setExcerptsChanged(false);
831 0 : }
|