LCOV - code coverage report
Current view: top level - src/engraving/dom - excerpt.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 66.6 % 1157 771
Test Date: 2026-11-05 04:07:02

            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 : }
        

Generated by: LCOV version 2.0-1