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