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 : #include "scoreorder.h"
23 :
24 : #include "rw/xmlreader.h"
25 : #include "rw/xmlwriter.h"
26 :
27 : #include "dom/bracketitem.h"
28 : #include "dom/instrtemplate.h"
29 : #include "dom/part.h"
30 : #include "dom/score.h"
31 : #include "dom/staff.h"
32 :
33 : #include "../editing/editstaffbrackets.h"
34 :
35 : #include "log.h"
36 :
37 : using namespace mu;
38 :
39 : namespace mu::engraving {
40 : static const String SOLOISTS_ID(u"<soloists>");
41 : static const String UNSORTED_ID(u"<unsorted>");
42 :
43 : //---------------------------------------------------------
44 : // clone
45 : //---------------------------------------------------------
46 :
47 0 : ScoreOrder ScoreOrder::clone() const
48 : {
49 0 : ScoreOrder newOrder;
50 :
51 0 : newOrder.id = id;
52 0 : newOrder.name = name;
53 0 : newOrder.customized = customized;
54 0 : newOrder.instrumentMap = instrumentMap;
55 :
56 0 : for (const ScoreGroup& sg : groups) {
57 0 : ScoreGroup newGroup;
58 :
59 0 : newGroup.family = sg.family;
60 0 : newGroup.section = sg.section;
61 0 : newGroup.unsorted = sg.unsorted;
62 0 : newGroup.notUnsorted = sg.notUnsorted;
63 0 : newGroup.bracket = sg.bracket;
64 0 : newGroup.barLineSpan = sg.barLineSpan;
65 0 : newGroup.thinBracket = sg.thinBracket;
66 :
67 0 : newOrder.groups.push_back(newGroup);
68 0 : }
69 :
70 0 : return newOrder;
71 0 : }
72 :
73 : //---------------------------------------------------------
74 : // readBoolAttribute
75 : //---------------------------------------------------------
76 :
77 0 : bool ScoreOrder::operator==(const ScoreOrder& order) const
78 : {
79 0 : return id == order.id;
80 : }
81 :
82 0 : bool ScoreOrder::operator!=(const ScoreOrder& order) const
83 : {
84 0 : return !(*this == order);
85 : }
86 :
87 4896 : bool ScoreOrder::readBoolAttribute(XmlReader& reader, const char* attrName, bool defvalue)
88 : {
89 4896 : if (!reader.hasAttribute(attrName)) {
90 1514 : return defvalue;
91 : }
92 3382 : String attr { reader.attribute(attrName) };
93 3382 : if (attr.toLower() == "false") {
94 274 : return false;
95 3108 : } else if (attr.toLower() == "true") {
96 3108 : return true;
97 : }
98 0 : LOGD("invalid value \"%s\" for attribute \"%s\", using default \"%d\"", muPrintable(attr), attrName, defvalue);
99 0 : return defvalue;
100 3382 : }
101 :
102 : //---------------------------------------------------------
103 : // readInstrument
104 : //---------------------------------------------------------
105 :
106 347 : void ScoreOrder::readInstrument(XmlReader& reader)
107 : {
108 347 : String instrumentId { reader.attribute("id") };
109 347 : if (!mu::engraving::searchTemplate(instrumentId)) {
110 0 : LOGD("cannot find instrument templates for <%s>", muPrintable(instrumentId));
111 0 : reader.skipCurrentElement();
112 0 : return;
113 : }
114 694 : while (reader.readNextStartElement()) {
115 347 : if (reader.name() == "family") {
116 347 : InstrumentOverwrite io;
117 347 : io.id = reader.attribute("id");
118 347 : io.name = reader.readText();
119 347 : instrumentMap.insert({ instrumentId, io });
120 347 : } else {
121 0 : reader.unknown();
122 : }
123 : }
124 347 : }
125 :
126 : //---------------------------------------------------------
127 : // readSoloists
128 : //---------------------------------------------------------
129 :
130 274 : void ScoreOrder::readSoloists(XmlReader& reader, const String section)
131 : {
132 274 : reader.skipCurrentElement();
133 274 : if (hasGroup(SOLOISTS_ID)) {
134 0 : return;
135 : }
136 274 : ScoreGroup sg;
137 274 : sg.family = String(SOLOISTS_ID);
138 274 : sg.section = section;
139 274 : groups.push_back(sg);
140 274 : }
141 :
142 : //---------------------------------------------------------
143 : // readSection
144 : //---------------------------------------------------------
145 :
146 1691 : void ScoreOrder::readSection(XmlReader& reader)
147 : {
148 1691 : String sectionId { reader.attribute("id") };
149 1691 : bool barLineSpan = readBoolAttribute(reader, "barLineSpan", true);
150 1691 : bool thinBrackets = readBoolAttribute(reader, "thinBrackets", true);
151 7927 : while (reader.readNextStartElement()) {
152 6236 : if (reader.name() == "family") {
153 5479 : ScoreGroup sg;
154 5479 : sg.family = reader.readText();
155 5479 : sg.section = sectionId;
156 5479 : sg.bracket = true;
157 5479 : sg.barLineSpan = barLineSpan;
158 5479 : sg.thinBracket = thinBrackets;
159 5479 : groups.push_back(sg);
160 6236 : } else if (reader.name() == "unsorted") {
161 757 : String group { reader.attribute("group", String()) };
162 :
163 757 : if (hasGroup(UNSORTED_ID, group)) {
164 0 : reader.skipCurrentElement();
165 0 : return;
166 : }
167 :
168 757 : ScoreGroup sg;
169 757 : sg.family = String(UNSORTED_ID);
170 757 : sg.section = sectionId;
171 757 : sg.unsorted = group;
172 757 : sg.notUnsorted = false;
173 757 : sg.bracket = true;
174 757 : sg.barLineSpan = readBoolAttribute(reader, "barLineSpan", true);
175 757 : sg.thinBracket = readBoolAttribute(reader, "thinBrackets", true);
176 757 : groups.push_back(sg);
177 757 : reader.skipCurrentElement();
178 757 : } else {
179 0 : reader.unknown();
180 : }
181 : }
182 1691 : }
183 :
184 : //---------------------------------------------------------
185 : // hasGroup
186 : //---------------------------------------------------------
187 :
188 1579 : bool ScoreOrder::hasGroup(const String& familyId, const String& group) const
189 : {
190 31158 : for (const ScoreGroup& sg : groups) {
191 29853 : if ((sg.family == familyId) && (group == sg.unsorted)) {
192 274 : return true;
193 : }
194 : }
195 1305 : return false;
196 : }
197 :
198 : //---------------------------------------------------------
199 : // isValid
200 : //---------------------------------------------------------
201 :
202 1724 : bool ScoreOrder::isValid() const
203 : {
204 1724 : return !id.empty();
205 : }
206 :
207 : //---------------------------------------------------------
208 : // isCustom
209 : //---------------------------------------------------------
210 :
211 26 : bool ScoreOrder::isCustom() const
212 : {
213 26 : return id == String(u"custom");
214 : }
215 :
216 : //---------------------------------------------------------
217 : // getName
218 : //---------------------------------------------------------
219 :
220 0 : TranslatableString ScoreOrder::getName() const
221 : {
222 0 : return customized ? TranslatableString("engraving/scoreorder", "%1 (Customized)").arg(name) : name;
223 : }
224 :
225 : //---------------------------------------------------------
226 : // getFamilyName
227 : //---------------------------------------------------------
228 :
229 36 : String ScoreOrder::getFamilyName(const InstrumentTemplate* instrTemplate, bool soloist) const
230 : {
231 36 : if (!instrTemplate) {
232 0 : return String(u"<unsorted>");
233 : }
234 :
235 36 : if (soloist) {
236 0 : return String(u"<soloists>");
237 36 : } else if (muse::contains(instrumentMap, instrTemplate->id)) {
238 36 : return instrumentMap.at(instrTemplate->id).id;
239 0 : } else if (instrTemplate->family) {
240 0 : return instrTemplate->family->id;
241 : }
242 0 : return String(u"<unsorted>");
243 : }
244 :
245 : //---------------------------------------------------------
246 : // newUnsortedGroup
247 : //---------------------------------------------------------
248 :
249 274 : ScoreGroup ScoreOrder::newUnsortedGroup(const String group, const String section) const
250 : {
251 274 : ScoreGroup sg;
252 274 : sg.family = String(UNSORTED_ID);
253 274 : sg.section = section;
254 274 : sg.unsorted = group;
255 274 : sg.notUnsorted = false;
256 274 : sg.bracket = false;
257 274 : sg.barLineSpan = false;
258 274 : sg.thinBracket = false;
259 274 : return sg;
260 : }
261 :
262 : //---------------------------------------------------------
263 : // getGroup
264 : //---------------------------------------------------------
265 :
266 36 : ScoreGroup ScoreOrder::getGroup(const String family, const String instrumentGroup) const
267 : {
268 36 : static const String UNSORTED = String(u"<unsorted>");
269 :
270 36 : ScoreGroup unsortedScoreGroup;
271 936 : for (const ScoreGroup& sg : groups) {
272 936 : if ((sg.family == UNSORTED) && sg.unsorted.isEmpty()) {
273 36 : unsortedScoreGroup = sg;
274 36 : break;
275 : }
276 : }
277 :
278 36 : if (family.isEmpty()) {
279 0 : return unsortedScoreGroup;
280 : }
281 :
282 36 : for (const ScoreGroup& sg : groups) {
283 36 : if (sg.family == family) {
284 36 : return sg;
285 : }
286 0 : if ((sg.family == UNSORTED) && (sg.unsorted == instrumentGroup)) {
287 0 : unsortedScoreGroup = sg;
288 : }
289 : }
290 0 : return unsortedScoreGroup;
291 36 : }
292 :
293 : //---------------------------------------------------------
294 : // instrumentSortingIndex
295 : //---------------------------------------------------------
296 :
297 55 : int ScoreOrder::instrumentSortingIndex(const String& instrumentId, bool isSoloist) const
298 : {
299 55 : static const String SoloistsGroup(u"<soloists>");
300 55 : static const String UnsortedGroup(u"<unsorted>");
301 :
302 : enum class Priority : unsigned char {
303 : Undefined,
304 : Unsorted,
305 : UnsortedGroup,
306 : Family,
307 : Soloist
308 : };
309 :
310 55 : InstrumentIndex ii { searchTemplateIndexForId(instrumentId) };
311 55 : if (!ii.instrTemplate) {
312 16 : return 0;
313 : }
314 :
315 39 : String family = muse::contains(instrumentMap, instrumentId) ? instrumentMap.at(instrumentId).id : ii.instrTemplate->familyId();
316 :
317 39 : size_t index = groups.size();
318 :
319 39 : auto calculateIndex = [instrumentId, &ii](size_t index) {
320 39 : return index * ii.templateCount + ii.instrTemplate->sequenceOrder;
321 39 : };
322 :
323 39 : Priority priority = Priority::Undefined;
324 :
325 1119 : for (size_t i = 0; i < groups.size(); ++i) {
326 1080 : const ScoreGroup& sg = groups.at(i);
327 :
328 1080 : if ((sg.family == SoloistsGroup) && isSoloist) {
329 0 : return static_cast<int>(calculateIndex(i));
330 1080 : } else if ((priority < Priority::Family) && (sg.family == family)) {
331 36 : index = i;
332 36 : priority = Priority::Family;
333 0 : } else if ((priority < Priority::UnsortedGroup) && (sg.family == UnsortedGroup)
334 1044 : && (sg.unsorted == ii.instrTemplate->groupId)) {
335 0 : index = i;
336 0 : priority = Priority::UnsortedGroup;
337 0 : } else if ((priority < Priority::Unsorted) && (sg.family == UnsortedGroup)
338 1044 : && (sg.unsorted.empty())) {
339 0 : index = i;
340 0 : priority = Priority::Unsorted;
341 : }
342 : }
343 :
344 39 : return static_cast<int>(calculateIndex(index));
345 39 : }
346 :
347 : //---------------------------------------------------------
348 : // isScoreOrder
349 : //---------------------------------------------------------
350 :
351 26 : bool ScoreOrder::isScoreOrder(const std::vector<int>& indices) const
352 : {
353 26 : if (isCustom()) {
354 0 : return true;
355 : }
356 :
357 26 : int prvIndex { -1 };
358 81 : for (int curIndex : indices) {
359 55 : if (curIndex < prvIndex) {
360 0 : return false;
361 : }
362 55 : prvIndex = curIndex;
363 : }
364 26 : return true;
365 : }
366 :
367 26 : bool ScoreOrder::isScoreOrder(const Score* score) const
368 : {
369 26 : std::vector<int> indices;
370 26 : indices.reserve(score->parts().size());
371 81 : for (const Part* part : score->parts()) {
372 55 : indices.push_back(instrumentSortingIndex(part->instrument()->id(), part->soloist()));
373 : }
374 :
375 52 : return isScoreOrder(indices);
376 26 : }
377 :
378 : //---------------------------------------------------------
379 : // setBracketsAndBarlines
380 : //---------------------------------------------------------
381 :
382 21 : void ScoreOrder::setBracketsAndBarlines(Score* score)
383 : {
384 21 : if (groups.size() <= 0) {
385 9 : return;
386 : }
387 :
388 12 : bool prvThnBracket { false };
389 12 : bool prvBarLineSpan { false };
390 12 : String prvSection;
391 12 : int prvInstrument { -1 };
392 12 : Staff* prvStaff { nullptr };
393 :
394 12 : Staff* thkBracketStaff { nullptr };
395 12 : Staff* thnBracketStaff { nullptr };
396 12 : int thkBracketSpan { 0 };
397 12 : int thnBracketSpan { 0 };
398 :
399 48 : for (Part* part : score->parts()) {
400 36 : InstrumentIndex ii = searchTemplateIndexForId(part->instrument()->id());
401 36 : const InstrumentTemplate* instrTemplate = ii.instrTemplate;
402 36 : if (!instrTemplate) {
403 0 : continue;
404 : }
405 :
406 36 : String family { getFamilyName(instrTemplate, part->soloist()) };
407 36 : const ScoreGroup sg = getGroup(family, instrumentGroups[ii.groupIndex]->id);
408 :
409 36 : staff_idx_t staffIdx { 0 };
410 36 : const bool isMultiStaffInstrument = instrTemplate->staffCount > 1;
411 36 : const size_t partStaffCount = part->nstaves();
412 :
413 36 : const int templateBracketSpan = std::accumulate(instrTemplate->bracketSpan, instrTemplate->bracketSpan + MAX_STAVES, 0);
414 36 : const bool bracketSpansAllStaves = templateBracketSpan == (int)instrTemplate->staffCount;
415 36 : const int templateBarlineSpan = std::count(instrTemplate->barlineSpan.begin(), instrTemplate->barlineSpan.end(), true);
416 36 : const bool barlineSpansAllStaves = templateBarlineSpan == (int)instrTemplate->staffCount - 1;
417 84 : for (Staff* staff : part->staves()) {
418 : // Create copy, because the original is modified while we are iterating over it
419 48 : std::vector<BracketItem*> brackets = score->brackets(staff->idx());
420 :
421 81 : for (BracketItem* bi : brackets) {
422 33 : if (bi->bracketType() == BracketType::GROUP) {
423 0 : continue;
424 : }
425 33 : score->undo(new RemoveBracket(staff, bi->column(), bi->bracketType(), bi->bracketSpan()));
426 : }
427 48 : staff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, false);
428 :
429 : // Reset template braces & barline spans
430 : // The number of staves could differ from the template
431 : // Use template spans if span doesn't cover whole set of staves (eg. organ), otherwise cover all staves
432 48 : if (partStaffCount > 1) {
433 24 : size_t bracketSpan = bracketSpansAllStaves ? partStaffCount : instrTemplate->bracketSpan[staffIdx];
434 24 : EditStaffBrackets::undoAddBracket(score, staff, 0, instrTemplate->bracket[staffIdx], bracketSpan);
435 :
436 24 : int barlineSpan = instrTemplate->barlineSpan[barlineSpansAllStaves - 1 ? 0 : staffIdx];
437 24 : staff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, barlineSpan);
438 : }
439 :
440 48 : if ((prvSection.isEmpty() || (sg.section != prvSection)) && score->parts().size() > 1) {
441 12 : if (thkBracketStaff && (thkBracketSpan > 1)) {
442 0 : EditStaffBrackets::undoAddBracket(score, thkBracketStaff, 0, BracketType::NORMAL, thkBracketSpan);
443 : }
444 12 : if (!staffIdx) {
445 12 : thkBracketStaff = sg.bracket ? staff : nullptr;
446 12 : thkBracketSpan = 0;
447 : }
448 : }
449 48 : if (sg.bracket && !staffIdx) {
450 36 : thkBracketSpan += static_cast<int>(part->nstaves());
451 : }
452 :
453 48 : if (prvInstrument == -1 || (ii.instrIndex != prvInstrument) || isMultiStaffInstrument) {
454 24 : if (thnBracketStaff && (thnBracketSpan > 1)) {
455 12 : EditStaffBrackets::undoAddBracket(score, thnBracketStaff, 1, BracketType::SQUARE, thnBracketSpan);
456 : }
457 24 : if (ii.instrIndex != prvInstrument || isMultiStaffInstrument) {
458 24 : thnBracketStaff = nullptr;
459 24 : thnBracketSpan = 0;
460 : }
461 : }
462 48 : if (!thnBracketStaff && sg.thinBracket && !isMultiStaffInstrument && !staffIdx) {
463 24 : thnBracketStaff = staff;
464 24 : thnBracketSpan = 0;
465 : }
466 :
467 48 : if (isMultiStaffInstrument) {
468 0 : if (staffIdx < instrTemplate->staffCount) {
469 0 : ++staffIdx;
470 : }
471 0 : prvStaff = nullptr;
472 : } else {
473 48 : if (sg.thinBracket && !staffIdx) {
474 36 : thnBracketSpan += static_cast<int>(part->nstaves());
475 : }
476 48 : if (prvStaff) {
477 36 : const bool newBarlineSpan = prvBarLineSpan && (!prvSection.isEmpty() && (sg.section == prvSection));
478 36 : prvStaff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, newBarlineSpan);
479 : }
480 48 : prvStaff = staff;
481 48 : ++staffIdx;
482 : }
483 48 : prvSection = sg.section;
484 48 : prvBarLineSpan = sg.barLineSpan;
485 48 : prvThnBracket = sg.thinBracket;
486 48 : }
487 :
488 36 : prvInstrument = ii.instrIndex;
489 36 : }
490 :
491 12 : if (thkBracketStaff && (thkBracketSpan > 1)) {
492 12 : EditStaffBrackets::undoAddBracket(score, thkBracketStaff, 0, BracketType::NORMAL, thkBracketSpan);
493 : }
494 12 : if (thnBracketStaff && (thnBracketSpan > 1) && prvThnBracket) {
495 12 : EditStaffBrackets::undoAddBracket(score, thnBracketStaff, 1, BracketType::SQUARE, thnBracketSpan);
496 : }
497 12 : }
498 :
499 : //---------------------------------------------------------
500 : // read
501 : //---------------------------------------------------------
502 :
503 274 : void ScoreOrder::read(XmlReader& reader)
504 : {
505 274 : id = reader.attribute("id");
506 274 : const String sectionId;
507 4230 : while (reader.readNextStartElement()) {
508 3956 : if (reader.name() == "name") {
509 274 : name = TranslatableString("engraving/scoreorder", reader.readText());
510 3682 : } else if (reader.name() == "section") {
511 1691 : readSection(reader);
512 1991 : } else if (reader.name() == "instrument") {
513 347 : readInstrument(reader);
514 1644 : } else if (reader.name() == "family") {
515 1096 : ScoreGroup sg;
516 1096 : sg.family = reader.readText();
517 1096 : sg.section = sectionId;
518 1096 : sg.bracket = false;
519 1096 : sg.barLineSpan = false;
520 1096 : sg.thinBracket = false;
521 1096 : groups.push_back(sg);
522 1644 : } else if (reader.name() == "soloists") {
523 274 : readSoloists(reader, sectionId);
524 274 : } else if (reader.name() == "unsorted") {
525 274 : String group { reader.attribute("group", String()) };
526 :
527 274 : if (!hasGroup(UNSORTED_ID, group)) {
528 274 : groups.push_back(newUnsortedGroup(group, sectionId));
529 : }
530 :
531 274 : reader.skipCurrentElement();
532 274 : } else {
533 0 : reader.unknown();
534 : }
535 : }
536 :
537 274 : String group;
538 274 : if (!hasGroup(UNSORTED_ID, group)) {
539 0 : groups.push_back(newUnsortedGroup(group, id));
540 : }
541 274 : }
542 :
543 : //---------------------------------------------------------
544 : // write
545 : //---------------------------------------------------------
546 :
547 62 : void ScoreOrder::write(XmlWriter& xml) const
548 : {
549 62 : if (!isValid()) {
550 0 : return;
551 : }
552 :
553 186 : xml.startElement("Order", { { "id", id } });
554 62 : xml.tag("name", name.str);
555 :
556 131 : for (const auto& p : instrumentMap) {
557 207 : xml.startElement("instrument", { { "id", p.first } });
558 276 : xml.tag("family", { { "id", p.second.id } }, p.second.name);
559 69 : xml.endElement();
560 : }
561 :
562 62 : String section { u"" };
563 1868 : for (const ScoreGroup& sg : groups) {
564 1806 : if (sg.section != section) {
565 452 : if (!section.isEmpty()) {
566 328 : xml.endElement();
567 : }
568 452 : if (!sg.section.isEmpty()) {
569 2232 : xml.startElement("section", { { "id", sg.section },
570 372 : { "brackets", sg.bracket ? "true" : "false" },
571 372 : { "barLineSpan", sg.barLineSpan ? "true" : "false" },
572 744 : { "thinBrackets", sg.thinBracket ? "true" : "false" } });
573 : }
574 452 : section = sg.section;
575 : }
576 1806 : if (sg.family == SOLOISTS_ID) {
577 62 : xml.tag("soloists");
578 1744 : } else if (sg.notUnsorted) {
579 1488 : xml.tag("family", sg.family);
580 256 : } else if (sg.unsorted.isEmpty()) {
581 62 : xml.tag("unsorted");
582 : } else {
583 582 : xml.tag("unsorted", { { "group", sg.unsorted } });
584 : }
585 : }
586 62 : if (!section.isEmpty()) {
587 44 : xml.endElement();
588 : }
589 62 : xml.endElement();
590 1944 : }
591 :
592 : //---------------------------------------------------------
593 : // updateInstruments
594 : //---------------------------------------------------------
595 :
596 62 : void ScoreOrder::updateInstruments(const Score* score)
597 : {
598 133 : for (const Part* part : score->parts()) {
599 71 : InstrumentIndex ii = searchTemplateIndexForId(part->instrument()->id());
600 71 : if (!ii.instrTemplate || !ii.instrTemplate->family) {
601 0 : continue;
602 : }
603 :
604 71 : InstrumentOverwrite io;
605 71 : io.id = ii.instrTemplate->family->id;
606 71 : io.name = ii.instrTemplate->family->name;
607 71 : instrumentMap.emplace(ii.instrTemplate->id, std::move(io));
608 71 : }
609 62 : }
610 : }
|