LCOV - code coverage report
Current view: top level - src/engraving/rw/read400 - tread.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 63.8 % 2996 1912
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 "tread.h"
      24              : 
      25              : #include "iengravingconfiguration.h" // IWYU pragma: keep
      26              : 
      27              : #include "../../types/typesconv.h"
      28              : #include "../../types/symnames.h"
      29              : #include "../../infrastructure/rtti.h"
      30              : 
      31              : #include "../../compat/midi/event.h"
      32              : 
      33              : #include "../../dom/accidental.h"
      34              : #include "../../dom/actionicon.h"
      35              : #include "../../dom/ambitus.h"
      36              : #include "../../dom/arpeggio.h"
      37              : #include "../../dom/articulation.h"
      38              : #include "../../dom/bagpembell.h"
      39              : #include "../../dom/barline.h"
      40              : #include "../../dom/beam.h"
      41              : #include "../../dom/bend.h"
      42              : #include "../../dom/box.h"
      43              : #include "../../dom/bracket.h"
      44              : #include "../../dom/breath.h"
      45              : #include "../../dom/chord.h"
      46              : #include "../../dom/chordline.h"
      47              : #include "../../dom/chordlist.h"
      48              : #include "../../dom/clef.h"
      49              : #include "../../dom/drumset.h"
      50              : #include "../../dom/dynamic.h"
      51              : #include "../../dom/excerpt.h"
      52              : #include "../../dom/expression.h"
      53              : #include "../../dom/factory.h"
      54              : #include "../../dom/fermata.h"
      55              : #include "../../dom/figuredbass.h"
      56              : #include "../../dom/fingering.h"
      57              : #include "../../dom/fret.h"
      58              : #include "../../dom/glissando.h"
      59              : #include "../../dom/gradualtempochange.h"
      60              : #include "../../dom/groups.h"
      61              : #include "../../dom/hairpin.h"
      62              : #include "../../dom/harmonicmark.h"
      63              : #include "../../dom/harmony.h"
      64              : #include "../../dom/harppedaldiagram.h"
      65              : #include "../../dom/hook.h"
      66              : #include "../../dom/image.h"
      67              : #include "../../dom/instrchange.h"
      68              : #include "../../dom/instrument.h"
      69              : #include "../../dom/interval.h"
      70              : #include "../../dom/jump.h"
      71              : #include "../../dom/keysig.h"
      72              : #include "../../dom/layoutbreak.h"
      73              : #include "../../dom/ledgerline.h"
      74              : #include "../../dom/letring.h"
      75              : #include "../../dom/line.h"
      76              : #include "../../dom/linkedobjects.h"
      77              : #include "../../dom/lyrics.h"
      78              : #include "../../dom/marker.h"
      79              : #include "../../dom/masterscore.h"
      80              : #include "../../dom/measurenumber.h"
      81              : #include "../../dom/measurerepeat.h"
      82              : #include "../../dom/mmrest.h"
      83              : #include "../../dom/mmrestrange.h"
      84              : #include "../../dom/mscore.h"
      85              : #include "../../dom/note.h"
      86              : #include "../../dom/notedot.h"
      87              : #include "../../dom/noteline.h"
      88              : #include "../../dom/ornament.h"
      89              : #include "../../dom/ottava.h"
      90              : #include "../../dom/page.h"
      91              : #include "../../dom/palmmute.h"
      92              : #include "../../dom/part.h"
      93              : #include "../../dom/pedal.h"
      94              : #include "../../dom/playtechannotation.h"
      95              : #include "../../dom/rasgueado.h"
      96              : #include "../../dom/rehearsalmark.h"
      97              : #include "../../dom/rest.h"
      98              : #include "../../dom/score.h"
      99              : #include "../../dom/segment.h"
     100              : #include "../../dom/slur.h"
     101              : #include "../../dom/slurtie.h"
     102              : #include "../../dom/spacer.h"
     103              : #include "../../dom/spanner.h"
     104              : #include "../../dom/staff.h"
     105              : #include "../../dom/staffstate.h"
     106              : #include "../../dom/stafftext.h"
     107              : #include "../../dom/stafftextbase.h"
     108              : #include "../../dom/stafftype.h"
     109              : #include "../../dom/stafftypechange.h"
     110              : #include "../../dom/stem.h"
     111              : #include "../../dom/stemslash.h"
     112              : #include "../../dom/sticking.h"
     113              : #include "../../dom/system.h"
     114              : #include "../../dom/systemdivider.h"
     115              : #include "../../dom/systemtext.h"
     116              : #include "../../dom/tempotext.h"
     117              : #include "../../dom/text.h"
     118              : #include "../../dom/textline.h"
     119              : #include "../../dom/textlinebase.h"
     120              : #include "../../dom/tie.h"
     121              : #include "../../dom/timesig.h"
     122              : #include "../../dom/tremolobar.h"
     123              : #include "../../dom/tremolosinglechord.h"
     124              : #include "../../dom/tremolotwochord.h"
     125              : #include "../../dom/trill.h"
     126              : #include "../../dom/tuplet.h"
     127              : #include "../../dom/vibrato.h"
     128              : #include "../../dom/volta.h"
     129              : #include "../../dom/whammybar.h"
     130              : 
     131              : #include "../../editing/editstaffbrackets.h"
     132              : #include "../../editing/transpose.h"
     133              : 
     134              : #include "../xmlreader.h"
     135              : #include "../read206/read206.h"
     136              : #include "../read500/tread.h"
     137              : #include "../compat/compatutils.h"
     138              : #include "readcontext.h"
     139              : #include "connectorinforeader.h"
     140              : 
     141              : #include "log.h"
     142              : 
     143              : using namespace muse::draw;
     144              : using namespace mu::engraving;
     145              : using namespace mu::engraving::read400;
     146              : 
     147              : using ReadTypes = rtti::TypeList<Accidental, ActionIcon, Ambitus, Arpeggio, Articulation,
     148              :                                  BagpipeEmbellishment, BarLine, Beam, Bend,  HBox, VBox, FBox, TBox, Bracket, Breath,
     149              :                                  Chord, ChordLine, Clef,
     150              :                                  Dynamic, Expression,
     151              :                                  Fermata, FiguredBass, Fingering, FretDiagram,
     152              :                                  Glissando, GradualTempoChange,
     153              :                                  Hairpin, Harmony, HarmonicMark, HarpPedalDiagram, Hook,
     154              :                                  Image, InstrumentChange,
     155              :                                  Jump,
     156              :                                  KeySig,
     157              :                                  LayoutBreak, LedgerLine, LetRing, Lyrics,
     158              :                                  Marker, MeasureNumber, MeasureRepeat, MMRest, MMRestRange,
     159              :                                  Note, NoteDot, NoteHead, NoteLine,
     160              :                                  Page, PalmMute, Pedal, PlayTechAnnotation,
     161              :                                  Rasgueado, RehearsalMark, Rest,
     162              :                                  Ornament, Ottava,
     163              :                                  Segment, Slur, Spacer, StaffState, StaffText, StaffTypeChange, Stem, StemSlash, Sticking,
     164              :                                  Symbol, FSymbol, System, SystemDivider, SystemText,
     165              :                                  TempoText, Text, TextLine, Tie, TimeSig, TremoloBar, Trill, Tuplet,
     166              :                                  Vibrato, Volta,
     167              :                                  WhammyBar>;
     168              : 
     169              : class ReadVisitor : public rtti::Visitor<ReadVisitor>
     170              : {
     171              : public:
     172              :     template<typename T>
     173       121155 :     static bool doVisit(EngravingItem* item, XmlReader& xml, ReadContext& ctx)
     174              :     {
     175       121155 :         if (T::classof(item)) {
     176         2376 :             TRead::read(static_cast<T*>(item), xml, ctx);
     177         2376 :             return true;
     178              :         }
     179       118779 :         return false;
     180              :     }
     181              : };
     182              : 
     183         2376 : void TRead::readItem(EngravingItem* item, XmlReader& xml, ReadContext& ctx)
     184              : {
     185         2376 :     bool found = ReadVisitor::visit(ReadTypes {}, item, xml, ctx);
     186         2376 :     DO_ASSERT(found);
     187         2376 : }
     188              : 
     189         2658 : PropertyValue TRead::readPropertyValue(Pid id, XmlReader& e, ReadContext& ctx)
     190              : {
     191         2658 :     switch (propertyType(id)) {
     192          120 :     case P_TYPE::BOOL:
     193          120 :         return PropertyValue(bool(e.readInt()));
     194          348 :     case P_TYPE::INT:
     195          348 :         return PropertyValue(e.readInt());
     196            8 :     case P_TYPE::REAL:
     197            8 :         return PropertyValue(e.readDouble());
     198           65 :     case P_TYPE::SPATIUM: return PropertyValue(Spatium(e.readDouble()));
     199            0 :     case P_TYPE::ABSOLUTE: return PropertyValue(Spatium(e.readDouble())); //! NOTE type mm, but stored in xml as spatium
     200            0 :     case P_TYPE::TEMPO:
     201            0 :         return PropertyValue(e.readDouble());
     202            0 :     case P_TYPE::FRACTION:
     203            0 :         return PropertyValue::fromValue(e.readFraction());
     204              : 
     205            0 :     case P_TYPE::SYMID:
     206            0 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), SymId::noSym));
     207            0 :     case P_TYPE::COLOR:
     208            0 :         return PropertyValue::fromValue(e.readColor());
     209            5 :     case P_TYPE::ORNAMENT_STYLE:
     210            5 :         return PropertyValue::fromValue(TConv::fromXml(e.readAsciiText(), OrnamentStyle::DEFAULT));
     211           21 :     case P_TYPE::ORNAMENT_INTERVAL:
     212           21 :         return PropertyValue(TConv::fromXml(e.readText(), DEFAULT_ORNAMENT_INTERVAL));
     213            0 :     case P_TYPE::ORNAMENT_SHOW_ACCIDENTAL:
     214            0 :         return OrnamentShowAccidental(e.readInt());
     215          138 :     case P_TYPE::POINT:
     216          138 :         return PropertyValue::fromValue(e.readPoint());
     217            0 :     case P_TYPE::SCALE:
     218            0 :         return PropertyValue::fromValue(e.readScale());
     219            0 :     case P_TYPE::SIZE:
     220            0 :         return PropertyValue::fromValue(e.readSize());
     221          235 :     case P_TYPE::STRING:
     222          235 :         return PropertyValue(e.readText());
     223              : 
     224           60 :     case P_TYPE::ALIGN:
     225           60 :         return PropertyValue(TConv::fromXml(e.readText(), Align()));
     226           10 :     case P_TYPE::PLACEMENT_V:
     227           10 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), PlacementV::ABOVE));
     228            0 :     case P_TYPE::PLACEMENT_H:
     229            0 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), PlacementH::LEFT));
     230            6 :     case P_TYPE::TEXT_PLACE:
     231            6 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), TextPlace::AUTO));
     232          702 :     case P_TYPE::DIRECTION_V:
     233          702 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), DirectionV::AUTO));
     234            1 :     case P_TYPE::DIRECTION_H:
     235            1 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), DirectionH::AUTO));
     236            0 :     case P_TYPE::ORIENTATION:
     237            0 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), Orientation::VERTICAL));
     238              : 
     239          555 :     case P_TYPE::LAYOUTBREAK_TYPE:
     240          555 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), LayoutBreakType::NOBREAK));
     241            3 :     case P_TYPE::VELO_TYPE:
     242            3 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), VeloType::OFFSET_VAL));
     243            0 :     case P_TYPE::GLISS_STYLE:
     244            0 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), GlissandoStyle::CHROMATIC));
     245            0 :     case P_TYPE::BARLINE_TYPE:
     246            0 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), BarLineType::NORMAL));
     247              : 
     248            7 :     case P_TYPE::NOTEHEAD_TYPE:
     249            7 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), NoteHeadType::HEAD_AUTO));
     250            3 :     case P_TYPE::NOTEHEAD_SCHEME:
     251            3 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), NoteHeadScheme::HEAD_AUTO));
     252           16 :     case P_TYPE::NOTEHEAD_GROUP:
     253           16 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), NoteHeadGroup::HEAD_NORMAL));
     254              : 
     255            0 :     case P_TYPE::CLEF_TYPE:
     256            0 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), ClefType::G));
     257            0 :     case P_TYPE::CLEF_TO_BARLINE_POS:
     258            0 :         return ClefToBarlinePosition(e.readInt());
     259              : 
     260            0 :     case P_TYPE::DYNAMIC_TYPE:
     261            0 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), DynamicType::OTHER));
     262              : 
     263            3 :     case P_TYPE::LINE_TYPE:
     264            3 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), LineType::SOLID));
     265           95 :     case P_TYPE::HOOK_TYPE:
     266           95 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), HookType::NONE));
     267              : 
     268            0 :     case P_TYPE::KEY_MODE:
     269            0 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), KeyMode::NONE));
     270              : 
     271          256 :     case P_TYPE::TEXT_STYLE:
     272          256 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), TextStyleType::DEFAULT));
     273            0 :     case P_TYPE::SLUR_STYLE_TYPE:
     274            0 :         return SlurStyleType(e.readInt());
     275              : 
     276            0 :     case P_TYPE::CHANGE_METHOD:
     277            0 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), ChangeMethod::NORMAL));
     278              : 
     279            0 :     case P_TYPE::BEAM_MODE:
     280            0 :         return PropertyValue(int(0));
     281            0 :     case P_TYPE::GROUPS: {
     282            0 :         Groups g;
     283            0 :         read400::TRead::read(&g, e, ctx);
     284            0 :         return PropertyValue::fromValue(g.nodes());
     285            0 :     }
     286            0 :     case P_TYPE::DURATION_TYPE_WITH_DOTS:
     287              :     case P_TYPE::INT_VEC:
     288            0 :         return PropertyValue();
     289              : 
     290            1 :     case P_TYPE::PLAYTECH_TYPE:
     291            1 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), PlayingTechniqueType::Natural));
     292            0 :     case P_TYPE::TEMPOCHANGE_TYPE:
     293            0 :         return PropertyValue(TConv::fromXml(e.readAsciiText(), GradualTempoChangeType::Undefined));
     294            0 :     default:
     295            0 :         ASSERT_X("unhandled PID type");
     296              :         break;
     297              :     }
     298              :     return PropertyValue();
     299              : }
     300              : 
     301        89008 : bool TRead::readProperty(EngravingItem* item, const AsciiStringView& tag, XmlReader& xml, ReadContext& ctx, Pid pid)
     302              : {
     303        89008 :     if (tag == propertyName(pid)) {
     304         1738 :         readProperty(item, xml, ctx, pid);
     305         1738 :         return true;
     306              :     }
     307        87270 :     return false;
     308              : }
     309              : 
     310         2658 : void TRead::readProperty(EngravingItem* item, XmlReader& xml, ReadContext& ctx, Pid pid)
     311              : {
     312         2658 :     double spatium = ctx.score() ? ctx.spatium() : item->spatium();
     313         2658 :     PropertyValue v = readPropertyValue(pid, xml, ctx);
     314         2658 :     switch (propertyType(pid)) {
     315            0 :     case P_TYPE::ABSOLUTE: //! NOTE type mm, but stored in xml as spatium
     316            0 :         v = v.value<Spatium>().toAbsolute(spatium);
     317            0 :         break;
     318          138 :     case P_TYPE::POINT:
     319          138 :         if (item->offsetIsSpatiumDependent()) {
     320          134 :             v = v.value<PointF>() * spatium;
     321              :         } else {
     322            4 :             v = v.value<PointF>() * DPMM;
     323              :         }
     324          138 :         break;
     325         2520 :     default:
     326         2520 :         break;
     327              :     }
     328              : 
     329              :     // Pre-4.4 compatibility: these items now use DIRECTION property
     330         2658 :     if (pid == Pid::PLACEMENT && item->hasVoiceAssignmentProperties()) {
     331            0 :         pid = Pid::DIRECTION;
     332            0 :         v = v.value<PlacementV>() == PlacementV::ABOVE ? PropertyValue(DirectionV::UP) : PropertyValue(DirectionV::DOWN);
     333              :     }
     334              : 
     335         2658 :     if (pid == Pid::OFFSET && ctx.mscVersion() >= 302) {    // in 301 files "pos" is written before placement. Migrate offset after both have been read
     336           29 :         compat::CompatUtils::migrateOffset500(item, v);
     337              :     }
     338              : 
     339         2658 :     if (!ctx.shouldSkipProperty(pid)) {
     340         2658 :         item->setProperty(pid, v);
     341         2658 :         if (item->isStyled(pid)) {
     342          585 :             item->setPropertyFlags(pid, PropertyFlags::UNSTYLED);
     343              :         }
     344              :     }
     345         2658 : }
     346              : 
     347        10736 : bool TRead::readStyledProperty(EngravingItem* item, const AsciiStringView& tag, XmlReader& xml, ReadContext& ctx)
     348              : {
     349        36323 :     for (const StyledProperty& spp : *item->styledProperties()) {
     350        25951 :         if (readProperty(item, tag, xml, ctx, spp.pid)) {
     351          364 :             return true;
     352              :         }
     353              :     }
     354        10372 :     return false;
     355              : }
     356              : 
     357         3152 : bool TRead::readItemProperties(EngravingItem* item, XmlReader& e, ReadContext& ctx)
     358              : {
     359         3152 :     const AsciiStringView tag(e.name());
     360              : 
     361         3152 :     if (TRead::readProperty(item, tag, e, ctx, Pid::SIZE_SPATIUM_DEPENDENT)) {
     362         3152 :     } else if (TRead::readProperty(item, tag, e, ctx, Pid::OFFSET)) {
     363         3037 :     } else if (TRead::readProperty(item, tag, e, ctx, Pid::MIN_DISTANCE)) {
     364         3016 :     } else if (TRead::readProperty(item, tag, e, ctx, Pid::AUTOPLACE)) {
     365         3010 :     } else if (tag == "track") {
     366           59 :         item->setTrack(e.readInt() + ctx.trackOffset());
     367         2951 :     } else if (tag == "color") {
     368           15 :         item->setColor(e.readColor());
     369         2936 :     } else if (tag == "visible") {
     370           43 :         item->setVisible(e.readInt());
     371         2893 :     } else if (tag == "selected") { // obsolete
     372           10 :         e.readInt();
     373         2883 :     } else if ((tag == "linked") || (tag == "linkedMain")) {
     374         1093 :         Staff* s = item->staff();
     375         1093 :         if (!s) {
     376           28 :             s = ctx.score()->staff(ctx.track() / VOICES);
     377           28 :             if (!s) {
     378            0 :                 LOGW("EngravingItem::readProperties: linked element's staff not found (%s)", item->typeName());
     379            0 :                 e.skipCurrentElement();
     380            0 :                 return true;
     381              :             }
     382              :         }
     383         1093 :         if (tag == "linkedMain") {
     384          546 :             item->setLinks(new LinkedObjects());
     385          546 :             item->links()->push_back(item);
     386              : 
     387          546 :             ctx.addLink(s, item->links(), ctx.location(true));
     388              : 
     389          546 :             e.readNext();
     390              :         } else {
     391          547 :             Staff* ls = s->links() ? toStaff(s->links()->mainElement()) : nullptr;
     392          547 :             bool linkedIsMaster = ls ? ls->score()->isMaster() : false;
     393          547 :             Location loc = ctx.location(true);
     394          547 :             if (ls) {
     395          540 :                 loc.setStaff(static_cast<int>(ls->idx()));
     396              :             }
     397          547 :             Location mainLoc = Location::relative();
     398          547 :             bool locationRead = false;
     399          547 :             int localIndexDiff = 0;
     400          564 :             while (e.readNextStartElement()) {
     401           17 :                 const AsciiStringView ntag(e.name());
     402              : 
     403           17 :                 if (ntag == "score") {
     404            3 :                     String val(e.readText());
     405            3 :                     if (val == "same") {
     406            3 :                         linkedIsMaster = item->score()->isMaster();
     407              :                     }
     408           17 :                 } else if (ntag == "location") {
     409            3 :                     TRead::read(&mainLoc, e, ctx);
     410            3 :                     mainLoc.toAbsolute(loc);
     411            3 :                     locationRead = true;
     412           11 :                 } else if (ntag == "indexDiff") {
     413           11 :                     localIndexDiff = e.readInt();
     414              :                 } else {
     415            0 :                     e.unknown();
     416              :                 }
     417              :             }
     418          547 :             if (!locationRead) {
     419          544 :                 mainLoc = loc;
     420              :             }
     421          547 :             LinkedObjects* link = ctx.getLink(linkedIsMaster, mainLoc, localIndexDiff);
     422          547 :             if (link) {
     423          547 :                 EngravingObject* linked = link->mainElement();
     424          547 :                 if (linked->type() == item->type()) {
     425          547 :                     item->linkTo(linked);
     426              :                 } else {
     427            0 :                     LOGW("EngravingItem::readProperties: linked elements have different types: %s, %s. Input file corrupted?",
     428              :                          item->typeName(), linked->typeName());
     429              :                 }
     430              :             }
     431          547 :             if (!item->links()) {
     432            0 :                 LOGW("EngravingItem::readProperties: could not link %s at staff %d", item->typeName(), mainLoc.staff() + 1);
     433              :             }
     434              :         }
     435         1790 :     } else if (tag == "tick") {
     436           31 :         int val = e.readInt();
     437           31 :         if (val >= 0) {
     438           31 :             ctx.setTick(Fraction::fromTicks(ctx.fileDivision(val)));             // obsolete
     439              :         }
     440         1759 :     } else if (tag == "pos") {           // obsolete
     441           16 :         TRead::readProperty(item, e, ctx, Pid::OFFSET);
     442         1743 :     } else if (tag == "voice") {
     443            0 :         item->setVoice(e.readInt());
     444         1743 :     } else if (tag == "tag") {
     445            0 :         e.skipCurrentElement();
     446         1743 :     } else if (TRead::readProperty(item, tag, e, ctx, Pid::PLACEMENT)) {
     447         1733 :     } else if (tag == "z") {
     448            0 :         item->setZ(e.readInt());
     449              :     } else {
     450         1733 :         return false;
     451              :     }
     452         1419 :     return true;
     453              : }
     454              : 
     455           53 : void TRead::read(TextBase* t, XmlReader& xml, ReadContext& ctx)
     456              : {
     457          166 :     while (xml.readNextStartElement()) {
     458           60 :         if (!readProperties(toTextBase(t), xml, ctx)) {
     459            0 :             xml.unknown();
     460              :         }
     461              :     }
     462              : 
     463           53 :     compat::CompatUtils::migrateOffsetPre302(t, ctx.mscVersion());
     464           53 : }
     465              : 
     466          134 : void TRead::read(TempoText* t, XmlReader& e, ReadContext& ctx)
     467              : {
     468          504 :     while (e.readNextStartElement()) {
     469          370 :         const AsciiStringView tag(e.name());
     470          370 :         if (readProperty(t, tag, e, ctx, Pid::PLAY)) {
     471          370 :         } else if (tag == "tempo") {
     472          134 :             t->setTempo(TConv::fromXml(e.readAsciiText(), Constants::DEFAULT_TEMPO));
     473          236 :         } else if (tag == "followText") {
     474           84 :             t->setFollowText(e.readInt());
     475          152 :         } else if (!readProperties(toTextBase(t), e, ctx)) {
     476            2 :             e.unknown();
     477              :         }
     478              :     }
     479              :     // check sanity
     480          134 :     if (t->xmlText().isEmpty()) {
     481            0 :         t->setXmlText(String(u"<sym>metNoteQuarterUp</sym> = %1").arg(int(lrint(t->tempo().toBPM().val))));
     482            0 :         t->setVisible(false);
     483              :     }
     484              : 
     485          134 :     compat::CompatUtils::migrateOffsetPre302(t, ctx.mscVersion());
     486              : 
     487          134 :     t->resetProperty(Pid::MUSIC_SYMBOL_SIZE);
     488          134 : }
     489              : 
     490          162 : void TRead::read(StaffText* t, XmlReader& xml, ReadContext& ctx)
     491              : {
     492          162 :     read(static_cast<StaffTextBase*>(t), xml, ctx);
     493          162 : }
     494              : 
     495          196 : void TRead::read(StaffTextBase* t, XmlReader& xml, ReadContext& ctx)
     496              : {
     497          196 :     t->clear();
     498              : 
     499          718 :     while (xml.readNextStartElement()) {
     500          326 :         if (!readProperties(t, xml, ctx)) {
     501            0 :             xml.unknown();
     502              :         }
     503              :     }
     504          196 :     compat::CompatUtils::migrateOffsetPre302(t, ctx.mscVersion());
     505          196 : }
     506              : 
     507          344 : bool TRead::readProperties(StaffTextBase* t, XmlReader& e, ReadContext& ctx)
     508              : {
     509          344 :     const AsciiStringView tag(e.name());
     510              : 
     511          344 :     if (tag == "MidiAction") {
     512            0 :         int channel = e.intAttribute("channel", 0);
     513            0 :         String name = e.attribute("name");
     514            0 :         bool found = false;
     515            0 :         size_t n = t->channelActions().size();
     516            0 :         for (size_t i = 0; i < n; ++i) {
     517            0 :             ChannelActions* a = &t->channelActions()[i];
     518            0 :             if (a->channel == channel) {
     519            0 :                 a->midiActionNames.append(name);
     520            0 :                 found = true;
     521            0 :                 break;
     522              :             }
     523              :         }
     524            0 :         if (!found) {
     525            0 :             ChannelActions a;
     526            0 :             a.channel = channel;
     527            0 :             a.midiActionNames.append(name);
     528            0 :             t->channelActions().push_back(a);
     529            0 :         }
     530            0 :         e.readNext();
     531          344 :     } else if (tag == "channelSwitch" || tag == "articulationChange") {
     532            0 :         voice_idx_t voice = static_cast<voice_idx_t>(e.intAttribute("voice", -1));
     533            0 :         if (voice < VOICES) {
     534            0 :             t->setChannelName(voice, e.attribute("name"));
     535            0 :         } else if (voice == muse::nidx) {
     536              :             // no voice applies channel to all voices for
     537              :             // compatibility
     538            0 :             for (voice_idx_t i = 0; i < VOICES; ++i) {
     539            0 :                 t->setChannelName(i, e.attribute("name"));
     540              :             }
     541              :         }
     542            0 :         e.readNext();
     543          344 :     } else if (tag == "aeolus") {
     544            0 :         int group = e.intAttribute("group", -1);
     545            0 :         if (group >= 0 && group < 4) {
     546            0 :             t->setAeolusStop(group, e.readInt());
     547              :         } else {
     548            0 :             e.readNext();
     549              :         }
     550            0 :         t->setSetAeolusStops(true);
     551          344 :     } else if (tag == "swing") {
     552            1 :         DurationType swingUnit = TConv::fromXml(e.asciiAttribute("unit"), DurationType::V_INVALID);
     553            1 :         int unit = 0;
     554            1 :         if (swingUnit == DurationType::V_EIGHTH) {
     555            1 :             unit = Constants::DIVISION / 2;
     556            0 :         } else if (swingUnit == DurationType::V_16TH) {
     557            0 :             unit = Constants::DIVISION / 4;
     558            0 :         } else if (swingUnit == DurationType::V_ZERO) {
     559            0 :             unit = 0;
     560              :         }
     561            1 :         int ratio = e.intAttribute("ratio", 60);
     562            1 :         t->setSwing(true);
     563            1 :         t->setSwingParameters(unit, ratio);
     564            1 :         e.readNext();
     565          343 :     } else if (tag == "capo") {
     566            0 :         int fretId = e.intAttribute("fretId", 0);
     567            0 :         t->setCapo(fretId);
     568            0 :         e.readNext();
     569          343 :     } else if (!readProperties(toTextBase(t), e, ctx)) {
     570            4 :         return false;
     571              :     }
     572          340 :     return true;
     573              : }
     574              : 
     575           65 : void TRead::read(Dynamic* d, XmlReader& e, ReadContext& ctx)
     576              : {
     577           65 :     int mscVersion = d->score()->mscVersion();
     578          206 :     while (e.readNextStartElement()) {
     579          141 :         const AsciiStringView tag = e.name();
     580          141 :         if (tag == "subtype") {
     581           65 :             d->setDynamicType(e.readText());
     582           76 :         } else if (tag == "velocity") {
     583           65 :             d->setVelocity(e.readInt());
     584           11 :         } else if (tag == "dynType") {
     585            0 :             d->setVoiceAssignment(read206::Read206::readDynamicRange(e.readInt()));
     586           11 :         } else if (tag == "veloChange") {
     587            0 :             d->setChangeInVelocity(e.readInt());
     588           11 :         } else if (tag == "veloChangeSpeed") {
     589            0 :             d->setVelChangeSpeed(TConv::fromXml(e.readAsciiText(), DynamicSpeed::NORMAL));
     590           11 :         } else if (tag == "size" && mscVersion < 400) {
     591            0 :             e.skipCurrentElement();
     592           11 :         } else if (tag == "dynamicsSize") {
     593            0 :             d->setSymbolScale(e.readDouble());
     594           11 :         } else if (readProperty(d, tag, e, ctx, Pid::AVOID_BARLINES)) {
     595           11 :         } else if (readProperty(d, tag, e, ctx, Pid::CENTER_ON_NOTEHEAD)) {
     596           11 :         } else if (!readProperties(toTextBase(d), e, ctx)) {
     597            0 :             e.unknown();
     598              :         }
     599              :     }
     600           65 :     if (mscVersion < 400) {
     601           61 :         d->setSize(10.0);
     602           61 :         if (d->xmlText().contains(u"<sym>") && !d->xmlText().contains(u"<font")) {
     603           60 :             d->setAlign(Align(AlignH::HCENTER, AlignV::BASELINE));
     604              :         }
     605              :     }
     606              : 
     607           65 :     compat::CompatUtils::migrateOffsetPre302(d, ctx.mscVersion());
     608           65 : }
     609              : 
     610            0 : void TRead::read(Expression* expr, XmlReader& xml, ReadContext& ctx)
     611              : {
     612            0 :     while (xml.readNextStartElement()) {
     613            0 :         const AsciiStringView tag = xml.name();
     614            0 :         if (tag == "snapToDynamics") {
     615            0 :             readProperty(expr, xml, ctx, Pid::SNAP_TO_DYNAMICS);
     616            0 :         } else if (!readProperties(toTextBase(expr), xml, ctx)) {
     617            0 :             xml.unknown();
     618              :         }
     619              :     }
     620              : 
     621            0 :     compat::CompatUtils::migrateOffsetPre302(expr, ctx.mscVersion());
     622            0 : }
     623              : 
     624           41 : void TRead::read(FretDiagram* d, XmlReader& e, ReadContext& ctx)
     625              : {
     626              :     // Read the old format first
     627           41 :     bool hasBarre = false;
     628           41 :     bool haveReadNew = false;
     629              : 
     630          331 :     while (e.readNextStartElement()) {
     631          290 :         const AsciiStringView tag(e.name());
     632              : 
     633              :         // Check for new format fret diagram
     634          290 :         if (haveReadNew) {
     635           78 :             e.skipCurrentElement();
     636           78 :             continue;
     637              :         }
     638          212 :         if (tag == "fretDiagram") {
     639              :             // Read new
     640           91 :             while (e.readNextStartElement()) {
     641           78 :                 const AsciiStringView tag2(e.name());
     642              : 
     643           78 :                 if (tag2 == "string") {
     644           78 :                     int no = e.intAttribute("no");
     645          156 :                     while (e.readNextStartElement()) {
     646           78 :                         const AsciiStringView t(e.name());
     647           78 :                         if (t == "dot") {
     648           39 :                             int fret = e.intAttribute("fret", 0);
     649           39 :                             FretDotType dtype = FretItem::nameToDotType(e.readText());
     650           39 :                             d->setDot(no, fret, true, dtype);
     651           39 :                         } else if (t == "marker") {
     652           39 :                             FretMarkerType mtype = FretItem::nameToMarkerType(e.readText());
     653           39 :                             d->setMarker(no, mtype);
     654            0 :                         } else if (t == "fingering") {
     655            0 :                             e.readText();
     656              :                             /*setFingering(no, e.readInt()); NOTE:JT todo */
     657              :                         } else {
     658            0 :                             e.unknown();
     659              :                         }
     660              :                     }
     661            0 :                 } else if (tag2 == "barre") {
     662            0 :                     int start = e.intAttribute("start", -1);
     663            0 :                     int end = e.intAttribute("end", -1);
     664            0 :                     int fret = e.readInt();
     665              : 
     666            0 :                     d->setBarre(start, end, fret);
     667            0 :                 } else if (!readItemProperties(d, e, ctx)) {
     668            0 :                     e.unknown();
     669              :                 }
     670              :             }
     671           13 :             haveReadNew = true;
     672              :         }
     673              :         // Check for new properties
     674          199 :         else if (tag == "showNut") {
     675            0 :             TRead::readProperty(d, e, ctx, Pid::FRET_NUT);
     676          199 :         } else if (tag == "orientation") {
     677            0 :             TRead::readProperty(d, e, ctx, Pid::ORIENTATION);
     678              :         }
     679              :         // Then read the rest if there is no new format diagram (compatibility read)
     680          199 :         else if (tag == "strings") {
     681            0 :             TRead::readProperty(d, e, ctx, Pid::FRET_STRINGS);
     682          199 :         } else if (tag == "frets") {
     683           13 :             TRead::readProperty(d, e, ctx, Pid::FRET_FRETS);
     684          186 :         } else if (tag == "fretOffset") {
     685            0 :             TRead::readProperty(d, e, ctx, Pid::FRET_OFFSET);
     686          186 :         } else if (tag == "string") {
     687          168 :             int no = e.intAttribute("no");
     688          336 :             while (e.readNextStartElement()) {
     689          168 :                 const AsciiStringView t(e.name());
     690          168 :                 if (t == "dot") {
     691           84 :                     d->setDot(no, e.readInt());
     692           84 :                 } else if (t == "marker") {
     693           84 :                     d->setMarker(no, Char(e.readInt()) == u'X' ? FretMarkerType::CROSS : FretMarkerType::CIRCLE);
     694              :                 }
     695              :                 /*else if (t == "fingering")
     696              :                       setFingering(no, e.readInt());*/
     697              :                 else {
     698            0 :                     e.unknown();
     699              :                 }
     700              :             }
     701           18 :         } else if (tag == "barre") {
     702            0 :             hasBarre = e.readBool();
     703           18 :         } else if (tag == "mag") {
     704            0 :             TRead::readProperty(d, e, ctx, Pid::MAG);
     705           18 :         } else if (tag == "Harmony") {
     706           10 :             Harmony* h = new Harmony(d->score()->dummy());
     707           10 :             read(h, e, ctx);
     708           10 :             if (h->chords().empty()) {
     709              :                 // Invalid harmony
     710            0 :                 delete h;
     711              :             } else {
     712           10 :                 d->add(h);
     713              :             }
     714            8 :         } else if (!readItemProperties(d, e, ctx)) {
     715            0 :             e.unknown();
     716              :         }
     717              :     }
     718              : 
     719              :     // Old handling of barres
     720           41 :     if (hasBarre) {
     721            0 :         for (int s = 0; s < d->strings(); ++s) {
     722            0 :             for (auto& dot : d->dot(s)) {
     723            0 :                 if (dot.exists()) {
     724            0 :                     d->setBarre(s, -1, dot.fret);
     725            0 :                     return;
     726              :                 }
     727            0 :             }
     728              :         }
     729              :     }
     730              : }
     731              : 
     732           23 : void TRead::read(TremoloBar* b, XmlReader& e, ReadContext& ctx)
     733              : {
     734           93 :     while (e.readNextStartElement()) {
     735           70 :         auto tag = e.name();
     736           70 :         if (tag == "point") {
     737           69 :             PitchValue pv;
     738           69 :             pv.time    = e.intAttribute("time");
     739           69 :             pv.pitch   = e.intAttribute("pitch");
     740           69 :             pv.vibrato = e.intAttribute("vibrato");
     741           69 :             b->points().push_back(pv);
     742           69 :             e.readNext();
     743            1 :         } else if (tag == "mag") {
     744            1 :             b->setUserMag(e.readDouble(0.1, 10.0));
     745            0 :         } else if (TRead::readStyledProperty(b, tag, e, ctx)) {
     746            0 :         } else if (tag == "play") {
     747            0 :             b->setPlay(e.readInt());
     748            0 :         } else if (TRead::readProperty(b, tag, e, ctx, Pid::LINE_WIDTH)) {
     749              :         } else {
     750            0 :             e.unknown();
     751              :         }
     752              :     }
     753           23 : }
     754              : 
     755            0 : void TRead::read(Sticking* s, XmlReader& xml, ReadContext& ctx)
     756              : {
     757            0 :     read(toTextBase(s), xml, ctx);
     758            0 : }
     759              : 
     760           34 : void TRead::read(SystemText* t, XmlReader& xml, ReadContext& ctx)
     761              : {
     762           34 :     read(static_cast<StaffTextBase*>(t), xml, ctx);
     763           34 : }
     764              : 
     765            2 : void TRead::read(PlayTechAnnotation* a, XmlReader& xml, ReadContext& ctx)
     766              : {
     767            5 :     while (xml.readNextStartElement()) {
     768            3 :         const AsciiStringView tag(xml.name());
     769              : 
     770            3 :         if (TRead::readProperty(a, tag, xml, ctx, Pid::PLAY_TECH_TYPE)) {
     771            1 :             continue;
     772              :         }
     773              : 
     774            2 :         if (!readProperties(static_cast<StaffTextBase*>(a), xml, ctx)) {
     775            0 :             xml.unknown();
     776              :         }
     777              :     }
     778            2 :     compat::CompatUtils::migrateOffsetPre302(a, ctx.mscVersion());
     779            2 : }
     780              : 
     781           53 : void TRead::read(RehearsalMark* m, XmlReader& xml, ReadContext& ctx)
     782              : {
     783           53 :     read(toTextBase(m), xml, ctx);
     784           53 : }
     785              : 
     786          786 : void TRead::read(Instrument* item, XmlReader& e, ReadContext& ctx, Part* part)
     787              : {
     788          786 :     bool customDrumset = false;
     789          786 :     bool readSingleNoteDynamics = false;
     790              : 
     791          786 :     item->clearChannels();         // remove default channel
     792          786 :     item->setId(e.attribute("id"));
     793        16521 :     while (e.readNextStartElement()) {
     794        15735 :         const AsciiStringView tag(e.name());
     795        15735 :         if (tag == "singleNoteDynamics") {
     796            6 :             item->setSingleNoteDynamics(e.readBool());
     797            6 :             readSingleNoteDynamics = true;
     798        15729 :         } else if (!readProperties(item, e, ctx, part, &customDrumset)) {
     799            0 :             e.unknown();
     800              :         }
     801              :     }
     802              : 
     803          786 :     item->updateInstrumentId();
     804              : 
     805          786 :     if (item->channel().empty()) { // for backward compatibility
     806            0 :         InstrChannel* fallbackChannel = new InstrChannel;
     807            0 :         fallbackChannel->setName(String::fromUtf8(InstrChannel::DEFAULT_NAME));
     808            0 :         fallbackChannel->setProgram(item->recognizeMidiProgram());
     809            0 :         item->appendChannel(fallbackChannel);
     810          786 :     } else if (item->channel(0)->program() == -1) {
     811           60 :         item->channel(0)->setProgram(item->recognizeMidiProgram());
     812              :     }
     813              : 
     814          786 :     if (!readSingleNoteDynamics) {
     815          780 :         item->setSingleNoteDynamicsFromTemplate();
     816              :     }
     817          786 : }
     818              : 
     819        16709 : bool TRead::readProperties(Instrument* item, XmlReader& e, ReadContext& ctx, Part* part, bool* customDrumset)
     820              : {
     821        16709 :     PartAudioSettingsCompat partAudioSetting;
     822        16709 :     InstrumentTrackId trackId;
     823        16709 :     if (part && part->score()) {
     824        50127 :         trackId = { part->score()->parts().size() + 1, item->id() };//part is not assigned to score, _id field is not correct
     825              :     }
     826        16709 :     partAudioSetting.instrumentId = trackId;
     827              : 
     828        16709 :     const AsciiStringView tag(e.name());
     829        16709 :     if (tag == "longName") {
     830          729 :         item->setLongName(read500::TRead::readLegacyStaffName(e));
     831        15980 :     } else if (tag == "shortName") {
     832          710 :         item->setShortName(read500::TRead::readLegacyStaffName(e));
     833        15270 :     } else if (tag == "trackName") {
     834          867 :         item->setTrackName(e.readText());
     835        14403 :     } else if (tag == "minPitch") {      // obsolete
     836            0 :         int pitch = e.readInt();
     837            0 :         item->setMinPitchP(pitch);
     838            0 :         item->setMinPitchA(pitch);
     839        14403 :     } else if (tag == "maxPitch") {       // obsolete
     840            0 :         int pitch = e.readInt();
     841            0 :         item->setMaxPitchP(pitch);
     842            0 :         item->setMaxPitchA(pitch);
     843        14403 :     } else if (tag == "minPitchA") {
     844          683 :         item->setMinPitchA(e.readInt());
     845        13720 :     } else if (tag == "minPitchP") {
     846          683 :         item->setMinPitchP(e.readInt());
     847        13037 :     } else if (tag == "maxPitchA") {
     848          683 :         item->setMaxPitchA(e.readInt());
     849        12354 :     } else if (tag == "maxPitchP") {
     850          683 :         item->setMaxPitchP(e.readInt());
     851        11671 :     } else if (tag == "transposition") {    // obsolete
     852            0 :         Interval transpose;
     853            0 :         transpose.chromatic = e.readInt();
     854            0 :         transpose.diatonic = Interval::chromatic2diatonic(transpose.chromatic);
     855            0 :         item->setTranspose(transpose);
     856        11671 :     } else if (tag == "transposeChromatic") {
     857           25 :         Interval transpose = item->transpose();
     858           25 :         transpose.chromatic = e.readInt();
     859           25 :         item->setTranspose(transpose);
     860        11646 :     } else if (tag == "transposeDiatonic") {
     861           25 :         Interval transpose = item->transpose();
     862           25 :         transpose.diatonic = e.readInt();
     863           25 :         item->setTranspose(transpose);
     864        11621 :     } else if (tag == "instrumentId") {
     865          498 :         item->setMusicXmlId(e.readText());
     866        11123 :     } else if (tag == "useDrumset") {
     867          180 :         item->setUseDrumset(e.readInt());
     868          180 :         if (item->useDrumset()) {
     869          180 :             item->setDrumset(new Drumset(*smDrumset));
     870              :         }
     871        10943 :     } else if (tag == "Drum") {
     872              :         // if we see one of these tags, a custom drumset will be created
     873         4254 :         if (!item->drumset()) {
     874            0 :             item->setDrumset(new Drumset(*smDrumset));
     875              :         }
     876         4254 :         if (!(*customDrumset)) {
     877          174 :             const_cast<Drumset*>(item->drumset())->clear();
     878          174 :             *customDrumset = true;
     879              :         }
     880         4254 :         const_cast<Drumset*>(item->drumset())->loadDrum(e);
     881         6689 :     } else if (tag == "percussionPanelColumns") {
     882              :         // if we see one of these tags, a custom drumset will be created
     883            0 :         if (!item->drumset()) {
     884            0 :             item->setDrumset(new Drumset(*smDrumset));
     885              :         }
     886            0 :         if (!(*customDrumset)) {
     887            0 :             const_cast<Drumset*>(item->drumset())->clear();
     888            0 :             *customDrumset = true;
     889              :         }
     890            0 :         item->drumset()->setPercussionPanelColumns(e.readInt());
     891              :     }
     892              :     // support tag "Tablature" for a while for compatibility with existent 2.0 scores
     893         6689 :     else if (tag == "Tablature" || tag == "StringData") {
     894           34 :         StringData sd;
     895           34 :         read(&sd, e);
     896           34 :         item->setStringData(sd);
     897         6689 :     } else if (tag == "MidiAction") {
     898            0 :         NamedEventList a;
     899            0 :         read(&a, e);
     900            0 :         item->addMidiAction(a);
     901         6655 :     } else if (tag == "Articulation") {
     902         5150 :         MidiArticulation a;
     903         5150 :         read(&a, e);
     904         5150 :         item->addMidiArticulation(a);
     905         6655 :     } else if (tag == "Channel" || tag == "channel") {
     906         1028 :         InstrChannel* a = new InstrChannel;
     907         1028 :         read(a, e, ctx, part, trackId);
     908         1028 :         item->appendChannel(a);
     909          477 :     } else if (tag == "clef") {           // sets both transposing and concert clef
     910          473 :         int idx = e.intAttribute("staff", 1) - 1;
     911          473 :         ClefType ct = TConv::fromXml(e.readAsciiText(), ClefType::G);
     912          473 :         item->setClefType(idx, ClefTypeList(ct, ct));
     913            4 :     } else if (tag == "concertClef") {
     914            2 :         int idx = e.intAttribute("staff", 1) - 1;
     915            2 :         item->setClefType(idx, ClefTypeList(TConv::fromXml(e.readAsciiText(), ClefType::G), item->clefType(idx).transposingClef));
     916            2 :     } else if (tag == "transposingClef") {
     917            2 :         int idx = e.intAttribute("staff", 1) - 1;
     918            2 :         item->setClefType(idx, ClefTypeList(item->clefType(idx).concertClef, TConv::fromXml(e.readAsciiText(), ClefType::G)));
     919              :     } else {
     920            0 :         return false;
     921              :     }
     922              : 
     923        16709 :     return true;
     924        50127 : }
     925              : 
     926        13444 : void TRead::read(InstrChannel* item, XmlReader& e, ReadContext& ctx, Part* part, const InstrumentTrackId& instrId)
     927              : {
     928        13444 :     item->setNotifyAboutChangedEnabled(false);
     929              : 
     930              :     // synti = 0;
     931        13444 :     item->setName(e.attribute("name"));
     932        13444 :     if (item->name() == "") {
     933         9197 :         item->setName(String::fromUtf8(InstrChannel::DEFAULT_NAME));
     934              :     }
     935              : 
     936        13444 :     int midiPort = -1;
     937        13444 :     int midiChannel = -1;
     938        13444 :     PartAudioSettingsCompat partAudioSetting;
     939        13444 :     partAudioSetting.instrumentId = instrId;
     940              : 
     941        30226 :     while (e.readNextStartElement()) {
     942        16782 :         const AsciiStringView tag(e.name());
     943        16782 :         if (tag == "program") {
     944        13384 :             item->setProgram(e.intAttribute("value", -1));
     945        13384 :             if (item->program() == -1) {
     946            0 :                 item->setProgram(e.readInt());
     947              :             } else {
     948        13384 :                 e.readNext();
     949              :             }
     950         3398 :         } else if (tag == "controller") {
     951         2808 :             int value = e.intAttribute("value", 0);
     952         2808 :             int ctrl  = e.intAttribute("ctrl", 0);
     953         2808 :             switch (ctrl) {
     954         1875 :             case CTRL_HBANK:
     955         1875 :                 item->setBank((value << 7) + (item->bank() & 0x7f));
     956         1875 :                 item->setUserBankController(true);
     957         1875 :                 break;
     958          721 :             case CTRL_LBANK:
     959          721 :                 item->setBank((item->bank() & ~0x7f) + (value & 0x7f));
     960          721 :                 item->setUserBankController(true);
     961          721 :                 break;
     962           31 :             case CTRL_VOLUME:
     963           31 :                 item->setVolume(value);
     964           31 :                 break;
     965           33 :             case CTRL_PANPOT:
     966           33 :                 item->setPan(value);
     967           33 :                 break;
     968           74 :             case CTRL_CHORUS_SEND:
     969           74 :                 item->setChorus(value);
     970           74 :                 break;
     971           74 :             case CTRL_REVERB_SEND:
     972           74 :                 item->setReverb(value);
     973           74 :                 break;
     974            0 :             default:
     975              :             {
     976            0 :                 Event ev(ME_CONTROLLER);
     977            0 :                 ev.setOntime(-1);
     978            0 :                 ev.setChannel(0);
     979            0 :                 ev.setDataA(ctrl);
     980            0 :                 ev.setDataB(value);
     981            0 :                 item->addToInit(ev);
     982            0 :             }
     983            0 :             break;
     984              :             }
     985         2808 :             e.readNext();
     986          590 :         } else if (tag == "Articulation") {
     987            0 :             MidiArticulation a;
     988            0 :             read(&a, e);
     989            0 :             item->articulation.push_back(a);
     990          590 :         } else if (tag == "MidiAction") {
     991            0 :             NamedEventList a;
     992            0 :             read(&a, e);
     993            0 :             item->midiActions.push_back(a);
     994          590 :         } else if (tag == "synti") {
     995          270 :             item->setSynti(e.readText());
     996          320 :         } else if (tag == "color") {
     997            0 :             item->setColor(e.readInt());
     998          320 :         } else if (tag == "mute") {
     999            0 :             partAudioSetting.mute = e.readInt();
    1000          320 :         } else if (tag == "solo") {
    1001            0 :             partAudioSetting.solo = e.readInt();
    1002          320 :         } else if (tag == "midiPort") {
    1003          160 :             midiPort = e.readInt();
    1004          160 :         } else if (tag == "midiChannel") {
    1005          160 :             midiChannel = e.readInt();
    1006              :         } else {
    1007            0 :             e.unknown();
    1008              :         }
    1009              :     }
    1010              : 
    1011        13444 :     item->setMustUpdateInit(true);
    1012              : 
    1013        13444 :     ctx.addPartAudioSettingCompat(partAudioSetting);
    1014              : 
    1015        13444 :     item->setNotifyAboutChangedEnabled(true);
    1016              : 
    1017        13444 :     if ((midiPort != -1 || midiChannel != -1) && part && part->score()->isMaster()) {
    1018          160 :         part->masterScore()->addMidiMapping(item, part, midiPort, midiChannel);
    1019              :     }
    1020        13444 : }
    1021              : 
    1022         5614 : void TRead::read(MidiArticulation* item, XmlReader& e)
    1023              : {
    1024         5614 :     item->name = e.attribute("name");
    1025        16714 :     while (e.readNextStartElement()) {
    1026        11100 :         const AsciiStringView tag(e.name());
    1027        11100 :         if (tag == "velocity") {
    1028         5614 :             String text(e.readText());
    1029         5614 :             if (text.endsWith(u"%")) {
    1030            0 :                 text = text.left(text.size() - 1);
    1031              :             }
    1032         5614 :             item->velocity = text.toInt();
    1033        11100 :         } else if (tag == "gateTime") {
    1034         5486 :             String text(e.readText());
    1035         5486 :             if (text.endsWith(u"%")) {
    1036            0 :                 text = text.left(text.size() - 1);
    1037              :             }
    1038         5486 :             item->gateTime = text.toInt();
    1039         5486 :         } else if (tag == "descr") {
    1040            0 :             item->descr = e.readText();
    1041              :         } else {
    1042            0 :             e.unknown();
    1043              :         }
    1044              :     }
    1045         5614 : }
    1046              : 
    1047            0 : void TRead::read(NamedEventList* item, XmlReader& e)
    1048              : {
    1049            0 :     item->name = e.attribute("name");
    1050            0 :     while (e.readNextStartElement()) {
    1051            0 :         const AsciiStringView tag(e.name());
    1052            0 :         if (tag == "program") {
    1053            0 :             MidiCoreEvent ev(ME_CONTROLLER, 0, CTRL_PROGRAM, e.intAttribute("value", 0));
    1054            0 :             item->events.push_back(ev);
    1055            0 :             e.skipCurrentElement();
    1056            0 :         } else if (tag == "controller") {
    1057            0 :             MidiCoreEvent ev;
    1058            0 :             ev.setType(ME_CONTROLLER);
    1059            0 :             ev.setDataA(e.intAttribute("ctrl", 0));
    1060            0 :             ev.setDataB(e.intAttribute("value", 0));
    1061            0 :             item->events.push_back(ev);
    1062            0 :             e.skipCurrentElement();
    1063            0 :         } else if (tag == "descr") {
    1064            0 :             item->descr = e.readText();
    1065              :         } else {
    1066            0 :             e.unknown();
    1067              :         }
    1068              :     }
    1069            0 : }
    1070              : 
    1071           26 : void TRead::read(InstrumentChange* c, XmlReader& e, ReadContext& ctx)
    1072              : {
    1073           26 :     Instrument inst;
    1074           78 :     while (e.readNextStartElement()) {
    1075           52 :         const AsciiStringView tag(e.name());
    1076           52 :         if (tag == "Instrument") {
    1077           26 :             read(&inst, e, ctx, c->part());
    1078           26 :         } else if (tag == "init") {
    1079            0 :             c->setInit(e.readBool());
    1080           26 :         } else if (!readProperties(toTextBase(c), e, ctx)) {
    1081            0 :             e.unknown();
    1082              :         }
    1083              :     }
    1084              : 
    1085           26 :     compat::CompatUtils::migrateOffsetPre302(c, ctx.mscVersion());
    1086              : 
    1087           26 :     if (c->score()->mscVersion() < 206) {
    1088              :         // previous versions did not honor transposition of instrument change
    1089              :         // except in ways that it should not have
    1090              :         // notes entered before the instrument change was added would not be altered,
    1091              :         // so original transposition remained in effect
    1092              :         // notes added afterwards would be transposed by both intervals, resulting in tpc corruption
    1093              :         // here we set the instrument change to inherit the staff transposition to emulate previous versions
    1094              :         // in Note::read(), we attempt to fix the tpc corruption
    1095              :         // There is also code in read206 to try to deal with this, but it is out of date and therefore disabled
    1096              :         // What this means is, scores created in 2.1 or later should be fine, scores created in 2.0 maybe not so much
    1097              : 
    1098            0 :         Interval v = c->staff() ? c->staff()->part()->instrument(c->tick())->transpose() : 0;
    1099            0 :         inst.setTranspose(v);
    1100              :     }
    1101              : 
    1102           26 :     c->setInstrument(inst);
    1103           26 : }
    1104              : 
    1105              : //---------------------------------------------------------
    1106              : //    for import of 1.3 scores
    1107              : //---------------------------------------------------------
    1108              : 
    1109            8 : static SymId convertFromOldId(int val)
    1110              : {
    1111            8 :     SymId symId = SymId::noSym;
    1112            8 :     switch (val) {
    1113            3 :     case 32: symId = SymId::accidentalSharp;
    1114            3 :         break;
    1115            0 :     case 33: symId = SymId::accidentalThreeQuarterTonesSharpArrowUp;
    1116            0 :         break;
    1117            0 :     case 34: symId = SymId::accidentalQuarterToneSharpArrowDown;
    1118            0 :         break;
    1119              :     // case 35: // "sharp arrow both" missing in SMuFL
    1120            0 :     case 36: symId = SymId::accidentalQuarterToneSharpStein;
    1121            0 :         break;
    1122            0 :     case 37: symId = SymId::accidentalBuyukMucennebSharp;
    1123            0 :         break;
    1124            0 :     case 38: symId = SymId::accidentalKomaSharp;
    1125            0 :         break;
    1126            0 :     case 39: symId = SymId::accidentalThreeQuarterTonesSharpStein;
    1127            0 :         break;
    1128            2 :     case 40: symId = SymId::accidentalNatural;
    1129            2 :         break;
    1130            0 :     case 41: symId = SymId::accidentalQuarterToneSharpNaturalArrowUp;
    1131            0 :         break;
    1132            0 :     case 42: symId = SymId::accidentalQuarterToneFlatNaturalArrowDown;
    1133            0 :         break;
    1134              :     // case 43: // "natural arrow both" missing in SMuFL
    1135            3 :     case 44: symId = SymId::accidentalFlat;
    1136            3 :         break;
    1137            0 :     case 45: symId = SymId::accidentalQuarterToneFlatArrowUp;
    1138            0 :         break;
    1139            0 :     case 46: symId = SymId::accidentalThreeQuarterTonesFlatArrowDown;
    1140            0 :         break;
    1141              :     // case 47: // "flat arrow both" missing in SMuFL
    1142            0 :     case 48: symId = SymId::accidentalBakiyeFlat;
    1143            0 :         break;
    1144            0 :     case 49: symId = SymId::accidentalBuyukMucennebFlat;
    1145            0 :         break;
    1146            0 :     case 50: symId = SymId::accidentalThreeQuarterTonesFlatZimmermann;
    1147            0 :         break;
    1148            0 :     case 51: symId = SymId::accidentalQuarterToneFlatStein;
    1149            0 :         break;
    1150              :     // case 52: // "mirrored flat slash" missing in SMuFL
    1151            0 :     case 53: symId = SymId::accidentalDoubleFlat;
    1152            0 :         break;
    1153              :     // case 54: // "flat flat slash" missing in SMuFL
    1154            0 :     case 55: symId = SymId::accidentalDoubleSharp;
    1155            0 :         break;
    1156            0 :     case 56: symId = SymId::accidentalSori;
    1157            0 :         break;
    1158            0 :     case 57: symId = SymId::accidentalKoron;
    1159            0 :         break;
    1160            0 :     default:
    1161            0 :         LOGD("MuseScore 1.3 symbol id corresponding to <%d> not found", val);
    1162            0 :         symId = SymId::noSym;
    1163            0 :         break;
    1164              :     }
    1165            8 :     return symId;
    1166              : }
    1167              : 
    1168           33 : void TRead::read(KeyList* item, XmlReader& e, ReadContext& ctx)
    1169              : {
    1170           69 :     while (e.readNextStartElement()) {
    1171           36 :         if (e.name() == "key") {
    1172              :             Key k;
    1173           36 :             int tick = e.intAttribute("tick", 0);
    1174           36 :             if (e.hasAttribute("custom")) {
    1175            0 :                 k = Key::C;              // ke.setCustomType(e.intAttribute("custom"));
    1176              :             } else {
    1177           36 :                 k = Key(e.intAttribute("idx"));
    1178              :             }
    1179           36 :             KeySigEvent ke;
    1180           36 :             ke.setConcertKey(k);
    1181           36 :             (*item)[ctx.fileDivision(tick)] = ke;
    1182           36 :             e.readNext();
    1183           36 :         } else {
    1184            0 :             e.unknown();
    1185              :         }
    1186              :     }
    1187           33 : }
    1188              : 
    1189          171 : void TRead::read(KeySig* s, XmlReader& e, ReadContext& ctx)
    1190              : {
    1191          171 :     KeySigEvent sig;
    1192          171 :     int subtype = 0;
    1193          171 :     Part* p = s->part();
    1194              : 
    1195          360 :     while (e.readNextStartElement()) {
    1196          189 :         const AsciiStringView tag(e.name());
    1197          189 :         if (tag == "CustDef" || tag == "KeySym") {
    1198            8 :             CustDef cd;
    1199           24 :             while (e.readNextStartElement()) {
    1200           16 :                 const AsciiStringView t(e.name());
    1201           16 :                 if (t == "sym") {
    1202            8 :                     AsciiStringView val(e.readAsciiText());
    1203              :                     bool valid;
    1204            8 :                     SymId id = SymId(val.toInt(&valid));
    1205            8 :                     if (!valid) {
    1206            0 :                         id = SymNames::symIdByName(val);
    1207              :                     }
    1208            8 :                     if (s->score()->mscVersion() <= 114) {
    1209            8 :                         if (valid) {
    1210            8 :                             id = convertFromOldId(val.toInt(&valid));
    1211              :                         }
    1212            8 :                         if (!valid) {
    1213            0 :                             id = SymNames::symIdByOldName(val);
    1214              :                         }
    1215              :                     }
    1216            8 :                     cd.sym = id;
    1217            8 :                 } else if (t == "def") {
    1218            0 :                     cd.degree = e.intAttribute("degree", 0);
    1219            0 :                     cd.octAlt = e.intAttribute("octAlt", 0);
    1220            0 :                     cd.xAlt = Spatium(e.doubleAttribute("xAlt", 0.0));
    1221            0 :                     e.readNext();
    1222            8 :                 } else if (t == "pos") { // for older files
    1223            8 :                     Spatium prevx = 0_sp;
    1224            8 :                     Spatium accidentalGap = ctx.score()->style().styleS(Sid::keysigAccidentalDistance);
    1225            8 :                     double _spatium = s->spatium();
    1226              :                     // count default x position
    1227           19 :                     for (CustDef& cd2 : sig.customKeyDefs()) {
    1228           11 :                         prevx += Spatium::fromAbsolute(s->symWidth(cd2.sym), _spatium) + accidentalGap + cd2.xAlt;
    1229              :                     }
    1230            8 :                     bool flat = std::string(SymNames::nameForSymId(cd.sym).ascii()).find("Flat") != std::string::npos;
    1231              :                     // if x not there, use default step
    1232            8 :                     cd.xAlt = Spatium(e.doubleAttribute("x", prevx.val())) - prevx;
    1233              :                     // if y not there, use middle line
    1234            8 :                     int line = static_cast<int>(e.doubleAttribute("y", 2) * 2);
    1235            8 :                     cd.degree = (3 - line) % 7;
    1236            8 :                     cd.degree += (cd.degree < 0) ? 7 : 0;
    1237            8 :                     line += flat ? -1 : 1; // top point in treble for # is -1 (gis), and for b is 1 (es)
    1238            8 :                     cd.octAlt = static_cast<int>((line - (line >= 0 ? 0 : 6)) / 7);
    1239            8 :                     e.readNext();
    1240              :                 } else {
    1241            0 :                     e.unknown();
    1242              :                 }
    1243              :             }
    1244            8 :             sig.customKeyDefs().push_back(cd);
    1245          181 :         } else if (TRead::readProperty(s, tag, e, ctx, Pid::SHOW_COURTESY)) {
    1246          177 :         } else if (tag == "showNaturals") {           // obsolete
    1247            4 :             e.readInt();
    1248          173 :         } else if (tag == "accidental") {             // we need to guess proper concert key
    1249          165 :             Key key = Key(e.readInt());
    1250          165 :             Key cKey = key;
    1251          165 :             if (p && !s->concertPitch()) {
    1252          164 :                 Interval v = p->instrument(s->tick())->transpose();
    1253          164 :                 if (!v.isZero()) {
    1254           19 :                     cKey = Transpose::transposeKey(key, v);
    1255              :                 }
    1256              :             }
    1257          165 :             sig.setConcertKey(cKey);
    1258          165 :             sig.setKey(key);
    1259            8 :         } else if (tag == "natural") {                // obsolete
    1260            0 :             e.readInt();
    1261            8 :         } else if (tag == "custom") {
    1262            2 :             e.readInt();
    1263            2 :             sig.setCustom(true);
    1264            6 :         } else if (tag == "mode") {
    1265            2 :             sig.setMode(TConv::fromXml(e.readAsciiText(), KeyMode::UNKNOWN));
    1266            4 :         } else if (tag == "subtype") {
    1267            3 :             subtype = e.readInt();
    1268            1 :         } else if (tag == "forInstrumentChange") {
    1269            0 :             sig.setForInstrumentChange(e.readBool());
    1270            1 :         } else if (!readItemProperties(s, e, ctx)) {
    1271            0 :             e.unknown();
    1272              :         }
    1273              :     }
    1274              :     // for backward compatibility
    1275          171 :     if (!sig.isValid()) {
    1276            4 :         sig.initFromSubtype(subtype);
    1277              :     }
    1278          171 :     if (sig.custom() && sig.customKeyDefs().empty()) {
    1279            2 :         sig.setMode(KeyMode::NONE);
    1280              :     }
    1281              :     // if there are more than 6 accidentals in transposing key, it cannot be PreferSharpFlat::AUTO
    1282          171 :     if (p && !s->concertPitch() && (sig.key() > 6 || sig.key() < -6)
    1283          342 :         && p->preferSharpFlat() == PreferSharpFlat::AUTO && !p->instrument(s->tick())->transpose().isZero()) {
    1284            0 :         p->setPreferSharpFlat(PreferSharpFlat::NONE);
    1285              :     }
    1286              : 
    1287          171 :     s->setKeySigEvent(sig);
    1288          171 : }
    1289              : 
    1290            0 : void TRead::read(StaffState* s, XmlReader& e, ReadContext& ctx)
    1291              : {
    1292            0 :     while (e.readNextStartElement()) {
    1293            0 :         const AsciiStringView tag(e.name());
    1294            0 :         if (tag == "subtype") {
    1295            0 :             s->setStaffStateType(StaffStateType(e.readInt()));
    1296            0 :         } else if (tag == "Instrument") {
    1297            0 :             Instrument i;
    1298            0 :             read(&i, e, ctx, nullptr);
    1299            0 :             s->setInstrument(std::move(i));
    1300            0 :         } else if (!readItemProperties(s, e, ctx)) {
    1301            0 :             e.unknown();
    1302              :         }
    1303              :     }
    1304            0 : }
    1305              : 
    1306           43 : void TRead::read(FiguredBass* b, XmlReader& e, ReadContext& ctx)
    1307              : {
    1308           43 :     String normalizedText;
    1309           43 :     int idx = 0;
    1310          168 :     while (e.readNextStartElement()) {
    1311          125 :         const AsciiStringView tag(e.name());
    1312          125 :         if (tag == "ticks") {
    1313           43 :             b->setTicks(e.readFraction());
    1314           82 :         } else if (tag == "onNote") {
    1315           19 :             b->setOnNote(e.readInt() != 0l);
    1316           63 :         } else if (tag == "FiguredBassItem") {
    1317           44 :             FiguredBassItem* pItem = b->createItem(idx++);
    1318           44 :             pItem->setTrack(b->track());
    1319           44 :             pItem->setOwnershipParent(b);
    1320           44 :             TRead::read(pItem, e, ctx);
    1321           44 :             b->appendItem(pItem);
    1322              :             // add item normalized text
    1323           44 :             if (!normalizedText.isEmpty()) {
    1324           20 :                 normalizedText.append('\n');
    1325              :             }
    1326           44 :             normalizedText.append(pItem->normalizedText());
    1327              :         }
    1328              : //            else if (tag == "style")
    1329              : //                  setStyledPropertyListIdx(e.readElementText());
    1330           19 :         else if (!readProperties(toTextBase(b), e, ctx)) {
    1331            0 :             e.unknown();
    1332              :         }
    1333              :     }
    1334              :     // if items could be parsed set normalized text
    1335           43 :     if (b->items().size() > 0) {
    1336           24 :         b->setXmlText(normalizedText);          // this is the text to show while editing
    1337              :     }
    1338              : 
    1339           43 :     compat::CompatUtils::migrateOffsetPre302(b, ctx.mscVersion());
    1340           43 : }
    1341              : 
    1342           44 : void TRead::read(FiguredBassItem* i, XmlReader& e, ReadContext& ctx)
    1343              : {
    1344          133 :     while (e.readNextStartElement()) {
    1345           89 :         const AsciiStringView tag(e.name());
    1346              : 
    1347           89 :         if (tag == "brackets") {
    1348           44 :             i->setParenth1((FiguredBassItem::Parenthesis)e.intAttribute("b0"));
    1349           44 :             i->setParenth2((FiguredBassItem::Parenthesis)e.intAttribute("b1"));
    1350           44 :             i->setParenth3((FiguredBassItem::Parenthesis)e.intAttribute("b2"));
    1351           44 :             i->setParenth4((FiguredBassItem::Parenthesis)e.intAttribute("b3"));
    1352           44 :             i->setParenth5((FiguredBassItem::Parenthesis)e.intAttribute("b4"));
    1353           44 :             e.readNext();
    1354           45 :         } else if (tag == "prefix") {
    1355            1 :             i->setPrefix((FiguredBassItem::Modifier)(e.readInt()));
    1356           44 :         } else if (tag == "digit") {
    1357           43 :             i->setDigit(e.readInt());
    1358            1 :         } else if (tag == "suffix") {
    1359            1 :             i->setSuffix((FiguredBassItem::Modifier)(e.readInt()));
    1360            0 :         } else if (tag == "continuationLine") {
    1361            0 :             i->setContLine((FiguredBassItem::ContLine)(e.readInt()));
    1362            0 :         } else if (!readItemProperties(i, e, ctx)) {
    1363            0 :             e.unknown();
    1364              :         }
    1365              :     }
    1366           44 : }
    1367              : 
    1368           39 : void TRead::read(Fermata* f, XmlReader& e, ReadContext& ctx)
    1369              : {
    1370          126 :     while (e.readNextStartElement()) {
    1371           48 :         if (!readProperties(f, e, ctx)) {
    1372            0 :             e.unknown();
    1373              :         }
    1374              :     }
    1375           39 : }
    1376              : 
    1377           52 : bool TRead::readProperties(Fermata* f, XmlReader& xml, ReadContext& ctx)
    1378              : {
    1379           52 :     const AsciiStringView tag(xml.name());
    1380              : 
    1381           52 :     if (tag == "subtype") {
    1382           39 :         AsciiStringView s = xml.readAsciiText();
    1383           39 :         SymId id = SymNames::symIdByName(s);
    1384           39 :         f->setSymIdAndTimeStretch(id);
    1385           13 :     } else if (tag == "play") {
    1386            3 :         f->setPlay(xml.readBool());
    1387           10 :     } else if (tag == "timeStretch") {
    1388            0 :         f->setTimeStretch(xml.readDouble());
    1389           10 :     } else if (tag == "offset") {
    1390            0 :         if (f->score()->mscVersion() > 114) {
    1391            0 :             readItemProperties(f, xml, ctx);
    1392              :         } else {
    1393            0 :             xml.skipCurrentElement();       // ignore manual layout in older scores
    1394              :         }
    1395           10 :     } else if (readItemProperties(f, xml, ctx)) {
    1396              :     } else {
    1397            0 :         return false;
    1398              :     }
    1399           52 :     return true;
    1400              : }
    1401              : 
    1402            0 : void TRead::read(Image* img, XmlReader& e, ReadContext& ctx)
    1403              : {
    1404              :     //! TODO Should be replaced with `ctx.mscVersion()`
    1405              :     //! But at the moment, `ctx` is not set everywhere
    1406            0 :     int mscVersion = img->score()->mscVersion();
    1407            0 :     if (mscVersion <= 114) {
    1408            0 :         img->setSizeIsSpatium(false);
    1409              :     }
    1410              : 
    1411            0 :     bool loaded = false;
    1412              : 
    1413            0 :     while (e.readNextStartElement()) {
    1414            0 :         const AsciiStringView tag(e.name());
    1415            0 :         if (tag == "autoScale") {
    1416            0 :             TRead::readProperty(img, e, ctx, Pid::AUTOSCALE);
    1417            0 :         } else if (tag == "size") {
    1418            0 :             TRead::readProperty(img, e, ctx, Pid::SIZE);
    1419            0 :         } else if (tag == "lockAspectRatio") {
    1420            0 :             TRead::readProperty(img, e, ctx, Pid::LOCK_ASPECT_RATIO);
    1421            0 :         } else if (tag == "sizeIsSpatium") {
    1422              :             // setting this using the property Pid::SIZE_IS_SPATIUM breaks, because the
    1423              :             // property setter attempts to maintain a constant size. If we're reading, we
    1424              :             // don't want to do that, because the stored size will be in:
    1425              :             //    absolute if size isn't spatium
    1426              :             //    sp if size is spatium
    1427            0 :             img->setSizeIsSpatium(e.readBool());
    1428            0 :         } else if (tag == "path") {
    1429            0 :             loaded = img->loadFromStore(e.readText().toStdString());
    1430            0 :         } else if (tag == "linkPath") {
    1431            0 :             if (img->configuration()->allowReadingImagesFromOutsideMscz() && !loaded) {
    1432            0 :                 loaded = img->loadFromFile(e.readText());
    1433              :             } else {
    1434            0 :                 e.skipCurrentElement();
    1435              :             }
    1436            0 :         } else if (tag == "subtype") {    // obsolete
    1437            0 :             e.skipCurrentElement();
    1438            0 :         } else if (!TRead::readProperties(img, e, ctx)) {
    1439            0 :             e.unknown();
    1440              :         }
    1441              :     }
    1442              : 
    1443            0 :     compat::CompatUtils::convertPre470ImageSize(img);
    1444            0 : }
    1445              : 
    1446          342 : void TRead::read(Tuplet* t, XmlReader& e, ReadContext& ctx)
    1447              : {
    1448          342 :     Text* number = nullptr;
    1449          342 :     Fraction ratio;
    1450          342 :     TDuration baseLen;
    1451              : 
    1452         1740 :     while (e.readNextStartElement()) {
    1453         1398 :         const AsciiStringView tag(e.name());
    1454              : 
    1455         1398 :         if (TRead::readStyledProperty(t, tag, e, ctx)) {
    1456         1122 :         } else if (tag == "bold") { //important that these properties are read after number is created
    1457            0 :             bool val = e.readInt();
    1458            0 :             if (number) {
    1459            0 :                 number->setBold(val);
    1460              :             }
    1461            0 :             if (t->isStyled(Pid::FONT_STYLE)) {
    1462            0 :                 t->setPropertyFlags(Pid::FONT_STYLE, PropertyFlags::UNSTYLED);
    1463              :             }
    1464         1122 :         } else if (tag == "italic") {
    1465            0 :             bool val = e.readInt();
    1466            0 :             if (number) {
    1467            0 :                 number->setItalic(val);
    1468              :             }
    1469            0 :             if (t->isStyled(Pid::FONT_STYLE)) {
    1470            0 :                 t->setPropertyFlags(Pid::FONT_STYLE, PropertyFlags::UNSTYLED);
    1471              :             }
    1472         1122 :         } else if (tag == "underline") {
    1473            0 :             bool val = e.readInt();
    1474            0 :             if (number) {
    1475            0 :                 number->setUnderline(val);
    1476              :             }
    1477            0 :             if (t->isStyled(Pid::FONT_STYLE)) {
    1478            0 :                 t->setPropertyFlags(Pid::FONT_STYLE, PropertyFlags::UNSTYLED);
    1479              :             }
    1480         1122 :         } else if (tag == "strike") {
    1481            0 :             bool val = e.readInt();
    1482            0 :             if (number) {
    1483            0 :                 number->setStrike(val);
    1484              :             }
    1485            0 :             if (t->isStyled(Pid::FONT_STYLE)) {
    1486            0 :                 t->setPropertyFlags(Pid::FONT_STYLE, PropertyFlags::UNSTYLED);
    1487              :             }
    1488         1122 :         } else if (tag == "normalNotes") {
    1489          342 :             ratio.setDenominator(e.readInt());
    1490          780 :         } else if (tag == "actualNotes") {
    1491          342 :             ratio.setNumerator(e.readInt());
    1492          438 :         } else if (tag == "p1") {
    1493            3 :             t->setUserPoint1(e.readPoint() * t->style().spatium());
    1494          435 :         } else if (tag == "p2") {
    1495            3 :             t->setUserPoint2(e.readPoint() * t->style().spatium());
    1496          432 :         } else if (tag == "baseNote") {
    1497          342 :             baseLen = TDuration(TConv::fromXml(e.readAsciiText(), DurationType::V_INVALID));
    1498           90 :         } else if (tag == "baseDots") {
    1499            0 :             baseLen.setDots(e.readInt());
    1500           90 :         } else if (tag == "Number") {
    1501           78 :             number = Factory::createText(t, TextStyleType::TUPLET);
    1502           78 :             number->setComposition(true);
    1503           78 :             Tuplet::resetNumberProperty(number);
    1504           78 :             TRead::read(number, e, ctx);
    1505           78 :             number->setVisible(t->visible());         //?? override saved property
    1506           78 :             number->setColor(t->color());
    1507           78 :             number->setTrack(t->track());
    1508              :             // move property flags from _number back to tuplet
    1509          468 :             for (auto p : { Pid::FONT_FACE, Pid::FONT_SIZE, Pid::FONT_STYLE, Pid::ALIGN, Pid::POSITION }) {
    1510          390 :                 t->setPropertyFlags(p, number->propertyFlags(p));
    1511              :             }
    1512           12 :         } else if (!readItemProperties(t, e, ctx)) {
    1513            0 :             e.unknown();
    1514              :         }
    1515              :     }
    1516              : 
    1517          342 :     t->setNumber(number);
    1518          342 :     t->setBaseLen(baseLen);
    1519          342 :     t->setRatio(ratio);
    1520              : 
    1521          342 :     Fraction f =  t->baseLen().fraction() * t->ratio().denominator();
    1522          342 :     t->setTicks(f.reduced());
    1523          342 : }
    1524              : 
    1525          790 : void TRead::read(Beam* b, XmlReader& e, ReadContext& ctx)
    1526              : {
    1527         2496 :     while (e.readNextStartElement()) {
    1528         1706 :         const AsciiStringView tag(e.name());
    1529         1706 :         if (tag == "StemDirection") {
    1530          217 :             TRead::readProperty(b, e, ctx, Pid::STEM_DIRECTION);
    1531         1489 :         } else if (tag == "distribute") {
    1532            0 :             e.skipCurrentElement(); // obsolete
    1533         1489 :         } else if (TRead::readStyledProperty(b, tag, e, ctx)) {
    1534         1489 :         } else if (tag == "growLeft") {
    1535            0 :             b->setGrowLeft(e.readDouble());
    1536         1489 :         } else if (tag == "growRight") {
    1537            0 :             b->setGrowRight(e.readDouble());
    1538         1489 :         } else if (tag == "Fragment") {
    1539            6 :             BeamFragment* f = new BeamFragment;
    1540            6 :             int idx = (b->direction() == DirectionV::AUTO || b->direction() == DirectionV::DOWN) ? 0 : 1;
    1541            6 :             b->setUserModified(true);
    1542            6 :             double _spatium = b->spatium();
    1543           18 :             while (e.readNextStartElement()) {
    1544           12 :                 const AsciiStringView tag1(e.name());
    1545           12 :                 if (tag1 == "y1") {
    1546            6 :                     f->py1[idx] = e.readDouble() * _spatium;
    1547            6 :                 } else if (tag1 == "y2") {
    1548            6 :                     f->py2[idx] = e.readDouble() * _spatium;
    1549              :                 } else {
    1550            0 :                     e.unknown();
    1551              :                 }
    1552              :             }
    1553            6 :             b->addBeamFragment(f);
    1554         1483 :         } else if (tag == "l1" || tag == "l2") {      // ignore
    1555         1480 :             e.skipCurrentElement();
    1556            3 :         } else if (tag == "subtype") {          // obsolete
    1557            0 :             e.skipCurrentElement();
    1558            3 :         } else if (tag == "y1" || tag == "y2") { // obsolete
    1559            0 :             e.skipCurrentElement();
    1560            3 :         } else if (!readItemProperties(b, e, ctx)) {
    1561            0 :             e.unknown();
    1562              :         }
    1563              :     }
    1564          790 : }
    1565              : 
    1566            0 : void TRead::read(Ambitus* a, XmlReader& e, ReadContext& ctx)
    1567              : {
    1568            0 :     while (e.readNextStartElement()) {
    1569            0 :         if (!readProperties(a, e, ctx)) {
    1570            0 :             e.unknown();
    1571              :         }
    1572              :     }
    1573            0 : }
    1574              : 
    1575           11 : bool TRead::readProperties(Ambitus* a, XmlReader& e, ReadContext& ctx)
    1576              : {
    1577           11 :     const AsciiStringView tag(e.name());
    1578           11 :     if (tag == "head") {
    1579            0 :         TRead::readProperty(a, e, ctx, Pid::HEAD_GROUP);
    1580           11 :     } else if (tag == "headType") {
    1581            0 :         TRead::readProperty(a, e, ctx, Pid::HEAD_TYPE);
    1582           11 :     } else if (tag == "mirror") {
    1583            1 :         TRead::readProperty(a, e, ctx, Pid::MIRROR_HEAD);
    1584           10 :     } else if (tag == "hasLine") {
    1585            0 :         a->setHasLine(e.readInt());
    1586           10 :     } else if (tag == "lineWidth") {
    1587            0 :         TRead::readProperty(a, e, ctx, Pid::LINE_WIDTH);
    1588           10 :     } else if (tag == "topPitch") {
    1589            2 :         a->setTopPitch(e.readInt(), false);
    1590            8 :     } else if (tag == "bottomPitch") {
    1591            2 :         a->setBottomPitch(e.readInt(), false);
    1592            6 :     } else if (tag == "topTpc") {
    1593            2 :         a->setTopTpc(e.readInt(), false);
    1594            4 :     } else if (tag == "bottomTpc") {
    1595            2 :         a->setBottomTpc(e.readInt(), false);
    1596            2 :     } else if (tag == "topAccidental") {
    1597            0 :         while (e.readNextStartElement()) {
    1598            0 :             if (e.name() == "Accidental") {
    1599            0 :                 if (a->score()->mscVersion() < 301) {
    1600            0 :                     read206::Read206::readAccidental206(a->topAccidental(), e, ctx);
    1601              :                 } else {
    1602            0 :                     TRead::read(a->topAccidental(), e, ctx);
    1603              :                 }
    1604              :             } else {
    1605            0 :                 e.skipCurrentElement();
    1606              :             }
    1607              :         }
    1608            2 :     } else if (tag == "bottomAccidental") {
    1609            2 :         while (e.readNextStartElement()) {
    1610            1 :             if (e.name() == "Accidental") {
    1611            1 :                 if (a->score()->mscVersion() < 301) {
    1612            1 :                     read206::Read206::readAccidental206(a->bottomAccidental(), e, ctx);
    1613              :                 } else {
    1614            0 :                     TRead::read(a->bottomAccidental(), e, ctx);
    1615              :                 }
    1616              :             } else {
    1617            0 :                 e.skipCurrentElement();
    1618              :             }
    1619              :         }
    1620            1 :     } else if (readItemProperties(a, e, ctx)) {
    1621              :     } else {
    1622            0 :         return false;
    1623              :     }
    1624           11 :     return true;
    1625              : }
    1626              : 
    1627          333 : void TRead::read(Accidental* a, XmlReader& e, ReadContext& ctx)
    1628              : {
    1629          695 :     while (e.readNextStartElement()) {
    1630          362 :         const AsciiStringView tag(e.name());
    1631          362 :         if (tag == "bracket") {
    1632            0 :             int i = e.readInt();
    1633            0 :             if (i == 0 || i == 1 || i == 2) {
    1634            0 :                 a->setBracket(AccidentalBracket(i));
    1635              :             }
    1636          362 :         } else if (tag == "subtype") {
    1637          333 :             a->setSubtype(e.readAsciiText());
    1638           29 :         } else if (tag == "role") {
    1639           22 :             a->setRole(TConv::fromXml(e.readAsciiText(), AccidentalRole::AUTO));
    1640            7 :         } else if (tag == "small") {
    1641            0 :             a->setSmall(e.readInt());
    1642            7 :         } else if (readItemProperties(a, e, ctx)) {
    1643              :         } else {
    1644            0 :             e.unknown();
    1645              :         }
    1646              :     }
    1647          333 : }
    1648              : 
    1649           96 : void TRead::read(Marker* m, XmlReader& e, ReadContext& ctx)
    1650              : {
    1651           96 :     MarkerType mt = MarkerType::SEGNO;
    1652              : 
    1653          384 :     while (e.readNextStartElement()) {
    1654          288 :         const AsciiStringView tag(e.name());
    1655          288 :         if (tag == "label") {
    1656           96 :             AsciiStringView s(e.readAsciiText());
    1657           96 :             m->setLabel(String::fromAscii(s.ascii()));
    1658           96 :             mt = TConv::fromXml(s, MarkerType::USER);
    1659          192 :         } else if (!readProperties(toTextBase(m), e, ctx)) {
    1660            0 :             e.unknown();
    1661              :         }
    1662              :     }
    1663           96 :     m->setMarkerType(mt);
    1664              : 
    1665           96 :     compat::CompatUtils::migrateOffsetPre302(m, ctx.mscVersion());
    1666           96 : }
    1667              : 
    1668           74 : void TRead::read(Jump* j, XmlReader& e, ReadContext& ctx)
    1669              : {
    1670          454 :     while (e.readNextStartElement()) {
    1671          380 :         const AsciiStringView tag(e.name());
    1672          380 :         if (tag == "jumpTo") {
    1673           74 :             j->setJumpTo(e.readText());
    1674          306 :         } else if (tag == "playUntil") {
    1675           74 :             j->setPlayUntil(e.readText());
    1676          232 :         } else if (tag == "continueAt") {
    1677           74 :             j->setContinueAt(e.readText());
    1678          158 :         } else if (tag == "playRepeats") {
    1679            8 :             j->setPlayRepeats(e.readBool());
    1680          150 :         } else if (!readProperties(toTextBase(j), e, ctx)) {
    1681            0 :             e.unknown();
    1682              :         }
    1683              :     }
    1684              : 
    1685           74 :     compat::CompatUtils::migrateOffsetPre302(j, ctx.mscVersion());
    1686           74 : }
    1687              : 
    1688            1 : void TRead::read(MeasureNumber* n, XmlReader& xml, ReadContext& ctx)
    1689              : {
    1690            1 :     read(static_cast<MeasureNumberBase*>(n), xml, ctx);
    1691            1 : }
    1692              : 
    1693            1 : void TRead::read(MeasureNumberBase* b, XmlReader& xml, ReadContext& ctx)
    1694              : {
    1695            3 :     while (xml.readNextStartElement()) {
    1696            1 :         if (!readProperties(b, xml, ctx)) {
    1697            0 :             xml.unknown();
    1698              :         }
    1699              :     }
    1700              : 
    1701            1 :     compat::CompatUtils::migrateOffsetPre302(b, ctx.mscVersion());
    1702            1 : }
    1703              : 
    1704            1 : bool TRead::readProperties(MeasureNumberBase* n, XmlReader& xml, ReadContext& ctx)
    1705              : {
    1706            1 :     if (TRead::readProperty(n, xml.name(), xml, ctx, Pid::HPLACEMENT)) {
    1707            0 :         return true;
    1708              :     } else {
    1709            1 :         return readProperties(toTextBase(n), xml, ctx);
    1710              :     }
    1711              : }
    1712              : 
    1713            0 : void TRead::read(MMRestRange* r, XmlReader& xml, ReadContext& ctx)
    1714              : {
    1715            0 :     read(static_cast<MeasureNumberBase*>(r), xml, ctx);
    1716            0 : }
    1717              : 
    1718            0 : void TRead::read(SystemDivider* d, XmlReader& e, ReadContext& ctx)
    1719              : {
    1720            0 :     if (e.attribute("type") == "left") {
    1721            0 :         d->setDividerType(SystemDividerType::LEFT);
    1722              :     } else {
    1723            0 :         d->setDividerType(SystemDividerType::RIGHT);
    1724              :     }
    1725            0 :     TRead::read(static_cast<Symbol*>(d), e, ctx);
    1726            0 : }
    1727              : 
    1728            0 : void TRead::read(ActionIcon* i, XmlReader& e, ReadContext&)
    1729              : {
    1730            0 :     while (e.readNextStartElement()) {
    1731            0 :         const AsciiStringView tag(e.name());
    1732            0 :         if (tag == "action") {
    1733            0 :             i->setActionType(TConv::fromXml(e.readAsciiText(), ActionIconType::UNDEFINED));
    1734            0 :         } else if (tag == "subtype") {
    1735            0 :             i->setActionType(static_cast<ActionIconType>(e.readInt()));
    1736              :         } else {
    1737            0 :             e.unknown();
    1738              :         }
    1739              :     }
    1740            0 : }
    1741              : 
    1742           43 : void TRead::read(Arpeggio* a, XmlReader& e, ReadContext& ctx)
    1743              : {
    1744          102 :     while (e.readNextStartElement()) {
    1745           59 :         const AsciiStringView tag(e.name());
    1746           59 :         if (tag == "subtype") {
    1747           43 :             a->setArpeggioType(TConv::fromXml(e.readAsciiText(), ArpeggioType::NORMAL));
    1748           16 :         } else if (tag == "userLen1") {
    1749            3 :             a->setUserLen1(e.readDouble() * a->spatium());
    1750           13 :         } else if (tag == "userLen2") {
    1751            0 :             a->setUserLen2(e.readDouble() * a->spatium());
    1752           13 :         } else if (tag == "span") {
    1753              :             // Span now refers to number of voices spanned, not staves
    1754            1 :             a->setSpan((e.readInt() - 1) * VOICES + 1);
    1755           12 :         } else if (tag == "play") {
    1756            0 :             a->setPlayArpeggio(e.readBool());
    1757           12 :         } else if (tag == "timeStretch") {
    1758            3 :             a->setStretch(e.readDouble());
    1759            9 :         } else if (!readItemProperties(a, e, ctx)) {
    1760            0 :             e.unknown();
    1761              :         }
    1762              :     }
    1763           43 : }
    1764              : 
    1765           14 : void TRead::read(Ornament* o, XmlReader& xml, ReadContext& ctx)
    1766              : {
    1767           84 :     while (xml.readNextStartElement()) {
    1768           56 :         if (!readProperties(o, xml, ctx)) {
    1769            0 :             xml.unknown();
    1770              :         }
    1771              :     }
    1772           14 : }
    1773              : 
    1774           56 : bool TRead::readProperties(Ornament* o, XmlReader& xml, ReadContext& ctx)
    1775              : {
    1776           56 :     const AsciiStringView tag(xml.name());
    1777           56 :     if (readProperty(o, tag, xml, ctx, Pid::INTERVAL_ABOVE)) {
    1778           42 :     } else if (readProperty(o, tag, xml, ctx, Pid::INTERVAL_BELOW)) {
    1779           35 :     } else if (readProperty(o, tag, xml, ctx, Pid::ORNAMENT_SHOW_ACCIDENTAL)) {
    1780           35 :     } else if (readProperty(o, tag, xml, ctx, Pid::START_ON_UPPER_NOTE)) {
    1781           35 :     } else if (readProperties(toArticulation(o), xml, ctx)) {
    1782           21 :     } else if (tag == "Accidental") {
    1783           14 :         Accidental* accidental = Factory::createAccidental(o);
    1784           14 :         TRead::read(accidental, xml, ctx);
    1785           14 :         accidental->placement() == PlacementV::ABOVE ? o->setAccidentalAbove(accidental) : o->setAccidentalBelow(accidental);
    1786            7 :     } else if (tag == "Chord") {
    1787            7 :         Chord* chord = Factory::createChord(ctx.score()->dummy());
    1788            7 :         TRead::read(chord, xml, ctx);
    1789            7 :         chord->setTrack(ctx.track());
    1790            7 :         o->setCueNoteChord(chord);
    1791            7 :         o->setNoteAbove(chord->notes().front());
    1792           14 :         for (Note* note : chord->notes()) {
    1793            7 :             compat::CompatUtils::doMigrateNoteParens(note);
    1794              :         }
    1795              :     } else {
    1796            0 :         return false;
    1797              :     }
    1798           56 :     return true;
    1799              : }
    1800              : 
    1801           95 : void TRead::read(Articulation* a, XmlReader& xml, ReadContext& ctx)
    1802              : {
    1803          316 :     while (xml.readNextStartElement()) {
    1804          126 :         if (!readProperties(a, xml, ctx)) {
    1805            0 :             xml.unknown();
    1806              :         }
    1807              :     }
    1808           95 : }
    1809              : 
    1810          255 : bool TRead::readProperties(Articulation* a, XmlReader& xml, ReadContext& ctx)
    1811              : {
    1812          255 :     const AsciiStringView tag(xml.name());
    1813              : 
    1814          255 :     if (tag == "subtype") {
    1815          109 :         AsciiStringView s = xml.readAsciiText();
    1816          109 :         ArticulationTextType textType = TConv::fromXml(s, ArticulationTextType::NO_TEXT);
    1817          109 :         if (textType != ArticulationTextType::NO_TEXT) {
    1818            0 :             a->setTextType(textType);
    1819              :         } else {
    1820          109 :             SymId id = SymNames::symIdByName(s);
    1821          109 :             if (id == SymId::noSym) {
    1822            0 :                 id = read206::Read206::articulationNames2SymId206(s); // compatibility hack for "old" 3.0 scores
    1823              :             }
    1824          109 :             if (id == SymId::noSym || s == "ornamentMordentInverted") {   // SMuFL < 1.30
    1825            0 :                 id = SymId::ornamentMordent;
    1826              :             }
    1827              : 
    1828              :             //! TODO Should be replaced with `ctx.mscoreVersion()`
    1829              :             //! But at the moment, `ctx` is not set everywhere
    1830          109 :             String programVersion = a->score()->mscoreVersion();
    1831          109 :             if (!programVersion.isEmpty() && programVersion < u"3.6") {
    1832            0 :                 if (id == SymId::noSym || s == "ornamentMordent") {   // SMuFL < 1.30 and MuseScore < 3.6
    1833            0 :                     id = SymId::ornamentShortTrill;
    1834              :                 }
    1835              :             }
    1836          109 :             a->setSymId(id);
    1837          109 :         }
    1838          146 :     } else if (tag == "channel") {
    1839            0 :         a->setChannelName(xml.attribute("name"));
    1840            0 :         xml.readNext();
    1841          146 :     } else if (tag == "anchor") {
    1842           32 :         if (ctx.mscVersion() <= 400) {
    1843           32 :             int v = xml.readInt();
    1844           32 :             ArticulationAnchor aa = compat::CompatUtils::translateToNewArticulationAnchor(v);
    1845           32 :             a->setAnchor(aa);
    1846           32 :             if (a->isStyled(Pid::ARTICULATION_ANCHOR)) {
    1847           32 :                 a->setPropertyFlags(Pid::ARTICULATION_ANCHOR, PropertyFlags::UNSTYLED);
    1848              :             }
    1849              :         } else {
    1850            0 :             TRead::readProperty(a, tag, xml, ctx, Pid::ARTICULATION_ANCHOR);
    1851              :         }
    1852          114 :     } else if (tag == "direction") {
    1853            0 :         TRead::readProperty(a, xml, ctx, Pid::DIRECTION);
    1854          114 :     } else if (tag == "ornamentStyle") {
    1855            5 :         TRead::readProperty(a, xml, ctx, Pid::ORNAMENT_STYLE);
    1856          109 :     } else if (tag == "play") {
    1857            4 :         a->setPlayArticulation(xml.readBool());
    1858          105 :     } else if (tag == "offset") {
    1859            1 :         if (a->score()->mscVersion() >= 400) {
    1860            1 :             readItemProperties(a, xml, ctx);
    1861              :         } else {
    1862            0 :             xml.skipCurrentElement();       // ignore manual layout in older scores
    1863              :         }
    1864          104 :     } else if (readItemProperties(a, xml, ctx)) {
    1865              :     } else {
    1866           77 :         return false;
    1867              :     }
    1868          178 :     return true;
    1869              : }
    1870              : 
    1871            0 : void TRead::read(BagpipeEmbellishment* b, XmlReader& e, ReadContext&)
    1872              : {
    1873            0 :     while (e.readNextStartElement()) {
    1874            0 :         const AsciiStringView tag(e.name());
    1875            0 :         if (tag == "subtype") {
    1876            0 :             b->setEmbelType(TConv::fromXml(e.readAsciiText(), EmbellishmentType(0)));
    1877              :         } else {
    1878            0 :             e.unknown();
    1879              :         }
    1880              :     }
    1881            0 : }
    1882              : 
    1883          752 : void TRead::read(BarLine* b, XmlReader& e, ReadContext& ctx)
    1884              : {
    1885              :     // initialize span properties with staff values
    1886          752 :     b->resetProperty(Pid::BARLINE_SPAN);
    1887          752 :     b->resetProperty(Pid::BARLINE_SPAN_FROM);
    1888          752 :     b->resetProperty(Pid::BARLINE_SPAN_TO);
    1889              : 
    1890         1879 :     while (e.readNextStartElement()) {
    1891         1127 :         const AsciiStringView tag(e.name());
    1892         1127 :         if (tag == "subtype") {
    1893          459 :             b->setBarLineType(TConv::fromXml(e.readAsciiText(), BarLineType::NORMAL));
    1894          668 :         } else if (tag == "span") {
    1895          660 :             b->setSpanStaff(e.readBool());
    1896            8 :         } else if (tag == "spanFromOffset") {
    1897            1 :             b->setSpanFrom(e.readInt());
    1898            7 :         } else if (tag == "spanToOffset") {
    1899            1 :             b->setSpanTo(e.readInt());
    1900            6 :         } else if (tag == "Articulation") {
    1901            0 :             Articulation* a = Factory::createArticulation(b->score()->dummy());
    1902            0 :             TRead::read(a, e, ctx);
    1903            0 :             b->add(a);
    1904            6 :         } else if (tag == "Symbol") {
    1905            0 :             Symbol* s = Factory::createSymbol(b);
    1906            0 :             s->setTrack(b->track());
    1907            0 :             TRead::read(s, e, ctx);
    1908            0 :             b->add(s);
    1909            6 :         } else if (tag == "Image") {
    1910            0 :             if (MScore::noImages) {
    1911            0 :                 e.skipCurrentElement();
    1912              :             } else {
    1913            0 :                 Image* image = Factory::createImage(b);
    1914            0 :                 image->setTrack(b->track());
    1915            0 :                 TRead::read(image, e, ctx);
    1916            0 :                 b->add(image);
    1917              :             }
    1918            6 :         } else if (!readItemProperties(b, e, ctx)) {
    1919            0 :             e.unknown();
    1920              :         }
    1921              :     }
    1922          752 : }
    1923              : 
    1924            4 : void TRead::read(Bend* b, XmlReader& e, ReadContext& ctx)
    1925              : {
    1926           17 :     while (e.readNextStartElement()) {
    1927           13 :         const AsciiStringView tag(e.name());
    1928              : 
    1929           13 :         if (TRead::readStyledProperty(b, tag, e, ctx)) {
    1930           13 :         } else if (tag == "point") {
    1931           12 :             PitchValue pv;
    1932           12 :             pv.time    = e.intAttribute("time");
    1933           12 :             pv.pitch   = e.intAttribute("pitch");
    1934           12 :             pv.vibrato = e.intAttribute("vibrato");
    1935           12 :             b->addPoint(pv);
    1936           12 :             e.readNext();
    1937            1 :         } else if (tag == "play") {
    1938            1 :             b->setPlayBend(e.readBool());
    1939            0 :         } else if (!readItemProperties(b, e, ctx)) {
    1940            0 :             e.unknown();
    1941              :         }
    1942              :     }
    1943            4 : }
    1944              : 
    1945          370 : void TRead::read(Box* b, XmlReader& e, ReadContext& ctx)
    1946              : {
    1947         2049 :     while (e.readNextStartElement()) {
    1948         1309 :         if (!readProperties(b, e, ctx)) {
    1949            0 :             e.unknown();
    1950              :         }
    1951              :     }
    1952              : 
    1953          370 :     if (b->score()->mscVersion() < 302) {
    1954          313 :         b->setAutoSizeEnabled(false);    // disable auto-size for older scores by default.
    1955              :     }
    1956          370 : }
    1957              : 
    1958           45 : void TRead::read(HBox* b, XmlReader& e, ReadContext& ctx)
    1959              : {
    1960          226 :     while (e.readNextStartElement()) {
    1961          136 :         if (!readProperties(b, e, ctx)) {
    1962            0 :             e.unknown();
    1963              :         }
    1964              :     }
    1965           45 :     if (b->score()->mscVersion() < 302) {
    1966           45 :         b->setAutoSizeEnabled(false);    // disable auto-size for older scores by default.
    1967              :     }
    1968           45 : }
    1969              : 
    1970          136 : bool TRead::readProperties(HBox* b, XmlReader& e, ReadContext& ctx)
    1971              : {
    1972          136 :     const AsciiStringView tag(e.name());
    1973          136 :     if (readProperty(b, tag, e, ctx, Pid::CREATE_SYSTEM_HEADER)) {
    1974          136 :     } else if (readProperties(static_cast<Box*>(b), e, ctx)) {
    1975              :     } else {
    1976            0 :         return false;
    1977              :     }
    1978          136 :     return true;
    1979              : }
    1980              : 
    1981          370 : void TRead::read(VBox* b, XmlReader& xml, ReadContext& ctx)
    1982              : {
    1983          370 :     TRead::read(static_cast<Box*>(b), xml, ctx);
    1984          370 : }
    1985              : 
    1986            0 : void TRead::read(FBox* b, XmlReader& xml, ReadContext& ctx)
    1987              : {
    1988            0 :     TRead::read(static_cast<Box*>(b), xml, ctx);
    1989            0 : }
    1990              : 
    1991           95 : void TRead::read(TBox* b, XmlReader& e, ReadContext& ctx)
    1992              : {
    1993          634 :     while (e.readNextStartElement()) {
    1994          539 :         const AsciiStringView tag(e.name());
    1995          539 :         if (tag == "Text") {
    1996           95 :             TRead::read(b->text(), e, ctx);
    1997          444 :         } else if (TRead::readProperties(static_cast<Box*>(b), e, ctx)) {
    1998              :         } else {
    1999            0 :             e.unknown();
    2000              :         }
    2001              :     }
    2002           95 : }
    2003              : 
    2004           32 : bool TRead::readBoxProperties(Box* b, XmlReader& e, ReadContext& ctx)
    2005              : {
    2006           32 :     if (b->isHBox()) {
    2007            0 :         return readProperties(dynamic_cast<HBox*>(b), e, ctx);
    2008              :     }
    2009           32 :     return readProperties(b, e, ctx);
    2010              : }
    2011              : 
    2012         1921 : bool TRead::readProperties(Box* b, XmlReader& e, ReadContext& ctx)
    2013              : {
    2014         1921 :     const AsciiStringView tag(e.name());
    2015         1921 :     if (tag == "height") {
    2016          482 :         b->setBoxHeight(Spatium(e.readDouble()));
    2017         1439 :     } else if (tag == "width") {
    2018           45 :         b->setBoxWidth(Spatium(e.readDouble()));
    2019         1394 :     } else if (tag == "topGap") {
    2020           68 :         double gap = e.readDouble();
    2021           68 :         b->setTopGap(Spatium(gap));
    2022           68 :         if (b->score()->mscVersion() >= 206) {
    2023           68 :             b->setTopGap(Spatium(gap));
    2024              :         }
    2025           68 :         b->setPropertyFlags(Pid::TOP_GAP, PropertyFlags::UNSTYLED);
    2026         1326 :     } else if (tag == "bottomGap") {
    2027           94 :         double gap = e.readDouble();
    2028           94 :         b->setBottomGap(Spatium(gap));
    2029           94 :         if (b->score()->mscVersion() >= 206) {
    2030           94 :             b->setBottomGap(Spatium(gap));
    2031              :         }
    2032           94 :         b->setPropertyFlags(Pid::BOTTOM_GAP, PropertyFlags::UNSTYLED);
    2033         1232 :     } else if (tag == "leftMargin") {
    2034          108 :         b->setLeftMargin(e.readDouble());
    2035         1124 :     } else if (tag == "rightMargin") {
    2036          108 :         b->setRightMargin(e.readDouble());
    2037         1016 :     } else if (tag == "topMargin") {
    2038          109 :         b->setTopMargin(e.readDouble());
    2039          907 :     } else if (tag == "bottomMargin") {
    2040          108 :         b->setBottomMargin(e.readDouble());
    2041          799 :     } else if (tag == "boxAutoSize") {
    2042            5 :         b->setAutoSizeEnabled(e.readBool());
    2043          794 :     } else if (tag == "Text") {
    2044              :         Text* t;
    2045          747 :         if (b->isTBox()) {
    2046            0 :             t = toTBox(b)->text();
    2047            0 :             TRead::read(t, e, ctx);
    2048              :         } else {
    2049          747 :             t = Factory::createText(b);
    2050          747 :             TRead::read(t, e, ctx);
    2051          747 :             if (t->empty()) {
    2052            0 :                 LOGD("read empty text");
    2053              :             } else {
    2054          747 :                 b->add(t);
    2055              :             }
    2056              :         }
    2057           47 :     } else if (tag == "Symbol") {
    2058            0 :         Symbol* s = new Symbol(b);
    2059            0 :         TRead::read(s, e, ctx);
    2060            0 :         b->add(s);
    2061           47 :     } else if (tag == "Image") {
    2062            0 :         if (MScore::noImages) {
    2063            0 :             e.skipCurrentElement();
    2064              :         } else {
    2065            0 :             Image* image = new Image(b);
    2066            0 :             image->setTrack(ctx.track());
    2067            0 :             TRead::read(image, e, ctx);
    2068            0 :             b->add(image);
    2069              :         }
    2070           47 :     } else if (tag == "FretDiagram") {
    2071            0 :         FretDiagram* f = Factory::createFretDiagram(b->score()->dummy());
    2072            0 :         TRead::read(f, e, ctx);
    2073              :         //! TODO Looks like a bug.
    2074              :         //! The FretDiagram parent must be Segment
    2075              :         //! there is a method: `Segment* segment() const { return toSegment(parent()); }`,
    2076              :         //! but when we add it to Box, the parent will be rewritten.
    2077            0 :         b->add(f);
    2078           47 :     } else if (tag == "HBox") {
    2079              :         // m_parent is used here (rather than system()) because explicit parent isn't set for this object
    2080              :         // until it is fully read. m_parent is nonetheless valid and using it here matches MU3 behavior.
    2081              :         // If we do not set the parent of this new box correctly, it will cause a crash on read()
    2082              :         // because it needs access to the system it is being added to. (c.r. issue #14643)
    2083            0 :         if (b->parent() && b->parent()->isSystem()) {
    2084            0 :             HBox* hb = new HBox(b->score());
    2085            0 :             TRead::read(hb, e, ctx);
    2086              :             //! TODO Looks like a bug.
    2087              :             //! The HBox parent must be System
    2088              :             //! there is a method: `System* system() const { return (System*)parent(); }`,
    2089              :             //! but when we add it to Box, the parent will be rewritten.
    2090            0 :             b->add(hb);
    2091              :         }
    2092           47 :     } else if (tag == "VBox") {
    2093            0 :         if (b->parent() && b->parent()->isSystem()) {
    2094            0 :             VBox* vb = new VBox(b->score());
    2095            0 :             TRead::read(vb, e, ctx);
    2096              :             //! TODO Looks like a bug.
    2097              :             //! The VBox parent must be System
    2098              :             //! there is a method: `System* system() const { return (System*)parent(); }`,
    2099              :             //! but when we add it to Box, the parent will be rewritten.
    2100            0 :             b->add(vb);
    2101              :         }
    2102           47 :     } else if (TRead::readProperties(static_cast<MeasureBase*>(b), e, ctx)) {
    2103              :     } else {
    2104           10 :         return false;
    2105              :     }
    2106         1911 :     return true;
    2107              : }
    2108              : 
    2109          597 : bool TRead::readProperties(MeasureBase* b, XmlReader& e, ReadContext& ctx)
    2110              : {
    2111          597 :     const AsciiStringView tag(e.name());
    2112          597 :     if (tag == "LayoutBreak") {
    2113          555 :         LayoutBreak* lb = Factory::createLayoutBreak(b);
    2114          555 :         TRead::read(lb, e, ctx);
    2115          555 :         lb->setTrack(0);
    2116          555 :         bool doAdd = true;
    2117          555 :         switch (lb->layoutBreakType()) {
    2118          506 :         case LayoutBreakType::LINE:
    2119          506 :             if (b->lineBreak()) {
    2120            0 :                 doAdd = false;
    2121              :             }
    2122          506 :             break;
    2123           10 :         case LayoutBreakType::PAGE:
    2124           10 :             if (b->pageBreak()) {
    2125            0 :                 doAdd = false;
    2126              :             }
    2127           10 :             break;
    2128           31 :         case LayoutBreakType::SECTION:
    2129           31 :             if (b->sectionBreak()) {
    2130            0 :                 doAdd = false;
    2131              :             }
    2132           31 :             break;
    2133            8 :         case LayoutBreakType::NOBREAK:
    2134            8 :             if (b->noBreak()) {
    2135            0 :                 doAdd = false;
    2136              :             }
    2137            8 :             break;
    2138              :         }
    2139          555 :         if (doAdd) {
    2140          555 :             b->add(lb);
    2141          555 :             b->cleanupLayoutBreaks(false);
    2142              :         }
    2143           42 :     } else if (tag == "StaffTypeChange") {
    2144           18 :         StaffTypeChange* stc = Factory::createStaffTypeChange(b);
    2145           18 :         stc->setTrack(ctx.track());
    2146           18 :         TRead::read(stc, e, ctx);
    2147           18 :         b->add(stc);
    2148           24 :     } else if (readItemProperties(b, e, ctx)) {
    2149              :     } else {
    2150           10 :         return false;
    2151              :     }
    2152          587 :     return true;
    2153              : }
    2154              : 
    2155            0 : void TRead::read(Bracket* b, XmlReader& e, ReadContext& ctx)
    2156              : {
    2157            0 :     BracketItem* bi = Factory::createBracketItem(b->score()->dummy());
    2158            0 :     bi->setBracketType(TConv::fromXml(e.asciiAttribute("type"), BracketType::NORMAL));
    2159              : 
    2160            0 :     while (e.readNextStartElement()) {
    2161            0 :         if (e.name() == "level") {
    2162            0 :             bi->setColumn(e.readInt());
    2163            0 :         } else if (!readItemProperties(b, e, ctx)) {
    2164            0 :             e.unknown();
    2165              :         }
    2166              :     }
    2167              : 
    2168            0 :     b->setBracketItem(bi);
    2169            0 : }
    2170              : 
    2171           34 : void TRead::read(Breath* b, XmlReader& e, ReadContext& ctx)
    2172              : {
    2173           80 :     while (e.readNextStartElement()) {
    2174           46 :         const AsciiStringView tag(e.name());
    2175           46 :         if (tag == "subtype") {                 // obsolete
    2176            3 :             SymId symId = SymId::noSym;
    2177            3 :             switch (e.readInt()) {
    2178            1 :             case 0:
    2179              :             case 1:
    2180            1 :                 symId = SymId::breathMarkComma;
    2181            1 :                 break;
    2182            1 :             case 2:
    2183            1 :                 symId = SymId::caesuraCurved;
    2184            1 :                 break;
    2185            1 :             case 3:
    2186            1 :                 symId = SymId::caesura;
    2187            1 :                 break;
    2188              :             }
    2189            3 :             b->setSymId(symId);
    2190           43 :         } else if (tag == "symbol") {
    2191           31 :             b->setSymId(SymNames::symIdByName(e.readAsciiText()));
    2192           12 :         } else if (tag == "pause") {
    2193            3 :             b->setPause(e.readDouble());
    2194            9 :         } else if (!readItemProperties(b, e, ctx)) {
    2195            0 :             e.unknown();
    2196              :         }
    2197              :     }
    2198           34 : }
    2199              : 
    2200           18 : bool TRead::readProperties(BSymbol* sym, XmlReader& e, ReadContext& ctx)
    2201              : {
    2202           18 :     const AsciiStringView tag = e.name();
    2203              : 
    2204           18 :     if (readItemProperties(sym, e, ctx)) {
    2205           18 :         return true;
    2206            0 :     } else if (tag == "systemFlag") {
    2207            0 :         sym->setSystemFlag(e.readInt());
    2208            0 :     } else if (tag == "Symbol") {
    2209            0 :         Symbol* s = Factory::createSymbol(sym);
    2210            0 :         TRead::read(s, e, ctx);
    2211            0 :         sym->add(s);
    2212            0 :     } else if (tag == "FSymbol") {
    2213            0 :         FSymbol* s = Factory::createFSymbol(sym);
    2214            0 :         TRead::read(s, e, ctx);
    2215            0 :         sym->add(s);
    2216            0 :     } else if (tag == "Image") {
    2217            0 :         if (MScore::noImages) {
    2218            0 :             e.skipCurrentElement();
    2219              :         } else {
    2220            0 :             Image* image = new Image(sym);
    2221            0 :             TRead::read(image, e, ctx);
    2222            0 :             sym->add(image);
    2223              :         }
    2224              :     } else {
    2225            0 :         return false;
    2226              :     }
    2227            0 :     return true;
    2228              : }
    2229              : 
    2230           70 : void TRead::read(Symbol* sym, XmlReader& e, ReadContext& ctx)
    2231              : {
    2232           70 :     SymId symId = SymId::noSym;
    2233           70 :     String fontName;
    2234          176 :     while (e.readNextStartElement()) {
    2235          106 :         const AsciiStringView tag(e.name());
    2236          106 :         if (tag == "name") {
    2237           70 :             String val(e.readText());
    2238           70 :             symId = SymNames::symIdByName(val);
    2239           70 :             if (val != "noSym" && symId == SymId::noSym) {
    2240              :                 // if symbol name not found, fall back to user names
    2241              :                 // TODO: does it make sense? user names are probably localized
    2242            0 :                 symId = SymNames::symIdByUserName(val);
    2243            0 :                 if (symId == SymId::noSym) {
    2244            0 :                     LOGD("unknown symbol <%s>, falling back to no symbol", muPrintable(val));
    2245              :                     // set a default symbol, or layout() will crash
    2246            0 :                     symId = SymId::noSym;
    2247              :                 }
    2248              :             }
    2249           70 :             sym->setSym(symId);
    2250          106 :         } else if (tag == "font") {
    2251           18 :             fontName = e.readText();
    2252           18 :         } else if (tag == "Symbol") {
    2253            0 :             Symbol* s = new Symbol(sym);
    2254            0 :             TRead::read(s, e, ctx);
    2255            0 :             sym->add(s);
    2256           18 :         } else if (tag == "Image") {
    2257            0 :             if (MScore::noImages) {
    2258            0 :                 e.skipCurrentElement();
    2259              :             } else {
    2260            0 :                 Image* image = new Image(sym);
    2261            0 :                 TRead::read(image, e, ctx);
    2262            0 :                 sym->add(image);
    2263              :             }
    2264           18 :         } else if (tag == "small" || tag == "subtype") {    // obsolete
    2265            0 :             e.skipCurrentElement();
    2266           36 :         } else if (tag == "offset" && sym->score()->mscVersion() < 400 && sym->onTabStaff()
    2267           36 :                    && (symId == SymId::noteheadParenthesisLeft || symId == SymId::noteheadParenthesisRight)) {
    2268              :             // Reset mu3 TAB parentheses offset
    2269            0 :             e.skipCurrentElement();
    2270           18 :         } else if (!TRead::readProperties(static_cast<BSymbol*>(sym), e, ctx)) {
    2271            0 :             e.unknown();
    2272              :         }
    2273              :     }
    2274              : 
    2275           70 :     std::shared_ptr<IEngravingFont> scoreFont = nullptr;
    2276           70 :     if (!fontName.empty()) {
    2277           18 :         scoreFont = ctx.engravingFonts()->fontByName(fontName.toStdString());
    2278              :     }
    2279              : 
    2280           70 :     sym->setPos(PointF());
    2281           70 :     sym->setSym(symId, scoreFont);
    2282           70 : }
    2283              : 
    2284            0 : void TRead::read(FSymbol* sym, XmlReader& e, ReadContext& ctx)
    2285              : {
    2286            0 :     Font font = sym->font();
    2287            0 :     while (e.readNextStartElement()) {
    2288            0 :         const AsciiStringView tag(e.name());
    2289            0 :         if (tag == "font") {
    2290            0 :             font.setFamily(e.readText(), Font::Type::Unknown);
    2291            0 :         } else if (tag == "fontsize") {
    2292            0 :             font.setPointSizeF(e.readDouble());
    2293            0 :         } else if (tag == "code") {
    2294            0 :             sym->setCode(e.readInt());
    2295            0 :         } else if (!TRead::readProperties(static_cast<BSymbol*>(sym), e, ctx)) {
    2296            0 :             e.unknown();
    2297              :         }
    2298              :     }
    2299              : 
    2300            0 :     sym->setPos(PointF());
    2301            0 :     sym->setFont(font);
    2302            0 : }
    2303              : 
    2304         7323 : void TRead::read(Chord* ch, XmlReader& e, ReadContext& ctx)
    2305              : {
    2306        31696 :     while (e.readNextStartElement()) {
    2307        17050 :         if (TRead::readProperties(ch, e, ctx)) {
    2308              :         } else {
    2309            0 :             e.unknown();
    2310              :         }
    2311              :     }
    2312              : 
    2313              :     //! TODO Should be replaced with `ctx.mscVersion()`
    2314              :     //! But at the moment, `ctx` is not set everywhere
    2315         7323 :     int mscVersion = ch->score()->mscVersion();
    2316              : 
    2317              :     // Reset horizontal offset of grace notes when migrating from before 4.0
    2318         7323 :     if (ch->isGrace() && mscVersion < 400) {
    2319           72 :         ch->rxoffset() = 0;
    2320              :     }
    2321         7323 : }
    2322              : 
    2323        17050 : bool TRead::readProperties(Chord* ch, XmlReader& e, ReadContext& ctx)
    2324              : {
    2325        17050 :     const AsciiStringView tag(e.name());
    2326              : 
    2327        17050 :     if (tag == "Note") {
    2328         7906 :         Note* note = Factory::createNote(ch);
    2329              :         // the note needs to know the properties of the track it belongs to
    2330         7906 :         note->setTrack(ch->track());
    2331         7906 :         TRead::read(note, e, ctx);
    2332         7906 :         ch->add(note);
    2333         9144 :     } else if (TRead::readProperties(toChordRest(ch), e, ctx)) {
    2334          558 :     } else if (tag == "Stem") {
    2335            2 :         Stem* s = Factory::createStem(ch);
    2336            2 :         TRead::read(s, e, ctx);
    2337            2 :         ch->add(s);
    2338          556 :     } else if (tag == "Hook") {
    2339            0 :         Hook* hook = new Hook(ch);
    2340            0 :         TRead::read(hook, e, ctx);
    2341            0 :         ch->setHook(hook);
    2342            0 :         ch->add(hook);
    2343          556 :     } else if (tag == "appoggiatura") {
    2344           26 :         ch->setNoteType(NoteType::APPOGGIATURA);
    2345           26 :         e.readNext();
    2346          530 :     } else if (tag == "acciaccatura") {
    2347           41 :         ch->setNoteType(NoteType::ACCIACCATURA);
    2348           41 :         e.readNext();
    2349          489 :     } else if (tag == "grace4") {
    2350            1 :         ch->setNoteType(NoteType::GRACE4);
    2351            1 :         e.readNext();
    2352          488 :     } else if (tag == "grace16") {
    2353            0 :         ch->setNoteType(NoteType::GRACE16);
    2354            0 :         e.readNext();
    2355          488 :     } else if (tag == "grace32") {
    2356           19 :         ch->setNoteType(NoteType::GRACE32);
    2357           19 :         e.readNext();
    2358          469 :     } else if (tag == "grace8after") {
    2359           10 :         ch->setNoteType(NoteType::GRACE8_AFTER);
    2360           10 :         e.readNext();
    2361          459 :     } else if (tag == "grace16after") {
    2362            3 :         ch->setNoteType(NoteType::GRACE16_AFTER);
    2363            3 :         e.readNext();
    2364          456 :     } else if (tag == "grace32after") {
    2365            1 :         ch->setNoteType(NoteType::GRACE32_AFTER);
    2366            1 :         e.readNext();
    2367          455 :     } else if (tag == "StemSlash") {
    2368            0 :         StemSlash* ss = Factory::createStemSlash(ch);
    2369            0 :         TRead::read(ss, e, ctx);
    2370            0 :         ch->add(ss);
    2371          455 :     } else if (TRead::readProperty(ch, tag, e, ctx, Pid::STEM_DIRECTION)) {
    2372           89 :     } else if (tag == "noStem") {
    2373            0 :         ch->setNoStem(e.readInt());
    2374           89 :     } else if (tag == "Arpeggio") {
    2375           43 :         Arpeggio* arpeggio = Factory::createArpeggio(ch);
    2376           43 :         arpeggio->setTrack(ch->track());
    2377           43 :         TRead::read(arpeggio, e, ctx);
    2378           43 :         ch->setArpeggio(arpeggio);
    2379           46 :     } else if (tag == "Tremolo") {
    2380           46 :         TremoloCompat tcompat;
    2381           46 :         tcompat.parent = ch;
    2382           46 :         TRead::read(tcompat, e, ctx);
    2383           46 :         if (tcompat.two) {
    2384            9 :             tcompat.two->setOwnershipParent(ch);
    2385            9 :             tcompat.two->setDurationType(ch->durationType());
    2386            9 :             ch->setTremoloTwoChord(tcompat.two, false);
    2387           37 :         } else if (tcompat.single) {
    2388           37 :             tcompat.single->setOwnershipParent(ch);
    2389           37 :             tcompat.single->setDurationType(ch->durationType());
    2390           37 :             ch->setTremoloSingleChord(tcompat.single);
    2391              :         } else {
    2392            0 :             UNREACHABLE;
    2393              :         }
    2394            0 :     } else if (tag == "tickOffset") {      // obsolete
    2395            0 :     } else if (tag == "ChordLine") {
    2396            0 :         ChordLine* cl = Factory::createChordLine(ch);
    2397            0 :         TRead::read(cl, e, ctx);
    2398            0 :         ch->add(cl);
    2399              :     } else {
    2400            0 :         return false;
    2401              :     }
    2402        17050 :     return true;
    2403              : }
    2404              : 
    2405        16401 : bool TRead::readProperties(ChordRest* ch, XmlReader& e, ReadContext& ctx)
    2406              : {
    2407        16401 :     const AsciiStringView tag(e.name());
    2408              : 
    2409              :     //! TODO Should be replaced with `ctx.mscVersion()`
    2410              :     //! But at the moment, `ctx` is not set everywhere
    2411        16401 :     int mscVersion = ch->score()->mscVersion();
    2412              : 
    2413        16401 :     if (tag == "durationType") {
    2414        11667 :         ch->setDurationType(TConv::fromXml(e.readAsciiText(), DurationType::V_QUARTER));
    2415        11667 :         if (ch->actualDurationType().type() != DurationType::V_MEASURE) {
    2416            0 :             if (mscVersion < 112 && (ch->isRest())
    2417            0 :                 &&            // for backward compatibility, convert V_WHOLE rests to V_MEASURE
    2418              :                               // if long enough to fill a measure.
    2419              :                               // OTOH, freshly created (un-initialized) rests have numerator == 0 (< 4/4)
    2420              :                               // (see Fraction() constructor in fraction.h; this happens for instance
    2421              :                               // when pasting selection from clipboard): they should not be converted
    2422            0 :                 ch->ticks().numerator() != 0
    2423         8982 :                 &&            // rest durations are initialized to full measure duration when
    2424              :                               // created upon reading the <Rest> tag (see Measure::read() )
    2425              :                               // so a V_WHOLE rest in a measure of 4/4 or less => V_MEASURE
    2426         8982 :                 (ch->actualDurationType() == DurationType::V_WHOLE && ch->ticks() <= Fraction(4, 4))) {
    2427              :                 // old pre 2.0 scores: convert
    2428            0 :                 ch->setDurationType(DurationType::V_MEASURE);
    2429              :             } else {    // not from old score: set duration fraction from duration type
    2430         8982 :                 ch->setTicks(ch->actualDurationType().fraction());
    2431              :             }
    2432              :         } else {
    2433         2685 :             if (mscVersion <= 114) {
    2434            0 :                 SigEvent event = ctx.compatTimeSigMap()->timesig(ctx.tick());
    2435            0 :                 ch->setTicks(event.timesig());
    2436              :             }
    2437              :         }
    2438         4734 :     } else if (tag == "BeamMode") {
    2439              :         // 400 and previous used begin32/begin64 for beam mode
    2440              :         // 410 uses begin16/begin32
    2441           11 :         auto txt = e.readAsciiText();
    2442           11 :         if (txt == "begin64") {
    2443            0 :             txt = "begin32";
    2444           11 :         } else if (txt == "begin32") {
    2445            0 :             txt = "begin16";
    2446              :         }
    2447           11 :         ch->setBeamMode(TConv::fromXml(txt, BeamMode::AUTO));
    2448         4723 :     } else if (tag == "Articulation") {
    2449           95 :         Articulation* atr = Factory::createArticulation(ch);
    2450           95 :         atr->setTrack(ch->track());
    2451           95 :         TRead::read(atr, e, ctx);
    2452           95 :         ch->add(atr);
    2453         4628 :     } else if (tag == "Ornament") {
    2454           14 :         Ornament* ornament = Factory::createOrnament(ch);
    2455           14 :         ornament->setTrack(ch->track());
    2456           14 :         TRead::read(ornament, e, ctx);
    2457           14 :         ch->add(ornament);
    2458         4614 :     } else if (tag == "leadingSpace" || tag == "trailingSpace") {
    2459            0 :         LOGD("ChordRest: %s obsolete", tag.ascii());
    2460            0 :         e.skipCurrentElement();
    2461         4614 :     } else if (tag == "small") {
    2462           13 :         ch->setSmall(e.readInt());
    2463         4601 :     } else if (tag == "duration") {
    2464         2704 :         ch->setTicks(e.readFraction());
    2465         1897 :     } else if (tag == "ticklen") {      // obsolete (version < 1.12)
    2466            0 :         int mticks = ctx.compatTimeSigMap()->timesig(ctx.tick()).timesig().ticks();
    2467            0 :         int i = e.readInt();
    2468            0 :         if (i == 0) {
    2469            0 :             i = mticks;
    2470              :         }
    2471            0 :         if ((ch->isRest()) && (mticks == i)) {
    2472            0 :             ch->setDurationType(DurationType::V_MEASURE);
    2473            0 :             ch->setTicks(Fraction::fromTicks(i));
    2474              :         } else {
    2475            0 :             Fraction f = Fraction::fromTicks(i);
    2476            0 :             ch->setTicks(f);
    2477            0 :             ch->setDurationType(TDuration(f));
    2478              :         }
    2479         1897 :     } else if (tag == "dots") {
    2480          163 :         ch->setDots(e.readInt());
    2481         1734 :     } else if (tag == "staffMove") {
    2482          104 :         ch->setStaffMove(e.readInt());
    2483          104 :         if (ch->vStaffIdx() < ch->part()->staves().front()->idx() || ch->vStaffIdx() > ch->part()->staves().back()->idx()) {
    2484            0 :             ch->setStaffMove(0);
    2485              :         }
    2486         1630 :     } else if (tag == "Spanner") {
    2487          420 :         readSpanner(e, ctx, ch, ch->track());
    2488         1210 :     } else if (tag == "Lyrics") {
    2489          155 :         Lyrics* lyr = Factory::createLyrics(ch);
    2490          155 :         lyr->setTrack(ctx.track());
    2491          155 :         TRead::read(lyr, e, ctx);
    2492          155 :         ch->add(lyr);
    2493         1055 :     } else if (tag == "pos") {
    2494            0 :         PointF pt = e.readPoint();
    2495            0 :         ch->setOffset(pt * ch->spatium());
    2496              :     }
    2497              : //      else if (tag == "offset")
    2498              : //            DurationElement::readProperties(e);
    2499         1055 :     else if (!readItemProperties(ch, e, ctx)) {
    2500          558 :         return false;
    2501              :     }
    2502        15843 :     return true;
    2503              : }
    2504              : 
    2505            0 : void TRead::read(ChordLine* l, XmlReader& e, ReadContext& ctx)
    2506              : {
    2507            0 :     while (e.readNextStartElement()) {
    2508            0 :         const AsciiStringView tag(e.name());
    2509            0 :         if (tag == "Path") {
    2510            0 :             compat::CompatUtils::readChordLinePath(l, e);
    2511            0 :         } else if (tag == "subtype") {
    2512            0 :             l->setChordLineType(TConv::fromXml(e.readAsciiText(), ChordLineType::NOTYPE));
    2513            0 :         } else if (tag == "straight") {
    2514            0 :             l->setStraight(e.readInt());
    2515            0 :         } else if (tag == "wavy") {
    2516            0 :             l->setWavy(e.readInt());
    2517            0 :         } else if (tag == "lengthX" || tag == "lengthY") { // obsolete
    2518            0 :             e.skipCurrentElement();
    2519            0 :         } else if (tag == "offset" && l->score()->mscVersion() < 400) { // default positions has changed in 4.0 so ignore previous offset
    2520            0 :             e.skipCurrentElement();
    2521            0 :         } else if (!readItemProperties(l, e, ctx)) {
    2522            0 :             e.unknown();
    2523              :         }
    2524              :     }
    2525            0 : }
    2526              : 
    2527          303 : void TRead::read(Clef* c, XmlReader& e, ReadContext& ctx, bool* isHeader)
    2528              : {
    2529          918 :     while (e.readNextStartElement()) {
    2530          615 :         const AsciiStringView tag(e.name());
    2531          615 :         if (tag == "concertClefType") {
    2532          303 :             c->setConcertClef(TConv::fromXml(e.readAsciiText(), ClefType::G));
    2533          312 :         } else if (tag == "transposingClefType") {
    2534          303 :             c->setTransposingClef(TConv::fromXml(e.readAsciiText(), ClefType::G));
    2535            9 :         } else if (tag == "showCourtesyClef") {
    2536            0 :             c->setShowCourtesy(e.readInt());
    2537            9 :         } else if (tag == "forInstrumentChange") {
    2538            0 :             c->setForInstrumentChange(e.readBool());
    2539            9 :         } else if (tag == "clefToBarlinePos") {
    2540            0 :             c->setClefToBarlinePosition(ClefToBarlinePosition(e.readInt()));
    2541            9 :         } else if (tag == "isHeader") {
    2542            1 :             const bool header = e.readBool();
    2543            1 :             if (isHeader) {
    2544            1 :                 *isHeader = header;
    2545              :             }
    2546            8 :         } else if (!readItemProperties(c, e, ctx)) {
    2547            0 :             e.unknown();
    2548              :         }
    2549              :     }
    2550              : 
    2551          303 :     if (c->clefType() == ClefType::INVALID) {
    2552            0 :         c->setClefType(ClefType::G);
    2553              :     }
    2554          303 : }
    2555              : 
    2556            0 : bool TRead::readProperties(Clef* c, XmlReader& xml, ReadContext& ctx)
    2557              : {
    2558            0 :     return readItemProperties(c, xml, ctx);
    2559              : }
    2560              : 
    2561           98 : void TRead::read(Fingering* f, XmlReader& e, ReadContext& ctx)
    2562              : {
    2563          317 :     while (e.readNextStartElement()) {
    2564          121 :         if (!readProperties(toTextBase(f), e, ctx)) {
    2565            0 :             e.unknown();
    2566              :         }
    2567              :     }
    2568           98 :     compat::CompatUtils::migrateOffsetPre302(f, ctx.mscVersion());
    2569           98 : }
    2570              : 
    2571           69 : void TRead::read(Glissando* g, XmlReader& e, ReadContext& ctx)
    2572              : {
    2573           69 :     g->eraseSpannerSegments();
    2574              : 
    2575           69 :     if (g->score()->mscVersion() < 301) {
    2576            0 :         ctx.addSpanner(e.intAttribute("id", -1), g);
    2577              :     }
    2578              : 
    2579           69 :     staff_idx_t staffIdx = track2staff(ctx.track());
    2580           69 :     Staff* staff = ctx.score()->staff(staffIdx);
    2581           69 :     if (staff) {
    2582           69 :         String instrId = staff->part()->instrumentId(g->tick());
    2583           69 :         g->setIsHarpGliss(instrId == "harp");
    2584           69 :     }
    2585           69 :     g->resetProperty(Pid::GLISS_STYLE);
    2586           69 :     g->setShowText(false);
    2587          303 :     while (e.readNextStartElement()) {
    2588          234 :         const AsciiStringView tag = e.name();
    2589          234 :         if (tag == "text") {
    2590           68 :             g->setShowText(true);
    2591           68 :             TRead::readProperty(g, e, ctx, Pid::GLISS_TEXT);
    2592          166 :         } else if (tag == "subtype") {
    2593           25 :             g->setGlissandoType(TConv::fromXml(e.readAsciiText(), GlissandoType::STRAIGHT));
    2594          141 :         } else if (tag == "glissandoStyle") {
    2595            0 :             TRead::readProperty(g, e, ctx, Pid::GLISS_STYLE);
    2596          141 :         } else if (tag == "easeInSpin") {
    2597            0 :             g->setEaseIn(e.readInt());
    2598          141 :         } else if (tag == "easeOutSpin") {
    2599            0 :             g->setEaseOut(e.readInt());
    2600          141 :         } else if (TRead::readStyledProperty(g, tag, e, ctx)) {
    2601          141 :         } else if (!TRead::readProperties(static_cast<SLine*>(g), e, ctx)) {
    2602           69 :             e.unknown();
    2603              :         }
    2604              :     }
    2605           69 : }
    2606              : 
    2607            0 : void TRead::read(GradualTempoChange* c, XmlReader& xml, ReadContext& ctx)
    2608              : {
    2609            0 :     while (xml.readNextStartElement()) {
    2610            0 :         const AsciiStringView tag(xml.name());
    2611              : 
    2612            0 :         if (TRead::readProperty(c, tag, xml, ctx, Pid::LINE_WIDTH)) {
    2613            0 :             c->setPropertyFlags(Pid::LINE_WIDTH, PropertyFlags::UNSTYLED);
    2614            0 :             continue;
    2615              :         }
    2616              : 
    2617            0 :         if (TRead::readProperty(c, tag, xml, ctx, Pid::TEMPO_CHANGE_TYPE)) {
    2618            0 :             continue;
    2619              :         }
    2620              : 
    2621            0 :         if (TRead::readProperty(c, tag, xml, ctx, Pid::TEMPO_EASING_METHOD)) {
    2622            0 :             continue;
    2623              :         }
    2624              : 
    2625            0 :         if (TRead::readProperty(c, tag, xml, ctx, Pid::TEMPO_CHANGE_FACTOR)) {
    2626            0 :             continue;
    2627              :         }
    2628              : 
    2629            0 :         if (!readProperties(toTextLineBase(c), xml, ctx)) {
    2630            0 :             xml.unknown();
    2631              :         }
    2632              :     }
    2633            0 :     compat::CompatUtils::resetHookHeightSign(c);
    2634            0 :     compat::CompatUtils::setTextLineTextPositionFromAlign(c);
    2635            0 : }
    2636              : 
    2637            0 : void TRead::read(Groups* g, XmlReader& e, ReadContext&)
    2638              : {
    2639            0 :     while (e.readNextStartElement()) {
    2640            0 :         const AsciiStringView tag(e.name());
    2641            0 :         if (tag == "Node") {
    2642            0 :             GroupNode n;
    2643            0 :             n.pos    = e.intAttribute("pos");
    2644            0 :             n.action = e.intAttribute("action");
    2645            0 :             g->addNode(n);
    2646            0 :             e.skipCurrentElement();
    2647              :         } else {
    2648            0 :             e.unknown();
    2649              :         }
    2650              :     }
    2651            0 : }
    2652              : 
    2653          126 : void TRead::read(Hairpin* h, XmlReader& e, ReadContext& ctx)
    2654              : {
    2655          126 :     h->eraseSpannerSegments();
    2656              : 
    2657          335 :     while (e.readNextStartElement()) {
    2658          209 :         const AsciiStringView tag(e.name());
    2659          209 :         if (tag == "subtype") {
    2660          126 :             h->setHairpinType(HairpinType(e.readInt()));
    2661           83 :         } else if (TRead::readStyledProperty(h, tag, e, ctx)) {
    2662           33 :         } else if (tag == "hairpinCircledTip") {
    2663            0 :             h->setHairpinCircledTip(e.readInt());
    2664           33 :         } else if (tag == "veloChange") {
    2665           16 :             h->setVeloChange(e.readInt());
    2666           17 :         } else if (tag == "dynType") {
    2667            0 :             h->setVoiceAssignment(read206::Read206::readDynamicRange(e.readInt()));
    2668           17 :         } else if (tag == "useTextLine") {        // obsolete
    2669            0 :             e.readInt();
    2670            0 :             if (h->hairpinType() == HairpinType::CRESC_HAIRPIN) {
    2671            0 :                 h->setHairpinType(HairpinType::CRESC_LINE);
    2672            0 :             } else if (h->hairpinType() == HairpinType::DIM_HAIRPIN) {
    2673            0 :                 h->setHairpinType(HairpinType::DIM_LINE);
    2674              :             }
    2675           17 :         } else if (tag == "singleNoteDynamics") {
    2676            0 :             h->setSingleNoteDynamics(e.readBool());
    2677           17 :         } else if (tag == "veloChangeMethod") {
    2678            0 :             h->setVeloChangeMethod(TConv::fromXml(e.readAsciiText(), ChangeMethod::NORMAL));
    2679           17 :         } else if (!readProperties(toTextLineBase(h), e, ctx)) {
    2680            0 :             e.unknown();
    2681              :         }
    2682              :     }
    2683          126 :     compat::CompatUtils::resetHookHeightSign(h);
    2684          126 :     compat::CompatUtils::setTextLineTextPositionFromAlign(h);
    2685              : 
    2686          126 :     h->styleChanged();
    2687          126 : }
    2688              : 
    2689          591 : void TRead::read(Harmony* h, XmlReader& e, ReadContext& ctx)
    2690              : {
    2691          591 :     HarmonyInfo* info = new HarmonyInfo(ctx.score());
    2692         1696 :     while (e.readNextStartElement()) {
    2693         1105 :         const AsciiStringView tag(e.name());
    2694         1105 :         if (tag == "base") {
    2695            1 :             info->setBassTpc(e.readInt());
    2696         1104 :         } else if (tag == "baseCase") {
    2697            0 :             h->setBassCase(static_cast<NoteCaseType>(e.readInt()));
    2698         1104 :         } else if (tag == "extension") {
    2699            0 :             info->setId(e.readInt());
    2700         1104 :         } else if (tag == "name") {
    2701          445 :             info->setTextName(e.readText());
    2702          659 :         } else if (tag == "root") {
    2703          583 :             info->setRootTpc(e.readInt());
    2704           76 :         } else if (tag == "rootCase") {
    2705            0 :             h->setRootCase(static_cast<NoteCaseType>(e.readInt()));
    2706           76 :         } else if (tag == "function") {
    2707            1 :             compat::CompatUtils::setHarmonyRootTpcFromFunction(info, h, e.readText());
    2708           75 :         } else if (tag == "degree") {
    2709            0 :             int degreeValue = 0;
    2710            0 :             int degreeAlter = 0;
    2711            0 :             String degreeType;
    2712            0 :             while (e.readNextStartElement()) {
    2713            0 :                 const AsciiStringView t(e.name());
    2714            0 :                 if (t == "degree-value") {
    2715            0 :                     degreeValue = e.readInt();
    2716            0 :                 } else if (t == "degree-alter") {
    2717            0 :                     degreeAlter = e.readInt();
    2718            0 :                 } else if (t == "degree-type") {
    2719            0 :                     degreeType = e.readText();
    2720              :                 } else {
    2721            0 :                     e.unknown();
    2722              :                 }
    2723              :             }
    2724            0 :             if (degreeValue <= 0 || degreeValue > 13
    2725            0 :                 || degreeAlter < -2 || degreeAlter > 2
    2726            0 :                 || (degreeType != "add" && degreeType != "alter" && degreeType != "subtract")) {
    2727            0 :                 LOGD("incorrect degree: degreeValue=%d degreeAlter=%d degreeType=%s",
    2728            0 :                      degreeValue, degreeAlter, muPrintable(degreeType));
    2729              :             } else {
    2730            0 :                 if (degreeType == "add") {
    2731            0 :                     h->addDegree(HDegree(degreeValue, degreeAlter, HDegreeType::ADD));
    2732            0 :                 } else if (degreeType == "alter") {
    2733            0 :                     h->addDegree(HDegree(degreeValue, degreeAlter, HDegreeType::ALTER));
    2734            0 :                 } else if (degreeType == "subtract") {
    2735            0 :                     h->addDegree(HDegree(degreeValue, degreeAlter, HDegreeType::SUBTRACT));
    2736              :                 }
    2737              :             }
    2738           75 :         } else if (tag == "leftParen") {
    2739            0 :             h->setParenthesesMode(h->rightParen() ? ParenthesesMode::BOTH : ParenthesesMode::LEFT, true, false);
    2740            0 :             e.readNext();
    2741           75 :         } else if (tag == "rightParen") {
    2742            0 :             h->setParenthesesMode(h->leftParen() ? ParenthesesMode::BOTH : ParenthesesMode::RIGHT, true, false);
    2743            0 :             e.readNext();
    2744           75 :         } else if (TRead::readProperty(h, tag, e, ctx, Pid::POS_ABOVE)) {
    2745           75 :         } else if (TRead::readProperty(h, tag, e, ctx, Pid::HARMONY_TYPE)) {
    2746           69 :         } else if (TRead::readProperty(h, tag, e, ctx, Pid::PLAY)) {
    2747           69 :         } else if (TRead::readProperty(h, tag, e, ctx, Pid::HARMONY_VOICE_LITERAL)) {
    2748           44 :         } else if (TRead::readProperty(h, tag, e, ctx, Pid::HARMONY_VOICING)) {
    2749           19 :         } else if (TRead::readProperty(h, tag, e, ctx, Pid::HARMONY_DURATION)) {
    2750           13 :         } else if (!readProperties(toTextBase(h), e, ctx)) {
    2751            2 :             e.unknown();
    2752              :         }
    2753              :     }
    2754              : 
    2755          591 :     compat::CompatUtils::migrateOffsetPre302(h, ctx.mscVersion());
    2756              : 
    2757          591 :     h->addChord(info);
    2758              : 
    2759          591 :     h->afterRead();
    2760          591 : }
    2761              : 
    2762            0 : void TRead::read(HarmonicMark* h, XmlReader& xml, ReadContext& ctx)
    2763              : {
    2764            0 :     TRead::read(toTextLineBase(h), xml, ctx);
    2765            0 : }
    2766              : 
    2767            0 : void TRead::read(HarpPedalDiagram* h, XmlReader& xml, ReadContext& ctx)
    2768              : {
    2769            0 :     while (xml.readNextStartElement()) {
    2770            0 :         const AsciiStringView tag = xml.name();
    2771            0 :         if (tag == "isDiagram") {
    2772            0 :             h->setIsDiagram(xml.readBool());
    2773            0 :         } else if (tag == "pedalState") {
    2774            0 :             while (xml.readNextStartElement()) {
    2775            0 :                 const AsciiStringView stringTag = xml.name();
    2776            0 :                 if (stringTag == "string") {
    2777            0 :                     HarpStringType str = HarpStringType(xml.intAttribute("name"));
    2778            0 :                     PedalPosition pos = PedalPosition(xml.readInt());
    2779            0 :                     h->setPedal(str, pos);
    2780              :                 } else {
    2781            0 :                     xml.unknown();
    2782              :                 }
    2783              :             }
    2784            0 :             h->setPlayableTpcs();
    2785            0 :         } else if (!readProperties(h, xml, ctx)) {
    2786            0 :             xml.unknown();
    2787              :         }
    2788              :     }
    2789            0 : }
    2790              : 
    2791            0 : void TRead::read(Hook* h, XmlReader& xml, ReadContext& ctx)
    2792              : {
    2793            0 :     TRead::read(static_cast<Symbol*>(h), xml, ctx);
    2794            0 : }
    2795              : 
    2796          555 : void TRead::read(LayoutBreak* b, XmlReader& e, ReadContext& ctx)
    2797              : {
    2798         1116 :     while (e.readNextStartElement()) {
    2799          561 :         const AsciiStringView tag(e.name());
    2800          561 :         if (tag == "subtype") {
    2801          555 :             TRead::readProperty(b, e, ctx, Pid::LAYOUT_BREAK);
    2802            6 :         } else if (tag == "pause") {
    2803            6 :             TRead::readProperty(b, e, ctx, Pid::PAUSE);
    2804            0 :         } else if (tag == "startWithLongNames") {
    2805            0 :             TRead::readProperty(b, e, ctx, Pid::START_WITH_LONG_NAMES);
    2806            0 :         } else if (tag == "startWithMeasureOne") {
    2807            0 :             TRead::readProperty(b, e, ctx, Pid::START_WITH_MEASURE_ONE);
    2808            0 :         } else if (tag == "firstSystemIndentation") {
    2809            0 :             TRead::readProperty(b, e, ctx, Pid::FIRST_SYSTEM_INDENTATION);
    2810            0 :         } else if (!readItemProperties(b, e, ctx)) {
    2811            0 :             e.unknown();
    2812              :         }
    2813              :     }
    2814          555 : }
    2815              : 
    2816            0 : void TRead::read(LedgerLine* l, XmlReader& e, ReadContext& ctx)
    2817              : {
    2818            0 :     while (e.readNextStartElement()) {
    2819            0 :         if (!readProperties(l, e, ctx)) {
    2820            0 :             e.unknown();
    2821              :         }
    2822              :     }
    2823            0 : }
    2824              : 
    2825            0 : bool TRead::readProperties(LedgerLine* l, XmlReader& e, ReadContext&)
    2826              : {
    2827            0 :     const AsciiStringView tag(e.name());
    2828              : 
    2829            0 :     if (tag == "lineWidth") {
    2830              :         //! NOTE Probably need to be removed, because it is calculated in Layout
    2831            0 :         LedgerLine::LayoutData* ldata = l->mutldata();
    2832            0 :         ldata->lineWidth = (e.readDouble() * l->spatium());
    2833            0 :     } else if (tag == "lineLen") {
    2834            0 :         l->setLen(e.readDouble() * l->spatium());
    2835            0 :     } else if (tag == "vertical") {
    2836            0 :         l->setVertical(e.readInt());
    2837              :     } else {
    2838            0 :         return false;
    2839              :     }
    2840            0 :     return true;
    2841              : }
    2842              : 
    2843            0 : void TRead::read(LetRing* r, XmlReader& e, ReadContext& ctx)
    2844              : {
    2845            0 :     if (r->score()->mscVersion() < 301) {
    2846            0 :         ctx.addSpanner(e.intAttribute("id", -1), r);
    2847              :     }
    2848            0 :     while (e.readNextStartElement()) {
    2849            0 :         if (TRead::readProperty(r, e.name(), e, ctx, Pid::LINE_WIDTH)) {
    2850            0 :             r->setPropertyFlags(Pid::LINE_WIDTH, PropertyFlags::UNSTYLED);
    2851            0 :         } else if (!readProperties(toTextLineBase(r), e, ctx)) {
    2852            0 :             e.unknown();
    2853              :         }
    2854              :     }
    2855            0 :     compat::CompatUtils::resetHookHeightSign(r);
    2856            0 :     compat::CompatUtils::setTextLineTextPositionFromAlign(r);
    2857            0 : }
    2858              : 
    2859         3146 : void TRead::read(Location* l, XmlReader& e, ReadContext&)
    2860              : {
    2861         6592 :     while (e.readNextStartElement()) {
    2862         3446 :         const AsciiStringView tag(e.name());
    2863              : 
    2864         3446 :         if (tag == "staves") {
    2865           15 :             l->setStaff(e.readInt());
    2866         3431 :         } else if (tag == "voices") {
    2867           20 :             l->setVoice(e.readInt());
    2868         3411 :         } else if (tag == "measures") {
    2869          898 :             l->setMeasure(e.readInt());
    2870         2513 :         } else if (tag == "fractions") {
    2871         2505 :             l->setFrac(e.readFraction());
    2872            8 :         } else if (tag == "grace") {
    2873            2 :             l->setGraceIndex(e.readInt());
    2874            6 :         } else if (tag == "notes") {
    2875            6 :             l->setNote(e.readInt());
    2876              :         } else {
    2877            0 :             e.unknown();
    2878              :         }
    2879              :     }
    2880         3146 : }
    2881              : 
    2882          155 : void TRead::read(Lyrics* l, XmlReader& e, ReadContext& ctx)
    2883              : {
    2884          655 :     while (e.readNextStartElement()) {
    2885          345 :         if (!TRead::readProperties(l, e, ctx)) {
    2886            0 :             e.unknown();
    2887              :         }
    2888              :     }
    2889          155 :     if (!l->isStyled(Pid::OFFSET) && !ctx.pasteMode()) {
    2890              :         // fix offset for pre-3.1 scores
    2891              :         // 3.0: y offset was meaningless if autoplace is set
    2892          155 :         String version = ctx.mscoreVersion();
    2893          155 :         if (l->autoplace() && !version.isEmpty() && version < u"3.1") {
    2894            0 :             PointF off = l->propertyDefault(Pid::OFFSET).value<PointF>();
    2895            0 :             l->ryoffset() = off.y();
    2896              :         }
    2897          155 :     }
    2898              : 
    2899          155 :     compat::CompatUtils::migrateOffsetPre302(l, ctx.mscVersion());
    2900          155 : }
    2901              : 
    2902           23 : void TRead::read(LineSegment* l, XmlReader& e, ReadContext& ctx)
    2903              : {
    2904          122 :     while (e.readNextStartElement()) {
    2905           76 :         if (!readProperties(l, e, ctx)) {
    2906            0 :             e.unknown();
    2907              :         }
    2908              :     }
    2909              : 
    2910           23 :     compat::CompatUtils::migrateOffsetPre302(l, ctx.mscVersion());
    2911           23 : }
    2912              : 
    2913           87 : bool TRead::readProperties(LineSegment* l, XmlReader& e, ReadContext& ctx)
    2914              : {
    2915           87 :     const AsciiStringView tag(e.name());
    2916           87 :     if (tag == "subtype") {
    2917           23 :         l->setSpannerSegmentType(SpannerSegmentType(e.readInt()));
    2918           64 :     } else if (tag == "off2") {
    2919           26 :         l->setUserOff2(e.readPoint() * l->style().spatium());
    2920           38 :     } else if (!readItemProperties(l, e, ctx)) {
    2921            0 :         return false;
    2922              :     }
    2923           87 :     return true;
    2924              : }
    2925              : 
    2926          346 : bool TRead::readProperties(Lyrics* l, XmlReader& e, ReadContext& ctx)
    2927              : {
    2928          346 :     const AsciiStringView tag(e.name());
    2929              : 
    2930          346 :     if (tag == "no") {
    2931            1 :         l->setVerse(e.readInt());
    2932            1 :         if (l->isEven()) {
    2933            1 :             l->initTextStyleType(TextStyleType::LYRICS_EVEN);
    2934              :         }
    2935          345 :     } else if (tag == "syllabic") {
    2936          119 :         l->setSyllabic(TConv::fromXml(e.readAsciiText(), LyricsSyllabic::SINGLE));
    2937          226 :     } else if (tag == "ticks") {          // obsolete
    2938           19 :         l->setTicks(e.readFraction());     // will fall back to reading integer ticks on older scores
    2939          207 :     } else if (tag == "ticks_f") {
    2940            0 :         l->setTicks(e.readFraction());
    2941          207 :     } else if (TRead::readProperty(l, tag, e, ctx, Pid::PLACEMENT)) {
    2942          207 :     } else if (!readProperties(toTextBase(l), e, ctx)) {
    2943            0 :         return false;
    2944              :     }
    2945          346 :     return true;
    2946              : }
    2947              : 
    2948           41 : void TRead::read(MeasureRepeat* r, XmlReader& e, ReadContext& ctx)
    2949              : {
    2950          144 :     while (e.readNextStartElement()) {
    2951          103 :         const AsciiStringView tag(e.name());
    2952          103 :         if (tag == "subtype") {
    2953           14 :             r->setNumMeasures(e.readInt());
    2954           89 :         } else if (!readProperties(toRest(r), e, ctx)) {
    2955            0 :             e.unknown();
    2956              :         }
    2957              :     }
    2958           41 : }
    2959              : 
    2960           34 : void TRead::read(MMRest* r, XmlReader& e, ReadContext& ctx)
    2961              : {
    2962          102 :     while (e.readNextStartElement()) {
    2963           68 :         const AsciiStringView tag(e.name());
    2964           68 :         if (tag == "Symbol") {
    2965            0 :             Symbol* s = new Symbol(r);
    2966            0 :             s->setTrack(r->track());
    2967            0 :             TRead::read(s, e, ctx);
    2968            0 :             r->add(s);
    2969           68 :         } else if (tag == "Image") {
    2970            0 :             if (MScore::noImages) {
    2971            0 :                 e.skipCurrentElement();
    2972              :             } else {
    2973            0 :                 Image* image = new Image(r);
    2974            0 :                 image->setTrack(r->track());
    2975            0 :                 TRead::read(image, e, ctx);
    2976            0 :                 r->add(image);
    2977              :             }
    2978           68 :         } else if (tag == "NoteDot") {
    2979            0 :             NoteDot* dot = Factory::createNoteDot(r);
    2980            0 :             TRead::read(dot, e, ctx);
    2981            0 :             r->add(dot);
    2982           68 :         } else if (tag == "mmRestNumberPos") {
    2983              :             // Old property, deprecated in 4.5
    2984            0 :             r->setNumberOffset(Spatium(e.readDouble()) - ctx.style().styleS(Sid::mmRestNumberPos));
    2985           68 :         } else if (TRead::readStyledProperty(r, tag, e, ctx)) {
    2986           68 :         } else if (readProperties(r, e, ctx)) {
    2987              :         } else {
    2988            0 :             e.unknown();
    2989              :         }
    2990              :     }
    2991           34 : }
    2992              : 
    2993           68 : bool TRead::readProperties(MMRest* r, XmlReader& xml, ReadContext& ctx)
    2994              : {
    2995           68 :     const AsciiStringView tag(xml.name());
    2996           68 :     if (tag == "mmRestNumberVisible") {
    2997            0 :         r->setProperty(Pid::MMREST_NUMBER_VISIBLE, xml.readBool());
    2998           68 :     } else if (TRead::readProperties(toRest(r), xml, ctx)) {
    2999              :     } else {
    3000            0 :         return false;
    3001              :     }
    3002           68 :     return true;
    3003              : }
    3004              : 
    3005         7906 : void TRead::read(Note* n, XmlReader& e, ReadContext& ctx)
    3006              : {
    3007         7906 :     n->setTpc1(Tpc::TPC_INVALID);
    3008         7906 :     n->setTpc2(Tpc::TPC_INVALID);
    3009              : 
    3010        34799 :     while (e.readNextStartElement()) {
    3011        18987 :         if (TRead::readProperties(n, e, ctx)) {
    3012              :         } else {
    3013            0 :             e.unknown();
    3014              :         }
    3015              :     }
    3016              : 
    3017         7906 :     n->setupAfterRead(ctx.tick(), ctx.pasteMode());
    3018         7906 : }
    3019              : 
    3020        18987 : bool TRead::readProperties(Note* n, XmlReader& e, ReadContext& ctx)
    3021              : {
    3022        18987 :     const AsciiStringView tag(e.name());
    3023              : 
    3024        18987 :     if (tag == "pitch") {
    3025         7906 :         n->setPitch(clampPitch(e.readInt()), false);
    3026        11081 :     } else if (tag == "tpc") {
    3027         7906 :         int tcp = e.readInt();
    3028         7906 :         n->setTpc1(tcp);
    3029         7906 :         n->setTpc2(tcp);
    3030         3175 :     } else if (tag == "track") {          // for performance
    3031            0 :         n->setTrack(e.readInt());
    3032         3175 :     } else if (tag == "Accidental") {
    3033          319 :         Accidental* a = Factory::createAccidental(n);
    3034          319 :         a->setTrack(n->track());
    3035          319 :         TRead::read(a, e, ctx);
    3036          319 :         n->add(a);
    3037         2856 :     } else if (tag == "Spanner") {
    3038         1980 :         readSpanner(e, ctx, n, n->track());
    3039          876 :     } else if (tag == "tpc2") {
    3040          190 :         n->setTpc2(e.readInt());
    3041          686 :     } else if (tag == "small") {
    3042           14 :         n->setSmall(e.readInt());
    3043          672 :     } else if (tag == "mirror") {
    3044            0 :         TRead::readProperty(n, e, ctx, Pid::MIRROR_HEAD);
    3045          672 :     } else if (tag == "dotPosition") {
    3046            0 :         TRead::readProperty(n, e, ctx, Pid::DOT_POSITION);
    3047          672 :     } else if (tag == "fixed") {
    3048            0 :         n->setFixed(e.readBool());
    3049          672 :     } else if (tag == "fixedLine") {
    3050            0 :         n->setFixedLine(e.readInt());
    3051          672 :     } else if (tag == "headScheme") {
    3052            3 :         TRead::readProperty(n, e, ctx, Pid::HEAD_SCHEME);
    3053          669 :     } else if (tag == "head") {
    3054           16 :         TRead::readProperty(n, e, ctx, Pid::HEAD_GROUP);
    3055          653 :     } else if (tag == "velocity") {
    3056            3 :         if (n->score()->mscVersion() >= 400) {
    3057            0 :             n->setUserVelocity(e.readInt());
    3058              :         } else {
    3059              :             // TODO: convert pre-MU4 velocity values to MU4
    3060              :             // but doing so is non-trivial,
    3061              :             // since MU3 "offset" is based on a percentage of the current dynamic (which is not known at this time)
    3062              :             // and MU4 "user" is not raw MIDI velocity like MU3 is
    3063              :             // so until a new velocity system is designed for MU4 (or another solution is found),
    3064              :             // the best we can do is ignore pre-MU4 velocity values
    3065            3 :             e.skipCurrentElement();
    3066              :         }
    3067          650 :     } else if (tag == "play") {
    3068            6 :         n->setPlay(e.readInt());
    3069          644 :     } else if (tag == "tuning") {
    3070            3 :         n->setTuning(e.readDouble());
    3071          641 :     } else if (tag == "fret") {
    3072           59 :         n->setFret(e.readInt());
    3073          582 :     } else if (tag == "string") {
    3074           59 :         n->setString(e.readInt());
    3075          523 :     } else if (tag == "ghost") {
    3076            0 :         if (n->score()->mscVersion() < 400) {
    3077            0 :             n->setDeadNote(e.readInt());
    3078              :         } else {
    3079            0 :             n->setGhost(e.readInt());
    3080              :         }
    3081          523 :     } else if (tag == "dead") {
    3082            0 :         n->setDeadNote(e.readInt());
    3083          523 :     } else if (tag == "headType") {
    3084            7 :         TRead::readProperty(n, e, ctx, Pid::HEAD_TYPE);
    3085          516 :     } else if (tag == "veloType") {
    3086            3 :         TRead::readProperty(n, e, ctx, Pid::VELO_TYPE);
    3087          513 :     } else if (tag == "line") {
    3088            0 :         n->setLine(e.readInt());
    3089          513 :     } else if (tag == "Fingering") {
    3090           98 :         Fingering* f = Factory::createFingering(n);
    3091           98 :         f->setTrack(n->track());
    3092           98 :         TRead::read(f, e, ctx);
    3093           98 :         n->add(f);
    3094          415 :     } else if (tag == "Symbol") {
    3095           52 :         Symbol* s = new Symbol(n);
    3096           52 :         s->setTrack(n->track());
    3097           52 :         TRead::read(s, e, ctx);
    3098           52 :         if (s->sym() == SymId::noteheadParenthesisLeft) {
    3099           26 :             n->setParenthesesMode(ParenthesesMode::BOTH);
    3100           26 :             s->deleteLater();
    3101           26 :         } else if (s->sym() == SymId::noteheadParenthesisRight) {
    3102           26 :             n->setParenthesesMode(ParenthesesMode::BOTH);
    3103           26 :             s->deleteLater();
    3104              :         } else {
    3105            0 :             n->add(s);
    3106              :         }
    3107          363 :     } else if (tag == "Image") {
    3108            0 :         if (MScore::noImages) {
    3109            0 :             e.skipCurrentElement();
    3110              :         } else {
    3111            0 :             Image* image = new Image(n);
    3112            0 :             image->setTrack(n->track());
    3113            0 :             TRead::read(image, e, ctx);
    3114            0 :             n->add(image);
    3115              :         }
    3116          363 :     } else if (tag == "Bend") {
    3117            0 :         Bend* b = Factory::createBend(n);
    3118            0 :         b->setTrack(n->track());
    3119            0 :         TRead::read(b, e, ctx);
    3120            0 :         n->add(b);
    3121          363 :     } else if (tag == "NoteDot") {
    3122            0 :         NoteDot* dot = Factory::createNoteDot(n);
    3123            0 :         TRead::read(dot, e, ctx);
    3124            0 :         n->add(dot);
    3125          363 :     } else if (tag == "Events") {
    3126            0 :         NoteEventList playEvents;
    3127            0 :         while (e.readNextStartElement()) {
    3128            0 :             const AsciiStringView t(e.name());
    3129            0 :             if (t == "Event") {
    3130            0 :                 NoteEvent ne;
    3131            0 :                 read400::TRead::read(&ne, e, ctx);
    3132            0 :                 playEvents.push_back(ne);
    3133              :             } else {
    3134            0 :                 e.unknown();
    3135              :             }
    3136              :         }
    3137            0 :         n->setPlayEvents(playEvents);
    3138            0 :         if (n->chord()) {
    3139            0 :             n->chord()->setPlayEventType(PlayEventType::User);
    3140              :         }
    3141          363 :     } else if (tag == "offset") {
    3142            0 :         readItemProperties(n, e, ctx);
    3143          363 :     } else if (tag == "ChordLine" && n->chord()) {
    3144            0 :         ChordLine* cl = Factory::createChordLine(n->chord());
    3145            0 :         TRead::read(cl, e, ctx);
    3146            0 :         cl->setNote(n);
    3147            0 :         n->chord()->add(cl);
    3148          363 :     } else if (readItemProperties(n, e, ctx)) {
    3149              :     } else {
    3150            0 :         return false;
    3151              :     }
    3152        18987 :     return true;
    3153              : }
    3154              : 
    3155            0 : void TRead::read(NoteEvent* item, XmlReader& e, ReadContext&)
    3156              : {
    3157            0 :     while (e.readNextStartElement()) {
    3158            0 :         const AsciiStringView tag(e.name());
    3159            0 :         if (tag == "pitch") {
    3160            0 :             item->setPitch(e.readInt());
    3161            0 :         } else if (tag == "ontime") {
    3162            0 :             item->setOntime(e.readInt());
    3163            0 :         } else if (tag == "len") {
    3164            0 :             item->setLen(e.readInt());
    3165              :         } else {
    3166            0 :             e.unknown();
    3167              :         }
    3168              :     }
    3169            0 : }
    3170              : 
    3171            3 : void TRead::read(NoteDot* d, XmlReader& e, ReadContext& ctx)
    3172              : {
    3173            6 :     while (e.readNextStartElement()) {
    3174            3 :         if (e.name() == "name") {      // obsolete
    3175            0 :             e.readText();
    3176            3 :         } else if (e.name() == "subtype") {     // obsolete
    3177            0 :             e.readText();
    3178            3 :         } else if (!readItemProperties(d, e, ctx)) {
    3179            0 :             e.unknown();
    3180              :         }
    3181              :     }
    3182            3 : }
    3183              : 
    3184            0 : void TRead::read(NoteHead* h, XmlReader& xml, ReadContext& ctx)
    3185              : {
    3186            0 :     read(static_cast<Symbol*>(h), xml, ctx);
    3187            0 : }
    3188              : 
    3189           34 : void TRead::read(Ottava* o, XmlReader& e, ReadContext& ctx)
    3190              : {
    3191           34 :     o->eraseSpannerSegments();
    3192           34 :     if (o->score()->mscVersion() < 301) {
    3193            0 :         ctx.addSpanner(e.intAttribute("id", -1), o);
    3194              :     }
    3195           78 :     while (e.readNextStartElement()) {
    3196           44 :         readProperties(o, e, ctx);
    3197              :     }
    3198           34 :     compat::CompatUtils::resetHookHeightSign(o);
    3199           34 :     compat::CompatUtils::setTextLineTextPositionFromAlign(o);
    3200           34 :     if (o->ottavaType() != OttavaType::OTTAVA_8VA || o->numbersOnly() != o->propertyDefault(Pid::NUMBERS_ONLY).toBool()) {
    3201            0 :         o->styleChanged();
    3202              :     }
    3203           34 : }
    3204              : 
    3205           44 : bool TRead::readProperties(Ottava* o, XmlReader& e, ReadContext& ctx)
    3206              : {
    3207           44 :     const AsciiStringView tag(e.name());
    3208           44 :     if (tag == "subtype") {
    3209           34 :         String s = e.readText();
    3210              :         bool ok;
    3211           34 :         int idx = s.toInt(&ok);
    3212           34 :         if (!ok) {
    3213           34 :             o->setOttavaType(OttavaType::OTTAVA_8VA);
    3214           34 :             for (OttavaDefault d : ottavaDefault) {
    3215           34 :                 if (s == d.name) {
    3216           34 :                     o->setOttavaType(d.type);
    3217           34 :                     break;
    3218              :                 }
    3219           34 :             }
    3220            0 :         } else if (o->score()->mscVersion() <= 114) {
    3221              :             //subtype are now in a different order...
    3222            0 :             if (idx == 1) {
    3223            0 :                 idx = 2;
    3224            0 :             } else if (idx == 2) {
    3225            0 :                 idx = 1;
    3226              :             }
    3227            0 :             o->setOttavaType(OttavaType(idx));
    3228              :         } else {
    3229            0 :             o->setOttavaType(OttavaType(idx));
    3230              :         }
    3231           44 :     } else if (readStyledProperty(o, tag, e, ctx)) {
    3232            0 :         return true;
    3233           10 :     } else if (!readProperties(toTextLineBase(o), e, ctx)) {
    3234            0 :         e.unknown();
    3235            0 :         return false;
    3236              :     }
    3237           44 :     return true;
    3238              : }
    3239              : 
    3240            0 : void TRead::read(Page* p, XmlReader& e, ReadContext& ctx)
    3241              : {
    3242            0 :     while (e.readNextStartElement()) {
    3243            0 :         if (e.name() == "System") {
    3244            0 :             System* system = Factory::createSystem(p->score());
    3245            0 :             p->score()->systems().push_back(system);
    3246            0 :             read(system, e, ctx);
    3247              :         } else {
    3248            0 :             e.unknown();
    3249              :         }
    3250              :     }
    3251            0 : }
    3252              : 
    3253            0 : void TRead::read(PalmMute* p, XmlReader& e, ReadContext& ctx)
    3254              : {
    3255            0 :     if (p->score()->mscVersion() < 301) {
    3256            0 :         ctx.addSpanner(e.intAttribute("id", -1), p);
    3257              :     }
    3258            0 :     while (e.readNextStartElement()) {
    3259            0 :         if (readProperty(p, e.name(), e, ctx, Pid::LINE_WIDTH)) {
    3260            0 :             p->setPropertyFlags(Pid::LINE_WIDTH, PropertyFlags::UNSTYLED);
    3261            0 :         } else if (!readProperties(toTextLineBase(p), e, ctx)) {
    3262            0 :             e.unknown();
    3263              :         }
    3264              :     }
    3265            0 :     compat::CompatUtils::resetHookHeightSign(p);
    3266            0 :     compat::CompatUtils::setTextLineTextPositionFromAlign(p);
    3267            0 : }
    3268              : 
    3269          760 : void TRead::read(Part* p, XmlReader& e, ReadContext& ctx)
    3270              : {
    3271          760 :     p->setId(e.intAttribute("id", 0));
    3272              : 
    3273          760 :     StaffHideModes staffHideModes;
    3274              : 
    3275         3919 :     while (e.readNextStartElement()) {
    3276         2399 :         if (!readProperties(p, e, ctx, staffHideModes)) {
    3277          760 :             e.unknown();
    3278              :         }
    3279              :     }
    3280              : 
    3281          760 :     read(p, staffHideModes, ctx.style().styleB(Sid::hideEmptyStaves));
    3282          760 : }
    3283              : 
    3284          997 : void TRead::read(Part* p, StaffHideModes& staffHideModes, const bool globalHideEmptyStaves)
    3285              : {
    3286          997 :     IF_ASSERT_FAILED(p->nstaves() > 0) {
    3287            0 :         return;
    3288              :     }
    3289              : 
    3290          997 :     if (p->nstaves() == 1) {
    3291          830 :         switch (staffHideModes[p->staves().front()]) { // inserts AUTO if not found
    3292          827 :         case StaffHideMode::AUTO:
    3293          827 :             break;
    3294            1 :         case StaffHideMode::ALWAYS:
    3295            1 :             p->setHideWhenEmpty(AutoOnOff::ON);
    3296            1 :             break;
    3297            1 :         case StaffHideMode::NEVER:
    3298            1 :             p->setHideWhenEmpty(AutoOnOff::OFF);
    3299            1 :             break;
    3300            1 :         case StaffHideMode::INSTRUMENT:
    3301            1 :             break;
    3302              :         }
    3303          830 :         return;
    3304              :     }
    3305              : 
    3306          167 :     bool hasAuto = false;
    3307          167 :     bool allAlways = true;
    3308          167 :     bool allNever = true;
    3309          167 :     bool hasInstrument = false;
    3310              : 
    3311          515 :     for (Staff* staff : p->staves()) {
    3312          348 :         StaffHideMode mode = staffHideModes[staff]; // inserts AUTO if not found
    3313              : 
    3314          348 :         hasAuto |= (mode == StaffHideMode::AUTO);
    3315          348 :         allAlways &= (mode == StaffHideMode::ALWAYS);
    3316          348 :         allNever &= (mode == StaffHideMode::NEVER);
    3317          348 :         hasInstrument |= (mode == StaffHideMode::INSTRUMENT);
    3318              :     }
    3319              : 
    3320          167 :     if (allAlways) {
    3321            1 :         p->setHideWhenEmpty(AutoOnOff::ON);
    3322            1 :         p->setHideStavesWhenIndividuallyEmpty(true);
    3323            1 :         return;
    3324          166 :     } else if (allNever) {
    3325            1 :         p->setHideWhenEmpty(AutoOnOff::OFF);
    3326            1 :         return;
    3327              :     }
    3328              : 
    3329          165 :     if (globalHideEmptyStaves && hasAuto && !hasInstrument) {
    3330            3 :         p->setHideStavesWhenIndividuallyEmpty(true);
    3331              :     }
    3332              : 
    3333          509 :     for (Staff* staff : p->staves()) {
    3334          344 :         switch (staffHideModes[staff]) {
    3335          333 :         case StaffHideMode::AUTO:
    3336              :             // If this Part contains a mix of AUTO and INSTRUMENT staves, that
    3337              :             // can't really be represented in the new system. The INSTRUMENT
    3338              :             // staves force us to leave `hideStavesWhenIndividuallyEmpty` false,
    3339              :             // so if we choose `AutoOnOff::AUTO`, that will have the effect of
    3340              :             // INSTRUMENT. That means the staff might appear in some systems
    3341              :             // where it is empty but one of the other staves is not empty. The
    3342              :             // alternative is that we choose `AutoOnOff::ON`, but in that case
    3343              :             // the staff will be hidden in the first system if it's empty there,
    3344              :             // which is even less likely to match the user's intent.
    3345          333 :             break;
    3346            3 :         case StaffHideMode::ALWAYS:
    3347            3 :             staff->setHideWhenEmpty(AutoOnOff::ON);
    3348            3 :             break;
    3349            3 :         case StaffHideMode::NEVER:
    3350            3 :             staff->setHideWhenEmpty(AutoOnOff::OFF);
    3351            3 :             break;
    3352            5 :         case StaffHideMode::INSTRUMENT:
    3353            5 :             break;
    3354              :         }
    3355              :     }
    3356              : }
    3357              : 
    3358         2399 : bool TRead::readProperties(Part* p, XmlReader& e, ReadContext& ctx, StaffHideModes& staffHideModes)
    3359              : {
    3360         2399 :     const AsciiStringView tag(e.name());
    3361         2399 :     if (tag == "id") {
    3362            0 :         p->setId(e.readInt());
    3363         2399 :     } else if (tag == "Staff") {
    3364          878 :         Staff* staff = Factory::createStaff(p);
    3365          878 :         p->score()->appendStaff(staff);
    3366          878 :         TRead::read(staff, e, ctx, staffHideModes);
    3367         1521 :     } else if (tag == "Instrument") {
    3368          760 :         Instrument* instr = new Instrument;
    3369          760 :         read(instr, e, ctx, p);
    3370          760 :         p->setInstrument(instr, Fraction(-1, 1));
    3371          761 :     } else if (tag == "name") {
    3372            0 :         p->instrument()->setLongName(e.readText());
    3373          761 :     } else if (tag == "color") {
    3374            0 :         e.skipCurrentElement(); // Unused
    3375          761 :     } else if (tag == "shortName") {
    3376            0 :         p->instrument()->setShortName(e.readText());
    3377          761 :     } else if (tag == "show") {
    3378            0 :         p->setShow(e.readInt());
    3379          761 :     } else if (tag == "soloist") {
    3380            0 :         p->setSoloist(e.readInt());
    3381          761 :     } else if (tag == "preferSharpFlat") {
    3382            1 :         String val = e.readText();
    3383            1 :         if (val == "sharps") {
    3384            0 :             p->setPreferSharpFlat(PreferSharpFlat::SHARPS);
    3385            1 :         } else if (val == "flats") {
    3386            1 :             p->setPreferSharpFlat(PreferSharpFlat::FLATS);
    3387              :         } else {
    3388            0 :             p->setPreferSharpFlat(PreferSharpFlat::AUTO);
    3389              :         }
    3390            1 :     } else {
    3391          760 :         return false;
    3392              :     }
    3393         1639 :     return true;
    3394              : }
    3395              : 
    3396           24 : void TRead::read(Pedal* p, XmlReader& e, ReadContext& ctx)
    3397              : {
    3398           24 :     if (p->score()->mscVersion() < 301) {
    3399            0 :         ctx.addSpanner(e.intAttribute("id", -1), p);
    3400              :     }
    3401              : 
    3402           24 :     bool beginTextTag = false;
    3403           24 :     bool continueTextTag = false;
    3404           24 :     bool endTextTag = false;
    3405              : 
    3406           76 :     while (e.readNextStartElement()) {
    3407           52 :         const AsciiStringView tag(e.name());
    3408           52 :         beginTextTag = beginTextTag || tag == "beginText";
    3409           52 :         continueTextTag = continueTextTag || tag == "continueText";
    3410           52 :         endTextTag = endTextTag || tag == "endText";
    3411           52 :         if (readStyledProperty(p, tag, e, ctx)) {
    3412           28 :         } else if (!readProperties(toTextLineBase(p), e, ctx)) {
    3413            0 :             e.unknown();
    3414              :         }
    3415              :     }
    3416              : 
    3417              :     // Set to the 400 defaults if no value was specified;
    3418              :     // or follow the new style setting if the specified value matches it
    3419           24 :     if (!beginTextTag) {
    3420            2 :         p->setBeginText(String());
    3421            2 :         p->setPropertyFlags(Pid::BEGIN_TEXT, PropertyFlags::UNSTYLED);
    3422           22 :     } else if (p->beginText() == p->propertyDefault(Pid::BEGIN_TEXT).value<String>()) {
    3423           21 :         p->setPropertyFlags(Pid::BEGIN_TEXT, PropertyFlags::STYLED);
    3424              :     }
    3425           24 :     if (!continueTextTag) {
    3426           22 :         p->setContinueText(String());
    3427           22 :         p->setPropertyFlags(Pid::CONTINUE_TEXT, PropertyFlags::UNSTYLED);
    3428            2 :     } else if (p->continueText() == p->propertyDefault(Pid::CONTINUE_TEXT).value<String>()) {
    3429            0 :         p->setPropertyFlags(Pid::CONTINUE_TEXT, PropertyFlags::STYLED);
    3430              :     }
    3431           24 :     if (!endTextTag) {
    3432           24 :         p->setEndText(String());
    3433           24 :         p->setPropertyFlags(Pid::END_TEXT, PropertyFlags::UNSTYLED);
    3434            0 :     } else if (p->endText() == p->propertyDefault(Pid::END_TEXT).value<String>()) {
    3435            0 :         p->setPropertyFlags(Pid::END_TEXT, PropertyFlags::STYLED);
    3436              :     }
    3437              : 
    3438           24 :     compat::CompatUtils::resetHookHeightSign(p);
    3439           24 :     compat::CompatUtils::setTextLineTextPositionFromAlign(p);
    3440           24 : }
    3441              : 
    3442            0 : void TRead::read(Rasgueado* r, XmlReader& xml, ReadContext& ctx)
    3443              : {
    3444            0 :     TRead::read(toTextLineBase(r), xml, ctx);
    3445            0 : }
    3446              : 
    3447         4277 : void TRead::read(Rest* r, XmlReader& e, ReadContext& ctx)
    3448              : {
    3449        11380 :     while (e.readNextStartElement()) {
    3450         7103 :         const AsciiStringView tag(e.name());
    3451         7103 :         if (tag == "Symbol") {
    3452            0 :             Segment* seg = toSegment(r->parent());
    3453            0 :             Symbol* s = new Symbol(seg);
    3454            0 :             s->setTrack(r->track());
    3455            0 :             TRead::read(s, e, ctx);
    3456            0 :             seg->add(s);
    3457         7103 :         } else if (tag == "Image") {
    3458            0 :             if (MScore::noImages) {
    3459            0 :                 e.skipCurrentElement();
    3460              :             } else {
    3461            0 :                 Segment* seg = toSegment(r->parent());
    3462            0 :                 Image* image = new Image(seg);
    3463            0 :                 image->setTrack(r->track());
    3464            0 :                 TRead::read(image, e, ctx);
    3465            0 :                 seg->add(image);
    3466              :             }
    3467         7103 :         } else if (tag == "NoteDot") {
    3468            3 :             NoteDot* dot = Factory::createNoteDot(r);
    3469            3 :             TRead::read(dot, e, ctx);
    3470            3 :             r->add(dot);
    3471         7100 :         } else if (TRead::readStyledProperty(r, tag, e, ctx)) {
    3472         7100 :         } else if (TRead::readProperties(r, e, ctx)) {
    3473              :         } else {
    3474            0 :             e.unknown();
    3475              :         }
    3476              :     }
    3477         4277 : }
    3478              : 
    3479            0 : void TRead::read(Segment* s, XmlReader& e, ReadContext& ctx)
    3480              : {
    3481              :     UNUSED(ctx);
    3482            0 :     while (e.readNextStartElement()) {
    3483            0 :         const AsciiStringView tag(e.name());
    3484              : 
    3485            0 :         if (tag == "subtype") {
    3486            0 :             e.skipCurrentElement();
    3487            0 :         } else if (tag == "leadingSpace") {
    3488            0 :             s->setExtraLeadingSpace(Spatium(e.readDouble()));
    3489            0 :         } else if (tag == "trailingSpace") {          // obsolete
    3490            0 :             e.readDouble();
    3491              :         } else {
    3492            0 :             e.unknown();
    3493              :         }
    3494              :     }
    3495            0 : }
    3496              : 
    3497            0 : void TRead::read(SLine* l, XmlReader& e, ReadContext& ctx)
    3498              : {
    3499            0 :     l->eraseSpannerSegments();
    3500              : 
    3501            0 :     if (l->score()->mscVersion() < 301) {
    3502            0 :         ctx.addSpanner(e.intAttribute("id", -1), l);
    3503              :     }
    3504              : 
    3505            0 :     while (e.readNextStartElement()) {
    3506            0 :         if (!readProperties(l, e, ctx)) {
    3507            0 :             e.unknown();
    3508              :         }
    3509              :     }
    3510            0 : }
    3511              : 
    3512          241 : bool TRead::readProperties(SLine* l, XmlReader& e, ReadContext& ctx)
    3513              : {
    3514          241 :     const AsciiStringView tag(e.name());
    3515              : 
    3516          241 :     if (tag == "tick2") {                  // obsolete
    3517            4 :         if (l->tick() == Fraction(-1, 1)) {   // not necessarily set (for first note of score?) #30151
    3518            3 :             l->setTick(ctx.tick());
    3519              :         }
    3520            4 :         l->setTick2(Fraction::fromTicks(e.readInt()));
    3521          237 :     } else if (tag == "tick") {           // obsolete
    3522            1 :         l->setTick(Fraction::fromTicks(e.readInt()));
    3523          236 :     } else if (tag == "ticks") {
    3524            0 :         l->setTicks(Fraction::fromTicks(e.readInt()));
    3525          236 :     } else if (tag == "Segment") {
    3526           23 :         LineSegment* ls = l->createLineSegment();
    3527           23 :         ls->setTrack(l->track());     // needed in read to get the right staff mag
    3528           23 :         l->add(ls);
    3529           23 :         TRead::read(ls, e, ctx);
    3530           23 :         ls->setVisible(l->visible());
    3531          213 :     } else if (TRead::readProperty(l, tag, e, ctx, Pid::DIAGONAL)) {
    3532          139 :     } else if (TRead::readProperty(l, tag, e, ctx, Pid::LINE_WIDTH)) {
    3533           99 :     } else if (TRead::readProperty(l, tag, e, ctx, Pid::LINE_STYLE)) {
    3534           98 :     } else if (TRead::readProperty(l, tag, e, ctx, Pid::DASH_LINE_LEN)) {
    3535           98 :     } else if (TRead::readProperty(l, tag, e, ctx, Pid::DASH_GAP_LEN)) {
    3536           98 :     } else if (tag == "lineColor" || tag == "color") {
    3537            3 :         l->setLineColor(e.readColor());
    3538           95 :     } else if (!readProperties(toSpanner(l), e, ctx)) {
    3539           71 :         return false;
    3540              :     }
    3541          170 :     return true;
    3542              : }
    3543              : 
    3544          210 : void TRead::read(Slur* s, XmlReader& e, ReadContext& ctx)
    3545              : {
    3546          495 :     while (e.readNextStartElement()) {
    3547           75 :         if (!readProperties(s, e, ctx)) {
    3548            0 :             e.unknown();
    3549              :         }
    3550              :     }
    3551          210 : }
    3552              : 
    3553           75 : bool TRead::readProperties(Slur* s, XmlReader& e, ReadContext& ctx)
    3554              : {
    3555           75 :     return readProperties(toSlurTie(s), e, ctx);
    3556              : }
    3557              : 
    3558          921 : void TRead::read(SlurTie* s, XmlReader& e, ReadContext& ctx)
    3559              : {
    3560         1868 :     while (e.readNextStartElement()) {
    3561           26 :         if (!readProperties(s, e, ctx)) {
    3562            0 :             e.unknown();
    3563              :         }
    3564              :     }
    3565          921 : }
    3566              : 
    3567          101 : bool TRead::readProperties(SlurTie* s, XmlReader& e, ReadContext& ctx)
    3568              : {
    3569          101 :     const AsciiStringView tag(e.name());
    3570              : 
    3571          101 :     if (TRead::readProperty(s, tag, e, ctx, Pid::SLUR_DIRECTION)) {
    3572           82 :     } else if (tag == "lineType") {
    3573            0 :         s->setStyleType(static_cast<SlurStyleType>(e.readInt()));
    3574           82 :     } else if (tag == "SlurSegment" || tag == "TieSegment") {
    3575           63 :         const int idx = e.intAttribute("no", 0);
    3576           63 :         const int n = int(s->spannerSegments().size());
    3577           63 :         for (int i = n; i < idx; ++i) {
    3578            0 :             s->add(s->newSlurTieSegment());
    3579              :         }
    3580           63 :         SlurTieSegment* sts = s->newSlurTieSegment();
    3581           63 :         s->add(sts);
    3582           63 :         TRead::read(sts, e, ctx);
    3583           19 :     } else if (!readProperties(toSpanner(s), e, ctx)) {
    3584            0 :         return false;
    3585              :     }
    3586          101 :     return true;
    3587              : }
    3588              : 
    3589           63 : void TRead::read(SlurTieSegment* s, XmlReader& e, ReadContext& ctx)
    3590              : {
    3591          219 :     while (e.readNextStartElement()) {
    3592          156 :         const AsciiStringView tag(e.name());
    3593          156 :         if (s->score()->mscVersion() < 400 && (tag == "o1" || tag == "o2" || tag == "o3" || tag == "o4")) {
    3594          147 :             e.skipCurrentElement(); // Ignore slur user offsets from pre-4.0
    3595            9 :         } else if (readProperty(s, tag, e, ctx, Pid::BEZIER_OFFSET1)) {
    3596            9 :         } else if (readProperty(s, tag, e, ctx, Pid::BEZIER_OFFSET2)) {
    3597            9 :         } else if (readProperty(s, tag, e, ctx, Pid::BEZIER_OFFSET3)) {
    3598            9 :         } else if (readProperty(s, tag, e, ctx, Pid::BEZIER_OFFSET4)) {
    3599            9 :         } else if (!readItemProperties(s, e, ctx)) {
    3600            0 :             e.unknown();
    3601              :         }
    3602              :     }
    3603           63 : }
    3604              : 
    3605          114 : bool TRead::readProperties(Spanner* s, XmlReader& e, ReadContext& ctx)
    3606              : {
    3607          114 :     const AsciiStringView tag(e.name());
    3608          114 :     if (ctx.pasteMode() && (tag == "ticks_f")) {
    3609            0 :         s->setTicks(e.readFraction());
    3610            0 :         return true;
    3611          114 :     } else if (tag == "play") {
    3612            1 :         s->setPlaySpanner(e.readBool());
    3613            1 :         return true;
    3614          113 :     } else if (!readItemProperties(s, e, ctx)) {
    3615           71 :         return false;
    3616              :     }
    3617           42 :     return true;
    3618              : }
    3619              : 
    3620            0 : void TRead::read(Spacer* s, XmlReader& e, ReadContext& ctx)
    3621              : {
    3622            0 :     while (e.readNextStartElement()) {
    3623            0 :         const AsciiStringView tag(e.name());
    3624            0 :         if (tag == "subtype") {
    3625            0 :             s->setSpacerType(SpacerType(e.readInt()));
    3626            0 :         } else if (tag == "space") {
    3627            0 :             s->setGap(Spatium(e.readDouble()));
    3628            0 :         } else if (!readItemProperties(s, e, ctx)) {
    3629            0 :             e.unknown();
    3630              :         }
    3631              :     }
    3632            0 : }
    3633              : 
    3634          956 : void TRead::read(StaffType* t, XmlReader& e, ReadContext&)
    3635              : {
    3636          956 :     t->setGroup(TConv::fromXml(e.asciiAttribute("group"), StaffGroup::STANDARD));
    3637              : 
    3638          956 :     if (t->group() == StaffGroup::TAB) {
    3639            4 :         t->setGenKeysig(false);
    3640              :     }
    3641              : 
    3642          956 :     t->setFretUseTextStyle(false);
    3643              : 
    3644         2091 :     while (e.readNextStartElement()) {
    3645         1135 :         const AsciiStringView tag(e.name());
    3646         1135 :         if (tag == "name") {
    3647          879 :             t->setType(TConv::fromXml(e.readAsciiText(), StaffTypes::STANDARD));
    3648          256 :         } else if (tag == "lines") {
    3649            9 :             t->setLines(e.readInt());
    3650          247 :         } else if (tag == "lineDistance") {
    3651            4 :             t->setLineDistance(Spatium(e.readDouble()));
    3652          243 :         } else if (tag == "yoffset") {
    3653            0 :             t->setYoffset(Spatium(e.readDouble()));
    3654          243 :         } else if (tag == "mag") {
    3655            0 :             t->setUserMag(e.readDouble());
    3656          243 :         } else if (tag == "small") {
    3657            1 :             t->setSmall(e.readBool());
    3658          242 :         } else if (tag == "stepOffset") {
    3659            0 :             t->setStepOffset(e.readInt());
    3660          242 :         } else if (tag == "clef") {
    3661            0 :             t->setGenClef(e.readInt());
    3662          242 :         } else if ((tag == "slashStyle") || (tag == "stemless")) {
    3663            0 :             bool val = e.readInt() != 0;
    3664            0 :             t->setStemless(val);
    3665            0 :             t->setShowBackTied(!val);        // for compatibility with 2.0.2 scores where this prop
    3666              :         }                                 // was lacking and controlled by "slashStyle" instead
    3667          242 :         else if (tag == "barlines") {
    3668            0 :             t->setShowBarlines(e.readInt());
    3669          242 :         } else if (tag == "timesig") {
    3670            2 :             t->setGenTimesig(e.readInt());
    3671          240 :         } else if (tag == "noteheadScheme") {
    3672            0 :             t->setNoteHeadScheme(TConv::fromXml(e.readAsciiText(), NoteHeadScheme::HEAD_NORMAL));
    3673          240 :         } else if (tag == "keysig") {
    3674          178 :             t->setGenKeysig(e.readInt());
    3675           62 :         } else if (tag == "ledgerlines") {
    3676            0 :             t->setShowLedgerLines(e.readInt());
    3677           62 :         } else if (tag == "invisible") {
    3678            0 :             t->setInvisible(e.readInt());
    3679           62 :         } else if (tag == "color") {
    3680            0 :             t->setColor(e.readColor());
    3681           62 :         } else if (tag == "durations") {
    3682            4 :             t->setGenDurations(e.readBool());
    3683           58 :         } else if (tag == "durationFontName") {
    3684            4 :             t->setDurationFontName(e.readText());
    3685           54 :         } else if (tag == "durationFontSize") {
    3686            4 :             t->setDurationFontSize(e.readDouble());
    3687           50 :         } else if (tag == "durationFontY") {
    3688            4 :             t->setDurationFontUserY(e.readDouble());
    3689           46 :         } else if (tag == "fretFontName") {
    3690            4 :             t->setFretPreset(e.readText());
    3691           42 :         } else if (tag == "fretFontSize") {
    3692            4 :             t->setFretFontSize(e.readDouble());
    3693           38 :         } else if (tag == "fretFontY") {
    3694            4 :             t->setFretFontUserY(e.readDouble());
    3695           34 :         } else if (tag == "symbolRepeat") {
    3696            0 :             t->setSymbolRepeat((TablatureSymbolRepeat)e.readInt());
    3697           34 :         } else if (tag == "linesThrough") {
    3698            4 :             t->setLinesThrough(e.readBool());
    3699           30 :         } else if (tag == "minimStyle") {
    3700            4 :             t->setMinimStyle((TablatureMinimStyle)e.readInt());
    3701           26 :         } else if (tag == "onLines") {
    3702            4 :             t->setOnLines(e.readBool());
    3703           22 :         } else if (tag == "showRests") {
    3704            4 :             t->setShowRests(e.readBool());
    3705           18 :         } else if (tag == "stemsDown") {
    3706            4 :             t->setStemsDown(e.readBool());
    3707           14 :         } else if (tag == "stemsThrough") {
    3708            4 :             t->setStemsThrough(e.readBool());
    3709           10 :         } else if (tag == "upsideDown") {
    3710            4 :             t->setUpsideDown(e.readBool());
    3711            6 :         } else if (tag == "showTabFingering") {
    3712            2 :             t->setShowTabFingering(e.readBool());
    3713            4 :         } else if (tag == "useNumbers") {
    3714            4 :             t->setUseNumbers(e.readBool());
    3715            0 :         } else if (tag == "showBackTied") {           // must be after reading "slashStyle"/"stemless" prop, as in older
    3716            0 :             t->setShowBackTied(e.readBool());            // scores, this prop was lacking and controlled by "slashStyle"
    3717              :         } else {
    3718            0 :             e.unknown();
    3719              :         }
    3720              :     }
    3721          956 : }
    3722              : 
    3723           18 : void TRead::read(StaffTypeChange* c, XmlReader& e, ReadContext& ctx)
    3724              : {
    3725           36 :     while (e.readNextStartElement()) {
    3726           18 :         const AsciiStringView tag(e.name());
    3727           18 :         if (tag == "StaffType") {
    3728           18 :             StaffType* st = new StaffType();
    3729           18 :             TRead::read(st, e, ctx);
    3730              :             // Measure::add() will replace this with a pointer to a copy in the staff
    3731           18 :             c->setStaffType(st, true);
    3732            0 :         } else if (!readItemProperties(c, e, ctx)) {
    3733            0 :             e.unknown();
    3734              :         }
    3735              :     }
    3736           18 : }
    3737              : 
    3738          878 : void TRead::read(Staff* s, XmlReader& e, ReadContext& ctx, StaffHideModes& staffHideModes)
    3739              : {
    3740         3167 :     while (e.readNextStartElement()) {
    3741         1411 :         if (!readProperties(s, e, ctx, staffHideModes)) {
    3742            0 :             e.unknown();
    3743              :         }
    3744              :     }
    3745          878 : }
    3746              : 
    3747            0 : AutoOnOff TRead::readStaffHideMode(AsciiStringView asciiText)
    3748              : {
    3749            0 :     if (asciiText == "0") { // AUTO
    3750              :         // Cannot be represented anymore, so convert to the equivalent of INSTRUMENT
    3751            0 :         return AutoOnOff::AUTO;
    3752            0 :     } else if (asciiText == "1") { // ALWAYS
    3753            0 :         return AutoOnOff::ON;
    3754            0 :     } else if (asciiText == "2") { // NEVER
    3755            0 :         return AutoOnOff::OFF;
    3756              :     } else { // INSTRUMENT
    3757            0 :         return AutoOnOff::AUTO;
    3758              :     }
    3759              : }
    3760              : 
    3761         1411 : bool TRead::readProperties(Staff* s, XmlReader& e, ReadContext& ctx, StaffHideModes& staffHideModes)
    3762              : {
    3763         1411 :     const AsciiStringView tag(e.name());
    3764         1411 :     if (tag == "StaffType") {
    3765          878 :         StaffType st;
    3766          878 :         TRead::read(&st, e, ctx);
    3767          878 :         s->setStaffType(Fraction(0, 1), st);
    3768         1411 :     } else if (tag == "defaultClef") {           // sets both default transposing and concert clef
    3769          261 :         ClefType ct = TConv::fromXml(e.readAsciiText(), ClefType::G);
    3770          261 :         s->setDefaultClefType(ClefTypeList(ct, ct));
    3771          272 :     } else if (tag == "defaultConcertClef") {
    3772            2 :         s->setDefaultClefType(ClefTypeList(TConv::fromXml(e.readAsciiText(), ClefType::G), s->defaultClefType().transposingClef));
    3773          270 :     } else if (tag == "defaultTransposingClef") {
    3774            2 :         s->setDefaultClefType(ClefTypeList(s->defaultClefType().concertClef, TConv::fromXml(e.readAsciiText(), ClefType::G)));
    3775          268 :     } else if (tag == "small") {                // obsolete
    3776            0 :         s->staffType(Fraction(0, 1))->setSmall(e.readInt());
    3777          268 :     } else if (tag == "invisible") {
    3778            0 :         s->staffType(Fraction(0, 1))->setInvisible(e.readInt());              // same as: setInvisible(Fraction(0,1)), e.readInt())
    3779          268 :     } else if (tag == "hideWhenEmpty") {
    3780            0 :         staffHideModes[s] = static_cast<StaffHideMode>(e.readInt());
    3781          268 :     } else if (tag == "cutaway") {
    3782            0 :         s->setCutaway(e.readInt());
    3783          268 :     } else if (tag == "showIfSystemEmpty") {
    3784            0 :         s->setShowIfEntireSystemEmpty(e.readInt());
    3785          268 :     } else if (tag == "hideSystemBarLine") {
    3786            0 :         s->setHideSystemBarLine(e.readInt());
    3787          268 :     } else if (tag == "mergeMatchingRests") {
    3788            0 :         s->setMergeMatchingRests(e.readInt() ? AutoOnOff::ON : AutoOnOff::AUTO);
    3789          268 :     } else if (tag == "isStaffVisible") {
    3790            0 :         s->setVisible(e.readBool());
    3791          268 :     } else if (tag == "keylist") {
    3792            0 :         read400::TRead::read(s->keyList(), e, ctx);
    3793          268 :     } else if (tag == "bracket") {
    3794          113 :         int col = e.intAttribute("col", -1);
    3795          113 :         if (col == -1) {
    3796            0 :             col = static_cast<int>(ctx.score()->brackets(s->idx()).size());
    3797              :         }
    3798          113 :         EditStaffBrackets::setBracketType(ctx.score(), s->idx(), col, BracketType(e.intAttribute("type", -1)));
    3799          113 :         EditStaffBrackets::setBracketSpan(ctx.score(), s->idx(), col, e.intAttribute("span", 0));
    3800          113 :         EditStaffBrackets::setBracketVisible(ctx.score(), s->idx(), col, static_cast<bool>(e.intAttribute("visible", 1)));
    3801          113 :         e.readNext();
    3802          155 :     } else if (tag == "barLineSpan") {
    3803          126 :         const int barLineSpan = e.readInt();
    3804          126 :         if (barLineSpan < 0) {
    3805            0 :             LOGW() << "barLineSpan is negative: " << barLineSpan;
    3806            0 :             s->setBarLineSpan(false);
    3807          126 :         } else if (barLineSpan > 1) {
    3808           14 :             LOGW() << "barLineSpan is > 1: " << barLineSpan;
    3809           14 :             s->setBarLineSpan(true);
    3810              :         } else {
    3811          112 :             s->setBarLineSpan(static_cast<bool>(barLineSpan));
    3812              :         }
    3813           29 :     } else if (tag == "barLineSpanFrom") {
    3814            0 :         s->setBarLineFrom(e.readInt());
    3815           29 :     } else if (tag == "barLineSpanTo") {
    3816            0 :         s->setBarLineTo(e.readInt());
    3817           29 :     } else if (tag == "distOffset") {
    3818            1 :         s->setUserDist(Spatium(e.readDouble()));
    3819           28 :     } else if (tag == "mag") {
    3820              :         /*_userMag =*/
    3821            0 :         e.readDouble(0.1, 10.0);
    3822           28 :     } else if (tag == "linkedTo") {
    3823           27 :         int v = e.readInt() - 1;
    3824           27 :         Staff* st = s->score()->masterScore()->staff(v);
    3825           27 :         if (s->links()) {
    3826            0 :             LOGD("Staff::readProperties: multiple <linkedTo> tags");
    3827            0 :             if (!st || s->isLinked(st)) {     // maybe we don't need actually to relink...
    3828            0 :                 return true;
    3829              :             }
    3830              :             // not using unlink() here as it may delete _links
    3831              :             // a pointer to which is stored also in XmlReader.
    3832            0 :             s->links()->remove(s);
    3833            0 :             s->setLinks(nullptr);
    3834              :         }
    3835           27 :         if (st && st != s) {
    3836           24 :             s->linkTo(st);
    3837            3 :         } else if (!s->score()->isMaster() && !st) {
    3838              :             // if it is a master score it is OK not to find
    3839              :             // a staff which is going after the current one.
    3840            0 :             LOGD("staff %d not found in parent", v);
    3841              :         }
    3842            1 :     } else if (tag == "color") {
    3843            0 :         s->staffType(Fraction(0, 1))->setColor(e.readColor());
    3844            1 :     } else if (tag == "transposeDiatonic") {
    3845            0 :         ctx.setTransposeDiatonic(static_cast<int8_t>(e.readInt()));
    3846            1 :     } else if (tag == "transposeChromatic") {
    3847            0 :         ctx.setTransposeChromatic(static_cast<int8_t>(e.readInt()));
    3848            1 :     } else if (tag == "playbackVoice1") {
    3849            0 :         s->setPlaybackVoice(0, e.readInt());
    3850            1 :     } else if (tag == "playbackVoice2") {
    3851            1 :         s->setPlaybackVoice(1, e.readInt());
    3852            0 :     } else if (tag == "playbackVoice3") {
    3853            0 :         s->setPlaybackVoice(2, e.readInt());
    3854            0 :     } else if (tag == "playbackVoice4") {
    3855            0 :         s->setPlaybackVoice(3, e.readInt());
    3856              :     } else {
    3857            0 :         return false;
    3858              :     }
    3859         1411 :     return true;
    3860              : }
    3861              : 
    3862            3 : void TRead::read(Stem* s, XmlReader& e, ReadContext& ctx)
    3863              : {
    3864           11 :     while (e.readNextStartElement()) {
    3865            5 :         if (!TRead::readProperties(s, e, ctx)) {
    3866            0 :             e.unknown();
    3867              :         }
    3868              :     }
    3869            3 : }
    3870              : 
    3871            5 : bool TRead::readProperties(Stem* s, XmlReader& e, ReadContext& ctx)
    3872              : {
    3873            5 :     const AsciiStringView tag(e.name());
    3874              : 
    3875            5 :     if (tag == "userLen" && s->score()->mscVersion() < 400) {
    3876              :         // Ignore stem length pre-4.0
    3877            1 :         e.skipCurrentElement();
    3878            1 :         s->setUserLength(Spatium(0));
    3879            4 :     } else if (TRead::readProperty(s, tag, e, ctx, Pid::USER_LEN)) {
    3880            4 :     } else if (TRead::readStyledProperty(s, tag, e, ctx)) {
    3881            4 :     } else if (readItemProperties(s, e, ctx)) {
    3882              :     } else {
    3883            0 :         return false;
    3884              :     }
    3885            5 :     return true;
    3886              : }
    3887              : 
    3888            0 : void TRead::read(StemSlash* s, XmlReader& e, ReadContext& ctx)
    3889              : {
    3890            0 :     while (e.readNextStartElement()) {
    3891            0 :         if (!readItemProperties(s, e, ctx)) {
    3892            0 :             e.unknown();
    3893              :         }
    3894              :     }
    3895            0 : }
    3896              : 
    3897         1250 : void TRead::read(StringData* item, XmlReader& e)
    3898              : {
    3899         1250 :     item->stringList().clear();
    3900         9302 :     while (e.readNextStartElement()) {
    3901         8052 :         const AsciiStringView tag(e.name());
    3902         8052 :         if (tag == "frets") {
    3903         1250 :             item->setFrets(e.readInt());
    3904         6802 :         } else if (tag == "string") {
    3905         6802 :             instrString strg;
    3906         6802 :             strg.open  = e.intAttribute("open", 0);
    3907         6802 :             strg.pitch = e.readInt();
    3908         6802 :             item->stringList().push_back(strg);
    3909              :         } else {
    3910            0 :             e.unknown();
    3911              :         }
    3912              :     }
    3913         1250 :     if (item->isFiveStringBanjo()) {
    3914           16 :         item->configBanjo5thString();
    3915              :     }
    3916         1250 : }
    3917              : 
    3918            0 : void TRead::read(System* s, XmlReader& e, ReadContext& ctx)
    3919              : {
    3920            0 :     while (e.readNextStartElement()) {
    3921            0 :         const AsciiStringView tag(e.name());
    3922            0 :         if (tag == "SystemDivider") {
    3923            0 :             SystemDivider* sd = new SystemDivider(s);
    3924            0 :             TRead::read(sd, e, ctx);
    3925            0 :             s->add(sd);
    3926              :         } else {
    3927            0 :             e.unknown();
    3928              :         }
    3929              :     }
    3930            0 : }
    3931              : 
    3932          920 : void TRead::read(Text* t, XmlReader& e, ReadContext& ctx)
    3933              : {
    3934         2745 :     while (e.readNextStartElement()) {
    3935         1825 :         const AsciiStringView tag(e.name());
    3936         1825 :         if (tag == "style") {
    3937          884 :             TextStyleType s = TConv::fromXml(e.readAsciiText(), TextStyleType::DEFAULT);
    3938          884 :             if (TextStyleType::TUPLET == s) {  // ugly hack for compatibility
    3939           42 :                 continue;
    3940              :             }
    3941          842 :             t->initTextStyleType(s);
    3942          941 :         } else if (!readProperties(toTextBase(t), e, ctx)) {
    3943            0 :             e.unknown();
    3944              :         }
    3945              :     }
    3946          920 :     compat::CompatUtils::migrateOffsetPre302(t, ctx.mscVersion());
    3947          920 : }
    3948              : 
    3949           13 : void TRead::read(TextLine* l, XmlReader& e, ReadContext& ctx)
    3950              : {
    3951           13 :     bool system =  e.intAttribute("system", 0) == 1;
    3952           13 :     l->setSystemFlag(system);
    3953           13 :     l->initStyle();
    3954           13 :     TRead::read(toTextLineBase(l), e, ctx);
    3955           13 : }
    3956              : 
    3957           13 : void TRead::read(TextLineBase* b, XmlReader& e, ReadContext& ctx)
    3958              : {
    3959           13 :     b->eraseSpannerSegments();
    3960              : 
    3961           13 :     if (b->score()->mscVersion() < 301) {
    3962            0 :         ctx.addSpanner(e.intAttribute("id", -1), b);
    3963              :     }
    3964              : 
    3965           76 :     while (e.readNextStartElement()) {
    3966           50 :         if (!readProperties(b, e, ctx)) {
    3967            0 :             e.unknown();
    3968              :         }
    3969              :     }
    3970           13 :     compat::CompatUtils::resetHookHeightSign(b);
    3971           13 :     compat::CompatUtils::setTextLineTextPositionFromAlign(b);
    3972           13 : }
    3973              : 
    3974          921 : void TRead::read(Tie* t, XmlReader& xml, ReadContext& ctx)
    3975              : {
    3976          921 :     TRead::read(toSlurTie(t), xml, ctx);
    3977          921 : }
    3978              : 
    3979          892 : void TRead::read(TimeSig* s, XmlReader& e, ReadContext& ctx)
    3980              : {
    3981          892 :     int n=0, z1=0, z2=0, z3=0, z4=0;
    3982          892 :     bool old = false;
    3983              : 
    3984          892 :     TimeSigType timeSigType = TimeSigType::NORMAL;
    3985          892 :     Fraction sig;
    3986          892 :     Fraction stretch(1, 1);
    3987          892 :     String numeratorString;
    3988          892 :     String denominatorString;
    3989              : 
    3990         2903 :     while (e.readNextStartElement()) {
    3991         2011 :         const AsciiStringView tag(e.name());
    3992              : 
    3993         2011 :         if (tag == "den") {
    3994           33 :             old = true;
    3995           33 :             n = e.readInt();
    3996         1978 :         } else if (tag == "nom1") {
    3997           33 :             old = true;
    3998           33 :             z1 = e.readInt();
    3999         1945 :         } else if (tag == "nom2") {
    4000            0 :             old = true;
    4001            0 :             z2 = e.readInt();
    4002         1945 :         } else if (tag == "nom3") {
    4003            0 :             old = true;
    4004            0 :             z3 = e.readInt();
    4005         1945 :         } else if (tag == "nom4") {
    4006            0 :             old = true;
    4007            0 :             z4 = e.readInt();
    4008         1945 :         } else if (tag == "subtype") {
    4009           86 :             int i = e.readInt();
    4010           86 :             if (s->score()->mscVersion() <= 114) {
    4011           33 :                 if (i == 0x40000104) {
    4012            0 :                     timeSigType = TimeSigType::FOUR_FOUR;
    4013           33 :                 } else if (i == 0x40002084) {
    4014            0 :                     timeSigType = TimeSigType::ALLA_BREVE;
    4015              :                 } else {
    4016           33 :                     timeSigType = TimeSigType::NORMAL;
    4017              :                 }
    4018              :             } else {
    4019           53 :                 timeSigType = TimeSigType(i);
    4020              :             }
    4021         1859 :         } else if (tag == "showCourtesySig") {
    4022           62 :             s->setShowCourtesySig(e.readInt());
    4023         1797 :         } else if (tag == "sigN") {
    4024          859 :             sig.setNumerator(e.readInt());
    4025          938 :         } else if (tag == "sigD") {
    4026          859 :             sig.setDenominator(e.readInt());
    4027           79 :         } else if (tag == "stretchN") {
    4028            1 :             stretch.setNumerator(e.readInt());
    4029           78 :         } else if (tag == "stretchD") {
    4030            1 :             stretch.setDenominator(e.readInt());
    4031           77 :         } else if (tag == "textN") {
    4032            0 :             numeratorString = e.readText();
    4033           77 :         } else if (tag == "textD") {
    4034            0 :             denominatorString = e.readText();
    4035           77 :         } else if (tag == "Groups") {
    4036            0 :             Groups groups;
    4037            0 :             TRead::read(&groups, e, ctx);
    4038            0 :             s->setGroups(groups);
    4039           77 :         } else if (TRead::readStyledProperty(s, tag, e, ctx)) {
    4040           77 :         } else if (!readItemProperties(s, e, ctx)) {
    4041           16 :             e.unknown();
    4042              :         }
    4043              :     }
    4044          892 :     if (old) {
    4045           33 :         sig.set(z1 + z2 + z3 + z4, n);
    4046              :     }
    4047          892 :     stretch.reduce();
    4048              : 
    4049              :     // HACK: handle time signatures from scores before 3.5 differently on some special occasions.
    4050              :     // See https://musescore.org/node/308139.
    4051          892 :     String version = s->score()->mscoreVersion();
    4052          892 :     if (!version.isEmpty() && (version >= u"3.0") && (version < u"3.5")) {
    4053           25 :         if ((timeSigType == TimeSigType::NORMAL) && !numeratorString.isEmpty() && denominatorString.isEmpty()) {
    4054            0 :             if (numeratorString == String::number(sig.numerator())) {
    4055            0 :                 numeratorString.clear();
    4056              :             } else {
    4057            0 :                 denominatorString = String::number(sig.denominator());
    4058              :             }
    4059              :         }
    4060              :     }
    4061              : 
    4062          892 :     s->setSig(sig, timeSigType);
    4063          892 :     s->setStretch(stretch);
    4064          892 :     s->setNumeratorString(numeratorString);
    4065          892 :     s->setDenominatorString(denominatorString);
    4066          892 : }
    4067              : 
    4068           46 : void TRead::read(TremoloCompat& t, XmlReader& e, ReadContext& ctx)
    4069              : {
    4070            0 :     auto createDefaultTremolo = [](TremoloCompat& t) {
    4071            0 :         t.single = Factory::createTremoloSingleChord(t.parent);
    4072            0 :         t.single->setTrack(t.parent->track());
    4073            0 :         t.single->setTremoloType(TremoloType::R8);
    4074            0 :     };
    4075              : 
    4076            2 :     auto item = [createDefaultTremolo](TremoloCompat& t) -> EngravingItem* {
    4077            2 :         if (t.two) {
    4078            2 :             return t.two;
    4079              :         }
    4080              : 
    4081            0 :         if (!t.single) {
    4082              :             // If no item been created yet at this point,
    4083              :             // that means no "subtype" tag was present in the XML file.
    4084              :             // In this case, we create a single eighth-note tremolo,
    4085              :             // since that was the default.
    4086            0 :             createDefaultTremolo(t);
    4087              :         }
    4088              : 
    4089            0 :         return t.single;
    4090              :     };
    4091              : 
    4092           93 :     while (e.readNextStartElement()) {
    4093           47 :         const AsciiStringView tag(e.name());
    4094           47 :         if (tag == "subtype") {
    4095           46 :             TremoloType type = TConv::fromXml(e.readAsciiText(), TremoloType::INVALID_TREMOLO);
    4096           46 :             if (isTremoloTwoChord(type)) {
    4097            9 :                 t.two = Factory::createTremoloTwoChord(t.parent);
    4098            9 :                 t.two->setTrack(t.parent->track());
    4099            9 :                 t.two->setTremoloType(type);
    4100              :             } else {
    4101           37 :                 t.single = Factory::createTremoloSingleChord(t.parent);
    4102           37 :                 t.single->setTrack(t.parent->track());
    4103           37 :                 t.single->setTremoloType(type);
    4104              :             }
    4105              :         }
    4106              :         // Style needs special handling other than readStyledProperty()
    4107              :         // to avoid calling customStyleApplicable() in setProperty(),
    4108              :         // which cannot be called now because durationType() isn't defined yet.
    4109            1 :         else if (tag == "strokeStyle") {
    4110            0 :             if (t.two) {
    4111            0 :                 t.two->setTremoloStyle(TremoloStyle(e.readInt()));
    4112            0 :                 t.two->setPropertyFlags(Pid::TREMOLO_STYLE, PropertyFlags::UNSTYLED);
    4113              :             } else {
    4114            0 :                 UNREACHABLE;
    4115              :                 e.skipCurrentElement();
    4116              :             }
    4117            1 :         } else if (tag == "Fragment") {
    4118            0 :             if (t.two) {
    4119            0 :                 BeamFragment f = BeamFragment();
    4120            0 :                 int idx = t.two->directionIdx();
    4121            0 :                 t.two->setUserModified(true);
    4122            0 :                 double _spatium = t.two->spatium();
    4123            0 :                 while (e.readNextStartElement()) {
    4124            0 :                     const AsciiStringView tag1(e.name());
    4125            0 :                     if (tag1 == "y1") {
    4126            0 :                         f.py1[idx] = e.readDouble() * _spatium;
    4127            0 :                     } else if (tag1 == "y2") {
    4128            0 :                         f.py2[idx] = e.readDouble() * _spatium;
    4129              :                     } else {
    4130            0 :                         e.unknown();
    4131              :                     }
    4132              :                 }
    4133            0 :                 t.two->setBeamFragment(f);
    4134              :             } else {
    4135            0 :                 UNREACHABLE;
    4136              :                 e.skipCurrentElement();
    4137              :             }
    4138            1 :         } else if (tag == "play") {
    4139            0 :             if (t.two) {
    4140            0 :                 t.two->setPlayTremolo(e.readBool());
    4141            0 :             } else if (t.single) {
    4142            0 :                 t.single->setPlayTremolo(e.readBool());
    4143              :             } else {
    4144            0 :                 UNREACHABLE;
    4145              :             }
    4146            1 :         } else if (TRead::readStyledProperty(item(t), tag, e, ctx)) {
    4147            1 :         } else if (!readItemProperties(item(t), e, ctx)) {
    4148            0 :             e.unknown();
    4149              :         }
    4150              :     }
    4151              : 
    4152           46 :     if (!t.two && !t.single) {
    4153              :         // If no item been created yet at this point,
    4154              :         // that means no "subtype" tag was present in the XML file.
    4155              :         // In this case, we create a single eighth-note tremolo,
    4156              :         // since that was the default.
    4157            0 :         createDefaultTremolo(t);
    4158              :     }
    4159           46 : }
    4160              : 
    4161              : static constexpr std::array<Pid, 18> TextBasePropertyId { {
    4162              :     Pid::TEXT_STYLE,
    4163              :     Pid::FONT_FACE,
    4164              :     Pid::FONT_SIZE,
    4165              :     Pid::TEXT_LINE_SPACING,
    4166              :     Pid::FONT_STYLE,
    4167              :     Pid::COLOR,
    4168              :     Pid::FRAME_TYPE,
    4169              :     Pid::FRAME_WIDTH,
    4170              :     Pid::FRAME_PADDING,
    4171              :     Pid::FRAME_ROUND,
    4172              :     Pid::FRAME_FG_COLOR,
    4173              :     Pid::FRAME_BG_COLOR,
    4174              :     Pid::ALIGN,
    4175              : } };
    4176              : 
    4177          222 : bool TRead::readTextProperties(TextBase* t, XmlReader& xml, ReadContext& ctx)
    4178              : {
    4179          222 :     if (t->isStaffTextBase()) {
    4180           16 :         return readProperties(dynamic_cast<StaffTextBase*>(t), xml, ctx);
    4181              :     }
    4182          206 :     return readProperties(t, xml, ctx);
    4183              : }
    4184              : 
    4185         2442 : bool TRead::readProperties(TextBase* t, XmlReader& e, ReadContext& ctx)
    4186              : {
    4187         2442 :     const AsciiStringView tag(e.name());
    4188        41521 :     for (Pid i : TextBasePropertyId) {
    4189        39379 :         if (TRead::readProperty(t, tag, e, ctx, i)) {
    4190          300 :             if (tag != "align") {
    4191          274 :                 return true;
    4192              :             }
    4193              : 
    4194           26 :             t->setPosition(t->align().horizontal);
    4195           26 :             if (t->position() != t->propertyDefault(Pid::POSITION).value<AlignH>()) {
    4196           23 :                 t->setPropertyFlags(Pid::POSITION, PropertyFlags::UNSTYLED);
    4197              :             }
    4198              : 
    4199           26 :             return true;
    4200              :         }
    4201              :     }
    4202              : 
    4203         2142 :     if (tag == "text") {
    4204         1876 :         String str = e.readXml();
    4205         1876 :         t->setXmlText(str);
    4206         1876 :         t->checkCustomFormatting(str);
    4207         2142 :     } else if (tag == "bold") {
    4208            2 :         bool val = e.readInt();
    4209            2 :         if (val) {
    4210            1 :             t->setFontStyle(t->fontStyle() + FontStyle::Bold);
    4211              :         } else {
    4212            1 :             t->setFontStyle(t->fontStyle() - FontStyle::Bold);
    4213              :         }
    4214            2 :         if (t->isStyled(Pid::FONT_STYLE)) {
    4215            2 :             t->setPropertyFlags(Pid::FONT_STYLE, PropertyFlags::UNSTYLED);
    4216              :         }
    4217          264 :     } else if (tag == "italic") {
    4218            1 :         bool val = e.readInt();
    4219            1 :         if (val) {
    4220            0 :             t->setFontStyle(t->fontStyle() + FontStyle::Italic);
    4221              :         } else {
    4222            1 :             t->setFontStyle(t->fontStyle() - FontStyle::Italic);
    4223              :         }
    4224            1 :         if (t->isStyled(Pid::FONT_STYLE)) {
    4225            1 :             t->setPropertyFlags(Pid::FONT_STYLE, PropertyFlags::UNSTYLED);
    4226              :         }
    4227          263 :     } else if (tag == "underline") {
    4228            0 :         bool val = e.readInt();
    4229            0 :         if (val) {
    4230            0 :             t->setFontStyle(t->fontStyle() + FontStyle::Underline);
    4231              :         } else {
    4232            0 :             t->setFontStyle(t->fontStyle() - FontStyle::Underline);
    4233              :         }
    4234            0 :         if (t->isStyled(Pid::FONT_STYLE)) {
    4235            0 :             t->setPropertyFlags(Pid::FONT_STYLE, PropertyFlags::UNSTYLED);
    4236              :         }
    4237          263 :     } else if (tag == "strike") {
    4238            0 :         bool val = e.readInt();
    4239            0 :         if (val) {
    4240            0 :             t->setFontStyle(t->fontStyle() + FontStyle::Strike);
    4241              :         } else {
    4242            0 :             t->setFontStyle(t->fontStyle() - FontStyle::Strike);
    4243              :         }
    4244            0 :         if (t->isStyled(Pid::FONT_STYLE)) {
    4245            0 :             t->setPropertyFlags(Pid::FONT_STYLE, PropertyFlags::UNSTYLED);
    4246              :         }
    4247          263 :     } else if (!readItemProperties(t, e, ctx)) {
    4248           96 :         return false;
    4249              :     }
    4250         2046 :     return true;
    4251              : }
    4252              : 
    4253          310 : bool TRead::readProperties(TextLineBase* b, XmlReader& e, ReadContext& ctx)
    4254              : {
    4255          310 :     const AsciiStringView tag(e.name());
    4256         6510 :     for (Pid i : TextLineBase::textLineBasePropertyIds()) {
    4257         6442 :         if (TRead::readProperty(b, tag, e, ctx, i)) {
    4258          242 :             b->setPropertyFlags(i, PropertyFlags::UNSTYLED);
    4259          242 :             return true;
    4260              :         }
    4261              :     }
    4262           68 :     return readProperties(static_cast<SLine*>(b), e, ctx);
    4263              : }
    4264              : 
    4265           34 : void TRead::read(Trill* t, XmlReader& e, ReadContext& ctx)
    4266              : {
    4267           34 :     t->eraseSpannerSegments();
    4268              : 
    4269          100 :     while (e.readNextStartElement()) {
    4270           66 :         const AsciiStringView tag(e.name());
    4271           66 :         if (tag == "subtype") {
    4272           34 :             t->setTrillType(TConv::fromXml(e.readAsciiText(), TrillType::TRILL_LINE));
    4273           32 :         } else if (tag == "Ornament") {
    4274            0 :             Ornament* ornament = t->ornament();
    4275            0 :             if (!ornament) {
    4276            0 :                 ornament = Factory::createOrnament(toChordRest(t->ownershipParent()));
    4277            0 :                 t->setOrnament(ornament);
    4278              :             }
    4279            0 :             ornament->setSymId(Ornament::fromTrillType(t->trillType()));
    4280            0 :             TRead::read(ornament, e, ctx);
    4281            0 :             ornament->setTrack(t->track());
    4282           32 :         } else if (tag == "Accidental") {
    4283            0 :             Accidental* accidental = Factory::createAccidental(t);
    4284            0 :             TRead::read(accidental, e, ctx);
    4285            0 :             t->setAccidental(accidental);
    4286            0 :             if (t->ornament()) {
    4287            0 :                 t->ornament()->setTrillOldCompatAccidental(accidental);
    4288              :             }
    4289           32 :         } else if (tag == "ornamentStyle") {
    4290            0 :             readProperty(t, e, ctx, Pid::ORNAMENT_STYLE);
    4291           32 :         } else if (!readProperties(static_cast<SLine*>(t), e, ctx)) {
    4292            0 :             e.unknown();
    4293              :         }
    4294              :     }
    4295           34 : }
    4296              : 
    4297            0 : void TRead::read(Vibrato* v, XmlReader& e, ReadContext& ctx)
    4298              : {
    4299            0 :     v->eraseSpannerSegments();
    4300              : 
    4301            0 :     while (e.readNextStartElement()) {
    4302            0 :         const AsciiStringView tag(e.name());
    4303            0 :         if (tag == "subtype") {
    4304            0 :             v->setVibratoType(TConv::fromXml(e.readAsciiText(), VibratoType::GUITAR_VIBRATO));
    4305            0 :         } else if (!readProperties(static_cast<SLine*>(v), e, ctx)) {
    4306            0 :             e.unknown();
    4307              :         }
    4308              :     }
    4309            0 : }
    4310              : 
    4311          100 : void TRead::read(Volta* v, XmlReader& e, ReadContext& ctx)
    4312              : {
    4313          100 :     v->eraseSpannerSegments();
    4314              : 
    4315          384 :     while (e.readNextStartElement()) {
    4316          284 :         const AsciiStringView tag(e.name());
    4317          284 :         if (tag == "endings") {
    4318          100 :             v->setEndings(TConv::fromXml(e.readText(), std::vector<int>()));
    4319          184 :         } else if (TRead::readStyledProperty(v, tag, e, ctx)) {
    4320          184 :         } else if (!readProperties(v, e, ctx)) {
    4321            0 :             e.unknown();
    4322              :         }
    4323              :     }
    4324              : 
    4325          100 :     compat::CompatUtils::resetHookHeightSign(v);
    4326          100 :     compat::CompatUtils::setTextLineTextPositionFromAlign(v);
    4327          100 : }
    4328              : 
    4329          184 : bool TRead::readProperties(Volta* v, XmlReader& e, ReadContext& ctx)
    4330              : {
    4331          184 :     if (!readProperties(toTextLineBase(v), e, ctx)) {
    4332            0 :         return false;
    4333              :     }
    4334              : 
    4335          184 :     return true;
    4336              : }
    4337              : 
    4338         3062 : void TRead::readSpanner(XmlReader& e, ReadContext& ctx, EngravingItem* current, track_idx_t track)
    4339              : {
    4340         3062 :     std::shared_ptr<ConnectorInfoReader> info(new ConnectorInfoReader(e, &ctx, current, static_cast<int>(track)));
    4341         3062 :     ConnectorInfoReader::readConnector(info, e, ctx);
    4342         3062 : }
    4343              : 
    4344            0 : void TRead::readSpanner(XmlReader& e, ReadContext& ctx, Score* current, track_idx_t track)
    4345              : {
    4346            0 :     std::shared_ptr<ConnectorInfoReader> info(new ConnectorInfoReader(e, &ctx, current, static_cast<int>(track)));
    4347            0 :     ConnectorInfoReader::readConnector(info, e, ctx);
    4348            0 : }
        

Generated by: LCOV version 2.0-1