LCOV - code coverage report
Current view: top level - src/notation/internal - masternotationparts.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 184 0
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 "masternotationparts.h"
      24              : 
      25              : #include "engraving/dom/masterscore.h"
      26              : #include "engraving/dom/scoreorder.h"
      27              : #include "engraving/dom/excerpt.h"
      28              : #include "engraving/dom/utils.h"
      29              : #include "engraving/editing/editexcerpt.h"
      30              : #include "engraving/editing/transaction/transaction.h"
      31              : #include "engraving/editing/transpose.h"
      32              : 
      33              : #include "inotation.h"
      34              : #include "iexcerptnotation.h" // IWYU pragma: keep
      35              : 
      36              : #include "log.h"
      37              : 
      38              : using namespace muse;
      39              : using namespace mu::notation;
      40              : 
      41            0 : static NotationParts* get_impl(const INotationPartsPtr& parts)
      42              : {
      43            0 :     return static_cast<NotationParts*>(parts.get());
      44              : }
      45              : 
      46            0 : MasterNotationParts::MasterNotationParts(IGetScore* getScore, INotationInteractionPtr interaction, INotationUndoStackPtr undoStack,
      47            0 :                                          INotationStylePtr style)
      48            0 :     : NotationParts(getScore, interaction, undoStack, style)
      49              : {
      50            0 : }
      51              : 
      52            0 : void MasterNotationParts::setExcerpts(ExcerptNotationList excerpts)
      53              : {
      54            0 :     m_excerpts = excerpts;
      55            0 : }
      56              : 
      57            0 : void MasterNotationParts::startGlobalEdit(const muse::TranslatableString& actionName)
      58              : {
      59            0 :     NotationParts::startEdit(actionName);
      60            0 :     undoStack()->lock();
      61            0 : }
      62              : 
      63            0 : void MasterNotationParts::endGlobalEdit()
      64              : {
      65            0 :     undoStack()->unlock();
      66            0 :     NotationParts::apply();
      67            0 : }
      68              : 
      69            0 : void MasterNotationParts::setParts(const PartInstrumentList& partList, const ScoreOrder& order)
      70              : {
      71            0 :     TRACEFUNC;
      72              : 
      73            0 :     mu::engraving::KeyList keyList = score()->keyList();
      74              : 
      75            0 :     endInteractionWithScore();
      76            0 :     startGlobalEdit(TranslatableString("undoableAction", "Add/remove instruments"));
      77              : 
      78            0 :     doSetScoreOrder(order);
      79            0 :     removeMissingParts(partList);
      80            0 :     insertNewParts(partList, keyList);
      81            0 :     updateSoloist(partList);
      82            0 :     sortParts(partList);
      83            0 :     setBracketsAndBarlines();
      84              : 
      85            0 :     for (const INotationPartsPtr& excerptParts : excerptsParts()) {
      86            0 :         auto impl = get_impl(excerptParts);
      87              : 
      88            0 :         impl->removeMissingParts(partList);
      89              : 
      90            0 :         PartInstrumentList excerptPartList;
      91            0 :         for (mu::engraving::Part* part: impl->score()->parts()) {
      92            0 :             PartInstrument pi;
      93            0 :             pi.isExistingPart = true;
      94            0 :             pi.partId = part->id();
      95            0 :             excerptPartList << pi;
      96            0 :         }
      97              : 
      98            0 :         impl->sortParts(excerptPartList);
      99              : 
     100            0 :         impl->setBracketsAndBarlines();
     101            0 :     }
     102              : 
     103            0 :     updatePartsAndSystemObjectStaves();
     104            0 :     endGlobalEdit();
     105            0 : }
     106              : 
     107            0 : void MasterNotationParts::removeParts(const IDList& partsIds)
     108              : {
     109            0 :     TRACEFUNC;
     110              : 
     111            0 :     startGlobalEdit(TranslatableString("undoableAction", "Remove instruments"));
     112              : 
     113            0 :     NotationParts::removeParts(partsIds);
     114              : 
     115            0 :     for (const INotationPartsPtr& parts : excerptsParts()) {
     116            0 :         parts->removeParts(partsIds);
     117            0 :     }
     118              : 
     119            0 :     endGlobalEdit();
     120            0 : }
     121              : 
     122            0 : void MasterNotationParts::removeStaves(const IDList& stavesIds)
     123              : {
     124            0 :     TRACEFUNC;
     125              : 
     126            0 :     startGlobalEdit(TranslatableString("undoableAction", "Remove staves"));
     127              : 
     128            0 :     NotationParts::removeStaves(stavesIds);
     129              : 
     130            0 :     for (const INotationPartsPtr& parts : excerptsParts()) {
     131            0 :         parts->removeStaves(stavesIds);
     132            0 :     }
     133              : 
     134            0 :     endGlobalEdit();
     135            0 : }
     136              : 
     137            0 : bool MasterNotationParts::appendStaff(Staff* staff, const ID& destinationPartId)
     138              : {
     139            0 :     TRACEFUNC;
     140              : 
     141            0 :     IF_ASSERT_FAILED(staff) {
     142            0 :         return false;
     143              :     }
     144              : 
     145            0 :     startGlobalEdit(TranslatableString("undoableAction", "Add staff"));
     146              : 
     147              :     //! NOTE: will be generated later after adding to the score
     148            0 :     staff->setId(mu::engraving::INVALID_ID);
     149              : 
     150            0 :     NotationParts::appendStaff(staff, destinationPartId);
     151              : 
     152            0 :     for (const INotationPartsPtr& parts : excerptsParts()) {
     153            0 :         Staff* excerptStaff = staff->clone();
     154            0 :         if (!parts->appendStaffLinkedToMaster(excerptStaff, staff, destinationPartId)) {
     155            0 :             excerptStaff->undoUnlink();
     156            0 :             delete excerptStaff;
     157              :         }
     158            0 :     }
     159              : 
     160            0 :     endGlobalEdit();
     161            0 :     return true;
     162            0 : }
     163              : 
     164            0 : bool MasterNotationParts::appendLinkedStaff(Staff* staff, const muse::ID& sourceStaffId, const muse::ID& destinationPartId)
     165              : {
     166            0 :     TRACEFUNC;
     167              : 
     168            0 :     IF_ASSERT_FAILED(staff) {
     169            0 :         return false;
     170              :     }
     171              : 
     172            0 :     startGlobalEdit(TranslatableString("undoableAction", "Add linked staff"));
     173              : 
     174              :     //! NOTE: will be generated later after adding to the score
     175            0 :     staff->setId(mu::engraving::INVALID_ID);
     176              : 
     177            0 :     NotationParts::appendLinkedStaff(staff, sourceStaffId, destinationPartId);
     178              : 
     179            0 :     for (const INotationPartsPtr& parts : excerptsParts()) {
     180            0 :         Staff* excerptStaff = staff->clone();
     181            0 :         if (!parts->appendLinkedStaff(excerptStaff, sourceStaffId, destinationPartId)) {
     182            0 :             excerptStaff->unlink();
     183            0 :             delete excerptStaff;
     184              :         }
     185            0 :     }
     186              : 
     187            0 :     endGlobalEdit();
     188            0 :     return true;
     189            0 : }
     190              : 
     191            0 : void MasterNotationParts::replaceInstrument(const InstrumentKey& instrumentKey, const Instrument& newInstrument,
     192              :                                             const StaffType* newStaffType)
     193              : {
     194            0 :     TRACEFUNC;
     195              : 
     196            0 :     startGlobalEdit(TranslatableString("undoableAction", "Replace instrument"));
     197              : 
     198            0 :     Part* part = partModifiable(instrumentKey.partId);
     199            0 :     bool isMainInstrument = part && isMainInstrumentForPart(instrumentKey, part);
     200              : 
     201            0 :     mu::engraving::Interval oldTranspose = part ? part->instrument()->transpose() : mu::engraving::Interval(0, 0);
     202              : 
     203            0 :     NotationParts::replaceInstrument(instrumentKey, newInstrument, newStaffType);
     204              : 
     205            0 :     for (const INotationPartsPtr& parts : excerptsParts()) {
     206            0 :         parts->replaceInstrument(instrumentKey, newInstrument, newStaffType);
     207            0 :     }
     208              : 
     209            0 :     mu::engraving::Interval newTranspose = newInstrument.transpose();
     210              :     // this also transposes all linked parts
     211            0 :     engraving::Transaction& tx = score()->transactionManager()->currentOrDummyTransaction();
     212            0 :     if (newTranspose.reduced() != oldTranspose.reduced()) {
     213            0 :         engraving::Transpose::transpositionChanged(tx, score(), part, Part::MAIN_INSTRUMENT_TICK, oldTranspose);
     214              :     }
     215              : 
     216            0 :     if (isMainInstrument) {
     217            0 :         if (mu::engraving::Excerpt* excerpt = findExcerpt(part->id())) {
     218            0 :             StringList allExcerptLowerNames;
     219            0 :             for (const mu::engraving::Excerpt* excerpt2 : score()->masterScore()->excerpts()) {
     220            0 :                 allExcerptLowerNames.push_back(excerpt2->name().toLower());
     221              :             }
     222              : 
     223            0 :             String newName = mu::engraving::formatUniqueExcerptName(part->partName(), allExcerptLowerNames);
     224            0 :             excerpt->excerptScore()->undo(new mu::engraving::ChangeExcerptTitle(excerpt, newName));
     225            0 :         }
     226              :     }
     227              : 
     228            0 :     endGlobalEdit();
     229            0 : }
     230              : 
     231            0 : void MasterNotationParts::replaceDrumset(const InstrumentKey& instrumentKey, const Drumset& newDrumset, bool undoable)
     232              : {
     233            0 :     TRACEFUNC;
     234              : 
     235            0 :     startGlobalEdit(TranslatableString("undoableAction", "Edit drumset"));
     236              : 
     237            0 :     NotationParts::replaceDrumset(instrumentKey, newDrumset, undoable);
     238              : 
     239            0 :     for (const INotationPartsPtr& parts : excerptsParts()) {
     240            0 :         parts->replaceDrumset(instrumentKey, newDrumset, undoable);
     241            0 :     }
     242              : 
     243            0 :     endGlobalEdit();
     244            0 : }
     245              : 
     246            0 : void MasterNotationParts::onPartsRemoved(const std::vector<Part*>& parts)
     247              : {
     248            0 :     mu::engraving::MasterScore* master = score()->masterScore();
     249            0 :     std::vector<mu::engraving::Excerpt*> excerpts = master->excerpts();
     250              : 
     251            0 :     for (mu::engraving::Excerpt* excerpt : excerpts) {
     252            0 :         const ID& initialPartId = excerpt->initialPartId();
     253            0 :         if (!initialPartId.isValid()) {
     254            0 :             continue;
     255              :         }
     256              : 
     257            0 :         bool deleteExcerpt = std::find_if(parts.cbegin(), parts.cend(), [initialPartId](const Part* part) {
     258            0 :             return part->id() == initialPartId;
     259            0 :         }) != parts.cend();
     260              : 
     261            0 :         if (deleteExcerpt) {
     262            0 :             master->deleteExcerpt(excerpt);
     263              :         }
     264              :     }
     265            0 : }
     266              : 
     267            0 : void MasterNotationParts::addSystemObjects(const muse::IDList& stavesIds)
     268              : {
     269            0 :     if (stavesIds.empty()) {
     270            0 :         return;
     271              :     }
     272              : 
     273            0 :     startGlobalEdit(TranslatableString("undoableAction", "Add system markings"));
     274              : 
     275            0 :     NotationParts::addSystemObjects(stavesIds);
     276              : 
     277            0 :     endGlobalEdit();
     278              : }
     279              : 
     280            0 : void MasterNotationParts::removeSystemObjects(const muse::IDList& stavesIds)
     281              : {
     282            0 :     if (stavesIds.empty()) {
     283            0 :         return;
     284              :     }
     285              : 
     286            0 :     startGlobalEdit(TranslatableString("undoableAction", "Remove system markings"));
     287              : 
     288            0 :     NotationParts::removeSystemObjects(stavesIds);
     289              : 
     290            0 :     endGlobalEdit();
     291              : }
     292              : 
     293            0 : void MasterNotationParts::moveSystemObjects(const muse::ID& sourceStaffId, const muse::ID& destinationStaffId)
     294              : {
     295            0 :     startGlobalEdit(TranslatableString("undoableAction", "Move system markings"));
     296              : 
     297            0 :     NotationParts::moveSystemObjects(sourceStaffId, destinationStaffId);
     298              : 
     299            0 :     for (const INotationPartsPtr& parts : excerptsParts()) {
     300            0 :         parts->moveSystemObjects(sourceStaffId, destinationStaffId);
     301            0 :     }
     302              : 
     303            0 :     endGlobalEdit();
     304            0 : }
     305              : 
     306            0 : void MasterNotationParts::moveSystemObjectLayerBelowBottomStaff()
     307              : {
     308            0 :     IF_ASSERT_FAILED(score()->nstaves() > 1) {
     309            0 :         return;
     310              :     }
     311              : 
     312            0 :     startGlobalEdit(TranslatableString("undoableAction", "Add system object layer below the bottom staff"));
     313              : 
     314            0 :     NotationParts::moveSystemObjectLayerBelowBottomStaff();
     315              : 
     316            0 :     endGlobalEdit();
     317              : }
     318              : 
     319            0 : void MasterNotationParts::moveSystemObjectLayerAboveBottomStaff()
     320              : {
     321            0 :     IF_ASSERT_FAILED(score()->nstaves() > 1) {
     322            0 :         return;
     323              :     }
     324              : 
     325            0 :     startGlobalEdit(TranslatableString("undoableAction", "Remove system object layer below the bottom staff"));
     326              : 
     327            0 :     NotationParts::moveSystemObjectLayerAboveBottomStaff();
     328              : 
     329            0 :     endGlobalEdit();
     330              : }
     331              : 
     332            0 : std::vector<INotationPartsPtr> MasterNotationParts::excerptsParts() const
     333              : {
     334            0 :     std::vector<INotationPartsPtr> result;
     335              : 
     336            0 :     for (const IExcerptNotationPtr& excerpt : m_excerpts) {
     337            0 :         result.push_back(excerpt->notation()->parts());
     338              :     }
     339              : 
     340            0 :     return result;
     341            0 : }
     342              : 
     343            0 : mu::engraving::Excerpt* MasterNotationParts::findExcerpt(const ID& initialPartId) const
     344              : {
     345            0 :     const std::vector<mu::engraving::Excerpt*>& excerpts = score()->masterScore()->excerpts();
     346            0 :     for (mu::engraving::Excerpt* excerpt : excerpts) {
     347            0 :         if (excerpt->initialPartId() == initialPartId) {
     348            0 :             return excerpt;
     349              :         }
     350              :     }
     351              : 
     352            0 :     return nullptr;
     353              : }
        

Generated by: LCOV version 2.0-1