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