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