1 //=============================================================================
2 //  MuseScore
3 //  Music Composition & Notation
4 //
5 //  Copyright (C) 2016 Werner Schweer and others
6 //
7 //  This program is free software; you can redistribute it and/or modify
8 //  it under the terms of the GNU General Public License version 2
9 //  as published by the Free Software Foundation and appearing in
10 //  the file LICENSE.GPL
11 //=============================================================================
12 
13 #include "xml.h"
14 #include "score.h"
15 #include "staff.h"
16 #include "revisions.h"
17 #include "part.h"
18 #include "page.h"
19 #include "style.h"
20 #include "sym.h"
21 #include "arpeggio.h"
22 #include "audio.h"
23 #include "sig.h"
24 #include "barline.h"
25 #include "measure.h"
26 #include "ambitus.h"
27 #include "bend.h"
28 #include "chordline.h"
29 #include "hook.h"
30 #include "tuplet.h"
31 #include "systemdivider.h"
32 #include "spacer.h"
33 #include "keysig.h"
34 #include "stafftext.h"
35 #include "dynamic.h"
36 #include "drumset.h"
37 #include "timesig.h"
38 #include "slur.h"
39 #include "tie.h"
40 #include "chord.h"
41 #include "rest.h"
42 #include "breath.h"
43 #include "repeat.h"
44 #include "utils.h"
45 #include "read206.h"
46 #include "excerpt.h"
47 #include "articulation.h"
48 #include "volta.h"
49 #include "pedal.h"
50 #include "hairpin.h"
51 #include "glissando.h"
52 #include "ottava.h"
53 #include "trill.h"
54 #include "rehearsalmark.h"
55 #include "box.h"
56 #include "textframe.h"
57 #include "textline.h"
58 #include "fingering.h"
59 #include "fermata.h"
60 #include "image.h"
61 #include "stem.h"
62 #include "stemslash.h"
63 #include "undo.h"
64 #include "lyrics.h"
65 #include "tempotext.h"
66 #include "measurenumber.h"
67 #include "marker.h"
68 
69 #ifdef OMR
70 #include "omr/omr.h"
71 #include "omr/omrpage.h"
72 #endif
73 
74 
75 namespace Ms {
76 
77 static void readText206(XmlReader& e, TextBase* t, Element* be);
78 
79 //---------------------------------------------------------
80 //   excessTextStyles206
81 //    The first map has the name of the style as the string
82 //    The second map has the mapping of each Sid that the style identifies
83 //    to the default value for that sid.
84 //---------------------------------------------------------
85 
86 static std::map<QString, std::map<Sid, QVariant>> excessTextStyles206;
87 
88 //---------------------------------------------------------
89 //   setPageFormat
90 //    set Style from PageFormat
91 //---------------------------------------------------------
92 
setPageFormat(MStyle * style,const PageFormat & pf)93 void setPageFormat(MStyle* style, const PageFormat& pf)
94       {
95       style->set(Sid::pageWidth,            pf.size().width());
96       style->set(Sid::pageHeight,           pf.size().height());
97       style->set(Sid::pagePrintableWidth,   pf.printableWidth());
98       style->set(Sid::pageEvenLeftMargin,   pf.evenLeftMargin());
99       style->set(Sid::pageOddLeftMargin,    pf.oddLeftMargin());
100       style->set(Sid::pageEvenTopMargin,    pf.evenTopMargin());
101       style->set(Sid::pageEvenBottomMargin, pf.evenBottomMargin());
102       style->set(Sid::pageOddTopMargin,     pf.oddTopMargin());
103       style->set(Sid::pageOddBottomMargin,  pf.oddBottomMargin());
104       style->set(Sid::pageTwosided,         pf.twosided());
105       }
106 
107 //---------------------------------------------------------
108 //   initPageFormat
109 //    initialize PageFormat from Style
110 //---------------------------------------------------------
111 
initPageFormat(MStyle * style,PageFormat * pf)112 void initPageFormat(MStyle* style, PageFormat* pf)
113       {
114       QSizeF sz;
115       sz.setWidth(style->value(Sid::pageWidth).toReal());
116       sz.setHeight(style->value(Sid::pageHeight).toReal());
117       pf->setSize(sz);
118       pf->setPrintableWidth(style->value(Sid::pagePrintableWidth).toReal());
119       pf->setEvenLeftMargin(style->value(Sid::pageEvenLeftMargin).toReal());
120       pf->setOddLeftMargin(style->value(Sid::pageOddLeftMargin).toReal());
121       pf->setEvenTopMargin(style->value(Sid::pageEvenTopMargin).toReal());
122       pf->setEvenBottomMargin(style->value(Sid::pageEvenBottomMargin).toReal());
123       pf->setOddTopMargin(style->value(Sid::pageOddTopMargin).toReal());
124       pf->setOddBottomMargin(style->value(Sid::pageOddBottomMargin).toReal());
125       pf->setTwosided(style->value(Sid::pageTwosided).toBool());
126       }
127 
128 //---------------------------------------------------------
129 //   readPageFormat
130 //---------------------------------------------------------
131 
readPageFormat(MStyle * style,XmlReader & e)132 void readPageFormat(MStyle* style, XmlReader& e)
133       {
134       PageFormat pf;
135       initPageFormat(style, &pf);
136       pf.read(e);
137       setPageFormat(style, pf);
138       }
139 
140 //---------------------------------------------------------
141 //   readTextStyle206
142 //---------------------------------------------------------
143 
readTextStyle206(MStyle * style,XmlReader & e,std::map<QString,std::map<Sid,QVariant>> & excessStyles)144 void readTextStyle206(MStyle* style, XmlReader& e, std::map<QString, std::map<Sid, QVariant>>& excessStyles)
145       {
146       QString family = "FreeSerif";
147       double size = 10;
148       bool sizeIsSpatiumDependent = false;
149       FontStyle fontStyle = FontStyle::Normal;
150       Align align = Align::LEFT;
151       QPointF offset;
152       OffsetType offsetType = OffsetType::SPATIUM;
153 
154       FrameType frameType = FrameType::NO_FRAME;
155       Spatium paddingWidth(0.0);
156       Spatium frameWidth(0.0);
157       QColor foregroundColor = QColor(0, 0, 0, 255);
158       QColor backgroundColor = QColor(255, 255, 255, 0);
159 
160       Placement placement = Placement::ABOVE;
161       bool placementValid = false;
162 
163       QString name = e.attribute("name");
164       QColor frameColor = QColor(0, 0, 0, 255);
165 
166       bool systemFlag = false;
167       qreal lineWidth = -1.0;
168 
169       while (e.readNextStartElement()) {
170             const QStringRef& tag(e.name());
171 
172             if (tag == "name")
173                   name = e.readElementText();
174             else if (tag == "family")
175                   family = e.readElementText();
176             else if (tag == "size")
177                   size = e.readDouble();
178             else if (tag == "bold") {
179                   if (e.readInt())
180                         fontStyle = fontStyle + FontStyle::Bold;
181                   }
182             else if (tag == "italic") {
183                   if (e.readInt())
184                         fontStyle = fontStyle + FontStyle::Italic;
185                   }
186             else if (tag == "underline") {
187                   if (e.readInt())
188                         fontStyle = fontStyle + FontStyle::Underline;
189                   }
190             else if (tag == "align")
191                   align = Align(e.readInt());
192             else if (tag == "anchor")     // obsolete
193                   e.skipCurrentElement();
194 
195             else if (tag == "halign") {
196                   const QString& val(e.readElementText());
197                   if (val == "center")
198                         align = align | Align::HCENTER;
199                   else if (val == "right")
200                         align = align | Align::RIGHT;
201                   else if (val == "left")
202                         ;
203                   else
204                         qDebug("Text::readProperties: unknown alignment: <%s>", qPrintable(val));
205                   }
206             else if (tag == "valign") {
207                   const QString& val(e.readElementText());
208                   if (val == "center")
209                         align = align | Align::VCENTER;
210                   else if (val == "bottom")
211                         align = align | Align::BOTTOM;
212                   else if (val == "baseline")
213                         align = align | Align::BASELINE;
214                   else if (val == "top")
215                         ;
216                   else
217                         qDebug("Text::readProperties: unknown alignment: <%s>", qPrintable(val));
218                   }
219             else if (tag == "xoffset") {
220                   qreal xo = e.readDouble();
221                   if (offsetType == OffsetType::ABS)
222                         xo /= INCH;
223                   offset.setX(xo);
224                   }
225             else if (tag == "yoffset") {
226                   qreal yo = e.readDouble();
227                   if (offsetType == OffsetType::ABS)
228                         yo /= INCH;
229                   offset.setY(yo);
230                   }
231             else if (tag == "rxoffset" || tag == "ryoffset")         // obsolete
232                   e.readDouble();
233             else if (tag == "offsetType") {
234                   const QString& val(e.readElementText());
235                   OffsetType ot = OffsetType::ABS;
236                   if (val == "spatium" || val == "1")
237                         ot = OffsetType::SPATIUM;
238                   if (ot != offsetType) {
239                         offsetType = ot;
240                         if (ot == OffsetType::ABS)
241                               offset /= INCH;  // convert spatium -> inch
242                         else
243                               offset *= INCH;  // convert inch -> spatium
244                         }
245                   }
246             else if (tag == "sizeIsSpatiumDependent" || tag == "spatiumSizeDependent")
247                   sizeIsSpatiumDependent = e.readInt();
248             else if (tag == "frameWidth") { // obsolete
249                   frameType = FrameType::SQUARE;
250                   /*frameWidthMM =*/ e.readDouble();
251                   }
252             else if (tag == "frameWidthS") {
253                   frameType = FrameType::SQUARE;
254                   frameWidth = Spatium(e.readDouble());
255                   }
256             else if (tag == "frame")
257                   frameType = e.readInt() ? FrameType::SQUARE : FrameType::NO_FRAME;
258             else if (tag == "paddingWidth")          // obsolete
259                   /*paddingWidthMM =*/ e.readDouble();
260             else if (tag == "paddingWidthS")
261                   paddingWidth = Spatium(e.readDouble());
262             else if (tag == "frameRound")
263                   e.readInt();
264             else if (tag == "frameColor")
265                   frameColor = e.readColor();
266             else if (tag == "foregroundColor")
267                   foregroundColor = e.readColor();
268             else if (tag == "backgroundColor")
269                   backgroundColor = e.readColor();
270             else if (tag == "circle")
271                   frameType = e.readInt() ? FrameType::CIRCLE : FrameType::NO_FRAME;
272             else if (tag == "systemFlag")
273                   systemFlag = e.readInt();
274             else if (tag == "placement") {
275                   QString value(e.readElementText());
276                   if (value == "above")
277                         placement = Placement::ABOVE;
278                   else if (value == "below")
279                         placement = Placement::BELOW;
280                   placementValid = true;
281                   }
282             else if (tag == "lineWidth")
283                   lineWidth = e.readDouble();
284             else
285                   e.unknown();
286             }
287       if (family == "MuseJazz")
288             family = "MuseJazz Text";
289 
290       struct StyleTable {
291             const char* name;
292             Tid ss;
293             } styleTable[] = {
294             { "",                        Tid::DEFAULT },
295             { "Title",                   Tid::TITLE },
296             { "Subtitle",                Tid::SUBTITLE },
297             { "Composer",                Tid::COMPOSER },
298             { "Lyricist",                Tid::POET },
299             { "Lyrics Odd Lines",        Tid::LYRICS_ODD },
300             { "Lyrics Even Lines",       Tid::LYRICS_EVEN },
301             { "Fingering",               Tid::FINGERING },
302             { "LH Guitar Fingering",     Tid::LH_GUITAR_FINGERING },
303             { "RH Guitar Fingering",     Tid::RH_GUITAR_FINGERING },
304             { "String Number",           Tid::STRING_NUMBER },
305             { "Instrument Name (Long)",  Tid::INSTRUMENT_LONG },
306             { "Instrument Name (Short)", Tid::INSTRUMENT_SHORT },
307             { "Instrument Name (Part)",  Tid::INSTRUMENT_EXCERPT },
308             { "Dynamics",                Tid::DYNAMICS },
309             { "Technique",               Tid::EXPRESSION },
310             { "Tempo",                   Tid::TEMPO },
311             { "Metronome",               Tid::METRONOME },
312             { "Measure Number",          Tid::MEASURE_NUMBER },
313             { "Translator",              Tid::TRANSLATOR },
314             { "Tuplet",                  Tid::TUPLET },
315             { "System",                  Tid::SYSTEM },
316             { "Staff",                   Tid::STAFF },
317             { "Chord Symbol",            Tid::HARMONY_A },
318             { "Rehearsal Mark",          Tid::REHEARSAL_MARK },
319             { "Repeat Text Left",        Tid::REPEAT_LEFT },
320             { "Repeat Text Right",       Tid::REPEAT_RIGHT },
321             { "Frame",                   Tid::FRAME },
322             { "Text Line",               Tid::TEXTLINE },
323             { "Glissando",               Tid::GLISSANDO },
324             { "Ottava",                  Tid::OTTAVA },
325             { "Pedal",                   Tid::PEDAL },
326             { "Hairpin",                 Tid::HAIRPIN },
327             { "Bend",                    Tid::BEND },
328             { "Header",                  Tid::HEADER },
329             { "Footer",                  Tid::FOOTER },
330             { "Instrument Change",       Tid::INSTRUMENT_CHANGE },
331             { "Repeat Text",             Tid::IGNORED_STYLES, },     // Repeat Text style no longer exists
332             { "Figured Bass",            Tid::IGNORED_STYLES, },     // F.B. data are in style properties
333             { "Volta",                   Tid::VOLTA },
334             };
335       Tid ss = Tid::TEXT_STYLES;
336       for (const auto& i : styleTable) {
337             if (name == i.name) {
338                   ss = i.ss;
339                   break;
340                   }
341             }
342 
343       if (ss == Tid::IGNORED_STYLES)
344             return;
345 
346       bool isExcessStyle = false;
347       if (ss == Tid::TEXT_STYLES) {
348             ss = e.addUserTextStyle(name);
349             if (ss == Tid::TEXT_STYLES) {
350                   qDebug("unhandled substyle <%s>", qPrintable(name));
351                   isExcessStyle = true;
352                   }
353             else {
354                   int idx = int(ss) - int(Tid::USER1);
355                   if ((int(ss) < int(Tid::USER1)) || (int(ss) > int(Tid::USER12))) {
356                         qDebug("User style index %d outside of range.", idx);
357                         return;
358                         }
359                   Sid sid[] = { Sid::user1Name, Sid::user2Name, Sid::user3Name, Sid::user4Name, Sid::user5Name, Sid::user6Name,
360                                 Sid::user7Name, Sid::user8Name, Sid::user9Name, Sid::user10Name, Sid::user11Name, Sid::user12Name};
361                   style->set(sid[idx], name);
362                   }
363             }
364 
365       std::map<Sid, QVariant> excessPairs;
366       const TextStyle* ts;
367       if (isExcessStyle)
368             ts = textStyle("User-1");
369       else
370             ts = textStyle(ss);
371       for (const auto& i : *ts) {
372             QVariant value;
373             if (i.sid == Sid::NOSTYLE)
374                   break;
375             switch (i.pid) {
376                   case Pid::SUB_STYLE:
377                         value = int(ss);
378                         break;
379                   case Pid::BEGIN_FONT_FACE:
380                   case Pid::CONTINUE_FONT_FACE:
381                   case Pid::END_FONT_FACE:
382                   case Pid::FONT_FACE:
383                         value = family;
384                         break;
385                   case Pid::BEGIN_FONT_SIZE:
386                   case Pid::CONTINUE_FONT_SIZE:
387                   case Pid::END_FONT_SIZE:
388                   case Pid::FONT_SIZE:
389                         value = size;
390                         break;
391                   case Pid::BEGIN_FONT_STYLE:
392                   case Pid::CONTINUE_FONT_STYLE:
393                   case Pid::END_FONT_STYLE:
394                   case Pid::FONT_STYLE:
395                         value = int(fontStyle);
396                         break;
397                   case Pid::FRAME_TYPE:
398                         value = int(frameType);
399                         break;
400                   case Pid::FRAME_WIDTH:
401                         value = frameWidth;
402                         break;
403                   case Pid::FRAME_PADDING:
404                         value = paddingWidth;
405                         break;
406                   case Pid::FRAME_FG_COLOR:
407                         value = frameColor;
408                         break;
409                   case Pid::FRAME_BG_COLOR:
410                         value = backgroundColor;
411                         break;
412                   case Pid::SIZE_SPATIUM_DEPENDENT:
413                         value = sizeIsSpatiumDependent;
414                         break;
415                   case Pid::BEGIN_TEXT_ALIGN:
416                   case Pid::CONTINUE_TEXT_ALIGN:
417                   case Pid::END_TEXT_ALIGN:
418                   case Pid::ALIGN:
419                         value = QVariant::fromValue(align);
420                         break;
421 #if 0  //TODO-offset
422                   case Pid::OFFSET:
423                         if (offsetValid) {
424                               if (ss == Tid::TEMPO) {
425                                     style->set(Sid::tempoPosAbove, Spatium(offset.y()));
426                                     offset = QPointF();
427                                     }
428                               else if (ss == Tid::STAFF) {
429                                     style->set(Sid::staffTextPosAbove, Spatium(offset.y()));
430                                     offset = QPointF();
431                                     }
432                               else if (ss == Tid::REHEARSAL_MARK) {
433                                     style->set(Sid::rehearsalMarkPosAbove, Spatium(offset.y()));
434                                     offset = QPointF();
435                                     }
436                               value = offset;
437                               }
438                         break;
439                   case Pid::OFFSET_TYPE:
440                         value = int(offsetType);
441                         break;
442 #endif
443                   case Pid::SYSTEM_FLAG:
444                         value = systemFlag;
445                         break;
446                   case Pid::BEGIN_HOOK_HEIGHT:
447                   case Pid::END_HOOK_HEIGHT:
448                         value = QVariant();
449                         break;
450                   case Pid::PLACEMENT:
451                         if (placementValid)
452                               value = int(placement);
453                         break;
454                   case Pid::LINE_WIDTH:
455                         if (lineWidth != -1.0)
456                               value = lineWidth;
457                         break;
458                   default:
459 //                        qDebug("unhandled property <%s>%d", propertyName(i.pid), int (i.pid));
460                         break;
461                   }
462             if (value.isValid()) {
463                   if (isExcessStyle)
464                         excessPairs[i.sid] = value;
465                   else
466                         style->set(i.sid, value);
467                   }
468 //            else
469 //                  qDebug("invalid style value <%s> pid<%s>", MStyle::valueName(i.sid), propertyName(i.pid));
470             }
471 
472       if (isExcessStyle && excessPairs.size() > 0)
473             excessStyles[name] = excessPairs;
474       }
475 
476 //---------------------------------------------------------
477 //   readAccidental206
478 //---------------------------------------------------------
479 
readAccidental206(Accidental * a,XmlReader & e)480 void readAccidental206(Accidental* a, XmlReader& e)
481       {
482       while (e.readNextStartElement()) {
483             const QStringRef& tag(e.name());
484             if (tag == "bracket") {
485                   int i = e.readInt();
486                   if (i == 0 || i == 1)
487                         a->setBracket(AccidentalBracket(i));
488                   }
489             else if (tag == "subtype") {
490                   QString text = e.readElementText();
491                   const static std::map<QString, AccidentalType> accMap = {
492                      {"none",               AccidentalType::NONE},
493                      {"sharp",              AccidentalType::SHARP},
494                      {"flat",               AccidentalType::FLAT},
495                      {"natural",            AccidentalType::NATURAL},
496                      {"double sharp",       AccidentalType::SHARP2},
497                      {"double flat",        AccidentalType::FLAT2},
498                      {"flat-slash",         AccidentalType::FLAT_SLASH},
499                      {"flat-slash2",        AccidentalType::FLAT_SLASH2},
500                      {"mirrored-flat2",     AccidentalType::MIRRORED_FLAT2},
501                      {"mirrored-flat",      AccidentalType::MIRRORED_FLAT},
502                      {"sharp-slash",        AccidentalType::SHARP_SLASH},
503                      {"sharp-slash2",       AccidentalType::SHARP_SLASH2},
504                      {"sharp-slash3",       AccidentalType::SHARP_SLASH3},
505                      {"sharp-slash4",       AccidentalType::SHARP_SLASH4},
506                      {"sharp arrow up",     AccidentalType::SHARP_ARROW_UP},
507                      {"sharp arrow down",   AccidentalType::SHARP_ARROW_DOWN},
508                      {"flat arrow up",      AccidentalType::FLAT_ARROW_UP},
509                      {"flat arrow down",    AccidentalType::FLAT_ARROW_DOWN},
510                      {"natural arrow up",   AccidentalType::NATURAL_ARROW_UP},
511                      {"natural arrow down", AccidentalType::NATURAL_ARROW_DOWN},
512                      {"sori",               AccidentalType::SORI},
513                      {"koron",              AccidentalType::KORON}
514                      };
515                   auto it = accMap.find(text);
516                   if (it == accMap.end()) {
517                         qDebug("invalid type %s", qPrintable(text));
518                         a->setAccidentalType(AccidentalType::NONE);
519                         }
520                   else
521                         a->setAccidentalType(it->second);
522                   }
523             else if (tag == "role") {
524                   AccidentalRole r = AccidentalRole(e.readInt());
525                   if (r == AccidentalRole::AUTO || r == AccidentalRole::USER)
526                         a->setRole(r);
527                   }
528             else if (tag == "small")
529                   a->setSmall(e.readInt());
530             else if (a->Element::readProperties(e))
531                   ;
532             else
533                   e.unknown();
534             }
535       }
536 
convertHeadGroup(int i)537 static NoteHead::Group convertHeadGroup(int i)
538       {
539       NoteHead::Group val;
540       switch (i) {
541             case 1:
542                   val = NoteHead::Group::HEAD_CROSS;
543                   break;
544             case 2:
545                   val = NoteHead::Group::HEAD_DIAMOND;
546                   break;
547             case 3:
548                   val = NoteHead::Group::HEAD_TRIANGLE_DOWN;
549                   break;
550             case 4:
551                   val = NoteHead::Group::HEAD_MI;
552                   break;
553             case 5:
554                   val = NoteHead::Group::HEAD_SLASH;
555                   break;
556             case 6:
557                   val = NoteHead::Group::HEAD_XCIRCLE;
558                   break;
559             case 7:
560                   val = NoteHead::Group::HEAD_DO;
561                   break;
562             case 8:
563                   val = NoteHead::Group::HEAD_RE;
564                   break;
565             case 9:
566                   val = NoteHead::Group::HEAD_FA;
567                   break;
568             case 10:
569                   val = NoteHead::Group::HEAD_LA;
570                   break;
571             case 11:
572                   val = NoteHead::Group::HEAD_TI;
573                   break;
574             case 12:
575                   val = NoteHead::Group::HEAD_SOL;
576                   break;
577             case 13:
578                   val = NoteHead::Group::HEAD_BREVIS_ALT;
579                   break;
580             case 0:
581             default:
582                   val = NoteHead::Group::HEAD_NORMAL;
583             }
584       return val;
585       }
586 
convertHeadType(int i)587 static NoteHead::Type convertHeadType(int i)
588       {
589       NoteHead::Type val;
590       switch (i) {
591             case 0:
592                   val = NoteHead::Type::HEAD_WHOLE;
593                   break;
594             case 1:
595                   val = NoteHead::Type::HEAD_HALF;
596                   break;
597             case 2:
598                   val = NoteHead::Type::HEAD_QUARTER;
599                   break;
600             case 3:
601                   val = NoteHead::Type::HEAD_BREVIS;
602                   break;
603             default:
604                   val = NoteHead::Type::HEAD_AUTO;;
605             }
606       return val;
607       }
608 
609 //---------------------------------------------------------
610 //   ArticulationNames
611 //---------------------------------------------------------
612 
613 static struct ArticulationNames {
614       SymId id;
615       const char* name;
616       } articulationNames[] = {
617       { SymId::fermataAbove,              "fermata",                   },
618       { SymId::fermataShortAbove,         "shortfermata",              },
619       { SymId::fermataLongAbove,          "longfermata",               },
620       { SymId::fermataVeryLongAbove,      "verylongfermata",           },
621       { SymId::articAccentAbove,          "sforzato",                  },
622       { SymId::articStaccatoAbove,        "staccato",                  },
623       { SymId::articStaccatissimoAbove,   "staccatissimo",             },
624       { SymId::articTenutoAbove,          "tenuto",                    },
625       { SymId::articTenutoStaccatoAbove,  "portato",                   },
626       { SymId::articMarcatoAbove,         "marcato",                   },
627       { SymId::guitarFadeIn,              "fadein",                    },
628       { SymId::guitarFadeOut,             "fadeout",                   },
629       { SymId::guitarVolumeSwell,         "volumeswell",               },
630       { SymId::wiggleSawtooth,            "wigglesawtooth",            },
631       { SymId::wiggleSawtoothWide,        "wigglesawtoothwide",        },
632       { SymId::wiggleVibratoLargeFaster,  "wigglevibratolargefaster",  },
633       { SymId::wiggleVibratoLargeSlowest, "wigglevibratolargeslowest", },
634       { SymId::brassMuteOpen,             "ouvert",                    },
635       { SymId::brassMuteClosed,           "plusstop",                  },
636       { SymId::stringsUpBow,              "upbow",                     },
637       { SymId::stringsDownBow,            "downbow",                   },
638       { SymId::ornamentTurnInverted,      "reverseturn",               },
639       { SymId::ornamentTurn,              "turn",                      },
640       { SymId::ornamentTrill,             "trill",                     },
641       { SymId::ornamentShortTrill,        "prall",                     },
642       { SymId::ornamentMordent,           "mordent",                   },
643       { SymId::ornamentTremblement,       "prallprall",                },
644       { SymId::ornamentPrallMordent,      "prallmordent",              },
645       { SymId::ornamentUpPrall,           "upprall",                   },
646       { SymId::ornamentUpMordent,         "upmordent",                 },
647       { SymId::ornamentDownMordent,       "downmordent",               },
648       { SymId::ornamentPrallDown,         "pralldown",                 },
649       { SymId::ornamentPrallUp,           "prallup",                   },
650       { SymId::ornamentLinePrall,         "lineprall",                 },
651       { SymId::ornamentPrecompSlide,      "schleifer",                 },
652       { SymId::pluckedSnapPizzicatoAbove, "snappizzicato",             },
653       { SymId::stringsThumbPosition,      "thumb",                     },
654       { SymId::luteFingeringRHThumb,      "lutefingeringthumb",        },
655       { SymId::luteFingeringRHFirst,      "lutefingering1st",          },
656       { SymId::luteFingeringRHSecond,     "lutefingering2nd",          },
657       { SymId::luteFingeringRHThird,      "lutefingering3rd",          },
658 
659       { SymId::ornamentPrecompMordentUpperPrefix, "downprall"   },
660       { SymId::ornamentPrecompMordentUpperPrefix, "ornamentDownPrall"   },
661       };
662 
663 //---------------------------------------------------------
664 //   oldArticulationNames2SymId
665 //---------------------------------------------------------
666 
oldArticulationNames2SymId(const QString & s)667 SymId oldArticulationNames2SymId(const QString& s)
668       {
669       for (auto i : articulationNames) {
670             if (i.name == s)
671                   return i.id;
672             }
673       return SymId::noSym;
674       }
675 
676 //---------------------------------------------------------
677 //   readDrumset
678 //---------------------------------------------------------
679 
readDrumset(Drumset * ds,XmlReader & e)680 static void readDrumset(Drumset* ds, XmlReader& e)
681       {
682       int pitch = e.intAttribute("pitch", -1);
683       if (pitch < 0 || pitch > 127) {
684             qDebug("load drumset: invalid pitch %d", pitch);
685             return;
686             }
687       while (e.readNextStartElement()) {
688             const QStringRef& tag(e.name());
689             if (tag == "head")
690                   ds->drum(pitch).notehead = convertHeadGroup(e.readInt());
691             else if (tag == "variants") {
692                   while(e.readNextStartElement()) {
693                         const QStringRef& tagv(e.name());
694                         if (tagv == "variant") {
695                               DrumInstrumentVariant div;
696                               div.pitch = e.attribute("pitch").toInt();
697                               while (e.readNextStartElement()) {
698                                     const QStringRef& taga(e.name());
699                                     if (taga == "articulation") {
700                                           QString oldArticulationName = e.readElementText();
701                                           SymId oldId = oldArticulationNames2SymId(oldArticulationName);
702                                           div.articulationName = Articulation::symId2ArticulationName(oldId);
703                                           }
704                                     else if (taga == "tremolo") {
705                                           div.tremolo = Tremolo::name2Type(e.readElementText());
706                                           }
707                                     }
708                               ds->drum(pitch).addVariant(div);
709                               }
710                         }
711                   }
712             else if (ds->readProperties(e, pitch))
713                   ;
714             else
715                   e.unknown();
716             }
717       }
718 
719 //---------------------------------------------------------
720 //   readInstrument
721 //---------------------------------------------------------
722 
readInstrument(Instrument * i,Part * p,XmlReader & e)723 static void readInstrument(Instrument *i, Part* p, XmlReader& e)
724       {
725       int bank    = 0;
726       int volume  = 100;
727       int pan     = 60;
728       int chorus  = 30;
729       int reverb  = 30;
730       bool customDrumset = false;
731       i->clearChannels();       // remove default channel
732       while (e.readNextStartElement()) {
733             const QStringRef& tag(e.name());
734             if (tag == "Drum") {
735                   // if we see one of this tags, a custom drumset will
736                   // be created
737                   if (!i->drumset())
738                         i->setDrumset(new Drumset(*smDrumset));
739                   if (!customDrumset) {
740                         i->drumset()->clear();
741                         customDrumset = true;
742                         }
743                   readDrumset(i->drumset(), e);
744                   }
745 
746             else if (i->readProperties(e, p, &customDrumset))
747                   ;
748             else
749                  e.unknown();
750             }
751 
752       if (i->instrumentId().isEmpty())
753             i->setInstrumentId(i->recognizeInstrumentId());
754 
755       // Read single-note dynamics from template
756       i->setSingleNoteDynamicsFromTemplate();
757 
758       if (i->channel().empty()) {      // for backward compatibility
759             Channel* a = new Channel;
760             a->setName(Channel::DEFAULT_NAME);
761             a->setProgram(i->recognizeMidiProgram());
762             a->setBank(bank);
763             a->setVolume(volume);
764             a->setPan(pan);
765             a->setReverb(reverb);
766             a->setChorus(chorus);
767             i->appendChannel(a);
768             }
769       else if (i->channel(0)->program() < 0){
770             i->channel(0)->setProgram(i->recognizeMidiProgram());
771             }
772       if (i->useDrumset()) {
773             if (i->channel()[0]->bank() == 0)
774                   i->channel()[0]->setBank(128);
775             }
776       }
777 
778 //---------------------------------------------------------
779 //   readStaff
780 //---------------------------------------------------------
781 
readStaff(Staff * staff,XmlReader & e)782 static void readStaff(Staff* staff, XmlReader& e)
783       {
784       while (e.readNextStartElement()) {
785             const QStringRef& tag(e.name());
786             if (tag == "type") {    // obsolete
787                   int staffTypeIdx = e.readInt();
788                   qDebug("obsolete: Staff::read staffTypeIdx %d", staffTypeIdx);
789                   }
790             else if (tag == "neverHide") {
791                   bool v = e.readInt();
792                   if (v)
793                         staff->setHideWhenEmpty(Staff::HideMode::NEVER);
794                   }
795             else if (tag == "barLineSpan") {
796                   staff->setBarLineFrom(e.intAttribute("from", 0));
797                   staff->setBarLineTo(e.intAttribute("to", 0));
798                   int span     = e.readInt();
799                   staff->setBarLineSpan(span - 1);
800                   }
801             else if (staff->readProperties(e))
802                   ;
803             else
804                   e.unknown();
805             }
806       }
807 
808 //---------------------------------------------------------
809 //   readPart
810 //---------------------------------------------------------
811 
readPart206(Part * part,XmlReader & e)812 void readPart206(Part* part, XmlReader& e)
813       {
814       while (e.readNextStartElement()) {
815             const QStringRef& tag(e.name());
816             if (tag == "Instrument") {
817                   Instrument* i = part->_instruments.instrument(/* tick */ -1);
818                   readInstrument(i, part, e);
819                   Drumset* ds = i->drumset();
820                   Staff*   s = part->staff(0);
821                   int lld = s ? qRound(s->lineDistance(Fraction(0,1))) : 1;
822                   if (ds && s && lld > 1) {
823                         for (int j = 0; j < DRUM_INSTRUMENTS; ++j)
824                               ds->drum(j).line /= lld;
825                         }
826                   }
827             else if (tag == "Staff") {
828                   Staff* staff = new Staff(part->score());
829                   staff->setPart(part);
830                   part->score()->staves().push_back(staff);
831                   part->staves()->push_back(staff);
832                   readStaff(staff, e);
833                   }
834             else if (part->readProperties(e))
835                   ;
836             else
837                   e.unknown();
838             }
839       }
840 
841 //---------------------------------------------------------
842 //   readAmbitus
843 //---------------------------------------------------------
844 
readAmbitus(Ambitus * ambitus,XmlReader & e)845 static void readAmbitus(Ambitus* ambitus, XmlReader& e)
846       {
847       while (e.readNextStartElement()) {
848             const QStringRef& tag(e.name());
849             if (tag == "head")
850                   ambitus->setNoteHeadGroup(convertHeadGroup(e.readInt()));
851             else if (tag == "headType")
852                   ambitus->setNoteHeadType(convertHeadType(e.readInt()));
853             else if (ambitus->readProperties(e))
854                   ;
855             else
856                   e.unknown();
857             }
858       }
859 
860 //---------------------------------------------------------
861 //   readNote
862 //---------------------------------------------------------
863 
readNote(Note * note,XmlReader & e)864 static void readNote(Note* note, XmlReader& e)
865       {
866       note->setTpc1(Tpc::TPC_INVALID);
867       note->setTpc2(Tpc::TPC_INVALID);
868 
869       while (e.readNextStartElement()) {
870             const QStringRef& tag(e.name());
871             if (tag == "Accidental") {
872                   Accidental* a = new Accidental(note->score());
873                   a->setTrack(note->track());
874                   readAccidental206(a, e);
875                   note->add(a);
876                   }
877             else if (tag == "head") {
878                   int i = e.readInt();
879                   NoteHead::Group val = convertHeadGroup(i);
880                   note->setHeadGroup(val);
881                   }
882             else if (tag == "headType") {
883                   int i = e.readInt();
884                   NoteHead::Type val = convertHeadType(i);
885                   note->setHeadType(val);
886                   }
887             else if (readNoteProperties206(note, e))
888                   ;
889             else
890                   e.unknown();
891             }
892       // ensure sane values:
893       note->setPitch(limit(note->pitch(), 0, 127));
894 
895       if (!tpcIsValid(note->tpc1()) && !tpcIsValid(note->tpc2())) {
896             Key key = (note->staff() && note->chord()) ? note->staff()->key(note->chord()->tick()) : Key::C;
897             int tpc = pitch2tpc(note->pitch(), key, Prefer::NEAREST);
898             if (note->concertPitch())
899                   note->setTpc1(tpc);
900             else
901                   note->setTpc2(tpc);
902             }
903       if (!(tpcIsValid(note->tpc1()) && tpcIsValid(note->tpc2()))) {
904             Fraction tick = note->chord() ? note->chord()->tick() : Fraction(-1,1);
905             Interval v = note->staff() ? note->part()->instrument(tick)->transpose() : Interval();
906             if (tpcIsValid(note->tpc1())) {
907                   v.flip();
908                   if (v.isZero())
909                         note->setTpc2(note->tpc1());
910                   else
911                         note->setTpc2(Ms::transposeTpc(note->tpc1(), v, true));
912                   }
913             else {
914                   if (v.isZero())
915                         note->setTpc1(note->tpc2());
916                   else
917                         note->setTpc1(Ms::transposeTpc(note->tpc2(), v, true));
918                   }
919             }
920 #if 0
921       // TODO - adapt this code
922 
923       // check consistency of pitch, tpc1, tpc2, and transposition
924       // see note in InstrumentChange::read() about a known case of tpc corruption produced in 2.0.x
925       // but since there are other causes of tpc corruption (eg, https://musescore.org/en/node/74746)
926       // including perhaps some we don't know about yet,
927       // we will attempt to fix some problems here regardless of version
928 
929       if (staff() && !staff()->isDrumStaff(e.tick()) && !e.pasteMode() && !MScore::testMode) {
930             int tpc1Pitch = (tpc2pitch(_tpc[0]) + 12) % 12;
931             int tpc2Pitch = (tpc2pitch(_tpc[1]) + 12) % 12;
932             int soundingPitch = _pitch % 12;
933             if (tpc1Pitch != soundingPitch) {
934                   qDebug("bad tpc1 - soundingPitch = %d, tpc1 = %d", soundingPitch, tpc1Pitch);
935                   _pitch += tpc1Pitch - soundingPitch;
936                   }
937             if (staff()) {
938                   Interval v = staff()->part()->instrument(e.tick())->transpose();
939                   int writtenPitch = (_pitch - v.chromatic) % 12;
940                   if (tpc2Pitch != writtenPitch) {
941                         qDebug("bad tpc2 - writtenPitch = %d, tpc2 = %d", writtenPitch, tpc2Pitch);
942                         if (concertPitch()) {
943                               // assume we want to keep sounding pitch
944                               // so fix written pitch (tpc only)
945                               v.flip();
946                               _tpc[1] = Ms::transposeTpc(_tpc[0], v, true);
947                               }
948                         else {
949                               // assume we want to keep written pitch
950                               // so fix sounding pitch (both tpc and pitch)
951                               _tpc[0] = Ms::transposeTpc(_tpc[1], v, true);
952                               _pitch += tpc2Pitch - writtenPitch;
953                               }
954                         }
955                   }
956             }
957 #endif
958       }
959 
960 //---------------------------------------------------------
961 //   adjustPlacement
962 //---------------------------------------------------------
963 
adjustPlacement(Element * e)964 static void adjustPlacement(Element* e)
965       {
966       if (!e || !e->staff())
967             return;
968 
969       // element to use to determine placement
970       // for spanners, choose first segment
971       Element* ee;
972       Spanner* spanner;
973       if (e->isSpanner()) {
974             spanner = toSpanner(e);
975             if (spanner->spannerSegments().empty())
976                   return;
977             ee = spanner->spannerSegments().front();
978             if (!ee)
979                   return;
980             }
981       else {
982             spanner = nullptr;
983             ee = e;
984             }
985 
986       // determine placement based on offset
987       // anything below staff will be set to below
988       qreal staffHeight = e->staff()->height();
989       qreal threshold = staffHeight;
990       qreal offsetAdjust = 0.0;
991       Placement defaultPlacement = Placement(e->propertyDefault(Pid::PLACEMENT).toInt());
992       Placement newPlacement;
993       // most offsets will be recorded as relative to top staff line
994       // exceptions are styled offsets on elements with default placement below
995       qreal normalize;
996       if (defaultPlacement == Placement::BELOW && ee->propertyFlags(Pid::OFFSET) == PropertyFlags::STYLED)
997             normalize = staffHeight;
998       else
999             normalize = 0.0;
1000       qreal ypos = ee->offset().y() + normalize;
1001       if (ypos >= threshold) {
1002             newPlacement = Placement::BELOW;
1003             offsetAdjust -= staffHeight;
1004             }
1005       else {
1006             newPlacement = Placement::ABOVE;
1007             }
1008 
1009       // set placement
1010       e->setProperty(Pid::PLACEMENT, int(newPlacement));
1011       if (newPlacement != defaultPlacement)
1012             e->setPropertyFlags(Pid::PLACEMENT, PropertyFlags::UNSTYLED);
1013 
1014       // adjust offset
1015       if (spanner) {
1016             // adjust segments individually
1017             for (auto a : spanner->spannerSegments()) {
1018                   // spanner segments share the placement setting of the spanner
1019                   // just adjust offset
1020                   if (defaultPlacement == Placement::BELOW && a->propertyFlags(Pid::OFFSET) == PropertyFlags::STYLED)
1021                         normalize = staffHeight;
1022                   else
1023                         normalize = 0.0;
1024                   qreal yp = a->offset().y() + normalize;
1025                   a->ryoffset() += normalize + offsetAdjust;
1026 
1027                   // if any segments are offset to opposite side of staff from placement,
1028                   // or if they are within staff,
1029                   // disable autoplace
1030                   bool disableAutoplace;
1031                   if (yp + a->height() <= 0.0)
1032                         disableAutoplace = (newPlacement == Placement::BELOW);
1033                   else if (yp > staffHeight)
1034                         disableAutoplace = (newPlacement == Placement::ABOVE);
1035                   else
1036                         disableAutoplace = true;
1037                   if (disableAutoplace)
1038                         a->setAutoplace(false);
1039                   // needed for https://musescore.org/en/node/281312
1040                   // ideally we would rebase and calculate new offset
1041                   // but this may not be possible
1042                   // since original offset is relative to system
1043                   a->rxoffset() = 0;
1044                   }
1045             }
1046       else {
1047             e->ryoffset() += normalize + offsetAdjust;
1048             // if within staff, disable autoplace
1049             if (ypos + e->height() > 0.0 && ypos <= staffHeight)
1050                   e->setAutoplace(false);
1051             }
1052       }
1053 
1054 //---------------------------------------------------------
1055 //   readNoteProperties206
1056 //---------------------------------------------------------
1057 
readNoteProperties206(Note * note,XmlReader & e)1058 bool readNoteProperties206(Note* note, XmlReader& e)
1059       {
1060       const QStringRef& tag(e.name());
1061 
1062       if (tag == "pitch")
1063             note->setPitch(e.readInt());
1064       else if (tag == "tpc") {
1065             const int tpc = e.readInt();
1066             note->setTpc1(tpc);
1067             note->setTpc2(tpc);
1068             }
1069       else if (tag == "track")            // for performance
1070             note->setTrack(e.readInt());
1071       else if (tag == "Accidental") {
1072             Accidental* a = new Accidental(note->score());
1073             a->setTrack(note->track());
1074             a->read(e);
1075             note->add(a);
1076             }
1077       else if (tag == "Tie") {
1078             Tie* tie = new Tie(note->score());
1079             tie->setParent(note);
1080             tie->setTrack(note->track());
1081             readTie206(e, tie);
1082             tie->setStartNote(note);
1083             note->setTieFor(tie);
1084             }
1085       else if (tag == "tpc2")
1086             note->setTpc2(e.readInt());
1087       else if (tag == "small")
1088             note->setSmall(e.readInt());
1089       else if (tag == "mirror")
1090             note->readProperty(e, Pid::MIRROR_HEAD);
1091       else if (tag == "dotPosition")
1092             note->readProperty(e, Pid::DOT_POSITION);
1093       else if (tag == "fixed")
1094             note->setFixed(e.readBool());
1095       else if (tag == "fixedLine")
1096             note->setFixedLine(e.readInt());
1097       else if (tag == "head")
1098             note->readProperty(e, Pid::HEAD_GROUP);
1099       else if (tag == "velocity")
1100             note->setVeloOffset(e.readInt());
1101       else if (tag == "play")
1102             note->setPlay(e.readInt());
1103       else if (tag == "tuning")
1104             note->setTuning(e.readDouble());
1105       else if (tag == "fret")
1106             note->setFret(e.readInt());
1107       else if (tag == "string")
1108             note->setString(e.readInt());
1109       else if (tag == "ghost")
1110             note->setGhost(e.readInt());
1111       else if (tag == "headType")
1112             note->readProperty(e, Pid::HEAD_TYPE);
1113       else if (tag == "veloType")
1114             note->readProperty(e, Pid::VELO_TYPE);
1115       else if (tag == "line")
1116             note->setLine(e.readInt());
1117       else if (tag == "Fingering") {
1118             Fingering* f = new Fingering(note->score());
1119             f->setTrack(note->track());
1120             readText206(e, f, note);
1121             note->add(f);
1122             }
1123       else if (tag == "Symbol") {
1124             Symbol* s = new Symbol(note->score());
1125             s->setTrack(note->track());
1126             s->read(e);
1127             note->add(s);
1128             }
1129       else if (tag == "Image") {
1130             if (MScore::noImages)
1131                   e.skipCurrentElement();
1132             else {
1133                   Image* image = new Image(note->score());
1134                   image->setTrack(note->track());
1135                   image->read(e);
1136                   note->add(image);
1137                   }
1138             }
1139       else if (tag == "Bend") {
1140             Bend* b = new Bend(note->score());
1141             b->setTrack(note->track());
1142             b->read(e);
1143             note->add(b);
1144             }
1145       else if (tag == "NoteDot") {
1146             NoteDot* dot = new NoteDot(note->score());
1147             dot->read(e);
1148             note->add(dot);
1149             }
1150       else if (tag == "Events") {
1151             note->playEvents().clear();    // remove default event
1152             while (e.readNextStartElement()) {
1153                   const QStringRef& etag(e.name());
1154                   if (etag == "Event") {
1155                         NoteEvent ne;
1156                         ne.read(e);
1157                         note->playEvents().append(ne);
1158                         }
1159                   else
1160                         e.unknown();
1161                   }
1162             if (Chord* ch = note->chord())
1163                   ch->setPlayEventType(PlayEventType::User);
1164             }
1165       else if (tag == "endSpanner") {
1166             int id = e.intAttribute("id");
1167             Spanner* sp = e.findSpanner(id);
1168             if (sp) {
1169                   sp->setEndElement(note);
1170                   if (sp->isTie())
1171                         note->setTieBack(toTie(sp));
1172                   else {
1173                         if (sp->isGlissando() && note->parent() && note->parent()->isChord())
1174                               toChord(note->parent())->setEndsGlissando(true);
1175                         note->addSpannerBack(sp);
1176                         }
1177                   e.removeSpanner(sp);
1178                   }
1179             else {
1180                   // End of a spanner whose start element will appear later;
1181                   // may happen for cross-staff spanner from a lower to a higher staff
1182                   // (for instance a glissando from bass to treble staff of piano).
1183                   // Create a place-holder spanner with end data
1184                   // (a TextLine is used only because both Spanner or SLine are abstract,
1185                   // the actual class does not matter, as long as it is derived from Spanner)
1186                   int id1 = e.intAttribute("id", -1);
1187                   Staff* staff = note->staff();
1188                   if (id1 != -1 &&
1189                               // DISABLE if pasting into a staff with linked staves
1190                               // because the glissando is not properly cloned into the linked staves
1191                               staff && (!e.pasteMode() || !staff->links() || staff->links()->empty())) {
1192                         Spanner* placeholder = new TextLine(note->score());
1193                         placeholder->setAnchor(Spanner::Anchor::NOTE);
1194                         placeholder->setEndElement(note);
1195                         placeholder->setTrack2(note->track());
1196                         placeholder->setTick(Fraction(0,1));
1197                         placeholder->setTick2(e.tick());
1198                         e.addSpanner(id1, placeholder);
1199                         }
1200                   }
1201             e.readNext();
1202             }
1203       else if (tag == "TextLine"
1204             || tag == "Glissando") {
1205             Spanner* sp = toSpanner(Element::name2Element(tag, note->score()));
1206             // check this is not a lower-to-higher cross-staff spanner we already got
1207             int id = e.intAttribute("id");
1208             Spanner* placeholder = e.findSpanner(id);
1209             if (placeholder && placeholder->endElement()) {
1210                   // if it is, fill end data from place-holder
1211                   sp->setAnchor(Spanner::Anchor::NOTE);           // make sure we can set a Note as end element
1212                   sp->setEndElement(placeholder->endElement());
1213                   sp->setTrack2(placeholder->track2());
1214                   sp->setTick(e.tick());                          // make sure tick2 will be correct
1215                   sp->setTick2(placeholder->tick2());
1216                   toNote(placeholder->endElement())->addSpannerBack(sp);
1217                   // remove no longer needed place-holder before reading the new spanner,
1218                   // as reading it also adds it to XML reader list of spanners,
1219                   // which would overwrite the place-holder
1220                   e.removeSpanner(placeholder);
1221                   delete placeholder;
1222                   }
1223             sp->setTrack(note->track());
1224             sp->read(e);
1225             Staff* staff = note->staff();
1226             // DISABLE pasting of glissandi into staves with other lionked staves
1227             // because the glissando is not properly cloned into the linked staves
1228             if (e.pasteMode() && staff && staff->links() && !staff->links()->empty()) {
1229                   e.removeSpanner(sp);    // read() added the element to the XMLReader: remove it
1230                   delete sp;
1231                   }
1232             else {
1233                   sp->setAnchor(Spanner::Anchor::NOTE);
1234                   sp->setStartElement(note);
1235                   sp->setTick(e.tick());
1236                   note->addSpannerFor(sp);
1237                   sp->setParent(note);
1238                   adjustPlacement(sp);
1239                   }
1240             }
1241       else if (tag == "offset")
1242             note->Element::readProperties(e);
1243       else if (note->Element::readProperties(e))
1244             ;
1245       else
1246             return false;
1247       return true;
1248       }
1249 
1250 //---------------------------------------------------------
1251 //   ReadStyleName206
1252 //    Retrieve the content of the "style" tag from the
1253 //    QString with the content of the whole text tag
1254 //---------------------------------------------------------
1255 
ReadStyleName206(QString xmlTag)1256 static QString ReadStyleName206(QString xmlTag)
1257       {
1258       QString s;
1259       if (xmlTag.contains("<style>")) {
1260             QRegExp re("<style>([^<]+)</style>");
1261             if (re.indexIn(xmlTag) > -1)
1262                   s = re.cap(1);
1263             }
1264       return s;
1265       }
1266 
1267 //---------------------------------------------------------
1268 //   readTextPropertyStyle206
1269 //    This reads only the 'style' tag, so that it can be read
1270 //    before setting anything else.
1271 //---------------------------------------------------------
1272 
readTextPropertyStyle206(QString xmlTag,const XmlReader & e,TextBase * t,Element * be)1273 static bool readTextPropertyStyle206(QString xmlTag, const XmlReader& e, TextBase* t, Element* be)
1274       {
1275       QString s = ReadStyleName206(xmlTag);
1276 
1277       if (s.isEmpty())
1278             return false;
1279 
1280       if (!be->isTuplet()) {      // Hack
1281             if (excessTextStyles206.find(s) != excessTextStyles206.end()) {
1282                   // Init the text with a style that can't be stored as a user style
1283                   // due to the limit on the number of user styles possible.
1284                   // Use User-1, since it has all the possible user style pids
1285                   t->initTid(Tid::DEFAULT);
1286                   std::map<Sid, QVariant> styleVals = excessTextStyles206[s];
1287                   for (const StyledProperty& p : *textStyle("User-1")) {
1288                         if (t->getProperty(p.pid) == t->propertyDefault(p.pid) && styleVals.find(p.sid) != styleVals.end())
1289                               t->setProperty(p.pid, styleVals[p.sid]);
1290                         }
1291                   }
1292             else {
1293                   Tid ss;
1294                   ss = e.lookupUserTextStyle(s);
1295                   if (ss == Tid::TEXT_STYLES)
1296                         ss = textStyleFromName(s);
1297                   if (ss != Tid::TEXT_STYLES)
1298                         t->initTid(ss);
1299                   }
1300             }
1301 
1302       return true;
1303       }
1304 
1305 //---------------------------------------------------------
1306 //   readTextProperties206
1307 //---------------------------------------------------------
1308 
readTextProperties206(XmlReader & e,TextBase * t)1309 static bool readTextProperties206(XmlReader& e, TextBase* t)
1310       {
1311       const QStringRef& tag(e.name());
1312       if (tag == "style") {
1313             e.skipCurrentElement(); // read in readTextPropertyStyle206
1314             }
1315       else if (tag == "foregroundColor")  // same as "color" ?
1316             e.skipCurrentElement();
1317       else if (tag == "frame") {
1318             t->setFrameType(e.readBool() ? FrameType::SQUARE : FrameType::NO_FRAME);
1319             t->setPropertyFlags(Pid::FRAME_TYPE, PropertyFlags::UNSTYLED);
1320             }
1321       else if (tag == "frameRound")
1322             t->readProperty(e, Pid::FRAME_ROUND);
1323       else if (tag == "circle") {
1324             if (e.readBool())
1325                   t->setFrameType(FrameType::CIRCLE);
1326             else {
1327                   if (t->circle())
1328                         t->setFrameType(FrameType::SQUARE);
1329                   }
1330             t->setPropertyFlags(Pid::FRAME_TYPE, PropertyFlags::UNSTYLED);
1331             }
1332       else if (tag == "paddingWidthS")
1333             t->readProperty(e, Pid::FRAME_PADDING);
1334       else if (tag == "frameWidthS")
1335             t->readProperty(e, Pid::FRAME_WIDTH);
1336       else if (tag == "frameColor")
1337             t->readProperty(e, Pid::FRAME_FG_COLOR);
1338       else if (tag == "backgroundColor")
1339             t->readProperty(e, Pid::FRAME_BG_COLOR);
1340       else if (tag == "halign") {
1341             Align align = Align(int(t->align()) & int(~Align::HMASK));
1342             const QString& val(e.readElementText());
1343             if (val == "center")
1344                   align = align | Align::HCENTER;
1345             else if (val == "right")
1346                   align = align | Align::RIGHT;
1347             else if (val == "left")
1348                   ;
1349             else
1350                   qDebug("unknown alignment: <%s>", qPrintable(val));
1351             t->setAlign(align);
1352             t->setPropertyFlags(Pid::ALIGN, PropertyFlags::UNSTYLED);
1353             }
1354       else if (tag == "valign") {
1355             Align align = Align(int(t->align()) & int(~Align::VMASK));
1356             const QString& val(e.readElementText());
1357             if (val == "center")
1358                   align = align | Align::VCENTER;
1359             else if (val == "bottom")
1360                   align = align | Align::BOTTOM;
1361             else if (val == "baseline")
1362                   align = align | Align::BASELINE;
1363             else if (val == "top")
1364                   ;
1365             else
1366                   qDebug("unknown alignment: <%s>", qPrintable(val));
1367             t->setAlign(align);
1368             t->setPropertyFlags(Pid::ALIGN, PropertyFlags::UNSTYLED);
1369             }
1370       else if (tag == "pos") {
1371             t->readProperty(e, Pid::OFFSET);
1372             if ((char(t->align()) & char(Align::VMASK)) == char(Align::TOP))
1373                   t->ryoffset() += .5 * t->score()->spatium();     // HACK: bbox is different in 2.x
1374             adjustPlacement(t);
1375             }
1376       else if (!t->readProperties(e))
1377             return false;
1378       return true;
1379       }
1380 
1381 //---------------------------------------------------------
1382 //   TextReaderContext206
1383 //    For 2.x files, the style tag could be in a different
1384 //    position with respect to 3.x files. Since seek
1385 //    position is not reliable for readline in QIODevices (for
1386 //    example because of non-single-byte characters in at least
1387 //    one of the fields; some two-byte characters are counted as
1388 //    two single-byte characters and thus the reading could
1389 //    start at the wrong position), a copy of the text tag
1390 //    is created and read in a separate XmlReader, while
1391 //    the text style is extracted from a QString containing
1392 //    the whole text xml tag.
1393 //    TextReaderContext206 takes care of this process
1394 //---------------------------------------------------------
1395 
1396 class TextReaderContext206 {
1397       XmlReader& origReader;
1398       XmlReader tagReader;
1399       QString xmlTag;
1400 
1401    public:
TextReaderContext206(XmlReader & e)1402       TextReaderContext206(XmlReader& e)
1403          : origReader(e), tagReader(QString())
1404             {
1405             // Create a new xml document containing only the (text) xml chunk
1406             QString name = origReader.name().toString();
1407             qint64 additionalLines = origReader.lineNumber() - 2; // Subtracting the 2 new lines that will be added
1408             xmlTag = origReader.readXml();
1409             xmlTag.prepend("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<" + name + ">");
1410             xmlTag.append("</" + name + ">\n");
1411             tagReader.addData(xmlTag); // Add the xml data to the XmlReader
1412             // the additional lines are needed to output the correct line number
1413             // of the original file in case of error
1414             tagReader.setOffsetLines(additionalLines);
1415             copyProperties(origReader, tagReader);
1416             tagReader.readNextStartElement(); // read up to the first "name" tag
1417             }
1418 
1419       // Disable copying the TextReaderContext206
1420       TextReaderContext206(const TextReaderContext206&) = delete;
1421       TextReaderContext206& operator=(const TextReaderContext206&) = delete;
1422 
~TextReaderContext206()1423       ~TextReaderContext206()
1424             {
1425             // Ensure to copy back the potentially changed properties
1426             // to the original XmlReader before destruction
1427             copyProperties(tagReader, origReader);
1428             }
1429 
reader()1430       XmlReader& reader() { return tagReader; }
tag()1431       const QString& tag() { return xmlTag; }
1432    private:
1433       void copyProperties(XmlReader& original, XmlReader& derived);
1434       };
1435 
1436 //---------------------------------------------------------
1437 //   copyProperties
1438 //    Copy some of the XmlReader properties of an original
1439 //    XmlReader object to a "derived" XmlReader object.
1440 //    This is used for example when using the derived XmlReader
1441 //    to read the element properties of a 2.x text, or to
1442 //    update the base XmlReader object (for example, its
1443 //    link list) after reading the properties of a 2.x text
1444 //---------------------------------------------------------
1445 
copyProperties(XmlReader & original,XmlReader & derived)1446 void TextReaderContext206::copyProperties(XmlReader& original, XmlReader& derived)
1447       {
1448       derived.setDocName(original.getDocName());
1449       derived.setTrackOffset(original.trackOffset());
1450       derived.setTrack(original.track() - original.trackOffset());
1451 
1452       derived.setTickOffset(original.tickOffset());
1453       derived.setTick(original.tick() - original.tickOffset());
1454 
1455       derived.setLastMeasure(original.lastMeasure());
1456       derived.setCurrentMeasure(original.currentMeasure());
1457       derived.setCurrentMeasureIndex(original.currentMeasureIndex());
1458 
1459       derived.linkIds() = original.linkIds();
1460       derived.staffLinkedElements() = original.staffLinkedElements();
1461       }
1462 
1463 //---------------------------------------------------------
1464 //   readText206
1465 //---------------------------------------------------------
1466 
readText206(XmlReader & e,TextBase * t,Element * be)1467 static void readText206(XmlReader& e, TextBase* t, Element* be)
1468       {
1469       TextReaderContext206 ctx(e);
1470       readTextPropertyStyle206(ctx.tag(), e, t, be);
1471       while (ctx.reader().readNextStartElement()) {
1472             if (!readTextProperties206(ctx.reader(), t))
1473                   ctx.reader().unknown();
1474             }
1475       }
1476 
1477 //---------------------------------------------------------
1478 //   read
1479 //---------------------------------------------------------
1480 
readTempoText(TempoText * t,XmlReader & e)1481 static void readTempoText(TempoText* t, XmlReader& e)
1482       {
1483       TextReaderContext206 ctx(e);
1484       readTextPropertyStyle206(ctx.tag(), e, t, t);
1485       while (ctx.reader().readNextStartElement()) {
1486             const QStringRef& tag(ctx.reader().name());
1487             if (tag == "tempo")
1488                   t->setTempo(ctx.reader().readDouble());
1489             else if (tag == "followText")
1490                   t->setFollowText(ctx.reader().readInt());
1491             else if (!readTextProperties206(ctx.reader(), t))
1492                   ctx.reader().unknown();
1493             }
1494       // check sanity
1495       if (t->xmlText().isEmpty()) {
1496             t->setXmlText(QString("<sym>metNoteQuarterUp</sym> = %1").arg(lrint(60 * t->tempo())));
1497             t->setVisible(false);
1498             }
1499       else
1500             t->setXmlText(t->xmlText().replace("<sym>unicode", "<sym>met"));
1501       }
1502 
1503 //---------------------------------------------------------
1504 //   readMarker
1505 //---------------------------------------------------------
1506 
readMarker(Marker * m,XmlReader & e)1507 static void readMarker(Marker* m, XmlReader& e)
1508       {
1509       TextReaderContext206 ctx(e);
1510       readTextPropertyStyle206(ctx.tag(), e, m, m);
1511       Marker::Type mt = Marker::Type::SEGNO;
1512 
1513       while (ctx.reader().readNextStartElement()) {
1514             const QStringRef& tag(ctx.reader().name());
1515             if (tag == "label") {
1516                   QString s(ctx.reader().readElementText());
1517                   m->setLabel(s);
1518                   mt = m->markerType(s);
1519                   }
1520             else if (!readTextProperties206(ctx.reader(), m))
1521                   ctx.reader().unknown();
1522             }
1523       m->setMarkerType(mt);
1524       }
1525 
1526 //---------------------------------------------------------
1527 //   readDynamic
1528 //---------------------------------------------------------
1529 
readDynamic(Dynamic * d,XmlReader & e)1530 static void readDynamic(Dynamic* d, XmlReader& e)
1531       {
1532       TextReaderContext206 ctx(e);
1533       readTextPropertyStyle206(ctx.tag(), e, d, d);
1534       while (ctx.reader().readNextStartElement()) {
1535             const QStringRef& tag = ctx.reader().name();
1536             if (tag == "subtype")
1537                   d->setDynamicType(ctx.reader().readElementText());
1538             else if (tag == "velocity")
1539                   d->setVelocity(ctx.reader().readInt());
1540             else if (tag == "dynType")
1541                   d->setDynRange(Dynamic::Range(ctx.reader().readInt()));
1542             else if (!readTextProperties206(ctx.reader(), d))
1543                   ctx.reader().unknown();
1544             }
1545       }
1546 
1547 //---------------------------------------------------------
1548 //   readTuplet
1549 //---------------------------------------------------------
1550 
readTuplet(Tuplet * tuplet,XmlReader & e)1551 static void readTuplet(Tuplet* tuplet, XmlReader& e)
1552       {
1553       tuplet->setId(e.intAttribute("id", 0));
1554       while (e.readNextStartElement()) {
1555             const QStringRef& tag(e.name());
1556             if (tag == "Number") {
1557                   Text* _number = new Text(tuplet->score());
1558                   _number->setParent(tuplet);
1559                   _number->setComposition(true);
1560                   tuplet->setNumber(_number);
1561                   // _number reads property defaults from parent tuplet as "composition" is set:
1562                   tuplet->resetNumberProperty();
1563                   readText206(e, _number, tuplet);
1564                   _number->setVisible(tuplet->visible());     //?? override saved property
1565                   _number->setTrack(tuplet->track());
1566                   // move property flags from _number
1567                   for (auto p : { Pid::FONT_FACE, Pid::FONT_SIZE, Pid::FONT_STYLE, Pid::ALIGN, Pid::SIZE_SPATIUM_DEPENDENT })
1568                         tuplet->setPropertyFlags(p, _number->propertyFlags(p));
1569                   }
1570             else if (!readTupletProperties206(e, tuplet))
1571                   e.unknown();
1572             }
1573       Fraction r = (tuplet->ratio() == Fraction(1,1)) ? tuplet->ratio() : tuplet->ratio().reduced();
1574       Fraction f(r.denominator(), tuplet->baseLen().fraction().denominator());
1575       tuplet->setTicks(f.reduced());
1576       }
1577 
1578 //---------------------------------------------------------
1579 //   readLyrics
1580 //---------------------------------------------------------
1581 
readLyrics(Lyrics * lyrics,XmlReader & e)1582 static void readLyrics(Lyrics* lyrics, XmlReader& e)
1583       {
1584       int   iEndTick = 0;           // used for backward compatibility
1585       Text* _verseNumber = 0;
1586 
1587       while (e.readNextStartElement()) {
1588             const QStringRef& tag(e.name());
1589             if (tag == "endTick") {
1590                   // store <endTick> tag value until a <ticks> tag has been read
1591                   // which positions this lyrics element in the score
1592                   iEndTick = e.readInt();
1593                   }
1594             else if (tag == "Number") {
1595                   _verseNumber = new Text(lyrics->score());
1596                   readText206(e, _verseNumber, lyrics);
1597                   _verseNumber->setParent(lyrics);
1598                   }
1599             else if (tag == "style")
1600                   e.readElementText();    // ignore style
1601             else if (!lyrics->readProperties(e))
1602                   e.unknown();
1603             }
1604 
1605       // if any endTick, make it relative to current tick
1606       if (iEndTick)
1607             lyrics->setTicks(Fraction::fromTicks(iEndTick) - e.tick());
1608       if (_verseNumber) {
1609             // TODO: add text to main text
1610             delete _verseNumber;
1611             }
1612       lyrics->setAutoplace(true);
1613       if (!lyrics->isStyled(Pid::OFFSET) && !e.pasteMode()) {
1614             // fix offset for pre-3.1 scores
1615             // 2.x and earlier: y offset was relative to staff; x offset was relative to center of notehead
1616             lyrics->rxoffset() -= lyrics->symWidth(SymId::noteheadBlack) * 0.5;
1617             //lyrics->ryoffset() -= lyrics->placeBelow() && lyrics->staff() ? lyrics->staff()->height() : 0.0;
1618             // temporarily set placement to above, since the original offset is relative to top of staff
1619             // depend on adjustPlacement() to change the placement if appropriate
1620             lyrics->setPlacement(Placement::ABOVE);
1621             adjustPlacement(lyrics);
1622             }
1623       }
1624 
1625 //---------------------------------------------------------
1626 //   readDurationProperties206
1627 //---------------------------------------------------------
1628 
readDurationProperties206(XmlReader & e,DurationElement * de)1629 bool readDurationProperties206(XmlReader& e, DurationElement* de)
1630       {
1631       if (e.name() == "Tuplet") {
1632             int i = e.readInt();
1633             Tuplet* t = e.findTuplet(i);
1634             if (!t) {
1635                   qDebug("readDurationProperties206(): Tuplet id %d not found", i);
1636                   t = de->score()->searchTuplet(e, i);
1637                   if (t) {
1638                         qDebug("   ...found outside measure, input file corrupted?");
1639                         e.addTuplet(t);
1640                         }
1641                   }
1642             if (t) {
1643                   de->setTuplet(t);
1644                   if (!de->score()->undoStack()->active())     // HACK, also added in Undo::AddElement()
1645                         t->add(de);
1646                   }
1647             return true;
1648             }
1649       else if (de->Element::readProperties(e))
1650             return true;
1651       return false;
1652       }
1653 
1654 //---------------------------------------------------------
1655 //   readTupletProperties206
1656 //---------------------------------------------------------
1657 
readTupletProperties206(XmlReader & e,Tuplet * de)1658 bool readTupletProperties206(XmlReader& e, Tuplet* de)
1659       {
1660       const QStringRef& tag(e.name());
1661 
1662       if (de->readStyledProperty(e, tag))
1663             ;
1664       else if (tag == "normalNotes")
1665             de->setProperty(Pid::NORMAL_NOTES, e.readInt());
1666       else if (tag == "actualNotes")
1667             de->setProperty(Pid::ACTUAL_NOTES, e.readInt());
1668       else if (tag == "p1")
1669             de->setProperty(Pid::P1, e.readPoint() * de->score()->spatium());
1670       else if (tag == "p2")
1671             de->setProperty(Pid::P2, e.readPoint() * de->score()->spatium());
1672       else if (tag == "baseNote")
1673             de->setBaseLen(TDuration(e.readElementText()));
1674       else if (tag == "Number") {
1675             Text* _number = new Text(de->score());
1676             de->setNumber(_number);
1677             _number->setComposition(true);
1678             _number->setParent(de);
1679 //            _number->setSubStyleId(SubStyleId::TUPLET);
1680 //            initSubStyle(SubStyleId::TUPLET);   // hack: initialize number
1681             for (auto p : { Pid::FONT_FACE, Pid::FONT_SIZE, Pid::FONT_STYLE, Pid::ALIGN })
1682                   _number->resetProperty(p);
1683             readText206(e, _number, de);
1684             _number->setVisible(de->visible());     //?? override saved property
1685             _number->setTrack(de->track());
1686             // move property flags from _number
1687             for (auto p : { Pid::FONT_FACE, Pid::FONT_SIZE, Pid::FONT_STYLE, Pid::ALIGN })
1688                   de->setPropertyFlags(p, _number->propertyFlags(p));
1689             }
1690       else if (!readDurationProperties206(e, de))
1691             return false;
1692       return true;
1693       }
1694 
1695 //---------------------------------------------------------
1696 //   readChordRestProperties206
1697 //---------------------------------------------------------
1698 
readChordRestProperties206(XmlReader & e,ChordRest * ch)1699 bool readChordRestProperties206(XmlReader& e, ChordRest* ch)
1700       {
1701       const QStringRef& tag(e.name());
1702 
1703       if (tag == "durationType") {
1704             ch->setDurationType(e.readElementText());
1705             if (ch->actualDurationType().type() != TDuration::DurationType::V_MEASURE) {
1706                   if (ch->score()->mscVersion() < 112 && (ch->type() == ElementType::REST) &&
1707                               // for backward compatibility, convert V_WHOLE rests to V_MEASURE
1708                               // if long enough to fill a measure.
1709                               // OTOH, freshly created (un-initialized) rests have numerator == 0 (< 4/4)
1710                               // (see Fraction() constructor in fraction.h; this happens for instance
1711                               // when pasting selection from clipboard): they should not be converted
1712                               ch->ticks().numerator() != 0 &&
1713                               // rest durations are initialized to full measure duration when
1714                               // created upon reading the <Rest> tag (see Measure::read() )
1715                               // so a V_WHOLE rest in a measure of 4/4 or less => V_MEASURE
1716                               (ch->actualDurationType()==TDuration::DurationType::V_WHOLE && ch->ticks() <= Fraction(4, 4)) ) {
1717                         // old pre 2.0 scores: convert
1718                         ch->setDurationType(TDuration::DurationType::V_MEASURE);
1719                         }
1720                   else  // not from old score: set duration fraction from duration type
1721                         ch->setTicks(ch->actualDurationType().fraction());
1722                   }
1723             else {
1724                   if (ch->score()->mscVersion() <= 114) {
1725                         SigEvent event = ch->score()->sigmap()->timesig(e.tick());
1726                         ch->setTicks(event.timesig());
1727                         }
1728                   }
1729             }
1730       else if (tag == "BeamMode") {
1731             QString val(e.readElementText());
1732             Beam::Mode bm = Beam::Mode::AUTO;
1733             if (val == "auto")
1734                   bm = Beam::Mode::AUTO;
1735             else if (val == "begin")
1736                   bm = Beam::Mode::BEGIN;
1737             else if (val == "mid")
1738                   bm = Beam::Mode::MID;
1739             else if (val == "end")
1740                   bm = Beam::Mode::END;
1741             else if (val == "no")
1742                   bm = Beam::Mode::NONE;
1743             else if (val == "begin32")
1744                   bm = Beam::Mode::BEGIN32;
1745             else if (val == "begin64")
1746                   bm = Beam::Mode::BEGIN64;
1747             else
1748                   bm = Beam::Mode(val.toInt());
1749             ch->setBeamMode(bm);
1750             }
1751       else if (tag == "Articulation") {
1752             Element* el = readArticulation(ch, e);
1753             if (el->isFermata())
1754                   ch->segment()->add(el);
1755             else
1756                   ch->add(el);
1757             }
1758       else if (tag == "leadingSpace" || tag == "trailingSpace") {
1759             qDebug("ChordRest: %s obsolete", tag.toLocal8Bit().data());
1760             e.skipCurrentElement();
1761             }
1762       else if (tag == "Beam") {
1763             int id = e.readInt();
1764             Beam* beam = e.findBeam(id);
1765             if (beam)
1766                   beam->add(ch);        // also calls ch->setBeam(beam)
1767             else
1768                   qDebug("Beam id %d not found", id);
1769             }
1770       else if (tag == "small")
1771             ch->setSmall(e.readInt());
1772       else if (tag == "duration")
1773             ch->setTicks(e.readFraction());
1774       else if (tag == "ticklen") {      // obsolete (version < 1.12)
1775             int mticks = ch->score()->sigmap()->timesig(e.tick()).timesig().ticks();
1776             int i = e.readInt();
1777             if (i == 0)
1778                   i = mticks;
1779             if ((ch->type() == ElementType::REST) && (mticks == i)) {
1780                   ch->setDurationType(TDuration::DurationType::V_MEASURE);
1781                   ch->setTicks(Fraction::fromTicks(i));
1782                   }
1783             else {
1784                   Fraction f = Fraction::fromTicks(i);
1785                   ch->setTicks(f);
1786                   ch->setDurationType(TDuration(f));
1787                   }
1788             }
1789       else if (tag == "dots")
1790             ch->setDots(e.readInt());
1791       else if (tag == "move")
1792             ch->setStaffMove(e.readInt());
1793       else if (tag == "Slur") {
1794             int id = e.intAttribute("id");
1795             if (id == 0)
1796                   id = e.intAttribute("number");                  // obsolete
1797             Spanner* spanner = e.findSpanner(id);
1798             QString atype(e.attribute("type"));
1799 
1800             if (!spanner) {
1801                   if (atype == "stop") {
1802                         SpannerValues sv;
1803                         sv.spannerId = id;
1804                         sv.track2    = ch->track();
1805                         sv.tick2     = e.tick();
1806                         e.addSpannerValues(sv);
1807                         }
1808                   else if (atype == "start")
1809                         qDebug("spanner: start without spanner");
1810                   }
1811             else {
1812                   if (atype == "start") {
1813                         if (spanner->ticks() > Fraction(0,1) && spanner->tick() == Fraction(-1,1)) // stop has been read first
1814                               spanner->setTicks(spanner->ticks() - e.tick() - Fraction::fromTicks(1));
1815                         spanner->setTick(e.tick());
1816                         spanner->setTrack(ch->track());
1817                         if (spanner->type() == ElementType::SLUR)
1818                               spanner->setStartElement(ch);
1819                         if (e.pasteMode()) {
1820                               for (ScoreElement* el : spanner->linkList()) {
1821                                     if (el == spanner)
1822                                           continue;
1823                                     Spanner* ls = static_cast<Spanner*>(el);
1824                                     ls->setTick(spanner->tick());
1825                                     for (ScoreElement* ee : ch->linkList()) {
1826                                           ChordRest* cr = toChordRest(ee);
1827                                           if (cr->score() == ee->score() && cr->staffIdx() == ls->staffIdx()) {
1828                                                 ls->setTrack(cr->track());
1829                                                 if (ls->type() == ElementType::SLUR)
1830                                                       ls->setStartElement(cr);
1831                                                 break;
1832                                                 }
1833                                           }
1834                                     }
1835                               }
1836                         }
1837                   else if (atype == "stop") {
1838                         spanner->setTick2(e.tick());
1839                         spanner->setTrack2(ch->track());
1840                         if (spanner->isSlur())
1841                               spanner->setEndElement(ch);
1842                         ChordRest* start = toChordRest(spanner->startElement());
1843                         if (start)
1844                               spanner->setTrack(start->track());
1845                         if (e.pasteMode()) {
1846                               for (ScoreElement* el : spanner->linkList()) {
1847                                     if (el == spanner)
1848                                           continue;
1849                                     Spanner* ls = static_cast<Spanner*>(el);
1850                                     ls->setTick2(spanner->tick2());
1851                                     for (ScoreElement* ee : ch->linkList()) {
1852                                           ChordRest* cr = toChordRest(ee);
1853                                           if (cr->score() == ee->score() && cr->staffIdx() == ls->staffIdx()) {
1854                                                 ls->setTrack2(cr->track());
1855                                                 if (ls->type() == ElementType::SLUR)
1856                                                       ls->setEndElement(cr);
1857                                                 break;
1858                                                 }
1859                                           }
1860                                     }
1861                               }
1862                         }
1863                   else
1864                         qDebug("readChordRestProperties206(): unknown Slur type <%s>", qPrintable(atype));
1865                   }
1866             e.readNext();
1867             }
1868       else if (tag == "Lyrics") {
1869             Lyrics* l = new Lyrics(ch->score());
1870             l->setTrack(e.track());
1871             readLyrics(l, e);
1872             ch->add(l);
1873             }
1874       else if (tag == "pos") {
1875             QPointF pt = e.readPoint();
1876             ch->setOffset(pt * ch->spatium());
1877             }
1878       else if (ch->isRest() && tag == "Image"){
1879             if (MScore::noImages)
1880                   e.skipCurrentElement();
1881             else {
1882                   Image *image = new Image(ch->score());
1883                   image->setTrack(e.track());
1884                   image->read(e);
1885                   ch->add(image);
1886                   }
1887             }
1888       else if (!readDurationProperties206(e, ch))
1889             return false;
1890       return true;
1891       }
1892 
1893 //---------------------------------------------------------
1894 //   readChordProperties206
1895 //---------------------------------------------------------
1896 
readChordProperties206(XmlReader & e,Chord * ch)1897 bool readChordProperties206(XmlReader& e, Chord* ch)
1898       {
1899       const QStringRef& tag(e.name());
1900 
1901       if (tag == "Note") {
1902             Note* note = new Note(ch->score());
1903             // the note needs to know the properties of the track it belongs to
1904             note->setTrack(ch->track());
1905             note->setChord(ch);
1906             readNote(note, e);
1907             ch->add(note);
1908             }
1909       else if (readChordRestProperties206(e, ch))
1910             ;
1911       else if (tag == "Stem") {
1912             Stem* s = new Stem(ch->score());
1913             s->read(e);
1914             ch->add(s);
1915             }
1916       else if (tag == "Hook") {
1917             Hook* hook = new Hook(ch->score());
1918             hook->read(e);
1919             ch->add(hook);
1920             }
1921       else if (tag == "appoggiatura") {
1922             ch->setNoteType(NoteType::APPOGGIATURA);
1923             e.readNext();
1924             }
1925       else if (tag == "acciaccatura") {
1926             ch->setNoteType(NoteType::ACCIACCATURA);
1927             e.readNext();
1928             }
1929       else if (tag == "grace4") {
1930             ch->setNoteType(NoteType::GRACE4);
1931             e.readNext();
1932             }
1933       else if (tag == "grace16") {
1934             ch->setNoteType(NoteType::GRACE16);
1935             e.readNext();
1936             }
1937       else if (tag == "grace32") {
1938             ch->setNoteType(NoteType::GRACE32);
1939             e.readNext();
1940             }
1941       else if (tag == "grace8after") {
1942             ch->setNoteType(NoteType::GRACE8_AFTER);
1943             e.readNext();
1944             }
1945       else if (tag == "grace16after") {
1946             ch->setNoteType(NoteType::GRACE16_AFTER);
1947             e.readNext();
1948             }
1949       else if (tag == "grace32after") {
1950             ch->setNoteType(NoteType::GRACE32_AFTER);
1951             e.readNext();
1952             }
1953       else if (tag == "StemSlash") {
1954             StemSlash* ss = new StemSlash(ch->score());
1955             ss->read(e);
1956             ch->add(ss);
1957             }
1958       else if (ch->readProperty(tag, e, Pid::STEM_DIRECTION))
1959             ;
1960       else if (tag == "noStem")
1961             ch->setNoStem(e.readInt());
1962       else if (tag == "Arpeggio") {
1963             Arpeggio* arpeggio = new Arpeggio(ch->score());
1964             arpeggio->setTrack(ch->track());
1965             arpeggio->read(e);
1966             arpeggio->setParent(ch);
1967             ch->add(arpeggio);
1968             }
1969       // old glissando format, chord-to-chord, attached to its final chord
1970       else if (tag == "Glissando") {
1971             // the measure we are reading is not inserted in the score yet
1972             // as well as, possibly, the glissando intended initial chord;
1973             // then we cannot fully link the glissando right now;
1974             // temporarily attach the glissando to its final note as a back spanner;
1975             // after the whole score is read, Score::connectTies() will look for
1976             // the suitable initial note
1977             Note* finalNote = ch->upNote();
1978             Glissando* gliss = new Glissando(ch->score());
1979             gliss->read(e);
1980             gliss->setAnchor(Spanner::Anchor::NOTE);
1981             gliss->setStartElement(nullptr);
1982             gliss->setEndElement(nullptr);
1983             // in TAB, use straight line with no text
1984             if (ch->score()->staff(e.track() >> 2)->isTabStaff(ch->tick())) {
1985                   gliss->setGlissandoType(GlissandoType::STRAIGHT);
1986                   gliss->setShowText(false);
1987                   }
1988             finalNote->addSpannerBack(gliss);
1989             }
1990       else if (tag == "Tremolo") {
1991             Tremolo* tremolo = new Tremolo(ch->score());
1992             tremolo->setTrack(ch->track());
1993             tremolo->read(e);
1994             tremolo->setParent(ch);
1995             tremolo->setDurationType(ch->durationType());
1996             ch->setTremolo(tremolo);
1997             }
1998       else if (tag == "tickOffset")       // obsolete
1999             ;
2000       else if (tag == "ChordLine") {
2001             ChordLine* cl = new ChordLine(ch->score());
2002             cl->read(e);
2003             QPointF o = cl->offset();
2004             cl->setOffset(0.0, 0.0);
2005             ch->add(cl);
2006             e.fixOffsets().append({cl, o});
2007             }
2008       else
2009             return false;
2010       return true;
2011       }
2012 
2013 //---------------------------------------------------------
2014 //   convertDoubleArticulations
2015 //    Replace double articulations with proper SMuFL
2016 //    symbols which were not available for use prior to 3.0
2017 //---------------------------------------------------------
2018 
convertDoubleArticulations(Chord * chord,XmlReader & e)2019 static void convertDoubleArticulations(Chord* chord, XmlReader& e)
2020       {
2021       std::vector<Articulation*> pairableArticulations;
2022       for (Articulation* a : chord->articulations()) {
2023             if (a->isStaccato() || a->isTenuto()
2024                || a->isAccent() || a->isMarcato()) {
2025                   pairableArticulations.push_back(a);
2026                   };
2027             }
2028       if (pairableArticulations.size() != 2)
2029             // Do not replace triple articulation if this happens
2030             return;
2031 
2032       SymId newSymId = SymId::noSym;
2033       for (int i = 0; i < 2; ++i) {
2034             if (newSymId != SymId::noSym)
2035                   break;
2036             Articulation* ai = pairableArticulations[i];
2037             Articulation* aj = pairableArticulations[(i == 0) ? 1 : 0];
2038             if (ai->isStaccato()) {
2039                   if (aj->isAccent())
2040                         newSymId = SymId::articAccentStaccatoAbove;
2041                   else if (aj->isMarcato())
2042                         newSymId = SymId::articMarcatoStaccatoAbove;
2043                   else if (aj->isTenuto())
2044                         newSymId = SymId::articTenutoStaccatoAbove;
2045                   }
2046             else if (ai->isTenuto()) {
2047                   if (aj->isAccent())
2048                         newSymId = SymId::articTenutoAccentAbove;
2049                   else if (aj->isMarcato())
2050                         newSymId = SymId::articMarcatoTenutoAbove;
2051                   }
2052             }
2053 
2054       if (newSymId != SymId::noSym) {
2055             // We reuse old articulation and change symbol ID
2056             // rather than constructing a new articulation
2057             // in order to preserve its other properties.
2058             Articulation* newArtic = pairableArticulations[0];
2059             for (Articulation* a : pairableArticulations) {
2060                   chord->remove(a);
2061                   if (a != newArtic) {
2062                         if (LinkedElements* link = a->links())
2063                               e.linkIds().remove(link->lid());
2064                         delete a;
2065                         }
2066                   }
2067 
2068             ArticulationAnchor anchor = newArtic->anchor();
2069             newArtic->setSymId(newSymId);
2070             newArtic->setAnchor(anchor);
2071             chord->add(newArtic);
2072             }
2073       }
2074 
2075 //---------------------------------------------------------
2076 //   fixTies
2077 //---------------------------------------------------------
2078 
fixTies(Chord * chord)2079 static void fixTies(Chord* chord)
2080       {
2081       std::vector<Note*> notes;
2082       for (Note* note : chord->notes()) {
2083             Tie* tie = note->tieBack();
2084             if (tie && tie->startNote()->pitch() != note->pitch()) {
2085                   notes.push_back(tie->startNote());
2086                   }
2087             }
2088       for (Note* note : notes) {
2089             Note* endNote = chord->findNote(note->pitch());
2090             note->tieFor()->setEndNote(endNote);
2091             }
2092       }
2093 
2094 //---------------------------------------------------------
2095 //   readChord
2096 //---------------------------------------------------------
2097 
readChord(Chord * chord,XmlReader & e)2098 static void readChord(Chord* chord, XmlReader& e)
2099       {
2100       while (e.readNextStartElement()) {
2101             const QStringRef& tag(e.name());
2102             if (tag == "Note") {
2103                   Note* note = new Note(chord->score());
2104                   // the note needs to know the properties of the track it belongs to
2105                   note->setTrack(chord->track());
2106                   note->setChord(chord);
2107                   readNote(note, e);
2108                   chord->add(note);
2109                   }
2110             else if (tag == "Stem") {
2111                   Stem* stem = new Stem(chord->score());
2112                   while (e.readNextStartElement()) {
2113                         const QStringRef& t(e.name());
2114                         if (t == "subtype")        // obsolete
2115                               e.skipCurrentElement();
2116                         else if (!stem->readProperties(e))
2117                               e.unknown();
2118                         }
2119                   chord->add(stem);
2120                   }
2121             else if (tag == "Lyrics") {
2122                   Lyrics* lyrics = new Lyrics(chord->score());
2123                   lyrics->setTrack(e.track());
2124                   readLyrics(lyrics, e);
2125                   chord->add(lyrics);
2126                   }
2127             else if (readChordProperties206(e, chord))
2128                   ;
2129             else
2130                   e.unknown();
2131             }
2132       convertDoubleArticulations(chord, e);
2133       fixTies(chord);
2134       }
2135 
2136 //---------------------------------------------------------
2137 //   readRest
2138 //---------------------------------------------------------
2139 
readRest(Rest * rest,XmlReader & e)2140 static void readRest(Rest* rest, XmlReader& e)
2141       {
2142       while (e.readNextStartElement()) {
2143             if (!readChordRestProperties206(e, rest))
2144                   e.unknown();
2145             }
2146       }
2147 
2148 //---------------------------------------------------------
2149 //   readTextLineProperties
2150 //---------------------------------------------------------
2151 
readTextLineProperties(XmlReader & e,TextLineBase * tl)2152 static bool readTextLineProperties(XmlReader& e, TextLineBase* tl)
2153       {
2154       const QStringRef& tag(e.name());
2155 
2156       if (tag == "beginText") {
2157             Text* text = new Text(tl->score());
2158             readText206(e, text, tl);
2159             tl->setBeginText(text->xmlText());
2160             delete text;
2161             }
2162       else if (tag == "continueText") {
2163             Text* text = new Text(tl->score());
2164             readText206(e, text, tl);
2165             tl->setContinueText(text->xmlText());
2166             delete text;
2167             }
2168       else if (tag == "endText") {
2169             Text* text = new Text(tl->score());
2170             readText206(e, text, tl);
2171             tl->setEndText(text->xmlText());
2172             delete text;
2173             }
2174       else if (tag == "beginHook")
2175             tl->setBeginHookType(e.readBool() ? HookType::HOOK_90 : HookType::NONE);
2176       else if (tag == "endHook")
2177             tl->setEndHookType(e.readBool() ? HookType::HOOK_90 : HookType::NONE);
2178       else if (tag == "beginHookType")
2179             tl->setBeginHookType(e.readInt() == 0 ? HookType::HOOK_90 : HookType::HOOK_45);
2180       else if (tag == "endHookType")
2181             tl->setEndHookType(e.readInt() == 0 ? HookType::HOOK_90 : HookType::HOOK_45);
2182       else if (tl->readProperties(e))
2183             return true;
2184       return true;
2185       }
2186 
2187 //---------------------------------------------------------
2188 //   readVolta206
2189 //---------------------------------------------------------
2190 
readVolta206(XmlReader & e,Volta * volta)2191 static void readVolta206(XmlReader& e, Volta* volta)
2192       {
2193       while (e.readNextStartElement()) {
2194             const QStringRef& tag(e.name());
2195             if (tag == "endings") {
2196                   QString s = e.readElementText();
2197                   QStringList sl = s.split(",", QString::SkipEmptyParts);
2198                   volta->endings().clear();
2199                   for (const QString& l : qAsConst(sl)) {
2200                         int i = l.simplified().toInt();
2201                         volta->endings().append(i);
2202                         }
2203                   }
2204             else if (tag == "lineWidth") {
2205                   volta->setLineWidth(e.readDouble() * volta->spatium());
2206                   // TODO lineWidthStyle = PropertyStyle::UNSTYLED;
2207                   }
2208             else if (!readTextLineProperties(e, volta))
2209                   e.unknown();
2210             }
2211       adjustPlacement(volta);
2212       }
2213 
2214 //---------------------------------------------------------
2215 //   readPedal
2216 //---------------------------------------------------------
2217 
readPedal(XmlReader & e,Pedal * pedal)2218 static void readPedal(XmlReader& e, Pedal* pedal)
2219       {
2220       while (e.readNextStartElement()) {
2221             if (!readTextLineProperties(e, pedal))
2222                   e.unknown();
2223             }
2224       adjustPlacement(pedal);
2225       }
2226 
2227 //---------------------------------------------------------
2228 //   readOttava
2229 //---------------------------------------------------------
2230 
readOttava(XmlReader & e,Ottava * ottava)2231 static void readOttava(XmlReader& e, Ottava* ottava)
2232       {
2233       while (e.readNextStartElement()) {
2234             const QStringRef& tag(e.name());
2235             if (tag == "subtype") {
2236                   QString s = e.readElementText();
2237                   bool ok;
2238                   int idx = s.toInt(&ok);
2239                   if (!ok) {
2240                         idx = 0;    // OttavaType::OTTAVA_8VA;
2241                         int i = 0;
2242                         for (auto p :  { "8va","8vb","15ma","15mb","22ma","22mb" } ) {
2243                               if (p == s) {
2244                                     idx = i;
2245                                     break;
2246                                     }
2247                               ++i;
2248                               }
2249                         }
2250                   ottava->setOttavaType(OttavaType(idx));
2251                   }
2252             else if (tag == "numbersOnly") {
2253                   ottava->setNumbersOnly(e.readBool());
2254                   //TODO numbersOnlyStyle = PropertyFlags::UNSTYLED;
2255                   }
2256             else if (!readTextLineProperties(e, ottava))
2257                   e.unknown();
2258             }
2259       ottava->styleChanged();
2260       adjustPlacement(ottava);
2261       }
2262 
2263 //---------------------------------------------------------
2264 //   readHairpin206
2265 //---------------------------------------------------------
2266 
readHairpin206(XmlReader & e,Hairpin * h)2267 void readHairpin206(XmlReader& e, Hairpin* h)
2268       {
2269       bool useText = false;
2270       while (e.readNextStartElement()) {
2271             const QStringRef& tag(e.name());
2272             if (tag == "subtype")
2273                   h->setHairpinType(HairpinType(e.readInt()));
2274             else if (tag == "lineWidth") {
2275                   h->setLineWidth(e.readDouble() * h->spatium());
2276                   // lineWidthStyle = PropertyFlags::UNSTYLED;
2277                   }
2278             else if (tag == "hairpinHeight") {
2279                   h->setHairpinHeight(Spatium(e.readDouble()));
2280                   // hairpinHeightStyle = PropertyFlags::UNSTYLED;
2281                   }
2282             else if (tag == "hairpinContHeight") {
2283                   h->setHairpinContHeight(Spatium(e.readDouble()));
2284                   // hairpinContHeightStyle = PropertyFlags::UNSTYLED;
2285                   }
2286             else if (tag == "hairpinCircledTip")
2287                   h->setHairpinCircledTip(e.readInt());
2288             else if (tag == "veloChange")
2289                   h->setVeloChange(e.readInt());
2290             else if (tag == "dynType")
2291                   h->setDynRange(Dynamic::Range(e.readInt()));
2292             else if (tag == "useTextLine") {      // < 206
2293                   e.readInt();
2294                   if (h->hairpinType() == HairpinType::CRESC_HAIRPIN)
2295                         h->setHairpinType(HairpinType::CRESC_LINE);
2296                   else if (h->hairpinType() == HairpinType::DECRESC_HAIRPIN)
2297                         h->setHairpinType(HairpinType::DECRESC_LINE);
2298                   useText = true;
2299                   }
2300             else if (!readTextLineProperties(e, h))
2301                   e.unknown();
2302             }
2303       if (!useText) {
2304             h->setBeginText("");
2305             h->setContinueText("");
2306             h->setEndText("");
2307             }
2308       adjustPlacement(h);
2309       }
2310 
2311 //---------------------------------------------------------
2312 //   readTrill206
2313 //---------------------------------------------------------
2314 
readTrill206(XmlReader & e,Trill * t)2315 void readTrill206(XmlReader& e, Trill* t)
2316       {
2317       while (e.readNextStartElement()) {
2318             const QStringRef& tag(e.name());
2319             if (tag == "subtype")
2320                   t->setTrillType(e.readElementText());
2321             else if (tag == "Accidental") {
2322                   Accidental* _accidental = new Accidental(t->score());
2323                   readAccidental206(_accidental, e);
2324                   _accidental->setParent(t);
2325                   t->setAccidental(_accidental);
2326                   }
2327             else if (tag == "ornamentStyle")
2328                   t->readProperty(e, Pid::ORNAMENT_STYLE);
2329             else if (tag == "play")
2330                   t->setPlayArticulation(e.readBool());
2331             else if (!t->SLine::readProperties(e))
2332                   e.unknown();
2333             }
2334       adjustPlacement(t);
2335       }
2336 
2337 //---------------------------------------------------------
2338 //   readTextLine206
2339 //---------------------------------------------------------
2340 
readTextLine206(XmlReader & e,TextLineBase * tlb)2341 void readTextLine206(XmlReader& e, TextLineBase* tlb)
2342       {
2343       while (e.readNextStartElement()) {
2344             if (!readTextLineProperties(e, tlb))
2345                   e.unknown();
2346             }
2347       adjustPlacement(tlb);
2348       }
2349 
2350 //---------------------------------------------------------
2351 //   setFermataPlacement
2352 //    set fermata placement from old ArticulationAnchor
2353 //    for backwards compatibility
2354 //---------------------------------------------------------
2355 
setFermataPlacement(Element * el,ArticulationAnchor anchor,Direction direction)2356 static void setFermataPlacement(Element* el, ArticulationAnchor anchor, Direction direction)
2357       {
2358       if (direction == Direction::UP)
2359             el->setPlacement(Placement::ABOVE);
2360       else if (direction == Direction::DOWN)
2361             el->setPlacement(Placement::BELOW);
2362       else {
2363             switch (anchor) {
2364                   case ArticulationAnchor::TOP_STAFF:
2365                   case ArticulationAnchor::TOP_CHORD:
2366                         el->setPlacement(Placement::ABOVE);
2367                         break;
2368 
2369                   case ArticulationAnchor::BOTTOM_STAFF:
2370                   case ArticulationAnchor::BOTTOM_CHORD:
2371                         el->setPlacement(Placement::BELOW);
2372                         break;
2373 
2374                   case ArticulationAnchor::CHORD:
2375                         break;
2376                   default:
2377                         break;
2378                   }
2379             }
2380       }
2381 
2382 //---------------------------------------------------------
2383 //   readArticulation
2384 //---------------------------------------------------------
2385 
readArticulation(Element * parent,XmlReader & e)2386 Element* readArticulation(Element* parent, XmlReader& e)
2387       {
2388       Element* el = 0;
2389       SymId sym = SymId::fermataAbove;          // default -- backward compatibility (no type = ufermata in 1.2)
2390       ArticulationAnchor anchor  = ArticulationAnchor::TOP_STAFF;
2391       Direction direction = Direction::AUTO;
2392       Score* score = parent->score();
2393       int track = parent->track();
2394       double timeStretch = 0.0;
2395       bool useDefaultPlacement = true;
2396 
2397       while (e.readNextStartElement()) {
2398             const QStringRef& tag(e.name());
2399             if (tag == "subtype") {
2400                   QString s = e.readElementText();
2401                   if (s[0].isDigit()) {
2402                         int oldType = s.toInt();
2403                         sym = articulationNames[oldType].id;
2404                         }
2405                   else {
2406                         sym = oldArticulationNames2SymId(s);
2407                         if (sym == SymId::noSym) {
2408                               struct {
2409                                     const char* name;
2410                                     bool up;
2411                                     SymId id;
2412                                     } al[] =
2413                                     {
2414                                     { "fadein",                    true,  SymId::guitarFadeIn },
2415                                     { "fadeout",                   true,  SymId::guitarFadeOut },
2416                                     { "volumeswell",               true,  SymId::guitarVolumeSwell },
2417                                     { "wigglesawtooth",            true,  SymId::wiggleSawtooth },
2418                                     { "wigglesawtoothwide",        true,  SymId::wiggleSawtoothWide },
2419                                     { "wigglevibratolargefaster",  true,  SymId::wiggleVibratoLargeFaster },
2420                                     { "wigglevibratolargeslowest", true,  SymId::wiggleVibratoLargeSlowest },
2421                                     { "umarcato",                  true,  SymId::articMarcatoAbove },
2422                                     { "dmarcato",                  false, SymId::articMarcatoBelow },
2423                                     { "ufermata",                  true,  SymId::fermataAbove },
2424                                     { "dfermata",                  false, SymId::fermataBelow },
2425                                     { "ushortfermata",             true,  SymId::fermataShortAbove },
2426                                     { "dshortfermata",             false, SymId::fermataShortBelow },
2427                                     { "ulongfermata",              true,  SymId::fermataLongAbove },
2428                                     { "dlongfermata",              false, SymId::fermataLongBelow },
2429                                     { "uverylongfermata",          true,  SymId::fermataVeryLongAbove },
2430                                     { "dverylongfermata",          false, SymId::fermataVeryLongBelow },
2431 
2432                                     // watch out, bug in 1.2 uportato and dportato are reversed
2433                                     { "dportato",                  true,  SymId::articTenutoStaccatoAbove },
2434                                     { "uportato",                  false, SymId::articTenutoStaccatoBelow },
2435                                     { "ustaccatissimo",            true,  SymId::articStaccatissimoAbove },
2436                                     { "dstaccatissimo",            false, SymId::articStaccatissimoBelow }
2437                                     };
2438                               int i;
2439                               int n = sizeof(al) / sizeof(*al);
2440                               for (i = 0; i < n; ++i) {
2441                                     if (s == al[i].name) {
2442                                           sym       = al[i].id;
2443                                           bool up   = al[i].up;
2444                                           direction = up ? Direction::UP : Direction::DOWN;
2445                                           if ((direction == Direction::DOWN) != (track & 1))
2446                                                 useDefaultPlacement = false;
2447                                           break;
2448                                           }
2449                                     }
2450                               if (i == n) {
2451                                     sym = Sym::name2id(s);
2452                                     if (sym == SymId::noSym)
2453                                           qDebug("Articulation: unknown type <%s>", qPrintable(s));
2454                                     }
2455                               }
2456                         }
2457                   switch (sym) {
2458                         case SymId::fermataAbove:
2459                         case SymId::fermataBelow:
2460                         case SymId::fermataShortAbove:
2461                         case SymId::fermataShortBelow:
2462                         case SymId::fermataLongAbove:
2463                         case SymId::fermataLongBelow:
2464                         case SymId::fermataVeryLongAbove:
2465                         case SymId::fermataVeryLongBelow:
2466                               el = new Fermata(sym, score);
2467                               break;
2468                         default:
2469                               el = new Articulation(sym, score);
2470                               toArticulation(el)->setDirection(direction);
2471                               break;
2472                         };
2473                   }
2474             else if (tag == "anchor") {
2475                   useDefaultPlacement = false;
2476                   if (!el || el->isFermata())
2477                         anchor = ArticulationAnchor(e.readInt());
2478                   else
2479                         el->readProperties(e);
2480                   }
2481             else  if (tag == "direction") {
2482                   useDefaultPlacement = false;
2483                   if (!el || el->isFermata())
2484                         direction = toDirection(e.readElementText());
2485                   else
2486                         el->readProperties(e);
2487                   }
2488             else if (tag == "timeStretch") {
2489                   timeStretch = e.readDouble();
2490                   }
2491             else {
2492                   if (!el) {
2493                         qDebug("not handled <%s>", qPrintable(tag.toString()));
2494                         }
2495                   if (!el || !el->readProperties(e))
2496                         e.unknown();
2497                   }
2498             }
2499       // Special case for "no type" = ufermata, with missing subtype tag
2500       if (!el)
2501             el = new Fermata(sym, score);
2502       if (el->isFermata()) {
2503             if (timeStretch != 0.0)
2504                   el->setProperty(Pid::TIME_STRETCH, timeStretch);
2505             if (useDefaultPlacement)
2506                   el->setPlacement(track & 1 ? Placement::BELOW : Placement::ABOVE);
2507             else
2508                   setFermataPlacement(el, anchor, direction);
2509             }
2510       el->setTrack(track);
2511       return el;
2512       }
2513 
2514 //---------------------------------------------------------
2515 //   readSlurTieProperties
2516 //---------------------------------------------------------
2517 
readSlurTieProperties(XmlReader & e,SlurTie * st)2518 static bool readSlurTieProperties(XmlReader& e, SlurTie* st)
2519       {
2520       const QStringRef& tag(e.name());
2521 
2522       if (st->readProperty(tag, e, Pid::SLUR_DIRECTION))
2523             ;
2524       else if (tag == "lineType")
2525             st->setLineType(e.readInt());
2526       else if (tag == "SlurSegment") {
2527             SlurTieSegment* s = st->newSlurTieSegment();
2528             s->read(e);
2529             st->add(s);
2530             }
2531       else if (!st->Element::readProperties(e))
2532             return false;
2533       return true;
2534       }
2535 
2536 //---------------------------------------------------------
2537 //   readSlur206
2538 //---------------------------------------------------------
2539 
readSlur206(XmlReader & e,Slur * s)2540 void readSlur206(XmlReader& e, Slur* s)
2541       {
2542       s->setTrack(e.track());      // set staff
2543       e.addSpanner(e.intAttribute("id"), s);
2544       while (e.readNextStartElement()) {
2545             const QStringRef& tag(e.name());
2546             if (tag == "track2")
2547                   s->setTrack2(e.readInt());
2548             else if (tag == "startTrack")       // obsolete
2549                   s->setTrack(e.readInt());
2550             else if (tag == "endTrack")         // obsolete
2551                   e.readInt();
2552             else if (!readSlurTieProperties(e, s))
2553                   e.unknown();
2554             }
2555       if (s->track2() == -1)
2556             s->setTrack2(s->track());
2557       }
2558 
2559 //---------------------------------------------------------
2560 //   readTie206
2561 //---------------------------------------------------------
2562 
readTie206(XmlReader & e,Tie * t)2563 void readTie206(XmlReader& e, Tie* t)
2564       {
2565       e.addSpanner(e.intAttribute("id"), t);
2566       while (e.readNextStartElement()) {
2567             if (readSlurTieProperties(e, t))
2568                   ;
2569             else
2570                   e.unknown();
2571             }
2572       if (t->score()->mscVersion() <= 114 && t->spannerSegments().size() == 1) {
2573             // ignore manual adjustments to single-segment ties in older scores
2574             TieSegment* ss = t->frontSegment();
2575             QPointF zeroP;
2576             ss->ups(Grip::START).off     = zeroP;
2577             ss->ups(Grip::BEZIER1).off   = zeroP;
2578             ss->ups(Grip::BEZIER2).off   = zeroP;
2579             ss->ups(Grip::END).off       = zeroP;
2580             ss->setOffset(zeroP);
2581             ss->setUserOff2(zeroP);
2582             }
2583       }
2584 
2585 //---------------------------------------------------------
2586 //   readMeasure
2587 //---------------------------------------------------------
2588 
readMeasure(Measure * m,int staffIdx,XmlReader & e)2589 static void readMeasure(Measure* m, int staffIdx, XmlReader& e)
2590       {
2591       Segment* segment = 0;
2592       qreal _spatium = m->spatium();
2593       Score* score = m->score();
2594 
2595       QList<Chord*> graceNotes;
2596       e.tuplets().clear();
2597       e.setTrack(staffIdx * VOICES);
2598 
2599       m->createStaves(staffIdx);
2600 
2601       // tick is obsolete
2602       if (e.hasAttribute("tick"))
2603             e.setTick(Fraction::fromTicks(score->fileDivision(e.intAttribute("tick"))));
2604 
2605       bool irregular;
2606       if (e.hasAttribute("len")) {
2607             QStringList sl = e.attribute("len").split('/');
2608             if (sl.size() == 2)
2609                   m->setTicks(Fraction(sl[0].toInt(), sl[1].toInt()));
2610             else
2611                   qDebug("illegal measure size <%s>", qPrintable(e.attribute("len")));
2612             irregular = true;
2613             score->sigmap()->add(m->tick().ticks(), SigEvent(m->ticks(), m->timesig()));
2614             score->sigmap()->add(m->endTick().ticks(), SigEvent(m->timesig()));
2615             }
2616       else
2617             irregular = false;
2618 
2619       Staff* staff = score->staff(staffIdx);
2620       Fraction timeStretch(staff->timeStretch(m->tick()));
2621 
2622       // keep track of tick of previous element
2623       // this allows markings that need to apply to previous element to do so
2624       // even though we may have already advanced to next tick position
2625       Fraction lastTick = e.tick();
2626 
2627       while (e.readNextStartElement()) {
2628             const QStringRef& tag(e.name());
2629 
2630             if (tag == "move")
2631                   e.setTick(e.readFraction() + m->tick());
2632             else if (tag == "tick") {
2633                   e.setTick(Fraction::fromTicks(score->fileDivision(e.readInt())));
2634                   lastTick = e.tick();
2635                   }
2636             else if (tag == "BarLine") {
2637                   Fermata* fermataAbove = nullptr;
2638                   Fermata* fermataBelow = nullptr;
2639                   BarLine* bl = new BarLine(score);
2640                   bl->setTrack(e.track());
2641                   while (e.readNextStartElement()) {
2642                         const QStringRef& t(e.name());
2643                         if (t == "subtype")
2644                               bl->setBarLineType(e.readElementText());
2645                         else if (t == "customSubtype")                      // obsolete
2646                               e.readInt();
2647                         else if (t == "span") {
2648                               //TODO bl->setSpanFrom(e.intAttribute("from", bl->spanFrom()));  // obsolete
2649                               // bl->setSpanTo(e.intAttribute("to", bl->spanTo()));            // obsolete
2650                               int span = e.readInt();
2651                               if (span)
2652                                     span--;
2653                               bl->setSpanStaff(span);
2654                               }
2655                         else if (t == "spanFromOffset")
2656                               bl->setSpanFrom(e.readInt());
2657                         else if (t == "spanToOffset")
2658                               bl->setSpanTo(e.readInt());
2659                         else if (t == "Articulation") {
2660                               Element* el = readArticulation(bl, e);
2661                               if (el->isFermata()) {
2662                                     if (el->placement() == Placement::ABOVE)
2663                                           fermataAbove = toFermata(el);
2664                                     else {
2665                                           fermataBelow = toFermata(el);
2666                                           fermataBelow->setTrack((bl->staffIdx() + bl->spanStaff()) * VOICES);
2667                                           }
2668                                     }
2669                               else
2670                                     bl->add(el);
2671                               }
2672                         else if (!bl->Element::readProperties(e))
2673                               e.unknown();
2674                         }
2675                   //
2676                   //  StartRepeatBarLine: always at the beginning tick of a measure, always BarLineType::START_REPEAT
2677                   //  BarLine:            in the middle of a measure, has no semantic
2678                   //  EndBarLine:         at the end tick of a measure
2679                   //  BeginBarLine:       first segment of a measure
2680 
2681                   SegmentType st;
2682                   if ((e.tick() != m->tick()) && (e.tick() != m->endTick()))
2683                         st = SegmentType::BarLine;
2684                   else if (bl->barLineType() == BarLineType::START_REPEAT && e.tick() == m->tick())
2685                         st = SegmentType::StartRepeatBarLine;
2686                   else if (e.tick() == m->tick() && segment == 0)
2687                         st = SegmentType::BeginBarLine;
2688                   else
2689                         st = SegmentType::EndBarLine;
2690                   segment = m->getSegment(st, e.tick());
2691                   segment->add(bl);
2692                   bl->layout();
2693                   if (fermataAbove)
2694                         segment->add(fermataAbove);
2695                   if (fermataBelow)
2696                         segment->add(fermataBelow);
2697                   }
2698             else if (tag == "Chord") {
2699                   Chord* chord = new Chord(score);
2700                   chord->setTrack(e.track());
2701                   segment = m->getSegment(SegmentType::ChordRest, e.tick());
2702                   chord->setParent(segment);
2703                   readChord(chord, e);
2704                   if (chord->noteType() != NoteType::NORMAL)
2705                         graceNotes.push_back(chord);
2706                   else {
2707                         segment->add(chord);
2708                         for (int i = 0; i < graceNotes.size(); ++i) {
2709                               Chord* gc = graceNotes[i];
2710                               gc->setGraceIndex(i);
2711                               chord->add(gc);
2712                               }
2713                         graceNotes.clear();
2714                         Fraction crticks = chord->actualTicks();
2715                         lastTick         = e.tick();
2716                         e.incTick(crticks);
2717                         }
2718                   }
2719             else if (tag == "Rest") {
2720                   Rest* rest = new Rest(score);
2721                   rest->setDurationType(TDuration::DurationType::V_MEASURE);
2722                   rest->setTicks(m->timesig()/timeStretch);
2723                   rest->setTrack(e.track());
2724                   segment = m->getSegment(SegmentType::ChordRest, e.tick());
2725                   rest->setParent(segment);
2726                   readRest(rest, e);
2727                   segment->add(rest);
2728 
2729                   if (!rest->ticks().isValid())     // hack
2730                         rest->setTicks(m->timesig()/timeStretch);
2731 
2732                   lastTick = e.tick();
2733                   e.incTick(rest->actualTicks());
2734                   }
2735             else if (tag == "Breath") {
2736                   Breath* breath = new Breath(score);
2737                   breath->setTrack(e.track());
2738                   breath->setPlacement(Placement::ABOVE);
2739                   Fraction tick = e.tick();
2740                   breath->read(e);
2741                   // older scores placed the breath segment right after the chord to which it applies
2742                   // rather than before the next chordrest segment with an element for the staff
2743                   // result would be layout too far left if there are other segments due to notes in other staves
2744                   // we need to find tick of chord to which this applies, and add its duration
2745                   Fraction prevTick;
2746                   if (e.tick() < tick)
2747                         prevTick = e.tick();    // use our own tick if we explicitly reset to earlier position
2748                   else
2749                         prevTick = lastTick;    // otherwise use tick of previous tick/chord/rest tag
2750                   // find segment
2751                   Segment* prev = m->findSegment(SegmentType::ChordRest, prevTick);
2752                   if (prev) {
2753                         // find chordrest
2754                         ChordRest* lastCR = toChordRest(prev->element(e.track()));
2755                         if (lastCR)
2756                               tick = prevTick + lastCR->actualTicks();
2757                         }
2758                   segment = m->getSegment(SegmentType::Breath, tick);
2759                   segment->add(breath);
2760                   }
2761             else if (tag == "endSpanner") {
2762                   int id = e.attribute("id").toInt();
2763                   Spanner* spanner = e.findSpanner(id);
2764                   if (spanner) {
2765                         spanner->setTicks(e.tick() - spanner->tick());
2766                         // if (spanner->track2() == -1)
2767                               // the absence of a track tag [?] means the
2768                               // track is the same as the beginning of the slur
2769                         if (spanner->track2() == -1)
2770                               spanner->setTrack2(spanner->track() ? spanner->track() : e.track());
2771                         }
2772                   else {
2773                         // remember "endSpanner" values
2774                         SpannerValues sv;
2775                         sv.spannerId = id;
2776                         sv.track2    = e.track();
2777                         sv.tick2     = e.tick();
2778                         e.addSpannerValues(sv);
2779                         }
2780                   e.readNext();
2781                   }
2782             else if (tag == "Slur") {
2783                   Slur *sl = new Slur(score);
2784                   sl->setTick(e.tick());
2785                   readSlur206(e, sl);
2786                   //
2787                   // check if we already saw "endSpanner"
2788                   //
2789                   int id = e.spannerId(sl);
2790                   const SpannerValues* sv = e.spannerValues(id);
2791                   if (sv) {
2792                         sl->setTick2(sv->tick2);
2793                         sl->setTrack2(sv->track2);
2794                         }
2795                   score->addSpanner(sl);
2796                   }
2797             else if (tag == "HairPin"
2798                || tag == "Pedal"
2799                || tag == "Ottava"
2800                || tag == "Trill"
2801                || tag == "TextLine"
2802                || tag == "Volta") {
2803                   Spanner* sp = toSpanner(Element::name2Element(tag, score));
2804                   sp->setTrack(e.track());
2805                   sp->setTick(e.tick());
2806                   sp->eraseSpannerSegments();
2807                   e.addSpanner(e.intAttribute("id", -1), sp);
2808 
2809                   if (tag == "Volta")
2810                         readVolta206(e, toVolta(sp));
2811                   else if (tag == "Pedal")
2812                         readPedal(e, toPedal(sp));
2813                   else if (tag == "Ottava")
2814                         readOttava(e, toOttava(sp));
2815                   else if (tag == "HairPin")
2816                         readHairpin206(e, toHairpin(sp));
2817                   else if (tag == "Trill")
2818                         readTrill206(e, toTrill(sp));
2819                   else
2820                         readTextLine206(e, toTextLineBase(sp));
2821                   score->addSpanner(sp);
2822                   //
2823                   // check if we already saw "endSpanner"
2824                   //
2825                   int id = e.spannerId(sp);
2826                   const SpannerValues* sv = e.spannerValues(id);
2827                   if (sv) {
2828                         sp->setTicks(sv->tick2 - sp->tick());
2829                         sp->setTrack2(sv->track2);
2830                         }
2831                   }
2832             else if (tag == "RepeatMeasure") {
2833                   RepeatMeasure* rm = new RepeatMeasure(score);
2834                   rm->setTrack(e.track());
2835                   readRest(rm, e);
2836                   segment = m->getSegment(SegmentType::ChordRest, e.tick());
2837                   segment->add(rm);
2838                   lastTick = e.tick();
2839                   e.incTick(m->ticks());
2840                   }
2841             else if (tag == "Clef") {
2842                   Clef* clef = new Clef(score);
2843                   clef->setTrack(e.track());
2844                   clef->read(e);
2845                   clef->setGenerated(false);
2846                   if (e.tick().isZero()) {
2847                         if (score->staff(staffIdx)->clef(Fraction(0,1)) != clef->clefType())
2848                               score->staff(staffIdx)->setDefaultClefType(clef->clefType());
2849                         if (clef->links() && clef->links()->size() == 1) {
2850                               e.linkIds().remove(clef->links()->lid());
2851                               qDebug("remove link %d", clef->links()->lid());
2852                               }
2853                         delete clef;
2854                         continue;
2855                         }
2856                   // there may be more than one clef segment for same tick position
2857                   if (!segment) {
2858                         // this is the first segment of measure
2859                         segment = m->getSegment(SegmentType::Clef, e.tick());
2860                         }
2861                   else {
2862                         bool firstSegment = false;
2863                         // the first clef may be missing and is added later in layout
2864                         for (Segment* s = m->segments().first(); s && s->tick() == e.tick(); s = s->next()) {
2865                               if (s->segmentType() == SegmentType::Clef
2866                                     // hack: there may be other segment types which should
2867                                     // generate a clef at current position
2868                                  || s->segmentType() == SegmentType::StartRepeatBarLine
2869                                  ) {
2870                                     firstSegment = true;
2871                                     break;
2872                                     }
2873                               }
2874                         if (firstSegment) {
2875                               Segment* ns = 0;
2876                               if (segment->next()) {
2877                                     ns = segment->next();
2878                                     while (ns && ns->tick() < e.tick())
2879                                           ns = ns->next();
2880                                     }
2881                               segment = 0;
2882                               for (Segment* s = ns; s && s->tick() == e.tick(); s = s->next()) {
2883                                     if (s->segmentType() == SegmentType::Clef) {
2884                                           segment = s;
2885                                           break;
2886                                           }
2887                                     }
2888                               if (!segment) {
2889                                     segment = new Segment(m, SegmentType::Clef, e.tick() - m->tick());
2890                                     m->segments().insert(segment, ns);
2891                                     }
2892                               }
2893                         else {
2894                               // this is the first clef: move to left
2895                               segment = m->getSegment(SegmentType::Clef, e.tick());
2896                               }
2897                         }
2898                   if (e.tick() != m->tick())
2899                         clef->setSmall(true);         // TODO: layout does this ?
2900                   segment->add(clef);
2901                   }
2902             else if (tag == "TimeSig") {
2903                   TimeSig* ts = new TimeSig(score);
2904                   ts->setTrack(e.track());
2905                   ts->read(e);
2906                   // if time sig not at beginning of measure => courtesy time sig
2907                   Fraction currTick = e.tick();
2908                   bool courtesySig = (currTick > m->tick());
2909                   if (courtesySig) {
2910                         // if courtesy sig., just add it without map processing
2911                         segment = m->getSegment(SegmentType::TimeSigAnnounce, currTick);
2912                         segment->add(ts);
2913                         }
2914                   else {
2915                         // if 'real' time sig., do full process
2916                         segment = m->getSegment(SegmentType::TimeSig, currTick);
2917                         segment->add(ts);
2918 
2919                         timeStretch = ts->stretch().reduced();
2920                         m->setTimesig(ts->sig() / timeStretch);
2921 
2922                         if (irregular) {
2923                               score->sigmap()->add(m->tick().ticks(), SigEvent(m->ticks(), m->timesig()));
2924                               score->sigmap()->add(m->endTick().ticks(), SigEvent(m->timesig()));
2925                               }
2926                         else {
2927                               m->setTicks(m->timesig());
2928                               score->sigmap()->add(m->tick().ticks(), SigEvent(m->timesig()));
2929                               }
2930                         }
2931                   }
2932             else if (tag == "KeySig") {
2933                   KeySig* ks = new KeySig(score);
2934                   ks->setTrack(e.track());
2935                   ks->read(e);
2936                   Fraction curTick = e.tick();
2937                   if (!ks->isCustom() && !ks->isAtonal() && ks->key() == Key::C && curTick.isZero()) {
2938                         // ignore empty key signature
2939                         qDebug("remove keysig c at tick 0");
2940                         if (ks->links()) {
2941                               if (ks->links()->size() == 1)
2942                                     e.linkIds().remove(ks->links()->lid());
2943                               }
2944                         delete ks;
2945                         }
2946                   else {
2947                         // if key sig not at beginning of measure => courtesy key sig
2948                         bool courtesySig = (curTick == m->endTick());
2949                         segment = m->getSegment(courtesySig ? SegmentType::KeySigAnnounce : SegmentType::KeySig, curTick);
2950                         segment->add(ks);
2951                         if (!courtesySig)
2952                               staff->setKey(curTick, ks->keySigEvent());
2953                         }
2954                   }
2955             else if (tag == "Text" || tag == "StaffText") {
2956                   // MuseScore 3 has different types for system text and
2957                   // staff text while MuseScore 2 didn't.
2958                   // We need to decide first which one we should create.
2959                   TextReaderContext206 ctx(e);
2960                   QString styleName = ReadStyleName206(ctx.tag());
2961                   StaffTextBase* t;
2962                   if (styleName == "System"   || styleName == "Tempo"
2963                      || styleName == "Marker" || styleName == "Jump"
2964                      || styleName == "Volta") // TODO: is it possible to get it from style?
2965                         t = new SystemText(score);
2966                   else
2967                         t = new StaffText(score);
2968                   t->setTrack(e.track());
2969                   readTextPropertyStyle206(ctx.tag(), e, t, t);
2970                   while (ctx.reader().readNextStartElement()) {
2971                         if (!readTextProperties206(ctx.reader(), t))
2972                               ctx.reader().unknown();
2973                         }
2974                   if (t->empty()) {
2975                         if (t->links()) {
2976                               if (t->links()->size() == 1) {
2977                                     qDebug("reading empty text: deleted lid = %d", t->links()->lid());
2978                                     ctx.reader().linkIds().remove(t->links()->lid());
2979                                     delete t;
2980                                     }
2981                               }
2982                         }
2983                   else {
2984 #if 0
2985                         // This code was added at commit ed5b615
2986                         // but it seems to no longer be appropriate.
2987                         // autoplace is usually true,
2988                         // exception is text within staff,
2989                         // and in this case offset is already correct without further adjustment.
2990                         if (!t->autoplace()) {
2991                               // adjust position
2992                               qreal userY = t->offset().y() / t->spatium();
2993                               qreal yo = -(-2.0 - userY) * t->spatium();
2994                               t->layout();
2995                               t->setAlign(Align::LEFT | Align::TOP);
2996                               t->ryoffset() = yo;
2997                               }
2998 #endif
2999                         segment = m->getSegment(SegmentType::ChordRest, ctx.reader().tick());
3000                         segment->add(t);
3001                         }
3002                   }
3003 
3004             //----------------------------------------------------
3005             // Annotation
3006 
3007             else if (tag == "Dynamic") {
3008                   Dynamic* dyn = new Dynamic(score);
3009                   dyn->setTrack(e.track());
3010                   readDynamic(dyn, e);
3011                   segment = m->getSegment(SegmentType::ChordRest, e.tick());
3012                   segment->add(dyn);
3013                   }
3014             else if (tag == "RehearsalMark") {
3015                   RehearsalMark* el = new RehearsalMark(score);
3016                   el->setTrack(e.track());
3017                   readText206(e, el, el);
3018 //                  el->setOffset(el->offset() - el->score()->styleValue(Pid::OFFSET, Sid::rehearsalMarkPosAbove).toPointF());
3019 //                  if (el->offset().isNull())
3020 //                        el->setAutoplace(true);
3021                   segment = m->getSegment(SegmentType::ChordRest, e.tick());
3022                   segment->add(el);
3023                   }
3024 #if 0
3025             else if (tag == "StaffText") {
3026                   StaffText* el = new StaffText(score);
3027                   el->setTrack(e.track());
3028 
3029                   while (e.readNextStartElement()) {
3030                         const QStringRef& tag(e.name());
3031                         if (tag == "foregroundColor")
3032                               e.skipCurrentElement();
3033                         else if (!el->readProperties(e))
3034                               e.unknown();
3035                         }
3036                   TextBase* tt = static_cast<TextBase*>(el);
3037                   tt->setXmlText(tt->xmlText().replace("<sym>unicode", "<sym>met"));
3038                   segment = m->getSegment(SegmentType::ChordRest, e.tick());
3039                   segment->add(el);
3040                   }
3041 #endif
3042             else if (tag == "Harmony"
3043                || tag == "FretDiagram"
3044                || tag == "TremoloBar"
3045                || tag == "Symbol"
3046                || tag == "InstrumentChange"
3047                || tag == "StaffState"
3048                || tag == "FiguredBass"
3049                ) {
3050                   Element* el = Element::name2Element(tag, score);
3051                   // hack - needed because tick tags are unreliable in 1.3 scores
3052                   // for symbols attached to anything but a measure
3053                   el->setTrack(e.track());
3054                   el->read(e);
3055                   if (el->staff() && (el->isHarmony() || el->isFretDiagram() || el->isInstrumentChange()))
3056                         adjustPlacement(el);
3057                   segment = m->getSegment(SegmentType::ChordRest, e.tick());
3058                   segment->add(el);
3059                   }
3060             else if (tag == "Tempo") {
3061                   TempoText* tt = new TempoText(score);
3062                   // hack - needed because tick tags are unreliable in 1.3 scores
3063                   // for symbols attached to anything but a measure
3064                   tt->setTrack(e.track());
3065                   readTempoText(tt, e);
3066                   segment = m->getSegment(SegmentType::ChordRest, e.tick());
3067                   segment->add(tt);
3068                   }
3069             else if (tag == "Marker" || tag == "Jump") {
3070                   Element* el = Element::name2Element(tag, score);
3071                   el->setTrack(e.track());
3072                   if (tag == "Marker") {
3073                         Marker* ma = toMarker(el);
3074                         readMarker(ma, e);
3075                         Element* markerEl = toElement(ma);
3076                         m->add(markerEl);
3077                         }
3078                   else {
3079                         el->read(e);
3080                         m->add(el);
3081                         }
3082                   }
3083             else if (tag == "Image") {
3084                   if (MScore::noImages)
3085                         e.skipCurrentElement();
3086                   else {
3087                         Element* el = Element::name2Element(tag, score);
3088                         el->setTrack(e.track());
3089                         el->read(e);
3090                         segment = m->getSegment(SegmentType::ChordRest, e.tick());
3091                         segment->add(el);
3092                         }
3093                   }
3094             //----------------------------------------------------
3095             else if (tag == "stretch") {
3096                   double val = e.readDouble();
3097                   if (val < 0.0)
3098                         val = 0;
3099                   m->setUserStretch(val);
3100                   }
3101             else if (tag == "noOffset")
3102                   m->setNoOffset(e.readInt());
3103             else if (tag == "measureNumberMode")
3104                   m->setMeasureNumberMode(MeasureNumberMode(e.readInt()));
3105             else if (tag == "irregular")
3106                   m->setIrregular(e.readBool());
3107             else if (tag == "breakMultiMeasureRest")
3108                   m->setBreakMultiMeasureRest(e.readBool());
3109             else if (tag == "sysInitBarLineType") {
3110                   const QString& val(e.readElementText());
3111                   BarLine* barLine = new BarLine(score);
3112                   barLine->setTrack(e.track());
3113                   barLine->setBarLineType(val);
3114                   segment = m->getSegment(SegmentType::BeginBarLine, m->tick());
3115                   segment->add(barLine);
3116                   }
3117             else if (tag == "Tuplet") {
3118                   Tuplet* tuplet = new Tuplet(score);
3119                   tuplet->setTrack(e.track());
3120                   tuplet->setTick(e.tick());
3121                   tuplet->setParent(m);
3122                   readTuplet(tuplet, e);
3123                   e.addTuplet(tuplet);
3124                   }
3125             else if (tag == "startRepeat") {
3126                   m->setRepeatStart(true);
3127                   e.readNext();
3128                   }
3129             else if (tag == "endRepeat") {
3130                   m->setRepeatCount(e.readInt());
3131                   m->setRepeatEnd(true);
3132                   }
3133             else if (tag == "vspacer" || tag == "vspacerDown") {
3134                   if (!m->vspacerDown(staffIdx)) {
3135                         Spacer* spacer = new Spacer(score);
3136                         spacer->setSpacerType(SpacerType::DOWN);
3137                         spacer->setTrack(staffIdx * VOICES);
3138                         m->add(spacer);
3139                         }
3140                   m->vspacerDown(staffIdx)->setGap(e.readDouble() * _spatium);
3141                   }
3142             else if (tag == "vspacer" || tag == "vspacerUp") {
3143                   if (!m->vspacerUp(staffIdx)) {
3144                         Spacer* spacer = new Spacer(score);
3145                         spacer->setSpacerType(SpacerType::UP);
3146                         spacer->setTrack(staffIdx * VOICES);
3147                         m->add(spacer);
3148                         }
3149                   m->vspacerUp(staffIdx)->setGap(e.readDouble() * _spatium);
3150                   }
3151             else if (tag == "visible")
3152                   m->setStaffVisible(staffIdx, e.readInt());
3153             else if (tag == "slashStyle")
3154                   m->setStaffStemless(staffIdx, e.readInt());
3155             else if (tag == "Beam") {
3156                   Beam* beam = new Beam(score);
3157                   beam->setTrack(e.track());
3158                   beam->read(e);
3159                   beam->setParent(0);
3160                   e.addBeam(beam);
3161                   }
3162             else if (tag == "Segment") {
3163                   if (segment) segment->read(e);
3164                   else e.unknown();
3165                   }
3166             else if (tag == "MeasureNumber") {
3167                   MeasureNumber* noText = new MeasureNumber(score);
3168                   readText206(e, noText, m);
3169                   noText->setTrack(e.track());
3170                   noText->setParent(m);
3171                   m->setNoText(noText->staffIdx(), noText);
3172                   }
3173             else if (tag == "SystemDivider") {
3174                   SystemDivider* sd = new SystemDivider(score);
3175                   sd->read(e);
3176                   m->add(sd);
3177                   }
3178             else if (tag == "Ambitus") {
3179                   Ambitus* range = new Ambitus(score);
3180                   readAmbitus(range, e);
3181                   segment = m->getSegment(SegmentType::Ambitus, e.tick());
3182                   range->setParent(segment);          // a parent segment is needed for setTrack() to work
3183                   range->setTrack(trackZeroVoice(e.track()));
3184                   segment->add(range);
3185                   }
3186             else if (tag == "multiMeasureRest") {
3187                   m->setMMRestCount(e.readInt());
3188                   // set tick to previous measure
3189                   m->setTick(e.lastMeasure()->tick());
3190                   e.setTick(e.lastMeasure()->tick());
3191                   }
3192             else if (m->MeasureBase::readProperties(e))
3193                   ;
3194             else
3195                   e.unknown();
3196             }
3197       e.checkTuplets();
3198       m->connectTremolo();
3199       }
3200 
3201 //---------------------------------------------------------
3202 //   readBox
3203 //---------------------------------------------------------
3204 
readBox(Box * b,XmlReader & e)3205 static void readBox(Box* b, XmlReader& e)
3206       {
3207       b->setLeftMargin(0.0);
3208       b->setRightMargin(0.0);
3209       b->setTopMargin(0.0);
3210       b->setBottomMargin(0.0);
3211       b->setTopGap(0.0);
3212       b->setBottomGap(0.0);
3213       b->setAutoSizeEnabled(false);
3214       b->setPropertyFlags(Pid::TOP_GAP, PropertyFlags::UNSTYLED);
3215       b->setPropertyFlags(Pid::BOTTOM_GAP, PropertyFlags::UNSTYLED);
3216 
3217       b->setBoxHeight(Spatium(0));     // override default set in constructor
3218       b->setBoxWidth(Spatium(0));
3219       bool keepMargins = false;        // whether original margins have to be kept when reading old file
3220 
3221       while (e.readNextStartElement()) {
3222             const QStringRef& tag(e.name());
3223             if (tag == "HBox") {
3224                   HBox* hb = new HBox(b->score());
3225                   hb->read(e);
3226                   b->add(hb);
3227                   keepMargins = true;     // in old file, box nesting used outer box margins
3228                   }
3229             else if (tag == "VBox") {
3230                   VBox* vb = new VBox(b->score());
3231                   vb->read(e);
3232                   b->add(vb);
3233                   keepMargins = true;     // in old file, box nesting used outer box margins
3234                   }
3235             else if (tag == "Text") {
3236                   Text* t;
3237                   if (b->isTBox()) {
3238                         t = toTBox(b)->text();
3239                         readText206(e, t, t);
3240                         }
3241                   else {
3242                         t = new Text(b->score());
3243                         readText206(e, t, t);
3244                         if (t->empty()) {
3245                               qDebug("read empty text");
3246                               }
3247                         else
3248                               b->add(t);
3249                         }
3250                   }
3251             else if (!b->readProperties(e))
3252                   e.unknown();
3253             }
3254 
3255       // with .msc versions prior to 1.17, box margins were only used when nesting another box inside this box:
3256       // for backward compatibility set them to 0 in all other cases
3257 
3258       if (b->score()->mscVersion() <= 114 && (b->isHBox() || b->isVBox()) && !keepMargins)  {
3259             b->setLeftMargin(0.0);
3260             b->setRightMargin(0.0);
3261             b->setTopMargin(0.0);
3262             b->setBottomMargin(0.0);
3263             }
3264       }
3265 
3266 //---------------------------------------------------------
3267 //   readStaffContent
3268 //---------------------------------------------------------
3269 
readStaffContent(Score * score,XmlReader & e)3270 static void readStaffContent(Score* score, XmlReader& e)
3271       {
3272       int staff = e.intAttribute("id", 1) - 1;
3273       e.setTick(Fraction(0,1));
3274       e.setTrack(staff * VOICES);
3275       Box* lastReadBox = nullptr;
3276       bool readMeasureLast = false;
3277 
3278       if (staff == 0) {
3279             while (e.readNextStartElement()) {
3280                   const QStringRef& tag(e.name());
3281 
3282                   if (tag == "Measure") {
3283                         if (lastReadBox) {
3284                               lastReadBox->setBottomGap(lastReadBox->bottomGap() + lastReadBox->propertyDefault(Pid::BOTTOM_GAP).toReal());
3285                               lastReadBox = nullptr;
3286                               }
3287                         readMeasureLast = true;
3288 
3289                         Measure* measure = 0;
3290                         measure = new Measure(score);
3291                         measure->setTick(e.tick());
3292                         //
3293                         // inherit timesig from previous measure
3294                         //
3295                         Measure* m = e.lastMeasure(); // measure->prevMeasure();
3296                         Fraction f(m ? m->timesig() : Fraction(4,4));
3297                         measure->setTicks(f);
3298                         measure->setTimesig(f);
3299 
3300                         readMeasure(measure, staff, e);
3301                         measure->checkMeasure(staff);
3302                         if (!measure->isMMRest()) {
3303                               score->measures()->add(measure);
3304                               e.setLastMeasure(measure);
3305                               e.setTick(measure->endTick());
3306                               }
3307                         else {
3308                               // this is a multi measure rest
3309                               // always preceded by the first measure it replaces
3310                               Measure* lm = e.lastMeasure();
3311 
3312                               if (lm) {
3313                                     lm->setMMRest(measure);
3314                                     measure->setTick(lm->tick());
3315                                     }
3316                               }
3317                         }
3318                   else if (tag == "HBox" || tag == "VBox" || tag == "TBox" || tag == "FBox") {
3319                         Box* b = toBox(Element::name2Element(tag, score));
3320                         readBox(b, e);
3321                         b->setTick(e.tick());
3322                         score->measures()->add(b);
3323 
3324                         // If it's the first box, and comes before any measures, reset to
3325                         // 301 default.
3326                         if (!readMeasureLast && !lastReadBox) {
3327                               b->setTopGap(b->propertyDefault(Pid::TOP_GAP).toReal());
3328                               b->setPropertyFlags(Pid::TOP_GAP, PropertyFlags::STYLED);
3329                               }
3330                         else if (readMeasureLast)
3331                               b->setTopGap(b->topGap() + b->propertyDefault(Pid::TOP_GAP).toReal());
3332 
3333                         lastReadBox = b;
3334                         readMeasureLast = false;
3335                         }
3336                   else if (tag == "tick")
3337                         e.setTick(Fraction::fromTicks(score->fileDivision(e.readInt())));
3338                   else
3339                         e.unknown();
3340                   }
3341             }
3342       else {
3343             Measure* measure = score->firstMeasure();
3344             while (e.readNextStartElement()) {
3345                   const QStringRef& tag(e.name());
3346 
3347                   if (tag == "Measure") {
3348                         if (measure == 0) {
3349                               qDebug("Score::readStaff(): missing measure!");
3350                               measure = new Measure(score);
3351                               measure->setTick(e.tick());
3352                               score->measures()->add(measure);
3353                               }
3354                         e.setTick(measure->tick());
3355                         readMeasure(measure, staff, e);
3356                         measure->checkMeasure(staff);
3357                         if (measure->isMMRest())
3358                               measure = e.lastMeasure()->nextMeasure();
3359                         else {
3360                               e.setLastMeasure(measure);
3361                               if (measure->mmRest())
3362                                     measure = measure->mmRest();
3363                               else
3364                                     measure = measure->nextMeasure();
3365                               }
3366                         }
3367                   else if (tag == "tick")
3368                         e.setTick(Fraction::fromTicks(score->fileDivision(e.readInt())));
3369                   else
3370                         e.unknown();
3371                   }
3372             }
3373       }
3374 
3375 //---------------------------------------------------------
3376 //   readStyle
3377 //---------------------------------------------------------
3378 
readStyle(MStyle * style,XmlReader & e)3379 static void readStyle(MStyle* style, XmlReader& e)
3380       {
3381       QString oldChordDescriptionFile = style->value(Sid::chordDescriptionFile).toString();
3382       bool chordListTag = false;
3383       excessTextStyles206.clear();
3384       while (e.readNextStartElement()) {
3385             QString tag = e.name().toString();
3386             if (tag == "TextStyle")
3387                   readTextStyle206(style, e, excessTextStyles206);
3388             else if (tag == "Spatium")
3389                   style->set(Sid::spatium, e.readDouble() * DPMM);
3390             else if (tag == "page-layout")
3391                   readPageFormat(style, e);
3392             else if (tag == "displayInConcertPitch")
3393                   style->set(Sid::concertPitch, QVariant(bool(e.readInt())));
3394             else if (tag == "pedalY") {
3395                   qreal y = e.readDouble();
3396                   style->set(Sid::pedalPosBelow, QPointF(0.0, y));
3397                   }
3398             else if (tag == "lyricsDistance") {
3399                   qreal y = e.readDouble();
3400                   style->set(Sid::lyricsPosBelow, QPointF(0.0, y));
3401                   }
3402             else if (tag == "lyricsMinBottomDistance") {
3403                   // no longer meaningful since it is now measured from skyline rather than staff
3404                   //style->set(Sid::lyricsMinBottomDistance, QPointF(0.0, y));
3405                   e.skipCurrentElement();
3406                   }
3407             else if (tag == "ottavaHook") {
3408                   qreal y = qAbs(e.readDouble());
3409                   style->set(Sid::ottavaHookAbove, y);
3410                   style->set(Sid::ottavaHookBelow, -y);
3411                   }
3412             else if (tag == "endBarDistance") {
3413                   double d = e.readDouble();
3414                   d += style->value(Sid::barWidth).toDouble();
3415                   d += style->value(Sid::endBarWidth).toDouble();
3416                   style->set(Sid::endBarDistance, QVariant(d));
3417                   }
3418             else if (tag == "ChordList") {
3419                   style->chordList()->clear();
3420                   style->chordList()->read(e);
3421                   style->setCustomChordList(true);
3422                   for (ChordFont f : style->chordList()->fonts) {
3423                         if (f.family == "MuseJazz") {
3424                               f.family = "MuseJazz Text";
3425                               }
3426                         }
3427                   chordListTag = true;
3428                   }
3429             else if (tag == "harmonyY") {
3430                   qreal val = -e.readDouble();
3431                   if (val > 0.0) {
3432                         style->set(Sid::harmonyPlacement, int(Placement::BELOW));
3433                         style->set(Sid::chordSymbolAPosBelow,  QPointF(.0, val));
3434                         }
3435                   else {
3436                         style->set(Sid::harmonyPlacement, int(Placement::ABOVE));
3437                         style->set(Sid::chordSymbolAPosBelow,  QPointF(.0, val));
3438                         }
3439                   }
3440             else {
3441                   if (!style->readProperties(e)) {
3442                         e.skipCurrentElement();
3443                         }
3444                   }
3445             }
3446 
3447       bool disableHarmonyPlay = MScore::harmonyPlayDisableCompatibility && !MScore::testMode;
3448       if (disableHarmonyPlay) {
3449             style->set(Sid::harmonyPlay, false);
3450             }
3451 
3452       // if we just specified a new chord description file
3453       // and didn't encounter a ChordList tag
3454       // then load the chord description file
3455 
3456       QString newChordDescriptionFile = style->value(Sid::chordDescriptionFile).toString();
3457       if (newChordDescriptionFile != oldChordDescriptionFile && !chordListTag) {
3458             if (!newChordDescriptionFile.startsWith("chords_") && style->value(Sid::chordStyle).toString() == "std") {
3459                   // should not normally happen,
3460                   // but treat as "old" (114) score just in case
3461                   style->set(Sid::chordStyle, QVariant(QString("custom")));
3462                   style->set(Sid::chordsXmlFile, QVariant(true));
3463                   qDebug("StyleData::load: custom chord description file %s with chordStyle == std", qPrintable(newChordDescriptionFile));
3464                   }
3465             if (style->value(Sid::chordStyle).toString() == "custom")
3466                   style->setCustomChordList(true);
3467             else
3468                   style->setCustomChordList(false);
3469             style->chordList()->unload();
3470             }
3471 
3472       // make sure we have a chordlist
3473       if (!chordListTag)
3474             style->checkChordList();
3475       }
3476 
3477 //---------------------------------------------------------
3478 //   readScore
3479 //---------------------------------------------------------
3480 
readScore(Score * score,XmlReader & e)3481 static bool readScore(Score* score, XmlReader& e)
3482       {
3483       while (e.readNextStartElement()) {
3484             e.setTrack(-1);
3485             const QStringRef& tag(e.name());
3486             if (tag == "Staff")
3487                   readStaffContent(score, e);
3488             else if (tag == "siglist")
3489                   score->sigmap()->read(e, score->fileDivision());
3490             else if (tag == "Omr") {
3491 #ifdef OMR
3492                   score->masterScore()->setOmr(new Omr(score));
3493                   score->masterScore()->omr()->read(e);
3494 #else
3495                   e.skipCurrentElement();
3496 #endif
3497                   }
3498             else if (tag == "Audio") {
3499                   score->setAudio(new Audio);
3500                   score->audio()->read(e);
3501                   }
3502             else if (tag == "showOmr")
3503                   score->masterScore()->setShowOmr(e.readInt());
3504             else if (tag == "playMode")
3505                   score->setPlayMode(PlayMode(e.readInt()));
3506             else if (tag == "LayerTag") {
3507                   int id = e.intAttribute("id");
3508                   const QString& t = e.attribute("tag");
3509                   QString val(e.readElementText());
3510                   if (id >= 0 && id < 32) {
3511                         score->layerTags()[id] = t;
3512                         score->layerTagComments()[id] = val;
3513                         }
3514                   }
3515             else if (tag == "Layer") {
3516                   Layer layer;
3517                   layer.name = e.attribute("name");
3518                   layer.tags = e.attribute("mask").toUInt();
3519                   score->layer().append(layer);
3520                   e.readNext();
3521                   }
3522             else if (tag == "currentLayer")
3523                   score->setCurrentLayer(e.readInt());
3524             else if (tag == "Synthesizer")
3525                   score->synthesizerState().read(e);
3526             else if (tag == "page-offset")
3527                   score->setPageNumberOffset(e.readInt());
3528             else if (tag == "Division")
3529                   score->setFileDivision(e.readInt());
3530             else if (tag == "showInvisible")
3531                   score->setShowInvisible(e.readInt());
3532             else if (tag == "showUnprintable")
3533                   score->setShowUnprintable(e.readInt());
3534             else if (tag == "showFrames")
3535                   score->setShowFrames(e.readInt());
3536             else if (tag == "showMargins")
3537                   score->setShowPageborders(e.readInt());
3538             else if (tag == "Style") {
3539                   qreal sp = score->style().value(Sid::spatium).toDouble();
3540                   readStyle(&score->style(), e);
3541                   if (score->style().value(Sid::MusicalTextFont).toString() == "MuseJazz")
3542                         score->style().set(Sid::MusicalTextFont, "MuseJazz Text");
3543                   // if (_layoutMode == LayoutMode::FLOAT || _layoutMode == LayoutMode::SYSTEM) {
3544                   if (score->layoutMode() == LayoutMode::FLOAT) {
3545                         // style should not change spatium in
3546                         // float mode
3547                         score->style().set(Sid::spatium, sp);
3548                         }
3549                   score->setScoreFont(ScoreFont::fontFactory(score->style().value(Sid::MusicalSymbolFont).toString()));
3550                   }
3551             else if (tag == "copyright" || tag == "rights") {
3552                   Text* text = new Text(score);
3553                   readText206(e, text, text);
3554                   score->setMetaTag("copyright", text->xmlText());
3555                   delete text;
3556                   }
3557             else if (tag == "movement-number")
3558                   score->setMetaTag("movementNumber", e.readElementText());
3559             else if (tag == "movement-title")
3560                   score->setMetaTag("movementTitle", e.readElementText());
3561             else if (tag == "work-number")
3562                   score->setMetaTag("workNumber", e.readElementText());
3563             else if (tag == "work-title")
3564                   score->setMetaTag("workTitle", e.readElementText());
3565             else if (tag == "source")
3566                   score->setMetaTag("source", e.readElementText());
3567             else if (tag == "metaTag") {
3568                   QString name = e.attribute("name");
3569                   score->setMetaTag(name, e.readElementText());
3570                   }
3571             else if (tag == "Part") {
3572                   Part* part = new Part(score);
3573                   readPart206(part, e);
3574                   score->parts().push_back(part);
3575                   }
3576             else if ((tag == "HairPin")   // TODO: do this elements exist here?
3577                 || (tag == "Ottava")
3578                 || (tag == "TextLine")
3579                 || (tag == "Volta")
3580                 || (tag == "Trill")
3581                 || (tag == "Slur")
3582                 || (tag == "Pedal")) {
3583                   Spanner* s = toSpanner(Element::name2Element(tag, score));
3584                   if (tag == "HairPin")
3585                         readHairpin206(e, toHairpin(s));
3586                   else if (tag == "Ottava")
3587                         readOttava(e, toOttava(s));
3588                   else if (tag == "TextLine")
3589                         readTextLine206(e, toTextLine(s));
3590                   else if (tag == "Volta")
3591                         readVolta206(e, toVolta(s));
3592                   else if (tag == "Trill")
3593                         readTrill206(e, toTrill(s));
3594                   else if (tag == "Slur")
3595                         readSlur206(e, toSlur(s));
3596                   else {
3597                         Q_ASSERT(tag == "Pedal");
3598                         readPedal(e, toPedal(s));
3599                         }
3600                   score->addSpanner(s);
3601                   }
3602             else if (tag == "Excerpt") {
3603                   if (MScore::noExcerpts)
3604                         e.skipCurrentElement();
3605                   else {
3606                         if (score->isMaster()) {
3607                               Excerpt* ex = new Excerpt(static_cast<MasterScore*>(score));
3608                               ex->read(e);
3609                               score->excerpts().append(ex);
3610                               }
3611                         else {
3612                               qDebug("read206: readScore(): part cannot have parts");
3613                               e.skipCurrentElement();
3614                               }
3615                         }
3616                   }
3617             else if (tag == "Score") {          // recursion
3618                   if (MScore::noExcerpts)
3619                         e.skipCurrentElement();
3620                   else {
3621                         e.tracks().clear();
3622                         e.clearUserTextStyles();
3623                         MasterScore* m = score->masterScore();
3624                         Score* s = new Score(m, m->style());
3625                         s->setEnableVerticalSpread(false);
3626                         Excerpt* ex = new Excerpt(m);
3627 
3628                         ex->setPartScore(s);
3629                         e.setLastMeasure(nullptr);
3630                         readScore(s, e);
3631                         ex->setTracks(e.tracks());
3632                         m->addExcerpt(ex);
3633                         }
3634                   }
3635             else if (tag == "PageList")
3636                   e.skipCurrentElement();
3637             else if (tag == "name") {
3638                   QString n = e.readElementText();
3639                   if (!score->isMaster())             //ignore the name if it's not a child score
3640                         score->excerpt()->setTitle(n);
3641                   }
3642             else if (tag == "layoutMode") {
3643                   QString s = e.readElementText();
3644                   if (s == "line")
3645                         score->setLayoutMode(LayoutMode::LINE);
3646                   else if (s == "system")
3647                         score->setLayoutMode(LayoutMode::SYSTEM);
3648                   else
3649                         qDebug("layoutMode: %s", qPrintable(s));
3650                   }
3651             else
3652                   e.unknown();
3653             }
3654       if (e.error() != QXmlStreamReader::NoError) {
3655             qDebug("%s: xml read error at line %lld col %lld: %s",
3656                qPrintable(e.getDocName()), e.lineNumber(), e.columnNumber(),
3657                e.name().toUtf8().data());
3658             MScore::lastError = QObject::tr("XML read error at line %1, column %2: %3").arg(e.lineNumber()).arg(e.columnNumber()).arg(e.name().toString());
3659             return false;
3660             }
3661 
3662       score->connectTies();
3663 
3664       score->setFileDivision(MScore::division);
3665 
3666       //
3667       //    sanity check for barLineSpan
3668       //
3669 #if 0 // TODO:barline
3670       for (Staff* st : score->staves()) {
3671             int barLineSpan = st->barLineSpan();
3672             int idx = st->idx();
3673             int n = score->nstaves();
3674             if (idx + barLineSpan > n) {
3675                   qDebug("bad span: idx %d  span %d staves %d", idx, barLineSpan, n);
3676                   // span until last staff
3677                   barLineSpan = n - idx;
3678                   st->setBarLineSpan(barLineSpan);
3679                   }
3680             else if (idx == 0 && barLineSpan == 0) {
3681                   qDebug("bad span: idx %d  span %d staves %d", idx, barLineSpan, n);
3682                   // span from the first staff until the start of the next span
3683                   barLineSpan = 1;
3684                   for (int i = 1; i < n; ++i) {
3685                         if (score->staff(i)->barLineSpan() == 0)
3686                               ++barLineSpan;
3687                         else
3688                               break;
3689                         }
3690                   st->setBarLineSpan(barLineSpan);
3691                   }
3692             // check spanFrom
3693             int minBarLineFrom = st->lines(0) == 1 ? BARLINE_SPAN_1LINESTAFF_FROM : MIN_BARLINE_SPAN_FROMTO;
3694             if (st->barLineFrom() < minBarLineFrom)
3695                   st->setBarLineFrom(minBarLineFrom);
3696             if (st->barLineFrom() > st->lines(0) * 2)
3697                   st->setBarLineFrom(st->lines(0) * 2);
3698             // check spanTo
3699             Staff* stTo = st->barLineSpan() <= 1 ? st : score->staff(idx + st->barLineSpan() - 1);
3700             // 1-line staves have special bar line spans
3701             int maxBarLineTo        = stTo->lines(0) == 1 ? BARLINE_SPAN_1LINESTAFF_TO : stTo->lines(0)*2;
3702             int defaultBarLineTo    = stTo->lines(0) == 1 ? BARLINE_SPAN_1LINESTAFF_TO : (stTo->lines(0) - 1) * 2;
3703             if (st->barLineTo() == UNKNOWN_BARLINE_TO)
3704                   st->setBarLineTo(defaultBarLineTo);
3705             if (st->barLineTo() < MIN_BARLINE_SPAN_FROMTO)
3706                   st->setBarLineTo(MIN_BARLINE_SPAN_FROMTO);
3707             if (st->barLineTo() > maxBarLineTo)
3708                   st->setBarLineTo(maxBarLineTo);
3709             // on single staff span, check spanFrom and spanTo are distant enough
3710             if (st->barLineSpan() == 1) {
3711                   if (st->barLineTo() - st->barLineFrom() < MIN_BARLINE_FROMTO_DIST) {
3712                         st->setBarLineFrom(0);
3713                         st->setBarLineTo(defaultBarLineTo);
3714                         }
3715                   }
3716             }
3717 #endif
3718       score->fixTicks();
3719 
3720       for (Part* p : score->parts()) {
3721             p->updateHarmonyChannels(false);
3722             }
3723 
3724       if (score->isMaster()) {
3725             MasterScore* ms = static_cast<MasterScore*>(score);
3726             if (!ms->omr())
3727                   ms->setShowOmr(false);
3728             ms->rebuildMidiMapping();
3729             ms->updateChannel();
3730  //           ms->createPlayEvents();
3731             }
3732 
3733       return true;
3734       }
3735 
3736 //---------------------------------------------------------
3737 //   read
3738 //  <page-layout>
3739 //      <page-height>
3740 //      <page-width>
3741 //      <landscape>1</landscape>
3742 //      <page-margins type="both">
3743 //         <left-margin>28.3465</left-margin>
3744 //         <right-margin>28.3465</right-margin>
3745 //         <top-margin>28.3465</top-margin>
3746 //         <bottom-margin>56.6929</bottom-margin>
3747 //         </page-margins>
3748 //      </page-layout>
3749 //---------------------------------------------------------
3750 
read(XmlReader & e)3751 void PageFormat::read(XmlReader& e)
3752       {
3753       qreal _oddRightMargin  = 0.0;
3754       qreal _evenRightMargin = 0.0;
3755       QString type;
3756 
3757       while (e.readNextStartElement()) {
3758             const QStringRef& tag(e.name());
3759             if (tag == "page-margins") {
3760                   type = e.attribute("type","both");
3761                   qreal lm = 0.0, rm = 0.0, tm = 0.0, bm = 0.0;
3762                   while (e.readNextStartElement()) {
3763                         const QStringRef& t(e.name());
3764                         qreal val = e.readDouble() * 0.5 / PPI;
3765                         if (t == "left-margin")
3766                               lm = val;
3767                         else if (t == "right-margin")
3768                               rm = val;
3769                         else if (t == "top-margin")
3770                               tm = val;
3771                         else if (t == "bottom-margin")
3772                               bm = val;
3773                         else
3774                               e.unknown();
3775                         }
3776                   _twosided = type == "odd" || type == "even";
3777                   if (type == "odd" || type == "both") {
3778                         _oddLeftMargin   = lm;
3779                         _oddRightMargin  = rm;
3780                         _oddTopMargin    = tm;
3781                         _oddBottomMargin = bm;
3782                         }
3783                   if (type == "even" || type == "both") {
3784                         _evenLeftMargin   = lm;
3785                         _evenRightMargin  = rm;
3786                         _evenTopMargin    = tm;
3787                         _evenBottomMargin = bm;
3788                         }
3789                   }
3790             else if (tag == "page-height")
3791                   _size.rheight() = e.readDouble() * 0.5 / PPI;
3792             else if (tag == "page-width")
3793                   _size.rwidth() = e.readDouble() * .5 / PPI;
3794             else
3795                   e.unknown();
3796             }
3797       qreal w1        = _size.width() - _oddLeftMargin - _oddRightMargin;
3798       qreal w2        = _size.width() - _evenLeftMargin - _evenRightMargin;
3799       _printableWidth = qMin(w1, w2);     // silently adjust right margins
3800       }
3801 
3802 //---------------------------------------------------------
3803 //   read206
3804 //    import old version > 1.3  and < 3.x files
3805 //---------------------------------------------------------
3806 
read206(XmlReader & e)3807 Score::FileError MasterScore::read206(XmlReader& e)
3808       {
3809       while (e.readNextStartElement()) {
3810             const QStringRef& tag(e.name());
3811             if (tag == "programVersion") {
3812                   setMscoreVersion(e.readElementText());
3813                   parseVersion(mscoreVersion());
3814                   }
3815             else if (tag == "programRevision")
3816                   setMscoreRevision(e.readIntHex());
3817             else if (tag == "Score") {
3818                   if (!readScore(this, e))
3819                         return FileError::FILE_BAD_FORMAT;
3820                   }
3821             else if (tag == "Revision") {
3822                   Revision* revision = new Revision;
3823                   revision->read(e);
3824                   revisions()->add(revision);
3825                   }
3826             }
3827 
3828       setEnableVerticalSpread(false);
3829 
3830       int id = 1;
3831       for (LinkedElements* le : e.linkIds())
3832             le->setLid(this, id++);
3833 
3834       for (Staff* s : staves())
3835             s->updateOttava();
3836 
3837       // fix segment span
3838       SegmentType st = SegmentType::BarLineType;
3839       for (Segment* s = firstSegment(st); s; s = s->next1(st)) {
3840             for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
3841                   BarLine* b = toBarLine(s->element(staffIdx * VOICES));
3842                   if (!b)
3843                         continue;
3844                   int sp = b->spanStaff();
3845                   if (sp <= 0)
3846                         continue;
3847                   for (int span = 1; span <= sp; ++span) {
3848                         BarLine* nb = toBarLine(s->element((staffIdx + span) * VOICES));
3849                         if (!nb) {
3850                               nb = b->clone();
3851                               nb->setTrack((staffIdx + span) * VOICES);
3852                               s->add(nb);
3853                               }
3854                         nb->setSpanStaff(sp - span);
3855                         }
3856                   staffIdx += sp;
3857                   }
3858             }
3859       for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
3860             Staff* s = staff(staffIdx);
3861             int sp = s->barLineSpan();
3862             if (sp <= 0)
3863                   continue;
3864             for (int span = 1; span <= sp; ++span) {
3865                   Staff* ns = staff(staffIdx + span);
3866                   ns->setBarLineSpan(sp - span);
3867                   }
3868             staffIdx += sp;
3869             }
3870 
3871       // fix positions
3872       //    offset = saved offset - layout position
3873       doLayout();
3874       for (auto i : e.fixOffsets()) {
3875             i.first->setOffset(i.second - i.first->pos());
3876             }
3877 
3878       // treat reading a 2.06 file as import
3879       // on save warn if old file will be overwritten
3880       setCreated(true);
3881       // don't autosave (as long as there's no change to the score)
3882       setAutosaveDirty(false);
3883 
3884       return FileError::FILE_NO_ERROR;
3885       }
3886 
styleDefaults206()3887 MStyle* styleDefaults206()
3888       {
3889       static MStyle* result = nullptr;
3890 
3891       if (result)
3892             return result;
3893 
3894       result = new MStyle();
3895       QFile baseDefaults(":/styles/legacy-style-defaults-v2.mss");
3896 
3897       if (!baseDefaults.open(QIODevice::ReadOnly))
3898             return result;
3899 
3900       result->load(&baseDefaults);
3901 
3902       return result;
3903       }
3904 
3905 }
3906 
3907