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 "score.h"
24 :
25 : #include "compat/midi/compatmidirender.h"
26 : #include "dom/factory.h"
27 : #include "dom/instrtemplate.h"
28 : #include "dom/measure.h"
29 : #include "dom/scoreorder.h"
30 : #include "dom/part.h"
31 : #include "dom/score.h"
32 : #include "dom/masterscore.h"
33 : #include "dom/segment.h"
34 : #include "dom/staff.h"
35 : #include "dom/stafftype.h"
36 : #include "dom/text.h"
37 : #include "editing/editpart.h"
38 : #include "editing/editsystemlocks.h"
39 : #include "editing/transaction/transaction.h"
40 : #include "types/typesconv.h"
41 :
42 : #include "notation/inotation.h"
43 : #include "notation/inotationinteraction.h" // IWYU pragma: keep
44 : #include "notation/inotationundostack.h"
45 :
46 : // api
47 : #include "apistructs.h"
48 : #include "cursor.h"
49 : #include "elements.h"
50 : #include "instrument.h"
51 : #include "apitypes.h"
52 :
53 : using namespace mu::engraving::apiv1;
54 :
55 0 : Cursor* Score::newCursor()
56 : {
57 0 : return new Cursor(score());
58 : }
59 :
60 0 : void Score::addText(const QString& type, const QString& txt)
61 : {
62 0 : static const QMetaEnum meta = QMetaEnum::fromType<enums::TextStyleType>();
63 :
64 : mu::engraving::TextStyleType tid;
65 0 : const std::string key = type.toStdString();
66 0 : bool ok = false;
67 0 : int val = meta.keyToValue(key.c_str(), &ok);
68 0 : if (ok) {
69 0 : tid = static_cast<mu::engraving::TextStyleType>(val);
70 : } else {
71 0 : LOGE() << "Please use engraving::TextStyleType enum, the use of Xml tags is deprecated.";
72 0 : AsciiStringView t(key);
73 0 : tid = mu::engraving::TConv::fromXml(t, mu::engraving::TextStyleType::DEFAULT);
74 : }
75 :
76 0 : mu::engraving::MeasureBase* mb = score()->first();
77 0 : if (!mb || !mb->isVBox()) {
78 0 : score()->insertBox(ElementType::VBOX, mb);
79 0 : mb = score()->first();
80 : }
81 :
82 0 : mu::engraving::Text* text = mu::engraving::Factory::createText(mb, tid);
83 0 : text->setXmlText(txt);
84 0 : score()->undoAddElement(text);
85 0 : }
86 :
87 : //---------------------------------------------------------
88 : // defaultInstrTemplate
89 : //---------------------------------------------------------
90 :
91 0 : static const mu::engraving::InstrumentTemplate* defaultInstrTemplate()
92 : {
93 0 : static mu::engraving::InstrumentTemplate defaultInstrument;
94 0 : if (defaultInstrument.channel.empty()) {
95 0 : mu::engraving::InstrChannel a;
96 0 : a.setChorus(0);
97 0 : a.setReverb(0);
98 0 : a.setName(muse::String::fromUtf8(mu::engraving::InstrChannel::DEFAULT_NAME));
99 0 : a.setBank(0);
100 0 : a.setVolume(90);
101 0 : a.setPan(0);
102 0 : defaultInstrument.channel.push_back(a);
103 0 : }
104 0 : return &defaultInstrument;
105 : }
106 :
107 : //---------------------------------------------------------
108 : // instrTemplateFromName
109 : //---------------------------------------------------------
110 :
111 0 : const mu::engraving::InstrumentTemplate* Score::instrTemplateFromName(const QString& name)
112 : {
113 0 : const InstrumentTemplate* t = searchTemplate(name);
114 0 : if (!t) {
115 0 : LOGW("<%s> not found", qPrintable(name));
116 0 : t = defaultInstrTemplate();
117 : }
118 0 : return t;
119 : }
120 :
121 0 : mu::notation::INotationPtr Score::notation() const
122 : {
123 0 : return projectSession()->currentNotation();
124 : }
125 :
126 0 : mu::notation::INotationUndoStackPtr Score::undoStack() const
127 : {
128 0 : return notation() ? notation()->undoStack() : nullptr;
129 : }
130 :
131 : //---------------------------------------------------------
132 : // Score::appendPart
133 : //---------------------------------------------------------
134 :
135 0 : void Score::appendPart(const QString& instrumentId)
136 : {
137 0 : const InstrumentTemplate* t = searchTemplate(instrumentId);
138 :
139 0 : if (!t) {
140 0 : LOGW("appendPart: <%s> not found", qPrintable(instrumentId));
141 0 : t = defaultInstrTemplate();
142 : }
143 :
144 0 : score()->appendPart(t);
145 0 : }
146 :
147 : //---------------------------------------------------------
148 : // Score::appendPartByMusicXmlId
149 : //---------------------------------------------------------
150 :
151 0 : void Score::appendPartByMusicXmlId(const QString& instrumentMusicXmlId)
152 : {
153 0 : const InstrumentTemplate* t = searchTemplateForMusicXmlId(instrumentMusicXmlId);
154 :
155 0 : if (!t) {
156 0 : LOGW("appendPart: <%s> not found", qPrintable(instrumentMusicXmlId));
157 0 : t = defaultInstrTemplate();
158 : }
159 :
160 0 : score()->appendPart(t);
161 0 : }
162 :
163 : /** APIDOC
164 : * Replaces the instrument for a given part with a new instrument.
165 : * This changes the instrument definition including its name, clef, and sound.
166 : * @method
167 : * @param {Part} part - The Part object whose instrument should be replaced.
168 : * @param {string} instrumentId - ID of the new instrument from instruments.xml.
169 : */
170 3 : void Score::replaceInstrument(apiv1::Part* part, const QString& instrumentId)
171 : {
172 3 : if (!part) {
173 1 : LOGW("replaceInstrument: part is null");
174 2 : return;
175 : }
176 :
177 2 : const InstrumentTemplate* t = searchTemplate(instrumentId);
178 2 : if (!t) {
179 1 : LOGW("replaceInstrument: <%s> not found", qPrintable(instrumentId));
180 1 : return;
181 : }
182 :
183 1 : mu::engraving::Instrument newInstrument = mu::engraving::Instrument::fromTemplate(t);
184 1 : mu::engraving::EditPart::replacePartInstrument(score(), part->part(), newInstrument);
185 1 : }
186 :
187 1 : void Score::setPartVisible(apiv1::Part* part, bool visible)
188 : {
189 1 : if (!part) {
190 0 : LOGW("setPartVisible: part is null");
191 0 : return;
192 : }
193 :
194 1 : mu::engraving::EditPart::setPartVisible(score(), part->part(), visible);
195 : }
196 :
197 0 : void Score::setPartSharpFlat(apiv1::Part* part, int sharpFlat)
198 : {
199 0 : if (!part) {
200 0 : LOGW("setPartSharpFlat: part is null");
201 0 : return;
202 : }
203 :
204 0 : mu::engraving::EditPart::setPartSharpFlat(score(), part->part(), mu::engraving::PreferSharpFlat(sharpFlat));
205 : }
206 :
207 1 : void Score::setInstrumentName(apiv1::Part* part, apiv1::Fraction* tick, const QString& name)
208 : {
209 1 : if (!part) {
210 0 : LOGW("setInstrumentName: part is null");
211 0 : return;
212 : }
213 1 : if (!tick) {
214 0 : LOGW("setInstrumentName: tick is null");
215 0 : return;
216 : }
217 :
218 1 : mu::engraving::EditPart::setInstrumentName(score(), part->part(), tick->fraction(), name);
219 : }
220 :
221 0 : void Score::setInstrumentAbbreviature(apiv1::Part* part, apiv1::Fraction* tick, const QString& abbreviature)
222 : {
223 0 : if (!part) {
224 0 : LOGW("setInstrumentAbbreviature: part is null");
225 0 : return;
226 : }
227 0 : if (!tick) {
228 0 : LOGW("setInstrumentAbbreviature: tick is null");
229 0 : return;
230 : }
231 :
232 0 : mu::engraving::EditPart::setInstrumentAbbreviature(score(), part->part(), tick->fraction(), abbreviature);
233 : }
234 :
235 1 : void Score::setStaffType(apiv1::Staff* staff, int staffTypeId)
236 : {
237 1 : if (!staff) {
238 0 : LOGW("setStaffType: staff is null");
239 0 : return;
240 : }
241 :
242 1 : mu::engraving::EditPart::setStaffType(score(), staff->staff(), mu::engraving::StaffTypes(staffTypeId));
243 : }
244 :
245 1 : void Score::removeParts(QList<apiv1::Part*> parts)
246 : {
247 1 : std::vector<mu::engraving::Part*> domParts;
248 2 : for (apiv1::Part* p : parts) {
249 1 : if (!p) {
250 0 : LOGW("removeParts: null part in list");
251 0 : continue;
252 : }
253 1 : domParts.push_back(p->part());
254 : }
255 :
256 1 : mu::engraving::EditPart::removeParts(score(), domParts);
257 1 : }
258 :
259 0 : void Score::removeStaves(QList<apiv1::Staff*> staves)
260 : {
261 0 : std::vector<mu::engraving::Staff*> domStaves;
262 0 : for (apiv1::Staff* s : staves) {
263 0 : if (!s) {
264 0 : LOGW("removeStaves: null staff in list");
265 0 : continue;
266 : }
267 0 : domStaves.push_back(s->staff());
268 : }
269 :
270 0 : mu::engraving::EditPart::removeStaves(score(), domStaves);
271 0 : }
272 :
273 1 : void Score::moveParts(QList<apiv1::Part*> sourceParts, apiv1::Part* destinationPart, int insertMode)
274 : {
275 1 : if (!destinationPart) {
276 0 : LOGW("moveParts: destinationPart is null");
277 0 : return;
278 : }
279 :
280 1 : std::vector<mu::engraving::Part*> domParts;
281 2 : for (apiv1::Part* p : sourceParts) {
282 1 : if (!p) {
283 0 : LOGW("moveParts: null part in list");
284 0 : continue;
285 : }
286 1 : domParts.push_back(p->part());
287 : }
288 :
289 1 : mu::engraving::EditPart::moveParts(score(), domParts, destinationPart->part(), insertMode == 1);
290 1 : }
291 :
292 0 : void Score::moveStaves(QList<apiv1::Staff*> sourceStaves, apiv1::Staff* destinationStaff, int insertMode)
293 : {
294 0 : if (!destinationStaff) {
295 0 : LOGW("moveStaves: destinationStaff is null");
296 0 : return;
297 : }
298 :
299 0 : std::vector<mu::engraving::Staff*> domStaves;
300 0 : for (apiv1::Staff* s : sourceStaves) {
301 0 : if (!s) {
302 0 : LOGW("moveStaves: null staff in list");
303 0 : continue;
304 : }
305 0 : domStaves.push_back(s->staff());
306 : }
307 :
308 0 : mu::engraving::EditPart::moveStaves(score(), domStaves, destinationStaff->staff(), insertMode == 1);
309 0 : }
310 :
311 0 : void Score::addSystemObjects(QList<apiv1::Staff*> staves)
312 : {
313 0 : std::vector<mu::engraving::Staff*> domStaves;
314 0 : for (apiv1::Staff* s : staves) {
315 0 : if (!s) {
316 0 : LOGW("addSystemObjects: null staff in list");
317 0 : continue;
318 : }
319 0 : domStaves.push_back(s->staff());
320 : }
321 :
322 0 : mu::engraving::EditPart::addSystemObjects(score(), domStaves);
323 0 : }
324 :
325 0 : void Score::removeSystemObjects(QList<apiv1::Staff*> staves)
326 : {
327 0 : std::vector<mu::engraving::Staff*> domStaves;
328 0 : for (apiv1::Staff* s : staves) {
329 0 : if (!s) {
330 0 : LOGW("removeSystemObjects: null staff in list");
331 0 : continue;
332 : }
333 0 : domStaves.push_back(s->staff());
334 : }
335 :
336 0 : mu::engraving::EditPart::removeSystemObjects(score(), domStaves);
337 0 : }
338 :
339 0 : void Score::moveSystemObjects(apiv1::Staff* sourceStaff, apiv1::Staff* destinationStaff)
340 : {
341 0 : if (!sourceStaff) {
342 0 : LOGW("moveSystemObjects: sourceStaff is null");
343 0 : return;
344 : }
345 0 : if (!destinationStaff) {
346 0 : LOGW("moveSystemObjects: destinationStaff is null");
347 0 : return;
348 : }
349 :
350 0 : mu::engraving::EditPart::moveSystemObjects(score(), sourceStaff->staff(), destinationStaff->staff());
351 : }
352 :
353 1 : Staff* Score::appendStaff(Part* destinationPart)
354 : {
355 1 : if (!destinationPart) {
356 0 : LOGW("appendStaff: destinationPart is null");
357 0 : return nullptr;
358 : }
359 :
360 1 : mu::engraving::Staff* staff = mu::engraving::EditPart::appendStaff(score(), destinationPart->part());
361 1 : return staff ? wrap<Staff>(staff, Ownership::SCORE) : nullptr;
362 : }
363 :
364 1 : Staff* Score::appendLinkedStaff(Staff* sourceStaff, Part* destinationPart)
365 : {
366 1 : if (!sourceStaff) {
367 0 : LOGW("appendLinkedStaff: sourceStaff is null");
368 0 : return nullptr;
369 : }
370 1 : if (!destinationPart) {
371 0 : LOGW("appendLinkedStaff: destinationPart is null");
372 0 : return nullptr;
373 : }
374 :
375 1 : mu::engraving::Staff* staff = mu::engraving::EditPart::appendLinkedStaff(score(), sourceStaff->staff(), destinationPart->part());
376 1 : return staff ? wrap<Staff>(staff, Ownership::SCORE) : nullptr;
377 : }
378 :
379 1 : bool Score::setVoiceVisible(Staff* staff, int voiceIndex, bool visible)
380 : {
381 1 : if (!staff) {
382 0 : LOGW("setVoiceVisible: staff is null");
383 0 : return false;
384 : }
385 :
386 1 : return mu::engraving::EditPart::setVoiceVisible(score(), staff->staff(), voiceIndex, visible);
387 : }
388 :
389 1 : void Score::replaceDrumset(Part* part, apiv1::Fraction* tick, Drumset* drumset)
390 : {
391 1 : if (!part) {
392 0 : LOGW("replaceDrumset: part is null");
393 0 : return;
394 : }
395 1 : if (!tick) {
396 0 : LOGW("replaceDrumset: tick is null");
397 0 : return;
398 : }
399 1 : if (!drumset) {
400 0 : LOGW("replaceDrumset: drumset is null");
401 0 : return;
402 : }
403 :
404 1 : mu::engraving::EditPart::replaceDrumset(score(), part->part(), tick->fraction(), *drumset->drumset());
405 : }
406 :
407 1 : void Score::insertPart(const QString& instrumentId, int index)
408 : {
409 1 : const InstrumentTemplate* t = searchTemplate(instrumentId);
410 1 : if (!t) {
411 0 : LOGW("insertPart: <%s> not found", qPrintable(instrumentId));
412 0 : return;
413 : }
414 :
415 1 : mu::engraving::EditPart::insertPart(score(), t, static_cast<size_t>(index));
416 : }
417 :
418 1 : void Score::replacePart(Part* part, const QString& instrumentId)
419 : {
420 1 : if (!part) {
421 0 : LOGW("replacePart: part is null");
422 0 : return;
423 : }
424 :
425 1 : const InstrumentTemplate* t = searchTemplate(instrumentId);
426 1 : if (!t) {
427 0 : LOGW("replacePart: <%s> not found", qPrintable(instrumentId));
428 0 : return;
429 : }
430 :
431 1 : mu::engraving::EditPart::replacePart(score(), part->part(), t);
432 : }
433 :
434 1 : void Score::setScoreOrder(const QString& orderId)
435 : {
436 1 : muse::String id = muse::String::fromQString(orderId);
437 :
438 1 : for (const mu::engraving::ScoreOrder& order : mu::engraving::instrumentOrders) {
439 1 : if (order.id == id) {
440 1 : mu::engraving::EditPart::setScoreOrder(score(), order);
441 1 : return;
442 : }
443 : }
444 :
445 0 : LOGW("setScoreOrder: <%s> not found", qPrintable(orderId));
446 1 : }
447 :
448 : //---------------------------------------------------------
449 : // Score::firstSegment
450 : //---------------------------------------------------------
451 :
452 0 : Segment* Score::firstSegment(int segmentType)
453 : {
454 0 : return wrap<Segment>(score()->firstSegment(engraving::SegmentType(segmentType)), Ownership::SCORE);
455 : }
456 :
457 0 : Measure* Score::tick2measure(Fraction* f)
458 : {
459 0 : const mu::engraving::Fraction tick = f->fraction();
460 0 : if (!tick.isValid() || tick.negative()) {
461 0 : return nullptr;
462 : }
463 0 : return wrap<Measure>(score()->tick2measure(tick));
464 : }
465 :
466 0 : Segment* Score::findSegmentAtTick(int segmentTypes, Fraction* f)
467 : {
468 0 : const mu::engraving::Fraction tick = f->fraction();
469 0 : if (!tick.isValid() || tick.negative()) {
470 0 : return nullptr;
471 : }
472 0 : mu::engraving::Measure* measure = score()->tick2measure(tick);
473 0 : mu::engraving::Segment* segment = measure->findSegment(engraving::SegmentType(segmentTypes), tick);
474 0 : return segment ? wrap<Segment>(segment, Ownership::SCORE) : nullptr;
475 : }
476 :
477 : //---------------------------------------------------------
478 : // Score::lastSegment
479 : //---------------------------------------------------------
480 :
481 0 : Segment* Score::lastSegment()
482 : {
483 0 : return wrap<Segment>(score()->lastSegment(), Ownership::SCORE);
484 : }
485 :
486 : //---------------------------------------------------------
487 : // Score::firstMeasure
488 : //---------------------------------------------------------
489 :
490 0 : Measure* Score::firstMeasure()
491 : {
492 0 : return wrap<Measure>(score()->firstMeasure(), Ownership::SCORE);
493 : }
494 :
495 : //---------------------------------------------------------
496 : // Score::firstMeasureMM
497 : //---------------------------------------------------------
498 :
499 0 : Measure* Score::firstMeasureMM()
500 : {
501 0 : return wrap<Measure>(score()->firstMeasureMM(), Ownership::SCORE);
502 : }
503 :
504 : //---------------------------------------------------------
505 : // Score::lastMeasure
506 : //---------------------------------------------------------
507 :
508 0 : Measure* Score::lastMeasure()
509 : {
510 0 : return wrap<Measure>(score()->lastMeasure(), Ownership::SCORE);
511 : }
512 :
513 : //---------------------------------------------------------
514 : // Score::firstMeasureMM
515 : //---------------------------------------------------------
516 :
517 0 : Measure* Score::lastMeasureMM()
518 : {
519 0 : return wrap<Measure>(score()->lastMeasureMM(), Ownership::SCORE);
520 : }
521 :
522 0 : QString Score::name() const
523 : {
524 0 : return score()->masterScore()->name();
525 : }
526 :
527 0 : void Score::setName(const QString& /*name*/)
528 : {
529 0 : NOT_IMPLEMENTED;
530 0 : }
531 :
532 0 : void Score::createPlayEvents()
533 : {
534 0 : mu::engraving::CompatMidiRender::createPlayEvents(score());
535 0 : }
536 :
537 0 : QQmlListProperty<Staff> Score::staves() const
538 : {
539 0 : return wrapContainerProperty<Staff>(this, score()->staves());
540 : }
541 :
542 0 : QQmlListProperty<Part> Score::parts() const
543 : {
544 0 : return wrapContainerProperty<Part>(this, score()->parts());
545 : }
546 :
547 0 : QQmlListProperty<Excerpt> Score::excerpts() const
548 : {
549 0 : return wrapExcerptsContainerProperty<Excerpt>(this, score()->masterScore()->excerpts());
550 : }
551 :
552 0 : QQmlListProperty<Page> Score::pages() const
553 : {
554 0 : return wrapContainerProperty<Page>(this, score()->pages());
555 : }
556 :
557 0 : QQmlListProperty<System> Score::systems() const
558 : {
559 0 : return wrapContainerProperty<System>(this, score()->systems());
560 : }
561 :
562 0 : QQmlListProperty<EngravingItem> Score::brackets(int staffIdx)
563 : {
564 0 : return wrapContainerProperty<EngravingItem>(this, score()->brackets(static_cast<staff_idx_t>(staffIdx)));
565 : }
566 :
567 0 : bool Score::hasLyrics() const
568 : {
569 0 : return score()->hasLyrics();
570 : }
571 :
572 0 : int Score::lyricCount() const
573 : {
574 0 : return score()->lyricCount();
575 : }
576 :
577 0 : QQmlListProperty<Lyrics> Score::lyrics() const
578 : {
579 0 : static std::vector<engraving::Lyrics*> list;
580 0 : list = score()->lyrics();
581 0 : return wrapContainerProperty<Lyrics>(this, list);
582 : }
583 :
584 0 : QString Score::extractLyrics() const
585 : {
586 0 : return score()->extractLyrics();
587 : }
588 :
589 1 : QQmlListProperty<Spanner> Score::spanners()
590 : {
591 1 : static std::vector<mu::engraving::Spanner*> spannerList;
592 1 : spannerList = score()->spannerList();
593 1 : return wrapContainerProperty<Spanner>(this, spannerList);
594 : }
595 :
596 : //---------------------------------------------------------
597 : // Score::startCmd
598 : //---------------------------------------------------------
599 :
600 0 : void Score::startCmd(const QString& qActionName)
601 : {
602 0 : IF_ASSERT_FAILED(undoStack()) {
603 0 : return;
604 : }
605 :
606 0 : muse::TranslatableString actionName = qActionName.isEmpty()
607 0 : ? TranslatableString("undoableAction", "Plugin edit")
608 0 : : TranslatableString::untranslatable(qActionName);
609 :
610 0 : undoStack()->prepareChanges(actionName);
611 : // Lock the undo stack, so that all changes made by the plugin,
612 : // including PluginAPI::cmd(), are committed as a single command.
613 0 : undoStack()->lock();
614 0 : }
615 :
616 0 : void Score::endCmd(bool rollback)
617 : {
618 0 : IF_ASSERT_FAILED(undoStack()) {
619 0 : return;
620 : }
621 :
622 0 : undoStack()->unlock();
623 :
624 0 : if (rollback) {
625 0 : undoStack()->rollbackChanges();
626 : } else {
627 0 : undoStack()->commitChanges();
628 : }
629 :
630 0 : notation()->notationChanged().send(muse::RectF());
631 : }
632 :
633 0 : void Score::doLayout(Fraction* startTick, Fraction* endTick)
634 : {
635 0 : score()->doLayoutRange(startTick->fraction(), endTick->fraction());
636 0 : }
637 :
638 0 : void Score::addRemoveSystemLocks(int interval, bool lock)
639 : {
640 0 : Transaction& tx = score()->transactionManager()->currentOrDummyTransaction();
641 0 : EditSystemLocks::addRemoveSystemLocks(tx, score(), interval, lock);
642 0 : }
643 :
644 0 : void Score::makeIntoSystem(apiv1::MeasureBase* first, apiv1::MeasureBase* last)
645 : {
646 0 : Transaction& tx = score()->transactionManager()->currentOrDummyTransaction();
647 0 : EditSystemLocks::makeIntoSystem(tx, score(), first->measureBase(), last->measureBase());
648 0 : }
649 :
650 0 : void Score::showElementInScore(apiv1::EngravingItem* wrappedElement, int staffIdx)
651 : {
652 0 : if (!wrappedElement->element()) {
653 0 : return;
654 : }
655 0 : notation()->interaction()->showItem(wrappedElement->element(), staffIdx);
656 : }
|