1 //=============================================================================
2 // MuseScore
3 // Music Composition & Notation
4 //
5 // Copyright (C) 2008-2011 Werner Schweer
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License version 2
9 // as published by the Free Software Foundation and appearing in
10 // the file LICENCE.GPL
11 //=============================================================================
12
13 #include <iostream>
14
15 #include "scoreOrder.h"
16 #include "score.h"
17 #include "part.h"
18 #include "staff.h"
19 #include "bracketItem.h"
20 #include "instrtemplate.h"
21 #include "undo.h"
22 #include "xml.h"
23
24 namespace Ms {
25
26 ScoreOrderList scoreOrders;
27
28 //---------------------------------------------------------
29 // ScoreGroup
30 //---------------------------------------------------------
31
ScoreGroup(const QString id,const QString section,const QString unsorted,bool soloists)32 ScoreGroup::ScoreGroup(const QString id, const QString section, const QString unsorted, bool soloists)
33 : _id(id), _section(section), _soloists(soloists), _unsorted(unsorted)
34 {
35 _index = counter++;
36 bracket = false;
37 showSystemMarkings = false;
38 barLineSpan = true;
39 thinBracket = true;
40 }
41
~ScoreGroup()42 ScoreGroup::~ScoreGroup()
43 {
44 }
45
46 //---------------------------------------------------------
47 // clone
48 //---------------------------------------------------------
49
clone()50 ScoreGroup* ScoreGroup::clone()
51 {
52 ScoreGroup* sg = new ScoreGroup(_id, _section, _unsorted, _soloists);
53 sg->bracket = bracket;
54 sg->showSystemMarkings = showSystemMarkings;
55 sg->barLineSpan = barLineSpan;
56 sg->thinBracket = thinBracket;
57 return sg;
58 }
59
60 //---------------------------------------------------------
61 // write
62 //---------------------------------------------------------
63
write(XmlWriter & xml) const64 void ScoreGroup::write(XmlWriter& xml) const
65 {
66 if (_soloists)
67 xml.tagE("soloists");
68 else if (_unsorted.isNull())
69 xml.tag("family", _id);
70 else if (_unsorted.isEmpty())
71 xml.tagE("unsorted");
72 else
73 xml.tagE(QString("unsorted group=\"%1\"").arg(_unsorted));
74 }
75
76 //---------------------------------------------------------
77 // id
78 //---------------------------------------------------------
79
id() const80 const QString& ScoreGroup::id() const
81 {
82 return _id;
83 }
84
85 //---------------------------------------------------------
86 // section
87 //---------------------------------------------------------
88
section() const89 const QString& ScoreGroup::section() const
90 {
91 return _section;
92 }
93
94 //---------------------------------------------------------
95 // isSoloists
96 //---------------------------------------------------------
97
isSoloists() const98 bool ScoreGroup::isSoloists() const
99 {
100 return _soloists;
101 }
102
103 //---------------------------------------------------------
104 // isUnsorted
105 //---------------------------------------------------------
106
isUnsorted(const QString & group) const107 bool ScoreGroup::isUnsorted(const QString& group) const
108 {
109 if (_unsorted.isNull())
110 return false;
111 return group.isNull() || (_unsorted == group);
112 }
113
114 //---------------------------------------------------------
115 // index
116 //---------------------------------------------------------
117
index() const118 int ScoreGroup::index() const
119 {
120 return _index;
121 }
122
123 //---------------------------------------------------------
124 // dump
125 //---------------------------------------------------------
126
dump() const127 void ScoreGroup::dump() const
128 {
129 QString fullName = _section.isEmpty() ? _id : QString("%1/%2").arg(_section, _id);
130 std::cout << " " << _index << " : ";
131 if (_soloists)
132 std::cout << fullName.toStdString();
133 else if (!_unsorted.isNull()) {
134 std::cout << fullName.toStdString();
135 if (!_unsorted.isEmpty())
136 std::cout << ", group = " << _unsorted.toStdString();
137 }
138 else
139 std::cout << fullName.toStdString();
140
141 std::cout << " : "
142 << (showSystemMarkings ? "" : "no ") << "showSystemMarkings, "
143 << (barLineSpan ? "" : "no ") << "barLineSpan, "
144 << (thinBracket ? "" : "no ") << "thinBrackets, "
145 << (bracket ? "" : " no ") << "brackets";
146 std::cout << std::endl;
147 }
148
149 int ScoreGroup::counter { 0 };
150
151 //---------------------------------------------------------
152 // InstrumentOverwrite
153 //---------------------------------------------------------
154
InstrumentOverwrite(const QString instrId,const QString instrName)155 InstrumentOverwrite::InstrumentOverwrite(const QString instrId, const QString instrName)
156 {
157 id = instrId;
158 name = instrName;
159 }
160
161 //---------------------------------------------------------
162 // ScoreOrder
163 //---------------------------------------------------------
164
ScoreOrder(const QString orderId,const QString name)165 ScoreOrder::ScoreOrder(const QString orderId, const QString name)
166 {
167 _id = orderId;
168 init();
169 _name = name.isEmpty() ? orderId : name;
170 }
171
~ScoreOrder()172 ScoreOrder::~ScoreOrder()
173 {
174 while (!groups.isEmpty())
175 delete groups.takeFirst();
176 }
177
178 //---------------------------------------------------------
179 // clone
180 //---------------------------------------------------------
181
clone()182 ScoreOrder* ScoreOrder::clone()
183 {
184 ScoreOrder* order = new ScoreOrder(_id, _name);
185 for (ScoreGroup* sg : groups)
186 {
187 ScoreGroup* sgc = sg->clone();
188 if (sg->isSoloists())
189 order->_soloists = sgc;
190 if (sg->isUnsorted())
191 order->_unsorted = sgc;
192 order->groups.append(sgc);
193 }
194 order->instrumentMap = instrumentMap;
195 order->_groupMultiplier = _groupMultiplier;
196 order->_customized = true;
197 return order;
198 }
199
200 //---------------------------------------------------------
201 // init
202 //---------------------------------------------------------
203
init()204 void ScoreOrder::init()
205 {
206 _name = _id;
207 _soloists = nullptr;
208 _unsorted = nullptr;
209 _groupMultiplier = 1;
210 _customized = false;
211 if (!isCustom()) {
212 for (auto ig : instrumentGroups)
213 _groupMultiplier += ig->instrumentTemplates.size();
214 }
215
216 while (!groups.isEmpty())
217 delete groups.takeFirst();
218 }
219
220 //---------------------------------------------------------
221 // readBoolAttribute
222 //---------------------------------------------------------
223
readBoolAttribute(XmlReader & e,const char * name,bool defvalue)224 bool ScoreOrder::readBoolAttribute(XmlReader& e, const char* name, bool defvalue)
225 {
226 if (!e.hasAttribute(name))
227 return defvalue;
228 QString attr { e.attribute(name) };
229 if (attr.toLower() == "false")
230 return false;
231 if (attr.toLower() == "true")
232 return true;
233 qDebug("invalid value \"%s\" for attribute \"%s\", using default \"%d\"", qPrintable(attr), qPrintable(name), defvalue);
234 return defvalue;
235 }
236
237 //---------------------------------------------------------
238 // readName
239 //---------------------------------------------------------
240
readName(XmlReader & e)241 void ScoreOrder::readName(XmlReader& e)
242 {
243 _name = qApp->translate("OrderXML", e.readElementText().toUtf8().data());
244 }
245
246 //---------------------------------------------------------
247 // readInstrument
248 //---------------------------------------------------------
249
readInstrument(XmlReader & e)250 void ScoreOrder::readInstrument(XmlReader& e)
251 {
252 QString instrumentId { e.attribute("id") };
253 if (!searchTemplate(instrumentId)) {
254 qDebug("cannot find instrument templates for <%s>", qPrintable(instrumentId));
255 e.skipCurrentElement();
256 return;
257 }
258 while (e.readNextStartElement()) {
259 const QStringRef& tag(e.name());
260 if (tag == "family") {
261 const QString id { e.attribute("id") };
262 const QString name = qApp->translate("OrderXML", e.readElementText().toUtf8().data());
263 instrumentMap.insert(instrumentId, InstrumentOverwrite(id, name));
264 }
265 else {
266 e.unknown();
267 }
268 }
269 }
270
271 //---------------------------------------------------------
272 // readSoloists
273 //---------------------------------------------------------
274
readSoloists(XmlReader & e,const QString section)275 void ScoreOrder::readSoloists(XmlReader& e, const QString section)
276 {
277 e.skipCurrentElement();
278 if (_soloists)
279 return;
280 _soloists = new ScoreGroup(QString("<soloists>"), section, QString(), true);
281 groups.append(_soloists);
282 }
283
284 //---------------------------------------------------------
285 // readUnsorted
286 //---------------------------------------------------------
287
readUnsorted(XmlReader & e,const QString section,bool br,bool ssm,bool bls,bool tbr)288 void ScoreOrder::readUnsorted(XmlReader& e, const QString section, bool br, bool ssm, bool bls, bool tbr)
289 {
290 QString group { e.attribute("group", QString("")) };
291 e.skipCurrentElement();
292 for (auto sg : groups) {
293 if (sg->isUnsorted(group))
294 return;
295 };
296 ScoreGroup* sg = new ScoreGroup(QString("<unsorted>"), section, group);
297 sg->bracket = br;
298 sg->showSystemMarkings = ssm;
299 sg->barLineSpan = bls;
300 sg->thinBracket = tbr;
301 groups.append(sg);
302 if (!_unsorted && group.isEmpty())
303 _unsorted = sg;
304 }
305
306 //---------------------------------------------------------
307 // readFamily
308 //---------------------------------------------------------
309
readFamily(XmlReader & e,const QString section,bool br,bool ssm,bool bls,bool tbr)310 void ScoreOrder::readFamily(XmlReader& e, const QString section, bool br, bool ssm, bool bls, bool tbr)
311 {
312 const QString id { e.readElementText().toUtf8().data() };
313 for (auto sg : groups) {
314 if (sg->id() == id)
315 return;
316 }
317 ScoreGroup* sg = new ScoreGroup(id, section);
318 sg->bracket = br;
319 sg->showSystemMarkings = ssm;
320 sg->barLineSpan = bls;
321 sg->thinBracket = tbr;
322 groups.append(sg);
323 }
324
325 //---------------------------------------------------------
326 // readSection
327 //---------------------------------------------------------
328
readSection(XmlReader & e)329 void ScoreOrder::readSection(XmlReader& e)
330 {
331 QString id { e.attribute("id") };
332 bool ssm = readBoolAttribute(e, "showSystemMarkings", false);
333 bool bls = readBoolAttribute(e, "barLineSpan", true );
334 bool tbr = readBoolAttribute(e, "thinBrackets", true );
335 while (e.readNextStartElement()) {
336 const QStringRef& tag(e.name());
337 if (tag == "family")
338 readFamily(e, id, true, ssm, bls, tbr);
339 else if (tag == "unsorted")
340 readUnsorted(e, id, true, ssm, bls, tbr);
341 else
342 e.unknown();
343 }
344 }
345
346 //---------------------------------------------------------
347 // getFamilyName
348 //---------------------------------------------------------
349
getFamilyName(const InstrumentTemplate * instrTemplate,bool soloist) const350 QString ScoreOrder::getFamilyName(const InstrumentTemplate* instrTemplate, bool soloist) const
351 {
352 if (!instrTemplate)
353 return QString("<unsorted>");
354 if (soloist)
355 return QString("<soloists>");
356 else if (instrumentMap.contains(instrTemplate->id))
357 return instrumentMap[instrTemplate->id].id;
358 else if (instrTemplate->family)
359 return instrTemplate->family->id;
360 else
361 return QString("<unsorted>");
362 }
363
364 //---------------------------------------------------------
365 // createUnsortedGroup
366 //---------------------------------------------------------
367
createUnsortedGroup()368 void ScoreOrder::createUnsortedGroup()
369 {
370 _unsorted = new ScoreGroup(QString("<unsorted>"), "", "");
371 _unsorted->bracket = false;
372 _unsorted->showSystemMarkings = false;
373 _unsorted->barLineSpan = false;
374 _unsorted->thinBracket = false;
375 groups.append(_unsorted);
376 }
377
378 //---------------------------------------------------------
379 // getId
380 //---------------------------------------------------------
381
getId() const382 QString ScoreOrder::getId() const
383 {
384 if (_customized)
385 return QString("%1-%2").arg(_id).arg(reinterpret_cast<quintptr>(this), 0, 16);
386 else
387 return _id;
388 }
389
390 //---------------------------------------------------------
391 // getName
392 //---------------------------------------------------------
393
getName() const394 QString ScoreOrder::getName() const
395 {
396 if (isCustom())
397 return QObject::tr("Custom");
398 return _name;
399 }
400
401 //---------------------------------------------------------
402 // getFullName
403 //---------------------------------------------------------
404
getFullName() const405 QString ScoreOrder::getFullName() const
406 {
407 if (_customized)
408 return QObject::tr("%1 (Customized)").arg(_name);
409 else
410 return getName();
411 }
412
413 //---------------------------------------------------------
414 // isCustom
415 //---------------------------------------------------------
416
isCustom() const417 bool ScoreOrder::isCustom() const
418 {
419 return _id == QString("<custom>");
420 }
421
422 //---------------------------------------------------------
423 // isCustomized
424 //---------------------------------------------------------
425
isCustomized() const426 bool ScoreOrder::isCustomized() const
427 {
428 return _customized;
429 }
430
431 //---------------------------------------------------------
432 // setCustomized
433 //---------------------------------------------------------
434
setCustomized()435 void ScoreOrder::setCustomized()
436 {
437 if (!isCustom())
438 _customized = true;
439 }
440
441 //---------------------------------------------------------
442 // getGroup
443 //---------------------------------------------------------
444
getGroup(const QString family,const QString instrumentGroup) const445 ScoreGroup* ScoreOrder::getGroup(const QString family, const QString instrumentGroup) const
446 {
447 if (family.isEmpty())
448 return _unsorted;
449
450 ScoreGroup* unsorted { nullptr };
451 for (ScoreGroup* sg : groups) {
452 if (!sg->isUnsorted() && (sg->id() == family))
453 return sg;
454 if (sg->isUnsorted(instrumentGroup))
455 unsorted = sg;
456 }
457 return unsorted ? unsorted : _unsorted;
458 }
459
getGroup(const QString id,const bool soloist) const460 ScoreGroup* ScoreOrder::getGroup(const QString id, const bool soloist) const
461 {
462 InstrumentIndex ii = searchTemplateIndexForId(id);
463 if (!ii.instrTemplate)
464 return _unsorted;
465
466 QString family { getFamilyName(ii.instrTemplate, soloist) };
467 return getGroup(family, instrumentGroups[ii.groupIndex]->id);
468 }
469
470 //---------------------------------------------------------
471 // read
472 //---------------------------------------------------------
473
read(XmlReader & e)474 void ScoreOrder::read(XmlReader& e)
475 {
476 init();
477 const QString id { "" };
478 _customized = e.intAttribute("customized");
479 while (e.readNextStartElement()) {
480 const QStringRef& tag(e.name());
481 if (tag == "name")
482 readName(e);
483 else if (tag == "section")
484 readSection(e);
485 else if (tag == "instrument")
486 readInstrument(e);
487 else if (tag == "family")
488 readFamily(e, id, false, false, false, false);
489 else if (tag == "soloists")
490 readSoloists(e, id);
491 else if (tag == "unsorted")
492 readUnsorted(e, id, false, false, false, false);
493 else
494 e.unknown();
495 }
496 if (!_unsorted)
497 createUnsortedGroup();
498 }
499
500 //---------------------------------------------------------
501 // write
502 //---------------------------------------------------------
503
write(XmlWriter & xml) const504 void ScoreOrder::write(XmlWriter& xml) const
505 {
506 if (isCustom())
507 return;
508
509 xml.stag(QString("Order id=\"%1\" customized=\"%2\"").arg(_id).arg(_customized));
510 xml.tag("name", _name);
511
512 QMapIterator<QString, InstrumentOverwrite> i(instrumentMap);
513 while (i.hasNext()) {
514 i.next();
515 xml.stag(QString("instrument id=\"%1\"").arg(i.key()));
516 xml.tag(QString("family id=\"%1\"").arg(i.value().id), i.value().name);
517 xml.etag();
518 }
519
520 QString section { "" };
521 for (ScoreGroup* sg : groups)
522 {
523 if (sg->section() != section)
524 {
525 if (!section.isEmpty())
526 xml.etag();
527 if (!sg->section().isEmpty())
528 xml.stag(QString("section id=\"%1\" brackets=\"%2\" showSystemMarkings=\"%3\" barLineSpan=\"%4\" thinBrackets=\"%5\"")
529 .arg(sg->section(),
530 sg->bracket ? "true" : "false",
531 sg->showSystemMarkings ? "true" : "false",
532 sg->barLineSpan ? "true" : "false",
533 sg->thinBracket ? "true" : "false"));
534 section = sg->section();
535 }
536 sg->write(xml);
537 }
538 if (!section.isEmpty())
539 xml.etag();
540 xml.etag();
541 }
542
543 //---------------------------------------------------------
544 // instrumentIndex
545 //---------------------------------------------------------
546
instrumentIndex(const QString id,bool soloist) const547 int ScoreOrder::instrumentIndex(const QString id, bool soloist) const
548 {
549 ScoreGroup* sg = getGroup(id, soloist);
550 int groupIndex = sg ? sg->index() : _groupMultiplier;
551 bool unsorted = sg && sg->isUnsorted();
552
553 return _groupMultiplier * groupIndex + (unsorted ? 0 : searchTemplateIndexForId(id).instrIndex);
554 }
555
556 //---------------------------------------------------------
557 // instrumentInUnsortedSection
558 //---------------------------------------------------------
559
instrumentInUnsortedSection(const QString id,bool soloist) const560 bool ScoreOrder::instrumentInUnsortedSection(const QString id, bool soloist) const
561 {
562 return soloist || getGroup(id, soloist)->isUnsorted();
563 }
564
565 //---------------------------------------------------------
566 // updateInstruments
567 //---------------------------------------------------------
568
updateInstruments(const Score * score)569 void ScoreOrder::updateInstruments(const Score* score)
570 {
571 for (Part* part : score->parts())
572 {
573 InstrumentIndex ii = searchTemplateIndexForId(part->instrument()->getId());
574 if (!ii.instrTemplate || !ii.instrTemplate->family)
575 continue;
576
577 InstrumentFamily* family = ii.instrTemplate->family;
578 instrumentMap.insert(ii.instrTemplate->id, InstrumentOverwrite(family->id, family->name));
579 }
580 }
581
582 //---------------------------------------------------------
583 // setBracketsAndBarlines
584 //---------------------------------------------------------
585
setBracketsAndBarlines(Score * score)586 void ScoreOrder::setBracketsAndBarlines(Score* score)
587 {
588 if (isCustom())
589 return;
590
591 ScoreGroup* prvScoreGroup { nullptr };
592 int prvInstrument { 0 };
593 Staff* prvStaff { nullptr };
594
595 Staff* thkBracketStaff { nullptr };
596 Staff* thnBracketStaff { nullptr };
597 int thkBracketSpan { 0 };
598 int thnBracketSpan { 0 };
599
600 for (Part* part : score->parts())
601 {
602 InstrumentIndex ii = searchTemplateIndexForId(part->instrument()->getId());
603 if (!ii.instrTemplate)
604 continue;
605
606 QString family { getFamilyName(ii.instrTemplate, part->soloist()) };
607 ScoreGroup* sg = getGroup(family, instrumentGroups[ii.groupIndex]->id);
608
609 int staffIdx { 0 };
610 bool blockThinBracket { false };
611 for (Staff* staff : *part->staves())
612 {
613 for (BracketItem* bi : staff->brackets())
614 score->undo(new RemoveBracket(staff, bi->column(), bi->bracketType(), bi->bracketSpan()));
615 staff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, 0);
616
617 if (!prvScoreGroup || (sg->section() != prvScoreGroup->section())) {
618 if (thkBracketStaff && (thkBracketSpan > 1))
619 score->undoAddBracket(thkBracketStaff, 0, BracketType::NORMAL, thkBracketSpan);
620 if (sg->bracket && !staffIdx)
621 {
622 thkBracketStaff = sg->bracket ? staff : nullptr;
623 thkBracketSpan = 0;
624 }
625 }
626 if (sg->bracket && !staffIdx)
627 thkBracketSpan += part->nstaves();
628
629 if (!staffIdx || (ii.instrIndex != prvInstrument))
630 {
631 if (thnBracketStaff && (thnBracketSpan > 1))
632 score->undoAddBracket(thnBracketStaff, 1, BracketType::SQUARE, thnBracketSpan);
633 if (ii.instrIndex != prvInstrument)
634 {
635 thnBracketStaff = (sg->thinBracket && !blockThinBracket) ? staff : nullptr;
636 thnBracketSpan = 0;
637 }
638 }
639
640 if (ii.instrTemplate->nstaves() > 1)
641 {
642 blockThinBracket = true;
643 if (ii.instrTemplate->bracket[staffIdx] != BracketType::NO_BRACKET)
644 score->undoAddBracket(staff, 2, ii.instrTemplate->bracket[staffIdx], ii.instrTemplate->bracketSpan[staffIdx]);
645 staff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, ii.instrTemplate->barlineSpan[staffIdx]);
646 if (staffIdx < ii.instrTemplate->nstaves())
647 ++staffIdx;
648 prvStaff = nullptr;
649 }
650 else
651 {
652 if (sg->thinBracket && !staffIdx)
653 thnBracketSpan += part->nstaves();
654 if (prvStaff)
655 prvStaff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, (sg->barLineSpan && (!prvScoreGroup || (sg->section() == prvScoreGroup->section()))));
656 prvStaff = staff;
657 ++staffIdx;
658 }
659 prvScoreGroup = sg;
660 }
661
662 prvInstrument = ii.instrIndex;
663 }
664
665 if (thkBracketStaff && (thkBracketSpan > 1))
666 score->undoAddBracket(thkBracketStaff, 0, BracketType::NORMAL, thkBracketSpan);
667 if (thnBracketStaff && (thnBracketSpan > 1) && prvScoreGroup->thinBracket)
668 score->undoAddBracket(thnBracketStaff, 1, BracketType::SQUARE, thnBracketSpan);
669 }
670
671 //---------------------------------------------------------
672 // isScoreOrder
673 //---------------------------------------------------------
674
isScoreOrder(const QList<int> & indices) const675 bool ScoreOrder::isScoreOrder(const QList<int>& indices) const
676 {
677 if (isCustom())
678 return true;
679
680 int prvIndex { -1 };
681 for (int curIndex : indices)
682 {
683 if (curIndex < prvIndex)
684 return false;
685 prvIndex = curIndex;
686 }
687 return true;
688 }
689
isScoreOrder(const Score * score) const690 bool ScoreOrder::isScoreOrder(const Score* score) const
691 {
692 QList<int> indices;
693 for (Part* part : score->parts())
694 indices << instrumentIndex(part->instrument()->getId(), part->soloist());
695
696 return isScoreOrder(indices);
697 }
698
699 //---------------------------------------------------------
700 // dump
701 //---------------------------------------------------------
702
dump() const703 void ScoreOrder::dump() const
704 {
705 std::cout << " order : " << _id.toStdString() << ", name = " << _name.toStdString() << std::endl;
706 if (instrumentMap.isEmpty()) {
707 std::cout << " no instrument mapping" << std::endl;
708 }
709 else {
710 std::cout << " instrument mapping:" << std::endl;
711 QMapIterator<QString, InstrumentOverwrite> i(instrumentMap);
712 while (i.hasNext()) {
713 i.next();
714 std::cout << " " << i.key().toStdString() << " => " << i.value().id.toStdString() << std::endl;
715 }
716 }
717 std::cout << " sections:" << std::endl;
718 for (auto group : groups)
719 group->dump();
720 }
721
722 //---------------------------------------------------------
723 // ScoreOrderList
724 //---------------------------------------------------------
725
ScoreOrderList()726 ScoreOrderList::ScoreOrderList()
727 {
728 _orders.clear();
729 ScoreOrder* custom = new ScoreOrder(QString("<custom>"), "Custom"); // gets translated later, in `SortOrder::getName()`
730 custom->createUnsortedGroup();
731 addScoreOrder(custom);
732 }
733
~ScoreOrderList()734 ScoreOrderList::~ScoreOrderList()
735 {
736 while (!_orders.isEmpty())
737 delete _orders.takeFirst();
738 }
739
740 //---------------------------------------------------------
741 // append
742 //---------------------------------------------------------
743
append(ScoreOrder * order)744 void ScoreOrderList::append(ScoreOrder* order)
745 {
746 if (_orders.empty() || !_orders.last()->isCustom())
747 _orders.append(order);
748 else
749 _orders.insert(_orders.size()-1, order);
750 }
751
752 //---------------------------------------------------------
753 // findById
754 // searches for a ScoreOrder with specified id
755 // return nullptr if no matching ScoreOrder is found
756 //---------------------------------------------------------
757
findById(const QString & id) const758 ScoreOrder* ScoreOrderList::findById(const QString& id) const
759 {
760 for (ScoreOrder* order : _orders) {
761 if (order->getId() == id)
762 return order;
763 }
764 return nullptr;
765 }
766
767 //---------------------------------------------------------
768 // getById
769 // searches for a ScoreOrder with specified id
770 // create a new ScoreOrder if no matching ScoreOrder is found
771 //---------------------------------------------------------
772
getById(const QString & id)773 ScoreOrder* ScoreOrderList::getById(const QString& id)
774 {
775 ScoreOrder* order = findById(id);
776 if (!order) {
777 order = new ScoreOrder(id);
778 addScoreOrder(order);
779 }
780 return order;
781 }
782
783 //---------------------------------------------------------
784 // findByName
785 // searches for a ScoreOrder with specified name
786 // return nullptr if no matching ScoreOrder is found
787 //---------------------------------------------------------
788
findByName(const QString & name,bool customized)789 ScoreOrder* ScoreOrderList::findByName(const QString& name, bool customized)
790 {
791 ScoreOrder* customizedOrder { nullptr };
792 for (ScoreOrder* order : _orders) {
793 if (order->getName() == name) {
794 if (customized)
795 {
796 if (order->isCustomized())
797 return order;
798 }
799 else
800 {
801 if (order->isCustomized())
802 customizedOrder = order;
803 else
804 return order;
805 }
806 }
807 }
808 return customizedOrder;
809 }
810
811 //---------------------------------------------------------
812 // customScoreOrder
813 // return Custom ScoreOrder
814 //---------------------------------------------------------
815
customScoreOrder() const816 ScoreOrder* ScoreOrderList::customScoreOrder() const
817 {
818 for (ScoreOrder* so : _orders) {
819 if (so->isCustom())
820 return so;
821 }
822 return nullptr; // should never happen, there is always a custom score order.
823 }
824
825 //---------------------------------------------------------
826 // searchScoreOrder
827 // return the index of the ScoreOrder or 0 (= Custom)
828 // if the order is not found.
829 //---------------------------------------------------------
830
getScoreOrderIndex(const ScoreOrder * order) const831 int ScoreOrderList::getScoreOrderIndex(const ScoreOrder* order) const
832 {
833 int index { 0 };
834 for (ScoreOrder* so : _orders)
835 {
836 if (so == order)
837 return index;
838 ++index;
839 }
840 return 0;
841 }
842
843 //---------------------------------------------------------
844 // searchScoreOrder
845 //---------------------------------------------------------
846
searchScoreOrders(const QList<int> & indices) const847 QList<ScoreOrder*> ScoreOrderList::searchScoreOrders(const QList<int>& indices) const
848 {
849 QList<ScoreOrder*> orders;
850 for (ScoreOrder* order : _orders)
851 {
852 if (!order->isCustom() && order->isScoreOrder(indices))
853 orders << order;
854 }
855 return orders;
856 }
857
searchScoreOrders(const Score * score) const858 QList<ScoreOrder*> ScoreOrderList::searchScoreOrders(const Score* score) const
859 {
860 QList<ScoreOrder*> orders;
861 for (Part* part : score->parts()) {
862 QList<int> indices;
863 for (ScoreOrder* order : _orders)
864 {
865 if (order->isCustom())
866 continue;
867 indices << order->instrumentIndex(part->instrument()->getId(), part->soloist());
868 if (order->isScoreOrder(indices))
869 orders << order;
870 }
871 }
872
873 return orders;
874 }
875
876 //---------------------------------------------------------
877 // addScoreOrder
878 //---------------------------------------------------------
879
addScoreOrder(ScoreOrder * order)880 void ScoreOrderList::addScoreOrder(ScoreOrder* order)
881 {
882 if (!order || _orders.contains(order))
883 return;
884
885 if (!order->isCustomized()) {
886 append(order);
887 return;
888 }
889
890 for (int index { 0 }; index < _orders.size(); ++index) {
891 if (_orders[index]->getName() == order->getName()) {
892 _orders.insert(index+1, order);
893 return;
894 }
895 }
896 append(order);
897 order->_customized = false;
898 }
899
900 //---------------------------------------------------------
901 // removeScoreOrder
902 //---------------------------------------------------------
903
removeScoreOrder(ScoreOrder * order)904 void ScoreOrderList::removeScoreOrder(ScoreOrder* order)
905 {
906 if (!order)
907 return;
908 int index { getScoreOrderIndex(order) };
909 if (index)
910 _orders.removeAt(index);
911 }
912
913 //---------------------------------------------------------
914 // read
915 //---------------------------------------------------------
916
read(XmlReader & e)917 void ScoreOrderList::read(XmlReader& e)
918 {
919 while (e.readNextStartElement()) {
920 const QStringRef& tag(e.name());
921 if (tag == "Order")
922 scoreOrders.getById(e.attribute("id"))->read(e);
923 else
924 e.unknown();
925 }
926 }
927
928 //---------------------------------------------------------
929 // write
930 //---------------------------------------------------------
931
write(XmlWriter & xml) const932 void ScoreOrderList::write(XmlWriter& xml) const
933 {
934 for (auto so : _orders)
935 so->write(xml);
936 }
937
938 //---------------------------------------------------------
939 // size
940 //---------------------------------------------------------
941
size() const942 int ScoreOrderList::size() const
943 {
944 return _orders.size();
945 }
946
947 //---------------------------------------------------------
948 // opeator[
949 //---------------------------------------------------------
950
operator [](const int index) const951 ScoreOrder* ScoreOrderList::operator[](const int index) const
952 {
953 return _orders[index];
954 }
955
956 //---------------------------------------------------------
957 // dump
958 //---------------------------------------------------------
959
dump() const960 void ScoreOrderList::dump() const
961 {
962 std::cout << "Dump of ScoreOrders:" << std::endl;
963 for (auto order : _orders)
964 order->dump();
965 }
966
967 //---------------------------------------------------------
968 // loadScoreOrders
969 //---------------------------------------------------------
970
loadScoreOrders(const QString & scoreOrderFileName)971 bool loadScoreOrders(const QString& scoreOrderFileName)
972 {
973 QFile qf(scoreOrderFileName);
974 if (!qf.open(QIODevice::Text | QIODevice::ReadOnly)) {
975 qDebug("cannot load score orders at <%s>", qPrintable(scoreOrderFileName));
976 return false;
977 }
978
979 XmlReader e(&qf);
980 while (e.readNextStartElement()) {
981 if (e.name() == "museScore")
982 scoreOrders.read(e);
983 }
984
985 return true;
986 }
987
988
989 //---------------------------------------------------------
990 // saveScoreOrders
991 //---------------------------------------------------------
992
saveScoreOrders(const QString & scoreOrderFileNameName)993 bool saveScoreOrders(const QString& scoreOrderFileNameName)
994 {
995 QFile qf(scoreOrderFileNameName);
996 if (!qf.open(QIODevice::WriteOnly)) {
997 qDebug("cannot save instrument templates at <%s>", qPrintable(scoreOrderFileNameName));
998 return false;
999 }
1000 XmlWriter xml(0, &qf);
1001 xml << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1002 xml.stag("museScore");
1003 scoreOrders.write(xml);
1004 xml.etag();
1005 qf.close();
1006 return true;
1007 }
1008 }
1009