LCOV - code coverage report
Current view: top level - src/engraving/rw/read114 - read114.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 56.9 % 2175 1238
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              : 
      23              : #include "read114.h"
      24              : 
      25              : #include <cmath>
      26              : 
      27              : #include "global/defer.h"
      28              : 
      29              : #include "compat/pageformat.h"
      30              : 
      31              : #include "engravingerrors.h"
      32              : 
      33              : #include "infrastructure/htmlparser.h"
      34              : 
      35              : #include "rw/compat/compatutils.h"
      36              : #include "rw/compat/readchordlisthook.h"
      37              : 
      38              : #include "style/defaultstyle.h"
      39              : #include "style/style.h"
      40              : 
      41              : #include "types/bps.h"
      42              : #include "types/constants.h"
      43              : #include "types/typesconv.h"
      44              : 
      45              : #include "dom/accidental.h"
      46              : #include "dom/barline.h"
      47              : #include "dom/beam.h"
      48              : #include "dom/box.h"
      49              : #include "dom/bracketitem.h"
      50              : #include "dom/breath.h"
      51              : #include "dom/chord.h"
      52              : #include "dom/clef.h"
      53              : #include "dom/drumset.h"
      54              : #include "dom/dynamic.h"
      55              : #include "dom/excerpt.h"
      56              : #include "dom/factory.h"
      57              : #include "dom/fingering.h"
      58              : #include "dom/harmony.h"
      59              : #include "dom/image.h"
      60              : #include "dom/jump.h"
      61              : #include "dom/keysig.h"
      62              : #include "dom/lyrics.h"
      63              : #include "dom/marker.h"
      64              : #include "dom/masterscore.h"
      65              : #include "dom/measure.h"
      66              : #include "dom/measurenumber.h"
      67              : #include "dom/measurerepeat.h"
      68              : #include "dom/mmrest.h"
      69              : #include "dom/note.h"
      70              : #include "dom/ottava.h"
      71              : #include "dom/part.h"
      72              : #include "dom/pedal.h"
      73              : #include "dom/rest.h"
      74              : #include "dom/segment.h"
      75              : #include "dom/sig.h"
      76              : #include "dom/slur.h"
      77              : #include "dom/spacer.h"
      78              : #include "dom/staff.h"
      79              : #include "dom/stafftext.h"
      80              : #include "dom/stafftype.h"
      81              : #include "dom/stringdata.h"
      82              : #include "dom/tempotext.h"
      83              : #include "dom/text.h"
      84              : #include "dom/textline.h"
      85              : #include "dom/timesig.h"
      86              : #include "dom/tremolotwochord.h"
      87              : #include "dom/tremolosinglechord.h"
      88              : #include "dom/trill.h"
      89              : #include "dom/tuplet.h"
      90              : #include "dom/utils.h"
      91              : #include "dom/volta.h"
      92              : 
      93              : #include "../../editing/editstaffbrackets.h"
      94              : #include "../../editing/transpose.h"
      95              : 
      96              : #include "../compat/readchordlisthook.h"
      97              : #include "../compat/readstyle.h"
      98              : #include "../compat/tremolocompat.h"
      99              : #include "../read206/read206.h"
     100              : #include "../read400/tread.h"
     101              : 
     102              : #include "log.h"
     103              : 
     104              : using namespace mu;
     105              : using namespace mu::engraving;
     106              : using namespace mu::engraving::rw;
     107              : using namespace mu::engraving::read400;
     108              : using namespace mu::engraving::read114;
     109              : using namespace mu::engraving::read206;
     110              : using namespace mu::engraving::compat;
     111              : 
     112              : using mu::engraving::compat::TremoloCompat;
     113              : 
     114              : static int g_guitarStrings[] = { 40, 45, 50, 55, 59, 64 };
     115              : static int g_bassStrings[]   = { 28, 33, 38, 43 };
     116              : static int g_violinStrings[] = { 55, 62, 69, 76 };
     117              : static int g_violaStrings[]  = { 48, 55, 62, 69 };
     118              : static int g_celloStrings[]  = { 36, 43, 50, 57 };
     119              : 
     120              : #define MM(x) ((x) / INCH)
     121              : 
     122              : //---------------------------------------------------------
     123              : //   PaperSize
     124              : //---------------------------------------------------------
     125              : 
     126              : struct PaperSize {
     127              :     const char* name;
     128              :     double w, h;              // size in inch
     129          608 :     PaperSize(const char* n, double wi, double hi)
     130          608 :         : name(n), w(wi), h(hi) {}
     131              : };
     132              : 
     133              : static const PaperSize paperSizes114[] = {
     134              :     PaperSize("Custom",    MM(1),    MM(1)),
     135              :     PaperSize("A4",        MM(210),  MM(297)),
     136              :     PaperSize("B5",        MM(176),  MM(250)),
     137              :     PaperSize("Letter",    8.5,      11),
     138              :     PaperSize("Legal",     8.5,      14),
     139              :     PaperSize("Executive", 7.5,      10),
     140              :     PaperSize("A0",        MM(841),  MM(1189)),
     141              :     PaperSize("A1",        MM(594),  MM(841)),
     142              :     PaperSize("A2",        MM(420),  MM(594)),
     143              :     PaperSize("A3",        MM(297),  MM(420)),
     144              :     PaperSize("A5",        MM(148),  MM(210)),
     145              :     PaperSize("A6",        MM(105),  MM(148)),
     146              :     PaperSize("A7",        MM(74),   MM(105)),
     147              :     PaperSize("A8",        MM(52),   MM(74)),
     148              :     PaperSize("A9",        MM(37),   MM(52)),
     149              :     PaperSize("A10",       MM(26),   MM(37)),
     150              :     PaperSize("B0",        MM(1000), MM(1414)),
     151              :     PaperSize("B1",        MM(707),  MM(1000)),
     152              :     PaperSize("B2",        MM(500),  MM(707)),
     153              :     PaperSize("B3",        MM(353),  MM(500)),
     154              :     PaperSize("B4",        MM(250),  MM(353)),
     155              :     PaperSize("B6",        MM(125),  MM(176)),
     156              :     PaperSize("B7",        MM(88),   MM(125)),
     157              :     PaperSize("B8",        MM(62),   MM(88)),
     158              :     PaperSize("B9",        MM(44),   MM(62)),
     159              :     PaperSize("B10",       MM(31),   MM(44)),
     160              :     PaperSize("Comm10E",   MM(105),  MM(241)),
     161              :     PaperSize("DLE",       MM(110),  MM(220)),
     162              :     PaperSize("Folio",     MM(210),  MM(330)),
     163              :     PaperSize("Ledger",    MM(432),  MM(279)),
     164              :     PaperSize("Tabloid",   MM(279),  MM(432)),
     165              :     PaperSize(0,           MM(1),    MM(1))     // mark end of list
     166              : };
     167              : 
     168              : //---------------------------------------------------------
     169              : //   getPaperSize
     170              : //---------------------------------------------------------
     171              : 
     172           26 : static const PaperSize* getPaperSize114(const String& name)
     173              : {
     174           52 :     for (int i = 0; paperSizes114[i].name; ++i) {
     175           52 :         if (name == paperSizes114[i].name) {
     176           26 :             return &paperSizes114[i];
     177              :         }
     178              :     }
     179            0 :     LOGD("unknown paper size");
     180            0 :     return &paperSizes114[0];
     181              : }
     182              : 
     183              : //---------------------------------------------------------
     184              : //   readTextProperties
     185              : //---------------------------------------------------------
     186              : 
     187          241 : static bool readTextProperties(XmlReader& e, ReadContext& ctx, TextBase* t, EngravingItem*)
     188              : {
     189          241 :     const AsciiStringView tag(e.name());
     190          241 :     if (tag == "style") {
     191           73 :         int i = e.readInt();
     192           73 :         TextStyleType ss = TextStyleType::DEFAULT;
     193           73 :         switch (i) {
     194           10 :         case 2:  ss = TextStyleType::TITLE;
     195           10 :             break;
     196            2 :         case 3:  ss = TextStyleType::SUBTITLE;
     197            2 :             break;
     198            2 :         case 4:  ss = TextStyleType::COMPOSER;
     199            2 :             break;
     200            2 :         case 5:  ss = TextStyleType::LYRICIST;
     201            2 :             break;
     202              : 
     203            0 :         case 6:  ss = TextStyleType::LYRICS_ODD;
     204            0 :             break;
     205            0 :         case 7:  ss = TextStyleType::LYRICS_EVEN;
     206            0 :             break;
     207              : 
     208            0 :         case 8:  ss = TextStyleType::FINGERING;
     209            0 :             break;
     210           12 :         case 9:  ss = TextStyleType::INSTRUMENT_LONG;
     211           12 :             break;
     212           12 :         case 10: ss = TextStyleType::INSTRUMENT_SHORT;
     213           12 :             break;
     214            0 :         case 11: ss = TextStyleType::INSTRUMENT_EXCERPT;
     215            0 :             break;
     216              : 
     217            0 :         case 12: ss = TextStyleType::DYNAMICS;
     218            0 :             break;
     219            0 :         case 13: ss = TextStyleType::EXPRESSION;
     220            0 :             break;
     221            0 :         case 14: ss = TextStyleType::TEMPO;
     222            0 :             break;
     223            0 :         case 15: ss = TextStyleType::METRONOME;
     224            0 :             break;
     225            1 :         case 16: ss = TextStyleType::FOOTER;
     226            1 :             break;                                      // TextStyleType::COPYRIGHT
     227           26 :         case 17: ss = TextStyleType::MEASURE_NUMBER;
     228           26 :             break;
     229            0 :         case 18: ss = TextStyleType::FOOTER;
     230            0 :             break;                                     // TextStyleType::PAGE_NUMBER_ODD
     231            0 :         case 19: ss = TextStyleType::FOOTER;
     232            0 :             break;                                     // TextStyleType::PAGE_NUMBER_EVEN
     233            0 :         case 20: ss = TextStyleType::TRANSLATOR;
     234            0 :             break;
     235            0 :         case 21: ss = TextStyleType::TUPLET;
     236            0 :             break;
     237              : 
     238            0 :         case 22: ss = TextStyleType::SYSTEM;
     239            0 :             break;
     240            2 :         case 23: ss = TextStyleType::STAFF;
     241            2 :             break;
     242            0 :         case 24: ss = TextStyleType::HARMONY_A;
     243            0 :             break;
     244            0 :         case 25: ss = TextStyleType::REHEARSAL_MARK;
     245            0 :             break;
     246            0 :         case 26: ss = TextStyleType::REPEAT_LEFT;
     247            0 :             break;
     248            0 :         case 27: ss = TextStyleType::VOLTA;
     249            0 :             break;
     250            0 :         case 28: ss = TextStyleType::FRAME;
     251            0 :             break;
     252            2 :         case 29: ss = TextStyleType::TEXTLINE;
     253            2 :             break;
     254            0 :         case 30: ss = TextStyleType::GLISSANDO;
     255            0 :             break;
     256            0 :         case 31: ss = TextStyleType::STRING_NUMBER;
     257            0 :             break;
     258              : 
     259            2 :         case 32: ss = TextStyleType::OTTAVA;
     260            2 :             break;
     261              : //??                  case 33: ss = TextStyleName::BENCH;   break;
     262            0 :         case 34: ss = TextStyleType::HEADER;
     263            0 :             break;
     264            0 :         case 35: ss = TextStyleType::FOOTER;
     265            0 :             break;
     266            0 :         case 0:
     267              :         default:
     268            0 :             LOGD("style %d invalid", i);
     269            0 :             ss = TextStyleType::DEFAULT;
     270            0 :             break;
     271              :         }
     272           73 :         t->initTextStyleType(ss);
     273          168 :     } else if (tag == "subtype") {
     274           73 :         e.skipCurrentElement();
     275           95 :     } else if (tag == "html-data") {
     276           47 :         String ss = e.readXml();
     277           47 :         String s  = HtmlParser::parse(ss);
     278              : // LOGD("html-data <%s>", muPrintable(s));
     279           47 :         t->setXmlText(s);
     280           95 :     } else if (tag == "foregroundColor") { // same as "color" ?
     281            0 :         e.skipCurrentElement();
     282           48 :     } else if (tag == "frame") {
     283           47 :         t->setFrameType(e.readBool() ? FrameType::RECTANGLE : FrameType::NO_FRAME);
     284           47 :         t->setPropertyFlags(Pid::FRAME_TYPE, PropertyFlags::UNSTYLED);
     285            1 :     } else if (tag == "halign") {
     286            0 :         Align align = t->align();
     287            0 :         align.horizontal = TConv::fromXml(e.readAsciiText(), AlignH::LEFT);
     288            0 :         t->setAlign(align);
     289            0 :         t->setPropertyFlags(Pid::ALIGN, PropertyFlags::UNSTYLED);
     290            0 :         t->setPosition(align.horizontal);
     291            0 :         t->setPropertyFlags(Pid::POSITION, PropertyFlags::UNSTYLED);
     292            1 :     } else if (tag == "valign") {
     293            0 :         Align align = t->align();
     294            0 :         align.vertical = TConv::fromXml(e.readAsciiText(), AlignV::TOP);
     295            0 :         t->setAlign(align);
     296            0 :         t->setPropertyFlags(Pid::ALIGN, PropertyFlags::UNSTYLED);
     297            1 :     } else if (tag == "rxoffset") {
     298            0 :         e.readText();
     299            1 :     } else if (tag == "ryoffset") {
     300            0 :         e.readText();
     301            1 :     } else if (tag == "yoffset") {
     302            0 :         e.readText();
     303            1 :     } else if (tag == "systemFlag") {
     304            0 :         e.readText();
     305            1 :     } else if (!read400::TRead::readTextProperties(t, e, ctx)) {
     306            0 :         return false;
     307              :     }
     308          241 :     t->setOffset(PointF());       // ignore user offsets
     309          241 :     t->setAutoplace(true);
     310              : 
     311          241 :     t->setPosition(t->align().horizontal);
     312          241 :     if (t->position() != t->propertyDefault(Pid::POSITION).value<AlignH>()) {
     313            0 :         t->setPropertyFlags(Pid::POSITION, PropertyFlags::UNSTYLED);
     314              :     }
     315              : 
     316          241 :     return true;
     317              : }
     318              : 
     319              : //---------------------------------------------------------
     320              : //   readText114
     321              : //---------------------------------------------------------
     322              : 
     323           45 : static void readText114(XmlReader& e, ReadContext& ctx, TextBase* t, EngravingItem* be)
     324              : {
     325          271 :     while (e.readNextStartElement()) {
     326          181 :         if (!readTextProperties(e, ctx, t, be)) {
     327            0 :             e.unknown();
     328              :         }
     329              :     }
     330           45 : }
     331              : 
     332              : //---------------------------------------------------------
     333              : //   readAccidental
     334              : //---------------------------------------------------------
     335              : 
     336           26 : static void readAccidental(Accidental* a, XmlReader& e, ReadContext& ctx)
     337              : {
     338           78 :     while (e.readNextStartElement()) {
     339           52 :         const AsciiStringView tag(e.name());
     340           52 :         if (tag == "bracket") {
     341           26 :             int i = e.readInt();
     342           26 :             if (i == 0 || i == 1) {
     343           26 :                 a->setBracket(AccidentalBracket(i));
     344              :             }
     345           26 :         } else if (tag == "subtype") {
     346           26 :             String text(e.readText());
     347              :             bool isInt;
     348           26 :             int i = text.toInt(&isInt);
     349           26 :             if (isInt) {
     350            0 :                 a->setBracket(i & 0x8000 ? AccidentalBracket::PARENTHESIS : AccidentalBracket::BRACKET);
     351            0 :                 i &= ~0x8000;
     352              :                 AccidentalType at;
     353            0 :                 switch (i) {
     354            0 :                 case 0:
     355            0 :                     at = AccidentalType::NONE;
     356            0 :                     break;
     357            0 :                 case 6:
     358            0 :                     a->setBracket(AccidentalBracket::PARENTHESIS);
     359              :                 // fall through
     360            0 :                 case 1:
     361              :                 case 11:
     362            0 :                     at = AccidentalType::SHARP;
     363            0 :                     break;
     364            0 :                 case 7:
     365            0 :                     a->setBracket(AccidentalBracket::PARENTHESIS);
     366              :                 // fall through
     367            0 :                 case 2:
     368              :                 case 12:
     369            0 :                     at = AccidentalType::FLAT;
     370            0 :                     break;
     371            0 :                 case 8:
     372            0 :                     a->setBracket(AccidentalBracket::PARENTHESIS);
     373              :                 // fall through
     374            0 :                 case 3:
     375              :                 case 13:
     376            0 :                     at = AccidentalType::SHARP2;
     377            0 :                     break;
     378            0 :                 case 9:
     379            0 :                     a->setBracket(AccidentalBracket::PARENTHESIS);
     380              :                 // fall through
     381            0 :                 case 4:
     382              :                 case 14:
     383            0 :                     at = AccidentalType::FLAT2;
     384            0 :                     break;
     385            0 :                 case 10:
     386            0 :                     a->setBracket(AccidentalBracket::PARENTHESIS);
     387              :                 // fall through
     388            0 :                 case 5:
     389              :                 case 15:
     390            0 :                     at = AccidentalType::NATURAL;
     391            0 :                     break;
     392            0 :                 case 16:
     393            0 :                     at = AccidentalType::FLAT_SLASH;
     394            0 :                     break;
     395            0 :                 case 17:
     396            0 :                     at = AccidentalType::FLAT_SLASH2;
     397            0 :                     break;
     398            0 :                 case 18:
     399            0 :                     at = AccidentalType::MIRRORED_FLAT2;
     400            0 :                     break;
     401            0 :                 case 19:
     402            0 :                     at = AccidentalType::MIRRORED_FLAT;
     403            0 :                     break;
     404            0 :                 case 20:
     405            0 :                     at = AccidentalType::NONE;                //AccidentalType::MIRRORED_FLAT_SLASH;
     406            0 :                     break;
     407            0 :                 case 21:
     408            0 :                     at = AccidentalType::NONE;                //AccidentalType::FLAT_FLAT_SLASH;
     409            0 :                     break;
     410            0 :                 case 22:
     411            0 :                     at = AccidentalType::SHARP_SLASH;
     412            0 :                     break;
     413            0 :                 case 23:
     414            0 :                     at = AccidentalType::SHARP_SLASH2;
     415            0 :                     break;
     416            0 :                 case 24:
     417            0 :                     at = AccidentalType::SHARP_SLASH3;
     418            0 :                     break;
     419            0 :                 case 25:
     420            0 :                     at = AccidentalType::SHARP_SLASH4;
     421            0 :                     break;
     422            0 :                 case 26:
     423            0 :                     at = AccidentalType::SHARP_ARROW_UP;
     424            0 :                     break;
     425            0 :                 case 27:
     426            0 :                     at = AccidentalType::SHARP_ARROW_DOWN;
     427            0 :                     break;
     428            0 :                 case 28:
     429            0 :                     at = AccidentalType::NONE;                //AccidentalType::SHARP_ARROW_BOTH;
     430            0 :                     break;
     431            0 :                 case 29:
     432            0 :                     at = AccidentalType::FLAT_ARROW_UP;
     433            0 :                     break;
     434            0 :                 case 30:
     435            0 :                     at = AccidentalType::FLAT_ARROW_DOWN;
     436            0 :                     break;
     437            0 :                 case 31:
     438            0 :                     at = AccidentalType::NONE;                //AccidentalType::FLAT_ARROW_BOTH;
     439            0 :                     break;
     440            0 :                 case 32:
     441            0 :                     at = AccidentalType::NATURAL_ARROW_UP;
     442            0 :                     break;
     443            0 :                 case 33:
     444            0 :                     at = AccidentalType::NATURAL_ARROW_DOWN;
     445            0 :                     break;
     446            0 :                 case 34:
     447            0 :                     at = AccidentalType::NONE;                //AccidentalType::NATURAL_ARROW_BOTH;
     448            0 :                     break;
     449            0 :                 default:
     450            0 :                     at = AccidentalType::NONE;
     451            0 :                     break;
     452              :                 }
     453            0 :                 a->setAccidentalType(at);
     454              :             } else {
     455              :                 static const std::map<String, AccidentalType> accMap = {
     456            0 :                     { u"none", AccidentalType::NONE }, { u"sharp", AccidentalType::SHARP },
     457            0 :                     { u"flat", AccidentalType::FLAT }, { u"natural", AccidentalType::NATURAL },
     458            0 :                     { u"double sharp", AccidentalType::SHARP2 }, { u"double flat", AccidentalType::FLAT2 },
     459            0 :                     { u"flat-slash", AccidentalType::FLAT_SLASH }, { u"flat-slash2", AccidentalType::FLAT_SLASH2 },
     460            0 :                     { u"mirrored-flat2", AccidentalType::MIRRORED_FLAT2 }, { u"mirrored-flat", AccidentalType::MIRRORED_FLAT },
     461            0 :                     { u"sharp-slash", AccidentalType::SHARP_SLASH }, { u"sharp-slash2", AccidentalType::SHARP_SLASH2 },
     462            0 :                     { u"sharp-slash3", AccidentalType::SHARP_SLASH3 }, { u"sharp-slash4", AccidentalType::SHARP_SLASH4 },
     463            0 :                     { u"sharp arrow up", AccidentalType::SHARP_ARROW_UP }, { u"sharp arrow down", AccidentalType::SHARP_ARROW_DOWN },
     464            0 :                     { u"flat arrow up", AccidentalType::FLAT_ARROW_UP }, { u"flat arrow down", AccidentalType::FLAT_ARROW_DOWN },
     465            0 :                     { u"natural arrow up", AccidentalType::NATURAL_ARROW_UP },
     466            0 :                     { u"natural arrow down", AccidentalType::NATURAL_ARROW_DOWN },
     467            0 :                     { u"sori", AccidentalType::SORI }, { u"koron", AccidentalType::KORON }
     468           50 :                 };
     469           26 :                 auto it = accMap.find(text);
     470           26 :                 if (it == accMap.end()) {
     471            5 :                     LOGD("invalid type %s", muPrintable(text));
     472            5 :                     a->setAccidentalType(AccidentalType::NONE);
     473              :                 } else {
     474           21 :                     a->setAccidentalType(it->second);
     475              :                 }
     476              :             }
     477           26 :         } else if (tag == "role") {
     478            0 :             AccidentalRole r = AccidentalRole(e.readInt());
     479            0 :             if (r == AccidentalRole::AUTO || r == AccidentalRole::USER) {
     480            0 :                 a->setRole(r);
     481              :             }
     482            0 :         } else if (tag == "small") {
     483            0 :             a->setSmall(e.readInt());
     484            0 :         } else if (tag == "offset") {
     485            0 :             e.skipCurrentElement();       // ignore manual layout in older scores
     486            0 :         } else if (TRead::readItemProperties(a, e, ctx)) {
     487              :         } else {
     488            0 :             e.unknown();
     489              :         }
     490              :     }
     491           27 : }
     492              : 
     493              : //---------------------------------------------------------
     494              : //   readFingering114
     495              : //---------------------------------------------------------
     496              : 
     497            2 : static void readFingering114(XmlReader& e, Fingering* fing)
     498              : {
     499            2 :     bool isStringNumber = false;
     500           15 :     while (e.readNextStartElement()) {
     501           13 :         const AsciiStringView tag(e.name());
     502              : 
     503           13 :         if (tag == "html-data") {
     504            2 :             auto htmlDdata = HtmlParser::parse(e.readXml());
     505            2 :             fing->setXmlText(htmlDdata);
     506           13 :         } else if (tag == "subtype") {
     507            2 :             auto subtype = e.readText();
     508            2 :             if (subtype == "StringNumber") {
     509            1 :                 isStringNumber = true;
     510            1 :                 fing->setProperty(Pid::TEXT_STYLE, TextStyleType::STRING_NUMBER);
     511            1 :                 fing->setPropertyFlags(Pid::TEXT_STYLE, PropertyFlags::UNSTYLED);
     512              :             }
     513           11 :         } else if (tag == "frame") {
     514            2 :             auto frame = e.readInt();
     515            2 :             if (frame) {
     516            1 :                 if (isStringNumber) {
     517              :                     // default value is circle for string number, rectangle is set in <circle> tag
     518            1 :                     fing->setFrameType(FrameType::CIRCLE);
     519              :                 } else {
     520              :                     // default value is rectangle for string number, circle is set in <circle> tag
     521            0 :                     fing->setFrameType(FrameType::RECTANGLE);
     522              :                 }
     523              :             } else {
     524            1 :                 fing->setFrameType(FrameType::NO_FRAME);
     525              :             }
     526            7 :         } else if (tag == "circle") {
     527            0 :             auto circle = e.readInt();
     528            0 :             if (circle) {
     529            0 :                 fing->setFrameType(FrameType::CIRCLE);
     530              :             } else {
     531            0 :                 fing->setFrameType(FrameType::RECTANGLE);
     532              :             }
     533              :         } else {
     534            7 :             e.skipCurrentElement();
     535              :         }
     536              :     }
     537            2 : }
     538              : 
     539              : //---------------------------------------------------------
     540              : //   readNote
     541              : //---------------------------------------------------------
     542              : 
     543          439 : static void readNote(Note* note, XmlReader& e, ReadContext& ctx)
     544              : {
     545          439 :     bool hasAccidental = false; // used for userAccidental backward compatibility
     546              : 
     547          439 :     note->setTpc1(Tpc::TPC_INVALID);
     548          439 :     note->setTpc2(Tpc::TPC_INVALID);
     549              : 
     550          439 :     if (e.hasAttribute("pitch")) { // obsolete
     551            0 :         note->setPitch(e.intAttribute("pitch"));
     552              :     }
     553          439 :     if (e.hasAttribute("tpc")) { // obsolete
     554            0 :         note->setTpc1(e.intAttribute("tpc"));
     555              :     }
     556              : 
     557         1529 :     while (e.readNextStartElement()) {
     558         1090 :         const AsciiStringView tag(e.name());
     559         1090 :         if (tag == "Accidental") {
     560              :             // on older scores, a note could have both a <userAccidental> tag and an <Accidental> tag
     561              :             // if a userAccidental has some other property set (like for instance offset)
     562              :             Accidental* a;
     563           26 :             if (hasAccidental) { // if the other tag has already been read,
     564           22 :                 a = note->accidental(); // re-use the accidental it constructed
     565              :             } else {
     566            4 :                 a = Factory::createAccidental(note);
     567              :             }
     568              :             // the accidental needs to know the properties of the
     569              :             // track it belongs to (??)
     570           26 :             a->setTrack(note->track());
     571           26 :             readAccidental(a, e, ctx);
     572           26 :             if (!hasAccidental) { // only the new accidental, if it has been added previously
     573            4 :                 note->add(a);
     574              :             }
     575           26 :             hasAccidental = true; // we now have an accidental
     576         1064 :         } else if (tag == "Text") {
     577            2 :             Fingering* f = Factory::createFingering(note);
     578            2 :             readFingering114(e, f);
     579            2 :             note->add(f);
     580         1062 :         } else if (tag == "onTimeType") {
     581            0 :             if (e.readText() == "offset") {
     582            0 :                 note->setOnTimeType(2);
     583              :             } else {
     584            0 :                 note->setOnTimeType(1);
     585              :             }
     586         1062 :         } else if (tag == "offTimeType") {
     587            0 :             if (e.readText() == "offset") {
     588            0 :                 note->setOffTimeType(2);
     589              :             } else {
     590            0 :                 note->setOffTimeType(1);
     591              :             }
     592         1062 :         } else if (tag == "onTimeOffset") {
     593            0 :             if (note->onTimeType() == 1) {
     594            0 :                 note->setOnTimeOffset(e.readInt() * 1000 / note->chord()->actualTicks().ticks());
     595              :             } else {
     596            0 :                 note->setOnTimeOffset(e.readInt() * 10);
     597              :             }
     598         1062 :         } else if (tag == "offTimeOffset") {
     599            0 :             if (note->offTimeType() == 1) {
     600            0 :                 note->setOffTimeOffset(1000 + (e.readInt() * 1000 / note->chord()->actualTicks().ticks()));
     601              :             } else {
     602            0 :                 note->setOffTimeOffset(1000 + (e.readInt() * 10));
     603              :             }
     604         1062 :         } else if (tag == "userAccidental") {
     605           44 :             String val(e.readText());
     606              :             bool ok;
     607           44 :             int k = val.toInt(&ok);
     608           44 :             if (ok) {
     609              :                 // on older scores, a note could have both a <userAccidental> tag and an <Accidental> tag
     610              :                 // if a userAccidental has some other property set (like for instance offset)
     611              :                 // only construct a new accidental, if the other tag has not been read yet
     612              :                 // (<userAccidental> tag is only used in older scores: no need to check the score mscVersion)
     613           44 :                 if (!hasAccidental) {
     614           44 :                     Accidental* a = Factory::createAccidental(note);
     615           44 :                     note->add(a);
     616              :                 }
     617              :                 // TODO: for backward compatibility
     618           44 :                 bool bracket = k & 0x8000;
     619           44 :                 k &= 0xfff;
     620           44 :                 AccidentalType at = AccidentalType::NONE;
     621           44 :                 switch (k) {
     622            0 :                 case 0: at = AccidentalType::NONE;
     623            0 :                     break;
     624            0 :                 case 1: at = AccidentalType::SHARP;
     625            0 :                     break;
     626            0 :                 case 2: at = AccidentalType::FLAT;
     627            0 :                     break;
     628            0 :                 case 3: at = AccidentalType::SHARP2;
     629            0 :                     break;
     630            0 :                 case 4: at = AccidentalType::FLAT2;
     631            0 :                     break;
     632            2 :                 case 5: at = AccidentalType::NATURAL;
     633            2 :                     break;
     634              : 
     635            2 :                 case 6: at = AccidentalType::FLAT_SLASH;
     636            2 :                     break;
     637            2 :                 case 7: at = AccidentalType::FLAT_SLASH2;
     638            2 :                     break;
     639            2 :                 case 8: at = AccidentalType::MIRRORED_FLAT2;
     640            2 :                     break;
     641            2 :                 case 9: at = AccidentalType::MIRRORED_FLAT;
     642            2 :                     break;
     643            2 :                 case 10: at = AccidentalType::NONE; // AccidentalType::MIRRORED_FLAT_SLASH
     644            2 :                     break;
     645            2 :                 case 11: at = AccidentalType::NONE; // AccidentalType::FLAT_FLAT_SLASH
     646            2 :                     break;
     647              : 
     648            2 :                 case 12: at = AccidentalType::SHARP_SLASH;
     649            2 :                     break;
     650            2 :                 case 13: at = AccidentalType::SHARP_SLASH2;
     651            2 :                     break;
     652            2 :                 case 14: at = AccidentalType::SHARP_SLASH3;
     653            2 :                     break;
     654            2 :                 case 15: at = AccidentalType::SHARP_SLASH4;
     655            2 :                     break;
     656              : 
     657            2 :                 case 16: at = AccidentalType::SHARP_ARROW_UP;
     658            2 :                     break;
     659            2 :                 case 17: at = AccidentalType::SHARP_ARROW_DOWN;
     660            2 :                     break;
     661            2 :                 case 18: at = AccidentalType::NONE; // AccidentalType::SHARP_ARROW_BOTH
     662            2 :                     break;
     663            2 :                 case 19: at = AccidentalType::FLAT_ARROW_UP;
     664            2 :                     break;
     665            2 :                 case 20: at = AccidentalType::FLAT_ARROW_DOWN;
     666            2 :                     break;
     667            2 :                 case 21: at = AccidentalType::NONE; // AccidentalType::FLAT_ARROW_BOTH
     668            2 :                     break;
     669            2 :                 case 22: at = AccidentalType::NATURAL_ARROW_UP;
     670            2 :                     break;
     671            2 :                 case 23: at = AccidentalType::NATURAL_ARROW_DOWN;
     672            2 :                     break;
     673            2 :                 case 24: at = AccidentalType::NONE; // AccidentalType::NATURAL_ARROW_BOTH
     674            2 :                     break;
     675            2 :                 case 25: at = AccidentalType::SORI;
     676            2 :                     break;
     677            2 :                 case 26: at = AccidentalType::KORON;
     678            2 :                     break;
     679              :                 }
     680           44 :                 note->accidental()->setAccidentalType(at);
     681              : 
     682           44 :                 note->accidental()->setBracket(AccidentalBracket(bracket));
     683              : 
     684           44 :                 note->accidental()->setRole(AccidentalRole::USER);
     685           44 :                 hasAccidental = true; // we now have an accidental
     686              :             }
     687         1062 :         } else if (tag == "offset") {
     688            6 :             e.skipCurrentElement(); // ignore manual layout in older scores
     689         1012 :         } else if (tag == "move") {
     690            0 :             note->chord()->setStaffMove(e.readInt());
     691         1012 :         } else if (tag == "head") {
     692           59 :             int i = e.readInt();
     693           59 :             NoteHeadGroup val = Read206::convertHeadGroup(i);
     694           59 :             note->setHeadGroup(val);
     695          953 :         } else if (tag == "headType") {
     696           42 :             int i = e.readInt();
     697              :             NoteHeadType val;
     698           42 :             switch (i) {
     699           14 :             case 1:
     700           14 :                 val = NoteHeadType::HEAD_WHOLE;
     701           14 :                 break;
     702           14 :             case 2:
     703           14 :                 val = NoteHeadType::HEAD_HALF;
     704           14 :                 break;
     705            0 :             case 3:
     706            0 :                 val = NoteHeadType::HEAD_QUARTER;
     707            0 :                 break;
     708           14 :             case 4:
     709           14 :                 val = NoteHeadType::HEAD_BREVIS;
     710           14 :                 break;
     711            0 :             default:
     712            0 :                 val = NoteHeadType::HEAD_AUTO;
     713              :             }
     714           42 :             note->setHeadType(val);
     715          911 :         } else if (Read206::readNoteProperties206(note, e, ctx)) {
     716              :         } else {
     717            0 :             e.unknown();
     718              :         }
     719              :     }
     720              :     // ensure sane values:
     721          439 :     note->setPitch(clampPitch(note->pitch()));
     722              : 
     723          439 :     if (note->concertPitch()) {
     724            0 :         note->setTpc2(Tpc::TPC_INVALID);
     725              :     } else {
     726          439 :         note->setPitch(note->pitch() + note->transposition());
     727          439 :         note->setTpc2(note->tpc1());
     728          439 :         note->setTpc1(Tpc::TPC_INVALID);
     729              :     }
     730          439 :     if (!tpcIsValid(note->tpc1()) && !tpcIsValid(note->tpc2())) {
     731            0 :         Key key = (note->staff() && note->chord()) ? note->staff()->key(note->chord()->tick()) : Key::C;
     732            0 :         int tpc = pitch2tpc(note->pitch(), key, Prefer::NEAREST);
     733            0 :         if (note->concertPitch()) {
     734            0 :             note->setTpc1(tpc);
     735              :         } else {
     736            0 :             note->setTpc2(tpc);
     737              :         }
     738              :     }
     739          439 :     if (!(tpcIsValid(note->tpc1()) && tpcIsValid(note->tpc2()))) {
     740          439 :         Fraction tick = note->chord() ? note->chord()->tick() : Fraction(-1, 1);
     741          439 :         Interval v = note->staff() ? note->staff()->transpose(tick) : Interval();
     742          439 :         if (tpcIsValid(note->tpc1())) {
     743            0 :             v.flip();
     744            0 :             if (v.isZero()) {
     745            0 :                 note->setTpc2(note->tpc1());
     746              :             } else {
     747            0 :                 note->setTpc2(Transpose::transposeTpc(note->tpc1(), v, true));
     748              :             }
     749              :         } else {
     750          439 :             if (v.isZero()) {
     751          439 :                 note->setTpc1(note->tpc2());
     752              :             } else {
     753            0 :                 note->setTpc1(Transpose::transposeTpc(note->tpc2(), v, true));
     754              :             }
     755              :         }
     756              :     }
     757              : 
     758              :     // check consistency of pitch, tpc1, tpc2, and transposition
     759              :     // see note in InstrumentChange::read() about a known case of tpc corruption produced in 2.0.x
     760              :     // but since there are other causes of tpc corruption (eg, https://musescore.org/en/node/74746)
     761              :     // including perhaps some we don't know about yet,
     762              :     // we will attempt to fix some problems here regardless of version
     763              : 
     764          439 :     if (!ctx.pasteMode() && !MScore::testMode) {
     765            0 :         int tpc1Pitch = (tpc2pitch(note->tpc1()) + 12) % 12;
     766            0 :         int tpc2Pitch = (tpc2pitch(note->tpc2()) + 12) % 12;
     767            0 :         int concertPitch = note->pitch() % 12;
     768            0 :         if (tpc1Pitch != concertPitch) {
     769            0 :             LOGD("bad tpc1 - concertPitch = %d, tpc1 = %d", concertPitch, tpc1Pitch);
     770            0 :             note->setPitch(note->pitch() + tpc1Pitch - concertPitch);
     771              :         }
     772            0 :         Interval v = note->staff()->transpose(ctx.tick());
     773            0 :         int transposedPitch = (note->pitch() - v.chromatic) % 12;
     774            0 :         if (tpc2Pitch != transposedPitch) {
     775            0 :             LOGD("bad tpc2 - transposedPitch = %d, tpc2 = %d", transposedPitch, tpc2Pitch);
     776              :             // just in case the staff transposition info is not reliable here,
     777            0 :             v.flip();
     778            0 :             note->setTpc2(Transpose::transposeTpc(note->tpc1(), v, true));
     779              :         }
     780              :     }
     781          439 : }
     782              : 
     783              : //---------------------------------------------------------
     784              : //   readClefType
     785              : //---------------------------------------------------------
     786              : 
     787           72 : static ClefType readClefType(int i)
     788              : {
     789           72 :     ClefType ct = ClefType::G;
     790              :     // convert obsolete old coding
     791           72 :     switch (i) {
     792           23 :     default:
     793           23 :     case  0: ct = ClefType::G;
     794           23 :         break;
     795            4 :     case  1: ct = ClefType::G8_VA;
     796            4 :         break;
     797            2 :     case  2: ct = ClefType::G15_MA;
     798            2 :         break;
     799            3 :     case  3: ct = ClefType::G8_VB;
     800            3 :         break;
     801           10 :     case  4: ct = ClefType::F;
     802           10 :         break;
     803            2 :     case  5: ct = ClefType::F8_VB;
     804            2 :         break;
     805            2 :     case  6: ct = ClefType::F15_MB;
     806            2 :         break;
     807            2 :     case  7: ct = ClefType::F_B;
     808            2 :         break;
     809            2 :     case  8: ct = ClefType::F_C;
     810            2 :         break;
     811            2 :     case  9: ct = ClefType::C1;
     812            2 :         break;
     813            2 :     case 10: ct = ClefType::C2;
     814            2 :         break;
     815            2 :     case 11: ct = ClefType::C3;
     816            2 :         break;
     817            2 :     case 12: ct = ClefType::C4;
     818            2 :         break;
     819            2 :     case 13: ct = ClefType::TAB;
     820            2 :         break;
     821            3 :     case 14: ct = ClefType::PERC;
     822            3 :         break;
     823            2 :     case 15: ct = ClefType::C5;
     824            2 :         break;
     825            2 :     case 16: ct = ClefType::G_1;
     826            2 :         break;
     827            2 :     case 17: ct = ClefType::F_8VA;
     828            2 :         break;
     829            2 :     case 18: ct = ClefType::F_15MA;
     830            2 :         break;
     831            1 :     case 19: ct = ClefType::PERC;
     832            1 :         break;                                              // PERC2 no longer supported
     833            0 :     case 20: ct = ClefType::TAB_SERIF;
     834            0 :         break;
     835              :     }
     836              : 
     837           72 :     return ct;
     838              : }
     839              : 
     840              : //---------------------------------------------------------
     841              : //   readClef
     842              : //---------------------------------------------------------
     843              : 
     844           21 : static void readClef(Clef* clef, XmlReader& e, ReadContext& ctx)
     845              : {
     846           41 :     while (e.readNextStartElement()) {
     847           20 :         const AsciiStringView tag(e.name());
     848           20 :         if (tag == "subtype") {
     849           20 :             clef->setClefType(readClefType(e.readInt()));
     850            0 :         } else if (!read400::TRead::readProperties(clef, e, ctx)) {
     851            0 :             e.unknown();
     852              :         }
     853              :     }
     854           21 :     if (clef->clefType() == ClefType::INVALID) {
     855            1 :         clef->setClefType(ClefType::G);
     856              :     }
     857           21 : }
     858              : 
     859              : //---------------------------------------------------------
     860              : //   readTuplet
     861              : //---------------------------------------------------------
     862              : 
     863           16 : static void readTuplet(Tuplet* tuplet, XmlReader& e, ReadContext& ctx)
     864              : {
     865           16 :     int bl = -1;
     866              : 
     867           94 :     while (e.readNextStartElement()) {
     868           78 :         const AsciiStringView tag(e.name());
     869           78 :         if (tag == "subtype") {      // obsolete
     870            0 :             e.skipCurrentElement();
     871           78 :         } else if (tag == "hasNumber") {  // obsolete even in 1.3
     872            0 :             tuplet->setNumberType(e.readInt() ? TupletNumberType::SHOW_NUMBER : TupletNumberType::NO_TEXT);
     873           78 :         } else if (tag == "hasLine") {    // obsolete even in 1.3
     874            0 :             tuplet->setHasBracket(e.readInt());
     875            0 :             tuplet->setBracketType(TupletBracketType::AUTO_BRACKET);
     876           78 :         } else if (tag == "baseLen") {    // obsolete even in 1.3
     877            0 :             bl = e.readInt();
     878           78 :         } else if (tag == "tick") {
     879            4 :             tuplet->setTick(Fraction::fromTicks(e.readInt()));
     880           74 :         } else if (!Read206::readTupletProperties206(e, ctx, tuplet)) {
     881            0 :             e.unknown();
     882              :         }
     883              :     }
     884           16 :     Fraction r = (tuplet->ratio() == Fraction(1, 1)) ? tuplet->ratio() : tuplet->ratio().reduced();
     885              :     // this may be wrong, but at this stage it is kept for compatibility. It will be corrected afterwards
     886              :     // during "sanitize" step
     887           16 :     Fraction f(r.denominator(), tuplet->baseLen().fraction().denominator());
     888           16 :     tuplet->setTicks(f.reduced());
     889           16 :     if (bl != -1) {           // obsolete, even in 1.3
     890            0 :         TDuration d;
     891            0 :         d.setVal(bl);
     892            0 :         tuplet->setBaseLen(d);
     893            0 :         d.setVal(bl * tuplet->ratio().denominator());
     894            0 :         tuplet->setTicks(d.fraction());
     895              :     }
     896           16 : }
     897              : 
     898              : //---------------------------------------------------------
     899              : //   readTremolo
     900              : //---------------------------------------------------------
     901              : 
     902            6 : static void readTremolo(TremoloCompat* t, XmlReader& e, ReadContext& ctx)
     903              : {
     904            0 :     auto createDefaultTremolo = [](TremoloCompat* t) {
     905            0 :         t->single = Factory::createTremoloSingleChord(t->parent);
     906            0 :         t->single->setTrack(t->parent.as<Chord>()->track());
     907            0 :         t->single->setTremoloType(TremoloType::R8);
     908            0 :     };
     909              : 
     910            0 :     auto item = [createDefaultTremolo](TremoloCompat* t) -> EngravingItem* {
     911            0 :         if (t->two) {
     912            0 :             return t->two;
     913              :         }
     914              : 
     915            0 :         if (!t->single) {
     916              :             // If no item been created yet at this point,
     917              :             // that means no "subtype" tag was present in the XML file.
     918              :             // In this case, we create a single eighth-note tremolo,
     919              :             // since that was the default.
     920            0 :             createDefaultTremolo(t);
     921              :         }
     922              : 
     923            0 :         return t->single;
     924              :     };
     925              : 
     926              :     enum class OldTremoloType : char {
     927              :         OLD_R8 = 0,
     928              :         OLD_R16,
     929              :         OLD_R32,
     930              :         OLD_C8,
     931              :         OLD_C16,
     932              :         OLD_C32
     933              :     };
     934              : 
     935           12 :     while (e.readNextStartElement()) {
     936            6 :         if (e.name() == "subtype") {
     937            6 :             OldTremoloType sti = OldTremoloType(e.readText().toInt());
     938            6 :             TremoloType type = TremoloType::INVALID_TREMOLO;
     939            6 :             switch (sti) {
     940            0 :             default:
     941            0 :             case OldTremoloType::OLD_R8:  type = TremoloType::R8;
     942            0 :                 break;
     943            0 :             case OldTremoloType::OLD_R16: type = TremoloType::R16;
     944            0 :                 break;
     945            0 :             case OldTremoloType::OLD_R32: type = TremoloType::R32;
     946            0 :                 break;
     947            2 :             case OldTremoloType::OLD_C8:  type = TremoloType::C8;
     948            2 :                 break;
     949            2 :             case OldTremoloType::OLD_C16: type = TremoloType::C16;
     950            2 :                 break;
     951            2 :             case OldTremoloType::OLD_C32: type = TremoloType::C32;
     952            2 :                 break;
     953              :             }
     954              : 
     955            6 :             if (isTremoloTwoChord(type)) {
     956            6 :                 t->two = Factory::createTremoloTwoChord(t->parent);
     957            6 :                 t->two->setTrack(t->parent.as<Chord>()->track());
     958            6 :                 t->two->setTremoloType(type);
     959              :             } else {
     960            0 :                 t->single = Factory::createTremoloSingleChord(t->parent);
     961            0 :                 t->single->setTrack(t->parent.as<Chord>()->track());
     962            0 :                 t->single->setTremoloType(type);
     963              :             }
     964            0 :         } else if (!TRead::readItemProperties(item(t), e, ctx)) {
     965            0 :             e.unknown();
     966              :         }
     967              :     }
     968              : 
     969            6 :     if (!t->two && !t->single) {
     970              :         // If no item been created yet at this point,
     971              :         // that means no "subtype" tag was present in the XML file.
     972              :         // In this case, we create a single eighth-note tremolo,
     973              :         // since that was the default.
     974            0 :         createDefaultTremolo(t);
     975              :     }
     976            6 : }
     977              : 
     978              : //---------------------------------------------------------
     979              : //   readChord
     980              : //---------------------------------------------------------
     981              : 
     982          427 : static void readChord(Measure* m, Chord* chord, XmlReader& e, ReadContext& ctx)
     983              : {
     984         1602 :     while (e.readNextStartElement()) {
     985         1175 :         const AsciiStringView tag(e.name());
     986         1175 :         if (tag == "Note") {
     987          439 :             Note* note = Factory::createNote(chord);
     988              :             // the note needs to know the properties of the track it belongs to
     989          439 :             note->setTrack(chord->track());
     990          439 :             readNote(note, e, ctx);
     991          439 :             chord->add(note);
     992          736 :         } else if (tag == "Attribute" || tag == "Articulation") {
     993           35 :             EngravingItem* el = Read206::readArticulation(chord, e, ctx);
     994           35 :             if (el->isFermata()) {
     995            8 :                 if (!chord->segment()) {
     996            8 :                     chord->setOwnershipParent(m->getSegment(SegmentType::ChordRest, ctx.tick()));
     997              :                 }
     998            8 :                 chord->segment()->add(el);
     999              :             } else {
    1000           27 :                 chord->add(el);
    1001              :             }
    1002          701 :         } else if (tag == "Tremolo") {
    1003            6 :             TremoloCompat tcompat { chord };
    1004            6 :             readTremolo(&tcompat, e, ctx);
    1005            6 :             if (tcompat.two) {
    1006            6 :                 tcompat.two->setOwnershipParent(chord);
    1007            6 :                 tcompat.two->setDurationType(chord->durationType());
    1008            6 :                 chord->setTremoloTwoChord(tcompat.two, false);
    1009            0 :             } else if (tcompat.single) {
    1010            0 :                 tcompat.single->setOwnershipParent(chord);
    1011            0 :                 tcompat.single->setDurationType(chord->durationType());
    1012            0 :                 chord->setTremoloSingleChord(tcompat.single);
    1013              :             } else {
    1014            0 :                 UNREACHABLE;
    1015              :             }
    1016          695 :         } else if (Read206::readChordProperties206(e, ctx, chord)) {
    1017              :         } else {
    1018            0 :             e.unknown();
    1019              :         }
    1020              :     }
    1021          427 : }
    1022              : 
    1023              : //---------------------------------------------------------
    1024              : //   readRest
    1025              : //---------------------------------------------------------
    1026              : 
    1027          373 : static void readRest(Measure* m, Rest* rest, XmlReader& e, ReadContext& ctx)
    1028              : {
    1029          764 :     while (e.readNextStartElement()) {
    1030          391 :         const AsciiStringView tag(e.name());
    1031          391 :         if (tag == "Attribute" || tag == "Articulation") {
    1032            2 :             EngravingItem* el = Read206::readArticulation(rest, e, ctx);
    1033            2 :             if (el->isFermata()) {
    1034            2 :                 if (!rest->segment()) {
    1035            2 :                     rest->setOwnershipParent(m->getSegment(SegmentType::ChordRest, ctx.tick()));
    1036              :                 }
    1037            2 :                 rest->segment()->add(el);
    1038              :             } else {
    1039            0 :                 rest->add(el);
    1040              :             }
    1041          389 :         } else if (Read206::readChordRestProperties206(e, ctx, rest)) {
    1042              :         } else {
    1043            0 :             e.unknown();
    1044              :         }
    1045              :     }
    1046          373 : }
    1047              : 
    1048              : //---------------------------------------------------------
    1049              : //   readTempoText
    1050              : //---------------------------------------------------------
    1051              : 
    1052            0 : void readTempoText(TempoText* t, XmlReader& e, ReadContext& ctx)
    1053              : {
    1054            0 :     while (e.readNextStartElement()) {
    1055            0 :         const AsciiStringView tag(e.name());
    1056            0 :         if (tag == "tempo") {
    1057            0 :             t->setTempo(e.readDouble());
    1058            0 :         } else if (!readTextProperties(e, ctx, t, t)) {
    1059            0 :             e.unknown();
    1060              :         }
    1061              :     }
    1062              : 
    1063            0 :     t->resetProperty(Pid::MUSIC_SYMBOL_SIZE);
    1064            0 : }
    1065              : 
    1066              : //---------------------------------------------------------
    1067              : //   readStaffText
    1068              : //---------------------------------------------------------
    1069              : 
    1070           28 : void readStaffText(StaffText* t, XmlReader& e, ReadContext& ctx)
    1071              : {
    1072          116 :     while (e.readNextStartElement()) {
    1073           60 :         if (!readTextProperties(e, ctx, t, t)) {
    1074            0 :             e.unknown();
    1075              :         }
    1076              :     }
    1077           28 : }
    1078              : 
    1079              : //---------------------------------------------------------
    1080              : //   readLineSegment114
    1081              : //---------------------------------------------------------
    1082              : 
    1083            3 : static void readLineSegment114(XmlReader& e, ReadContext& ctx, LineSegment* ls)
    1084              : {
    1085           17 :     while (e.readNextStartElement()) {
    1086           14 :         const AsciiStringView tag(e.name());
    1087           14 :         if (tag == "off1") {
    1088            3 :             ls->setOffset(e.readPoint() * ls->spatium());
    1089              :         } else {
    1090           11 :             read400::TRead::readProperties(ls, e, ctx);
    1091              :         }
    1092              :     }
    1093            3 : }
    1094              : 
    1095              : //---------------------------------------------------------
    1096              : //   readTextLineProperties114
    1097              : //---------------------------------------------------------
    1098              : 
    1099           21 : static bool readTextLineProperties114(XmlReader& e, ReadContext& ctx, TextLineBase* tl)
    1100              : {
    1101           21 :     const AsciiStringView tag(e.name());
    1102              : 
    1103           21 :     if (tag == "beginText") {
    1104            2 :         Text* text = Factory::createText(tl, TextStyleType::DEFAULT, false);
    1105            2 :         readText114(e, ctx, text, tl);
    1106            2 :         tl->setBeginText(text->xmlText());
    1107            2 :         tl->setPropertyFlags(Pid::BEGIN_TEXT, PropertyFlags::UNSTYLED);
    1108            2 :         delete text;
    1109           19 :     } else if (tag == "continueText") {
    1110            2 :         Text* text = Factory::createText(tl, TextStyleType::DEFAULT, false);
    1111            2 :         readText114(e, ctx, text, tl);
    1112            2 :         tl->setContinueText(text->xmlText());
    1113            2 :         tl->setPropertyFlags(Pid::CONTINUE_TEXT, PropertyFlags::UNSTYLED);
    1114            2 :         delete text;
    1115           17 :     } else if (tag == "endText") {
    1116            0 :         Text* text = Factory::createText(tl, TextStyleType::DEFAULT, false);
    1117            0 :         readText114(e, ctx, text, tl);
    1118            0 :         tl->setEndText(text->xmlText());
    1119            0 :         tl->setPropertyFlags(Pid::END_TEXT, PropertyFlags::UNSTYLED);
    1120            0 :         delete text;
    1121           17 :     } else if (tag == "beginHook") {
    1122            0 :         tl->setBeginHookType(e.readBool() ? HookType::HOOK_90 : HookType::NONE);
    1123           17 :     } else if (tag == "endHook") {
    1124            0 :         tl->setEndHookType(e.readBool() ? HookType::HOOK_90 : HookType::NONE);
    1125           17 :     } else if (tag == "beginHookType") {
    1126            0 :         tl->setBeginHookType(e.readInt() == 0 ? HookType::HOOK_90 : HookType::HOOK_45);
    1127           17 :     } else if (tag == "endHookType") {
    1128            0 :         tl->setEndHookType(e.readInt() == 0 ? HookType::HOOK_90 : HookType::HOOK_45);
    1129           17 :     } else if (tag == "Segment") {
    1130            3 :         LineSegment* ls = tl->createLineSegment();
    1131            3 :         ls->setTrack(tl->track());     // needed in read to get the right staff mag
    1132            3 :         tl->add(ls);
    1133            3 :         readLineSegment114(e, ctx, ls);
    1134              :         // in v1.x "visible" is a property of the segment only;
    1135              :         // we must ensure that it propagates also to the parent element.
    1136              :         // That's why the visibility is set after adding the segment
    1137              :         // to the corresponding spanner
    1138            3 :         ls->setVisible(ls->visible());
    1139            3 :         ls->setOffset(PointF());            // ignore offsets
    1140            3 :         ls->setAutoplace(true);
    1141           14 :     } else if (!read400::TRead::readProperties(tl, e, ctx)) {
    1142            0 :         return false;
    1143              :     }
    1144           21 :     return true;
    1145              : }
    1146              : 
    1147              : //---------------------------------------------------------
    1148              : //   readVolta114
    1149              : //---------------------------------------------------------
    1150              : 
    1151            0 : static void readVolta114(XmlReader& e, ReadContext& ctx, Volta* volta)
    1152              : {
    1153            0 :     while (e.readNextStartElement()) {
    1154            0 :         const AsciiStringView tag(e.name());
    1155            0 :         if (tag == "endings") {
    1156            0 :             String s = e.readText();
    1157            0 :             StringList sl = s.split(u',', muse::SkipEmptyParts);
    1158            0 :             volta->endings().clear();
    1159            0 :             for (const String& l : sl) {
    1160            0 :                 int i = l.simplified().toInt();
    1161            0 :                 volta->endings().push_back(i);
    1162              :             }
    1163            0 :         } else if (tag == "subtype") {
    1164            0 :             volta->setVoltaType(e.readInt() == 1 ? Volta::Type::CLOSED : Volta::Type::OPEN);
    1165            0 :         } else if (tag == "lineWidth") {
    1166            0 :             volta->setLineWidth(Spatium(e.readDouble()));
    1167            0 :             volta->setPropertyFlags(Pid::LINE_WIDTH, PropertyFlags::UNSTYLED);
    1168            0 :         } else if (!readTextLineProperties114(e, ctx, volta)) {
    1169            0 :             e.unknown();
    1170              :         }
    1171              :     }
    1172            0 :     volta->setOffset(PointF());          // ignore offsets
    1173            0 :     volta->setAutoplace(true);
    1174            0 :     CompatUtils::resetHookHeightSign(volta);
    1175            0 : }
    1176              : 
    1177              : //---------------------------------------------------------
    1178              : //   readOttava114
    1179              : //---------------------------------------------------------
    1180              : 
    1181            1 : static void readOttava114(XmlReader& e, ReadContext& ctx, Ottava* ottava)
    1182              : {
    1183           12 :     while (e.readNextStartElement()) {
    1184           11 :         const AsciiStringView tag(e.name());
    1185           11 :         if (tag == "subtype") {
    1186            0 :             String s = e.readText();
    1187              :             bool ok;
    1188            0 :             int idx = s.toInt(&ok);
    1189            0 :             if (!ok) {
    1190            0 :                 idx = int(OttavaType::OTTAVA_8VA);
    1191            0 :                 for (unsigned i = 0; i < sizeof(ottavaDefault) / sizeof(*ottavaDefault); ++i) {
    1192            0 :                     if (s == ottavaDefault[i].name) {
    1193            0 :                         idx = i;
    1194            0 :                         break;
    1195              :                     }
    1196              :                 }
    1197              :             }
    1198              :             //subtype are now in a different order...
    1199            0 :             if (idx == 1) {
    1200            0 :                 idx = 2;
    1201            0 :             } else if (idx == 2) {
    1202            0 :                 idx = 1;
    1203              :             }
    1204            0 :             ottava->setOttavaType(OttavaType(idx));
    1205           11 :         } else if (tag == "numbersOnly") {
    1206            0 :             ottava->setNumbersOnly(e.readInt());
    1207           11 :         } else if (tag == "lineWidth") {
    1208            1 :             read400::TRead::readProperty(ottava, e, ctx, Pid::LINE_WIDTH);
    1209           10 :         } else if (tag == "lineStyle") {
    1210            1 :             read400::TRead::readProperty(ottava, e, ctx, Pid::LINE_STYLE);
    1211            9 :         } else if (tag == "beginSymbol") {                        // obsolete
    1212            9 :         } else if (tag == "continueSymbol") {                     // obsolete
    1213            9 :         } else if (!readTextLineProperties114(e, ctx, ottava)) {
    1214            0 :             e.unknown();
    1215              :         }
    1216              :     }
    1217              : 
    1218            1 :     CompatUtils::resetHookHeightSign(ottava);
    1219            1 : }
    1220              : 
    1221              : //---------------------------------------------------------
    1222              : //   resolveSymCompatibility
    1223              : //---------------------------------------------------------
    1224              : 
    1225            1 : static String resolveSymCompatibility(SymId i, String programVersion)
    1226              : {
    1227            1 :     if (!programVersion.isEmpty() && programVersion < u"1.1") {
    1228            0 :         i = SymId(int(i) + 5);
    1229              :     }
    1230            1 :     switch (int(i)) {
    1231            1 :     case 197:
    1232            1 :         return u"keyboardPedalPed";
    1233            0 :     case 191:
    1234            0 :         return u"keyboardPedalUp";
    1235            0 :     case 193:
    1236            0 :         return u"noSym";           //SymId(pedaldotSym);
    1237            0 :     case 192:
    1238            0 :         return u"noSym";           //SymId(pedaldashSym);
    1239            0 :     case 139:
    1240            0 :         return u"ornamentTrill";
    1241            0 :     default:
    1242            0 :         return u"noSym";
    1243              :     }
    1244              : }
    1245              : 
    1246              : //---------------------------------------------------------
    1247              : //   readTextLine114
    1248              : //---------------------------------------------------------
    1249              : 
    1250            1 : static void readTextLine114(XmlReader& e, ReadContext& ctx, TextLine* textLine)
    1251              : {
    1252           10 :     while (e.readNextStartElement()) {
    1253            9 :         const AsciiStringView tag(e.name());
    1254              : 
    1255            9 :         if (tag == "lineVisible") {
    1256            0 :             textLine->setLineVisible(e.readBool());
    1257            9 :         } else if (tag == "beginHookHeight") {
    1258            0 :             textLine->setBeginHookHeight(Spatium(e.readDouble()));
    1259            9 :         } else if (tag == "endHookHeight" || tag == "hookHeight") {   // hookHeight is obsolete
    1260            1 :             textLine->setEndHookHeight(Spatium(e.readDouble()));
    1261            1 :             textLine->setPropertyFlags(Pid::END_HOOK_HEIGHT, PropertyFlags::UNSTYLED);
    1262            8 :         } else if (tag == "hookUp") { // obsolete
    1263            0 :             textLine->setEndHookHeight(-1.0_sp);
    1264            8 :         } else if (tag == "beginSymbol" || tag == "symbol") {   // "symbol" is obsolete
    1265            0 :             String text(e.readText());
    1266            0 :             textLine->setBeginText(String(u"<sym>%1</sym>").arg(
    1267            0 :                                        text.at(0).isDigit()
    1268            0 :                                        ? resolveSymCompatibility(SymId(text.toInt()), ctx.mscoreVersion())
    1269              :                                        : text));
    1270            8 :         } else if (tag == "continueSymbol") {
    1271            0 :             String text(e.readText());
    1272            0 :             textLine->setContinueText(String(u"<sym>%1</sym>").arg(
    1273            0 :                                           text.at(0).isDigit()
    1274            0 :                                           ? resolveSymCompatibility(SymId(text.toInt()), ctx.mscoreVersion())
    1275              :                                           : text));
    1276            8 :         } else if (tag == "endSymbol") {
    1277            0 :             String text(e.readText());
    1278            0 :             textLine->setEndText(String(u"<sym>%1</sym>").arg(
    1279            0 :                                      text.at(0).isDigit()
    1280            0 :                                      ? resolveSymCompatibility(SymId(text.toInt()), ctx.mscoreVersion())
    1281              :                                      : text));
    1282            8 :         } else if (tag == "beginSymbolOffset") { // obsolete
    1283            0 :             e.readPoint();
    1284            8 :         } else if (tag == "continueSymbolOffset") { // obsolete
    1285            0 :             e.readPoint();
    1286            8 :         } else if (tag == "endSymbolOffset") { // obsolete
    1287            0 :             e.readPoint();
    1288            8 :         } else if (tag == "beginTextPlace") {
    1289            1 :             textLine->setBeginTextPlace(TConv::fromXml(e.readAsciiText(), TextPlace::AUTO));
    1290            7 :         } else if (tag == "continueTextPlace") {
    1291            1 :             textLine->setContinueTextPlace(TConv::fromXml(e.readAsciiText(), TextPlace::AUTO));
    1292            6 :         } else if (tag == "endTextPlace") {
    1293            0 :             textLine->setEndTextPlace(TConv::fromXml(e.readAsciiText(), TextPlace::AUTO));
    1294            6 :         } else if (!readTextLineProperties114(e, ctx, textLine)) {
    1295            0 :             e.unknown();
    1296              :         }
    1297              :     }
    1298              : 
    1299            1 :     CompatUtils::resetHookHeightSign(textLine);
    1300            1 : }
    1301              : 
    1302              : //---------------------------------------------------------
    1303              : //   readPedal114
    1304              : //---------------------------------------------------------
    1305              : 
    1306            1 : static void readPedal114(XmlReader& e, ReadContext& ctx, Pedal* pedal)
    1307              : {
    1308            1 :     bool beginTextTag = false;
    1309            1 :     bool continueTextTag = false;
    1310            1 :     bool endTextTag = false;
    1311              : 
    1312           12 :     while (e.readNextStartElement()) {
    1313           11 :         const AsciiStringView tag(e.name());
    1314           11 :         if (tag == "subtype") {
    1315            0 :             e.skipCurrentElement();
    1316           11 :         } else if (tag == "endHookHeight" || tag == "hookHeight") {   // hookHeight is obsolete
    1317            1 :             pedal->setEndHookHeight(Spatium(e.readDouble()));
    1318            1 :             pedal->setPropertyFlags(Pid::END_HOOK_HEIGHT, PropertyFlags::UNSTYLED);
    1319           10 :         } else if (tag == "lineWidth") {
    1320            1 :             pedal->setLineWidth(Spatium(e.readDouble()));
    1321            1 :             pedal->setPropertyFlags(Pid::LINE_WIDTH, PropertyFlags::UNSTYLED);
    1322            9 :         } else if (tag == "lineStyle") {
    1323            1 :             read400::TRead::readProperty(pedal, e, ctx, Pid::LINE_STYLE);
    1324            1 :             pedal->setPropertyFlags(Pid::LINE_STYLE, PropertyFlags::UNSTYLED);
    1325            8 :         } else if (tag == "beginSymbol" || tag == "symbol") {   // "symbol" is obsolete
    1326            1 :             beginTextTag = true;
    1327            1 :             String text(e.readText());
    1328            1 :             String symbol = String(u"<sym>%1</sym>").arg(
    1329            1 :                 text.at(0).isDigit()
    1330            2 :                 ? resolveSymCompatibility(SymId(text.toInt()), ctx.mscoreVersion())
    1331            1 :                 : text);
    1332            1 :             if (symbol != pedal->propertyDefault(Pid::BEGIN_TEXT).value<String>()) {
    1333            0 :                 pedal->setBeginText(symbol);
    1334            0 :                 pedal->setPropertyFlags(Pid::BEGIN_TEXT, PropertyFlags::UNSTYLED);
    1335              :             }
    1336            8 :         } else if (tag == "continueSymbol") {
    1337            0 :             continueTextTag = true;
    1338            0 :             String text(e.readText());
    1339            0 :             String symbol = String(u"<sym>%1</sym>").arg(
    1340            0 :                 text.at(0).isDigit()
    1341            0 :                 ? resolveSymCompatibility(SymId(text.toInt()), ctx.mscoreVersion())
    1342            0 :                 : text);
    1343            0 :             if (symbol != pedal->propertyDefault(Pid::CONTINUE_TEXT).value<String>()) {
    1344            0 :                 pedal->setContinueText(symbol);
    1345            0 :                 pedal->setPropertyFlags(Pid::CONTINUE_TEXT, PropertyFlags::UNSTYLED);
    1346              :             }
    1347            7 :         } else if (tag == "endSymbol") {
    1348            0 :             endTextTag = true;
    1349            0 :             String text(e.readText());
    1350            0 :             String symbol = String(u"<sym>%1</sym>").arg(
    1351            0 :                 text.at(0).isDigit()
    1352            0 :                 ? resolveSymCompatibility(SymId(text.toInt()), ctx.mscoreVersion())
    1353            0 :                 : text);
    1354            0 :             if (symbol != pedal->propertyDefault(Pid::END_TEXT).value<String>()) {
    1355            0 :                 pedal->setEndText(symbol);
    1356            0 :                 pedal->setPropertyFlags(Pid::END_TEXT, PropertyFlags::UNSTYLED);
    1357              :             }
    1358            7 :         } else if (tag == "beginSymbolOffset") { // obsolete
    1359            1 :             e.readPoint();
    1360            6 :         } else if (tag == "continueSymbolOffset") { // obsolete
    1361            0 :             e.readPoint();
    1362            6 :         } else if (tag == "endSymbolOffset") { // obsolete
    1363            0 :             e.readPoint();
    1364            6 :         } else if (readTextLineProperties114(e, ctx, pedal)) {
    1365            6 :             beginTextTag = beginTextTag || tag == "beginText";
    1366            6 :             continueTextTag = continueTextTag || tag == "continueText";
    1367            6 :             endTextTag = endTextTag || tag == "endText";
    1368              :         } else {
    1369            0 :             e.unknown();
    1370              :         }
    1371              :     }
    1372              : 
    1373              :     // Set to the 114 defaults if no value was specified;
    1374              :     // or follow the new style setting if the specified value matches it
    1375            1 :     if (!beginTextTag) {
    1376            0 :         pedal->setBeginText(String());
    1377            0 :         pedal->setPropertyFlags(Pid::BEGIN_TEXT, PropertyFlags::UNSTYLED);
    1378            1 :     } else if (pedal->beginText() == pedal->propertyDefault(Pid::BEGIN_TEXT).value<String>()) {
    1379            1 :         pedal->setPropertyFlags(Pid::BEGIN_TEXT, PropertyFlags::STYLED);
    1380              :     }
    1381            1 :     if (!continueTextTag) {
    1382            0 :         pedal->setContinueText(String());
    1383            0 :         pedal->setPropertyFlags(Pid::CONTINUE_TEXT, PropertyFlags::UNSTYLED);
    1384            1 :     } else if (pedal->continueText() == pedal->propertyDefault(Pid::CONTINUE_TEXT).value<String>()) {
    1385            0 :         pedal->setPropertyFlags(Pid::CONTINUE_TEXT, PropertyFlags::STYLED);
    1386              :     }
    1387            1 :     if (!endTextTag) {
    1388            1 :         pedal->setEndText(String());
    1389            1 :         pedal->setPropertyFlags(Pid::END_TEXT, PropertyFlags::UNSTYLED);
    1390            0 :     } else if (pedal->endText() == pedal->propertyDefault(Pid::END_TEXT).value<String>()) {
    1391            0 :         pedal->setPropertyFlags(Pid::END_TEXT, PropertyFlags::STYLED);
    1392              :     }
    1393              : 
    1394            1 :     CompatUtils::resetHookHeightSign(pedal);
    1395            1 : }
    1396              : 
    1397              : //---------------------------------------------------------
    1398              : //   readHarmony114
    1399              : //---------------------------------------------------------
    1400              : 
    1401            0 : static void readHarmony114(XmlReader& e, ReadContext& ctx, Harmony* h)
    1402              : {
    1403              :     // convert table to tpc values
    1404              :     static const int table[] = {
    1405              :         14, 9, 16, 11, 18, 13, 8, 15, 10, 17, 12, 19
    1406              :     };
    1407              : 
    1408            0 :     HarmonyInfo* info = new HarmonyInfo(ctx.score());
    1409              : 
    1410            0 :     while (e.readNextStartElement()) {
    1411            0 :         const AsciiStringView tag(e.name());
    1412            0 :         if (tag == "base") {
    1413            0 :             if (ctx.mscVersion() >= 106) {
    1414            0 :                 info->setBassTpc(e.readInt());
    1415              :             } else {
    1416            0 :                 info->setBassTpc(table[e.readInt() - 1]);
    1417              :             }
    1418            0 :         } else if (tag == "baseCase") {
    1419            0 :             h->setBassCase(static_cast<NoteCaseType>(e.readInt()));
    1420            0 :         } else if (tag == "extension") {
    1421            0 :             info->setId(e.readInt());
    1422            0 :         } else if (tag == "name") {
    1423            0 :             info->setTextName(e.readText());
    1424            0 :         } else if (tag == "root") {
    1425            0 :             if (ctx.mscVersion() >= 106) {
    1426            0 :                 info->setRootTpc(e.readInt());
    1427              :             } else {
    1428            0 :                 info->setRootTpc(table[e.readInt() - 1]);
    1429              :             }
    1430            0 :         } else if (tag == "rootCase") {
    1431            0 :             h->setRootCase(static_cast<NoteCaseType>(e.readInt()));
    1432            0 :         } else if (tag == "degree") {
    1433            0 :             int degreeValue = 0;
    1434            0 :             int degreeAlter = 0;
    1435            0 :             String degreeType;
    1436            0 :             while (e.readNextStartElement()) {
    1437            0 :                 const AsciiStringView t(e.name());
    1438            0 :                 if (t == "degree-value") {
    1439            0 :                     degreeValue = e.readInt();
    1440            0 :                 } else if (t == "degree-alter") {
    1441            0 :                     degreeAlter = e.readInt();
    1442            0 :                 } else if (t == "degree-type") {
    1443            0 :                     degreeType = e.readText();
    1444              :                 } else {
    1445            0 :                     e.unknown();
    1446              :                 }
    1447              :             }
    1448            0 :             if (degreeValue <= 0 || degreeValue > 13
    1449            0 :                 || degreeAlter < -2 || degreeAlter > 2
    1450            0 :                 || (degreeType != "add" && degreeType != "alter" && degreeType != "subtract")) {
    1451            0 :                 LOGD("incorrect degree: degreeValue=%d degreeAlter=%d degreeType=%s",
    1452            0 :                      degreeValue, degreeAlter, muPrintable(degreeType));
    1453              :             } else {
    1454            0 :                 if (degreeType == "add") {
    1455            0 :                     h->addDegree(HDegree(degreeValue, degreeAlter, HDegreeType::ADD));
    1456            0 :                 } else if (degreeType == "alter") {
    1457            0 :                     h->addDegree(HDegree(degreeValue, degreeAlter, HDegreeType::ALTER));
    1458            0 :                 } else if (degreeType == "subtract") {
    1459            0 :                     h->addDegree(HDegree(degreeValue, degreeAlter, HDegreeType::SUBTRACT));
    1460              :                 }
    1461              :             }
    1462            0 :         } else if (tag == "leftParen") {
    1463            0 :             h->setParenthesesMode(h->rightParen() ? ParenthesesMode::BOTH : ParenthesesMode::LEFT, true, false);
    1464            0 :             e.readNext();
    1465            0 :         } else if (tag == "rightParen") {
    1466            0 :             h->setParenthesesMode(h->leftParen() ? ParenthesesMode::BOTH : ParenthesesMode::RIGHT, true, false);
    1467            0 :             e.readNext();
    1468            0 :         } else if (!readTextProperties(e, ctx, h, h)) {
    1469            0 :             e.unknown();
    1470              :         }
    1471              :     }
    1472              : 
    1473            0 :     h->addChord(info);
    1474              : 
    1475            0 :     h->afterRead();
    1476            0 : }
    1477              : 
    1478              : //---------------------------------------------------------
    1479              : //   readMeasure
    1480              : //---------------------------------------------------------
    1481              : 
    1482          446 : static void readMeasure(Measure* m, int staffIdx, XmlReader& e, ReadContext& ctx)
    1483              : {
    1484          446 :     Segment* segment = 0;
    1485              : 
    1486          446 :     std::vector<Chord*> graceNotes;
    1487              : 
    1488              :     //sort tuplet elements. needed for nested tuplets #22537
    1489          457 :     for (auto& p : ctx.tuplets()) {
    1490           11 :         Tuplet* t = p.second;
    1491           11 :         t->sortElements();
    1492              :     }
    1493          446 :     ctx.tuplets().clear();
    1494          446 :     ctx.setTrack(staffIdx * VOICES);
    1495              : 
    1496          446 :     m->createStaves(staffIdx);
    1497              : 
    1498              :     // tick is obsolete
    1499          446 :     if (e.hasAttribute("tick")) {
    1500            0 :         ctx.setTick(Fraction::fromTicks(ctx.fileDivision(e.intAttribute("tick"))));
    1501              :     }
    1502              : 
    1503          446 :     if (e.hasAttribute("len")) {
    1504            0 :         StringList sl = e.attribute("len").split('/');
    1505            0 :         if (sl.size() == 2) {
    1506            0 :             m->setTicks(Fraction(sl[0].toInt(), sl[1].toInt()));
    1507              :         } else {
    1508            0 :             LOGD("illegal measure size <%s>", muPrintable(e.attribute("len")));
    1509              :         }
    1510            0 :         ctx.compatTimeSigMap()->add(m->tick().ticks(), SigEvent(m->ticks(), m->timesig()));
    1511            0 :         ctx.compatTimeSigMap()->add(m->endTick().ticks(), SigEvent(m->timesig()));
    1512            0 :     }
    1513              : 
    1514          446 :     Staff* staff = ctx.staff(staffIdx);
    1515          446 :     Fraction timeStretch(staff->timeStretch(m->tick()));
    1516              : 
    1517              :     // keep track of tick of previous element
    1518              :     // this allows markings that need to apply to previous element to do so
    1519              :     // even though we may have already advanced to next tick position
    1520          446 :     Fraction lastTick = ctx.tick();
    1521              : 
    1522         1430 :     while (e.readNextStartElement()) {
    1523          984 :         const AsciiStringView tag(e.name());
    1524              : 
    1525          984 :         if (tag == "move") {
    1526            0 :             ctx.setTick(e.readFraction() + m->tick());
    1527          984 :         } else if (tag == "tick") {
    1528            0 :             ctx.setTick(Fraction::fromTicks(ctx.fileDivision(e.readInt())));
    1529            0 :             lastTick = ctx.tick();
    1530          984 :         } else if (tag == "BarLine") {
    1531           30 :             BarLine* barLine = Factory::createBarLine(ctx.dummy());
    1532           30 :             barLine->setTrack(ctx.track());
    1533              :             // initialize span properties with values from staff
    1534           30 :             barLine->resetProperty(Pid::BARLINE_SPAN_FROM);
    1535           30 :             barLine->resetProperty(Pid::BARLINE_SPAN_TO);
    1536              : 
    1537           61 :             while (e.readNextStartElement()) {
    1538           31 :                 const AsciiStringView tg(e.name());
    1539           31 :                 if (tg == "subtype") {
    1540           25 :                     BarLineType t = BarLineType::NORMAL;
    1541           25 :                     switch (e.readInt()) {
    1542            0 :                     default:
    1543              :                     case 0:
    1544            0 :                         t = BarLineType::NORMAL;
    1545            0 :                         break;
    1546            0 :                     case 1:
    1547            0 :                         t = BarLineType::DOUBLE;
    1548            0 :                         break;
    1549            0 :                     case 2:
    1550            0 :                         t = BarLineType::START_REPEAT;
    1551            0 :                         break;
    1552            0 :                     case 3:
    1553            0 :                         t = BarLineType::END_REPEAT;
    1554            0 :                         break;
    1555            0 :                     case 4:
    1556            0 :                         t = BarLineType::BROKEN;
    1557            0 :                         break;
    1558           25 :                     case 5:
    1559           25 :                         t = BarLineType::END;
    1560           25 :                         break;
    1561            0 :                     case 6:
    1562            0 :                         t = BarLineType::END_START_REPEAT;
    1563            0 :                         break;
    1564              :                     }
    1565           25 :                     barLine->setBarLineType(t);
    1566            6 :                 } else if (!TRead::readItemProperties(barLine, e, ctx)) {
    1567            0 :                     e.unknown();
    1568              :                 }
    1569              :             }
    1570              : 
    1571              :             //
    1572              :             //  StartRepeatBarLine: always at the beginning tick of a measure, always BarLineType::START_REPEAT
    1573              :             //  BarLine:            in the middle of a measure, has no semantic
    1574              :             //  EndBarLine:         at the end tick of a measure
    1575              :             //  BeginBarLine:       first segment of a measure
    1576              : 
    1577              :             SegmentType st;
    1578           30 :             if ((ctx.tick() != m->tick()) && (ctx.tick() != m->endTick())) {
    1579            0 :                 st = SegmentType::BarLine;
    1580           30 :             } else if (barLine->barLineType() == BarLineType::START_REPEAT && ctx.tick() == m->tick()) {
    1581            0 :                 st = SegmentType::StartRepeatBarLine;
    1582           30 :             } else if (ctx.tick() == m->tick() && segment == 0) {
    1583            0 :                 st = SegmentType::BeginBarLine;
    1584              :             } else {
    1585           30 :                 st = SegmentType::EndBarLine;
    1586              :             }
    1587           30 :             segment = m->getSegment(st, ctx.tick());
    1588           30 :             segment->add(barLine);
    1589          954 :         } else if (tag == "Chord") {
    1590              :             // where the chord belongs is only known once it has been read: a grace note
    1591              :             // goes on the chord it decorates rather than on a segment of its own, and
    1592              :             // reading moves the tick the segment is looked up by
    1593          427 :             Chord* chord = Factory::createChord(ctx.score()->dummy());
    1594          427 :             chord->setTrack(ctx.track());
    1595          427 :             readChord(m, chord, e, ctx);
    1596          427 :             if (!chord->segment()) {
    1597          419 :                 chord->setOwnershipParent(m->getSegment(SegmentType::ChordRest, ctx.tick()));
    1598              :             }
    1599          427 :             segment = chord->segment();
    1600          427 :             if (chord->noteType() != NoteType::NORMAL) {
    1601            0 :                 graceNotes.push_back(chord);
    1602              :             } else {
    1603          427 :                 segment->add(chord);
    1604          427 :                 assert(segment->segmentType() == SegmentType::ChordRest);
    1605              : 
    1606          427 :                 for (size_t i = 0; i < graceNotes.size(); ++i) {
    1607            0 :                     Chord* gc = graceNotes[i];
    1608            0 :                     gc->setGraceIndex(static_cast<int>(i));
    1609            0 :                     chord->add(gc);
    1610              :                 }
    1611          427 :                 graceNotes.clear();
    1612          427 :                 Fraction crticks = chord->actualTicks();
    1613              : 
    1614          427 :                 if (chord->tremoloSingleChord()) {
    1615            0 :                     chord->tremoloSingleChord()->setOwnershipParent(chord);
    1616          427 :                 } else if (chord->tremoloTwoChord()) {
    1617            6 :                     TremoloTwoChord* tremolo = chord->tremoloTwoChord();
    1618            6 :                     track_idx_t track = chord->track();
    1619            6 :                     Segment* ss = 0;
    1620           12 :                     for (Segment* ps = m->first(SegmentType::ChordRest); ps; ps = ps->next(SegmentType::ChordRest)) {
    1621           12 :                         if (ps->tick() >= ctx.tick()) {
    1622            6 :                             break;
    1623              :                         }
    1624            6 :                         if (ps->element(track)) {
    1625            6 :                             ss = ps;
    1626              :                         }
    1627              :                     }
    1628            6 :                     Chord* pch = 0;                       // previous chord
    1629            6 :                     if (ss) {
    1630            6 :                         ChordRest* cr = toChordRest(ss->element(track));
    1631            6 :                         if (cr && cr->isChord()) {
    1632            6 :                             pch = toChord(cr);
    1633              :                         }
    1634              :                     }
    1635            6 :                     if (pch) {
    1636            6 :                         tremolo->setOwnershipParent(pch);
    1637            6 :                         pch->setTremoloTwoChord(tremolo);
    1638            6 :                         chord->setTremoloTwoChord(nullptr);
    1639              :                         // force duration to half
    1640            6 :                         Fraction pts(timeStretch * pch->globalTicks());
    1641            6 :                         pch->setTicks(pts * Fraction(1, 2));
    1642            6 :                         chord->setTicks(crticks * Fraction(1, 2));
    1643              :                     } else {
    1644            0 :                         LOGD("tremolo: first note not found");
    1645              :                     }
    1646            6 :                     crticks = crticks * Fraction(1, 2);
    1647              :                 }
    1648          427 :                 lastTick = ctx.tick();
    1649          427 :                 ctx.incTick(crticks);
    1650              :             }
    1651          527 :         } else if (tag == "Rest") {
    1652          373 :             if (m->isMMRest()) {
    1653            0 :                 segment = m->getSegment(SegmentType::ChordRest, ctx.tick());
    1654            0 :                 MMRest* mmr = Factory::createMMRest(segment);
    1655            0 :                 mmr->setTrack(ctx.track());
    1656            0 :                 read400::TRead::read(mmr, e, ctx);
    1657            0 :                 mmr->setTicks(m->ticks());
    1658            0 :                 segment->add(mmr);
    1659            0 :                 lastTick = ctx.tick();
    1660            0 :                 ctx.incTick(mmr->actualTicks());
    1661              :             } else {
    1662          373 :                 Rest* rest = Factory::createRest(ctx.score()->dummy());
    1663          373 :                 rest->setDurationType(DurationType::V_MEASURE);
    1664          373 :                 rest->setTicks(m->timesig() / timeStretch);
    1665          373 :                 rest->setTrack(ctx.track());
    1666          373 :                 readRest(m, rest, e, ctx);
    1667              : 
    1668          373 :                 Segment* segment2 = m->getSegment(SegmentType::ChordRest, ctx.tick());
    1669          373 :                 rest->setOwnershipParent(segment2);
    1670          373 :                 segment2->add(rest);
    1671              : 
    1672          373 :                 if (!rest->ticks().isValid()) {    // hack
    1673            0 :                     rest->setTicks(m->timesig() / timeStretch);
    1674              :                 }
    1675              : 
    1676          373 :                 lastTick = ctx.tick();
    1677          373 :                 ctx.incTick(rest->actualTicks());
    1678              :             }
    1679          154 :         } else if (tag == "Breath") {
    1680            0 :             segment = m->getSegment(SegmentType::ChordRest, ctx.tick());
    1681            0 :             Breath* breath = Factory::createBreath(segment);
    1682            0 :             breath->setTrack(ctx.track());
    1683            0 :             Fraction tick = ctx.tick();
    1684            0 :             breath->setPlacement(breath->track() & 1 ? PlacementV::BELOW : PlacementV::ABOVE);
    1685            0 :             read400::TRead::read(breath, e, ctx);
    1686              :             // older scores placed the breath segment right after the chord to which it applies
    1687              :             // rather than before the next chordrest segment with an element for the staff
    1688              :             // result would be layout too far left if there are other segments due to notes in other staves
    1689              :             // we need to find tick of chord to which this applies, and add its duration
    1690            0 :             Fraction prevTick;
    1691            0 :             if (ctx.tick() < tick) {
    1692            0 :                 prevTick = ctx.tick();            // use our own tick if we explicitly reset to earlier position
    1693              :             } else {
    1694            0 :                 prevTick = lastTick;            // otherwise use tick of previous tick/chord/rest tag
    1695              :             }
    1696              :             // find segment
    1697            0 :             Segment* prev = m->findSegment(SegmentType::ChordRest, prevTick);
    1698            0 :             if (prev) {
    1699              :                 // find chordrest
    1700            0 :                 ChordRest* lastCR = toChordRest(prev->element(ctx.track()));
    1701            0 :                 if (lastCR) {
    1702            0 :                     tick = prevTick + lastCR->actualTicks();
    1703              :                 }
    1704              :             }
    1705            0 :             segment = m->getSegment(SegmentType::Breath, tick);
    1706            0 :             segment->add(breath);
    1707          154 :         } else if (tag == "endSpanner") {
    1708            0 :             int id = e.attribute("id").toInt();
    1709            0 :             Spanner* spanner = ctx.findSpanner(id);
    1710            0 :             if (spanner) {
    1711            0 :                 spanner->setTicks(ctx.tick() - spanner->tick());
    1712              :                 // if (spanner->track2() == -1)
    1713              :                 // the absence of a track tag [?] means the
    1714              :                 // track is the same as the beginning of the slur
    1715            0 :                 if (spanner->track2() == muse::nidx) {
    1716            0 :                     spanner->setTrack2(spanner->track() ? spanner->track() : ctx.track());
    1717              :                 }
    1718              :             } else {
    1719              :                 // remember "endSpanner" values
    1720            0 :                 SpannerValues sv;
    1721            0 :                 sv.spannerId = id;
    1722            0 :                 sv.track2    = ctx.track();
    1723            0 :                 sv.tick2     = ctx.tick();
    1724            0 :                 ctx.addSpannerValues(sv);
    1725              :             }
    1726            0 :             e.readNext();
    1727          154 :         } else if (tag == "Slur") {
    1728            0 :             Slur* sl = Factory::createSlur(ctx.score()->dummy());
    1729            0 :             sl->setTick(ctx.tick());
    1730            0 :             Read206::readSlur206(e, ctx, sl);
    1731              :             //
    1732              :             // check if we already saw "endSpanner"
    1733              :             //
    1734            0 :             int id = ctx.spannerId(sl);
    1735            0 :             const SpannerValues* sv = ctx.spannerValues(id);
    1736            0 :             if (sv) {
    1737            0 :                 sl->setTick2(sv->tick2);
    1738            0 :                 sl->setTrack2(sv->track2);
    1739              :             }
    1740            0 :             ctx.addSpanner(sl);
    1741          154 :         } else if (tag == "HairPin"
    1742          154 :                    || tag == "Pedal"
    1743          154 :                    || tag == "Ottava"
    1744          154 :                    || tag == "Trill"
    1745          154 :                    || tag == "TextLine"
    1746          308 :                    || tag == "Volta") {
    1747            0 :             Spanner* sp = toSpanner(Factory::createItemByName(tag, m));
    1748            0 :             sp->setTrack(ctx.track());
    1749            0 :             sp->setTick(ctx.tick());
    1750              :             // ?? sp->setAnchor(Spanner::Anchor::SEGMENT);
    1751            0 :             if (tag == "Volta") {
    1752            0 :                 readVolta114(e, ctx, toVolta(sp));
    1753              :             } else {
    1754            0 :                 Read206::readTextLine206(e, ctx, toTextLineBase(sp));
    1755              :             }
    1756            0 :             ctx.addSpanner(sp);
    1757              :             //
    1758              :             // check if we already saw "endSpanner"
    1759              :             //
    1760            0 :             int id = ctx.spannerId(sp);
    1761            0 :             const SpannerValues* sv = ctx.spannerValues(id);
    1762            0 :             if (sv) {
    1763            0 :                 sp->setTicks(sv->tick2 - sp->tick());
    1764            0 :                 sp->setTrack2(sv->track2);
    1765              :             }
    1766          154 :         } else if (tag == "RepeatMeasure") {
    1767            0 :             segment = m->getSegment(SegmentType::ChordRest, ctx.tick());
    1768            0 :             MeasureRepeat* rm = Factory::createMeasureRepeat(segment);
    1769            0 :             rm->setTrack(ctx.track());
    1770            0 :             readRest(m, rm, e, ctx);
    1771            0 :             rm->setNumMeasures(1);
    1772            0 :             m->setMeasureRepeatCount(1, staffIdx);
    1773            0 :             segment->add(rm);
    1774            0 :             if (rm->actualTicks().isZero()) {     // might happen with 1.3 scores
    1775            0 :                 rm->setTicks(m->ticks());
    1776              :             }
    1777            0 :             lastTick = ctx.tick();
    1778            0 :             ctx.incTick(m->ticks());
    1779          154 :         } else if (tag == "Clef") {
    1780              :             // there may be more than one clef segment for same tick position
    1781              :             // the first clef may be missing and is added later in layout
    1782              : 
    1783              :             bool header;
    1784           21 :             if (ctx.tick() != m->tick()) {
    1785           20 :                 header = false;
    1786            1 :             } else if (!segment) {
    1787            1 :                 header = true;
    1788              :             } else {
    1789            0 :                 header = true;
    1790            0 :                 for (Segment* s = m->segments().first(); s && s->rtick().isZero(); s = s->next()) {
    1791            0 :                     if (s->isKeySigType() || s->isTimeSigType()) {
    1792              :                         // hack: there may be other segment types which should
    1793              :                         // generate a clef at current position
    1794            0 :                         header = false;
    1795            0 :                         break;
    1796              :                     }
    1797              :                 }
    1798              :             }
    1799           21 :             segment = m->getSegment(header ? SegmentType::HeaderClef : SegmentType::Clef, ctx.tick());
    1800              : 
    1801           21 :             Clef* clef = Factory::createClef(segment);
    1802           21 :             clef->setTrack(ctx.track());
    1803           21 :             readClef(clef, e, ctx);
    1804           21 :             if (ctx.mscVersion() < 113) {
    1805            0 :                 clef->setOffset(PointF());
    1806              :             }
    1807           21 :             clef->setGenerated(false);
    1808              :             // MS3 doesn't support wrong clef for staff type: Default to G
    1809           21 :             bool isDrumStaff = staff->isDrumStaff(ctx.tick());
    1810           21 :             if (clef->clefType() == ClefType::TAB
    1811           20 :                 || (clef->clefType() == ClefType::PERC && !isDrumStaff)
    1812           41 :                 || (clef->clefType() != ClefType::PERC && isDrumStaff)) {
    1813            2 :                 clef->setClefType(ClefType::G);
    1814            2 :                 staff->clefList().erase(ctx.tick().ticks());
    1815            2 :                 staff->clefList().insert(std::pair<int, ClefType>(ctx.tick().ticks(), ClefType::G));
    1816              :             }
    1817              : 
    1818              :             // Clef segments are sorted on layout now.  Previously, clef barline position could be out of sync with segment placement.
    1819           41 :             if (ctx.tick() != Fraction(0, 1) && ctx.tick() == m->tick()
    1820           41 :                 && !(m->prevMeasure() && m->prevMeasure()->repeatEnd()) && !header) {
    1821            0 :                 clef->setClefToBarlinePosition(ClefToBarlinePosition::AFTER);
    1822              :             }
    1823              : 
    1824           21 :             segment->add(clef);
    1825          133 :         } else if (tag == "TimeSig") {
    1826              :             // if time sig not at beginning of measure => courtesy time sig
    1827           33 :             Fraction currTick = ctx.tick();
    1828           33 :             bool courtesySig = (currTick > m->tick());
    1829           33 :             if (courtesySig) {
    1830              :                 // if courtesy sig., just add it without map processing
    1831            0 :                 segment = m->getSegment(SegmentType::TimeSigAnnounce, currTick);
    1832              :             } else {
    1833              :                 // if 'real' time sig., do full process
    1834           33 :                 segment = m->getSegment(SegmentType::TimeSig, currTick);
    1835              :             }
    1836              : 
    1837           33 :             TimeSig* ts = Factory::createTimeSig(segment);
    1838           33 :             ts->setTrack(ctx.track());
    1839           33 :             read400::TRead::read(ts, e, ctx);
    1840              : 
    1841           33 :             segment->add(ts);
    1842           33 :             if (!courtesySig) {
    1843           33 :                 timeStretch = ts->stretch().reduced();
    1844           33 :                 m->setTimesig(ts->sig() / timeStretch);
    1845              :             }
    1846          100 :         } else if (tag == "KeySig") {
    1847            4 :             KeySig* ks = Factory::createKeySig(ctx.dummy());
    1848            4 :             ks->setTrack(ctx.track());
    1849            4 :             read400::TRead::read(ks, e, ctx);
    1850            4 :             Fraction curTick = ctx.tick();
    1851              :             // if key sig not at beginning of measure => courtesy key sig
    1852            4 :             bool courtesySig = (curTick == m->endTick());
    1853            4 :             segment = m->getSegment(courtesySig ? SegmentType::KeySigAnnounce : SegmentType::KeySig, curTick);
    1854            4 :             segment->add(ks);
    1855            4 :             staff->setKey(curTick, ks->keySigEvent());
    1856           96 :         } else if (tag == "Lyrics") {
    1857            0 :             Lyrics* l = Factory::createLyrics(ctx.dummy());
    1858            0 :             l->setTrack(ctx.track());
    1859              : 
    1860            0 :             int iEndTick = 0;                 // used for backward compatibility
    1861            0 :             Text* _verseNumber = 0;
    1862              : 
    1863            0 :             while (e.readNextStartElement()) {
    1864            0 :                 const AsciiStringView t(e.name());
    1865            0 :                 if (t == "no") {
    1866            0 :                     l->setVerse(e.readInt());
    1867            0 :                     if (l->isEven()) {
    1868            0 :                         l->initTextStyleType(TextStyleType::LYRICS_EVEN);
    1869              :                     }
    1870            0 :                 } else if (t == "syllabic") {
    1871            0 :                     String val(e.readText());
    1872            0 :                     if (val == "single") {
    1873            0 :                         l->setSyllabic(LyricsSyllabic::SINGLE);
    1874            0 :                     } else if (val == "begin") {
    1875            0 :                         l->setSyllabic(LyricsSyllabic::BEGIN);
    1876            0 :                     } else if (val == "end") {
    1877            0 :                         l->setSyllabic(LyricsSyllabic::END);
    1878            0 :                     } else if (val == "middle") {
    1879            0 :                         l->setSyllabic(LyricsSyllabic::MIDDLE);
    1880              :                     } else {
    1881            0 :                         LOGD("bad syllabic property");
    1882              :                     }
    1883            0 :                 } else if (t == "endTick") {                // obsolete
    1884              :                     // store <endTick> tag value until a <ticks> tag has been read
    1885              :                     // which positions this lyrics element in the score
    1886            0 :                     iEndTick = e.readInt();
    1887            0 :                 } else if (t == "ticks") {
    1888            0 :                     l->setTicks(Fraction::fromTicks(e.readInt()));
    1889            0 :                 } else if (t == "Number") {                                 // obsolete
    1890            0 :                     _verseNumber = Factory::createText(l);
    1891            0 :                     readText114(e, ctx, _verseNumber, l);
    1892            0 :                 } else if (!readTextProperties(e, ctx, l, l)) {
    1893            0 :                     e.unknown();
    1894              :                 }
    1895              :             }
    1896              :             // if any endTick, make it relative to current tick
    1897            0 :             if (iEndTick) {
    1898            0 :                 l->setTicks(Fraction::fromTicks(iEndTick - ctx.tick().ticks()));
    1899              :                 // LOGD("Lyrics::endTick: %d  ticks %d", iEndTick, _ticks);
    1900              :             }
    1901              :             if (_verseNumber) {
    1902              :                 // TODO: add text to main text
    1903              :             }
    1904              : 
    1905            0 :             delete _verseNumber;
    1906              : 
    1907            0 :             segment       = m->getSegment(SegmentType::ChordRest, ctx.tick());
    1908            0 :             ChordRest* cr = toChordRest(segment->element(l->track()));
    1909            0 :             if (!cr) {
    1910            0 :                 cr = toChordRest(segment->element(ctx.track()));         // in case lyric itself has bad track info
    1911              :             }
    1912            0 :             if (!cr) {
    1913            0 :                 LOGD("Internal error: no chord/rest for lyrics");
    1914              :             } else {
    1915            0 :                 cr->add(l);
    1916              :             }
    1917           96 :         } else if (tag == "Text") {
    1918           26 :             segment = m->getSegment(SegmentType::ChordRest, ctx.tick());
    1919           26 :             StaffText* t = Factory::createStaffText(segment);
    1920           26 :             t->setTrack(ctx.track());
    1921           26 :             readStaffText(t, e, ctx);
    1922           26 :             if (t->empty()) {
    1923           26 :                 LOGD("reading empty text: deleted");
    1924           26 :                 delete t;
    1925              :             } else {
    1926            0 :                 segment->add(t);
    1927              :             }
    1928           70 :         } else if (tag == "Dynamic") {
    1929            0 :             segment = m->getSegment(SegmentType::ChordRest, ctx.tick());
    1930            0 :             Dynamic* dyn = Factory::createDynamic(segment);
    1931            0 :             dyn->setTrack(ctx.track());
    1932            0 :             read400::TRead::read(dyn, e, ctx); // for 114 scores, dynamics are frontloaded in the measure with <tick> attributes.
    1933              :             // so we need to reset its parent to the correct one after that element is read.
    1934            0 :             segment = m->getSegment(SegmentType::ChordRest, ctx.tick());
    1935            0 :             dyn->setOwnershipParent(segment);
    1936            0 :             if (dyn->dynamicType() == DynamicType::OTHER && dyn->xmlText().isEmpty()) {
    1937              :                 // if we add this dynamic, it will be an unselectable invisible object that
    1938              :                 // messes with collision detection.
    1939            0 :                 delete dyn;
    1940              :             } else {
    1941            0 :                 dyn->setDynamicType(dyn->xmlText());
    1942            0 :                 segment->add(dyn);
    1943              :             }
    1944           70 :         } else if (tag == "Tempo") {
    1945            0 :             segment = m->getSegment(SegmentType::ChordRest, ctx.tick());
    1946            0 :             TempoText* t = Factory::createTempoText(segment);
    1947            0 :             t->setTrack(ctx.track());
    1948            0 :             readTempoText(t, e, ctx);
    1949            0 :             segment->add(t);
    1950           70 :         } else if (tag == "StaffText") {
    1951            2 :             segment = m->getSegment(SegmentType::ChordRest, ctx.tick());
    1952            2 :             StaffText* t = Factory::createStaffText(segment);
    1953            2 :             t->setTrack(ctx.track());
    1954            2 :             readStaffText(t, e, ctx);
    1955            2 :             segment->add(t);
    1956           68 :         } else if (tag == "Harmony") {
    1957            0 :             segment = m->getSegment(SegmentType::ChordRest, ctx.tick());
    1958            0 :             Harmony* h = Factory::createHarmony(segment);
    1959            0 :             h->setTrack(ctx.track());
    1960            0 :             readHarmony114(e, ctx, h);
    1961            0 :             segment->add(h);
    1962           68 :         } else if (tag == "Harmony"
    1963           68 :                    || tag == "FretDiagram"
    1964           68 :                    || tag == "TremoloBar"
    1965           68 :                    || tag == "Symbol"
    1966           68 :                    || tag == "StaffText"
    1967           68 :                    || tag == "RehearsalMark"
    1968           68 :                    || tag == "InstrumentChange"
    1969          136 :                    || tag == "StaffState"
    1970              :                    ) {
    1971            0 :             EngravingItem* el = Factory::createItemByName(tag, ctx.dummy());
    1972              :             // hack - needed because tick tags are unreliable in 1.3 scores
    1973              :             // for symbols attached to anything but a measure
    1974            0 :             if (el->isSymbol()) {
    1975            0 :                 el->setOwnershipParent(m);            // this will get reset when adding to segment
    1976              :             }
    1977            0 :             el->setTrack(ctx.track());
    1978            0 :             read400::TRead::readItem(el, e, ctx);
    1979            0 :             segment = m->getSegment(SegmentType::ChordRest, ctx.tick());
    1980            0 :             segment->add(el);
    1981           68 :         } else if (tag == "Jump") {
    1982            6 :             Jump* j = Factory::createJump(m);
    1983            6 :             j->setTrack(ctx.track());
    1984           54 :             while (e.readNextStartElement()) {
    1985           48 :                 const AsciiStringView t(e.name());
    1986           48 :                 if (t == "jumpTo") {
    1987            6 :                     j->setJumpTo(e.readText());
    1988           42 :                 } else if (t == "playUntil") {
    1989            6 :                     j->setPlayUntil(e.readText());
    1990           36 :                 } else if (t == "continueAt") {
    1991            6 :                     j->setContinueAt(e.readText());
    1992           30 :                 } else if (t == "subtype") {
    1993            6 :                     e.skipCurrentElement(); // obsolete, always "Repeat"
    1994           24 :                 } else if (!TRead::readTextProperties(j, e, ctx)) {
    1995           18 :                     e.unknown();
    1996              :                 }
    1997              :             }
    1998              : 
    1999              :             // infer jump type
    2000            6 :             String jumpTo = j->jumpTo();
    2001            6 :             String playUntil = j->playUntil();
    2002            6 :             if (jumpTo == "start") {
    2003            3 :                 if (playUntil == "end") {
    2004            1 :                     j->setJumpType(JumpType::DC);
    2005            2 :                 } else if (playUntil == "fine") {
    2006            1 :                     j->setJumpType(JumpType::DC_AL_FINE);
    2007              :                 } else {
    2008            1 :                     j->setJumpType(JumpType::DC_AL_CODA);
    2009              :                 }
    2010              :             } else {
    2011            3 :                 if (playUntil == "end") {
    2012            1 :                     j->setJumpType(JumpType::DS);
    2013            2 :                 } else if (playUntil == "fine") {
    2014            1 :                     j->setJumpType(JumpType::DS_AL_FINE);
    2015              :                 } else {
    2016            1 :                     j->setJumpType(JumpType::DS_AL_CODA);
    2017              :                 }
    2018              :             }
    2019              : 
    2020            6 :             m->add(j);
    2021           68 :         } else if (tag == "Marker") {
    2022            6 :             Marker* a = Factory::createMarker(m);
    2023            6 :             a->setTrack(ctx.track());
    2024              : 
    2025            6 :             MarkerType mt = MarkerType::SEGNO;
    2026           46 :             while (e.readNextStartElement()) {
    2027           40 :                 const AsciiStringView t(e.name());
    2028           40 :                 if (t == "subtype" || t == "label") {
    2029           12 :                     AsciiStringView s(e.readAsciiText());
    2030           12 :                     a->setLabel(String::fromAscii(s.ascii()));
    2031           12 :                     mt = TConv::fromXml(s, MarkerType::USER);
    2032           28 :                 } else if (!TRead::readTextProperties(a, e, ctx)) {
    2033           22 :                     e.unknown();
    2034              :                 }
    2035              :             }
    2036            6 :             a->setMarkerType(mt);
    2037              : 
    2038           11 :             if (a->markerType() == MarkerType::SEGNO || a->markerType() == MarkerType::CODA
    2039           11 :                 || a->markerType() == MarkerType::VARCODA || a->markerType() == MarkerType::CODETTA) {
    2040              :                 // force the marker type for correct display
    2041            4 :                 a->setXmlText(u"");
    2042            4 :                 a->setMarkerType(a->markerType());
    2043            4 :                 a->setTextStyleType(TextStyleType::REPEAT_LEFT);
    2044              :             }
    2045            6 :             m->add(a);
    2046           56 :         } else if (tag == "Image") {
    2047            0 :             if (MScore::noImages) {
    2048            0 :                 e.skipCurrentElement();
    2049              :             } else {
    2050            0 :                 segment = m->getSegment(SegmentType::ChordRest, ctx.tick());
    2051            0 :                 EngravingItem* el = Factory::createItemByName(tag, segment);
    2052            0 :                 el->setTrack(ctx.track());
    2053            0 :                 read400::TRead::readItem(el, e, ctx);
    2054            0 :                 segment->add(el);
    2055              :             }
    2056           56 :         } else if (tag == "stretch") {
    2057              :             // Ignore measure stretch pre 4.0
    2058            0 :             e.skipCurrentElement();
    2059           56 :         } else if (tag == "noOffset") {
    2060            0 :             m->setMeasureNumberOffset(e.readInt());
    2061           56 :         } else if (tag == "measureNumberMode") {
    2062            0 :             m->setMeasureNumberMode(MeasureNumberMode(e.readInt()));
    2063           56 :         } else if (tag == "irregular") {
    2064            0 :             m->setExcludeFromNumbering(true);
    2065            0 :             e.skipCurrentElement();
    2066           56 :         } else if (tag == "breakMultiMeasureRest") {
    2067            0 :             m->setBreakMultiMeasureRest(e.readBool());
    2068           56 :         } else if (tag == "sysInitBarLineType") {
    2069            0 :             segment = m->getSegment(SegmentType::BeginBarLine, m->tick());
    2070            0 :             BarLine* barLine = Factory::createBarLine(segment);
    2071            0 :             barLine->setTrack(ctx.track());
    2072            0 :             barLine->setBarLineType(TConv::fromXml(e.readAsciiText(), BarLineType::NORMAL));
    2073            0 :             segment->add(barLine);
    2074           56 :         } else if (tag == "Tuplet") {
    2075           16 :             int tupletId = e.intAttribute("id");
    2076           16 :             Tuplet* tuplet = Factory::createTuplet(m);
    2077           16 :             tuplet->setTrack(ctx.track());
    2078           16 :             tuplet->setTick(ctx.tick());
    2079           16 :             readTuplet(tuplet, e, ctx);
    2080           16 :             ctx.addTuplet(tupletId, tuplet);
    2081           40 :         } else if (tag == "startRepeat") {
    2082            0 :             m->setRepeatStart(true);
    2083            0 :             e.readNext();
    2084           40 :         } else if (tag == "endRepeat") {
    2085            0 :             m->setRepeatCount(e.readInt());
    2086            0 :             m->setRepeatEnd(true);
    2087           40 :         } else if (tag == "vspacer" || tag == "vspacerDown") {
    2088            0 :             if (!m->vspacerDown(staffIdx)) {
    2089            0 :                 Spacer* spacer = Factory::createSpacer(m);
    2090            0 :                 spacer->setSpacerType(SpacerType::DOWN);
    2091            0 :                 spacer->setTrack(staffIdx * VOICES);
    2092            0 :                 m->add(spacer);
    2093              :             }
    2094            0 :             m->vspacerDown(staffIdx)->setGap(Spatium(e.readDouble()));
    2095           40 :         } else if (tag == "vspacer" || tag == "vspacerUp") {
    2096            0 :             if (!m->vspacerUp(staffIdx)) {
    2097            0 :                 Spacer* spacer = Factory::createSpacer(m);
    2098            0 :                 spacer->setSpacerType(SpacerType::UP);
    2099            0 :                 spacer->setTrack(staffIdx * VOICES);
    2100            0 :                 m->add(spacer);
    2101              :             }
    2102            0 :             m->vspacerUp(staffIdx)->setGap(Spatium(e.readDouble()));
    2103           40 :         } else if (tag == "visible") {
    2104            0 :             m->setStaffVisible(staffIdx, e.readInt());
    2105           40 :         } else if (tag == "slashStyle") {
    2106            0 :             m->setStaffStemless(staffIdx, e.readInt());
    2107           40 :         } else if (tag == "Beam") {
    2108           20 :             int beamId = e.intAttribute("id");
    2109           20 :             Beam* beam = Factory::createBeam(ctx.score());
    2110           20 :             beam->setTrack(ctx.track());
    2111           20 :             read400::TRead::read(beam, e, ctx);
    2112           20 :             ctx.addBeam(beamId, beam);
    2113           20 :         } else if (tag == "Segment") {
    2114            0 :             if (segment) {
    2115            0 :                 read400::TRead::read(segment, e, ctx);
    2116              :             }
    2117            0 :             while (e.readNextStartElement()) {
    2118            0 :                 const AsciiStringView t(e.name());
    2119            0 :                 if (t == "off1") {
    2120            0 :                     double o = e.readDouble();
    2121            0 :                     LOGD("TODO: off1 %f", o);
    2122              :                 } else {
    2123            0 :                     e.unknown();
    2124              :                 }
    2125              :             }
    2126           20 :         } else if (tag == "MeasureNumber") {
    2127            0 :             MeasureNumber* noText = new MeasureNumber(m);
    2128            0 :             readText114(e, ctx, noText, m);
    2129            0 :             noText->setTrack(ctx.track());
    2130            0 :             m->setMeasureNumber(noText->staffIdx(), noText);
    2131           20 :         } else if (tag == "multiMeasureRest") {
    2132            0 :             m->setMMRestCount(e.readInt());
    2133              :             // set tick to previous measure
    2134            0 :             m->setTick(ctx.lastMeasure()->tick());
    2135            0 :             ctx.setTick(ctx.lastMeasure()->tick());
    2136           20 :         } else if (TRead::readProperties(static_cast<MeasureBase*>(m), e, ctx)) {
    2137              :         } else {
    2138            0 :             e.unknown();
    2139              :         }
    2140              :     }
    2141              :     // For nested tuplets created with MuseScore 1.3 tuplet dialog (i.e. "Other..." dialog),
    2142              :     // the parent tuplet was not set. Try to infer if the tuplet was actually a nested tuplet
    2143          462 :     for (auto& p : ctx.tuplets()) {
    2144           16 :         Tuplet* tuplet = p.second;
    2145           16 :         Fraction tupletTick = tuplet->tick();
    2146           16 :         Fraction tupletDuration = tuplet->actualTicks() - Fraction::eps();
    2147           16 :         std::vector<DurationElement*> tElements = tuplet->elements();
    2148           48 :         for (auto& p2 : ctx.tuplets()) {
    2149           32 :             Tuplet* tuplet2 = p2.second;
    2150           32 :             if ((tuplet2->tuplet()) || (tuplet2->voice() != tuplet->voice())) {     // already a nested tuplet or in a different voice
    2151            2 :                 continue;
    2152              :             }
    2153              :             // int possibleDuration = tuplet2->duration().ticks() * tuplet->ratio().denominator() / tuplet->ratio().numerator() - 1;
    2154           30 :             Fraction possibleDuration = tuplet2->ticks() * Fraction(tuplet->ratio().denominator(), (tuplet->ratio().numerator() - 1));
    2155              : 
    2156           15 :             if ((tuplet2 != tuplet) && (tuplet2->tick() >= tupletTick) && (tuplet2->tick() < tupletTick + tupletDuration)
    2157           45 :                 && (tuplet2->tick() + possibleDuration < tupletTick + tupletDuration)) {
    2158            2 :                 bool found = false;
    2159            4 :                 for (DurationElement* de : tElements) {
    2160            2 :                     if (de == tuplet2) {
    2161            0 :                         found = true;
    2162              :                     }
    2163              :                 }
    2164            2 :                 if (!found) {
    2165            2 :                     LOGD("Adding tuplet %p as nested tuplet to tuplet %p", tuplet2, tuplet);
    2166            2 :                     tuplet2->setTuplet(tuplet);
    2167            2 :                     tuplet->add(tuplet2);
    2168              :                 }
    2169              :             }
    2170              :         }
    2171           16 :     }
    2172          446 :     ctx.checkTuplets();
    2173          446 :     m->connectTremolo();
    2174          446 : }
    2175              : 
    2176              : //---------------------------------------------------------
    2177              : //   readBoxProperties
    2178              : //---------------------------------------------------------
    2179              : 
    2180              : static void readBox(XmlReader& e, ReadContext& ctx, Box* b);
    2181              : 
    2182           71 : static bool readBoxProperties(XmlReader& e, ReadContext& ctx, Box* b)
    2183              : {
    2184           71 :     const AsciiStringView tag(e.name());
    2185           71 :     if (tag == "height") {
    2186           10 :         b->setBoxHeight(Spatium(e.readDouble()));
    2187           61 :     } else if (tag == "width") {
    2188            1 :         b->setBoxWidth(Spatium(e.readDouble()));
    2189           60 :     } else if (tag == "topGap") {
    2190            0 :         read400::TRead::readProperty(b, e, ctx, Pid::TOP_GAP);
    2191           60 :     } else if (tag == "bottomGap") {
    2192            0 :         read400::TRead::readProperty(b, e, ctx, Pid::BOTTOM_GAP);
    2193           60 :     } else if (tag == "leftMargin") {
    2194           11 :         b->setLeftMargin(e.readDouble());
    2195           49 :     } else if (tag == "rightMargin") {
    2196           11 :         b->setRightMargin(e.readDouble());
    2197           38 :     } else if (tag == "topMargin") {
    2198           11 :         b->setTopMargin(e.readDouble());
    2199           27 :     } else if (tag == "bottomMargin") {
    2200           11 :         b->setBottomMargin(e.readDouble());
    2201           16 :     } else if (tag == "offset") {
    2202            0 :         b->setOffset(e.readPoint() * b->spatium());
    2203           16 :     } else if (tag == "pos") {      // ignore
    2204            0 :         e.readPoint();
    2205           16 :     } else if (tag == "Text") {
    2206              :         Text* t;
    2207           16 :         if (b->isTBox()) {
    2208            0 :             t = toTBox(b)->text();
    2209            0 :             readText114(e, ctx, t, t);
    2210              :         } else {
    2211           16 :             t = Factory::createText(b);
    2212           16 :             readText114(e, ctx, t, t);
    2213           16 :             if (t->empty()) {
    2214            0 :                 LOGD("read empty text");
    2215              :             } else {
    2216           16 :                 b->add(t);
    2217              :             }
    2218              :         }
    2219            0 :     } else if (tag == "Symbol") {
    2220            0 :         Symbol* s = new Symbol(b);
    2221            0 :         read400::TRead::read(s, e, ctx);
    2222            0 :         b->add(s);
    2223            0 :     } else if (tag == "Image") {
    2224            0 :         if (MScore::noImages) {
    2225            0 :             e.skipCurrentElement();
    2226              :         } else {
    2227            0 :             Image* image = new Image(b);
    2228            0 :             image->setTrack(ctx.track());
    2229            0 :             read400::TRead::read(image, e, ctx);
    2230            0 :             b->add(image);
    2231              :         }
    2232            0 :     } else if (tag == "HBox") {
    2233            0 :         HBox* hb = Factory::createHBox(b->score());
    2234            0 :         readBox(e, ctx, hb);
    2235            0 :         b->add(hb);
    2236            0 :     } else if (tag == "VBox") {
    2237            0 :         VBox* vb = Factory::createVBox(b->score());
    2238            0 :         readBox(e, ctx, vb);
    2239            0 :         b->add(vb);
    2240              :     }
    2241              : //      else if (MeasureBase::readProperties(e))
    2242              : //            ;
    2243              :     else {
    2244            0 :         return false;
    2245              :     }
    2246           71 :     return true;
    2247              : }
    2248              : 
    2249              : //---------------------------------------------------------
    2250              : //   readBox
    2251              : //---------------------------------------------------------
    2252              : 
    2253           11 : static void readBox(XmlReader& e, ReadContext& ctx, Box* b)
    2254              : {
    2255           11 :     b->setAutoSizeEnabled(false); // didn't exist in Mu1
    2256              : 
    2257           11 :     b->setBoxHeight(0_sp); // override default set in constructor
    2258           11 :     b->setBoxWidth(0_sp);
    2259           11 :     bool keepMargins = false; // whether original margins have to be kept when reading old file
    2260              : 
    2261           82 :     while (e.readNextStartElement()) {
    2262           71 :         const AsciiStringView tag(e.name());
    2263           71 :         if (tag == "HBox") {
    2264            0 :             HBox* hb = Factory::createHBox(b->score());
    2265            0 :             readBox(e, ctx, hb);
    2266            0 :             b->add(hb);
    2267            0 :             keepMargins = true;           // in old file, box nesting used outer box margins
    2268           71 :         } else if (tag == "VBox") {
    2269            0 :             VBox* vb = Factory::createVBox(b->score());
    2270            0 :             readBox(e, ctx, vb);
    2271            0 :             b->add(vb);
    2272            0 :             keepMargins = true;           // in old file, box nesting used outer box margins
    2273           71 :         } else if (!readBoxProperties(e, ctx, b)) {
    2274            0 :             e.unknown();
    2275              :         }
    2276              :     }
    2277              : 
    2278              :     // with .msc versions prior to 1.17, box margins were only used when nesting another box inside this box:
    2279              :     // for backward compatibility set them to 0.0 in all other cases, the Mu1 defaults of 5.0 just look horrible in Mu3 and Mu4
    2280              : 
    2281           11 :     if (ctx.mscVersion() <= 114 && (b->isHBox() || b->isVBox()) && !keepMargins) {
    2282           11 :         b->setLeftMargin(0.0);
    2283           11 :         b->setRightMargin(0.0);
    2284           11 :         b->setTopMargin(0.0); // 2.0 would look closest to Mu1 and Mu2, but 0.0 is the default since Mu2
    2285           11 :         b->setBottomMargin(0.0); // 1.0 would look closest to Mu1 and Mu2, but 0.0 is the default since Mu2
    2286              :     }
    2287           11 : }
    2288              : 
    2289              : //---------------------------------------------------------
    2290              : //   readStaffContent
    2291              : //---------------------------------------------------------
    2292              : 
    2293           33 : static void readStaffContent(Score* score, XmlReader& e, ReadContext& ctx)
    2294              : {
    2295           33 :     int staff = e.intAttribute("id", 1) - 1;
    2296           33 :     ctx.setTick(Fraction(0, 1));
    2297           33 :     ctx.setTrack(staff * VOICES);
    2298              : 
    2299           33 :     Measure* measure = score->firstMeasure();
    2300          490 :     while (e.readNextStartElement()) {
    2301          457 :         const AsciiStringView tag(e.name());
    2302              : 
    2303          457 :         if (tag == "Measure") {
    2304          446 :             if (staff == 0) {
    2305          359 :                 measure = Factory::createMeasure(score);
    2306          359 :                 measure->setTick(ctx.tick());
    2307          359 :                 const SigEvent& ev = score->sigmap()->timesig(measure->tick());
    2308          359 :                 measure->setTicks(ev.timesig());
    2309          359 :                 measure->setTimesig(ev.nominal());
    2310              : 
    2311          359 :                 readMeasure(measure, staff, e, ctx);
    2312          359 :                 measure->checkMeasure(staff);
    2313              : 
    2314          359 :                 if (!measure->isMMRest()) {
    2315          359 :                     score->measures()->append(measure);
    2316          359 :                     ctx.setLastMeasure(measure);
    2317          359 :                     ctx.setTick(measure->endTick());
    2318              :                 } else {
    2319              :                     // this is a multi measure rest
    2320              :                     // always preceded by the first measure it replaces
    2321            0 :                     Measure* m = ctx.lastMeasure();
    2322              : 
    2323            0 :                     if (m) {
    2324            0 :                         m->setMMRest(measure);
    2325            0 :                         measure->setTick(m->tick());
    2326              :                     }
    2327              :                 }
    2328              :             } else {
    2329           87 :                 if (measure == 0) {
    2330            0 :                     LOGD("Score::readStaff(): missing measure!");
    2331            0 :                     measure = Factory::createMeasure(score);
    2332            0 :                     measure->setTick(ctx.tick());
    2333            0 :                     score->measures()->append(measure);
    2334              :                 }
    2335           87 :                 ctx.setTick(measure->tick());
    2336              : 
    2337           87 :                 readMeasure(measure, staff, e, ctx);
    2338           87 :                 measure->checkMeasure(staff);
    2339              : 
    2340           87 :                 if (measure->isMMRest()) {
    2341            0 :                     measure = ctx.lastMeasure()->nextMeasure();
    2342              :                 } else {
    2343           87 :                     ctx.setLastMeasure(measure);
    2344           87 :                     if (measure->mmRest()) {
    2345            0 :                         measure = measure->mmRest();
    2346              :                     } else {
    2347           87 :                         measure = measure->nextMeasure();
    2348              :                     }
    2349              :                 }
    2350              :             }
    2351           11 :         } else if (tag == "HBox" || tag == "VBox" || tag == "TBox" || tag == "FBox") {
    2352           11 :             Box* mb = toBox(Factory::createItemByName(tag, score->dummy()));
    2353           11 :             readBox(e, ctx, mb);
    2354           11 :             mb->setTick(ctx.tick());
    2355           11 :             score->measures()->append(mb);
    2356            0 :         } else if (tag == "tick") {
    2357            0 :             ctx.setTick(Fraction::fromTicks(score->fileDivision(e.readInt())));
    2358              :         } else {
    2359            0 :             e.unknown();
    2360              :         }
    2361              :     }
    2362           33 : }
    2363              : 
    2364              : //---------------------------------------------------------
    2365              : //   readStaff
    2366              : //---------------------------------------------------------
    2367              : 
    2368           33 : static void readStaff(Staff* staff, XmlReader& e, ReadContext& ctx)
    2369              : {
    2370          133 :     while (e.readNextStartElement()) {
    2371          100 :         const AsciiStringView tag(e.name());
    2372          100 :         if (tag == "lines") {
    2373            1 :             int lines = e.readInt();
    2374            1 :             staff->setLines(Fraction(0, 1), lines);
    2375            1 :             if (lines != 5) {
    2376            1 :                 staff->setBarLineFrom(lines == 1 ? BARLINE_SPAN_1LINESTAFF_FROM : 0);
    2377            1 :                 staff->setBarLineTo(lines == 1 ? BARLINE_SPAN_1LINESTAFF_TO : (lines - 1) * 2);
    2378              :             }
    2379           99 :         } else if (tag == "small") {
    2380            0 :             staff->staffType(Fraction(0, 1))->setSmall(e.readInt());
    2381           99 :         } else if (tag == "invisible") {
    2382            0 :             staff->setIsLinesInvisible(Fraction(0, 1), e.readInt());
    2383           99 :         } else if (tag == "slashStyle") {
    2384            0 :             e.skipCurrentElement();
    2385           99 :         } else if (tag == "cleflist") {
    2386           33 :             staff->clefList().clear();
    2387           85 :             while (e.readNextStartElement()) {
    2388           52 :                 if (e.name() == "clef") {
    2389           52 :                     int tick    = e.intAttribute("tick", 0);
    2390           52 :                     ClefType ct = readClefType(e.intAttribute("idx", 0));
    2391           52 :                     staff->clefList().insert(std::pair<int, ClefType>(ctx.fileDivision(tick), ct));
    2392           52 :                     e.readNext();
    2393              :                 } else {
    2394            0 :                     e.unknown();
    2395              :                 }
    2396              :             }
    2397           33 :             if (staff->clefList().empty()) {
    2398            0 :                 staff->clefList().insert(std::pair<int, ClefType>(0, ClefType::G));
    2399              :             }
    2400           66 :         } else if (tag == "keylist") {
    2401           33 :             read400::TRead::read(staff->keyList(), e, ctx);
    2402           33 :         } else if (tag == "bracket") {
    2403           26 :             size_t col = ctx.score()->brackets(staff->idx()).size();
    2404           26 :             EditStaffBrackets::setBracketType(ctx.score(), staff->idx(), col, BracketType(e.intAttribute("type", -1)));
    2405           26 :             EditStaffBrackets::setBracketSpan(ctx.score(), staff->idx(), col, e.intAttribute("span", 0));
    2406           26 :             e.readNext();
    2407            7 :         } else if (tag == "barLineSpan") {
    2408            7 :             const int barLineSpan = e.readInt();
    2409            7 :             if (barLineSpan > 0) {
    2410            7 :                 ctx.setStaffBarLineSpan(staff->idx(), static_cast<size_t>(barLineSpan - 1));
    2411              :             } else {
    2412            0 :                 if (barLineSpan < 0) {
    2413            0 :                     LOGW() << "barLineSpan is negative: " << barLineSpan;
    2414              :                 }
    2415            0 :                 ctx.setStaffBarLineSpan(staff->idx(), 0);
    2416              :             }
    2417              :         } else {
    2418            0 :             e.unknown();
    2419              :         }
    2420              :     }
    2421           33 : }
    2422              : 
    2423              : //---------------------------------------------------------
    2424              : //   readDrumset
    2425              : //---------------------------------------------------------
    2426              : 
    2427           26 : static void readDrumset(Drumset* ds, XmlReader& e)
    2428              : {
    2429           26 :     int pitch = e.intAttribute("pitch", -1);
    2430           26 :     if (!pitchIsValid(pitch)) {
    2431            0 :         LOGD("load drumset: invalid pitch %d", pitch);
    2432            0 :         return;
    2433              :     }
    2434          158 :     while (e.readNextStartElement()) {
    2435          132 :         const AsciiStringView tag(e.name());
    2436          132 :         if (tag == "head") {
    2437           26 :             ds->drum(pitch).notehead = Read206::convertHeadGroup(e.readInt());
    2438          106 :         } else if (ds->readDrumProperties(e, pitch)) {
    2439              :         } else {
    2440            0 :             e.unknown();
    2441              :         }
    2442              :     }
    2443              : }
    2444              : 
    2445              : //---------------------------------------------------------
    2446              : //   readInstrument
    2447              : //---------------------------------------------------------
    2448              : 
    2449           26 : static void readInstrument(Instrument* i, Part* p, XmlReader& e, ReadContext& ctx)
    2450              : {
    2451           26 :     int program = -1;
    2452           26 :     int bank    = 0;
    2453           26 :     int chorus  = 30;
    2454           26 :     int reverb  = 30;
    2455           26 :     int volume  = 100;
    2456           26 :     int pan     = 60;
    2457           26 :     bool customDrumset = false;
    2458           26 :     i->clearChannels();         // remove default channel
    2459          198 :     while (e.readNextStartElement()) {
    2460          172 :         const AsciiStringView tag(e.name());
    2461          172 :         if (tag == "chorus") {
    2462            0 :             chorus = e.readInt();
    2463          172 :         } else if (tag == "reverb") {
    2464            0 :             reverb = e.readInt();
    2465          172 :         } else if (tag == "midiProgram") {
    2466            0 :             program = e.readInt();
    2467          172 :         } else if (tag == "volume") {
    2468            0 :             volume = e.readInt();
    2469          172 :         } else if (tag == "pan") {
    2470            0 :             pan = e.readInt();
    2471          172 :         } else if (tag == "midiChannel") {
    2472            0 :             e.skipCurrentElement();
    2473          172 :         } else if (tag == "Drum") {
    2474              :             // if we see one of this tags, a custom drumset will
    2475              :             // be created
    2476           26 :             if (!i->drumset()) {
    2477            0 :                 i->setDrumset(new Drumset(*smDrumset));
    2478              :             }
    2479           26 :             if (!customDrumset) {
    2480            2 :                 i->drumset()->clear();
    2481            2 :                 customDrumset = true;
    2482              :             }
    2483           26 :             readDrumset(i->drumset(), e);
    2484          146 :         } else if (read400::TRead::readProperties(i, e, ctx, p, &customDrumset)) {
    2485              :         } else {
    2486            0 :             e.unknown();
    2487              :         }
    2488              :     }
    2489              : 
    2490           26 :     i->updateInstrumentId();
    2491              : 
    2492           26 :     if (program == -1) {
    2493           26 :         program = i->recognizeMidiProgram();
    2494              :     }
    2495              : 
    2496           26 :     if (i->channel().empty()) {        // for backward compatibility
    2497            0 :         InstrChannel* a = new InstrChannel;
    2498            0 :         a->setName(String::fromUtf8(InstrChannel::DEFAULT_NAME));
    2499            0 :         a->setProgram(program);
    2500            0 :         a->setBank(bank);
    2501            0 :         a->setVolume(volume);
    2502            0 :         a->setPan(pan);
    2503            0 :         a->setReverb(reverb);
    2504            0 :         a->setChorus(chorus);
    2505            0 :         i->appendChannel(a);
    2506           26 :     } else if (i->channel(0)->program() < 0) {
    2507            0 :         i->channel(0)->setProgram(program);
    2508              :     }
    2509           26 :     if (i->useDrumset()) {
    2510            2 :         if (i->channel()[0]->bank() == 0) {
    2511            0 :             i->channel()[0]->setBank(128);
    2512              :         }
    2513              :     }
    2514              : 
    2515              :     // Fix user bank controller read
    2516           52 :     for (InstrChannel* c : i->channel()) {
    2517           26 :         if (c->bank() == 0) {
    2518           24 :             c->setUserBankController(false);
    2519              :         }
    2520              :     }
    2521              : 
    2522              :     // Read single-note dynamics from template
    2523           26 :     i->setSingleNoteDynamicsFromTemplate();
    2524           26 : }
    2525              : 
    2526              : //---------------------------------------------------------
    2527              : //   readPart
    2528              : //---------------------------------------------------------
    2529              : 
    2530           26 : static void readPart(Part* part, XmlReader& e, ReadContext& ctx)
    2531              : {
    2532          109 :     while (e.readNextStartElement()) {
    2533           83 :         const AsciiStringView tag(e.name());
    2534           83 :         if (tag == "Staff") {
    2535           33 :             Staff* staff = Factory::createStaff(part);
    2536           33 :             staff->setStaffType(Fraction(0, 1), StaffType());       // will reset later if needed
    2537           33 :             ctx.appendStaff(staff);
    2538           33 :             readStaff(staff, e, ctx);
    2539           50 :         } else if (tag == "Instrument") {
    2540           26 :             Instrument* i = part->instrument();
    2541           26 :             readInstrument(i, part, e, ctx);
    2542              :             // add string data from MIDI program number, if possible
    2543           26 :             if (i->stringData()->strings() == 0
    2544           26 :                 && i->channel().size() > 0
    2545           52 :                 && i->drumset() == nullptr) {
    2546           24 :                 int program = i->channel(0)->program();
    2547           24 :                 if (program >= 24 && program <= 30) {             // guitars
    2548            1 :                     i->setStringData(StringData(19, 6, g_guitarStrings));
    2549           23 :                 } else if ((program >= 32 && program <= 39) || program == 43) {           // bass / double-bass
    2550            0 :                     i->setStringData(StringData(24, 4, g_bassStrings));
    2551           23 :                 } else if (program == 40) {                       // violin and other treble string instr.
    2552            0 :                     i->setStringData(StringData(24, 4, g_violinStrings));
    2553           23 :                 } else if (program == 41) {                       // viola and other alto string instr.
    2554            0 :                     i->setStringData(StringData(24, 4, g_violaStrings));
    2555           23 :                 } else if (program == 42) {                       // cello and other bass string instr.
    2556            0 :                     i->setStringData(StringData(24, 4, g_celloStrings));
    2557              :                 }
    2558              :             }
    2559           26 :             Drumset* d = i->drumset();
    2560           26 :             Staff* st = part->staff(0);
    2561           26 :             if (d && st && st->lines(Fraction(0, 1)) != 5) {
    2562            1 :                 int n = 0;
    2563            1 :                 if (st->lines(Fraction(0, 1)) == 1) {
    2564            1 :                     n = 4;
    2565              :                 }
    2566          129 :                 for (int j = 0; j < DRUM_INSTRUMENTS; ++j) {
    2567          128 :                     d->drum(j).line -= n;
    2568              :                 }
    2569              :             }
    2570           24 :         } else if (tag == "name") {
    2571           12 :             Text* t = Factory::createText(ctx.dummy(), TextStyleType::DEFAULT, false);
    2572           12 :             readText114(e, ctx, t, t);
    2573           12 :             part->instrument()->setLongName(t->xmlText());
    2574           12 :             delete t;
    2575           12 :         } else if (tag == "shortName") {
    2576           12 :             Text* t = Factory::createText(ctx.dummy(), TextStyleType::DEFAULT, false);
    2577           12 :             readText114(e, ctx, t, t);
    2578           12 :             part->instrument()->setShortName(t->xmlText());
    2579           12 :             delete t;
    2580            0 :         } else if (tag == "show") {
    2581            0 :             part->setShow(e.readInt());
    2582              :         } else {
    2583            0 :             e.unknown();
    2584              :         }
    2585              :     }
    2586              : 
    2587           26 :     if (part->instrument()->useDrumset()) {
    2588            4 :         for (Staff* staff : part->staves()) {
    2589            2 :             int lines = staff->lines(Fraction(0, 1));
    2590            2 :             int bf    = staff->barLineFrom();
    2591            2 :             int bt    = staff->barLineTo();
    2592            2 :             staff->setStaffType(Fraction(0, 1), *StaffType::getDefaultPreset(StaffGroup::PERCUSSION));
    2593              : 
    2594              :             // this allows 2/3-line percussion staves to keep the double spacing they had in 1.3
    2595              : 
    2596            2 :             if (lines == 2 || lines == 3) {
    2597            0 :                 ((StaffType*)(staff->staffType(Fraction(0, 1))))->setLineDistance(2.0_sp);
    2598              :             }
    2599              : 
    2600            2 :             staff->setLines(Fraction(0, 1), lines);             // this also sets stepOffset
    2601            2 :             staff->setBarLineFrom(bf);
    2602            2 :             staff->setBarLineTo(bt);
    2603              :         }
    2604              :     }
    2605              :     //set default articulations
    2606           26 :     std::vector<MidiArticulation> articulations;
    2607           26 :     articulations.push_back(MidiArticulation(u"", u"", 100, 100));
    2608           26 :     articulations.push_back(MidiArticulation(u"staccato", u"", 100, 50));
    2609           26 :     articulations.push_back(MidiArticulation(u"tenuto", u"", 100, 100));
    2610           26 :     articulations.push_back(MidiArticulation(u"sforzato", u"", 120, 100));
    2611           26 :     part->instrument()->setArticulation(articulations);
    2612           26 : }
    2613              : 
    2614              : //---------------------------------------------------------
    2615              : //   readPageFormat
    2616              : //---------------------------------------------------------
    2617              : 
    2618           26 : static void readPageFormat(PageFormat* pf, int& pageNumberOffset, XmlReader& e)
    2619              : {
    2620           26 :     double _oddRightMargin  = 0.0;
    2621           26 :     double _evenRightMargin = 0.0;
    2622           26 :     bool landscape         = false;
    2623           26 :     AsciiStringView type;
    2624              : 
    2625          128 :     while (e.readNextStartElement()) {
    2626          102 :         const AsciiStringView tag(e.name());
    2627          102 :         if (tag == "landscape") {
    2628            0 :             landscape = e.readInt();
    2629          102 :         } else if (tag == "page-margins") {
    2630           50 :             type = e.asciiAttribute("type", "both");
    2631           50 :             double lm = 0.0, rm = 0.0, tm = 0.0, bm = 0.0;
    2632          250 :             while (e.readNextStartElement()) {
    2633          200 :                 const AsciiStringView t(e.name());
    2634          200 :                 double val = e.readDouble() * 0.5 / PPI;
    2635          200 :                 if (t == "left-margin") {
    2636           50 :                     lm = val;
    2637          150 :                 } else if (t == "right-margin") {
    2638           50 :                     rm = val;
    2639          100 :                 } else if (t == "top-margin") {
    2640           50 :                     tm = val;
    2641           50 :                 } else if (t == "bottom-margin") {
    2642           50 :                     bm = val;
    2643              :                 } else {
    2644            0 :                     e.unknown();
    2645              :                 }
    2646              :             }
    2647           50 :             pf->setTwosided(type == "odd" || type == "even");
    2648           50 :             if (type == "odd" || type == "both") {
    2649           26 :                 pf->setOddLeftMargin(lm);
    2650           26 :                 _oddRightMargin = rm;
    2651           26 :                 pf->setOddTopMargin(tm);
    2652           26 :                 pf->setOddBottomMargin(bm);
    2653              :             }
    2654           50 :             if (type == "even" || type == "both") {
    2655           26 :                 pf->setEvenLeftMargin(lm);
    2656           26 :                 _evenRightMargin = rm;
    2657           26 :                 pf->setEvenTopMargin(tm);
    2658           26 :                 pf->setEvenBottomMargin(bm);
    2659              :             }
    2660           52 :         } else if (tag == "page-height") {
    2661            0 :             pf->setSize(SizeF(pf->size().width(), e.readDouble() * 0.5 / PPI));
    2662           52 :         } else if (tag == "page-width") {
    2663            0 :             pf->setSize(SizeF(e.readDouble() * 0.5 / PPI, pf->size().height()));
    2664           52 :         } else if (tag == "pageFormat") {
    2665           26 :             const PaperSize* s = getPaperSize114(e.readText());
    2666           26 :             pf->setSize(SizeF(s->w, s->h));
    2667           26 :         } else if (tag == "page-offset") {
    2668           26 :             pageNumberOffset = e.readInt();
    2669              :         } else {
    2670            0 :             e.unknown();
    2671              :         }
    2672              :     }
    2673           26 :     if (landscape) {
    2674            0 :         pf->setSize(pf->size().transposed());
    2675              :     }
    2676           26 :     double w1 = pf->size().width() - pf->oddLeftMargin() - _oddRightMargin;
    2677           26 :     double w2 = pf->size().width() - pf->evenLeftMargin() - _evenRightMargin;
    2678           26 :     pf->setPrintableWidth(std::min(w1, w2));       // silently adjust right margins
    2679           26 : }
    2680              : 
    2681              : //---------------------------------------------------------
    2682              : //   readStyle
    2683              : //---------------------------------------------------------
    2684              : 
    2685           25 : static void readStyle(MStyle* style, XmlReader& e, ReadChordListHook& readChordListHook)
    2686              : {
    2687          173 :     while (e.readNextStartElement()) {
    2688          148 :         String tag = String::fromAscii(e.name().ascii());
    2689              : 
    2690          148 :         if (tag == "lyricsDistance") {          // was renamed
    2691            1 :             tag = u"lyricsPosBelow";
    2692              :         }
    2693              : 
    2694          148 :         if (tag == "TextStyle") {
    2695            0 :             e.skipCurrentElement();
    2696          148 :         } else if (tag == "lyricsMinBottomDistance") {
    2697              :             // no longer meaningful since it is now measured from skyline rather than staff
    2698              :             //style->set(Sid::lyricsMinBottomDistance, PointF(0.0, y));
    2699            1 :             e.skipCurrentElement();
    2700          147 :         } else if (tag == "Spatium") {
    2701            0 :             style->set(Sid::spatium, e.readDouble() * DPMM);
    2702          147 :         } else if (tag == "page-layout") {
    2703            0 :             readPageFormat206(style, e);
    2704          147 :         } else if (tag == "displayInConcertPitch") {
    2705            0 :             style->set(Sid::concertPitch, bool(e.readInt()));
    2706          147 :         } else if (tag == "ChordList") {
    2707            0 :             readChordListHook.read(e);
    2708          147 :         } else if (tag == "pageFillLimit" || tag == "genTimesig" || tag == "FixMeasureNumbers" || tag == "FixMeasureWidth") {   // obsolete
    2709            8 :             e.skipCurrentElement();
    2710          139 :         } else if (tag == "systemDistance") {  // obsolete
    2711            6 :             style->set(Sid::minSystemDistance, e.readDouble());
    2712          133 :         } else if (tag == "stemDir") {
    2713            0 :             int voice = e.intAttribute("voice", 1) - 1;
    2714            0 :             switch (voice) {
    2715            0 :             case 0: tag = u"StemDir1";
    2716            0 :                 break;
    2717            0 :             case 1: tag = u"StemDir2";
    2718            0 :                 break;
    2719            0 :             case 2: tag = u"StemDir3";
    2720            0 :                 break;
    2721            0 :             case 3: tag = u"StemDir4";
    2722            0 :                 break;
    2723              :             }
    2724              :         }
    2725              :         // for compatibility:
    2726          133 :         else if (tag == "oddHeader" || tag == "evenHeader" || tag == "oddFooter" || tag == "evenFooter") {
    2727            0 :             tag += u"C";
    2728              :         } else {
    2729          133 :             if (!ReadStyleHook::readStyleProperties(style, e)) {
    2730           48 :                 e.skipCurrentElement();
    2731              :             }
    2732              :         }
    2733          148 :     }
    2734              : 
    2735           25 :     readChordListHook.validate();
    2736           25 : }
    2737              : 
    2738              : //---------------------------------------------------------
    2739              : //   read114
    2740              : //    import old version <= 1.3 files
    2741              : //---------------------------------------------------------
    2742              : 
    2743           26 : muse::Ret Read114::readScoreFile(Score* score, XmlReader& e, ReadInOutData* out)
    2744              : {
    2745           26 :     IF_ASSERT_FAILED(score->isMaster()) {
    2746            0 :         return make_ret(Err::FileUnknownError);
    2747              :     }
    2748              : 
    2749           26 :     ReadContext ctx(score);
    2750           26 :     if (out) {
    2751           26 :         if (out->overriddenSpatium.has_value()) {
    2752            0 :             ctx.setSpatium(out->overriddenSpatium.value());
    2753            0 :             ctx.setOverrideSpatium(true);
    2754              :         }
    2755              : 
    2756           26 :         ctx.setPropertiesToSkip(out->propertiesToSkip);
    2757           26 :         ctx.setForcePageMode(out->forcePageMode);
    2758              :     }
    2759              : 
    2760           26 :     DEFER {
    2761           26 :         if (out) {
    2762           26 :             out->settingsCompat = std::move(ctx.settingCompat());
    2763              :         }
    2764           26 :     };
    2765              : 
    2766           26 :     MasterScore* masterScore = static_cast<MasterScore*>(score);
    2767              : 
    2768           26 :     std::map<int, double> tm;
    2769          414 :     while (e.readNextStartElement()) {
    2770          388 :         ctx.setTrack(muse::nidx);
    2771          388 :         const AsciiStringView tag(e.name());
    2772          388 :         if (tag == "Staff") {
    2773           33 :             readStaffContent(masterScore, e, ctx);
    2774          355 :         } else if (tag == "KeySig") {                 // not supported
    2775            0 :             KeySig* ks = Factory::createKeySig(masterScore->dummy());
    2776            0 :             read400::TRead::read(ks, e, ctx);
    2777            0 :             delete ks;
    2778          355 :         } else if (tag == "siglist") {
    2779           26 :             Read206::readTimeSigMap(masterScore->m_sigmap, e, ctx);
    2780          329 :         } else if (tag == "programVersion") {
    2781           24 :             masterScore->setMscoreVersion(e.readText());
    2782          305 :         } else if (tag == "programRevision") {
    2783           24 :             masterScore->setMscoreRevision(e.readInt());
    2784          281 :         } else if (tag == "Mag"
    2785          281 :                    || tag == "MagIdx"
    2786          281 :                    || tag == "xoff"
    2787          281 :                    || tag == "Symbols"
    2788          281 :                    || tag == "cursorTrack"
    2789          562 :                    || tag == "yoff") {
    2790           23 :             e.skipCurrentElement();             // obsolete
    2791          258 :         } else if (tag == "tempolist") {
    2792              :             // store the tempo list to create invisible tempo text later
    2793           26 :             masterScore->setTempoMultiplier(e.doubleAttribute("fix", 2.0));
    2794           26 :             while (e.readNextStartElement()) {
    2795            0 :                 if (e.name() == "tempo") {
    2796            0 :                     int tick   = e.attribute("tick").toInt();
    2797            0 :                     double tmp = e.readText().toDouble();
    2798            0 :                     tick       = ctx.fileDivision(tick);
    2799            0 :                     tm[tick] = tmp;
    2800            0 :                 } else if (e.name() == "relTempo") {
    2801            0 :                     e.readText();
    2802              :                 } else {
    2803            0 :                     e.unknown();
    2804              :                 }
    2805              :             }
    2806          232 :         } else if (tag == "playMode") {
    2807            0 :             e.skipCurrentElement();
    2808          232 :         } else if (tag == "SyntiSettings") {
    2809            0 :             masterScore->m_synthesizerState.read(e);
    2810          232 :         } else if (tag == "Spatium") {
    2811           26 :             if (ctx.overrideSpatium()) {
    2812            0 :                 masterScore->style().setSpatium(ctx.spatium());
    2813            0 :                 if (out) {
    2814            0 :                     out->originalSpatium = e.readDouble() * DPMM;
    2815              :                 } else {
    2816            0 :                     e.skipCurrentElement();
    2817              :                 }
    2818              :             } else {
    2819           26 :                 masterScore->style().setSpatium(e.readDouble() * DPMM);
    2820              :             }
    2821          206 :         } else if (tag == "Division") {
    2822           26 :             masterScore->m_fileDivision = e.readInt();
    2823          180 :         } else if (tag == "showInvisible") {
    2824           26 :             masterScore->setShowInvisible(e.readInt());
    2825          154 :         } else if (tag == "showFrames") {
    2826           26 :             masterScore->setShowFrames(e.readInt());
    2827          128 :         } else if (tag == "showMargins") {
    2828            0 :             masterScore->setShowPageborders(e.readInt());
    2829          128 :         } else if (tag == "Style") {
    2830           25 :             double sp = masterScore->style().spatium();
    2831           25 :             compat::ReadChordListHook clhook(masterScore);
    2832           25 :             readStyle(&masterScore->style(), e, clhook);
    2833           25 :             if (ctx.overrideSpatium()) {
    2834            0 :                 masterScore->style().setSpatium(sp);
    2835              :             }
    2836          128 :         } else if (tag == "TextStyle") {
    2837           30 :             e.skipCurrentElement();
    2838           73 :         } else if (tag == "page-layout") {
    2839           26 :             compat::PageFormat pf;
    2840           26 :             int pageNumberOffset = 0;
    2841           26 :             initPageFormat(&masterScore->style(), &pf);
    2842           26 :             readPageFormat(&pf, pageNumberOffset, e);
    2843           26 :             setPageFormat(&masterScore->style(), pf);
    2844           26 :             masterScore->setPageNumberOffset(pageNumberOffset);
    2845           47 :         } else if (tag == "copyright" || tag == "rights") {
    2846            1 :             Text* text = Factory::createText(masterScore->dummy(), TextStyleType::DEFAULT, false);
    2847            1 :             readText114(e, ctx, text, text);
    2848            1 :             masterScore->setMetaTag(u"copyright", text->plainText());
    2849            1 :             delete text;
    2850           46 :         } else if (tag == "movement-number") {
    2851            0 :             masterScore->setMetaTag(u"movementNumber", e.readText());
    2852           46 :         } else if (tag == "movement-title") {
    2853            0 :             masterScore->setMetaTag(u"movementTitle", e.readText());
    2854           46 :         } else if (tag == "work-number") {
    2855            0 :             masterScore->setMetaTag(u"workNumber", e.readText());
    2856           46 :         } else if (tag == "work-title") {
    2857            0 :             masterScore->setMetaTag(u"workTitle", e.readText());
    2858           46 :         } else if (tag == "source") {
    2859            0 :             masterScore->setMetaTag(u"source", e.readText());
    2860           46 :         } else if (tag == "metaTag") {
    2861            0 :             String name = e.attribute("name");
    2862            0 :             masterScore->setMetaTag(name, e.readText());
    2863           46 :         } else if (tag == "Part") {
    2864           26 :             Part* part = new Part(masterScore);
    2865           26 :             readPart(part, e, ctx);
    2866           26 :             masterScore->appendPart(part);
    2867           20 :         } else if (tag == "Slur") {
    2868           16 :             Slur* slur = Factory::createSlur(ctx.dummy());
    2869           16 :             Read206::readSlur206(e, ctx, slur);
    2870           16 :             ctx.addSpanner(slur);
    2871            4 :         } else if ((tag == "HairPin")
    2872            3 :                    || (tag == "Ottava")
    2873            2 :                    || (tag == "TextLine")
    2874            1 :                    || (tag == "Volta")
    2875            1 :                    || (tag == "Trill")
    2876            7 :                    || (tag == "Pedal")) {
    2877            4 :             Spanner* s = toSpanner(Factory::createItemByName(tag, masterScore->dummy()));
    2878            4 :             if (tag == "Volta") {
    2879            0 :                 readVolta114(e, ctx, toVolta(s));
    2880            4 :             } else if (tag == "Ottava") {
    2881            1 :                 readOttava114(e, ctx, toOttava(s));
    2882            3 :             } else if (tag == "TextLine") {
    2883            1 :                 readTextLine114(e, ctx, toTextLine(s));
    2884            2 :             } else if (tag == "Pedal") {
    2885            1 :                 readPedal114(e, ctx, toPedal(s));
    2886            1 :             } else if (tag == "Trill") {
    2887            0 :                 Ornament* ornament = Factory::createOrnament(score->dummy());
    2888            0 :                 toTrill(s)->setOrnament(ornament);
    2889            0 :                 Read206::readTrill206(e, ctx, toTrill(s));
    2890              :             } else {
    2891            1 :                 assert(tag == "HairPin");
    2892            1 :                 Read206::readHairpin206(e, ctx, toHairpin(s));
    2893              :             }
    2894            4 :             if (s->track() == muse::nidx) {
    2895            0 :                 s->setTrack(ctx.track());
    2896              :             } else {
    2897            4 :                 ctx.setTrack(s->track());               // update current track
    2898              :             }
    2899            4 :             if (s->tick() == Fraction(-1, 1)) {
    2900            0 :                 s->setTick(ctx.tick());
    2901              :             } else {
    2902            4 :                 ctx.setTick(s->tick());              // update current tick
    2903              :             }
    2904            4 :             if (s->track2() == muse::nidx) {
    2905            0 :                 s->setTrack2(s->track());
    2906              :             }
    2907            4 :             if (s->ticks().isZero()) {
    2908            0 :                 LOGD("zero spanner %s ticks: %d", s->typeName(), s->ticks().ticks());
    2909            0 :                 delete s;
    2910              :             } else {
    2911            4 :                 masterScore->addSpanner(s);
    2912              :             }
    2913            0 :         } else if (tag == "Excerpt") {
    2914            0 :             Excerpt* ex = new Excerpt(masterScore);
    2915            0 :             readExcerpt(ex, e, ctx);
    2916            0 :             masterScore->m_excerpts.push_back(ex);
    2917            0 :         } else if (tag == "Beam") {
    2918            0 :             Beam* beam = Factory::createBeam(masterScore);
    2919            0 :             read400::TRead::read(beam, e, ctx);
    2920              :             // _beams.append(beam);
    2921            0 :             delete beam;
    2922              :         } else {
    2923            0 :             e.unknown();
    2924              :         }
    2925              :     }
    2926              : 
    2927           26 :     if (e.error() != muse::XmlStreamReader::NoError) {
    2928            0 :         LOGD() << "XML read error at byte offset " << e.byteOffset() << ": " << e.errorString();
    2929            0 :         return make_ret(Err::FileBadFormat, e.errorString());
    2930              :     }
    2931              : 
    2932           26 :     setBarLineSpanToStaves(masterScore, ctx);
    2933              : 
    2934           59 :     for (Staff* s : masterScore->staves()) {
    2935           33 :         size_t idx = s->idx();
    2936           33 :         track_idx_t track = idx * VOICES;
    2937              : 
    2938           85 :         for (auto i : s->clefList()) {
    2939           52 :             Fraction tick   = Fraction::fromTicks(i.first);
    2940           52 :             ClefType clefId = i.second.concertClef;
    2941           52 :             Measure* m      = masterScore->tick2measure(tick);
    2942           52 :             if (!m) {
    2943            0 :                 continue;
    2944              :             }
    2945           52 :             SegmentType st = SegmentType::Clef;
    2946           52 :             if (tick == m->tick()) {
    2947           52 :                 if (m->prevMeasure()) {
    2948           20 :                     m = m->prevMeasure();
    2949              :                 } else {
    2950           32 :                     st = SegmentType::HeaderClef;
    2951              :                 }
    2952              :             }
    2953           52 :             Segment* seg = m->getSegment(st, tick);
    2954           52 :             if (seg->element(track)) {
    2955           21 :                 seg->element(track)->setGenerated(false);
    2956              :             } else {
    2957           31 :                 Clef* clef = Factory::createClef(seg);
    2958           31 :                 clef->setClefType(clefId);
    2959           31 :                 clef->setTrack(track);
    2960           31 :                 clef->setGenerated(false);
    2961           31 :                 seg->add(clef);
    2962              :             }
    2963              :         }
    2964              : 
    2965              :         // create missing KeySig
    2966           33 :         KeyList* km = s->keyList();
    2967           69 :         for (auto i = km->begin(); i != km->end(); ++i) {
    2968           36 :             Fraction tick = Fraction::fromTicks(i->first);
    2969           36 :             if (tick < Fraction(0, 1)) {
    2970            0 :                 LOGD("read114: Key tick %d", tick.ticks());
    2971            0 :                 continue;
    2972              :             }
    2973           36 :             Measure* m = masterScore->tick2measure(tick);
    2974           36 :             if (!m) {               //empty score
    2975            0 :                 break;
    2976              :             }
    2977           36 :             Segment* seg = m->getSegment(SegmentType::KeySig, tick);
    2978           36 :             if (seg->element(track)) {
    2979            4 :                 toKeySig(seg->element(track))->setGenerated(false);
    2980              :             } else {
    2981           32 :                 KeySigEvent ke = i->second;
    2982           32 :                 KeySig* ks = Factory::createKeySig(seg);
    2983           32 :                 ks->setKeySigEvent(ke);
    2984           32 :                 ks->setTrack(track);
    2985           32 :                 ks->setGenerated(false);
    2986           32 :                 seg->add(ks);
    2987           32 :             }
    2988              :         }
    2989              :     }
    2990              : 
    2991           46 :     for (std::pair<int, Spanner*> p : masterScore->spanner()) {
    2992           20 :         Spanner* s = p.second;
    2993           20 :         if (s->isOttava() || s->isPedal() || s->isTrill() || s->isTextLine()) {
    2994            3 :             double yo = 0;
    2995            3 :             if (s->isOttava()) {
    2996              :                 // fix ottava position
    2997            1 :                 yo = masterScore->styleValue(Pid::OFFSET, Sid::ottavaPosAbove).value<PointF>().y();
    2998            1 :                 if (s->placeBelow()) {
    2999            0 :                     yo = -yo + s->staff()->staffHeight();
    3000              :                 }
    3001            2 :             } else if (s->isPedal()) {
    3002            1 :                 yo = masterScore->styleValue(Pid::OFFSET, Sid::pedalPosBelow).value<PointF>().y();
    3003            1 :             } else if (s->isTrill()) {
    3004            0 :                 yo = masterScore->styleValue(Pid::OFFSET, Sid::trillPosAbove).value<PointF>().y();
    3005            1 :             } else if (s->isTextLine()) {
    3006            1 :                 yo = -5.0 * masterScore->style().spatium();
    3007              :             }
    3008            3 :             if (!s->spannerSegments().empty()) {
    3009            6 :                 for (SpannerSegment* seg : s->spannerSegments()) {
    3010            3 :                     if (!seg->offset().isNull()) {
    3011            0 :                         seg->ryoffset() = seg->offset().y() - yo;
    3012              :                     }
    3013              :                 }
    3014              :             } else {
    3015            0 :                 s->ryoffset() = -yo;
    3016              :             }
    3017              :         }
    3018              :     }
    3019              : 
    3020              :     //
    3021              :     // remove "middle beam" flags from first ChordRest in
    3022              :     // measure
    3023              :     //
    3024          385 :     for (Measure* m = masterScore->firstMeasure(); m; m = m->nextMeasure()) {
    3025          359 :         size_t tracks = masterScore->nstaves() * VOICES;
    3026          359 :         bool first = true;
    3027         2143 :         for (size_t track = 0; track < tracks; ++track) {
    3028         7328 :             for (Segment* s = m->first(); s; s = s->next()) {
    3029         5544 :                 if (s->segmentType() != SegmentType::ChordRest) {
    3030         2272 :                     continue;
    3031              :                 }
    3032         3272 :                 ChordRest* cr = toChordRest(s->element(static_cast<int>(track)));
    3033         3272 :                 if (cr) {
    3034          800 :                     if (!first) {
    3035            0 :                         switch (cr->beamMode()) {
    3036            0 :                         case BeamMode::AUTO:
    3037              :                         case BeamMode::BEGIN:
    3038              :                         case BeamMode::END:
    3039              :                         case BeamMode::NONE:
    3040            0 :                             break;
    3041            0 :                         case BeamMode::MID:
    3042              :                         case BeamMode::BEGIN16:
    3043              :                         case BeamMode::BEGIN32:
    3044            0 :                             cr->setBeamMode(BeamMode::BEGIN);
    3045            0 :                             break;
    3046            0 :                         case BeamMode::INVALID:
    3047            0 :                             if (cr->isChord()) {
    3048            0 :                                 cr->setBeamMode(BeamMode::AUTO);
    3049              :                             } else {
    3050            0 :                                 cr->setBeamMode(BeamMode::NONE);
    3051              :                             }
    3052            0 :                             break;
    3053              :                         }
    3054            0 :                         first = false;
    3055              :                     }
    3056              :                 }
    3057              :             }
    3058              :         }
    3059              :     }
    3060          396 :     for (MeasureBase* mb = masterScore->first(); mb; mb = mb->next()) {
    3061          370 :         if (mb->isVBox()) {
    3062           10 :             VBox* b  = toVBox(mb);
    3063           10 :             Spatium y = masterScore->style().styleS(Sid::staffUpperBorder);
    3064           10 :             b->setBottomGap(y);
    3065              :         }
    3066              :     }
    3067              : 
    3068           26 :     masterScore->m_fileDivision = Constants::DIVISION;
    3069              : 
    3070           59 :     for (Staff* staff : masterScore->staves()) {
    3071           33 :         staff->updateOttava();
    3072              :     }
    3073              : 
    3074              :     // adjust some styles
    3075           26 :     if (masterScore->style().styleB(Sid::hideEmptyStaves)) {        // http://musescore.org/en/node/16228
    3076            1 :         masterScore->style().set(Sid::dontHideStavesInFirstSystem, false);
    3077              :     }
    3078           26 :     if (masterScore->style().styleB(Sid::showPageNumberOne)) {      // http://musescore.org/en/node/21207
    3079            1 :         masterScore->style().set(Sid::evenFooterL, String(u"$P"));
    3080            1 :         masterScore->style().set(Sid::oddFooterR, String(u"$P"));
    3081              :     }
    3082           26 :     if (masterScore->style().styleI(Sid::minEmptyMeasures) == 0) {
    3083            0 :         masterScore->style().set(Sid::minEmptyMeasures, 1);
    3084              :     }
    3085           26 :     masterScore->style().set(Sid::frameSystemDistance, masterScore->style().styleS(Sid::frameSystemDistance) + 6.0_sp);
    3086           26 :     masterScore->resetStyleValue(Sid::measureSpacing);
    3087              : 
    3088              :     // add invisible tempo text if necessary
    3089              :     // some 1.3 scores have tempolist but no tempo text
    3090           26 :     masterScore->updateTicksAndTimeSigMap();
    3091           26 :     for (const auto& i : tm) {
    3092            0 :         Fraction tick = Fraction::fromTicks(i.first);
    3093            0 :         BeatsPerSecond tempo   = i.second;
    3094              : 
    3095            0 :         if (i.first == tm.begin()->first && tempo == Constants::DEFAULT_TEMPO) {
    3096            0 :             continue;
    3097              :         }
    3098              : 
    3099            0 :         Measure* m = masterScore->tick2measure(tick);
    3100            0 :         if (!m) {
    3101            0 :             continue;
    3102              :         }
    3103              : 
    3104            0 :         Segment* seg = m->findSegment(SegmentType::ChordRest, tick);
    3105            0 :         bool hasMatchingTempo = false;
    3106            0 :         if (seg) {
    3107            0 :             for (const EngravingItem* segAnnotation : seg->annotations()) {
    3108            0 :                 if (segAnnotation->isTempoText() && toTempoText(segAnnotation)->tempo() == tempo) {
    3109            0 :                     hasMatchingTempo = true;
    3110            0 :                     break;
    3111              :                 }
    3112              :             }
    3113              :         }
    3114            0 :         if (hasMatchingTempo) {
    3115            0 :             continue;
    3116              :         }
    3117              : 
    3118            0 :         TempoText* tt = Factory::createTempoText(masterScore->dummy());
    3119            0 :         tt->setXmlText(String(u"<sym>metNoteQuarterUp</sym> = %1").arg(std::round(tempo.toBPM().val)));
    3120            0 :         tt->setTempo(tempo);
    3121            0 :         tt->setTrack(0);
    3122            0 :         tt->setVisible(false);
    3123              : 
    3124            0 :         if (!seg) {
    3125            0 :             seg = m->getSegment(SegmentType::ChordRest, tick);
    3126              :         }
    3127            0 :         seg->add(tt);
    3128              :     }
    3129              : 
    3130              :     // While reading the score, some elements might use `score->repeatList()` (which is incorrect
    3131              :     // anyway, because the repeatList will be incomplete because the score is incomplete, but some
    3132              :     // elements still do it).
    3133              :     // `score->repeatList()` calls `_repeatList->update()`; the repeat list then thinks that it is
    3134              :     // up-to-date from that point. But we weren't finished reading the score, so the score will still
    3135              :     // change. We need to tell the repeat list about that, so that it will be updated next time
    3136              :     // someone uses it.
    3137           26 :     masterScore->invalidateRepeatList();
    3138           26 :     masterScore->connectTies();
    3139           26 :     masterScore->undoRemoveStaleTieJumpPoints(false);
    3140              : 
    3141              :     // create excerpts
    3142              :     {
    3143           26 :         std::vector<Excerpt*> readExcerpts;
    3144           26 :         readExcerpts.swap(masterScore->m_excerpts);
    3145           26 :         for (Excerpt* excerpt : readExcerpts) {
    3146            0 :             if (excerpt->parts().empty()) {         // ignore empty parts
    3147            0 :                 delete excerpt;
    3148            0 :                 continue;
    3149              :             }
    3150            0 :             masterScore->m_excerpts.push_back(excerpt);
    3151            0 :             Score* nscore = masterScore->createScore();
    3152            0 :             ReadStyleHook::setupDefaultStyle(nscore);
    3153            0 :             excerpt->setExcerptScore(nscore);
    3154            0 :             nscore->style().set(Sid::createMultiMeasureRests, true);
    3155            0 :             Excerpt::createExcerpt(excerpt);
    3156              :         }
    3157           26 :     }
    3158              :     // volta offsets in older scores are hardcoded to be relative to a voltaY of -2.0sp
    3159              :     // we'll force this and live with it for the score
    3160              :     // but we wait until now to do it so parts don't have this issue
    3161              : 
    3162           26 :     if (masterScore->style().styleV(Sid::voltaPosAbove) == DefaultStyle::baseStyle().value(Sid::voltaPosAbove)) {
    3163           26 :         masterScore->style().set(Sid::voltaPosAbove, PointF(0.0, -2.0f));
    3164              :     }
    3165              : 
    3166           52 :     for (Part* p : masterScore->parts()) {
    3167           26 :         p->updateHarmonyChannels(false);
    3168              :     }
    3169              : 
    3170           26 :     masterScore->rebuildMidiMapping();
    3171           26 :     masterScore->updateChannel();
    3172              : 
    3173           26 :     CompatUtils::assignInitialPartToExcerpts(masterScore->excerpts());
    3174              : 
    3175              :     // Cleanup invalid spanners
    3176           26 :     std::vector<Spanner*> invalidSpanners;
    3177           52 :     auto spanners = score->spanner();
    3178           46 :     for (auto iter = spanners.begin(); iter != spanners.end(); ++iter) {
    3179           20 :         Spanner* spanner = (*iter).second;
    3180           20 :         bool invalid = spanner->tick().negative() || spanner->track() == muse::nidx;
    3181           20 :         if (invalid) {
    3182            0 :             invalidSpanners.push_back(spanner);
    3183              :         }
    3184              :     }
    3185           26 :     for (Spanner* invalidSpanner : invalidSpanners) {
    3186            0 :         score->removeElement(invalidSpanner);
    3187              :     }
    3188              : 
    3189           26 :     return muse::make_ok();
    3190           26 : }
    3191              : 
    3192            0 : void Read114::readExcerpt(Excerpt* item, XmlReader& e, ReadContext&)
    3193              : {
    3194            0 :     const std::vector<Part*>& pl = item->masterScore()->parts();
    3195            0 :     std::vector<Part*> parts;
    3196            0 :     while (e.readNextStartElement()) {
    3197            0 :         const AsciiStringView tag = e.name();
    3198            0 :         if (tag == "name" || tag == "title") {
    3199            0 :             item->setName(e.readText().trimmed());
    3200            0 :         } else if (tag == "part") {
    3201            0 :             size_t partIdx = static_cast<size_t>(e.readInt());
    3202            0 :             if (partIdx >= pl.size()) {
    3203            0 :                 LOGD("Excerpt::read: bad part index");
    3204              :             } else {
    3205            0 :                 parts.push_back(pl.at(partIdx));
    3206              :             }
    3207              :         }
    3208              :     }
    3209            0 :     item->setParts(parts);
    3210            0 : }
    3211              : 
    3212            0 : bool Read114::pasteStaff(XmlReader&, Segment*, staff_idx_t, Fraction)
    3213              : {
    3214            0 :     UNREACHABLE;
    3215              :     return false;
    3216              : }
    3217              : 
    3218            0 : void Read114::pasteSymbols(XmlReader&, ChordRest*)
    3219              : {
    3220            0 :     UNREACHABLE;
    3221              : }
    3222              : 
    3223            0 : void Read114::readTremoloCompat(TremoloCompat*, XmlReader&)
    3224              : {
    3225            0 :     UNREACHABLE;
    3226              : }
    3227              : 
    3228              : // also propagates to non-generated bar lines
    3229           59 : void Read114::setBarLineSpanToStaves(Score* score, const read400::ReadContext& ctx)
    3230              : {
    3231           59 :     const size_t numStaves = score->nstaves();
    3232           59 :     size_t barLineSpan = 0;
    3233          146 :     for (Staff* s : score->staves()) {
    3234           87 :         const staff_idx_t staffIdx = s->idx();
    3235           87 :         const size_t maxSpan = numStaves - staffIdx - 1;
    3236              : 
    3237           87 :         size_t staffBarLineSpan = ctx.getStaffBarLineSpan(staffIdx);
    3238           87 :         if (staffBarLineSpan > maxSpan) {
    3239            0 :             LOGW() << "invalid barline span " << staffBarLineSpan << " (max " << maxSpan << ")";
    3240            0 :             staffBarLineSpan = maxSpan;
    3241              :         }
    3242              : 
    3243           87 :         barLineSpan = std::max(barLineSpan, staffBarLineSpan);
    3244           87 :         if (barLineSpan == 0) {
    3245           79 :             s->setProperty(Pid::STAFF_BARLINE_SPAN, false);
    3246           79 :             continue;
    3247              :         }
    3248            8 :         --barLineSpan;
    3249              : 
    3250            8 :         s->setProperty(Pid::STAFF_BARLINE_SPAN, true);
    3251              :     }
    3252           59 : }
    3253              : 
    3254            0 : void Read114::doReadItem(EngravingItem*, XmlReader&)
    3255              : {
    3256            0 :     UNREACHABLE;
    3257              : }
        

Generated by: LCOV version 2.0-1