LCOV - code coverage report
Current view: top level - src/engraving/dom - scoreorder.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 81.0 % 331 268
Test Date: 2026-11-05 04:07:02

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

Generated by: LCOV version 2.0-1