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 "excerpt.h"
24 :
25 : #include "containers.h"
26 :
27 : #include "../editing/addremoveelement.h"
28 : #include "../editing/editexcerpt.h"
29 : #include "../editing/transpose.h"
30 : #include "../editing/transaction/transaction.h"
31 : #include "style/style.h"
32 :
33 : #include "barline.h"
34 : #include "beam.h"
35 : #include "box.h"
36 : #include "capo.h"
37 : #include "chord.h"
38 : #include "factory.h"
39 : #include "fret.h"
40 : #include "guitarbend.h"
41 : #include "harmony.h"
42 : #include "layoutbreak.h"
43 : #include "linkedobjects.h"
44 : #include "lyrics.h"
45 : #include "masterscore.h"
46 : #include "measure.h"
47 : #include "note.h"
48 : #include "ornament.h"
49 : #include "page.h"
50 : #include "part.h"
51 : #include "partialtie.h"
52 : #include "rest.h"
53 : #include "score.h"
54 : #include "segment.h"
55 : #include "staff.h"
56 : #include "stafftype.h"
57 : #include "system.h"
58 : #include "text.h"
59 : #include "textline.h"
60 : #include "tie.h"
61 : #include "tiemap.h"
62 : #include "tremolotwochord.h"
63 : #include "tuplet.h"
64 : #include "tupletmap.h"
65 : #include "utils.h"
66 :
67 : #include "log.h"
68 :
69 : using namespace mu;
70 : using namespace mu::engraving;
71 :
72 0 : Excerpt::Excerpt(const Excerpt& ex, bool copyContents)
73 0 : : m_masterScore(ex.m_masterScore), m_name(ex.m_name), m_parts(ex.m_parts), m_initialPartId(ex.m_initialPartId)
74 : {
75 0 : if (copyContents) {
76 0 : m_tracksMapping = ex.m_tracksMapping;
77 0 : m_excerptScore = ex.m_excerptScore ? ex.m_excerptScore->clone() : nullptr;
78 :
79 0 : if (m_excerptScore) {
80 0 : m_excerptScore->setExcerpt(this);
81 : }
82 : }
83 0 : }
84 :
85 150 : Excerpt::~Excerpt()
86 : {
87 150 : delete m_excerptScore;
88 150 : }
89 :
90 110 : bool Excerpt::inited() const
91 : {
92 110 : return m_inited;
93 : }
94 :
95 0 : void Excerpt::setInited(bool inited)
96 : {
97 0 : m_inited = inited;
98 0 : }
99 :
100 0 : bool Excerpt::custom() const
101 : {
102 0 : return !m_initialPartId.isValid();
103 : }
104 :
105 0 : void Excerpt::markAsCustom()
106 : {
107 0 : setInitialPartId(ID());
108 0 : }
109 :
110 190 : const ID& Excerpt::initialPartId() const
111 : {
112 190 : return m_initialPartId;
113 : }
114 :
115 34 : void Excerpt::setInitialPartId(const ID& id)
116 : {
117 34 : m_initialPartId = id;
118 34 : }
119 :
120 164 : void Excerpt::setExcerptScore(Score* s)
121 : {
122 164 : m_excerptScore = s;
123 :
124 164 : if (s) {
125 164 : s->setExcerpt(this);
126 164 : writeNameToMetaTags();
127 : }
128 164 : }
129 :
130 395 : const String& Excerpt::name() const
131 : {
132 395 : return m_name;
133 : }
134 :
135 161 : void Excerpt::setName(const String& name, bool saveAndNotify)
136 : {
137 161 : if (m_name == name) {
138 5 : return;
139 : }
140 :
141 156 : m_name = name;
142 :
143 156 : if (saveAndNotify) {
144 50 : writeNameToMetaTags();
145 50 : m_nameChanged.notify();
146 : }
147 : }
148 :
149 214 : void Excerpt::writeNameToMetaTags()
150 : {
151 214 : if (Score* score = excerptScore()) {
152 214 : if (Text* nameItem = score->getText(mu::engraving::TextStyleType::INSTRUMENT_EXCERPT)) {
153 0 : nameItem->setPlainText(m_name);
154 0 : score->setMetaTag(u"partName", m_name);
155 : }
156 : }
157 214 : }
158 :
159 0 : muse::async::Notification Excerpt::nameChanged() const
160 : {
161 0 : return m_nameChanged;
162 : }
163 :
164 0 : bool Excerpt::hasFileName() const
165 : {
166 0 : return !m_fileName.empty();
167 : }
168 :
169 0 : const String& Excerpt::fileName() const
170 : {
171 0 : IF_ASSERT_FAILED(!m_fileName.empty()) {
172 0 : const_cast<Excerpt*>(this)->updateFileName();
173 : }
174 :
175 0 : return m_fileName;
176 : }
177 :
178 11 : void Excerpt::setFileName(const String& fileName)
179 : {
180 11 : m_fileName = fileName;
181 11 : }
182 :
183 0 : void Excerpt::updateFileName(size_t index)
184 : {
185 0 : if (index == muse::nidx && m_masterScore) {
186 0 : index = muse::indexOf(m_masterScore->excerpts(), this);
187 : }
188 :
189 0 : const String escapedName = muse::io::escapeFileName(m_name).toString();
190 :
191 0 : if (index == muse::nidx) {
192 0 : m_fileName = escapedName;
193 : } else {
194 0 : m_fileName = String(u"%1_%2").arg(String::number(index), escapedName);
195 : }
196 0 : }
197 :
198 0 : bool Excerpt::containsPart(const Part* part) const
199 : {
200 0 : for (Part* _part : m_parts) {
201 0 : if (_part == part) {
202 0 : return true;
203 : }
204 : }
205 :
206 0 : return false;
207 : }
208 :
209 0 : void Excerpt::removePart(const ID& id)
210 : {
211 0 : size_t index = 0;
212 0 : for (const Part* part: parts()) {
213 0 : if (part->id() == id) {
214 0 : break;
215 : }
216 0 : ++index;
217 : }
218 0 : if (index >= m_parts.size()) {
219 0 : return;
220 : }
221 :
222 0 : excerptScore()->undoRemovePart(excerptScore()->parts().at(index), index);
223 : }
224 :
225 565 : size_t Excerpt::nstaves() const
226 : {
227 565 : size_t n = 0;
228 1130 : for (Part* p : m_parts) {
229 565 : n += p->nstaves();
230 : }
231 565 : return n;
232 : }
233 :
234 0 : bool Excerpt::isEmpty() const
235 : {
236 0 : return excerptScore() ? excerptScore()->parts().empty() : true;
237 : }
238 :
239 483 : const TracksMap& Excerpt::tracksMapping()
240 : {
241 483 : updateTracksMapping();
242 :
243 483 : return m_tracksMapping;
244 : }
245 :
246 601 : void Excerpt::setTracksMapping(const TracksMap& tracksMapping)
247 : {
248 601 : if (m_tracksMapping == tracksMapping) {
249 427 : return;
250 : }
251 :
252 174 : m_tracksMapping = tracksMapping;
253 :
254 174 : const Score* score = excerptScore();
255 174 : if (!score) {
256 0 : return;
257 : }
258 :
259 358 : for (Staff* staff : score->staves()) {
260 184 : const Staff* masterStaff = m_masterScore->staffById(staff->id());
261 184 : if (!masterStaff) {
262 0 : continue;
263 : }
264 :
265 184 : staff->updateVisibilityVoices(masterStaff, m_tracksMapping);
266 : }
267 : }
268 :
269 493 : void Excerpt::updateTracksMapping()
270 : {
271 493 : const Score* score = excerptScore();
272 493 : if (!score) {
273 0 : return;
274 : }
275 :
276 493 : TracksMap tracks;
277 :
278 494 : TRACEFUNC;
279 :
280 1014 : for (const Staff* staff : score->staves()) {
281 521 : const Staff* masterStaff = masterScore()->staffById(staff->id());
282 521 : if (!masterStaff) {
283 0 : continue;
284 : }
285 :
286 521 : staff_idx_t masterStaffIdx = masterStaff->idx();
287 521 : staff_idx_t staffIdx = staff->idx();
288 :
289 521 : voice_idx_t voice = 0;
290 521 : const std::array<bool, VOICES>& voicesVisibility = staff->visibilityVoices();
291 :
292 2605 : for (voice_idx_t i = 0; i < VOICES; ++i) {
293 2084 : if (!voicesVisibility[i]) {
294 51 : continue;
295 : }
296 :
297 2033 : tracks.insert({ masterStaffIdx* VOICES + i % VOICES, staffIdx * VOICES + voice % VOICES });
298 2033 : voice++;
299 : }
300 : }
301 :
302 493 : setTracksMapping(tracks);
303 493 : }
304 :
305 9 : void Excerpt::setVoiceVisible(Staff* staff, voice_idx_t voiceIndex, bool visible)
306 : {
307 10 : TRACEFUNC;
308 :
309 9 : if (!staff) {
310 0 : return;
311 : }
312 :
313 9 : Staff* masterStaff = masterScore()->staffById(staff->id());
314 9 : if (!masterStaff) {
315 0 : return;
316 : }
317 :
318 9 : bool wasVisible = staff->isVoiceVisible(voiceIndex);
319 9 : if (visible == wasVisible) {
320 0 : return;
321 : }
322 :
323 : // update tracks (temporarily modify the old staff)
324 9 : staff->setVoiceVisible(voiceIndex, visible);
325 9 : updateTracksMapping();
326 :
327 : // create new staff
328 9 : Staff* newStaff = Factory::createStaff(staff->part());
329 9 : newStaff->setId(staff->id());
330 9 : newStaff->init(staff);
331 :
332 : // revert changes to old staff, to make undo work properly
333 9 : staff->setVoiceVisible(voiceIndex, wasVisible);
334 :
335 : // remove old staff, insert new
336 9 : staff_idx_t staffIndex = staff->idx();
337 9 : staff_idx_t partStaffIndex = staffIndex - excerptScore()->staffIdx(staff->part());
338 9 : excerptScore()->undoRemoveStaff(staff);
339 9 : excerptScore()->undoInsertStaff(newStaff, partStaffIndex);
340 :
341 : // clone master staff to new staff with mapped tracks
342 9 : Fraction startTick = staff->score()->firstMeasure()->tick();
343 9 : Fraction endTick = staff->score()->lastMeasure()->endTick();
344 9 : cloneStaff2(masterStaff, newStaff, startTick, endTick);
345 :
346 : // link master staff to cloned
347 9 : excerptScore()->undo(new Link(newStaff, masterStaff));
348 9 : }
349 :
350 56 : void Excerpt::createExcerpt(Excerpt* excerpt)
351 : {
352 56 : MasterScore* masterScore = excerpt->masterScore();
353 56 : Score* score = excerpt->excerptScore();
354 56 : Transaction& tx = masterScore->transactionManager()->currentOrDummyTransaction();
355 :
356 56 : std::vector<Part*>& parts = excerpt->parts();
357 56 : std::vector<staff_idx_t> srcStaves;
358 :
359 56 : score->setPageNumberOffset(masterScore->pageNumberOffset());
360 :
361 : // Set instruments and create linked staves
362 111 : for (const Part* part : parts) {
363 55 : Part* p = new Part(score);
364 55 : p->setId(part->id());
365 55 : p->setInstrument(*part->instrument());
366 55 : p->setPreferSharpFlat(part->preferSharpFlat());
367 :
368 113 : for (Staff* staff : part->staves()) {
369 58 : Staff* s = Factory::createStaff(p);
370 58 : s->setId(staff->id());
371 58 : s->init(staff);
372 58 : s->setDefaultClefType(staff->defaultClefType());
373 : // the order of staff - s matters as staff should be the first entry in the
374 : // created link list to make primaryStaff() work
375 : // TODO: change implementation, maybe create an explicit "primary" flag
376 58 : score->undo(new Link(s, staff));
377 58 : score->appendStaff(s);
378 58 : srcStaves.push_back(staff->idx());
379 : }
380 55 : score->appendPart(p);
381 : }
382 :
383 : // Fill tracklist (map all tracks of a stave)
384 56 : if (excerpt->tracksMapping().empty()) {
385 1 : excerpt->updateTracksMapping();
386 : }
387 :
388 56 : cloneStaves(masterScore, score, srcStaves, excerpt->tracksMapping());
389 :
390 56 : MeasureBase* masterMeasure = masterScore->first();
391 56 : MeasureBase* scoreMeasure = score->first();
392 :
393 56 : if ((!scoreMeasure || !scoreMeasure->isVBox()) && (!masterMeasure || !masterMeasure->excludeFromOtherParts())) {
394 17 : score->insertBox(ElementType::VBOX, scoreMeasure);
395 17 : scoreMeasure = score->first();
396 : }
397 :
398 56 : VBox* titleFrameScore = scoreMeasure && scoreMeasure->isVBox() ? toVBox(scoreMeasure) : nullptr;
399 :
400 56 : if (titleFrameScore && masterMeasure && masterMeasure->isVBox()) {
401 39 : VBox* titleFrameMaster = toVBox(masterMeasure);
402 :
403 39 : titleFrameScore->copyValues(titleFrameMaster);
404 : }
405 :
406 56 : String partLabel = excerpt->name();
407 56 : if (!partLabel.empty()) {
408 50 : if (titleFrameScore) {
409 50 : Text* txt = Factory::createText(titleFrameScore, TextStyleType::INSTRUMENT_EXCERPT);
410 50 : txt->setPlainText(partLabel);
411 50 : titleFrameScore->add(txt);
412 : }
413 :
414 50 : score->setMetaTag(u"partName", partLabel);
415 : }
416 :
417 : // initial layout of score
418 56 : score->doLayout();
419 :
420 : // handle transposing instruments
421 56 : if (masterScore->style().styleB(Sid::concertPitch) != score->style().styleB(Sid::concertPitch)) {
422 4 : for (const Staff* staff : score->staves()) {
423 2 : if (staff->staffType(Fraction(0, 1))->group() == StaffGroup::PERCUSSION) {
424 1 : continue;
425 : }
426 :
427 : // if this staff has no transposition, and no instrument changes, we can skip it
428 2 : Interval interval = staff->part()->instrument()->transpose(); //tick?
429 2 : if (interval.isZero() && staff->part()->instruments().size() == 1) {
430 1 : continue;
431 : }
432 1 : bool flip = false;
433 1 : if (masterScore->style().styleB(Sid::concertPitch)) {
434 1 : interval.flip(); // flip the transposition for the original instrument
435 1 : flip = true; // transposeKeys() will flip transposition for each instrument change
436 : }
437 :
438 1 : staff_idx_t staffIdx = staff->idx();
439 1 : track_idx_t startTrack = staffIdx * VOICES;
440 1 : track_idx_t endTrack = startTrack + VOICES;
441 :
442 1 : Fraction endTick = Fraction(0, 1);
443 1 : if (score->lastSegment()) {
444 1 : endTick = score->lastSegment()->tick();
445 : }
446 1 : Transpose::transposeKeys(tx, score, staffIdx, staffIdx + 1, Fraction(0, 1), endTick, flip);
447 :
448 7 : for (auto segment = score->firstSegmentMM(SegmentType::ChordRest); segment;
449 6 : segment = segment->next1MM(SegmentType::ChordRest)) {
450 6 : interval = staff->transpose(segment->tick());
451 6 : if (interval.isZero()) {
452 0 : continue;
453 : }
454 6 : if (masterScore->style().styleB(Sid::concertPitch)) {
455 6 : interval.flip();
456 : }
457 :
458 7 : for (auto e : segment->annotations()) {
459 1 : if (!e->isHarmony() || (e->track() < startTrack) || (e->track() >= endTrack)) {
460 1 : continue;
461 : }
462 0 : Harmony* h = toHarmony(e);
463 : // mmrests are on by default in part
464 : // if this harmony is attached to an mmrest,
465 : // be sure to transpose harmony in underlying measure as well
466 0 : for (EngravingObject* se : h->linkList()) {
467 0 : Harmony* hh = toHarmony(se);
468 : // skip links to other staves (including in other scores)
469 0 : if (hh->staff() != h->staff()) {
470 0 : continue;
471 : }
472 0 : Transpose::undoTransposeHarmony(tx, hh, interval);
473 0 : }
474 : }
475 : }
476 : }
477 : }
478 :
479 : // update style values if spatium different for part
480 56 : if (masterScore->style().spatium() != score->style().spatium()) {
481 35 : score->spatiumChanged(masterScore->style().spatium(), score->style().spatium());
482 35 : score->styleChanged();
483 : }
484 :
485 : // second layout of score
486 56 : score->invalidateRepeatList();
487 56 : masterScore->rebuildMidiMapping();
488 56 : masterScore->updateChannel();
489 56 : score->remapBracketsAndBarlines();
490 :
491 56 : score->setLayoutAll();
492 56 : score->doLayout();
493 56 : }
494 :
495 1 : void MasterScore::deleteExcerpt(Excerpt* excerpt)
496 : {
497 1 : assert(excerpt->masterScore() == this);
498 1 : Score* partScore = excerpt->excerptScore();
499 :
500 1 : if (!partScore) {
501 0 : LOGD("deleteExcerpt: no partScore");
502 0 : return;
503 : }
504 :
505 : // unlink the staves in the excerpt
506 2 : for (Staff* st : partScore->staves()) {
507 1 : bool hasLinksInMaster = false;
508 1 : if (st->links()) {
509 1 : for (auto le : *st->links()) {
510 1 : if (le->score() == this) {
511 1 : hasLinksInMaster = true;
512 1 : break;
513 : }
514 : }
515 : }
516 1 : if (hasLinksInMaster) {
517 1 : staff_idx_t staffIdx = st->idx();
518 : // unlink the spanners
519 1 : for (auto i = partScore->spanner().begin(); i != partScore->spanner().cend(); ++i) {
520 0 : Spanner* sp = i->second;
521 0 : if (sp->staffIdx() == staffIdx) {
522 0 : sp->undoUnlink();
523 : }
524 : }
525 1 : track_idx_t sTrack = staffIdx * VOICES;
526 1 : track_idx_t eTrack = sTrack + VOICES;
527 : // unlink elements and annotation
528 6 : for (Segment* s = partScore->firstSegmentMM(SegmentType::All); s; s = s->next1MM()) {
529 25 : for (int track = static_cast<int>(eTrack) - 1; track >= static_cast<int>(sTrack); --track) {
530 20 : EngravingItem* el = s->element(track);
531 20 : if (el) {
532 5 : el->undoUnlink();
533 : }
534 : }
535 5 : for (EngravingItem* e : s->annotations()) {
536 0 : if (e->staffIdx() == staffIdx) {
537 0 : e->undoUnlink();
538 : }
539 : }
540 : }
541 : // unlink the staff
542 1 : undo(new Unlink(st));
543 : }
544 : }
545 1 : undo(new RemoveExcerpt(excerpt));
546 : }
547 :
548 0 : void MasterScore::initAndAddExcerpt(Excerpt* excerpt, bool fakeUndo)
549 : {
550 0 : initExcerpt(excerpt);
551 :
552 0 : auto excerptCmd = new AddExcerpt(excerpt);
553 0 : if (fakeUndo) {
554 0 : excerptCmd->redo();
555 : } else {
556 0 : excerpt->excerptScore()->undo(excerptCmd);
557 : }
558 0 : }
559 :
560 0 : void MasterScore::initExcerpt(Excerpt* excerpt)
561 : {
562 0 : if (excerpt->inited()) {
563 0 : excerpt->excerptScore()->doLayout();
564 0 : return;
565 : }
566 :
567 0 : Score* score = new Score(masterScore());
568 0 : excerpt->setExcerptScore(score);
569 0 : score->style().set(Sid::createMultiMeasureRests, true);
570 0 : initParts(excerpt);
571 :
572 0 : Excerpt::createExcerpt(excerpt);
573 0 : excerpt->setInited(true);
574 :
575 0 : if (!score->style().isDefault(Sid::timeSigPlacement)) {
576 0 : score->resetStyleValue(Sid::timeSigPlacement);
577 : }
578 : }
579 :
580 110 : void MasterScore::initParts(Excerpt* excerpt)
581 : {
582 110 : int nstaves { 1 }; // Initialise to 1 to force writing of the first part.
583 110 : std::set<ID> assignedStavesIds;
584 226 : for (Staff* excerptStaff : excerpt->excerptScore()->staves()) {
585 116 : const LinkedObjects* ls = excerptStaff->links();
586 116 : if (ls == 0) {
587 0 : continue;
588 : }
589 :
590 117 : for (auto le : *ls) {
591 117 : if (le->score() != this) {
592 0 : continue;
593 : }
594 :
595 117 : Staff* linkedMasterStaff = toStaff(le);
596 117 : if (muse::contains(assignedStavesIds, linkedMasterStaff->id())) {
597 1 : continue;
598 : }
599 :
600 116 : Part* excerptPart = excerptStaff->part();
601 116 : Part* masterPart = linkedMasterStaff->part();
602 :
603 : //! NOTE: parts/staves of excerpt must have the same ID as parts/staves of the master score
604 : //! In fact, excerpts are just viewers for the master score
605 116 : excerptStaff->setId(linkedMasterStaff->id());
606 116 : excerptPart->setId(masterPart->id());
607 :
608 116 : assignedStavesIds.insert(linkedMasterStaff->id());
609 :
610 : // For instruments with multiple staves, every staff will point to the
611 : // same part. To prevent adding the same part several times to the excerpt,
612 : // add only the part of the first staff pointing to the part.
613 116 : if (!(--nstaves)) {
614 110 : excerpt->parts().push_back(linkedMasterStaff->part());
615 110 : nstaves = static_cast<int>(linkedMasterStaff->part()->nstaves());
616 : }
617 116 : break;
618 : }
619 : }
620 :
621 110 : if (excerpt->tracksMapping().empty()) { // SHOULDN'T HAPPEN, protected in the UI, but it happens during read-in!!!
622 0 : excerpt->updateTracksMapping();
623 : }
624 110 : }
625 :
626 0 : void MasterScore::initEmptyExcerpt(Excerpt* excerpt)
627 : {
628 0 : if (excerpt->inited()) {
629 0 : return;
630 : }
631 :
632 0 : Excerpt::cloneMeasures(this, excerpt->excerptScore());
633 0 : excerpt->setInited(true);
634 : }
635 :
636 0 : static bool scoreContainsSpanner(const Score* score, Spanner* spanner)
637 : {
638 0 : const std::multimap<int, Spanner*>& spanners = score->spanner();
639 :
640 0 : for (auto it = spanners.cbegin(); it != spanners.cend(); ++it) {
641 0 : const Spanner* curSpanner = it->second;
642 0 : if (curSpanner->links() && curSpanner->links()->contains(spanner)) {
643 0 : return true;
644 : }
645 : }
646 :
647 0 : return false;
648 : }
649 :
650 43 : void Excerpt::cloneSpanner(Spanner* s, Score* score, track_idx_t dstTrack, track_idx_t dstTrack2)
651 : {
652 : // don’t clone system lines for track != 0
653 43 : if (isSystemTextLine(s) && s->track() != 0) {
654 0 : return;
655 : }
656 :
657 43 : Spanner* ns = toSpanner(s->linkedClone());
658 43 : ns->setScore(score);
659 43 : ns->moveToDummy();
660 43 : ns->setTrack(dstTrack);
661 43 : ns->setTrack2(dstTrack2);
662 :
663 43 : if (ns->isSlur()) {
664 : // set start/end element for slur
665 22 : ChordRest* cr1 = s->startCR();
666 22 : ChordRest* cr2 = s->endCR();
667 :
668 22 : ns->setStartElement(0);
669 22 : ns->setEndElement(0);
670 22 : if (cr1 && cr1->links()) {
671 47 : for (EngravingObject* e : *cr1->links()) {
672 47 : ChordRest* cr = toChordRest(e);
673 47 : if (cr == cr1) {
674 22 : continue;
675 : }
676 25 : if ((cr->score() == score) && (cr->tick() == ns->tick()) && cr->track() == dstTrack) {
677 22 : ns->setStartElement(cr);
678 22 : break;
679 : }
680 : }
681 : }
682 22 : if (cr2 && cr2->links()) {
683 47 : for (EngravingObject* e : *cr2->links()) {
684 47 : ChordRest* cr = toChordRest(e);
685 47 : if (cr == cr2) {
686 22 : continue;
687 : }
688 25 : if ((cr->score() == score) && (cr->tick() == ns->tick2()) && cr->track() == dstTrack2) {
689 22 : ns->setEndElement(cr);
690 22 : break;
691 : }
692 : }
693 : }
694 22 : if (!ns->startElement()) {
695 0 : LOGD("clone Slur: no start element");
696 : }
697 22 : if (!ns->endElement()) {
698 0 : LOGD("clone Slur: no end element");
699 : }
700 21 : } else if (ns->isTrill()) {
701 1 : ns->computeStartElement();
702 : } else {
703 20 : if (!ns->startElement()) {
704 1 : ns->computeStartElement();
705 : }
706 20 : if (!ns->endElement()) {
707 0 : ns->computeEndElement();
708 : }
709 : }
710 :
711 43 : if (!ns->startElement() || !ns->endElement()) {
712 0 : if (EngravingItem* startElement = ns->startElement()) {
713 0 : if (startElement->isChord()) {
714 0 : toChord(startElement)->removeStartingSpanner(ns);
715 : }
716 : }
717 0 : if (EngravingItem* endElement = ns->endElement()) {
718 0 : if (endElement->isChord()) {
719 0 : toChord(endElement)->removeEndingSpanner(ns);
720 : }
721 : }
722 0 : LOGD() << "No start or end element, can't add spanner: " << ns->tick().toString();
723 0 : delete ns;
724 0 : return;
725 : }
726 :
727 43 : score->doUndoAddElement(ns);
728 43 : ns->styleChanged();
729 : }
730 :
731 408 : static void updateSpatium(EngravingItem* oldElement, EngravingItem* newElement)
732 : {
733 408 : double oldSpatium = oldElement->spatium();
734 408 : double newSpatium = newElement->spatium();
735 408 : if (!muse::RealIsEqual(oldSpatium, newSpatium)) {
736 360 : newElement->spatiumChanged(oldSpatium, newSpatium);
737 : }
738 408 : }
739 :
740 28 : static void cloneTuplets(ChordRest* ocr, ChordRest* ncr, Tuplet* ot, TupletMap& tupletMap, Measure* nm, track_idx_t track)
741 : {
742 6 : const auto handleTuplet = [&](Tuplet* tuplet) {
743 6 : tuplet->clear();
744 6 : tuplet->setTrack(track);
745 6 : tuplet->setOwnershipParent(nm);
746 6 : tuplet->styleChanged();
747 12 : tuplet->scanElements([&](EngravingItem* newElement) { updateSpatium(ot, newElement); });
748 6 : };
749 :
750 28 : ot->setTrack(ocr->track());
751 :
752 28 : Tuplet* nt = tupletMap.findNew(ot);
753 28 : if (!nt) {
754 6 : nt = toTuplet(ot->linkedClone());
755 6 : handleTuplet(nt);
756 6 : tupletMap.add(ot, nt);
757 :
758 6 : Tuplet* nt1 = nt;
759 6 : while (ot->tuplet()) {
760 0 : Tuplet* nt2 = tupletMap.findNew(ot->tuplet());
761 0 : if (nt2 == 0) {
762 0 : nt2 = toTuplet(ot->tuplet()->linkedClone());
763 0 : handleTuplet(nt2);
764 0 : tupletMap.add(ot->tuplet(), nt2);
765 : }
766 0 : nt2->add(nt1);
767 0 : nt1->setTuplet(nt2);
768 0 : ot = ot->tuplet();
769 0 : nt1 = nt2;
770 : }
771 : }
772 28 : nt->add(ncr);
773 28 : ncr->setTuplet(nt);
774 28 : }
775 :
776 40 : static void processLinkedClone(EngravingItem* ne, Score* score, track_idx_t strack)
777 : {
778 40 : ne->setTrack(strack == muse::nidx ? 0 : strack);
779 40 : ne->setScore(score);
780 40 : }
781 :
782 1049 : static void addTies(Note* originalNote, Note* newNote, TieMap& tieMap, Score* score)
783 : {
784 1049 : if (originalNote->tieFor() && originalNote->tieFor()->type() == ElementType::TIE) {
785 29 : Tie* tie = toTie(originalNote->tieFor()->linkedClone());
786 29 : tie->setScore(score);
787 29 : newNote->setTieFor(tie);
788 29 : tie->setStartNote(newNote);
789 29 : tie->setTrack(newNote->track());
790 29 : tieMap.add(originalNote->tieFor(), tie);
791 : }
792 1049 : if (originalNote->tieBack() && originalNote->tieBack()->type() == ElementType::TIE) {
793 29 : Tie* tie = tieMap.findNew(originalNote->tieBack());
794 29 : if (tie) {
795 29 : newNote->setTieBack(tie);
796 29 : tie->setEndNote(newNote);
797 29 : tie->setTrack2(newNote->track());
798 : } else {
799 0 : LOGD("addTiesToMap: cannot find tie");
800 : }
801 : }
802 :
803 1049 : if (originalNote->outgoingPartialTie()) {
804 0 : tieMap.add(originalNote->outgoingPartialTie(), newNote->outgoingPartialTie());
805 : }
806 1049 : }
807 :
808 163 : static void addBackSpanners(Note* on, Note* nn, Score* score)
809 : {
810 : // add back spanners (going back from end to start spanner element
811 : // makes sure the 'other' spanner anchor element is already set up)
812 : // 'on' is the old spanner end note and 'nn' is the new spanner end note
813 163 : for (Spanner* oldSp : on->spannerBack()) {
814 0 : Note* newStart = Spanner::startElementFromSpanner(oldSp, nn);
815 0 : if (newStart != nullptr) {
816 0 : Spanner* newSp = toSpanner(oldSp->linkedClone());
817 0 : newSp->setNoteSpan(newStart, nn);
818 0 : score->addElement(newSp);
819 : } else {
820 0 : LOGD("cloneStaff2: cannot find spanner start note");
821 : }
822 : }
823 163 : }
824 :
825 1974 : static void addGraceNoteTiesAndBackSpanners(GraceNotesGroup& originalGraceNotes, Chord* newChord, TieMap& tieMap, Score* score)
826 : {
827 1996 : for (Chord* oldGrace : originalGraceNotes) {
828 22 : Chord* newGrace = newChord->graceNoteAt(oldGrace->graceIndex());
829 22 : if (!newGrace) {
830 0 : continue;
831 : }
832 :
833 22 : size_t notes = oldGrace->notes().size();
834 44 : for (size_t i = 0; i < notes; ++i) {
835 22 : Note* originalNote = oldGrace->notes().at(i);
836 22 : Note* newNote = newGrace->notes().at(i);
837 :
838 22 : addTies(originalNote, newNote, tieMap, score);
839 22 : addBackSpanners(originalNote, newNote, score);
840 : }
841 : }
842 1974 : }
843 :
844 987 : static void addTremoloTwoChord(Chord* oldChord, Chord* newChord, TremoloTwoChord*& prevTremolo)
845 : {
846 987 : if (!newChord || !oldChord || !newChord->tremoloTwoChord()) {
847 984 : return;
848 : }
849 :
850 3 : if (oldChord == oldChord->tremoloTwoChord()->chord1()) {
851 3 : prevTremolo = newChord->tremoloTwoChord();
852 0 : } else if (oldChord == oldChord->tremoloTwoChord()->chord2()) {
853 0 : if (!prevTremolo) {
854 0 : LOGD("first note for two note tremolo missing");
855 : } else {
856 0 : prevTremolo->setChords(prevTremolo->chord1(), newChord);
857 0 : newChord->setTremoloTwoChord(prevTremolo);
858 : }
859 : } else {
860 0 : LOGD("inconsistent two note tremolo");
861 : }
862 : }
863 :
864 152 : static void collectTieEndPoints(TieMap& tieMap)
865 : {
866 181 : for (auto& tie : tieMap) {
867 29 : Tie* newTie = toTie(tie.second);
868 29 : if (newTie->type() == ElementType::TIE || (newTie->type() == ElementType::PARTIAL_TIE && toPartialTie(newTie)->isOutgoing())) {
869 29 : newTie->updatePossibleJumpPoints();
870 : }
871 : }
872 152 : }
873 :
874 0 : static void transposeHarmony(Harmony* harmony, const Staff* srcStaff, bool scoreConcertPitch)
875 : {
876 0 : Interval interval = srcStaff->transpose(harmony->tick());
877 0 : if (interval.isZero() && srcStaff->part()->instruments().size() == 1) {
878 0 : return;
879 : }
880 :
881 0 : if (!scoreConcertPitch) {
882 0 : interval.flip();
883 : }
884 0 : Transpose::doUndoTransposeHarmony(harmony, interval);
885 : }
886 :
887 906 : static MeasureBase* cloneMeasure(MeasureBase* mb, Score* score, const Score* oscore,
888 : const std::vector<staff_idx_t>& sourceStavesIndexes,
889 : const TracksMap& trackList, TieMap& tieMap)
890 : {
891 906 : MeasureBase* nmb = nullptr;
892 :
893 906 : if (mb->isHBox()) {
894 1 : nmb = Factory::createHBox(score);
895 1 : nmb->setTick(mb->tick());
896 1 : nmb->setTicks(mb->ticks());
897 905 : } else if (mb->isVBox()) {
898 39 : if (toBox(mb)->isTitleFrame()) {
899 39 : nmb = Factory::createTitleVBox(score);
900 : } else {
901 0 : nmb = Factory::createVBox(score);
902 : }
903 39 : nmb->setTick(mb->tick());
904 39 : nmb->setTicks(mb->ticks());
905 866 : } else if (mb->isFBox()) {
906 0 : nmb = Factory::createFBox(score);
907 0 : nmb->setTick(mb->tick());
908 0 : nmb->setTicks(mb->ticks());
909 866 : } else if (mb->isTBox()) {
910 0 : nmb = Factory::createTBox(score);
911 0 : nmb->setTick(mb->tick());
912 0 : nmb->setTicks(mb->ticks());
913 0 : Text* text = toTBox(mb)->text();
914 0 : EngravingItem* ne = text->linkedClone();
915 0 : ne->setScore(score);
916 0 : nmb->add(ne);
917 866 : } else if (mb->isMeasure()) {
918 866 : const Measure* m = toMeasure(mb);
919 866 : Measure* nm = Factory::createMeasure(score);
920 866 : nmb = nm;
921 866 : nm->setTick(m->tick());
922 866 : nm->setTicks(m->ticks());
923 866 : nm->setTimesig(m->timesig());
924 :
925 866 : nm->setRepeatCount(m->repeatCount());
926 866 : nm->setRepeatStart(m->repeatStart());
927 866 : nm->setRepeatEnd(m->repeatEnd());
928 866 : nm->setRepeatJump(m->repeatJump());
929 :
930 866 : nm->setExcludeFromNumbering(m->excludeFromNumbering());
931 866 : nm->setMeasureNumber(m->measureNumber());
932 866 : nm->setMeasureNumberOffset(m->measureNumberOffset());
933 866 : nm->setBreakMultiMeasureRest(m->breakMultiMeasureRest());
934 :
935 1711 : for (staff_idx_t dstStaffIdx = 0; dstStaffIdx < sourceStavesIndexes.size(); ++dstStaffIdx) {
936 845 : nm->setStaffStemless(dstStaffIdx, m->stemless(sourceStavesIndexes[dstStaffIdx]));
937 : }
938 866 : size_t tracks = oscore->nstaves() * VOICES;
939 :
940 866 : if (sourceStavesIndexes.empty() && trackList.empty()) {
941 32 : tracks = 0;
942 : }
943 :
944 7482 : for (track_idx_t srcTrack = 0; srcTrack < tracks; ++srcTrack) {
945 6616 : TupletMap tupletMap; // tuplets cannot cross measure boundaries
946 :
947 6616 : track_idx_t strack = muse::value(trackList, srcTrack, muse::nidx);
948 :
949 6616 : TremoloTwoChord* prevTremolo = nullptr;
950 26296 : for (Segment* oseg = m->first(); oseg; oseg = oseg->next()) {
951 19680 : Segment* ns = nullptr; //create segment later, on demand
952 20096 : for (EngravingItem* e : oseg->annotations()) {
953 416 : if (e->generated() || e->excludeFromOtherParts()) {
954 0 : continue;
955 : }
956 470 : if ((e->track() == srcTrack && strack != muse::nidx && !e->systemFlag())
957 470 : || (e->systemFlag() && srcTrack == 0 && e->track() == srcTrack)) {
958 40 : EngravingItem* ne = e->linkedClone();
959 40 : processLinkedClone(ne, score, strack);
960 40 : if (!ns) {
961 37 : ns = nm->getSegment(oseg->segmentType(), oseg->tick());
962 : }
963 40 : ns->add(ne);
964 : // for chord symbols,
965 : // re-render with new style settings
966 40 : if (ne->isFretDiagram()) {
967 0 : Harmony* h = toHarmony(toFretDiagram(ne)->harmony());
968 0 : if (h) {
969 0 : processLinkedClone(h, score, strack);
970 : }
971 : }
972 : }
973 : }
974 :
975 : //If track is not mapped skip the following
976 19680 : if (muse::value(trackList, srcTrack, muse::nidx) == muse::nidx) {
977 9548 : continue;
978 : }
979 :
980 : // TimeSig and KeySig announce should never be cloned
981 10228 : if (oseg->isTimeSigAnnounceType() || oseg->isKeySigAnnounceType()) {
982 96 : continue;
983 : }
984 :
985 : //There are probably more destination tracks for the same source
986 10132 : std::vector<track_idx_t> t = muse::values(trackList, srcTrack);
987 :
988 20264 : for (track_idx_t track : t) {
989 : //Clone KeySig TimeSig and Clefs if voice 1 of source staff is not mapped to a track
990 10132 : EngravingItem* oef = oseg->element(trackZeroVoice(srcTrack));
991 9792 : if (oef && !oef->generated() && (oef->isTimeSig() || oef->isKeySig())
992 19924 : && !(trackList.size() == (score->excerpt()->nstaves() * VOICES))) {
993 0 : EngravingItem* ne = oef->linkedClone();
994 0 : ne->setTrack(trackZeroVoice(track));
995 0 : ne->setScore(score);
996 0 : ns = nm->getSegment(oseg->segmentType(), oseg->tick());
997 0 : ns->add(ne);
998 : }
999 :
1000 10132 : EngravingItem* oe = oseg->element(srcTrack);
1001 10132 : bool clone = oe && !oe->generated() && !oe->excludeFromOtherParts();
1002 :
1003 10132 : if (clone) {
1004 : EngravingItem* ne;
1005 1368 : oe->setGenerated(false);
1006 1368 : ne = oe->linkedClone();
1007 1368 : ne->setTrack(track);
1008 :
1009 1368 : if (!(ne->track() % VOICES) && ne->isRest()) {
1010 781 : toRest(ne)->setGap(false);
1011 : }
1012 :
1013 1368 : ne->setScore(score);
1014 1368 : if (oe->isChordRest()) {
1015 1229 : ChordRest* ocr = toChordRest(oe);
1016 1229 : ChordRest* ncr = toChordRest(ne);
1017 :
1018 1229 : if (ocr->beam() && !ocr->beam()->empty() && ocr->beam()->elements().front() == ocr) {
1019 12 : Beam* nb = ocr->beam()->clone();
1020 12 : nb->clear();
1021 12 : nb->setTrack(track);
1022 12 : nb->setScore(score);
1023 12 : nb->add(ncr);
1024 12 : ncr->setBeam(nb);
1025 : }
1026 :
1027 1229 : Tuplet* ot = ocr->tuplet();
1028 :
1029 1229 : if (ot) {
1030 0 : cloneTuplets(ocr, ncr, ot, tupletMap, nm, track);
1031 : }
1032 :
1033 1229 : if (oe->isChord()) {
1034 426 : Chord* och = toChord(ocr);
1035 426 : Chord* nch = toChord(ncr);
1036 426 : addGraceNoteTiesAndBackSpanners(och->graceNotesBefore(), nch, tieMap, score);
1037 426 : size_t n = och->notes().size();
1038 853 : for (size_t i = 0; i < n; ++i) {
1039 427 : Note* on = och->notes().at(i);
1040 427 : Note* nn = toNote(on->findLinkedInStaff(nch->staff()));
1041 427 : if (!nn) {
1042 0 : continue;
1043 : }
1044 :
1045 427 : addTies(on, nn, tieMap, score);
1046 : // add back spanners (going back from end to start spanner element
1047 : // makes sure the 'other' spanner anchor element is already set up)
1048 : // 'on' is the old spanner end note and 'nn' is the new spanner end note
1049 435 : for (Spanner* oldSp : on->spannerBack()) {
1050 16 : if (oldSp->startElement() && oldSp->endElement()
1051 8 : && oldSp->startElement()->findMeasure() == oldSp->endElement()->findMeasure()
1052 16 : && oldSp->startElement()->track() > oldSp->endElement()->track()) {
1053 0 : continue;
1054 : }
1055 8 : Note* newStart = Spanner::startElementFromSpanner(oldSp, nn);
1056 8 : if (newStart != nullptr) {
1057 8 : Spanner* newSp = toSpanner(oldSp->linkedClone());
1058 8 : newSp->setNoteSpan(newStart, nn);
1059 8 : score->addElement(newSp);
1060 : } else {
1061 0 : LOGD("cloneStaves: cannot find spanner start note");
1062 : }
1063 : }
1064 433 : for (Spanner* oldSp : on->spannerFor()) {
1065 12 : if (oldSp->startElement() && oldSp->endElement()
1066 17 : && (oldSp->startElement()->findMeasure() != oldSp->endElement()->findMeasure()
1067 5 : || oldSp->startElement()->track() <= oldSp->endElement()->track())) {
1068 6 : continue;
1069 : }
1070 0 : Note* newEnd = Spanner::endElementFromSpanner(oldSp, nn);
1071 0 : if (newEnd != nullptr) {
1072 0 : Spanner* newSp = toSpanner(oldSp->linkedClone());
1073 0 : newSp->setNoteSpan(nn, newEnd);
1074 0 : score->addElement(newSp);
1075 : } else {
1076 0 : LOGD("cloneStaves: cannot find spanner end note");
1077 : }
1078 : }
1079 : }
1080 426 : addGraceNoteTiesAndBackSpanners(och->graceNotesAfter(), nch, tieMap, score);
1081 426 : addTremoloTwoChord(och, nch, prevTremolo);
1082 : }
1083 : }
1084 1368 : if (!ns) {
1085 1340 : ns = nm->getSegment(oseg->segmentType(), oseg->tick());
1086 : }
1087 1368 : ns->add(ne);
1088 : }
1089 :
1090 10132 : Segment* tst = nm->segments().firstCRSegment();
1091 10132 : if (srcTrack % VOICES && !(track % VOICES) && (!tst || (!tst->element(track)))) {
1092 0 : Segment* segment = nm->getSegment(SegmentType::ChordRest, nm->tick());
1093 0 : Rest* rest = Factory::createRest(segment);
1094 0 : rest->setTicks(nm->ticks());
1095 0 : rest->setDurationType(nm->ticks());
1096 0 : rest->setTrack(track);
1097 0 : segment->add(rest);
1098 : }
1099 : }
1100 10132 : }
1101 6616 : }
1102 : }
1103 :
1104 906 : score->undo(new Link(nmb, mb));
1105 906 : nmb->setExcludeFromOtherParts(false);
1106 :
1107 969 : for (EngravingItem* e : mb->el()) {
1108 63 : if (e->isLayoutBreak()) {
1109 18 : LayoutBreakType st = toLayoutBreak(e)->layoutBreakType();
1110 18 : if (st != LayoutBreakType::SECTION) {
1111 18 : continue;
1112 : }
1113 : }
1114 45 : track_idx_t track = muse::nidx;
1115 45 : if (e->track() != muse::nidx) {
1116 : // try to map track
1117 2 : track = muse::value(trackList, e->track(), muse::nidx);
1118 2 : if (e->systemFlag() && !e->isTopSystemObject()) {
1119 0 : continue;
1120 : }
1121 2 : if (track == muse::nidx) {
1122 : // even if track not in excerpt, we need to clone system elements
1123 1 : if (e->systemFlag() && e->track() == 0) {
1124 0 : track = 0;
1125 1 : } else if (e->isFretDiagram() && toFretDiagram(e)->isInFretBox()) {
1126 0 : track = e->track();
1127 : } else {
1128 1 : continue;
1129 : }
1130 : }
1131 : }
1132 :
1133 : EngravingItem* ne;
1134 : // link text - title, subtitle, also repeats (eg, coda/segno)
1135 : // measure numbers are not stored in this list, but they should not be cloned anyhow
1136 : // layout breaks other than section were skipped above,
1137 : // but section breaks do need to be cloned & linked
1138 : // other measure-attached elements (?) are cloned but not linked
1139 44 : if (e->isText() && toText(e)->textStyleType() == TextStyleType::INSTRUMENT_EXCERPT) {
1140 : // skip part name in score
1141 0 : continue;
1142 44 : } else if (e->isTextBase() || e->isLayoutBreak()) {
1143 43 : ne = e->linkedClone();
1144 43 : ne->setAutoplace(true);
1145 : } else {
1146 1 : ne = e->clone();
1147 : }
1148 44 : ne->setScore(score);
1149 44 : ne->setTrack(track);
1150 44 : nmb->add(ne);
1151 : }
1152 :
1153 906 : return nmb;
1154 : }
1155 :
1156 56 : void Excerpt::cloneMMRests(Score* sourceScore, Score* dstScore, const std::vector<staff_idx_t>& sourceStavesIndexes,
1157 : const TracksMap& trackList, TieMap& tieMap)
1158 : {
1159 : // When MMRests are enabled in the main score and we are creating a new part or revealing a hidden stave within one, we must copy MMRests
1160 : // Elements are moved from underlying measures to MMRest measures. If we do not copy these MMRests, the elements attached to them will be lost
1161 : // It doesn't matter if the MMRest range is different in the part to the score. As long as the MMRest measure is present in the DOM, MMRestLayout will
1162 : // correct this, setting the appropriate length and moving the elements to the correct locations
1163 :
1164 922 : for (Measure* srcM = sourceScore->firstMeasure(); srcM; srcM = srcM->nextMeasure()) {
1165 866 : if (!srcM->hasMMRest()) {
1166 866 : continue;
1167 : }
1168 :
1169 0 : Measure* dstM = dstScore->tick2measure(srcM->tick());
1170 0 : if (!dstM) {
1171 0 : continue;
1172 : }
1173 :
1174 0 : Measure* srcMMRest = srcM->mmRest();
1175 :
1176 0 : Measure* dstMMRest = toMeasure(cloneMeasure(srcMMRest, dstScore, sourceScore, sourceStavesIndexes, trackList, tieMap));
1177 0 : dstMMRest->setMMRestCount(srcMMRest->mmRestCount());
1178 0 : dstMMRest->setPrev(dstM->prev());
1179 0 : dstMMRest->setNext(dstScore->tick2measure(srcMMRest->tick() + srcMMRest->ticks()));
1180 :
1181 0 : dstM->setMMRest(dstMMRest);
1182 : }
1183 56 : }
1184 :
1185 56 : void Excerpt::cloneStaves(Score* sourceScore, Score* dstScore, const std::vector<staff_idx_t>& sourceStavesIndexes,
1186 : const TracksMap& trackList)
1187 : {
1188 56 : MeasureBaseList* measures = dstScore->measures();
1189 56 : TieMap tieMap;
1190 :
1191 962 : for (MeasureBase* mb = sourceScore->measures()->first(); mb; mb = mb->next()) {
1192 906 : if (mb->excludeFromOtherParts()) {
1193 : // if excluded measure contains section break, add it to precedent measure in part
1194 0 : if (LayoutBreak* sectionBreak = mb->sectionBreakElement()) {
1195 0 : if (MeasureBase* prevMB = measures->last()) {
1196 0 : EngravingItem* newSectionBreak = sectionBreak->linkedClone();
1197 0 : newSectionBreak->setScore(dstScore);
1198 0 : newSectionBreak->setOwnershipParent(prevMB);
1199 0 : dstScore->doUndoAddElement(newSectionBreak);
1200 : }
1201 : }
1202 0 : continue;
1203 0 : }
1204 906 : MeasureBase* newMeasure = cloneMeasure(mb, dstScore, sourceScore, sourceStavesIndexes, trackList, tieMap);
1205 906 : measures->append(newMeasure);
1206 : }
1207 :
1208 56 : cloneMMRests(sourceScore, dstScore, sourceStavesIndexes, trackList, tieMap);
1209 :
1210 56 : size_t n = sourceStavesIndexes.size();
1211 114 : for (staff_idx_t dstStaffIdx = 0; dstStaffIdx < n; ++dstStaffIdx) {
1212 58 : Staff* srcStaff = sourceScore->staff(sourceStavesIndexes[dstStaffIdx]);
1213 :
1214 58 : Measure* m = sourceScore->firstMeasure();
1215 58 : Measure* nm = dstScore->firstMeasure();
1216 :
1217 903 : while (m && nm) {
1218 845 : nm->setMeasureRepeatCount(m->measureRepeatCount(srcStaff->idx()), dstStaffIdx);
1219 845 : m = m->nextMeasure();
1220 845 : nm = nm->nextMeasure();
1221 : }
1222 : }
1223 :
1224 56 : if (dstScore->noStaves()) {
1225 1 : return;
1226 : }
1227 :
1228 97 : for (auto i : sourceScore->spanner()) {
1229 42 : Spanner* s = i.second;
1230 42 : if (s->excludeFromOtherParts()) {
1231 0 : continue;
1232 : }
1233 :
1234 42 : track_idx_t dstTrack = muse::nidx;
1235 42 : track_idx_t dstTrack2 = muse::nidx;
1236 :
1237 42 : if (isSystemTextLine(s)) {
1238 : //always export voltas to first staff in part
1239 3 : dstTrack = 0;
1240 3 : dstTrack2 = 0;
1241 3 : cloneSpanner(s, dstScore, dstTrack, dstTrack2);
1242 : } else {
1243 39 : if (muse::value(trackList, s->track(), muse::nidx) == muse::nidx
1244 39 : || muse::value(trackList, s->track2(), muse::nidx) == muse::nidx) {
1245 17 : continue;
1246 : }
1247 22 : std::vector<track_idx_t> track1 = muse::values(trackList, s->track());
1248 22 : std::vector<track_idx_t> track2 = muse::values(trackList, s->track2());
1249 :
1250 22 : if (track1.size() != track2.size()) {
1251 0 : continue;
1252 : }
1253 :
1254 : //export other spanner if staffidx matches
1255 44 : for (auto it1 = track1.begin(), it2 = track2.begin(); it1 != track1.end(); ++it1, ++it2) {
1256 22 : dstTrack = *it1;
1257 22 : dstTrack2 = *it2;
1258 22 : cloneSpanner(s, dstScore, dstTrack, dstTrack2);
1259 : }
1260 22 : }
1261 : }
1262 55 : collectTieEndPoints(tieMap);
1263 56 : }
1264 :
1265 0 : void Excerpt::cloneMeasures(Score* oscore, Score* score)
1266 : {
1267 0 : MeasureBaseList* measures = score->measures();
1268 0 : TieMap tieMap;
1269 :
1270 0 : for (MeasureBase* mb = oscore->firstMeasure(); mb; mb = mb->next()) {
1271 0 : MeasureBase* newMeasure = cloneMeasure(mb, score, oscore, {}, {}, tieMap);
1272 0 : measures->append(newMeasure);
1273 : }
1274 :
1275 0 : cloneMMRests(oscore, score, {}, {}, tieMap);
1276 :
1277 0 : collectTieEndPoints(tieMap);
1278 0 : }
1279 :
1280 102 : void Excerpt::linkMeasures(Score* excerptScore, Score* masterScore)
1281 : {
1282 102 : MeasureBase* mbMaster = masterScore->first();
1283 2741 : for (MeasureBase* mb = excerptScore->first(); mb; mb = mb->next()) {
1284 2639 : if (!mb->isMeasure()) {
1285 93 : continue;
1286 : }
1287 2638 : while (mbMaster && !mbMaster->isMeasure()) {
1288 92 : mbMaster = mbMaster->next();
1289 : }
1290 2546 : if (!mbMaster) {
1291 0 : LOGD("Measures in MasterScore and Score are not in sync.");
1292 0 : break;
1293 : }
1294 2546 : mb->linkTo(mbMaster);
1295 2546 : mbMaster = mbMaster->next();
1296 : }
1297 102 : }
1298 :
1299 : //! NOTE For staves in the same score
1300 86 : void Excerpt::cloneStaff(Staff* srcStaff, Staff* dstStaff, bool cloneSpanners)
1301 : {
1302 86 : Score* score = srcStaff->score();
1303 86 : TieMap tieMap;
1304 :
1305 86 : score->undo(new Link(dstStaff, srcStaff));
1306 :
1307 86 : staff_idx_t srcStaffIdx = srcStaff->idx();
1308 86 : staff_idx_t dstStaffIdx = dstStaff->idx();
1309 :
1310 273 : for (Measure* m = score->firstMeasure(); m; m = m->nextMeasure()) {
1311 187 : m->setMeasureRepeatCount(m->measureRepeatCount(srcStaffIdx), dstStaffIdx);
1312 :
1313 187 : track_idx_t sTrack = srcStaffIdx * VOICES;
1314 187 : track_idx_t eTrack = sTrack + VOICES;
1315 935 : for (track_idx_t srcTrack = sTrack; srcTrack < eTrack; ++srcTrack) {
1316 748 : TupletMap tupletMap; // tuplets cannot cross measure boundaries
1317 748 : track_idx_t dstTrack = dstStaffIdx * VOICES + (srcTrack - sTrack);
1318 748 : TremoloTwoChord* prevTremolo = nullptr;
1319 3956 : for (Segment* seg = m->first(); seg; seg = seg->next()) {
1320 3208 : EngravingItem* oe = seg->element(srcTrack);
1321 3208 : if (oe == 0 || oe->generated()) {
1322 2427 : continue;
1323 : }
1324 781 : if (oe->isTimeSig()) {
1325 85 : continue;
1326 : }
1327 696 : EngravingItem* ne = 0;
1328 696 : if (oe->isClef()) {
1329 : // only clone clef if it matches staff group and does not exists yet
1330 0 : Clef* clef = toClef(oe);
1331 0 : Fraction tick = seg->tick();
1332 0 : if (ClefInfo::staffGroup(clef->concertClef()) == dstStaff->constStaffType(Fraction(0, 1))->group()
1333 0 : && dstStaff->clefType(tick) != clef->clefTypeList()) {
1334 0 : ne = oe->clone();
1335 : }
1336 : } else {
1337 696 : ne = oe->linkedClone();
1338 : }
1339 696 : if (ne) {
1340 696 : ne->setTrack(dstTrack);
1341 696 : ne->setOwnershipParent(seg);
1342 696 : ne->setScore(score);
1343 696 : if (ne->isChordRest()) {
1344 468 : ChordRest* ncr = toChordRest(ne);
1345 468 : if (ncr->tuplet()) {
1346 0 : ncr->setTuplet(0); //TODO nested tuplets
1347 : }
1348 : }
1349 696 : score->undoAddElement(ne);
1350 : }
1351 696 : if (oe->isChordRest()) {
1352 468 : ChordRest* ocr = toChordRest(oe);
1353 468 : ChordRest* ncr = toChordRest(ne);
1354 468 : Tuplet* ot = ocr->tuplet();
1355 468 : if (ot) {
1356 28 : cloneTuplets(ocr, ncr, ot, tupletMap, m, dstTrack);
1357 : }
1358 :
1359 : // remove lyrics from chord
1360 : // since only one set of lyrics is used with linked staves
1361 468 : for (Lyrics* l : ncr->lyrics()) {
1362 0 : if (l) {
1363 0 : l->unlink();
1364 : }
1365 : }
1366 468 : muse::DeleteAll(ncr->lyrics());
1367 468 : ncr->lyrics().clear();
1368 :
1369 468 : ncr->checkStaffMoveValidity();
1370 : // creating copy for iteration, cause seg->annotations() may change during loop
1371 468 : const std::vector<EngravingItem*> iterableAnnotations = seg->annotations();
1372 :
1373 649 : for (EngravingItem* e : iterableAnnotations) {
1374 181 : if (!e) {
1375 0 : LOGD("cloneStaff: corrupted annotation found.");
1376 0 : continue;
1377 : }
1378 181 : if (e->generated() || e->systemFlag()) {
1379 83 : continue;
1380 : }
1381 98 : if (e->track() != srcTrack) {
1382 36 : continue;
1383 : }
1384 62 : switch (e->type()) {
1385 : // exclude certain element types
1386 : // this should be same list excluded in Score::undoAddElement()
1387 60 : case ElementType::STAFF_TEXT:
1388 : case ElementType::SYSTEM_TEXT:
1389 : case ElementType::TRIPLET_FEEL:
1390 : case ElementType::PLAYTECH_ANNOTATION:
1391 : case ElementType::STRING_TUNINGS:
1392 : case ElementType::FRET_DIAGRAM:
1393 : case ElementType::HARMONY:
1394 : case ElementType::FIGURED_BASS:
1395 : case ElementType::DYNAMIC:
1396 : case ElementType::INSTRUMENT_CHANGE:
1397 : case ElementType::LYRICS: // not normally segment-attached
1398 60 : continue;
1399 2 : case ElementType::CAPO: {
1400 2 : const Capo* capo = toCapo(e);
1401 2 : dstStaff->insertCapoParams(e->tick(), capo->params(), true);
1402 2 : continue;
1403 2 : }
1404 0 : case ElementType::FERMATA:
1405 : {
1406 : // Fermatas are special since the belong to a segment but should
1407 : // be created and linked on each staff.
1408 0 : EngravingItem* ne1 = e->linkedClone();
1409 0 : ne1->setTrack(dstTrack);
1410 0 : ne1->setOwnershipParent(seg);
1411 0 : ne1->setScore(score);
1412 0 : score->doUndoAddElement(ne1);
1413 0 : continue;
1414 0 : }
1415 0 : default:
1416 0 : EngravingItem* ne1 = e->clone();
1417 0 : ne1->setTrack(dstTrack);
1418 0 : ne1->setOwnershipParent(seg);
1419 0 : ne1->setScore(score);
1420 0 : score->undoAddElement(ne1);
1421 60 : }
1422 : }
1423 468 : if (oe->isChord()) {
1424 420 : Chord* och = toChord(ocr);
1425 420 : Chord* nch = toChord(ncr);
1426 420 : addGraceNoteTiesAndBackSpanners(och->graceNotesBefore(), nch, tieMap, score);
1427 420 : size_t n = och->notes().size();
1428 879 : for (size_t i = 0; i < n; ++i) {
1429 459 : Note* on = och->notes().at(i);
1430 459 : Note* nn = toNote(on->findLinkedInStaff(dstStaff));
1431 459 : if (!nn) {
1432 0 : continue;
1433 : }
1434 459 : addTies(on, nn, tieMap, score);
1435 : // add back spanners (going back from end to start spanner element
1436 : // makes sure the 'other' spanner anchor element is already set up)
1437 : // 'on' is the old spanner end note and 'nn' is the new spanner end note
1438 461 : for (Spanner* oldSp : on->spannerBack()) {
1439 2 : Note* newStart = Spanner::startElementFromSpanner(oldSp, nn);
1440 2 : if (newStart != nullptr) {
1441 2 : Spanner* newSp = toSpanner(oldSp->linkedClone());
1442 2 : newSp->setNoteSpan(newStart, nn);
1443 2 : score->addElement(newSp);
1444 : } else {
1445 0 : LOGD("cloneStaff: cannot find spanner start note");
1446 : }
1447 : }
1448 : }
1449 420 : addGraceNoteTiesAndBackSpanners(och->graceNotesAfter(), nch, tieMap, score);
1450 420 : addTremoloTwoChord(och, nch, prevTremolo);
1451 :
1452 : // Check grace note staff move validity
1453 435 : for (Chord* gn : nch->graceNotes()) {
1454 15 : gn->checkStaffMoveValidity();
1455 : }
1456 420 : if (Ornament* o = nch->findOrnament()) {
1457 0 : Chord* cueChord = o->cueNoteChord();
1458 0 : if (cueChord) {
1459 0 : cueChord->checkStaffMoveValidity();
1460 : }
1461 : }
1462 : }
1463 468 : }
1464 : }
1465 748 : }
1466 : }
1467 :
1468 86 : if (cloneSpanners) {
1469 114 : for (auto i : score->spanner()) {
1470 28 : Spanner* s = i.second;
1471 28 : staff_idx_t staffIdx = s->staffIdx();
1472 28 : track_idx_t dstTrack = muse::nidx;
1473 28 : track_idx_t dstTrack2 = muse::nidx;
1474 28 : if (!isSystemTextLine(s)) {
1475 : //export other spanner if staffidx matches
1476 24 : if (srcStaffIdx == staffIdx) {
1477 12 : dstTrack = dstStaffIdx * VOICES + s->voice();
1478 12 : dstTrack2 = dstStaffIdx * VOICES + (s->track2() % VOICES);
1479 : }
1480 : }
1481 28 : if (dstTrack == muse::nidx) {
1482 16 : continue;
1483 : }
1484 12 : cloneSpanner(s, score, dstTrack, dstTrack2);
1485 : }
1486 : }
1487 :
1488 86 : collectTieEndPoints(tieMap);
1489 86 : }
1490 :
1491 : //! NOTE For staves potentially in different scores
1492 11 : void Excerpt::cloneStaff2(Staff* srcStaff, Staff* dstStaff, const Fraction& startTick, const Fraction& endTick)
1493 : {
1494 11 : Score* oscore = srcStaff->score();
1495 11 : Score* score = dstStaff->score();
1496 :
1497 11 : Transaction& tx = score->transactionManager()->currentOrDummyTransaction();
1498 :
1499 11 : Excerpt* oex = oscore->excerpt();
1500 11 : Excerpt* ex = score->excerpt();
1501 11 : TracksMap otracks, tracks;
1502 11 : if (oex) {
1503 0 : otracks = oex->tracksMapping();
1504 : }
1505 11 : if (ex) {
1506 11 : tracks = ex->tracksMapping();
1507 : }
1508 :
1509 11 : Measure* m1 = oscore->tick2measure(startTick);
1510 11 : Measure* m2 = oscore->tick2measure(endTick);
1511 :
1512 11 : if (m2->tick() < endTick) { // end of score
1513 9 : m2 = 0;
1514 : }
1515 :
1516 11 : TieMap tieMap;
1517 :
1518 11 : staff_idx_t srcStaffIdx = srcStaff->idx();
1519 11 : staff_idx_t dstStaffIdx = dstStaff->idx();
1520 :
1521 11 : track_idx_t sTrack = srcStaffIdx * VOICES;
1522 11 : track_idx_t eTrack = sTrack + VOICES;
1523 :
1524 11 : std::map<track_idx_t, track_idx_t> map;
1525 55 : for (track_idx_t i = sTrack; i < eTrack; i++) {
1526 44 : if (!oex && !ex) {
1527 0 : map.insert({ i, dstStaffIdx * VOICES + i % VOICES });
1528 44 : } else if (oex && !ex) {
1529 0 : track_idx_t k = muse::key(otracks, i, muse::nidx);
1530 0 : if (k != muse::nidx) {
1531 0 : map.insert({ i, k });
1532 : }
1533 44 : } else if (!oex && ex) {
1534 44 : for (track_idx_t j : muse::values(tracks, i)) {
1535 29 : if (dstStaffIdx * VOICES <= j && j < (dstStaffIdx + 1) * VOICES) {
1536 29 : map.insert({ i, j });
1537 29 : break;
1538 : }
1539 44 : }
1540 44 : } else if (oex && ex) {
1541 0 : track_idx_t k = muse::key(otracks, i, muse::nidx);
1542 0 : if (k != muse::nidx) {
1543 0 : for (track_idx_t j : muse::values(tracks, k)) {
1544 0 : if (dstStaffIdx * VOICES <= j && j < (dstStaffIdx + 1) * VOICES) {
1545 0 : map.insert({ i, j });
1546 0 : break;
1547 : }
1548 0 : }
1549 : }
1550 : }
1551 : }
1552 :
1553 11 : if (map.empty()) {
1554 0 : for (track_idx_t i = sTrack; i < eTrack; i++) {
1555 0 : map.insert({ i, dstStaffIdx * VOICES + i % VOICES });
1556 : }
1557 : }
1558 :
1559 11 : bool firstVoiceVisible = dstStaff->isVoiceVisible(0);
1560 :
1561 11 : const bool oscoreConcertPitch = oscore->style().styleB(Sid::concertPitch);
1562 11 : const bool scoreConcertPitch = score->style().styleB(Sid::concertPitch);
1563 11 : const bool needsTransposition = oscoreConcertPitch != scoreConcertPitch;
1564 :
1565 249 : auto addElement = [score](EngravingItem* element) {
1566 249 : score->undoAddElement(element, false /*addToLinkedStaves*/);
1567 260 : };
1568 :
1569 11 : Selection& sel = score->selection();
1570 11 : Fraction rangeStart;
1571 11 : Fraction rangeEnd;
1572 11 : if (sel.isRange()) {
1573 : // The for-loop below will invalidate the current range start/end segments. Save their
1574 : // ticks and restore them later using tick2measure (see below)...
1575 0 : rangeStart = sel.tickStart();
1576 0 : rangeEnd = sel.tickEnd();
1577 : }
1578 :
1579 66 : for (Measure* m = m1; m && (m != m2); m = m->nextMeasure()) {
1580 55 : Measure* nm = score->tick2measure(m->tick());
1581 55 : nm->setMeasureRepeatCount(m->measureRepeatCount(srcStaffIdx), dstStaffIdx);
1582 :
1583 55 : for (EngravingItem* oldEl : m->el()) {
1584 0 : if (oldEl->isLayoutBreak()) {
1585 0 : continue;
1586 : }
1587 0 : if ((oldEl->systemFlag() && dstStaffIdx != 0) || (!oldEl->systemFlag() && oldEl->staffIdx() != srcStaffIdx)) {
1588 0 : continue;
1589 : }
1590 0 : bool alreadyCloned = oldEl->systemFlag() && oldEl->findLinkedInScore(score);
1591 0 : bool cloneElement = !alreadyCloned && oldEl->elementAppliesToTrack(staff2track(dstStaffIdx));
1592 0 : if (!cloneElement) {
1593 0 : continue;
1594 : }
1595 0 : EngravingItem* newEl = oldEl->linkedClone();
1596 0 : newEl->setOwnershipParent(nm);
1597 0 : newEl->setStaffIdx(oldEl->systemFlag() ? 0 : dstStaffIdx);
1598 0 : newEl->setScore(score);
1599 0 : newEl->styleChanged();
1600 0 : addElement(newEl);
1601 : }
1602 :
1603 55 : TremoloTwoChord* prevTremolo = nullptr;
1604 182 : for (const auto& [srcTrack, dstTrack] : map) {
1605 127 : TupletMap tupletMap; // tuplets cannot cross measure boundaries
1606 858 : for (Segment* oseg = m->first(); oseg; oseg = oseg->next()) {
1607 731 : Segment* ns = nm->getSegment(oseg->segmentType(), oseg->tick());
1608 731 : EngravingItem* oef = oseg->element(trackZeroVoice(srcTrack));
1609 731 : if (oef && !oef->generated() && (oef->isTimeSig() || oef->isKeySig())) {
1610 90 : if (!firstVoiceVisible) {
1611 54 : EngravingItem* ne = oef->linkedClone();
1612 54 : ne->setTrack(trackZeroVoice(dstTrack));
1613 54 : ne->setOwnershipParent(ns);
1614 54 : ne->setScore(score);
1615 54 : ne->styleChanged();
1616 108 : ne->scanElements([&](EngravingItem* newElement) { updateSpatium(oef, newElement); });
1617 54 : addElement(ne);
1618 : }
1619 : }
1620 :
1621 761 : for (EngravingItem* e : oseg->annotations()) {
1622 30 : if (e->generated() || e->excludeFromOtherParts()) {
1623 0 : continue;
1624 : }
1625 30 : bool systemObject = e->systemFlag() && e->track() == 0;
1626 30 : EngravingItem* linkedElement = e->findLinkedInScore(score);
1627 30 : Segment* linkedParent = linkedElement ? toSegment(linkedElement->parent()) : nullptr;
1628 30 : bool alreadyCloned = linkedParent && (linkedParent == ns
1629 0 : || (linkedParent->isType(SegmentType::Duration)
1630 0 : && ns->isType(SegmentType::Duration)
1631 0 : && linkedParent->tick() == ns->tick()));
1632 30 : bool cloneAnnotation = !alreadyCloned && (e->elementAppliesToTrack(srcTrack) || systemObject);
1633 :
1634 30 : if (!cloneAnnotation) {
1635 29 : continue;
1636 : }
1637 1 : EngravingItem* ne1 = e->linkedClone();
1638 1 : ne1->setTrack(dstTrack);
1639 1 : ne1->setOwnershipParent(ns);
1640 1 : ne1->setScore(score);
1641 1 : ne1->styleChanged();
1642 1 : addElement(ne1);
1643 :
1644 1 : if (e->isHarmony() && needsTransposition) {
1645 0 : transposeHarmony(toHarmony(ne1), srcStaff, scoreConcertPitch);
1646 : }
1647 : }
1648 :
1649 731 : EngravingItem* oe = oseg->element(srcTrack);
1650 731 : if (oe == 0 || oe->generated() || oe->excludeFromOtherParts()) {
1651 590 : continue;
1652 : }
1653 :
1654 194 : oe->setGenerated(false);
1655 194 : EngravingItem* ne = oe->linkedClone();
1656 194 : ne->setTrack(dstTrack);
1657 194 : ne->setOwnershipParent(ns);
1658 194 : ne->setScore(score);
1659 194 : ne->styleChanged();
1660 542 : ne->scanElements([&](EngravingItem* newElement) { updateSpatium(oe, newElement); });
1661 194 : addElement(ne);
1662 :
1663 194 : if (!oe->isChordRest()) {
1664 17 : continue;
1665 : }
1666 :
1667 177 : ChordRest* ocr = toChordRest(oe);
1668 177 : ChordRest* ncr = toChordRest(ne);
1669 177 : Tuplet* ot = ocr->tuplet();
1670 177 : if (ot) {
1671 0 : cloneTuplets(ocr, ncr, ot, tupletMap, nm, dstTrack);
1672 : }
1673 :
1674 177 : if (!oe->isChord()) {
1675 36 : continue;
1676 : }
1677 :
1678 141 : Chord* och = toChord(ocr);
1679 141 : Chord* nch = toChord(ncr);
1680 141 : addGraceNoteTiesAndBackSpanners(och->graceNotesBefore(), nch, tieMap, score);
1681 141 : size_t n = och->notes().size();
1682 282 : for (size_t i = 0; i < n; ++i) {
1683 141 : Note* on = och->notes().at(i);
1684 141 : Note* nn = toNote(on->findLinkedInStaff(dstStaff));
1685 141 : if (!nn) {
1686 0 : continue;
1687 : }
1688 141 : addTies(on, nn, tieMap, score);
1689 141 : addBackSpanners(on, nn, score);
1690 141 : GuitarBend* bendBack = on->bendBack();
1691 141 : Note* newStartNote = bendBack ? toNote(bendBack->startNote()->findLinkedInStaff(dstStaff)) : nullptr;
1692 141 : if (bendBack && newStartNote) {
1693 0 : GuitarBend* newBend = toGuitarBend(bendBack->linkedClone());
1694 0 : newBend->setScore(score);
1695 0 : newBend->setOwnershipParent(newStartNote);
1696 0 : newBend->setTrack(newStartNote->track());
1697 0 : newBend->setTrack2(nn->track());
1698 0 : newBend->setStartElement(newStartNote);
1699 0 : newBend->setEndElement(nn);
1700 0 : newStartNote->addSpannerFor(newBend);
1701 0 : nn->addSpannerBack(newBend);
1702 : }
1703 141 : GuitarBend* bendFor = on->bendFor();
1704 141 : if (bendFor && bendFor->bendType() == GuitarBendType::SLIGHT_BEND) {
1705 : // Because slight bends aren't detected as "bendBack"
1706 0 : GuitarBend* newBend = toGuitarBend(bendFor->linkedClone());
1707 0 : newBend->setScore(score);
1708 0 : newBend->setOwnershipParent(nn);
1709 0 : newBend->setTrack(nn->track());
1710 0 : newBend->setTrack2(nn->track());
1711 0 : newBend->setStartElement(nn);
1712 0 : newBend->setEndElement(nn);
1713 0 : nn->addSpannerFor(newBend);
1714 : }
1715 : }
1716 141 : addGraceNoteTiesAndBackSpanners(och->graceNotesAfter(), nch, tieMap, score);
1717 141 : addTremoloTwoChord(och, nch, prevTremolo);
1718 : }
1719 127 : }
1720 55 : std::vector<Segment*> emptySegments;
1721 384 : for (Segment& seg : nm->segments()) {
1722 329 : seg.checkEmpty();
1723 329 : if (seg.empty()) {
1724 122 : emptySegments.push_back(&seg);
1725 : }
1726 : }
1727 177 : for (Segment* seg : emptySegments) {
1728 122 : nm->remove(seg);
1729 122 : delete seg;
1730 : }
1731 55 : if (!nm->hasVoices(dstStaffIdx, nm->tick(), nm->ticks())) {
1732 24 : promoteGapRestsToRealRests(nm, dstStaffIdx);
1733 : }
1734 55 : }
1735 :
1736 36 : for (auto i : oscore->spanner()) {
1737 25 : Spanner* s = i.second;
1738 25 : if (!(s->tick() >= startTick && s->tick2() <= endTick) || s->excludeFromOtherParts()) {
1739 4 : continue;
1740 : }
1741 :
1742 21 : staff_idx_t staffIdx = s->staffIdx();
1743 21 : track_idx_t dstTrack = muse::nidx;
1744 21 : track_idx_t dstTrack2 = muse::nidx;
1745 :
1746 21 : if (isSystemTextLine(s)) {
1747 0 : if (!scoreContainsSpanner(score, s)) {
1748 0 : dstTrack = s->track();
1749 0 : dstTrack2 = s->track2();
1750 : }
1751 : } else {
1752 : // export other spanner if staffidx matches
1753 21 : if (srcStaffIdx == staffIdx) {
1754 8 : if (muse::contains(map, s->track())) {
1755 6 : dstTrack = map.at(s->track());
1756 : }
1757 8 : if (muse::contains(map, s->track2())) {
1758 6 : dstTrack2 = map.at(s->track2());
1759 : }
1760 : }
1761 : }
1762 :
1763 21 : if (dstTrack == muse::nidx) {
1764 15 : continue;
1765 : }
1766 :
1767 6 : cloneSpanner(s, score, dstTrack, dstTrack2);
1768 : }
1769 :
1770 11 : if (needsTransposition) {
1771 0 : Interval interval = srcStaff->part()->instrument()->transpose();
1772 0 : if (!interval.isZero() || srcStaff->part()->instruments().size() != 1) {
1773 0 : if (!scoreConcertPitch) {
1774 0 : interval.flip();
1775 : }
1776 :
1777 0 : Transpose::transposeKeys(tx, score, dstStaffIdx, dstStaffIdx + 1, startTick, endTick, !scoreConcertPitch);
1778 : }
1779 : }
1780 :
1781 11 : collectTieEndPoints(tieMap);
1782 :
1783 : // Restore the range selection...
1784 11 : if (rangeStart.isValid()) {
1785 11 : Segment* newStart = score->tick2segment(rangeStart);
1786 11 : sel.setStartSegment(newStart);
1787 : }
1788 :
1789 11 : if (rangeEnd.isValid()) {
1790 11 : Segment* newEnd = score->tick2segment(rangeEnd);
1791 11 : sel.setEndSegment(newEnd);
1792 : }
1793 11 : }
1794 :
1795 24 : void Excerpt::promoteGapRestsToRealRests(const Measure* measure, staff_idx_t staffIdx)
1796 : {
1797 24 : track_idx_t startTrack = staff2track(staffIdx);
1798 24 : track_idx_t endTrack = startTrack + VOICES;
1799 107 : for (const Segment& seg : measure->segments()) {
1800 83 : if (!seg.isChordRestType()) {
1801 27 : continue;
1802 : }
1803 280 : for (track_idx_t track = startTrack; track < endTrack; ++track) {
1804 224 : EngravingItem* item = seg.element(track);
1805 224 : if (item && item->isRest() && toRest(item)->isGap()) {
1806 0 : toRest(item)->setGap(false);
1807 : }
1808 : }
1809 : }
1810 24 : }
1811 :
1812 0 : std::vector<Excerpt*> Excerpt::createExcerptsFromParts(const std::vector<Part*>& parts, MasterScore* score)
1813 : {
1814 0 : StringList allExcerptLowerNames;
1815 0 : allExcerptLowerNames.reserve(score->excerpts().size() + parts.size());
1816 0 : for (const Excerpt* e : score->excerpts()) {
1817 0 : allExcerptLowerNames.push_back(e->name().toLower());
1818 : }
1819 :
1820 0 : std::vector<Excerpt*> result;
1821 0 : result.reserve(parts.size());
1822 :
1823 0 : for (Part* part : parts) {
1824 0 : Excerpt* excerpt = new Excerpt(score);
1825 0 : excerpt->parts().push_back(part);
1826 :
1827 0 : const TrackRange range = part->trackRange();
1828 :
1829 0 : for (track_idx_t i = range.startTrack, j = 0; i < range.endTrack; ++i, ++j) {
1830 0 : excerpt->m_tracksMapping.insert({ i, j });
1831 : }
1832 :
1833 0 : String name = formatUniqueExcerptName(part->partName(), allExcerptLowerNames);
1834 0 : excerpt->setName(name);
1835 0 : excerpt->setInitialPartId(part->id());
1836 :
1837 0 : allExcerptLowerNames.push_back(name.toLower());
1838 0 : result.push_back(excerpt);
1839 0 : }
1840 :
1841 0 : return result;
1842 0 : }
1843 :
1844 : //---------------------------------------------------------
1845 : // createLinkedTabs
1846 : //---------------------------------------------------------
1847 :
1848 0 : void Excerpt::createLinkedTabs(MasterScore* score)
1849 : {
1850 : // store map of all initial spanners
1851 0 : std::unordered_map<staff_idx_t, std::vector<Spanner*> > spanners;
1852 : // for moving initial spanner to new index
1853 0 : std::unordered_map<staff_idx_t, staff_idx_t> indexMapping;
1854 0 : std::set<staff_idx_t> staffIndexesToCopy;
1855 0 : constexpr size_t stavesInPart = 2;
1856 :
1857 0 : for (auto it = score->spanner().cbegin(); it != score->spanner().cend(); ++it) {
1858 0 : Spanner* s = it->second;
1859 0 : spanners[s->staffIdx()].push_back(s);
1860 : }
1861 :
1862 0 : size_t curStaffIdx = 0;
1863 0 : size_t stavesOperated = 0;
1864 :
1865 : // creating linked staves and recalculating spanners indexes
1866 0 : for (size_t partNum = 0; partNum < score->parts().size(); partNum++) {
1867 0 : Part* part = score->parts()[partNum];
1868 0 : Fraction fr = Fraction(0, 1);
1869 0 : size_t lines = part->instrument()->stringData()->strings();
1870 0 : size_t stavesNum = part->nstaves();
1871 :
1872 0 : if (stavesNum != 1) {
1873 0 : for (size_t i = 0; i < stavesNum; i++) {
1874 0 : indexMapping[curStaffIdx] = stavesOperated + i;
1875 0 : curStaffIdx++;
1876 : }
1877 :
1878 0 : stavesOperated += stavesNum;
1879 0 : continue;
1880 0 : }
1881 :
1882 0 : bool needsTabStaff = !part->staff(0)->isDrumStaff(fr);
1883 :
1884 0 : if (needsTabStaff) {
1885 0 : part->setStaves(static_cast<int>(stavesInPart));
1886 :
1887 0 : Staff* srcStaff = part->staff(0);
1888 0 : Staff* dstStaff = part->staff(1);
1889 0 : cloneStaff(srcStaff, dstStaff, false);
1890 :
1891 : static const std::vector<StaffTypes> types {
1892 : StaffTypes::TAB_4SIMPLE,
1893 : StaffTypes::TAB_5SIMPLE,
1894 : StaffTypes::TAB_6SIMPLE,
1895 : StaffTypes::TAB_7SIMPLE,
1896 : StaffTypes::TAB_8SIMPLE,
1897 : StaffTypes::TAB_9SIMPLE,
1898 : StaffTypes::TAB_10SIMPLE,
1899 0 : };
1900 :
1901 0 : size_t index = (lines >= 4 && lines <= 10) ? lines - 4 : 2;
1902 :
1903 0 : dstStaff->setStaffType(fr, *StaffType::preset(types.at(index)));
1904 0 : dstStaff->setLines(fr, static_cast<int>(lines));
1905 :
1906 0 : staffIndexesToCopy.insert(curStaffIdx);
1907 : }
1908 :
1909 : // each spanner moves down to the staff with index,
1910 : // equal to number of spanners operated before it
1911 0 : indexMapping[curStaffIdx] = stavesOperated;
1912 0 : curStaffIdx++;
1913 :
1914 0 : stavesOperated += needsTabStaff ? stavesInPart : 1;
1915 : }
1916 :
1917 : // moving and copying spanner segments
1918 0 : for (auto& spannerMapElem : spanners) {
1919 0 : auto& spannerList = spannerMapElem.second;
1920 0 : staff_idx_t idx = spannerMapElem.first;
1921 0 : bool needsCopy = staffIndexesToCopy.find(idx) != staffIndexesToCopy.end();
1922 0 : for (Spanner* s : spannerList) {
1923 : /// moving
1924 0 : staff_idx_t newIdx = indexMapping[idx];
1925 0 : track_idx_t newTrackIdx = staff2track(newIdx);
1926 0 : s->setTrack(newTrackIdx);
1927 0 : s->setTrack2(newTrackIdx);
1928 0 : for (SpannerSegment* ss : s->spannerSegments()) {
1929 0 : ss->setTrack(newTrackIdx);
1930 : }
1931 :
1932 : /// copying
1933 0 : if (needsCopy) {
1934 0 : staff_idx_t dstStaffIdx = newIdx + 1;
1935 :
1936 0 : track_idx_t dstTrack = dstStaffIdx * VOICES + s->voice();
1937 0 : track_idx_t dstTrack2 = dstStaffIdx * VOICES + (s->track2() % VOICES);
1938 :
1939 0 : cloneSpanner(s, score, dstTrack, dstTrack2);
1940 : }
1941 : }
1942 : }
1943 0 : }
|