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