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

Generated by: LCOV version 2.0-1