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 : #include "twrite.h"
23 :
24 : #include <set>
25 :
26 : #include "../../iengravingconfiguration.h" // IWYU pragma: keep
27 : #include "../../iengravingfont.h"
28 : #include "../../types/typesconv.h"
29 : #include "../../types/symnames.h"
30 : #include "../../style/textstyle.h"
31 : #include "../../infrastructure/rtti.h"
32 :
33 : #include "dom/measure.h"
34 : #include "dom/score.h"
35 : #include "dom/masterscore.h"
36 : #include "dom/factory.h"
37 : #include "dom/linkedobjects.h"
38 : #include "dom/mscore.h"
39 : #include "dom/staff.h"
40 : #include "dom/part.h"
41 : #include "dom/utils.h"
42 :
43 : #include "dom/accidental.h"
44 : #include "dom/actionicon.h"
45 : #include "dom/ambitus.h"
46 : #include "dom/arpeggio.h"
47 : #include "dom/articulation.h"
48 :
49 : #include "dom/bagpembell.h"
50 : #include "dom/barline.h"
51 : #include "dom/beam.h"
52 : #include "dom/bend.h"
53 : #include "dom/box.h"
54 : #include "dom/bracket.h"
55 : #include "dom/breath.h"
56 :
57 : #include "dom/chord.h"
58 : #include "dom/chordbracket.h"
59 : #include "dom/chordline.h"
60 : #include "dom/chordrest.h"
61 : #include "dom/clef.h"
62 : #include "dom/capo.h"
63 :
64 : #include "dom/drumset.h"
65 : #include "dom/dynamic.h"
66 : #include "dom/expression.h"
67 : #include "dom/fermata.h"
68 : #include "dom/figuredbass.h"
69 : #include "dom/fingering.h"
70 : #include "dom/fret.h"
71 :
72 : #include "dom/glissando.h"
73 : #include "dom/gradualtempochange.h"
74 : #include "dom/groups.h"
75 : #include "dom/guitarbend.h"
76 :
77 : #include "dom/hairpin.h"
78 : #include "dom/hammeronpulloff.h"
79 : #include "dom/harmony.h"
80 : #include "dom/harmonicmark.h"
81 : #include "dom/harppedaldiagram.h"
82 : #include "dom/hook.h"
83 :
84 : #include "dom/image.h"
85 : #include "dom/imageStore.h"
86 : #include "dom/instrument.h"
87 : #include "dom/instrchange.h"
88 :
89 : #include "dom/jump.h"
90 :
91 : #include "dom/keysig.h"
92 :
93 : #include "dom/laissezvib.h"
94 : #include "dom/layoutbreak.h"
95 : #include "dom/ledgerline.h"
96 : #include "dom/letring.h"
97 : #include "dom/location.h"
98 : #include "dom/lyrics.h"
99 :
100 : #include "dom/marker.h"
101 : #include "dom/measurenumber.h"
102 : #include "dom/measurerepeat.h"
103 : #include "dom/mmrest.h"
104 : #include "dom/mmrestrange.h"
105 :
106 : #include "dom/note.h"
107 : #include "dom/notedot.h"
108 : #include "dom/noteline.h"
109 : #include "dom/ornament.h"
110 : #include "dom/ottava.h"
111 :
112 : #include "dom/page.h"
113 : #include "dom/palmmute.h"
114 : #include "dom/parenthesis.h"
115 : #include "dom/part.h"
116 : #include "dom/partialtie.h"
117 : #include "dom/pedal.h"
118 : #include "dom/pickscrape.h"
119 : #include "dom/playcounttext.h"
120 : #include "dom/playtechannotation.h"
121 :
122 : #include "dom/rasgueado.h"
123 : #include "dom/rehearsalmark.h"
124 : #include "dom/rest.h"
125 :
126 : #include "dom/sig.h"
127 : #include "dom/segment.h"
128 : #include "dom/sharedpart.h"
129 : #include "dom/slur.h"
130 : #include "dom/spacer.h"
131 : #include "dom/staffstate.h"
132 : #include "dom/stafftext.h"
133 : #include "dom/stafftype.h"
134 : #include "dom/stafftypechange.h"
135 : #include "dom/stavesharinglabel.h"
136 : #include "dom/stem.h"
137 : #include "dom/stemslash.h"
138 : #include "dom/sticking.h"
139 : #include "dom/stringdata.h"
140 : #include "dom/stringtunings.h"
141 : #include "dom/symbol.h"
142 : #include "dom/bsymbol.h"
143 : #include "dom/system.h"
144 : #include "dom/systemdivider.h"
145 : #include "dom/systemtext.h"
146 : #include "dom/soundflag.h"
147 :
148 : #include "dom/tapping.h"
149 : #include "dom/tempotext.h"
150 : #include "dom/text.h"
151 : #include "dom/textbase.h"
152 : #include "dom/textline.h"
153 : #include "dom/textlinebase.h"
154 : #include "dom/tie.h"
155 : #include "dom/timesig.h"
156 : #include "dom/tremolosinglechord.h"
157 : #include "dom/tremolotwochord.h"
158 : #include "dom/tremolobar.h"
159 : #include "dom/trill.h"
160 : #include "dom/tuplet.h"
161 :
162 : #include "dom/vibrato.h"
163 : #include "dom/volta.h"
164 :
165 : #include "dom/whammybar.h"
166 :
167 : #include "editing/transpose.h"
168 :
169 : #include "../xmlwriter.h"
170 : #include "types/types.h"
171 : #include "writecontext.h"
172 : #include "connectorinfowriter.h"
173 :
174 : #include "log.h"
175 :
176 : using namespace muse;
177 : using namespace mu::engraving;
178 : using namespace mu::engraving::write;
179 :
180 47286 : void TWrite::writeItem(const EngravingItem* item, XmlWriter& xml, WriteContext& ctx)
181 : {
182 47286 : switch (item->type()) {
183 0 : case ElementType::ACCIDENTAL: write(item_cast<const Accidental*>(item), xml, ctx);
184 0 : break;
185 0 : case ElementType::ACTION_ICON: write(item_cast<const ActionIcon*>(item), xml, ctx);
186 0 : break;
187 2 : case ElementType::AMBITUS: write(item_cast<const Ambitus*>(item), xml, ctx);
188 2 : break;
189 117 : case ElementType::ARPEGGIO: write(item_cast<const Arpeggio*>(item), xml, ctx);
190 117 : break;
191 614 : case ElementType::ARTICULATION: write(item_cast<const Articulation*>(item), xml, ctx);
192 614 : break;
193 0 : case ElementType::BAGPIPE_EMBELLISHMENT: write(item_cast<const BagpipeEmbellishment*>(item), xml, ctx);
194 0 : break;
195 1488 : case ElementType::BAR_LINE: write(item_cast<const BarLine*>(item), xml, ctx);
196 1488 : break;
197 0 : case ElementType::BEAM: write(item_cast<const Beam*>(item), xml, ctx);
198 0 : break;
199 4 : case ElementType::BEND: write(item_cast<const Bend*>(item), xml, ctx);
200 4 : break;
201 30 : case ElementType::HBOX: write(item_cast<const HBox*>(item), xml, ctx);
202 30 : break;
203 622 : case ElementType::VBOX: write(item_cast<const VBox*>(item), xml, ctx);
204 622 : break;
205 0 : case ElementType::FBOX: write(item_cast<const FBox*>(item), xml, ctx);
206 0 : break;
207 1 : case ElementType::TBOX: write(item_cast<const TBox*>(item), xml, ctx);
208 1 : break;
209 0 : case ElementType::BRACKET: write(item_cast<const Bracket*>(item), xml, ctx);
210 0 : break;
211 64 : case ElementType::BREATH: write(item_cast<const Breath*>(item), xml, ctx);
212 64 : break;
213 18367 : case ElementType::CHORD: write(item_cast<const Chord*>(item), xml, ctx);
214 18367 : break;
215 0 : case ElementType::CHORD_BRACKET: write(item_cast<const ChordBracket*>(item), xml, ctx);
216 0 : break;
217 28 : case ElementType::CHORDLINE: write(item_cast<const ChordLine*>(item), xml, ctx);
218 28 : break;
219 1093 : case ElementType::CLEF: write(item_cast<const Clef*>(item), xml, ctx);
220 1093 : break;
221 7 : case ElementType::CAPO: write(item_cast<const Capo*>(item), xml, ctx);
222 7 : break;
223 589 : case ElementType::DYNAMIC: write(item_cast<const Dynamic*>(item), xml, ctx);
224 589 : break;
225 13 : case ElementType::EXPRESSION: write(item_cast<const Expression*>(item), xml, ctx);
226 13 : break;
227 141 : case ElementType::FERMATA: write(item_cast<const Fermata*>(item), xml, ctx);
228 141 : break;
229 16 : case ElementType::FIGURED_BASS: write(item_cast<const FiguredBass*>(item), xml, ctx);
230 16 : break;
231 206 : case ElementType::FINGERING: write(item_cast<const Fingering*>(item), xml, ctx);
232 206 : break;
233 68 : case ElementType::FRET_DIAGRAM: write(item_cast<const FretDiagram*>(item), xml, ctx);
234 68 : break;
235 79 : case ElementType::GLISSANDO: write(item_cast<const Glissando*>(item), xml, ctx);
236 79 : break;
237 50 : case ElementType::GRADUAL_TEMPO_CHANGE: write(item_cast<const GradualTempoChange*>(item), xml, ctx);
238 50 : break;
239 304 : case ElementType::GUITAR_BEND: write(item_cast<const GuitarBend*>(item), xml, ctx);
240 304 : break;
241 148 : case ElementType::HAIRPIN: write(item_cast<const Hairpin*>(item), xml, ctx);
242 148 : break;
243 48 : case ElementType::HAMMER_ON_PULL_OFF: write(item_cast<const HammerOnPullOff*>(item), xml, ctx);
244 48 : break;
245 718 : case ElementType::HARMONY: write(item_cast<const Harmony*>(item), xml, ctx);
246 718 : break;
247 60 : case ElementType::HARMONIC_MARK: write(item_cast<const HarmonicMark*>(item), xml, ctx);
248 60 : break;
249 12 : case ElementType::HARP_DIAGRAM: write(item_cast<const HarpPedalDiagram*>(item), xml, ctx);
250 12 : break;
251 0 : case ElementType::HOOK: write(item_cast<const Hook*>(item), xml, ctx);
252 0 : break;
253 23 : case ElementType::IMAGE: write(item_cast<const Image*>(item), xml, ctx);
254 23 : break;
255 24 : case ElementType::INSTRUMENT_CHANGE: write(item_cast<const InstrumentChange*>(item), xml, ctx);
256 24 : break;
257 71 : case ElementType::JUMP: write(item_cast<const Jump*>(item), xml, ctx);
258 71 : break;
259 1226 : case ElementType::KEYSIG: write(item_cast<const KeySig*>(item), xml, ctx);
260 1226 : break;
261 0 : case ElementType::LAISSEZ_VIB: write(item_cast<const LaissezVib*>(item), xml, ctx);
262 0 : break;
263 531 : case ElementType::LAYOUT_BREAK: write(item_cast<const LayoutBreak*>(item), xml, ctx);
264 531 : break;
265 0 : case ElementType::LEDGER_LINE: write(item_cast<const LedgerLine*>(item), xml, ctx);
266 0 : break;
267 52 : case ElementType::LET_RING: write(item_cast<const LetRing*>(item), xml, ctx);
268 52 : break;
269 13 : case ElementType::LYRICS: write(item_cast<const Lyrics*>(item), xml, ctx);
270 13 : break;
271 2 : case ElementType::LYRICSLINE: write(item_cast<const LyricsLine*>(item), xml, ctx);
272 2 : break;
273 75 : case ElementType::MARKER: write(item_cast<const Marker*>(item), xml, ctx);
274 75 : break;
275 0 : case ElementType::MEASURE_NUMBER: write(item_cast<const MeasureNumber*>(item), xml, ctx);
276 0 : break;
277 82 : case ElementType::MEASURE_REPEAT: write(item_cast<const MeasureRepeat*>(item), xml, ctx);
278 82 : break;
279 34 : case ElementType::MMREST: write(item_cast<const MMRest*>(item), xml, ctx);
280 34 : break;
281 0 : case ElementType::MMREST_RANGE: write(item_cast<const MMRestRange*>(item), xml, ctx);
282 0 : break;
283 205 : case ElementType::NOTE: write(item_cast<const Note*>(item), xml, ctx);
284 205 : break;
285 0 : case ElementType::NOTEDOT: write(item_cast<const NoteDot*>(item), xml, ctx);
286 0 : break;
287 0 : case ElementType::NOTEHEAD: write(item_cast<const NoteHead*>(item), xml, ctx);
288 0 : break;
289 2 : case ElementType::NOTELINE: write(item_cast<const NoteLine*>(item), xml, ctx);
290 2 : break;
291 130 : case ElementType::ORNAMENT: write(item_cast<const Ornament*>(item), xml, ctx);
292 130 : break;
293 108 : case ElementType::OTTAVA: write(item_cast<const Ottava*>(item), xml, ctx);
294 108 : break;
295 0 : case ElementType::PAGE: write(item_cast<const Page*>(item), xml, ctx);
296 0 : break;
297 45 : case ElementType::PALM_MUTE: write(item_cast<const PalmMute*>(item), xml, ctx);
298 45 : break;
299 0 : case ElementType::PARENTHESIS: write(item_cast<const Parenthesis*>(item), xml, ctx);
300 0 : break;
301 6 : case ElementType::PARTIAL_LYRICSLINE: write(item_cast<const PartialLyricsLine*>(item), xml, ctx);
302 6 : break;
303 0 : case ElementType::PARTIAL_TIE: write(item_cast<const PartialTie*>(item), xml, ctx);
304 0 : break;
305 63 : case ElementType::PEDAL: write(item_cast<const Pedal*>(item), xml, ctx);
306 63 : break;
307 2 : case ElementType::PICK_SCRAPE: write(item_cast<const PickScrape*>(item), xml, ctx);
308 2 : break;
309 6 : case ElementType::PLAY_COUNT_TEXT: write(item_cast<const PlayCountText*>(item), xml, ctx);
310 6 : break;
311 6 : case ElementType::PLAYTECH_ANNOTATION: write(item_cast<const PlayTechAnnotation*>(item), xml, ctx);
312 6 : break;
313 35 : case ElementType::RASGUEADO: write(item_cast<const Rasgueado*>(item), xml, ctx);
314 35 : break;
315 50 : case ElementType::REHEARSAL_MARK: write(item_cast<const RehearsalMark*>(item), xml, ctx);
316 50 : break;
317 14503 : case ElementType::REST: write(item_cast<const Rest*>(item), xml, ctx);
318 14503 : break;
319 0 : case ElementType::SEGMENT: write(item_cast<const Segment*>(item), xml, ctx);
320 0 : break;
321 255 : case ElementType::SLUR: write(item_cast<const Slur*>(item), xml, ctx);
322 255 : break;
323 0 : case ElementType::SPACER: write(item_cast<const Spacer*>(item), xml, ctx);
324 0 : break;
325 0 : case ElementType::STAFF_STATE: write(item_cast<const StaffState*>(item), xml, ctx);
326 0 : break;
327 357 : case ElementType::STAFF_TEXT: write(item_cast<const StaffText*>(item), xml, ctx);
328 357 : break;
329 0 : case ElementType::STAVE_SHARING_LABEL: write(item_cast<const StaveSharingLabel*>(item), xml, ctx);
330 0 : break;
331 0 : case ElementType::STAFFTYPE_CHANGE: write(item_cast<const StaffTypeChange*>(item), xml, ctx);
332 0 : break;
333 0 : case ElementType::STEM: write(item_cast<const Stem*>(item), xml, ctx);
334 0 : break;
335 0 : case ElementType::STEM_SLASH: write(item_cast<const StemSlash*>(item), xml, ctx);
336 0 : break;
337 73 : case ElementType::STICKING: write(item_cast<const Sticking*>(item), xml, ctx);
338 73 : break;
339 19 : case ElementType::STRING_TUNINGS: write(item_cast<const StringTunings*>(item), xml, ctx);
340 19 : break;
341 11 : case ElementType::SYMBOL: write(item_cast<const Symbol*>(item), xml, ctx);
342 11 : break;
343 0 : case ElementType::FSYMBOL: write(item_cast<const FSymbol*>(item), xml, ctx);
344 0 : break;
345 0 : case ElementType::SYSTEM: write(item_cast<const System*>(item), xml, ctx);
346 0 : break;
347 0 : case ElementType::SYSTEM_DIVIDER: write(item_cast<const SystemDivider*>(item), xml, ctx);
348 0 : break;
349 26 : case ElementType::SYSTEM_TEXT: write(item_cast<const SystemText*>(item), xml, ctx);
350 26 : break;
351 0 : case ElementType::SOUND_FLAG: write(item_cast<const SoundFlag*>(item), xml, ctx);
352 0 : break;
353 3 : case ElementType::TAPPING: write(item_cast<const Tapping*>(item), xml, ctx);
354 3 : break;
355 565 : case ElementType::TEMPO_TEXT: write(item_cast<const TempoText*>(item), xml, ctx);
356 565 : break;
357 1016 : case ElementType::TEXT: write(item_cast<const Text*>(item), xml, ctx);
358 1016 : break;
359 45 : case ElementType::TEXTLINE: write(item_cast<const TextLine*>(item), xml, ctx);
360 45 : break;
361 598 : case ElementType::TIE: write(item_cast<const Tie*>(item), xml, ctx);
362 598 : break;
363 1979 : case ElementType::TIMESIG: write(item_cast<const TimeSig*>(item), xml, ctx);
364 1979 : break;
365 21 : case ElementType::TREMOLO_SINGLECHORD: write(item_cast<const TremoloSingleChord*>(item), xml, ctx);
366 21 : break;
367 0 : case ElementType::TREMOLO_TWOCHORD: write(item_cast<const TremoloTwoChord*>(item), xml, ctx);
368 0 : break;
369 4 : case ElementType::TREMOLOBAR: write(item_cast<const TremoloBar*>(item), xml, ctx);
370 4 : break;
371 18 : case ElementType::TRILL: write(item_cast<const Trill*>(item), xml, ctx);
372 18 : break;
373 0 : case ElementType::TUPLET: write(item_cast<const Tuplet*>(item), xml, ctx);
374 0 : break;
375 26 : case ElementType::VIBRATO: write(item_cast<const Vibrato*>(item), xml, ctx);
376 26 : break;
377 55 : case ElementType::VOLTA: write(item_cast<const Volta*>(item), xml, ctx);
378 55 : break;
379 32 : case ElementType::WHAMMY_BAR: write(item_cast<const WhammyBar*>(item), xml, ctx);
380 32 : break;
381 0 : default: {
382 0 : UNREACHABLE;
383 : LOGE() << "not implemented write for type: " << item->typeName();
384 : }
385 : }
386 47286 : }
387 :
388 38179 : void TWrite::writeItems(const ElementList& items, XmlWriter& xml, WriteContext& ctx)
389 : {
390 38407 : for (const EngravingItem* e : items) {
391 228 : writeItem(e, xml, ctx);
392 : }
393 38179 : }
394 :
395 : //-----------------------------------------------------------------------------
396 : // writeProperty
397 : //
398 : // - styled properties are never written
399 : // - other properties are written if forced or if different from default value
400 : //-----------------------------------------------------------------------------
401 :
402 1505755 : void TWrite::writeProperty(const EngravingItem* item, XmlWriter& xml, Pid pid, bool force)
403 : {
404 1505755 : if (item->isStyled(pid)) {
405 56309 : return;
406 : }
407 1453030 : PropertyValue p = item->getProperty(pid);
408 1453030 : if (!p.isValid()) {
409 47 : LOGD("%s invalid property %d <%s>", item->typeName(), int(pid), propertyName(pid));
410 47 : return;
411 : }
412 1452983 : PropertyValue d = force ? PropertyValue() : item->propertyDefault(pid);
413 :
414 1452983 : if (pid == Pid::FONT_STYLE) {
415 1 : FontStyle ds = FontStyle(d.isValid() ? d.toInt() : 0);
416 1 : FontStyle fs = FontStyle(p.toInt());
417 1 : if ((fs& FontStyle::Bold) != (ds & FontStyle::Bold)) {
418 0 : xml.tag("bold", fs & FontStyle::Bold);
419 : }
420 1 : if ((fs& FontStyle::Italic) != (ds & FontStyle::Italic)) {
421 1 : xml.tag("italic", fs & FontStyle::Italic);
422 : }
423 1 : if ((fs& FontStyle::Underline) != (ds & FontStyle::Underline)) {
424 0 : xml.tag("underline", fs & FontStyle::Underline);
425 : }
426 1 : if ((fs& FontStyle::Strike) != (ds & FontStyle::Strike)) {
427 0 : xml.tag("strike", fs & FontStyle::Strike);
428 : }
429 1 : return;
430 : }
431 :
432 1452982 : P_TYPE type = propertyType(pid);
433 1452982 : if (P_TYPE::ABSOLUTE == type) {
434 0 : double f1 = p.toReal();
435 0 : if (d.isValid() && std::abs(f1 - d.toReal()) < 0.0001) { // fuzzy compare
436 0 : return;
437 : }
438 0 : p = PropertyValue(Spatium::fromAbsolute(f1, item->spatium()));
439 0 : d = PropertyValue();
440 1452982 : } else if (P_TYPE::POINT == type) {
441 3828 : PointF p1 = p.value<PointF>();
442 3828 : if (d.isValid()) {
443 3649 : PointF p2 = d.value<PointF>();
444 3649 : if ((std::abs(p1.x() - p2.x()) < 0.0001) && (std::abs(p1.y() - p2.y()) < 0.0001)) {
445 3536 : return;
446 : }
447 : }
448 292 : double q = item->offsetIsSpatiumDependent() ? item->style().spatium() : DPMM;
449 292 : p = PropertyValue(p1 / q);
450 292 : d = PropertyValue();
451 : }
452 1449446 : xml.tagProperty(pid, p, d);
453 1456567 : }
454 :
455 1388 : void TWrite::writePageLocks(const Score* score, XmlWriter& xml)
456 : {
457 1388 : std::vector<const RangeLock*> locks = score->pageLocks()->allLocks();
458 1388 : if (locks.empty()) {
459 1388 : return;
460 : }
461 :
462 0 : xml.startElement("PageLocks");
463 0 : for (const RangeLock* sl : locks) {
464 0 : writePageLock(sl, xml);
465 : }
466 0 : xml.endElement();
467 1388 : }
468 :
469 1388 : void TWrite::writeSystemLocks(const Score* score, XmlWriter& xml)
470 : {
471 1388 : std::vector<const RangeLock*> locks = score->systemLocks()->allLocks();
472 1388 : if (locks.empty()) {
473 1388 : return;
474 : }
475 :
476 0 : xml.startElement("SystemLocks");
477 0 : for (const RangeLock* sl : locks) {
478 0 : writeSystemLock(sl, xml);
479 : }
480 0 : xml.endElement();
481 1388 : }
482 :
483 1388 : void TWrite::writeSystemDividers(const Score* score, XmlWriter& xml, WriteContext& ctx)
484 : {
485 1388 : std::vector<const System*> systemsToWrite;
486 4594 : for (const System* system : score->systems()) {
487 3209 : bool writeSystem = (system->systemDividerLeft() && !system->systemDividerLeft()->generated())
488 3209 : || (system->systemDividerRight() && !system->systemDividerRight()->generated());
489 3206 : if (writeSystem) {
490 1 : systemsToWrite.push_back(system);
491 : }
492 : }
493 :
494 1388 : if (systemsToWrite.empty()) {
495 1387 : return;
496 : }
497 :
498 1 : xml.startElement("SystemDividers");
499 2 : for (const System* system : systemsToWrite) {
500 4 : xml.startElement("system", { { "idx", muse::indexOf(score->systems(), system) } });
501 1 : SystemDivider* dividerLeft = system->systemDividerLeft();
502 1 : if (dividerLeft && !dividerLeft->generated()) {
503 1 : write(dividerLeft, xml, ctx);
504 : }
505 1 : SystemDivider* dividerRight = system->systemDividerRight();
506 1 : if (dividerRight && !dividerRight->generated()) {
507 1 : write(dividerRight, xml, ctx);
508 : }
509 1 : xml.endElement();
510 : }
511 1 : xml.endElement();
512 1390 : }
513 :
514 1388 : void TWrite::writeScoreSpanners(const Score* score, XmlWriter& xml, WriteContext& ctx)
515 : {
516 1388 : if (score->spannerMap().empty()) {
517 1155 : return;
518 : }
519 233 : std::set<Spanner*> pending;
520 884 : for (const auto& i : score->spannerMap().map()) {
521 651 : Spanner* s = i.second;
522 651 : if (!s->generated() && ctx.canWrite(s)) {
523 651 : pending.insert(s);
524 : }
525 : }
526 233 : xml.startElement("SpannerMap");
527 884 : for (const auto& i : score->spannerMap().map()) {
528 651 : Spanner* s = i.second;
529 651 : if (!pending.count(s)) {
530 2 : continue;
531 : }
532 649 : if (s->links()) {
533 : // The reader resolves linkedTo immediately, so write the eligible main first.
534 88 : Spanner* main = toSpanner(s->links()->mainElement());
535 88 : if (pending.erase(main)) {
536 44 : TWrite::writeItem(main, xml, ctx);
537 : }
538 : }
539 649 : if (pending.erase(s)) {
540 607 : TWrite::writeItem(s, xml, ctx);
541 : }
542 : }
543 233 : xml.endElement();
544 233 : }
545 :
546 81 : void TWrite::writeScoreSpanners(const Score* score, track_idx_t startTrack, track_idx_t endTrack, Segment* startSegment,
547 : Segment* endSegment, XmlWriter& xml, WriteContext& ctx)
548 : {
549 81 : IF_ASSERT_FAILED(ctx.clipboardmode()) {
550 0 : LOGD() << "Should only be used in clipboard mode";
551 : }
552 :
553 81 : std::vector<Spanner*> spannersToWrite;
554 81 : Fraction startTick = startSegment->tick();
555 81 : Fraction endTick = endSegment ? endSegment->tick() : score->endTick();
556 81 : const SpannerMap::IntervalList& spanners = score->spannerMap().findContained(startTick.ticks(), endTick.ticks());
557 115 : for (auto& i : spanners) {
558 34 : Spanner* s = i.value;
559 34 : if (s->generated() || s->track() < startTrack || s->effectiveTrack2() >= endTrack || !ctx.canWrite(s)) {
560 9 : continue;
561 : }
562 : // Do not write chordrest anchored spanners ending on the final tick of the region to the clipboard
563 : // The chordrest they are anchored to is outside of the selected region
564 27 : if (s->tick2() == endTick && s->anchor() == Spanner::Anchor::CHORDREST) {
565 1 : continue;
566 : }
567 : // Don't write voltas to clipboard
568 26 : if (s->isVolta() && s->systemFlag()) {
569 1 : continue;
570 : }
571 25 : spannersToWrite.push_back(s);
572 : }
573 :
574 81 : if (spannersToWrite.empty()) {
575 63 : return;
576 : }
577 :
578 18 : xml.startElement("SpannerMap");
579 43 : for (Spanner* s : spannersToWrite) {
580 25 : TWrite::writeItem(s, xml, ctx);
581 : }
582 18 : xml.endElement();
583 81 : }
584 :
585 94038 : void TWrite::writeItemEid(const EngravingObject* item, XmlWriter& xml)
586 : {
587 94038 : if (item->score()->isPaletteScore()) {
588 0 : return;
589 : }
590 :
591 94038 : EID eid = item->eid();
592 94038 : if (!eid.isValid()) {
593 67922 : eid = item->assignNewEID();
594 : }
595 :
596 94038 : std::array<char, EID::MAX_STR_SIZE> buf{};
597 282114 : const char* last = eid.toChars(buf.data(), buf.data() + buf.size());
598 94038 : const auto size = static_cast<size_t>(last - buf.data());
599 :
600 94038 : xml.tag("eid", AsciiStringView { buf.data(), size });
601 : }
602 :
603 78960 : void TWrite::writeItemLink(const EngravingObject* item, XmlWriter& xml, WriteContext& ctx)
604 : {
605 78960 : if (!item->links() || item->links()->size() <= 1 || ctx.clipboardmode()) {
606 65242 : return;
607 : }
608 :
609 13718 : EngravingItem* mainElement = static_cast<EngravingItem*>(item->links()->mainElement());
610 13718 : if (mainElement != item) {
611 7012 : EID eidOfMainElement = mainElement->eid();
612 7012 : DO_ASSERT(eidOfMainElement.isValid());
613 7012 : xml.tag("linkedTo", eidOfMainElement.toStdString());
614 : }
615 : }
616 :
617 0 : void TWrite::writePageLock(const RangeLock* pageLock, XmlWriter& xml)
618 : {
619 0 : xml.startElement("pageLock");
620 :
621 0 : xml.tag("startMeasure", pageLock->startMB()->eid().toStdString());
622 0 : xml.tag("endMeasure", pageLock->endMB()->eid().toStdString());
623 :
624 0 : xml.endElement();
625 0 : }
626 :
627 0 : void TWrite::writeSystemLock(const RangeLock* systemLock, XmlWriter& xml)
628 : {
629 0 : xml.startElement("systemLock");
630 :
631 0 : xml.tag("startMeasure", systemLock->startMB()->eid().toStdString());
632 0 : xml.tag("endMeasure", systemLock->endMB()->eid().toStdString());
633 :
634 0 : xml.endElement();
635 0 : }
636 :
637 5641 : String TWrite::lineBreakToTag(const String& str)
638 : {
639 : // Raw newlines appearing next to tags (<font size="10> or <sym>...) get eaten by XML readers.
640 5641 : String s = str;
641 11282 : return s.replace(u"\n", u"<br/>");
642 5641 : }
643 :
644 21315 : void TWrite::writeStyledProperties(const EngravingItem* item, XmlWriter& xml)
645 : {
646 66960 : for (const StyledProperty& spp : *item->styledProperties()) {
647 45645 : writeProperty(item, xml, spp.pid);
648 : }
649 21315 : }
650 :
651 76991 : void TWrite::writeItemProperties(const EngravingItem* item, XmlWriter& xml, WriteContext& ctx)
652 : {
653 76991 : writeItemEid(item, xml);
654 :
655 76991 : bool autoplaceEnabled = item->score()->style().styleB(Sid::autoplaceEnabled);
656 76991 : if (!autoplaceEnabled) {
657 0 : item->score()->style().set(Sid::autoplaceEnabled, true);
658 0 : writeProperty(item, xml, Pid::AUTOPLACE);
659 0 : item->score()->style().set(Sid::autoplaceEnabled, autoplaceEnabled);
660 : } else {
661 76991 : writeProperty(item, xml, Pid::AUTOPLACE);
662 : }
663 :
664 76991 : writeItemLink(item, xml, ctx);
665 :
666 76991 : bool writeSpannerTrack = item->isSpanner()
667 78041 : && (toSpanner(item)->anchor() == Spanner::Anchor::NOTE
668 1050 : || (toSpanner(item)->anchor() == Spanner::Anchor::SEGMENT && ctx.clipboardmode()));
669 76991 : if ((item->track() != ctx.curTrack() && item->track() != muse::nidx && !item->isBeam() && !item->isTuplet()) || writeSpannerTrack) {
670 : // Writing track number for beams and tuplets is redundant as it is calculated
671 : // during layout.
672 2352 : int t = static_cast<int>(item->track()) + ctx.trackDiff();
673 2352 : xml.tag("track", t);
674 : }
675 :
676 307964 : for (Pid pid : { Pid::COLOR, Pid::VISIBLE, Pid::Z }) {
677 230973 : if (item->propertyFlags(pid) == PropertyFlags::NOSTYLE) {
678 226597 : writeProperty(item, xml, pid);
679 : }
680 : }
681 :
682 76991 : if (!item->offset().isNull()) {
683 104 : writeProperty(item, xml, Pid::OFFSET);
684 : }
685 :
686 76991 : if (!item->hasVoiceAssignmentProperties() && item->propertyFlags(Pid::PLACEMENT) == PropertyFlags::NOSTYLE) {
687 72975 : writeProperty(item, xml, Pid::PLACEMENT);
688 : }
689 :
690 76991 : writeProperty(item, xml, Pid::POSITION_LINKED_TO_MASTER);
691 76991 : writeProperty(item, xml, Pid::APPEARANCE_LINKED_TO_MASTER);
692 76991 : writeProperty(item, xml, Pid::EXCLUDE_FROM_OTHER_PARTS);
693 :
694 76991 : bool leftGenerated = item->leftParen() && item->leftParen()->generated();
695 76991 : bool rightGenerated = item->rightParen() && item->rightParen()->generated();
696 76991 : if (!leftGenerated || !rightGenerated) {
697 76991 : writeProperty(item, xml, Pid::HAS_PARENTHESES);
698 : }
699 76991 : if (item->leftParen() && !item->leftParen()->generated()) {
700 0 : writeItem(item->leftParen(), xml, ctx);
701 : }
702 76991 : if (item->rightParen() && !item->rightParen()->generated()) {
703 0 : writeItem(item->rightParen(), xml, ctx);
704 : }
705 76991 : }
706 :
707 2618 : void TWrite::write(const Accidental* item, XmlWriter& xml, WriteContext& ctx)
708 : {
709 2618 : xml.startElement(item);
710 2618 : writeProperty(item, xml, Pid::ACCIDENTAL_BRACKET);
711 2618 : writeProperty(item, xml, Pid::ACCIDENTAL_ROLE);
712 2618 : writeProperty(item, xml, Pid::SMALL);
713 2618 : writeProperty(item, xml, Pid::ACCIDENTAL_TYPE);
714 2618 : writeProperty(item, xml, Pid::ACCIDENTAL_STACKING_ORDER_OFFSET);
715 2618 : writeItemProperties(item, xml, ctx);
716 2618 : xml.endElement();
717 2618 : }
718 :
719 0 : void TWrite::write(const ActionIcon* item, XmlWriter& xml, WriteContext&)
720 : {
721 0 : xml.startElement(item);
722 0 : xml.tag("action", TConv::toXml(item->actionType()));
723 0 : xml.endElement();
724 0 : }
725 :
726 2 : void TWrite::write(const Ambitus* item, XmlWriter& xml, WriteContext& ctx)
727 : {
728 2 : xml.startElement(item);
729 2 : xml.tagProperty(Pid::HEAD_GROUP, item->noteHeadGroup(), Ambitus::NOTEHEADGROUP_DEFAULT);
730 2 : xml.tagProperty(Pid::HEAD_TYPE, item->noteHeadType(), Ambitus::NOTEHEADTYPE_DEFAULT);
731 2 : xml.tagProperty(Pid::MIRROR_HEAD, item->direction(), Ambitus::DIRECTION_DEFAULT);
732 2 : xml.tag("hasLine", item->hasLine(), true);
733 2 : xml.tagProperty(Pid::LINE_WIDTH, item->lineWidth(), Ambitus::LINEWIDTH_DEFAULT);
734 2 : xml.tag("topPitch", item->topPitch());
735 2 : xml.tag("topTpc", item->topTpc());
736 2 : xml.tag("bottomPitch", item->bottomPitch());
737 2 : xml.tag("bottomTpc", item->bottomTpc());
738 2 : if (item->topAccidental()->accidentalType() != AccidentalType::NONE) {
739 0 : xml.startElement("topAccidental");
740 0 : write(item->topAccidental(), xml, ctx);
741 0 : xml.endElement();
742 : }
743 2 : if (item->bottomAccidental()->accidentalType() != AccidentalType::NONE) {
744 1 : xml.startElement("bottomAccidental");
745 1 : write(item->bottomAccidental(), xml, ctx);
746 1 : xml.endElement();
747 : }
748 2 : writeItemProperties(item, xml, ctx);
749 2 : xml.endElement();
750 2 : }
751 :
752 117 : void TWrite::write(const Arpeggio* item, XmlWriter& xml, WriteContext& ctx)
753 : {
754 117 : if (!ctx.canWrite(item)) {
755 3 : return;
756 : }
757 114 : xml.startElement(item);
758 114 : writeProperty(item, xml, Pid::ARPEGGIO_TYPE);
759 114 : writeProperties(item, xml, ctx);
760 :
761 114 : xml.endElement();
762 : }
763 :
764 0 : void TWrite::write(const ChordBracket* item, XmlWriter& xml, WriteContext& ctx)
765 : {
766 0 : if (!ctx.canWrite(item)) {
767 0 : return;
768 : }
769 :
770 0 : xml.startElement(item);
771 :
772 0 : writeProperty(item, xml, Pid::BRACKET_HOOK_LEN);
773 0 : writeProperty(item, xml, Pid::BRACKET_HOOK_POS);
774 0 : writeProperty(item, xml, Pid::BRACKET_RIGHT_SIDE);
775 :
776 0 : writeProperties(item, xml, ctx);
777 :
778 0 : xml.endElement();
779 : }
780 :
781 114 : void TWrite::writeProperties(const Arpeggio* item, XmlWriter& xml, WriteContext& ctx)
782 : {
783 114 : writeItemProperties(item, xml, ctx);
784 114 : if (!RealIsNull(item->userLen1())) {
785 0 : xml.tag("userLen1", item->userLen1() / item->spatium());
786 : }
787 114 : if (!RealIsNull(item->userLen2())) {
788 0 : xml.tag("userLen2", item->userLen2() / item->spatium());
789 : }
790 114 : if (item->span() != 1) {
791 54 : xml.tag("span", item->span());
792 : }
793 114 : writeProperty(item, xml, Pid::PLAY);
794 114 : writeProperty(item, xml, Pid::TIME_STRETCH);
795 114 : }
796 :
797 614 : void TWrite::write(const Articulation* item, XmlWriter& xml, WriteContext& ctx)
798 : {
799 614 : if (!ctx.canWrite(item)) {
800 2 : return;
801 : }
802 612 : if (item->isOrnament()) {
803 0 : write(toOrnament(item), xml, ctx);
804 0 : return;
805 : }
806 612 : xml.startElement(item);
807 612 : writeProperties(item, xml, ctx);
808 612 : xml.endElement();
809 : }
810 :
811 761 : void TWrite::writeProperties(const Articulation* item, XmlWriter& xml, WriteContext& ctx)
812 : {
813 761 : if (!item->channelName().isEmpty()) {
814 0 : xml.tag("channe", { { "name", item->channelName() } });
815 : }
816 :
817 761 : writeProperty(item, xml, Pid::DIRECTION);
818 761 : if (item->textType() != ArticulationTextType::NO_TEXT) {
819 22 : xml.tag("subtype", TConv::toXml(item->textType()));
820 739 : } else if (!item->isTapping()) {
821 736 : xml.tag("subtype", SymNames::nameForSymId(item->symId()));
822 : }
823 :
824 761 : writeProperty(item, xml, Pid::PLAY);
825 761 : writeProperty(item, xml, Pid::ORNAMENT_STYLE);
826 761 : writeStyledProperties(item, xml);
827 761 : writeItemProperties(item, xml, ctx);
828 761 : }
829 :
830 146 : void TWrite::write(const Ornament* item, XmlWriter& xml, WriteContext& ctx)
831 : {
832 146 : if (!ctx.canWrite(item)) {
833 0 : return;
834 : }
835 146 : xml.startElement(item);
836 :
837 146 : if (item->cueNoteChord()) {
838 5 : write(item->cueNoteChord(), xml, ctx);
839 : } else {
840 141 : if (item->accidentalAbove()) {
841 7 : write(item->accidentalAbove(), xml, ctx);
842 : }
843 141 : if (item->accidentalBelow()) {
844 2 : write(item->accidentalBelow(), xml, ctx);
845 : }
846 : }
847 :
848 146 : writeProperty(item, xml, Pid::INTERVAL_ABOVE);
849 146 : writeProperty(item, xml, Pid::INTERVAL_BELOW);
850 146 : writeProperty(item, xml, Pid::ORNAMENT_SHOW_ACCIDENTAL);
851 146 : writeProperty(item, xml, Pid::ORNAMENT_SHOW_CUE_NOTE);
852 146 : writeProperty(item, xml, Pid::START_ON_UPPER_NOTE);
853 146 : writeProperties(static_cast<const Articulation*>(item), xml, ctx);
854 146 : xml.endElement();
855 : }
856 :
857 0 : void TWrite::write(const BagpipeEmbellishment* item, XmlWriter& xml, WriteContext&)
858 : {
859 0 : xml.startElement(item);
860 0 : xml.tag("subtype", TConv::toXml(item->embelType()));
861 0 : xml.endElement();
862 0 : }
863 :
864 1488 : void TWrite::write(const BarLine* item, XmlWriter& xml, WriteContext& ctx)
865 : {
866 1488 : xml.startElement(item);
867 :
868 1488 : writeProperty(item, xml, Pid::BARLINE_TYPE);
869 1488 : writeProperty(item, xml, Pid::BARLINE_SPAN);
870 1488 : writeProperty(item, xml, Pid::BARLINE_SPAN_FROM);
871 1488 : writeProperty(item, xml, Pid::BARLINE_SPAN_TO);
872 :
873 1488 : for (const EngravingItem* e : *item->el()) {
874 0 : writeItem(e, xml, ctx);
875 : }
876 :
877 1488 : if (ctx.clipboardmode() && item->measure()) {
878 1 : xml.tag("playCount", item->measure()->repeatCount());
879 : }
880 :
881 1488 : writeItemProperties(item, xml, ctx);
882 1488 : xml.endElement();
883 1488 : }
884 :
885 1620 : void TWrite::write(const Beam* item, XmlWriter& xml, WriteContext& ctx)
886 : {
887 1620 : if (item->elements().empty()) {
888 0 : return;
889 : }
890 1620 : xml.startElement(item);
891 1620 : writeItemProperties(item, xml, ctx);
892 :
893 1620 : writeProperty(item, xml, Pid::STEM_DIRECTION);
894 1620 : writeProperty(item, xml, Pid::BEAM_NO_SLOPE);
895 1620 : writeProperty(item, xml, Pid::GROW_LEFT);
896 1620 : writeProperty(item, xml, Pid::GROW_RIGHT);
897 1620 : writeProperty(item, xml, Pid::BEAM_CROSS_STAFF_MOVE);
898 :
899 1620 : int idx = item->directionIdx();
900 1620 : if (item->userModified()) {
901 0 : double _spatium = item->spatium();
902 0 : for (BeamFragment* f : item->beamFragments()) {
903 0 : xml.startElement("Fragment");
904 0 : xml.tag("y1", f->py1[idx] / _spatium);
905 0 : xml.tag("y2", f->py2[idx] / _spatium);
906 0 : xml.endElement();
907 : }
908 : }
909 :
910 : // this info is used for regression testing
911 : // l1/l2 is the beam position of the layout engine
912 1620 : if (MScore::testMode) {
913 1620 : double spatium8 = item->spatium() * .125;
914 3240 : for (BeamFragment* f : item->beamFragments()) {
915 1620 : xml.tag("l1", int(lrint(f->py1[idx] / spatium8)));
916 1620 : xml.tag("l2", int(lrint(f->py2[idx] / spatium8)));
917 : }
918 : }
919 :
920 1620 : xml.endElement();
921 : }
922 :
923 4 : void TWrite::write(const Bend* item, XmlWriter& xml, WriteContext& ctx)
924 : {
925 4 : xml.startElement(item);
926 16 : for (const PitchValue& v : item->points()) {
927 60 : xml.tag("point", { { "time", v.time }, { "pitch", v.pitch }, { "vibrato", v.vibrato } });
928 : }
929 4 : writeStyledProperties(item, xml);
930 4 : writeProperty(item, xml, Pid::PLAY);
931 4 : writeItemProperties(item, xml, ctx);
932 4 : xml.endElement();
933 16 : }
934 :
935 622 : void TWrite::write(const Box* item, XmlWriter& xml, WriteContext& ctx)
936 : {
937 622 : xml.startElement(item);
938 622 : writeBoxProperties(item, xml, ctx);
939 622 : xml.endElement();
940 622 : }
941 :
942 622 : void TWrite::writeBoxProperties(const Box* item, XmlWriter& xml, WriteContext& ctx)
943 : {
944 622 : if (item->isHBox()) {
945 0 : return writeProperties(dynamic_cast<const HBox*>(item), xml, ctx);
946 : }
947 622 : return writeProperties(item, xml, ctx);
948 : }
949 :
950 653 : void TWrite::writeProperties(const Box* item, XmlWriter& xml, WriteContext& ctx)
951 : {
952 7183 : for (Pid id : {
953 : Pid::BOX_HEIGHT, Pid::BOX_WIDTH, Pid::TOP_GAP, Pid::BOTTOM_GAP,
954 : Pid::LEFT_MARGIN, Pid::RIGHT_MARGIN, Pid::TOP_MARGIN, Pid::BOTTOM_MARGIN, Pid::BOX_AUTOSIZE, Pid::SIZE_SPATIUM_DEPENDENT
955 7836 : }) {
956 6530 : bool force = ((item->isVBox() || item->isFBox()) && id == Pid::BOX_HEIGHT) || (item->isHBox() && id == Pid::BOX_WIDTH);
957 6530 : writeProperty(item, xml, id, force);
958 : }
959 653 : if (item->isVBoxBase()) {
960 623 : writeProperty(item, xml, Pid::PADDING_TO_NOTATION_ABOVE);
961 623 : writeProperty(item, xml, Pid::PADDING_TO_NOTATION_BELOW);
962 : }
963 653 : writeItemProperties(item, xml, ctx);
964 1679 : for (const EngravingItem* e : item->el()) {
965 1026 : writeItem(e, xml, ctx);
966 : }
967 653 : }
968 :
969 30 : void TWrite::write(const HBox* item, XmlWriter& xml, WriteContext& ctx)
970 : {
971 30 : xml.startElement(item);
972 30 : writeProperties(item, xml, ctx);
973 30 : xml.endElement();
974 30 : }
975 :
976 30 : void TWrite::writeProperties(const HBox* item, XmlWriter& xml, WriteContext& ctx)
977 : {
978 30 : writeProperty(item, xml, Pid::CREATE_SYSTEM_HEADER);
979 30 : writeProperties(static_cast<const Box*>(item), xml, ctx);
980 30 : }
981 :
982 622 : void TWrite::write(const VBox* item, XmlWriter& xml, WriteContext& ctx)
983 : {
984 622 : write(static_cast<const Box*>(item), xml, ctx);
985 622 : }
986 :
987 0 : void TWrite::write(const FBox* item, XmlWriter& xml, WriteContext& ctx)
988 : {
989 0 : xml.startElement(item);
990 :
991 0 : writeProperty(item, xml, Pid::FRET_FRAME_TEXT_SCALE);
992 0 : writeProperty(item, xml, Pid::FRET_FRAME_DIAGRAM_SCALE);
993 0 : writeProperty(item, xml, Pid::FRET_FRAME_COLUMN_GAP);
994 0 : writeProperty(item, xml, Pid::FRET_FRAME_ROW_GAP);
995 0 : writeProperty(item, xml, Pid::FRET_FRAME_CHORDS_PER_ROW);
996 0 : writeProperty(item, xml, Pid::FRET_FRAME_H_ALIGN);
997 :
998 0 : writeProperties(static_cast<const Box*>(item), xml, ctx);
999 :
1000 0 : xml.endElement();
1001 0 : }
1002 :
1003 1 : void TWrite::write(const TBox* item, XmlWriter& xml, WriteContext& ctx)
1004 : {
1005 1 : xml.startElement(item);
1006 1 : writeProperties(static_cast<const Box*>(item), xml, ctx);
1007 1 : write(item->text(), xml, ctx);
1008 1 : xml.endElement();
1009 1 : }
1010 :
1011 0 : void TWrite::write(const Bracket* item, XmlWriter& xml, WriteContext& ctx)
1012 : {
1013 0 : bool isStartTag = false;
1014 0 : switch (item->bracketItem()->bracketType()) {
1015 0 : case BracketType::BRACE:
1016 : case BracketType::SQUARE:
1017 : case BracketType::LINE:
1018 : case BracketType::GROUP:
1019 : {
1020 0 : xml.startElement(item, { { "type", TConv::toXml(item->bracketItem()->bracketType()) } });
1021 0 : isStartTag = true;
1022 : }
1023 0 : break;
1024 0 : case BracketType::NORMAL:
1025 0 : xml.startElement(item);
1026 0 : isStartTag = true;
1027 0 : break;
1028 0 : case BracketType::NO_BRACKET:
1029 0 : break;
1030 : }
1031 :
1032 0 : if (isStartTag) {
1033 0 : if (item->bracketItem()->column()) {
1034 0 : xml.tag("level", static_cast<int>(item->bracketItem()->column()));
1035 : }
1036 :
1037 0 : writeItemProperties(item, xml, ctx);
1038 :
1039 0 : xml.endElement();
1040 : }
1041 0 : }
1042 :
1043 64 : void TWrite::write(const Breath* item, XmlWriter& xml, WriteContext& ctx)
1044 : {
1045 64 : if (!ctx.canWrite(item)) {
1046 0 : return;
1047 : }
1048 64 : xml.startElement(item);
1049 64 : writeProperty(item, xml, Pid::SYMBOL);
1050 64 : writeProperty(item, xml, Pid::PAUSE);
1051 64 : writeItemProperties(item, xml, ctx);
1052 64 : xml.endElement();
1053 : }
1054 :
1055 18825 : void TWrite::write(const Chord* item, XmlWriter& xml, WriteContext& ctx)
1056 : {
1057 18825 : bool foundNotes = false;
1058 18825 : const size_t noteCount = item->notes().size();
1059 18937 : for (size_t noteIdx = 0; noteIdx < noteCount; ++noteIdx) {
1060 18865 : if (ctx.canWriteNoteIdx(noteIdx, noteCount)) {
1061 18753 : foundNotes = true;
1062 18753 : break;
1063 : }
1064 : }
1065 18825 : if (!foundNotes) {
1066 72 : if (item->tuplet()) {
1067 : // HACK: See PR #30178 - deselected chords still not fully handled in tuplets...
1068 30 : Rest* dummyRest = Factory::createRest(item->segment());
1069 30 : dummyRest->setDurationType(item->durationType());
1070 30 : dummyRest->setTuplet(item->tuplet());
1071 30 : dummyRest->setTicks(item->ticks());
1072 30 : dummyRest->setTrack(item->track());
1073 30 : write(dummyRest, xml, ctx);
1074 30 : dummyRest->deleteLater();
1075 : } else {
1076 : // No writable notes found, write a Location instead (skip this chord)...
1077 :
1078 42 : const Fraction tick = item->tick();
1079 42 : const track_idx_t track = item->track();
1080 :
1081 42 : Location curr = Location::absolute();
1082 42 : curr.setFrac(ctx.curTick());
1083 42 : curr.setTrack(static_cast<int>(ctx.curTrack()));
1084 :
1085 42 : Location dest = Location::absolute();
1086 42 : dest.setFrac(tick);
1087 42 : dest.setTrack(static_cast<int>(track));
1088 :
1089 42 : dest.toRelative(curr);
1090 42 : TWrite::write(&dest, xml, ctx);
1091 :
1092 42 : ctx.setCurTick(tick);
1093 42 : ctx.setCurTrack(track);
1094 : }
1095 72 : return;
1096 : }
1097 :
1098 19206 : for (Chord* ch : item->graceNotes()) {
1099 453 : write(ch, xml, ctx);
1100 : }
1101 18753 : writeChordRestBeam(item, xml, ctx);
1102 18753 : xml.startElement(item);
1103 18753 : writeProperties(toChordRest(item), xml, ctx);
1104 19466 : for (const Articulation* a : item->articulations()) {
1105 713 : writeItem(a, xml, ctx);
1106 : }
1107 18753 : switch (item->noteType()) {
1108 18300 : case NoteType::NORMAL:
1109 18300 : break;
1110 69 : case NoteType::ACCIACCATURA:
1111 69 : xml.tag("acciaccatura");
1112 69 : break;
1113 169 : case NoteType::APPOGGIATURA:
1114 169 : xml.tag("appoggiatura");
1115 169 : break;
1116 4 : case NoteType::GRACE4:
1117 4 : xml.tag("grace4");
1118 4 : break;
1119 32 : case NoteType::GRACE16:
1120 32 : xml.tag("grace16");
1121 32 : break;
1122 22 : case NoteType::GRACE32:
1123 22 : xml.tag("grace32");
1124 22 : break;
1125 147 : case NoteType::GRACE8_AFTER:
1126 147 : xml.tag("grace8after");
1127 147 : break;
1128 4 : case NoteType::GRACE16_AFTER:
1129 4 : xml.tag("grace16after");
1130 4 : break;
1131 6 : case NoteType::GRACE32_AFTER:
1132 6 : xml.tag("grace32after");
1133 6 : break;
1134 0 : default:
1135 0 : break;
1136 : }
1137 :
1138 18753 : if (item->noStem()) {
1139 244 : xml.tag("noStem", item->noStem());
1140 18509 : } else if (item->stem() && (item->stem()->isUserModified() || !item->stem()->userLength().isZero())) {
1141 3 : write(item->stem(), xml, ctx);
1142 : }
1143 18753 : if (item->hook() && item->hook()->isUserModified()) {
1144 0 : write(item->hook(), xml, ctx);
1145 : }
1146 18753 : if (item->showStemSlash() != item->propertyDefault(Pid::SHOW_STEM_SLASH).toBool()) {
1147 23 : xml.tag("showStemSlash", item->showStemSlash());
1148 : }
1149 18753 : if (item->stemSlash() && item->stemSlash()->isUserModified()) {
1150 0 : write(item->stemSlash(), xml, ctx);
1151 : }
1152 18753 : writeProperty(item, xml, Pid::STEM_DIRECTION);
1153 :
1154 42242 : for (size_t noteIdx = 0; noteIdx < noteCount; ++noteIdx) {
1155 23489 : if (!ctx.canWriteNoteIdx(noteIdx, noteCount)) {
1156 114 : continue;
1157 : }
1158 23375 : const Note* note = item->notes().at(noteIdx);
1159 23375 : write(note, xml, ctx);
1160 : }
1161 :
1162 : // Write parens
1163 18903 : for (const NoteParenthesisInfo* parenPair : item->noteParentheses()) {
1164 150 : xml.startElement("NoteParenGroup");
1165 :
1166 150 : write(parenPair->leftParen(), xml, ctx);
1167 150 : write(parenPair->rightParen(), xml, ctx);
1168 :
1169 150 : xml.startElement("Notes");
1170 307 : for (const Note* note : parenPair->notes()) {
1171 157 : EID noteEID = note->eid();
1172 157 : if (!noteEID.isValid()) {
1173 0 : noteEID = note->assignNewEID();
1174 : }
1175 157 : xml.tag("NoteEID", noteEID.toStdString());
1176 : }
1177 150 : xml.endElement();
1178 150 : xml.endElement();
1179 : }
1180 :
1181 18753 : if (item->arpeggio()) {
1182 117 : writeItem(item->arpeggio(), xml, ctx);
1183 : }
1184 :
1185 18753 : if (item->tremoloSingleChord()) {
1186 91 : write(item->tremoloSingleChord(), xml, ctx);
1187 18662 : } else if (item->tremoloTwoChord() && item->tremoloChordType() != TremoloChordType::TremoloSecondChord) {
1188 52 : write(item->tremoloTwoChord(), xml, ctx);
1189 : }
1190 :
1191 18753 : writeProperty(item, xml, Pid::COMBINE_VOICE);
1192 :
1193 18848 : for (EngravingItem* e : item->el()) {
1194 95 : if (e->isChordLine() && toChordLine(e)->note()) { // this is now written by Note
1195 69 : continue;
1196 : }
1197 26 : writeItem(e, xml, ctx);
1198 : }
1199 18753 : xml.endElement();
1200 : }
1201 :
1202 33236 : void TWrite::writeChordRestBeam(const ChordRest* item, XmlWriter& xml, WriteContext& ctx)
1203 : {
1204 33236 : Beam* b = item->beam();
1205 33236 : if (b && b->elements().front() == item && (MScore::testMode || !b->generated())) {
1206 1620 : write(b, xml, ctx);
1207 : }
1208 33236 : }
1209 :
1210 33352 : void TWrite::writeProperties(const ChordRest* item, XmlWriter& xml, WriteContext& ctx)
1211 : {
1212 33352 : writeItemProperties(item, xml, ctx);
1213 :
1214 : //
1215 : // BeamMode default:
1216 : // REST - BeamMode::NONE
1217 : // CHORD - BeamMode::AUTO
1218 : //
1219 33352 : if ((item->isRest() && item->beamMode() != BeamMode::NONE) || (item->isChord() && item->beamMode() != BeamMode::AUTO)) {
1220 1773 : xml.tag("BeamMode", TConv::toXml(item->beamMode()));
1221 : }
1222 33352 : writeProperty(item, xml, Pid::SMALL);
1223 33352 : if (item->actualDurationType().dots()) {
1224 500 : xml.tag("dots", item->actualDurationType().dots());
1225 : }
1226 33352 : writeProperty(item, xml, Pid::STAFF_MOVE);
1227 :
1228 33352 : if (item->actualDurationType().isValid()) {
1229 33347 : xml.tag("durationType", TConv::toXml(item->actualDurationType().type()));
1230 : }
1231 :
1232 56062 : if (!item->ticks().isZero() && (!item->actualDurationType().fraction().isValid()
1233 56062 : || (item->actualDurationType().fraction() != item->ticks()))) {
1234 10782 : xml.tagFraction("duration", item->ticks());
1235 : //xml.tagE("duration z=\"%d\" n=\"%d\"", ticks().numerator(), ticks().denominator());
1236 : }
1237 :
1238 33787 : for (Lyrics* lyrics : item->lyrics()) {
1239 435 : write(lyrics, xml, ctx);
1240 : }
1241 :
1242 33352 : if (!item->isGrace()) {
1243 32899 : Fraction t(item->globalTicks());
1244 32899 : if (item->staff()) {
1245 32899 : t /= item->staff()->timeStretch(ctx.curTick());
1246 : }
1247 32899 : ctx.incCurTick(t);
1248 : }
1249 33352 : }
1250 :
1251 33585 : void TWrite::writeTupletStart(DurationElement* item, XmlWriter& xml, WriteContext& ctx)
1252 : {
1253 33585 : if (item->tuplet() && item->tuplet()->elements().front() == item) {
1254 599 : writeTupletStart(item->tuplet(), xml, ctx); // recursion
1255 599 : TWrite::write(item->tuplet(), xml, ctx);
1256 : }
1257 33585 : }
1258 :
1259 33585 : void TWrite::writeTupletEnd(DurationElement* item, XmlWriter& xml, WriteContext& ctx)
1260 : {
1261 33585 : if (item->tuplet() && item->tuplet()->elements().back() == item) {
1262 599 : xml.tag("endTuplet");
1263 599 : writeTupletEnd(item->tuplet(), xml, ctx); // recursion
1264 : }
1265 33585 : }
1266 :
1267 97 : void TWrite::write(const ChordLine* item, XmlWriter& xml, WriteContext& ctx)
1268 : {
1269 97 : xml.startElement(item);
1270 97 : writeProperty(item, xml, Pid::CHORD_LINE_TYPE);
1271 97 : writeProperty(item, xml, Pid::CHORD_LINE_STRAIGHT);
1272 97 : writeProperty(item, xml, Pid::CHORD_LINE_WAVY);
1273 97 : writeProperty(item, xml, Pid::PLAY);
1274 97 : writeItemProperties(item, xml, ctx);
1275 485 : for (size_t i = 0; i < size_t(ChordLine::Point::POINTS_COUNT); ++i) {
1276 388 : writeProperty(item, xml, ChordLine::bezierOffsetPid(ChordLine::Point(i)));
1277 : }
1278 97 : xml.endElement();
1279 97 : }
1280 :
1281 1093 : void TWrite::write(const Clef* item, XmlWriter& xml, WriteContext& ctx)
1282 : {
1283 1093 : xml.startElement(item);
1284 1093 : writeProperty(item, xml, Pid::CLEF_TYPE_CONCERT);
1285 1093 : writeProperty(item, xml, Pid::CLEF_TYPE_TRANSPOSING);
1286 1093 : writeProperty(item, xml, Pid::CLEF_TO_BARLINE_POS);
1287 1093 : if (item->isHeader()) {
1288 983 : xml.tag("isHeader", true);
1289 : }
1290 1093 : writeProperty(item, xml, Pid::IS_COURTESY);
1291 1093 : if (!item->showCourtesy()) {
1292 3 : xml.tag("showCourtesyClef", item->showCourtesy());
1293 : }
1294 1093 : if (item->forInstrumentChange()) {
1295 0 : xml.tag("forInstrumentChange", item->forInstrumentChange());
1296 : }
1297 1093 : writeItemProperties(item, xml, ctx);
1298 1093 : xml.endElement();
1299 1093 : }
1300 :
1301 7 : void TWrite::write(const Capo* item, XmlWriter& xml, WriteContext& ctx)
1302 : {
1303 7 : xml.startElement(item);
1304 7 : writeProperty(item, xml, Pid::ACTIVE);
1305 7 : writeProperty(item, xml, Pid::CAPO_FRET_POSITION);
1306 7 : writeProperty(item, xml, Pid::CAPO_GENERATE_TEXT);
1307 7 : writeProperty(item, xml, Pid::CAPO_TRANSPOSE_MODE);
1308 :
1309 7 : std::set<string_idx_t> orderedStrings;
1310 7 : for (string_idx_t idx : item->params().ignoredStrings) {
1311 0 : orderedStrings.insert(idx);
1312 : }
1313 :
1314 7 : for (string_idx_t idx : orderedStrings) {
1315 0 : xml.startElement("string", { { "no", idx } });
1316 0 : xml.tag("apply", false);
1317 0 : xml.endElement();
1318 : }
1319 :
1320 7 : writeProperties(static_cast<const StaffTextBase*>(item), xml, ctx, true);
1321 7 : xml.endElement();
1322 7 : }
1323 :
1324 589 : void TWrite::write(const Dynamic* item, XmlWriter& xml, WriteContext& ctx)
1325 : {
1326 589 : if (!ctx.canWrite(item)) {
1327 1 : return;
1328 : }
1329 588 : xml.startElement(item);
1330 588 : writeProperty(item, xml, Pid::DYNAMIC_TYPE);
1331 588 : writeProperty(item, xml, Pid::VELOCITY);
1332 588 : writeProperty(item, xml, Pid::AVOID_BARLINES);
1333 588 : writeProperty(item, xml, Pid::CENTER_ON_NOTEHEAD);
1334 588 : writeProperty(item, xml, Pid::PLAY);
1335 588 : writeProperty(item, xml, Pid::ANCHOR_TO_END_OF_PREVIOUS);
1336 :
1337 588 : if (item->isVelocityChangeAvailable()) {
1338 16 : writeProperty(item, xml, Pid::VELO_CHANGE);
1339 16 : writeProperty(item, xml, Pid::VELO_CHANGE_SPEED);
1340 : }
1341 :
1342 588 : writeProperties(toTextBase(item), xml, ctx, toDynamic(item)->hasCustomText());
1343 588 : xml.endElement();
1344 : }
1345 :
1346 13 : void TWrite::write(const Expression* item, XmlWriter& xml, WriteContext& ctx)
1347 : {
1348 13 : if (!ctx.canWrite(item)) {
1349 0 : return;
1350 : }
1351 13 : xml.startElement(item);
1352 13 : writeProperties(toTextBase(item), xml, ctx, true);
1353 13 : xml.endElement();
1354 : }
1355 :
1356 4326 : void TWrite::writeProperties(const TextBase* item, XmlWriter& xml, WriteContext& ctx, bool writeText)
1357 : {
1358 4326 : if (item->hasVoiceAssignmentProperties()) {
1359 601 : writeProperty(item, xml, Pid::VOICE_ASSIGNMENT);
1360 601 : writeProperty(item, xml, Pid::DIRECTION);
1361 601 : writeProperty(item, xml, Pid::CENTER_BETWEEN_STAVES);
1362 : }
1363 :
1364 4326 : writeProperty(item, xml, Pid::TEXT_STYLE);
1365 4326 : writeItemProperties(item, xml, ctx);
1366 :
1367 4326 : writeStyledProperties(item, xml);
1368 :
1369 73542 : for (const auto& spp : *textStyle(item->textStyleType())) {
1370 276683 : if (item->isStyled(spp.pid)
1371 69397 : || (spp.pid == Pid::FONT_SIZE && item->getProperty(spp.pid).toDouble() == TextBase::UNDEFINED_FONT_SIZE)
1372 69397 : || (spp.pid == Pid::FONT_FACE && item->getProperty(spp.pid).value<String>() == TextBase::UNDEFINED_FONT_FAMILY)) {
1373 69035 : continue;
1374 : }
1375 181 : writeProperty(item, xml, spp.pid);
1376 : }
1377 4326 : if (writeText) {
1378 2991 : xml.writeXml(u"text", lineBreakToTag(item->xmlText()));
1379 : }
1380 :
1381 4326 : writeProperty(item, xml, Pid::TEXT_LINKED_TO_MASTER);
1382 4326 : }
1383 :
1384 141 : void TWrite::write(const Fermata* item, XmlWriter& xml, WriteContext& ctx)
1385 : {
1386 141 : if (!ctx.canWrite(item)) {
1387 0 : return;
1388 : }
1389 :
1390 141 : xml.startElement(item);
1391 141 : xml.tag("subtype", SymNames::nameForSymId(item->symId()));
1392 141 : writeProperty(item, xml, Pid::TIME_STRETCH);
1393 141 : writeProperty(item, xml, Pid::PLAY);
1394 141 : writeProperty(item, xml, Pid::MIN_DISTANCE);
1395 141 : writeItemProperties(item, xml, ctx);
1396 141 : xml.endElement();
1397 : }
1398 :
1399 16 : void TWrite::write(const FiguredBass* item, XmlWriter& xml, WriteContext& ctx)
1400 : {
1401 16 : if (!ctx.canWrite(item)) {
1402 4 : return;
1403 : }
1404 :
1405 12 : xml.startElement(item);
1406 12 : if (!item->onNote()) {
1407 0 : xml.tag("onNote", item->onNote());
1408 : }
1409 12 : if (item->ticks().isNotZero()) {
1410 5 : xml.tagFraction("ticks", item->ticks());
1411 : }
1412 : // if unparseable items, write full text data
1413 12 : if (item->items().size() < 1) {
1414 0 : writeProperties(toTextBase(item), xml, ctx, true);
1415 : } else {
1416 : // if (textStyleType() != StyledPropertyListIdx::FIGURED_BASS)
1417 : // // if all items parsed and not unstiled, we simply have a special style: write it
1418 : // xml.tag("style", textStyle().name());
1419 26 : for (FiguredBassItem* fBItem : item->items()) {
1420 14 : write(fBItem, xml, ctx);
1421 : }
1422 12 : writeStyledProperties(item, xml);
1423 12 : writeItemProperties(item, xml, ctx);
1424 : }
1425 12 : xml.endElement();
1426 : }
1427 :
1428 14 : void TWrite::write(const FiguredBassItem* item, XmlWriter& xml, WriteContext&)
1429 : {
1430 14 : xml.startElement("FiguredBassItem", item);
1431 98 : xml.tag("brackets", {
1432 0 : { "b0", int(item->parenth1()) },
1433 0 : { "b1", int(item->parenth2()) },
1434 0 : { "b2", int(item->parenth3()) },
1435 0 : { "b3", int(item->parenth4()) },
1436 0 : { "b4", int(item->parenth5()) }
1437 : });
1438 :
1439 14 : if (item->prefix() != FiguredBassItem::Modifier::NONE) {
1440 1 : xml.tag("prefix", int(item->prefix()));
1441 : }
1442 14 : if (item->digit() != FBIDigitNone) {
1443 13 : xml.tag("digit", item->digit());
1444 : }
1445 14 : if (item->suffix() != FiguredBassItem::Modifier::NONE) {
1446 7 : xml.tag("suffix", int(item->suffix()));
1447 : }
1448 14 : if (item->contLine() != FiguredBassItem::ContLine::NONE) {
1449 1 : xml.tag("continuationLine", int(item->contLine()));
1450 : }
1451 14 : xml.endElement();
1452 28 : }
1453 :
1454 206 : void TWrite::write(const Fingering* item, XmlWriter& xml, WriteContext& ctx)
1455 : {
1456 206 : if (!ctx.canWrite(item)) {
1457 4 : return;
1458 : }
1459 202 : xml.startElement(item);
1460 202 : writeProperties(toTextBase(item), xml, ctx, true);
1461 202 : xml.endElement();
1462 : }
1463 :
1464 68 : void TWrite::write(const FretDiagram* item, XmlWriter& xml, WriteContext& ctx)
1465 : {
1466 68 : if (!ctx.canWrite(item)) {
1467 1 : return;
1468 : }
1469 67 : xml.startElement(item);
1470 :
1471 : static const std::array<Pid, 10> pids { {
1472 : Pid::MIN_DISTANCE,
1473 : Pid::FRET_OFFSET,
1474 : Pid::FRET_FRETS,
1475 : Pid::FRET_STRINGS,
1476 : Pid::FRET_NUT,
1477 : Pid::MAG,
1478 : Pid::ORIENTATION,
1479 : Pid::FRET_SHOW_FINGERINGS,
1480 : Pid::FRET_FINGERING,
1481 : Pid::EXCLUDE_VERTICAL_ALIGN
1482 : } };
1483 :
1484 : // Write properties first and only once
1485 737 : for (Pid p : pids) {
1486 670 : writeProperty(item, xml, p);
1487 : }
1488 67 : writeItemProperties(item, xml, ctx);
1489 :
1490 67 : if (item->harmony()) {
1491 34 : write(item->harmony(), xml, ctx);
1492 : }
1493 :
1494 67 : xml.startElement("fretDiagram");
1495 : {
1496 469 : for (int i = 0; i < item->strings(); ++i) {
1497 402 : FretItem::Marker m = item->marker(i);
1498 402 : std::vector<FretItem::Dot> allDots = item->dot(i);
1499 :
1500 402 : bool dotExists = false;
1501 602 : for (auto const& d : allDots) {
1502 402 : if (d.exists() && !d.isPartOfSlurBarre) { // Don't write dot if part of slur barré (will be generate during layout)
1503 202 : dotExists = true;
1504 202 : break;
1505 : }
1506 : }
1507 :
1508 : // Only write a string if we have anything to write
1509 402 : if (!dotExists && !m.exists()) {
1510 70 : continue;
1511 : }
1512 :
1513 : // Start the string writing
1514 996 : xml.startElement("string", { { "no", i } });
1515 :
1516 : // Write marker
1517 332 : if (m.exists()) {
1518 130 : xml.tag("marker", FretItem::markerTypeToName(m.mtype));
1519 : }
1520 :
1521 : // Write any dots
1522 664 : for (auto const& d : allDots) {
1523 332 : if (d.exists()) {
1524 : // TODO: write fingering
1525 808 : xml.tag("dot", { { "fret", d.fret } }, FretItem::dotTypeToName(d.dtype));
1526 : }
1527 : }
1528 :
1529 332 : xml.endElement();
1530 402 : }
1531 :
1532 337 : for (int fi = 1; fi <= item->frets(); ++fi) {
1533 270 : FretItem::Barre b = item->barre(fi);
1534 270 : if (!b.exists()) {
1535 245 : continue;
1536 : }
1537 :
1538 125 : xml.tag("barre", { { "start", b.startString }, { "end", b.endString } }, fi);
1539 : }
1540 : }
1541 67 : xml.endElement();
1542 67 : xml.endElement();
1543 559 : }
1544 :
1545 79 : void TWrite::write(const Glissando* item, XmlWriter& xml, WriteContext& ctx)
1546 : {
1547 79 : if (!ctx.canWrite(item)) {
1548 2 : return;
1549 : }
1550 77 : xml.startElement(item);
1551 :
1552 77 : xml.tagProperty("text", item->text(), item->propertyDefault(Pid::GLISS_TEXT));
1553 :
1554 77 : if (ctx.clipboardmode() && item->isHarpGliss().has_value()) {
1555 3 : xml.tagProperty("isHarpGliss", PropertyValue(item->isHarpGliss().value()));
1556 : }
1557 :
1558 308 : for (auto id : { Pid::GLISS_SHIFT, Pid::GLISS_EASEIN, Pid::GLISS_EASEOUT }) {
1559 231 : writeProperty(item, xml, id);
1560 : }
1561 :
1562 77 : writeProperties(static_cast<const SLine*>(item), xml, ctx);
1563 77 : xml.endElement();
1564 : }
1565 :
1566 304 : void TWrite::write(const GuitarBend* item, XmlWriter& xml, WriteContext& ctx)
1567 : {
1568 304 : if (!ctx.canWrite(item)) {
1569 0 : return;
1570 : }
1571 304 : xml.startElement(item);
1572 304 : xml.tag("guitarBendType", TConv::toXml(item->bendType()));
1573 304 : xml.tag("bendStartTimeFactor", item->startTimeFactor());
1574 304 : xml.tag("bendEndTimeFactor", item->endTimeFactor());
1575 :
1576 304 : if (item->targetTimeFactor().has_value()) {
1577 0 : xml.tag("bendTargetTimeFactor", item->targetTimeFactor().value());
1578 : }
1579 :
1580 304 : writeProperty(item, xml, Pid::DIRECTION);
1581 304 : writeProperty(item, xml, Pid::BEND_SHOW_HOLD_LINE);
1582 304 : if (item->isDive()) {
1583 142 : writeProperty(item, xml, Pid::GUITAR_DIVE_TAB_POS);
1584 142 : writeProperty(item, xml, Pid::VIBRATO_LINE_TYPE);
1585 142 : writeProperty(item, xml, Pid::GUITAR_DIVE_IS_SLACK);
1586 142 : if (item->bendType() == GuitarBendType::DIP || item->overlappingBendOrDive()) {
1587 0 : writeProperty(item, xml, Pid::GUITAR_BEND_AMOUNT);
1588 : }
1589 : }
1590 :
1591 304 : writeProperties(static_cast<const SLine*>(item), xml, ctx);
1592 :
1593 304 : GuitarBendHold* hold = item->holdLine();
1594 304 : if (hold) {
1595 0 : xml.startElement(hold);
1596 0 : writeProperties(static_cast<const SLine*>(hold), xml, ctx);
1597 0 : xml.endElement();
1598 : }
1599 :
1600 304 : xml.endElement();
1601 : }
1602 :
1603 0 : void TWrite::writeProperties(const GuitarBendSegment* item, XmlWriter& xml, WriteContext& ctx)
1604 : {
1605 0 : writeProperty(item, xml, Pid::BEND_VERTEX_OFF);
1606 :
1607 0 : GuitarBendText* text = item->bendText();
1608 0 : if (text && text->isUserModified()) {
1609 0 : xml.startElement(text);
1610 0 : writeProperties(toTextBase(text), xml, ctx, false);
1611 0 : xml.endElement();
1612 : }
1613 0 : }
1614 :
1615 1130 : void TWrite::writeProperties(const SLine* item, XmlWriter& xml, WriteContext& ctx)
1616 : {
1617 1130 : writeProperties(static_cast<const Spanner*>(item), xml, ctx);
1618 1130 : if (item->diagonal()) {
1619 79 : xml.tag("diagonal", item->diagonal());
1620 : }
1621 :
1622 5650 : for (Pid pid : { Pid::LINE_WIDTH,
1623 : Pid::LINE_STYLE,
1624 : Pid::DASH_LINE_LEN,
1625 6780 : Pid::DASH_GAP_LEN }) {
1626 4520 : if (item->propertyFlags(pid) == PropertyFlags::NOSTYLE) {
1627 1416 : writeProperty(item, xml, pid);
1628 : }
1629 : }
1630 :
1631 1130 : writeStyledProperties(item, xml);
1632 :
1633 1130 : if (!item->isUserModified()) {
1634 1127 : return;
1635 : }
1636 :
1637 3 : double _spatium = item->style().spatium();
1638 6 : for (const SpannerSegment* seg : item->spannerSegments()) {
1639 3 : xml.startElement("Segment", seg);
1640 3 : xml.tag("subtype", int(seg->spannerSegmentType()));
1641 3 : xml.tagPoint("offset", seg->offset() / _spatium);
1642 3 : xml.tagPoint("off2", seg->userOff2() / _spatium);
1643 3 : writeProperty(seg, xml, Pid::MIN_DISTANCE);
1644 3 : if (seg->isGuitarBendSegment()) {
1645 0 : writeProperties(static_cast<const GuitarBendSegment*>(seg), xml, ctx);
1646 : }
1647 3 : writeItemProperties(seg, xml, ctx);
1648 3 : xml.endElement();
1649 : }
1650 : }
1651 :
1652 2096 : void TWrite::writeProperties(const Spanner* item, XmlWriter& xml, WriteContext& ctx)
1653 : {
1654 2096 : writeProperty(item, xml, Pid::PLAY);
1655 2096 : if (item->anchor() == Spanner::Anchor::SEGMENT) {
1656 692 : int t2 = static_cast<int>(item->track2()) + ctx.trackDiff();
1657 692 : xml.tag("track2", t2);
1658 692 : xml.tagFraction("startTick", item->tick(), /* default = */ Fraction(-1, 0)); // Need to be able to write start of score, so a different default is needed
1659 692 : xml.tagFraction("ticks", item->ticks());
1660 1404 : } else if (!item->isGuitarBendHold()) {
1661 : /* GuitarBendHold lines are regenerated during layout by GuitarBend::updateHoldLine(),
1662 : * which recomputes their endpoints, so there is no point in serializing them */
1663 :
1664 1404 : const bool isPartialTieOrLV = item->isPartialTie() || item->isLaissezVib();
1665 1404 : const bool hasStartEndElements = item->startElement() && item->endElement();
1666 1404 : IF_ASSERT_FAILED(item->score()->isPaletteScore() || isPartialTieOrLV || hasStartEndElements) {
1667 0 : writeItemProperties(item, xml, ctx);
1668 0 : return;
1669 : }
1670 1404 : if (EngravingItem* startEl = item->startElement()) {
1671 1376 : EID startElEID = startEl->eid();
1672 1376 : if (!startElEID.isValid()) {
1673 165 : assert(item->anchor() == Spanner::Anchor::NOTE);
1674 165 : startElEID = startEl->assignNewEID();
1675 : }
1676 1376 : xml.tag("startElement", startElEID.toStdString());
1677 : }
1678 1404 : if (EngravingItem* endEl = item->endElement()) {
1679 1367 : EID endElEID = endEl->eid();
1680 1367 : IF_ASSERT_FAILED(endElEID.isValid()) {
1681 0 : endElEID = endEl->assignNewEID();
1682 : }
1683 1367 : xml.tag("endElement", endElEID.toStdString());
1684 : }
1685 : }
1686 2096 : writeItemProperties(item, xml, ctx);
1687 : }
1688 :
1689 50 : void TWrite::write(const GradualTempoChange* item, XmlWriter& xml, WriteContext& ctx)
1690 : {
1691 50 : xml.startElement(item);
1692 250 : for (Pid pid : { Pid::TEMPO_CHANGE_TYPE,
1693 : Pid::TEMPO_EASING_METHOD,
1694 : Pid::TEMPO_CHANGE_FACTOR,
1695 300 : Pid::SNAP_AFTER }) {
1696 200 : if (item->propertyFlags(pid) == PropertyFlags::NOSTYLE) {
1697 200 : writeProperty(item, xml, pid);
1698 : }
1699 : }
1700 50 : writeProperties(static_cast<const TextLineBase*>(item), xml, ctx);
1701 50 : xml.endElement();
1702 50 : }
1703 :
1704 697 : void TWrite::writeProperties(const TextLineBase* item, XmlWriter& xml, WriteContext& ctx)
1705 : {
1706 32062 : for (Pid pid : TextLineBase::textLineBasePropertyIds()) {
1707 31365 : if (item->propertyFlags(pid) == PropertyFlags::NOSTYLE) {
1708 10333 : writeProperty(item, xml, pid);
1709 : }
1710 : }
1711 :
1712 697 : writeProperties(static_cast<const SLine*>(item), xml, ctx);
1713 697 : }
1714 :
1715 0 : void TWrite::write(const Groups* item, XmlWriter& xml, WriteContext&)
1716 : {
1717 0 : xml.startElement("Groups");
1718 0 : for (const GroupNode& n : item->nodes()) {
1719 0 : xml.tag("Node", { { "pos", n.pos }, { "action", n.action } });
1720 : }
1721 0 : xml.endElement();
1722 0 : }
1723 :
1724 148 : void TWrite::write(const Hairpin* item, XmlWriter& xml, WriteContext& ctx)
1725 : {
1726 148 : if (!ctx.canWrite(item)) {
1727 0 : return;
1728 : }
1729 148 : xml.startElement(item);
1730 148 : xml.tag("subtype", int(item->hairpinType()));
1731 :
1732 1776 : for (Pid pid : { Pid::VELO_CHANGE,
1733 : Pid::HAIRPIN_CIRCLEDTIP,
1734 : Pid::SINGLE_NOTE_DYNAMICS,
1735 : Pid::VELO_CHANGE_METHOD,
1736 : Pid::VOICE_ASSIGNMENT,
1737 : Pid::DIRECTION,
1738 : Pid::CENTER_BETWEEN_STAVES,
1739 : Pid::SNAP_BEFORE,
1740 : Pid::SNAP_AFTER,
1741 : Pid::HAIRPIN_HEIGHT,
1742 1924 : Pid::HAIRPIN_CONT_HEIGHT }) {
1743 1628 : if (item->propertyFlags(pid) == PropertyFlags::NOSTYLE) {
1744 1332 : writeProperty(item, xml, pid);
1745 : }
1746 : }
1747 :
1748 148 : writeProperties(static_cast<const TextLineBase*>(item), xml, ctx);
1749 148 : xml.endElement();
1750 : }
1751 :
1752 48 : void TWrite::write(const HammerOnPullOff* item, XmlWriter& xml, WriteContext& ctx)
1753 : {
1754 48 : if (!ctx.canWrite(item)) {
1755 0 : return;
1756 : }
1757 :
1758 48 : xml.startElement(item);
1759 :
1760 48 : writeProperty(item, xml, Pid::PARTIAL_SPANNER_DIRECTION);
1761 :
1762 48 : writeProperties(toSlur(item), xml, ctx);
1763 :
1764 48 : xml.endElement();
1765 : }
1766 :
1767 48 : void TWrite::writeProperties(const HammerOnPullOffSegment* seg, XmlWriter& xml, WriteContext& ctx)
1768 : {
1769 103 : for (size_t i = 0; i < seg->hopoText().size(); ++i) {
1770 55 : HammerOnPullOffText* hopoText = seg->hopoText()[i];
1771 55 : if (!hopoText->isUserModified()) {
1772 0 : continue;
1773 : }
1774 55 : write(hopoText, xml, ctx, i);
1775 : }
1776 48 : }
1777 :
1778 55 : void TWrite::write(const HammerOnPullOffText* item, XmlWriter& xml, WriteContext& ctx, size_t idx)
1779 : {
1780 165 : xml.startElement(item, { { "idx", idx } });
1781 :
1782 55 : writeProperties(toTextBase(item), xml, ctx, /*writeText*/ false);
1783 55 : xml.endElement();
1784 110 : }
1785 :
1786 749 : static void writeHarmonyInfo(const HarmonyInfo* item, const Harmony* h, XmlWriter& xml, WriteContext& ctx)
1787 : {
1788 749 : xml.startElement("harmonyInfo");
1789 749 : if (item->rootTpc() != Tpc::TPC_INVALID || item->bassTpc() != Tpc::TPC_INVALID) {
1790 729 : int rRootTpc = item->rootTpc();
1791 729 : int rBassTpc = item->bassTpc();
1792 729 : if (h->staff()) {
1793 : // parent can be a fret diagram
1794 729 : const Segment* segment = h->getParentSeg();
1795 729 : Fraction tick = segment ? segment->tick() : Fraction(-1, 1);
1796 729 : const Interval& interval = h->staff()->transpose(tick);
1797 729 : if (ctx.clipboardmode() && !h->score()->style().styleB(Sid::concertPitch) && interval.chromatic) {
1798 0 : rRootTpc = Transpose::transposeTpc(item->rootTpc(), interval, true);
1799 0 : rBassTpc = Transpose::transposeTpc(item->bassTpc(), interval, true);
1800 : }
1801 : }
1802 :
1803 729 : if (item->id() > 0) {
1804 48 : xml.tag("extension", item->id());
1805 : }
1806 : // parser uses leading "=" as a hidden specifier for minor
1807 : // this may or may not currently be incorporated into _textName
1808 729 : String writeName = item->textName();
1809 729 : if (item->parsedChord() && item->parsedChord()->name().startsWith(u'=') && !writeName.startsWith(u'=')) {
1810 0 : writeName = u"=" + writeName;
1811 : }
1812 729 : if (!writeName.isEmpty()) {
1813 540 : xml.tag("name", writeName);
1814 : }
1815 729 : if (rRootTpc != Tpc::TPC_INVALID) {
1816 729 : xml.tag("root", rRootTpc);
1817 : }
1818 729 : if (rBassTpc != Tpc::TPC_INVALID) {
1819 9 : xml.tag("bass", rBassTpc);
1820 : }
1821 729 : } else {
1822 20 : xml.tag("name", item->textName());
1823 : }
1824 :
1825 749 : xml.endElement();
1826 749 : }
1827 :
1828 752 : void TWrite::write(const Harmony* item, XmlWriter& xml, WriteContext& ctx)
1829 : {
1830 752 : if (!ctx.canWrite(item) || item->chords().empty()) {
1831 4 : return;
1832 : }
1833 748 : xml.startElement(item);
1834 748 : writeProperty(item, xml, Pid::HARMONY_TYPE);
1835 748 : writeProperty(item, xml, Pid::PLAY);
1836 :
1837 : //! needed to genarate harmony_to_diagram.xml
1838 : // xml.tag("name", item->harmonyName());
1839 :
1840 : // check tpcs valid?
1841 748 : if (item->rootCase() != NoteCaseType::CAPITAL) {
1842 1 : xml.tag("rootCase", TConv::toXml(item->rootCase()));
1843 : }
1844 :
1845 748 : if (item->bassCase() != NoteCaseType::CAPITAL) {
1846 0 : xml.tag("bassCase", TConv::toXml(item->bassCase()));
1847 : }
1848 :
1849 1497 : for (const HarmonyInfo* info : item->chords()) {
1850 749 : writeHarmonyInfo(info, item, xml, ctx);
1851 748 : }
1852 :
1853 748 : for (const HDegree& hd : item->degreeList()) { // Do we really still need this?
1854 0 : HDegreeType tp = hd.type();
1855 0 : if (tp == HDegreeType::ADD || tp == HDegreeType::ALTER || tp == HDegreeType::SUBTRACT) {
1856 0 : xml.startElement("degree");
1857 0 : xml.tag("degree-value", hd.value());
1858 0 : xml.tag("degree-alter", hd.alter());
1859 0 : switch (tp) {
1860 0 : case HDegreeType::ADD:
1861 0 : xml.tag("degree-type", "add");
1862 0 : break;
1863 0 : case HDegreeType::ALTER:
1864 0 : xml.tag("degree-type", "alter");
1865 0 : break;
1866 0 : case HDegreeType::SUBTRACT:
1867 0 : xml.tag("degree-type", "subtract");
1868 0 : break;
1869 0 : default:
1870 0 : break;
1871 : }
1872 0 : xml.endElement();
1873 : }
1874 : }
1875 :
1876 748 : writeProperty(item, xml, Pid::HARMONY_DO_NOT_STACK_MODIFIERS);
1877 748 : writeProperty(item, xml, Pid::EXCLUDE_VERTICAL_ALIGN);
1878 748 : writeProperties(toTextBase(item), xml, ctx, false);
1879 : //Pid::HARMONY_VOICE_LITERAL, Pid::HARMONY_VOICING, Pid::HARMONY_DURATION
1880 : //written by the above function call because they are part of element style
1881 748 : xml.endElement();
1882 : }
1883 :
1884 60 : void TWrite::write(const HarmonicMark* item, XmlWriter& xml, WriteContext& ctx)
1885 : {
1886 60 : if (!ctx.canWrite(item)) {
1887 0 : return;
1888 : }
1889 60 : xml.startElement(item);
1890 60 : writeProperties(static_cast<const TextLineBase*>(item), xml, ctx);
1891 60 : xml.endElement();
1892 : }
1893 :
1894 12 : void TWrite::write(const HarpPedalDiagram* item, XmlWriter& xml, WriteContext& ctx)
1895 : {
1896 12 : if (!ctx.canWrite(item)) {
1897 0 : return;
1898 : }
1899 12 : xml.startElement(item);
1900 12 : writeProperty(item, xml, Pid::HARP_IS_DIAGRAM);
1901 :
1902 : // Write vector of harp strings. Order is always D, C, B, E, F, G, A
1903 12 : xml.startElement("pedalState");
1904 192 : for (size_t idx = 0; idx < item->getPedalState().size(); idx++) {
1905 336 : xml.tag("string", { { "name", idx } }, static_cast<int>(item->getPedalState().at(idx)));
1906 : }
1907 12 : xml.endElement();
1908 :
1909 12 : writeProperties(toTextBase(item), xml, ctx, true);
1910 12 : xml.endElement();
1911 84 : }
1912 :
1913 0 : void TWrite::write(const Hook* item, XmlWriter& xml, WriteContext& ctx)
1914 : {
1915 0 : xml.startElement(item);
1916 0 : xml.tag("name", SymNames::nameForSymId(item->sym()));
1917 0 : if (item->scoreFont()) {
1918 0 : xml.tag("font", item->scoreFont()->name());
1919 : }
1920 0 : writeProperties(static_cast<const BSymbol*>(item), xml, ctx);
1921 0 : xml.endElement();
1922 0 : }
1923 :
1924 36 : void TWrite::writeProperties(const BSymbol* item, XmlWriter& xml, WriteContext& ctx)
1925 : {
1926 41 : for (const EngravingItem* e : item->leafs()) {
1927 5 : writeItem(e, xml, ctx);
1928 : }
1929 36 : writeItemProperties(item, xml, ctx);
1930 36 : }
1931 :
1932 23 : void TWrite::write(const Image* item, XmlWriter& xml, WriteContext& ctx)
1933 : {
1934 23 : xml.startElement(item);
1935 23 : writeProperties(static_cast<const BSymbol*>(item), xml, ctx);
1936 :
1937 23 : xml.tag("path", item->storeItem() ? item->storeItem()->hashName() : std::string());
1938 :
1939 23 : writeProperty(item, xml, Pid::AUTOSCALE);
1940 23 : writeProperty(item, xml, Pid::SIZE);
1941 23 : writeProperty(item, xml, Pid::LOCK_ASPECT_RATIO);
1942 23 : writeProperty(item, xml, Pid::SIZE_IS_SPATIUM);
1943 :
1944 23 : xml.endElement();
1945 23 : }
1946 :
1947 1762 : void TWrite::write(const Instrument* item, XmlWriter& xml, WriteContext&, const Part* part)
1948 : {
1949 1762 : if (item->id().isEmpty()) {
1950 39 : xml.startElement("Instrument");
1951 : } else {
1952 5169 : xml.startElement("Instrument", { { "id", item->id() } });
1953 : }
1954 :
1955 1762 : if (!item->soundId().empty()) {
1956 0 : xml.tag("soundId", item->soundId());
1957 : }
1958 :
1959 1762 : if (!item->instrumentLabel().empty()) {
1960 1428 : write(item->instrumentLabel(), xml);
1961 : }
1962 :
1963 : // if (!_trackName.empty())
1964 1762 : xml.tag("trackName", item->trackName());
1965 1762 : if (item->minPitchP() > MIN_PITCH) {
1966 1173 : xml.tag("minPitchP", item->minPitchP());
1967 : }
1968 1762 : if (item->maxPitchP() < MAX_PITCH) {
1969 1173 : xml.tag("maxPitchP", item->maxPitchP());
1970 : }
1971 1762 : if (item->minPitchA() > MIN_PITCH) {
1972 1173 : xml.tag("minPitchA", item->minPitchA());
1973 : }
1974 1762 : if (item->maxPitchA() < MAX_PITCH) {
1975 1173 : xml.tag("maxPitchA", item->maxPitchA());
1976 : }
1977 1762 : if (item->transpose().diatonic) {
1978 350 : xml.tag("transposeDiatonic", item->transpose().diatonic);
1979 : }
1980 1762 : if (item->transpose().chromatic) {
1981 350 : xml.tag("transposeChromatic", item->transpose().chromatic);
1982 : }
1983 1762 : if (!item->musicXmlId().isEmpty()) {
1984 1723 : xml.tag("instrumentId", item->musicXmlId());
1985 : }
1986 1762 : if (item->useDrumset()) {
1987 39 : xml.tag("useDrumset", item->useDrumset());
1988 39 : item->drumset()->save(xml);
1989 : }
1990 2775 : for (size_t i = 0; i < item->cleffTypeCount(); ++i) {
1991 1013 : ClefTypeList ct = item->clefType(i);
1992 1013 : if (ct.concertClef == ct.transposingClef) {
1993 999 : if (ct.concertClef != ClefType::G) {
1994 509 : if (i) {
1995 1748 : xml.tag("clef", { { "staff", i + 1 } }, TConv::toXml(ct.concertClef));
1996 : } else {
1997 72 : xml.tag("clef", TConv::toXml(ct.concertClef));
1998 : }
1999 : }
2000 : } else {
2001 14 : if (i) {
2002 0 : xml.tag("concertClef", { { "staff", i + 1 } }, TConv::toXml(ct.concertClef));
2003 0 : xml.tag("transposingClef", { { "staff", i + 1 } }, TConv::toXml(ct.transposingClef));
2004 : } else {
2005 14 : xml.tag("concertClef", TConv::toXml(ct.concertClef));
2006 14 : xml.tag("transposingClef", TConv::toXml(ct.transposingClef));
2007 : }
2008 : }
2009 : }
2010 :
2011 1762 : if (item->singleNoteDynamics() != item->getSingleNoteDynamicsFromTemplate()) {
2012 553 : xml.tag("singleNoteDynamics", item->singleNoteDynamics());
2013 : }
2014 :
2015 1762 : if (item->glissandoStyle() != GlissandoStyle::CHROMATIC) {
2016 44 : xml.tag("glissandoStyle", TConv::toXml(item->glissandoStyle()));
2017 : }
2018 :
2019 1762 : if (!item->stringData()->isNull()) {
2020 424 : write(item->stringData(), xml);
2021 : }
2022 :
2023 1762 : for (const NamedEventList& a : item->midiActions()) {
2024 0 : write(&a, xml, "MidiAction");
2025 : }
2026 10008 : for (const MidiArticulation& a : item->articulation()) {
2027 8246 : write(&a, xml);
2028 : }
2029 3728 : for (const InstrChannel* a : item->channel()) {
2030 1966 : write(a, xml, part);
2031 : }
2032 1762 : xml.endElement();
2033 3922 : }
2034 :
2035 2728 : static void midi_event_write(const MidiCoreEvent& e, XmlWriter& xml)
2036 : {
2037 2728 : switch (e.type()) {
2038 0 : case ME_NOTEON:
2039 0 : xml.tag("note-on", { { "channel", e.channel() }, { "pitch", e.pitch() }, { "velo", e.velo() } });
2040 0 : break;
2041 :
2042 0 : case ME_NOTEOFF:
2043 0 : xml.tag("note-off", { { "channel", e.channel() }, { "pitch", e.pitch() }, { "velo", e.velo() } });
2044 0 : break;
2045 :
2046 2728 : case ME_CONTROLLER:
2047 2728 : if (e.controller() == CTRL_PROGRAM) {
2048 1877 : if (e.channel() == 0) {
2049 5631 : xml.tag("program", { { "value", e.value() } });
2050 : } else {
2051 0 : xml.tag("program", { { "channel", e.channel() }, { "value", e.value() } });
2052 : }
2053 : } else {
2054 851 : if (e.channel() == 0) {
2055 3404 : xml.tag("controller", { { "ctrl", e.controller() }, { "value", e.value() } });
2056 : } else {
2057 0 : xml.tag("controller", { { "channel", e.channel() }, { "ctrl", e.controller() }, { "value", e.value() } });
2058 : }
2059 : }
2060 2728 : break;
2061 0 : default:
2062 0 : LOGD("MidiCoreEvent::write: unknown type");
2063 0 : break;
2064 : }
2065 5456 : }
2066 :
2067 1966 : void TWrite::write(const InstrChannel* item, XmlWriter& xml, const Part* part)
2068 : {
2069 1966 : if (item->name().isEmpty() || item->name() == InstrChannel::DEFAULT_NAME) {
2070 1717 : xml.startElement("Channel");
2071 : } else {
2072 747 : xml.startElement("Channel", { { "name", item->name() } });
2073 : }
2074 1966 : if (item->color() != InstrChannel::DEFAULT_COLOR) {
2075 0 : xml.tag("color", item->color());
2076 : }
2077 :
2078 15728 : for (const MidiCoreEvent& e : item->initList()) {
2079 13762 : if (e.type() == ME_INVALID) {
2080 89 : continue;
2081 : }
2082 13673 : if (e.type() == ME_CONTROLLER) {
2083 : // Don't write bank if automatically switched, but always write if switched by the user
2084 13673 : if (e.dataA() == CTRL_HBANK && e.dataB() == 0 && !item->userBankController()) {
2085 1888 : continue;
2086 : }
2087 11785 : if (e.dataA() == CTRL_LBANK && e.dataB() == 0 && !item->userBankController()) {
2088 1892 : continue;
2089 : }
2090 9893 : if (e.dataA() == CTRL_VOLUME && e.dataB() == 100) {
2091 1605 : continue;
2092 : }
2093 8288 : if (e.dataA() == CTRL_PANPOT && e.dataB() == 64) {
2094 1713 : continue;
2095 : }
2096 6575 : if (e.dataA() == CTRL_REVERB_SEND && e.dataB() == 0) {
2097 1924 : continue;
2098 : }
2099 4651 : if (e.dataA() == CTRL_CHORUS_SEND && e.dataB() == 0) {
2100 1923 : continue;
2101 : }
2102 : }
2103 :
2104 2728 : midi_event_write(e, xml);
2105 : }
2106 1966 : if (!MScore::testMode) {
2107 : // xml.tag("synti", ::synti->name(synti));
2108 0 : xml.tag("synti", item->synti());
2109 : }
2110 :
2111 1966 : if (part && part->masterScore()->exportMidiMapping() && part->score() == part->masterScore()) {
2112 242 : xml.tag("midiPort", part->masterScore()->midiMapping(item->channel())->port());
2113 242 : xml.tag("midiChannel", part->masterScore()->midiMapping(item->channel())->channel());
2114 : }
2115 1966 : for (const NamedEventList& a : item->midiActions) {
2116 0 : write(&a, xml, "MidiAction");
2117 : }
2118 1966 : for (const MidiArticulation& a : item->articulation) {
2119 0 : write(&a, xml);
2120 : }
2121 1966 : xml.endElement();
2122 2215 : }
2123 :
2124 8246 : void TWrite::write(const MidiArticulation* item, XmlWriter& xml)
2125 : {
2126 8246 : if (item->name.isEmpty()) {
2127 1115 : xml.startElement("Articulation");
2128 : } else {
2129 21393 : xml.startElement("Articulation", { { "name", item->name } });
2130 : }
2131 8246 : if (!item->descr.isEmpty()) {
2132 0 : xml.tag("descr", item->descr);
2133 : }
2134 8246 : xml.tag("velocity", item->velocity);
2135 8246 : xml.tag("gateTime", item->gateTime);
2136 8246 : xml.endElement();
2137 15377 : }
2138 :
2139 0 : void TWrite::write(const StaffLabel& item, XmlWriter& xml)
2140 : {
2141 0 : xml.startElement("StaffLabel");
2142 0 : writeProperties(item, xml);
2143 0 : xml.endElement();
2144 0 : }
2145 :
2146 1428 : void TWrite::writeProperties(const StaffLabel& item, XmlWriter& xml)
2147 : {
2148 1428 : String longName = item.longName();
2149 1428 : if (!longName.empty()) {
2150 1405 : xml.writeXml(u"longName", lineBreakToTag(longName));
2151 : }
2152 :
2153 1428 : String shortName = item.shortName();
2154 1428 : if (!shortName.empty()) {
2155 1245 : xml.writeXml(u"shortName", lineBreakToTag(shortName));
2156 : }
2157 1428 : }
2158 :
2159 1428 : void TWrite::write(const InstrumentLabel& item, XmlWriter& xml)
2160 : {
2161 1428 : xml.startElement("InstrumentLabel");
2162 1428 : writeProperties(item, xml);
2163 1428 : xml.endElement();
2164 1428 : }
2165 :
2166 1428 : void TWrite::writeProperties(const InstrumentLabel& item, XmlWriter& xml)
2167 : {
2168 1428 : writeProperties(static_cast<StaffLabel>(item), xml);
2169 :
2170 1428 : String transposition = item.transposition();
2171 1428 : if (!transposition.empty()) {
2172 0 : xml.writeXml(u"transposition", lineBreakToTag(transposition));
2173 : }
2174 :
2175 1428 : if (!item.showTranspositionLong()) {
2176 0 : xml.tag("showTranspositionLong", item.showTranspositionLong());
2177 : }
2178 :
2179 1428 : if (!item.showTranspositionShort()) {
2180 0 : xml.tag("showTranspositionShort", item.showTranspositionShort());
2181 : }
2182 :
2183 1428 : if (item.number() != 0) {
2184 2 : xml.tag("number", item.number());
2185 : }
2186 :
2187 1428 : if (!item.showNumberLong()) {
2188 1 : xml.tag("showNumberLong", item.showNumberLong());
2189 : }
2190 :
2191 1428 : if (!item.showNumberShort()) {
2192 1 : xml.tag("showNumberShort", item.showNumberShort());
2193 : }
2194 :
2195 1428 : if (item.useCustomName()) {
2196 0 : xml.tag("useCustomName", item.useCustomName());
2197 : }
2198 :
2199 1428 : if (!item.customNameLong().empty()) {
2200 0 : xml.writeXml(u"customNameLong", item.customNameLong());
2201 : }
2202 :
2203 1428 : if (!item.customNameShort().empty()) {
2204 0 : xml.writeXml(u"customNameShort", item.customNameShort());
2205 : }
2206 :
2207 1428 : if (!item.allowGroupName()) {
2208 1 : xml.tag("allowGroupName", item.allowGroupName());
2209 : }
2210 :
2211 1428 : if (!item.customNameLongGroup().empty()) {
2212 0 : xml.writeXml(u"customNameLongGroup", item.customNameLongGroup());
2213 : }
2214 :
2215 1428 : if (!item.customNameShortGroup().empty()) {
2216 0 : xml.writeXml(u"customNameShortGroup", item.customNameShortGroup());
2217 : }
2218 :
2219 1428 : if (item.useCustomGroupName()) {
2220 0 : xml.tag("useCustomGroupName", item.useCustomGroupName());
2221 : }
2222 :
2223 1428 : if (!item.customNameLongIndividual().empty()) {
2224 0 : xml.writeXml(u"customNameLongIndividual", item.customNameLongIndividual());
2225 : }
2226 :
2227 1428 : if (!item.customNameShortIndividual().empty()) {
2228 0 : xml.writeXml(u"customNameShortIndividual", item.customNameShortIndividual());
2229 : }
2230 :
2231 1428 : if (item.useCustomIndividualName()) {
2232 0 : xml.tag("useCustomIndividualName", item.useCustomIndividualName());
2233 : }
2234 1428 : }
2235 :
2236 0 : void TWrite::write(const NamedEventList* item, XmlWriter& xml, const AsciiStringView& n)
2237 : {
2238 0 : xml.startElement(n, { { "name", item->name } });
2239 0 : if (!item->descr.empty()) {
2240 0 : xml.tag("descr", item->descr);
2241 : }
2242 0 : for (const MidiCoreEvent& e : item->events) {
2243 0 : midi_event_write(e, xml);
2244 : }
2245 0 : xml.endElement();
2246 0 : }
2247 :
2248 24 : void TWrite::write(const InstrumentChange* item, XmlWriter& xml, WriteContext& ctx)
2249 : {
2250 24 : xml.startElement(item);
2251 24 : write(item->instrument(), xml, ctx, item->part());
2252 24 : if (item->init()) {
2253 1 : xml.tag("init", item->init());
2254 : }
2255 24 : writeProperties(toTextBase(item), xml, ctx, true);
2256 24 : xml.endElement();
2257 24 : }
2258 :
2259 71 : void TWrite::write(const Jump* item, XmlWriter& xml, WriteContext& ctx)
2260 : {
2261 71 : xml.startElement(item);
2262 71 : writeProperties(toTextBase(item), xml, ctx, true);
2263 71 : xml.tag("jumpTo", item->jumpTo());
2264 71 : xml.tag("playUntil", item->playUntil());
2265 71 : xml.tag("continueAt", item->continueAt());
2266 71 : writeProperty(item, xml, Pid::PLAY_REPEATS);
2267 71 : xml.endElement();
2268 71 : }
2269 :
2270 1226 : void TWrite::write(const KeySig* item, XmlWriter& xml, WriteContext& ctx)
2271 : {
2272 1226 : xml.startElement(item);
2273 1226 : writeItemProperties(item, xml, ctx);
2274 1226 : if (item->isAtonal()) {
2275 7 : xml.tag("custom", 1);
2276 : } else {
2277 1219 : xml.tag("concertKey", int(item->concertKey()));
2278 1219 : if (item->concertKey() != item->key()) {
2279 114 : xml.tag("actualKey", int(item->key()));
2280 : }
2281 1219 : if (item->isCustom()) {
2282 0 : xml.tag("custom", 1);
2283 0 : for (const CustDef& cd : item->customKeyDefs()) {
2284 0 : xml.startElement("CustDef");
2285 0 : xml.tag("sym", SymNames::nameForSymId(cd.sym));
2286 0 : xml.tag("def", { { "degree", cd.degree }, { "xAlt", cd.xAlt.val() }, { "octAlt", cd.octAlt } });
2287 0 : xml.endElement();
2288 : }
2289 : }
2290 : }
2291 :
2292 1226 : if (item->mode() != KeyMode::UNKNOWN) {
2293 312 : xml.tag("mode", TConv::toXml(item->mode()));
2294 : }
2295 :
2296 1226 : writeProperty(item, xml, Pid::SHOW_COURTESY);
2297 1226 : writeProperty(item, xml, Pid::IS_COURTESY);
2298 :
2299 1226 : if (item->forInstrumentChange()) {
2300 0 : xml.tag("forInstrumentChange", true);
2301 : }
2302 1226 : xml.endElement();
2303 1226 : }
2304 :
2305 21 : void TWrite::write(const LaissezVib* item, XmlWriter& xml, WriteContext& ctx)
2306 : {
2307 21 : xml.startElement(item);
2308 21 : writeProperty(item, xml, Pid::MIN_LENGTH);
2309 21 : writeProperty(item, xml, Pid::TIE_PLACEMENT);
2310 21 : writeProperties(static_cast<const SlurTie*>(item), xml, ctx);
2311 21 : xml.endElement();
2312 21 : }
2313 :
2314 531 : void TWrite::write(const LayoutBreak* item, XmlWriter& xml, WriteContext& ctx)
2315 : {
2316 531 : xml.startElement(item);
2317 531 : writeItemProperties(item, xml, ctx);
2318 :
2319 3717 : for (auto id :
2320 : { Pid::LAYOUT_BREAK, Pid::PAUSE, Pid::START_WITH_LONG_NAMES, Pid::START_WITH_MEASURE_ONE, Pid::FIRST_SYSTEM_INDENTATION,
2321 4248 : Pid::SHOW_COURTESY }) {
2322 3186 : writeProperty(item, xml, id);
2323 : }
2324 :
2325 531 : xml.endElement();
2326 531 : }
2327 :
2328 0 : void TWrite::write(const LedgerLine* item, XmlWriter& xml, WriteContext&)
2329 : {
2330 0 : xml.startElement(item);
2331 0 : xml.tag("lineWidth", item->width() / item->spatium());
2332 0 : xml.tag("lineLen", item->len() / item->spatium());
2333 0 : if (!item->vertical()) {
2334 0 : xml.tag("vertical", item->vertical());
2335 : }
2336 0 : xml.endElement();
2337 0 : }
2338 :
2339 52 : void TWrite::write(const LetRing* item, XmlWriter& xml, WriteContext& ctx)
2340 : {
2341 52 : if (!ctx.canWrite(item)) {
2342 0 : return;
2343 : }
2344 52 : xml.startElement(item);
2345 52 : writeProperties(static_cast<const TextLineBase*>(item), xml, ctx);
2346 52 : xml.endElement();
2347 : }
2348 :
2349 290 : void TWrite::write(const Location* item, XmlWriter& xml, WriteContext&)
2350 : {
2351 : static constexpr Location relDefaults = Location::relative();
2352 :
2353 290 : DO_ASSERT(item->isRelative());
2354 290 : xml.startElement("location");
2355 290 : xml.tag("staves", item->staff(), relDefaults.staff());
2356 290 : xml.tag("voices", item->voice(), relDefaults.voice());
2357 290 : xml.tag("measures", item->measure(), relDefaults.measure());
2358 290 : xml.tagFraction("fractions", item->frac().reduced(), relDefaults.frac());
2359 290 : xml.tag("grace", item->graceIndex(), relDefaults.graceIndex());
2360 290 : xml.tag("notes", item->note(), relDefaults.note());
2361 290 : xml.tag("timeTick", item->isTimeTick(), false);
2362 290 : xml.endElement();
2363 290 : }
2364 :
2365 448 : void TWrite::write(const Lyrics* item, XmlWriter& xml, WriteContext& ctx)
2366 : {
2367 448 : if (!ctx.canWrite(item)) {
2368 5 : return;
2369 : }
2370 443 : xml.startElement(item);
2371 443 : writeProperty(item, xml, Pid::VERSE);
2372 443 : if (item->syllabic() != LyricsSyllabic::SINGLE) {
2373 189 : xml.tag("syllabic", TConv::toXml(item->syllabic()));
2374 : }
2375 443 : writeProperty(item, xml, Pid::LYRIC_TICKS);
2376 443 : writeProperty(item, xml, Pid::CENTER_BETWEEN_STAVES);
2377 :
2378 443 : writeProperties(toTextBase(item), xml, ctx, true);
2379 443 : if (item->separator() && !item->separator()->generated()) {
2380 0 : write(item->separator(), xml, ctx);
2381 : }
2382 443 : xml.endElement();
2383 : }
2384 :
2385 2 : void TWrite::write(const LyricsLine* item, XmlWriter& xml, WriteContext& ctx)
2386 : {
2387 2 : if (!ctx.canWrite(item)) {
2388 0 : return;
2389 : }
2390 2 : xml.startElement(item);
2391 2 : writeProperties(static_cast<const SLine*>(item), xml, ctx);
2392 2 : xml.endElement();
2393 : }
2394 :
2395 75 : void TWrite::write(const Marker* item, XmlWriter& xml, WriteContext& ctx)
2396 : {
2397 75 : xml.startElement(item);
2398 75 : writeProperties(toTextBase(item), xml, ctx, true);
2399 75 : xml.tag("label", item->label());
2400 75 : writeProperty(item, xml, Pid::MARKER_TYPE);
2401 75 : writeProperty(item, xml, Pid::MARKER_CENTER_ON_SYMBOL);
2402 75 : xml.endElement();
2403 75 : }
2404 :
2405 0 : void TWrite::write(const MeasureNumber* item, XmlWriter& xml, WriteContext& ctx)
2406 : {
2407 0 : if (!ctx.canWrite(item)) {
2408 0 : return;
2409 : }
2410 0 : xml.startElement(item);
2411 0 : writeProperties(toTextBase(item), xml, ctx, true);
2412 0 : xml.endElement();
2413 : }
2414 :
2415 82 : void TWrite::write(const MeasureRepeat* item, XmlWriter& xml, WriteContext& ctx)
2416 : {
2417 82 : xml.startElement(item);
2418 82 : writeProperty(item, xml, Pid::SUBTYPE);
2419 82 : writeProperties(toRest(item), xml, ctx);
2420 82 : writeProperty(item, xml, Pid::MEASURE_REPEAT_NUMBER_OFFSET);
2421 82 : writeItems(item->el(), xml, ctx);
2422 82 : xml.endElement();
2423 82 : }
2424 :
2425 82 : void TWrite::writeProperties(const Rest* item, XmlWriter& xml, WriteContext& ctx)
2426 : {
2427 82 : writeProperties(toChordRest(item), xml, ctx);
2428 82 : }
2429 :
2430 34 : void TWrite::write(const MMRest* item, XmlWriter& xml, WriteContext& ctx)
2431 : {
2432 34 : xml.startElement("Rest"); // for compatibility, see also Measure::readVoice()
2433 34 : writeProperties(toChordRest(item), xml, ctx);
2434 34 : writeProperty(item, xml, Pid::MMREST_NUMBER_OFFSET);
2435 34 : writeProperty(item, xml, Pid::MMREST_NUMBER_VISIBLE);
2436 34 : writeItems(item->el(), xml, ctx);
2437 34 : xml.endElement();
2438 34 : }
2439 :
2440 0 : void TWrite::write(const MMRestRange* item, XmlWriter& xml, WriteContext& ctx)
2441 : {
2442 0 : if (!ctx.canWrite(item)) {
2443 0 : return;
2444 : }
2445 0 : xml.startElement(item);
2446 0 : writeProperties(toTextBase(item), xml, ctx, true);
2447 0 : xml.endElement();
2448 : }
2449 :
2450 23580 : void TWrite::write(const Note* item, XmlWriter& xml, WriteContext& ctx)
2451 : {
2452 23580 : xml.startElement(item);
2453 23580 : writeItemProperties(item, xml, ctx);
2454 :
2455 23580 : if (item->accidental()) {
2456 2608 : write(item->accidental(), xml, ctx);
2457 : }
2458 23580 : writeItems(item->el(), xml, ctx);
2459 23580 : bool write_dots = false;
2460 24028 : for (NoteDot* dot : item->dots()) {
2461 1344 : if (!dot->offset().isNull() || !dot->visible() || dot->color() != ctx.configuration()->defaultColor()
2462 1344 : || dot->visible() != item->visible()) {
2463 0 : write_dots = true;
2464 0 : break;
2465 : }
2466 : }
2467 23580 : if (write_dots) {
2468 0 : for (NoteDot* dot : item->dots()) {
2469 0 : write(dot, xml, ctx);
2470 : }
2471 : }
2472 :
2473 23580 : if (item->laissezVib()) {
2474 21 : write(item->laissezVib(), xml, ctx);
2475 : }
2476 :
2477 23580 : if (item->incomingPartialTie()) {
2478 28 : write(item->incomingPartialTie(), xml, ctx);
2479 : }
2480 :
2481 23580 : if (item->outgoingPartialTie()) {
2482 16 : write(item->outgoingPartialTie(), xml, ctx);
2483 : }
2484 :
2485 23580 : if (Tie* tieBack = item->tieBackNonPartial()) {
2486 599 : Note* tieStartNote = tieBack->startNote();
2487 599 : const std::optional<Fraction>& firstClipboardTick = ctx.firstClipboardTick();
2488 608 : bool tieStartOutOfRange = firstClipboardTick.has_value() && tieStartNote
2489 608 : && tieStartNote->tick() < firstClipboardTick.value();
2490 599 : if (!tieStartOutOfRange) {
2491 598 : TWrite::writeItem(tieBack, xml, ctx);
2492 : }
2493 : }
2494 :
2495 23580 : if ((item->chord() == 0 || item->chord()->playEventType() != PlayEventType::Auto) && !item->playEvents().empty()) {
2496 25 : xml.startElement("Events");
2497 50 : for (const NoteEvent& e : item->playEvents()) {
2498 25 : write(&e, xml, ctx);
2499 : }
2500 25 : xml.endElement();
2501 : }
2502 471600 : for (Pid id : { Pid::PITCH, Pid::CENT_OFFSET, Pid::TPC1, Pid::TPC2, Pid::SMALL, Pid::MIRROR_HEAD, Pid::DOT_POSITION,
2503 : Pid::HEAD_SCHEME, Pid::HEAD_GROUP, Pid::USER_VELOCITY, Pid::PLAY, Pid::TUNING, Pid::FRET, Pid::STRING,
2504 495180 : Pid::GHOST, Pid::DEAD, Pid::HEAD_TYPE, Pid::FIXED, Pid::FIXED_LINE }) {
2505 448020 : writeProperty(item, xml, id);
2506 : }
2507 :
2508 23965 : for (Spanner* e : item->spannerBack()) {
2509 385 : TWrite::writeItem(e, xml, ctx);
2510 : }
2511 :
2512 23675 : for (EngravingItem* e : item->chord()->el()) {
2513 95 : if (e->isChordLine() && toChordLine(e)->note() && toChordLine(e)->note() == item) {
2514 69 : write(toChordLine(e), xml, ctx);
2515 : }
2516 : }
2517 :
2518 23580 : if (item->overrideBendVisibilityRules()) {
2519 0 : xml.tag("overrideBendVisibilityRules", true);
2520 : }
2521 :
2522 23580 : writeProperty(item, xml, Pid::HIDE_GENERATED_PARENTHESES);
2523 :
2524 23580 : xml.endElement();
2525 23580 : }
2526 :
2527 25 : void TWrite::write(const NoteEvent* item, XmlWriter& xml, WriteContext&)
2528 : {
2529 25 : xml.startElement("Event");
2530 25 : xml.tag("pitch", item->pitch(), 0);
2531 25 : xml.tag("ontime", item->ontime(), 0);
2532 25 : xml.tag("len", item->len(), NoteEvent::NOTE_LENGTH);
2533 25 : xml.endElement();
2534 25 : }
2535 :
2536 8 : void TWrite::write(const NoteDot* item, XmlWriter& xml, WriteContext& ctx)
2537 : {
2538 8 : xml.startElement(item);
2539 8 : writeItemProperties(item, xml, ctx);
2540 8 : xml.endElement();
2541 8 : }
2542 :
2543 0 : void TWrite::write(const NoteHead* item, XmlWriter& xml, WriteContext& ctx)
2544 : {
2545 0 : write(static_cast<const Symbol*>(item), xml, ctx);
2546 0 : }
2547 :
2548 2 : void TWrite::write(const NoteLine* item, XmlWriter& xml, WriteContext& ctx)
2549 : {
2550 2 : if (!ctx.canWrite(item)) {
2551 0 : return;
2552 : }
2553 2 : xml.startElement(item);
2554 2 : writeProperties(static_cast<const TextLineBase*>(item), xml, ctx);
2555 2 : xml.endElement();
2556 : }
2557 :
2558 108 : void TWrite::write(const Ottava* item, XmlWriter& xml, WriteContext& ctx)
2559 : {
2560 108 : if (!ctx.canWrite(item)) {
2561 0 : return;
2562 : }
2563 108 : xml.startElement(item);
2564 324 : for (Pid pid : { Pid::OTTAVA_TYPE, Pid::NUMBERS_ONLY }) {
2565 216 : if (item->propertyFlags(pid) == PropertyFlags::NOSTYLE) {
2566 108 : writeProperty(item, xml, pid);
2567 : }
2568 : }
2569 108 : writeProperties(static_cast<const TextLineBase*>(item), xml, ctx);
2570 108 : xml.endElement();
2571 : }
2572 :
2573 0 : void TWrite::write(const Page* item, XmlWriter& xml, WriteContext& ctx)
2574 : {
2575 0 : xml.startElement(item);
2576 0 : for (System* system : item->systems()) {
2577 0 : write(system, xml, ctx);
2578 : }
2579 0 : xml.endElement();
2580 0 : }
2581 :
2582 45 : void TWrite::write(const PalmMute* item, XmlWriter& xml, WriteContext& ctx)
2583 : {
2584 45 : if (!ctx.canWrite(item)) {
2585 0 : return;
2586 : }
2587 45 : xml.startElement(item);
2588 45 : writeProperties(static_cast<const TextLineBase*>(item), xml, ctx);
2589 45 : xml.endElement();
2590 : }
2591 :
2592 300 : void TWrite::write(const Parenthesis* item, XmlWriter& xml, WriteContext& ctx)
2593 : {
2594 300 : if (!ctx.canWrite(item)) {
2595 0 : return;
2596 : }
2597 :
2598 300 : xml.startElement(item);
2599 300 : writeProperty(item, xml, Pid::HORIZONTAL_DIRECTION);
2600 300 : writeItemProperties(item, xml, ctx);
2601 300 : xml.endElement();
2602 : }
2603 :
2604 1738 : void TWrite::write(const Part* item, XmlWriter& xml, WriteContext& ctx)
2605 : {
2606 1738 : if (item->isSharedPart()) {
2607 1 : write(toSharedPart(item), xml, ctx);
2608 1 : return;
2609 : }
2610 :
2611 6948 : xml.startElement(item, { { "id", item->id().toUint64() } });
2612 :
2613 1737 : writeItemEid(item, xml);
2614 :
2615 1737 : writeProperties(item, xml, ctx);
2616 :
2617 1737 : if (SharedPart* sharedPart = item->sharedPart()) {
2618 2 : DO_ASSERT(sharedPart->eid().isValid());
2619 2 : xml.tag("sharedPart", sharedPart->eid().toStdString());
2620 : }
2621 :
2622 1737 : xml.endElement();
2623 3474 : }
2624 :
2625 1 : void TWrite::write(const SharedPart* item, XmlWriter& xml, WriteContext& ctx)
2626 : {
2627 4 : xml.startElement(item, { { "id", item->id().toUint64() } });
2628 :
2629 1 : writeItemEid(item, xml);
2630 :
2631 1 : xml.tag(propertyName(Pid::SHARED_PART_ENABLED), item->getProperty(Pid::SHARED_PART_ENABLED).toBool());
2632 :
2633 1 : writeProperties(item, xml, ctx);
2634 :
2635 1 : xml.endElement();
2636 3 : }
2637 :
2638 1738 : void TWrite::writeProperties(const Part* item, XmlWriter& xml, WriteContext& ctx)
2639 : {
2640 1969 : auto shouldWriteStaff = [&ctx](const Staff* staff) {
2641 1969 : if (!ctx.shouldWriteRange()) {
2642 1969 : return true;
2643 : }
2644 :
2645 0 : const WriteRange& range = ctx.range().value();
2646 0 : const staff_idx_t idx = staff->idx();
2647 :
2648 0 : return idx >= range.startStaffIdx && idx < range.endStaffIdx;
2649 1738 : };
2650 :
2651 3707 : for (const Staff* staff : item->staves()) {
2652 1969 : if (shouldWriteStaff(staff)) {
2653 1969 : write(staff, xml, ctx);
2654 : }
2655 : }
2656 :
2657 1738 : if (bool show = item->getProperty(Pid::VISIBLE).toBool(); !show) {
2658 4 : xml.tag("show", show);
2659 : }
2660 :
2661 1738 : if (item->soloist()) {
2662 1 : xml.tag("soloist", item->soloist());
2663 : }
2664 :
2665 1738 : if (item->hideWhenEmpty() != AutoOnOff::AUTO) {
2666 0 : xml.tag("hideWhenEmpty", TConv::toXml(item->hideWhenEmpty()));
2667 : }
2668 :
2669 1738 : if (item->hideStavesWhenIndividuallyEmpty()) {
2670 2 : xml.tag("hideStavesWhenIndividuallyEmpty", item->hideStavesWhenIndividuallyEmpty());
2671 : }
2672 :
2673 1738 : if (item->preferSharpFlat() != PreferSharpFlat::AUTO) {
2674 10 : switch (item->preferSharpFlat()) {
2675 0 : case PreferSharpFlat::AUTO:
2676 0 : break;
2677 4 : case PreferSharpFlat::FLATS:
2678 4 : xml.tag("preferSharpFlat", "flats");
2679 4 : break;
2680 2 : case PreferSharpFlat::SHARPS:
2681 2 : xml.tag("preferSharpFlat", "sharps");
2682 2 : break;
2683 4 : case PreferSharpFlat::NONE:
2684 4 : xml.tag("preferSharpFlat", "none");
2685 4 : break;
2686 0 : default:
2687 0 : break;
2688 : }
2689 : }
2690 :
2691 1738 : write(item->instrument(), xml, ctx, item);
2692 1738 : }
2693 :
2694 44 : void TWrite::write(const PartialTie* item, XmlWriter& xml, WriteContext& ctx)
2695 : {
2696 44 : xml.startElement(item);
2697 44 : writeProperty(item, xml, Pid::TIE_PLACEMENT);
2698 44 : writeProperty(item, xml, Pid::PARTIAL_SPANNER_DIRECTION);
2699 44 : writeProperties(static_cast<const SlurTie*>(item), xml, ctx);
2700 44 : xml.endElement();
2701 44 : }
2702 :
2703 6 : void TWrite::write(const PartialLyricsLine* item, XmlWriter& xml, WriteContext& ctx)
2704 : {
2705 6 : if (!ctx.canWrite(item)) {
2706 0 : return;
2707 : }
2708 6 : xml.startElement(item);
2709 18 : for (Pid pid : { Pid::VERSE, Pid::CENTER_BETWEEN_STAVES }) {
2710 12 : if (item->propertyFlags(pid) == PropertyFlags::NOSTYLE) {
2711 12 : writeProperty(item, xml, pid);
2712 : }
2713 : }
2714 6 : xml.tag("isEndMelisma", item->isEndMelisma());
2715 6 : writeProperties(static_cast<const SLine*>(item), xml, ctx);
2716 6 : xml.endElement();
2717 : }
2718 :
2719 63 : void TWrite::write(const Pedal* item, XmlWriter& xml, WriteContext& ctx)
2720 : {
2721 63 : if (!ctx.canWrite(item)) {
2722 0 : return;
2723 : }
2724 63 : xml.startElement(item);
2725 63 : writeProperties(static_cast<const TextLineBase*>(item), xml, ctx);
2726 63 : xml.endElement();
2727 : }
2728 :
2729 2 : void TWrite::write(const PickScrape* item, XmlWriter& xml, WriteContext& ctx)
2730 : {
2731 2 : if (!ctx.canWrite(item)) {
2732 0 : return;
2733 : }
2734 2 : xml.startElement(item);
2735 2 : writeProperties(static_cast<const TextLineBase*>(item), xml, ctx);
2736 2 : xml.endElement();
2737 : }
2738 :
2739 6 : void TWrite::write(const PlayCountText* item, XmlWriter& xml, WriteContext& ctx)
2740 : {
2741 6 : xml.startElement(item);
2742 6 : writeProperty(item, xml, Pid::PLAY_COUNT_TEXT_SETTING);
2743 6 : writeProperty(item, xml, Pid::PLAY_COUNT_TEXT);
2744 6 : writeProperties(toTextBase(item), xml, ctx, true);
2745 6 : xml.endElement();
2746 6 : }
2747 :
2748 6 : void TWrite::write(const PlayTechAnnotation* item, XmlWriter& xml, WriteContext& ctx)
2749 : {
2750 6 : xml.startElement(item);
2751 6 : writeProperty(item, xml, Pid::PLAY_TECH_TYPE);
2752 6 : writeProperty(item, xml, Pid::PLAY);
2753 6 : writeProperties(toTextBase(item), xml, ctx, true);
2754 6 : xml.endElement();
2755 6 : }
2756 :
2757 35 : void TWrite::write(const Rasgueado* item, XmlWriter& xml, WriteContext& ctx)
2758 : {
2759 35 : if (!ctx.canWrite(item)) {
2760 0 : return;
2761 : }
2762 35 : xml.startElement(item);
2763 35 : writeProperties(static_cast<const TextLineBase*>(item), xml, ctx);
2764 35 : xml.endElement();
2765 : }
2766 :
2767 50 : void TWrite::write(const RehearsalMark* item, XmlWriter& xml, WriteContext& ctx)
2768 : {
2769 50 : if (!ctx.canWrite(item)) {
2770 0 : return;
2771 : }
2772 50 : xml.startElement(item);
2773 50 : writeProperties(toTextBase(item), xml, ctx, true);
2774 50 : xml.endElement();
2775 : }
2776 :
2777 14533 : void TWrite::write(const Rest* item, XmlWriter& xml, WriteContext& ctx)
2778 : {
2779 14533 : if (item->isGap()) {
2780 50 : return;
2781 : }
2782 14483 : writeChordRestBeam(item, xml, ctx);
2783 14483 : xml.startElement(item);
2784 14483 : writeStyledProperties(item, xml);
2785 14483 : writeProperty(item, xml, Pid::ALIGN_WITH_OTHER_RESTS);
2786 14483 : writeProperties(toChordRest(item), xml, ctx);
2787 14483 : writeItems(item->el(), xml, ctx);
2788 14483 : bool write_dots = false;
2789 14575 : for (NoteDot* dot : item->dotList()) {
2790 300 : if (!dot->offset().isNull() || !dot->visible() || dot->color() != ctx.configuration()->defaultColor()
2791 300 : || dot->visible() != item->visible()) {
2792 8 : write_dots = true;
2793 8 : break;
2794 : }
2795 : }
2796 14483 : if (write_dots) {
2797 16 : for (NoteDot* dot: item->dotList()) {
2798 8 : write(dot, xml, ctx);
2799 : }
2800 : }
2801 14483 : xml.endElement();
2802 : }
2803 :
2804 38788 : void TWrite::write(const Segment* item, XmlWriter& xml, WriteContext&)
2805 : {
2806 38788 : if (item->written()) {
2807 9704 : return;
2808 : }
2809 29084 : item->setWritten(true);
2810 29084 : if (item->extraLeadingSpace().isZero()) {
2811 29084 : return;
2812 : }
2813 0 : xml.startElement(item);
2814 0 : xml.tag("leadingSpace", item->extraLeadingSpace().val());
2815 0 : xml.endElement();
2816 : }
2817 :
2818 255 : void TWrite::write(const Slur* item, XmlWriter& xml, WriteContext& ctx)
2819 : {
2820 255 : if (!ctx.canWrite(item)) {
2821 0 : return;
2822 : }
2823 :
2824 255 : xml.startElement(item);
2825 :
2826 255 : writeProperty(item, xml, Pid::PARTIAL_SPANNER_DIRECTION);
2827 :
2828 255 : writeProperties(static_cast<const SlurTie*>(item), xml, ctx);
2829 255 : xml.endElement();
2830 : }
2831 :
2832 966 : void TWrite::writeProperties(const SlurTie* item, XmlWriter& xml, WriteContext& ctx)
2833 : {
2834 966 : writeProperties(static_cast<const Spanner*>(item), xml, ctx);
2835 966 : int idx = 0;
2836 1954 : for (const SpannerSegment* ss : item->spannerSegments()) {
2837 988 : writeSlur(((SlurTieSegment*)ss), xml, ctx, idx++);
2838 : }
2839 966 : writeProperty(item, xml, Pid::SLUR_DIRECTION);
2840 966 : writeProperty(item, xml, Pid::SLUR_STYLE_TYPE);
2841 966 : writeProperty(item, xml, Pid::MASK_SLURTIE);
2842 966 : }
2843 :
2844 988 : void TWrite::writeSlur(const SlurTieSegment* seg, XmlWriter& xml, WriteContext& ctx, int no)
2845 : {
2846 988 : if (!seg->isUserModified()) {
2847 935 : return;
2848 : }
2849 :
2850 159 : xml.startElement(seg, { { "no", no } });
2851 :
2852 53 : writeProperty(seg, xml, Pid::BEZIER_OFFSET1);
2853 53 : writeProperty(seg, xml, Pid::BEZIER_OFFSET2);
2854 53 : writeProperty(seg, xml, Pid::BEZIER_OFFSET3);
2855 53 : writeProperty(seg, xml, Pid::BEZIER_OFFSET4);
2856 :
2857 53 : if (seg->isHammerOnPullOffSegment()) {
2858 48 : writeProperties(toHammerOnPullOffSegment(seg), xml, ctx);
2859 : }
2860 :
2861 53 : writeItemProperties(seg, xml, ctx);
2862 53 : xml.endElement();
2863 53 : }
2864 :
2865 0 : void TWrite::write(const Spacer* item, XmlWriter& xml, WriteContext& ctx)
2866 : {
2867 0 : xml.startElement(item);
2868 0 : xml.tag("subtype", int(item->spacerType()));
2869 0 : writeItemProperties(item, xml, ctx);
2870 0 : xml.tag("space", item->gap().val());
2871 0 : xml.endElement();
2872 0 : }
2873 :
2874 1969 : void TWrite::write(const Staff* item, XmlWriter& xml, WriteContext& ctx)
2875 : {
2876 1969 : xml.startElement(item);
2877 :
2878 1969 : writeItemEid(item, xml);
2879 1969 : writeItemLink(item, xml, ctx);
2880 :
2881 : // for copy/paste we need to know the actual transposition
2882 1969 : if (ctx.clipboardmode()) {
2883 0 : Interval v = item->part()->instrument()->transpose(); // TODO: tick?
2884 0 : if (v.diatonic) {
2885 0 : xml.tag("transposeDiatonic", v.diatonic);
2886 : }
2887 0 : if (v.chromatic) {
2888 0 : xml.tag("transposeChromatic", v.chromatic);
2889 : }
2890 : }
2891 :
2892 1969 : write(item->staffType(Fraction(0, 1)), xml, ctx);
2893 1969 : ClefTypeList ct = item->defaultClefType();
2894 1969 : if (ct.concertClef == ct.transposingClef) {
2895 1964 : if (ct.concertClef != ClefType::G) {
2896 216 : xml.tag("defaultClef", TConv::toXml(ct.concertClef));
2897 : }
2898 : } else {
2899 5 : xml.tag("defaultConcertClef", TConv::toXml(ct.concertClef));
2900 5 : xml.tag("defaultTransposingClef", TConv::toXml(ct.transposingClef));
2901 : }
2902 :
2903 1969 : if (item->hideWhenEmpty() != AutoOnOff::AUTO) {
2904 0 : xml.tag("hideWhenEmpty", TConv::toXml(item->hideWhenEmpty()));
2905 : }
2906 1969 : if (item->cutaway()) {
2907 0 : xml.tag("cutaway", item->cutaway());
2908 : }
2909 1969 : if (item->showIfEntireSystemEmpty()) {
2910 0 : xml.tag("showIfSystemEmpty", item->showIfEntireSystemEmpty());
2911 : }
2912 1969 : if (item->hideSystemBarLine()) {
2913 0 : xml.tag("hideSystemBarLine", item->hideSystemBarLine());
2914 : }
2915 1969 : if (item->mergeMatchingRests() != AutoOnOff::AUTO) {
2916 0 : xml.tag("mergeMatchingRests", TConv::toXml(item->mergeMatchingRests()));
2917 : }
2918 1969 : if (!item->visible()) {
2919 2 : xml.tag("isStaffVisible", item->visible());
2920 : }
2921 :
2922 2323 : for (const BracketItem* i : item->score()->brackets(item->idx())) {
2923 354 : write(i, xml);
2924 : }
2925 :
2926 1969 : writeProperty(item, xml, Pid::STAFF_BARLINE_SPAN);
2927 1969 : writeProperty(item, xml, Pid::STAFF_BARLINE_SPAN_FROM);
2928 1969 : writeProperty(item, xml, Pid::STAFF_BARLINE_SPAN_TO);
2929 1969 : writeProperty(item, xml, Pid::STAFF_USERDIST);
2930 1969 : writeProperty(item, xml, Pid::PLAYBACK_VOICE1);
2931 1969 : writeProperty(item, xml, Pid::PLAYBACK_VOICE2);
2932 1969 : writeProperty(item, xml, Pid::PLAYBACK_VOICE3);
2933 1969 : writeProperty(item, xml, Pid::PLAYBACK_VOICE4);
2934 1969 : writeProperty(item, xml, Pid::SHOW_MEASURE_NUMBERS);
2935 :
2936 1969 : xml.endElement();
2937 1969 : }
2938 :
2939 354 : void TWrite::write(const BracketItem* item, XmlWriter& xml)
2940 : {
2941 354 : if (item->bracketType() == BracketType::NO_BRACKET) {
2942 150 : return;
2943 : }
2944 :
2945 204 : xml.startElement(item);
2946 :
2947 204 : xml.tag("type", TConv::toXml(item->bracketType()));
2948 204 : writeProperty(item, xml, Pid::BRACKET_SPAN, /*force*/ true);
2949 204 : writeProperty(item, xml, Pid::BRACKET_COLUMN, /*force*/ true);
2950 204 : writeProperty(item, xml, Pid::VISIBLE);
2951 :
2952 204 : if (item->bracketType() == BracketType::GROUP) {
2953 0 : writeProperty(item, xml, Pid::GROUP_BRACKET_SHOW_TEXT);
2954 0 : writeProperty(item, xml, Pid::GROUP_BRACKET_SHOW_BRACKET);
2955 0 : write(item->label(), xml);
2956 : }
2957 :
2958 204 : xml.endElement();
2959 : }
2960 :
2961 0 : void TWrite::write(const StaffState* item, XmlWriter& xml, WriteContext& ctx)
2962 : {
2963 0 : xml.startElement(item);
2964 0 : xml.tag("subtype", int(item->staffStateType()));
2965 0 : if (item->staffStateType() == StaffStateType::INSTRUMENT) {
2966 0 : write(item->instrument(), xml, ctx, nullptr);
2967 : }
2968 0 : writeItemProperties(item, xml, ctx);
2969 0 : xml.endElement();
2970 0 : }
2971 :
2972 357 : void TWrite::write(const StaffText* item, XmlWriter& xml, WriteContext& ctx)
2973 : {
2974 357 : if (!ctx.canWrite(item)) {
2975 26 : return;
2976 : }
2977 :
2978 331 : xml.startElement(item);
2979 :
2980 331 : writeProperties(static_cast<const StaffTextBase*>(item), xml, ctx);
2981 :
2982 331 : if (const SoundFlag* flag = item->soundFlag()) {
2983 0 : writeItem(flag, xml, ctx);
2984 : }
2985 :
2986 331 : xml.endElement();
2987 : }
2988 :
2989 0 : void TWrite::write(const StaveSharingLabel* item, XmlWriter& xml, WriteContext& ctx)
2990 : {
2991 0 : if (!ctx.canWrite(item)) {
2992 0 : return;
2993 : }
2994 :
2995 0 : xml.startElement(item);
2996 :
2997 0 : writeProperties(static_cast<const StaffTextBase*>(item), xml, ctx);
2998 :
2999 0 : xml.endElement();
3000 : }
3001 :
3002 26 : void TWrite::write(const StaffTextBase* item, XmlWriter& xml, WriteContext& ctx)
3003 : {
3004 26 : if (!ctx.canWrite(item)) {
3005 5 : return;
3006 : }
3007 :
3008 21 : xml.startElement(item);
3009 21 : writeProperties(item, xml, ctx);
3010 21 : xml.endElement();
3011 : }
3012 :
3013 352 : void TWrite::writeProperties(const StaffTextBase* item, XmlWriter& xml, WriteContext& ctx)
3014 : {
3015 352 : for (const ChannelActions& s : item->channelActions()) {
3016 0 : int channel = s.channel;
3017 0 : for (const String& name : s.midiActionNames) {
3018 0 : xml.tag("MidiAction", { { "channel", channel }, { "name", name } });
3019 : }
3020 : }
3021 1760 : for (voice_idx_t voice = 0; voice < VOICES; ++voice) {
3022 1408 : if (!item->channelName(voice).isEmpty()) {
3023 0 : xml.tag("channelSwitch", { { "voice", voice }, { "name", item->channelName(voice) } });
3024 : }
3025 : }
3026 352 : if (item->setAeolusStops()) {
3027 0 : for (int i = 0; i < 4; ++i) {
3028 0 : xml.tag("aeolus", { { "group", i } }, item->aeolusStop(i));
3029 : }
3030 : }
3031 352 : if (item->swing()) {
3032 : DurationType swingUnit;
3033 5 : if (item->swingParameters().swingUnit == Constants::DIVISION / 2) {
3034 5 : swingUnit = DurationType::V_EIGHTH;
3035 0 : } else if (item->swingParameters().swingUnit == Constants::DIVISION / 4) {
3036 0 : swingUnit = DurationType::V_16TH;
3037 : } else {
3038 0 : swingUnit = DurationType::V_ZERO;
3039 : }
3040 5 : int swingRatio = item->swingParameters().swingRatio;
3041 20 : xml.tag("swing", { { "unit", TConv::toXml(swingUnit) }, { "ratio", swingRatio } });
3042 : }
3043 :
3044 352 : writeProperties(toTextBase(item), xml, ctx, true);
3045 357 : }
3046 :
3047 1969 : void TWrite::write(const StaffType* item, XmlWriter& xml, WriteContext& ctx)
3048 : {
3049 7876 : xml.startElement("StaffType", { { "group", TConv::toXml(item->group()) } });
3050 1969 : if (!item->xmlName().isEmpty()) {
3051 1969 : xml.tag("name", item->xmlName());
3052 : }
3053 1969 : if (!item->staffLabel().empty()) {
3054 0 : write(item->staffLabel(), xml);
3055 : }
3056 1969 : if (item->lines() != 5) {
3057 75 : xml.tag("lines", item->lines());
3058 : }
3059 1969 : if (!RealIsEqual(item->lineDistance().val(), 1.0)) {
3060 69 : xml.tag("lineDistance", item->lineDistance().val());
3061 : }
3062 1969 : if (!RealIsNull(item->yoffset().val())) {
3063 0 : xml.tag("yoffset", item->yoffset().val());
3064 : }
3065 1969 : if (!RealIsEqual(item->userMag(), 1.0)) {
3066 1 : xml.tag("mag", item->userMag());
3067 : }
3068 1969 : if (item->isSmall()) {
3069 1 : xml.tag("small", item->isSmall());
3070 : }
3071 1969 : if (item->stepOffset()) {
3072 0 : xml.tag("stepOffset", item->stepOffset());
3073 : }
3074 1969 : if (!item->genClef()) {
3075 0 : xml.tag("clef", item->genClef());
3076 : }
3077 1969 : if (item->stemless()) {
3078 66 : xml.tag("stemless", item->stemless());
3079 : }
3080 1969 : if (!item->showBarlines()) {
3081 0 : xml.tag("barlines", item->showBarlines());
3082 : }
3083 1969 : if (!item->genTimesig()) {
3084 66 : xml.tag("timesig", item->genTimesig());
3085 : }
3086 1969 : if (item->invisible()) {
3087 1 : xml.tag("invisible", item->invisible());
3088 : }
3089 1969 : if (item->color() != ctx.configuration()->defaultColor()) {
3090 1 : xml.tagProperty(Pid::COLOR, item->color());
3091 : }
3092 1969 : if (item->group() == StaffGroup::STANDARD) {
3093 1870 : xml.tag("noteheadScheme", TConv::toXml(item->noteHeadScheme()), TConv::toXml(NoteHeadScheme::HEAD_NORMAL));
3094 : }
3095 1969 : xml.tag("instrumentLabelVisibility", TConv::toXml(item->instrumentLabelVisibility()),
3096 1969 : TConv::toXml(InstrumentLabelVisibility::AUTO));
3097 1969 : if (item->group() == StaffGroup::STANDARD || item->group() == StaffGroup::PERCUSSION) {
3098 1900 : if (!item->genKeysig()) {
3099 30 : xml.tag("keysig", item->genKeysig());
3100 : }
3101 1900 : if (!item->showLedgerLines()) {
3102 0 : xml.tag("ledgerlines", item->showLedgerLines());
3103 : }
3104 : } else {
3105 69 : xml.tag("durations", item->genDurations());
3106 69 : xml.tag("durationFontName", item->durationFontName()); // write font names anyway for backward compatibility
3107 69 : xml.tag("durationFontSize", item->durationFontSize());
3108 69 : xml.tag("durationFontY", item->durationFontUserY());
3109 69 : if (item->symRepeat() != TablatureSymbolRepeat::NEVER) {
3110 1 : xml.tag("symbolRepeat", int(item->symRepeat()));
3111 : }
3112 69 : xml.tag("fretUseTextStyle", item->fretUseTextStyle());
3113 69 : if (item->fretUseTextStyle()) {
3114 68 : xml.tag("fretTextStyle", TConv::toXml(item->fretTextStyle()));
3115 : } else {
3116 1 : xml.tag("fretPresetIdx", item->fretPresetIdx());
3117 1 : xml.tag("fretFontSize", item->fretFontSize());
3118 1 : xml.tag("fretFontY", item->fretFontUserY());
3119 : }
3120 69 : xml.tag("linesThrough", item->linesThrough());
3121 69 : xml.tag("minimStyle", int(item->minimStyle()));
3122 69 : xml.tag("onLines", item->onLines());
3123 69 : xml.tag("showRests", item->showRests());
3124 69 : xml.tag("stemsDown", item->stemsDown());
3125 69 : xml.tag("stemsThrough", item->stemThrough());
3126 69 : xml.tag("upsideDown", item->upsideDown());
3127 69 : xml.tag("showTabFingering", item->showTabFingering(), false);
3128 69 : xml.tag("useNumbers", item->useNumbers());
3129 : // only output "showBackTied" if different from !"stemless"
3130 : // to match the behaviour in 2.0.2 scores (or older)
3131 69 : if (item->showBackTied() != !item->stemless()) {
3132 1 : xml.tag("showBackTied", item->showBackTied());
3133 : }
3134 : }
3135 1969 : xml.endElement();
3136 5907 : }
3137 :
3138 0 : void TWrite::write(const StaffTypeChange* item, XmlWriter& xml, WriteContext& ctx)
3139 : {
3140 0 : xml.startElement(item);
3141 0 : if (item->staffType()) {
3142 0 : write(item->staffType(), xml, ctx);
3143 : }
3144 0 : writeItemProperties(item, xml, ctx);
3145 0 : xml.endElement();
3146 0 : }
3147 :
3148 3 : void TWrite::write(const Stem* item, XmlWriter& xml, WriteContext& ctx)
3149 : {
3150 3 : xml.startElement(item);
3151 3 : writeItemProperties(item, xml, ctx);
3152 3 : writeProperty(item, xml, Pid::USER_LEN);
3153 3 : writeProperty(item, xml, Pid::LINE_WIDTH);
3154 3 : xml.endElement();
3155 3 : }
3156 :
3157 0 : void TWrite::write(const StemSlash* item, XmlWriter& xml, WriteContext& ctx)
3158 : {
3159 0 : xml.startElement(item);
3160 0 : writeItemProperties(item, xml, ctx);
3161 0 : xml.endElement();
3162 0 : }
3163 :
3164 73 : void TWrite::write(const Sticking* item, XmlWriter& xml, WriteContext& ctx)
3165 : {
3166 73 : if (!ctx.canWrite(item)) {
3167 0 : return;
3168 : }
3169 73 : xml.startElement(item);
3170 73 : writeProperties(toTextBase(item), xml, ctx, true);
3171 73 : xml.endElement();
3172 : }
3173 :
3174 443 : void TWrite::write(const StringData* item, XmlWriter& xml)
3175 : {
3176 443 : xml.startElement("StringData");
3177 443 : xml.tag("frets", item->frets());
3178 3045 : for (const instrString& strg : item->stringList()) {
3179 2602 : XmlWriter::Attributes attrs;
3180 :
3181 2602 : if (strg.open) {
3182 0 : attrs.push_back({ "open", "1" });
3183 : }
3184 :
3185 2602 : if (strg.useFlat) {
3186 16 : attrs.push_back({ "useFlat", "1" });
3187 : }
3188 :
3189 2602 : xml.tag("string", attrs, strg.pitch);
3190 2602 : }
3191 443 : xml.endElement();
3192 443 : }
3193 :
3194 19 : void TWrite::write(const StringTunings* item, XmlWriter& xml, WriteContext& ctx)
3195 : {
3196 19 : xml.startElement(item);
3197 :
3198 19 : writeProperty(item, xml, Pid::STRINGTUNINGS_PRESET);
3199 :
3200 19 : xml.tag("visibleStrings", TConv::toXml(item->visibleStrings()));
3201 :
3202 19 : if (!item->stringData()->isNull()) {
3203 19 : write(item->stringData(), xml);
3204 : }
3205 :
3206 19 : writeProperties(static_cast<const StaffTextBase*>(item), xml, ctx, true);
3207 19 : xml.endElement();
3208 19 : }
3209 :
3210 11 : void TWrite::write(const Symbol* item, XmlWriter& xml, WriteContext& ctx)
3211 : {
3212 11 : xml.startElement(item);
3213 11 : xml.tag("name", SymNames::nameForSymId(item->sym()));
3214 11 : if (item->scoreFont()) {
3215 0 : xml.tag("font", item->scoreFont()->name());
3216 0 : writeProperty(item, xml, Pid::SYMBOLS_SIZE);
3217 0 : writeProperty(item, xml, Pid::SYMBOL_ANGLE);
3218 : }
3219 11 : writeProperties(static_cast<const BSymbol*>(item), xml, ctx);
3220 11 : xml.endElement();
3221 11 : }
3222 :
3223 0 : void TWrite::write(const FSymbol* item, XmlWriter& xml, WriteContext& ctx)
3224 : {
3225 0 : xml.startElement(item);
3226 0 : xml.tag("font", item->font().family().id());
3227 0 : xml.tag("fontsize", item->font().pointSizeF());
3228 0 : xml.tag("code", item->code());
3229 0 : writeProperties(static_cast<const BSymbol*>(item), xml, ctx);
3230 0 : xml.endElement();
3231 0 : }
3232 :
3233 0 : void TWrite::write(const System* item, XmlWriter& xml, WriteContext& ctx)
3234 : {
3235 0 : xml.startElement(item);
3236 0 : if (item->systemDividerLeft() && item->systemDividerLeft()->isUserModified()) {
3237 0 : write(item->systemDividerLeft(), xml, ctx);
3238 : }
3239 0 : if (item->systemDividerRight() && item->systemDividerRight()->isUserModified()) {
3240 0 : write(item->systemDividerRight(), xml, ctx);
3241 : }
3242 0 : xml.endElement();
3243 0 : }
3244 :
3245 2 : void TWrite::write(const SystemDivider* item, XmlWriter& xml, WriteContext& ctx)
3246 : {
3247 8 : xml.startElement(item, { { "type", (item->dividerType() == SystemDividerType::LEFT ? "left" : "right") } });
3248 :
3249 2 : writeProperty(item, xml, Pid::SCORE_FONT);
3250 2 : writeProperty(item, xml, Pid::SYMBOLS_SIZE);
3251 2 : writeProperty(item, xml, Pid::SYMBOL_ANGLE);
3252 :
3253 2 : writeProperties(static_cast<const BSymbol*>(item), xml, ctx);
3254 2 : xml.endElement();
3255 6 : }
3256 :
3257 26 : void TWrite::write(const SystemText* item, XmlWriter& xml, WriteContext& ctx)
3258 : {
3259 26 : write(static_cast<const StaffTextBase*>(item), xml, ctx);
3260 26 : }
3261 :
3262 0 : void TWrite::write(const SoundFlag* item, XmlWriter& xml, WriteContext&)
3263 : {
3264 0 : if (item->soundPresets().empty() && item->playingTechnique().empty()) {
3265 0 : return;
3266 : }
3267 :
3268 0 : xml.startElement(item);
3269 :
3270 0 : writeProperty(item, xml, Pid::PLAY);
3271 :
3272 0 : if (!item->soundPresets().empty()) {
3273 0 : xml.tag("presets", item->soundPresets().join(u","));
3274 : }
3275 :
3276 0 : if (!item->playingTechnique().empty()) {
3277 0 : xml.tag("playingTechnique", item->playingTechnique());
3278 : }
3279 :
3280 0 : writeProperty(item, xml, Pid::APPLY_TO_ALL_STAVES);
3281 :
3282 0 : xml.endElement();
3283 : }
3284 :
3285 3 : void TWrite::write(const Tapping* item, XmlWriter& xml, WriteContext& ctx)
3286 : {
3287 3 : xml.startElement(item);
3288 :
3289 3 : xml.tag("hand", TConv::toXml(item->hand()));
3290 :
3291 3 : if (item->halfSlurAbove() && item->halfSlurAbove()->isUserModified()) {
3292 0 : write(item->halfSlurAbove(), xml, ctx);
3293 : }
3294 3 : if (item->halfSlurBelow() && item->halfSlurBelow()->isUserModified()) {
3295 0 : write(item->halfSlurBelow(), xml, ctx);
3296 : }
3297 :
3298 3 : writeProperties(toArticulation(item), xml, ctx);
3299 3 : xml.endElement();
3300 3 : }
3301 :
3302 0 : void TWrite::write(const TappingHalfSlur* item, XmlWriter& xml, WriteContext& ctx)
3303 : {
3304 0 : xml.startElement(item);
3305 :
3306 0 : xml.tag("isHalfSlurAbove", item->isHalfSlurAbove());
3307 0 : writeProperties(static_cast<const SlurTie*>(item), xml, ctx);
3308 :
3309 0 : xml.endElement();
3310 0 : }
3311 :
3312 565 : void TWrite::write(const TempoText* item, XmlWriter& xml, WriteContext& ctx)
3313 : {
3314 565 : xml.startElement(item);
3315 565 : writeProperty(item, xml, Pid::PLAY);
3316 565 : xml.tag("tempo", TConv::toXml(item->tempo(), Constants::TEMPO_PRECISION));
3317 565 : if (item->followText()) {
3318 86 : xml.tag("followText", item->followText());
3319 : }
3320 565 : switch (item->tempoTextType()) {
3321 565 : case TempoTextType::NORMAL:
3322 565 : break;
3323 0 : case TempoTextType::A_TEMPO:
3324 0 : xml.tag("type", "aTempo");
3325 0 : break;
3326 0 : case TempoTextType::TEMPO_PRIMO:
3327 0 : xml.tag("type", "tempoPrimo");
3328 0 : break;
3329 : }
3330 565 : writeProperties(toTextBase(item), xml, ctx, true);
3331 565 : xml.endElement();
3332 565 : }
3333 :
3334 1017 : void TWrite::write(const Text* item, XmlWriter& xml, WriteContext& ctx)
3335 : {
3336 1017 : if (!ctx.canWrite(item)) {
3337 0 : return;
3338 : }
3339 1017 : xml.startElement(item);
3340 1017 : writeProperties(toTextBase(item), xml, ctx, true);
3341 1017 : xml.endElement();
3342 : }
3343 :
3344 45 : void TWrite::write(const TextLine* item, XmlWriter& xml, WriteContext& ctx)
3345 : {
3346 45 : if (!ctx.canWrite(item)) {
3347 0 : return;
3348 : }
3349 45 : if (item->systemFlag()) {
3350 0 : xml.startElement(item, { { "system", "1" } });
3351 : } else {
3352 45 : xml.startElement(item);
3353 : }
3354 45 : writeProperties(static_cast<const TextLineBase*>(item), xml, ctx);
3355 45 : xml.endElement();
3356 0 : }
3357 :
3358 598 : void TWrite::write(const Tie* item, XmlWriter& xml, WriteContext& ctx)
3359 : {
3360 598 : xml.startElement(item);
3361 598 : writeProperty(item, xml, Pid::TIE_PLACEMENT);
3362 598 : writeProperties(static_cast<const SlurTie*>(item), xml, ctx);
3363 598 : xml.endElement();
3364 598 : }
3365 :
3366 1979 : void TWrite::write(const TimeSig* item, XmlWriter& xml, WriteContext& ctx)
3367 : {
3368 1979 : xml.startElement(item);
3369 1979 : writeProperty(item, xml, Pid::TIMESIG_TYPE);
3370 1979 : writeItemProperties(item, xml, ctx);
3371 :
3372 1979 : xml.tag("sigN", item->numerator());
3373 1979 : xml.tag("sigD", item->denominator());
3374 1979 : if (item->stretch() != Fraction(1, 1)) {
3375 1 : xml.tag("stretchN", item->stretch().numerator());
3376 1 : xml.tag("stretchD", item->stretch().denominator());
3377 : }
3378 1979 : writeProperty(item, xml, Pid::NUMERATOR_STRING);
3379 1979 : writeProperty(item, xml, Pid::DENOMINATOR_STRING);
3380 1979 : if (!item->groups().empty()) {
3381 0 : write(&item->groups(), xml, ctx);
3382 : }
3383 1979 : writeProperty(item, xml, Pid::SHOW_COURTESY);
3384 1979 : writeProperty(item, xml, Pid::IS_COURTESY);
3385 1979 : writeProperty(item, xml, Pid::SCALE);
3386 :
3387 1979 : xml.endElement();
3388 1979 : }
3389 :
3390 112 : void TWrite::write(const TremoloSingleChord* item, XmlWriter& xml, WriteContext& ctx)
3391 : {
3392 112 : if (!ctx.canWrite(item)) {
3393 2 : return;
3394 : }
3395 :
3396 110 : xml.startElement(item);
3397 :
3398 110 : writeProperty(item, xml, Pid::TREMOLO_TYPE);
3399 110 : writeProperty(item, xml, Pid::PLAY);
3400 110 : writeItemProperties(item, xml, ctx);
3401 110 : xml.endElement();
3402 : }
3403 :
3404 52 : void TWrite::write(const TremoloTwoChord* item, XmlWriter& xml, WriteContext& ctx)
3405 : {
3406 52 : if (!ctx.canWrite(item)) {
3407 1 : return;
3408 : }
3409 :
3410 51 : xml.startElement(item);
3411 :
3412 51 : writeProperty(item, xml, Pid::TREMOLO_TYPE);
3413 51 : writeProperty(item, xml, Pid::TREMOLO_STYLE);
3414 51 : writeProperty(item, xml, Pid::PLAY);
3415 51 : writeItemProperties(item, xml, ctx);
3416 :
3417 : // write manual adjustments to file
3418 51 : int idx = item->directionIdx();
3419 51 : if (item->userModified()) {
3420 0 : double _spatium = item->spatium();
3421 :
3422 0 : xml.startElement("Fragment");
3423 0 : xml.tag("y1", item->beamFragment().py1[idx] / _spatium);
3424 0 : xml.tag("y2", item->beamFragment().py2[idx] / _spatium);
3425 0 : xml.endElement();
3426 : }
3427 :
3428 : // this info is used for regression testing
3429 : // l1/l2 is the beam position of the layout engine
3430 51 : if (MScore::testMode) {
3431 51 : double spatium8 = item->spatium() * .125;
3432 51 : xml.tag("l1", int(lrint(item->beamFragment().py1[idx] / spatium8)));
3433 51 : xml.tag("l2", int(lrint(item->beamFragment().py2[idx] / spatium8)));
3434 : }
3435 51 : xml.endElement();
3436 : }
3437 :
3438 4 : void TWrite::write(const TremoloBar* item, XmlWriter& xml, WriteContext& ctx)
3439 : {
3440 4 : xml.startElement(item);
3441 4 : writeProperty(item, xml, Pid::MAG);
3442 4 : writeProperty(item, xml, Pid::LINE_WIDTH);
3443 4 : writeProperty(item, xml, Pid::PLAY);
3444 16 : for (const PitchValue& v : item->points()) {
3445 60 : xml.tag("point", { { "time", v.time }, { "pitch", v.pitch }, { "vibrato", v.vibrato } });
3446 : }
3447 4 : writeItemProperties(item, xml, ctx);
3448 4 : xml.endElement();
3449 16 : }
3450 :
3451 18 : void TWrite::write(const Trill* item, XmlWriter& xml, WriteContext& ctx)
3452 : {
3453 18 : if (!ctx.canWrite(item)) {
3454 0 : return;
3455 : }
3456 18 : xml.startElement(item);
3457 18 : xml.tag("subtype", TConv::toXml(item->trillType()));
3458 :
3459 18 : if (item->propertyFlags(Pid::ORNAMENT_STYLE) == PropertyFlags::NOSTYLE) {
3460 18 : writeProperty(item, xml, Pid::ORNAMENT_STYLE);
3461 : }
3462 18 : writeProperties(static_cast<const SLine*>(item), xml, ctx);
3463 :
3464 18 : if (item->ornament()) {
3465 16 : write(item->ornament(), xml, ctx);
3466 : }
3467 18 : xml.endElement();
3468 : }
3469 :
3470 599 : void TWrite::write(const Tuplet* item, XmlWriter& xml, WriteContext& ctx)
3471 : {
3472 599 : xml.startElement(item);
3473 599 : writeItemProperties(item, xml, ctx);
3474 :
3475 599 : writeProperty(item, xml, Pid::NORMAL_NOTES);
3476 599 : writeProperty(item, xml, Pid::ACTUAL_NOTES);
3477 599 : writeProperty(item, xml, Pid::P1);
3478 599 : writeProperty(item, xml, Pid::P2);
3479 :
3480 599 : xml.tag("baseNote", TConv::toXml(item->baseLen().type()));
3481 599 : if (int dots = item->baseLen().dots()) {
3482 1 : xml.tag("baseDots", dots);
3483 : }
3484 :
3485 599 : if (item->number()) {
3486 589 : xml.startElement("Number", item->number());
3487 589 : writeProperty(item->number(), xml, Pid::TEXT_STYLE);
3488 589 : writeProperty(item->number(), xml, Pid::TEXT);
3489 589 : xml.endElement();
3490 : }
3491 :
3492 599 : writeStyledProperties(item, xml);
3493 :
3494 599 : xml.endElement();
3495 599 : }
3496 :
3497 26 : void TWrite::write(const Vibrato* item, XmlWriter& xml, WriteContext& ctx)
3498 : {
3499 26 : if (!ctx.canWrite(item)) {
3500 0 : return;
3501 : }
3502 26 : xml.startElement(item);
3503 26 : xml.tag("subtype", TConv::toXml(item->vibratoType()));
3504 26 : writeProperties(static_cast<const SLine*>(item), xml, ctx);
3505 26 : xml.endElement();
3506 : }
3507 :
3508 55 : void TWrite::write(const Volta* item, XmlWriter& xml, WriteContext& ctx)
3509 : {
3510 55 : xml.startElement(item);
3511 55 : writeProperties(static_cast<const TextLineBase*>(item), xml, ctx);
3512 55 : xml.tag("endings", TConv::toXml(item->endings()));
3513 55 : xml.endElement();
3514 55 : }
3515 :
3516 32 : void TWrite::write(const WhammyBar* item, XmlWriter& xml, WriteContext& ctx)
3517 : {
3518 32 : if (!ctx.canWrite(item)) {
3519 0 : return;
3520 : }
3521 32 : xml.startElement(item);
3522 32 : writeProperties(static_cast<const TextLineBase*>(item), xml, ctx);
3523 32 : xml.endElement();
3524 : }
3525 :
3526 : //---------------------------------------------------------
3527 : // writeVoiceMove
3528 : // write <move> and starting <voice> tags to denote
3529 : // change in position.
3530 : // Returns true if <voice> tag was written.
3531 : //---------------------------------------------------------
3532 :
3533 17721 : static bool writeVoiceMove(XmlWriter& xml, WriteContext& ctx, Segment* seg, const Fraction& startTick, track_idx_t track,
3534 : int* lastTrackWrittenPtr)
3535 : {
3536 17721 : bool voiceTagWritten = false;
3537 17721 : int& lastTrackWritten = *lastTrackWrittenPtr;
3538 17721 : if ((lastTrackWritten < static_cast<int>(track)) && !ctx.clipboardmode()) {
3539 16952 : while (lastTrackWritten < (static_cast < int > (track) - 1)) {
3540 45 : xml.tag("voice");
3541 45 : ++lastTrackWritten;
3542 : }
3543 16907 : xml.startElement("voice");
3544 16907 : ctx.setCurTick(startTick);
3545 16907 : ctx.setCurTrack(track);
3546 16907 : ++lastTrackWritten;
3547 16907 : voiceTagWritten = true;
3548 : }
3549 :
3550 17721 : if ((ctx.curTick() != seg->tick()) || (track != ctx.curTrack())) {
3551 248 : Location curr = Location::absolute();
3552 248 : Location dest = Location::absolute();
3553 248 : curr.setFrac(ctx.curTick());
3554 248 : dest.setFrac(seg->tick());
3555 248 : curr.setTrack(static_cast<int>(ctx.curTrack()));
3556 248 : dest.setTrack(static_cast<int>(track));
3557 :
3558 248 : dest.toRelative(curr);
3559 248 : dest.setIsTimeTick(seg->isTimeTickType());
3560 248 : TWrite::write(&dest, xml, ctx);
3561 :
3562 248 : ctx.setCurTick(seg->tick());
3563 248 : ctx.setCurTrack(track);
3564 : }
3565 :
3566 17721 : return voiceTagWritten;
3567 : }
3568 :
3569 0 : static void writeTimeSig(Score* score, const Fraction& tick, XmlWriter& xml, WriteContext& ctx)
3570 : {
3571 0 : Fraction tsf = score->sigmap()->timesig(tick).nominal();
3572 0 : TimeSig* ts = Factory::createTimeSig(score->dummy());
3573 0 : ts->setSig(tsf);
3574 0 : TWrite::write(ts, xml, ctx);
3575 0 : ts->masterScore()->eidRegister()->removeItem(ts);
3576 0 : delete ts;
3577 0 : }
3578 :
3579 : //---------------------------------------------------------
3580 : // writeSegments
3581 : // ls - write upto this segment (excluding)
3582 : // can be zero
3583 : //---------------------------------------------------------
3584 84 : void TWrite::writeSegments(XmlWriter& xml, WriteContext& ctx, track_idx_t strack, track_idx_t etrack,
3585 : Segment* sseg, Segment* eseg, bool writeSystemElements, bool forceTimeSig)
3586 : {
3587 84 : bool clip = ctx.clipboardmode();
3588 :
3589 : // in clipboard mode, ls might be in an mmrest
3590 : // since we are traversing regular measures,
3591 : // force them out of mmRest
3592 84 : if (clip) {
3593 84 : Measure* lm = eseg ? eseg->measure() : 0;
3594 84 : Measure* fm = sseg ? sseg->measure() : 0;
3595 84 : if (lm && lm->isMMRest()) {
3596 0 : lm = lm->mmRestLast();
3597 0 : if (lm) {
3598 0 : eseg = lm->nextMeasure() ? lm->nextMeasure()->first() : nullptr;
3599 : } else {
3600 0 : LOGD("writeSegments: no measure for end segment in mmrest");
3601 : }
3602 : }
3603 84 : if (fm && fm->isMMRest()) {
3604 0 : fm = fm->mmRestFirst();
3605 0 : if (fm) {
3606 0 : sseg = fm->first(SegmentType::ChordRest);
3607 : }
3608 : }
3609 : }
3610 :
3611 84 : std::vector<Segment*> segments;
3612 619 : for (Segment* segment = sseg; segment && segment != eseg; segment = segment->next1()) {
3613 535 : segments.push_back(segment);
3614 : }
3615 :
3616 84 : writeSegments(xml, ctx, strack, etrack, segments, writeSystemElements, forceTimeSig);
3617 84 : }
3618 :
3619 16455 : void TWrite::writeSegments(XmlWriter& xml, WriteContext& ctx, track_idx_t strack, track_idx_t etrack, const std::vector<Segment*>& segments,
3620 : bool writeSystemElements, bool forceTimeSig)
3621 : {
3622 16455 : if (segments.empty()) {
3623 0 : return;
3624 : }
3625 :
3626 16455 : Segment* sseg = segments.front();
3627 16455 : Score* score = sseg->score();
3628 16455 : Fraction startTick = ctx.curTick();
3629 :
3630 16455 : int lastTrackWritten = static_cast<int>(strack - 1); // for counting necessary <voice> tags
3631 82275 : for (track_idx_t track = strack; track < etrack; ++track) {
3632 65820 : if (!ctx.canWriteVoice(track)) {
3633 5 : if (forceTimeSig && track2voice(track) == 0) {
3634 0 : bool voiceTagWritten = writeVoiceMove(xml, ctx, sseg, startTick, track, &lastTrackWritten);
3635 0 : writeTimeSig(score, startTick, xml, ctx);
3636 0 : if (voiceTagWritten) {
3637 0 : xml.endElement(); // </voice>
3638 : }
3639 : }
3640 :
3641 5 : continue;
3642 5 : }
3643 :
3644 65815 : bool voiceTagWritten = false;
3645 65815 : bool timeSigWritten = false; // for forceTimeSig
3646 65815 : bool crWritten = false; // for forceTimeSig
3647 65815 : bool keySigWritten = false; // for forceTimeSig
3648 :
3649 310719 : for (Segment* segment : segments) {
3650 244904 : if (!segment->enabled()) {
3651 1824 : continue;
3652 : }
3653 243080 : if (track == 0) {
3654 44656 : segment->setWritten(false);
3655 : }
3656 243080 : EngravingItem* e = segment->element(track);
3657 :
3658 : //
3659 : // special case: - barline span > 1
3660 : // - part (excerpt) staff starts after
3661 : // barline element
3662 243080 : bool needMove = (segment->tick() != ctx.curTick() || (static_cast<int>(track) > lastTrackWritten));
3663 243080 : if ((segment->isEndBarLineType()) && !e && writeSystemElements && ((track % VOICES) == 0)) {
3664 : // search barline:
3665 55 : for (int idx = static_cast<int>(track - VOICES); idx >= 0; idx -= static_cast<int>(VOICES)) {
3666 0 : if (segment->element(idx)) {
3667 0 : int oDiff = ctx.trackDiff();
3668 0 : ctx.setTrackDiff(idx); // staffIdx should be zero
3669 0 : TWrite::writeItem(segment->element(idx), xml, ctx);
3670 0 : ctx.setTrackDiff(oDiff);
3671 0 : break;
3672 : }
3673 : }
3674 : }
3675 257000 : for (EngravingItem* e1 : segment->annotations()) {
3676 13920 : if (e1->generated()) {
3677 0 : continue;
3678 : }
3679 13920 : bool writeSystem = writeSystemElements;
3680 13920 : if (!writeSystem) {
3681 3624 : ElementType et = e1->type();
3682 3624 : if ((et == ElementType::REHEARSAL_MARK)
3683 3476 : || (et == ElementType::SYSTEM_TEXT)
3684 3444 : || (et == ElementType::TRIPLET_FEEL)
3685 3444 : || (et == ElementType::PLAYTECH_ANNOTATION)
3686 3436 : || (et == ElementType::CAPO)
3687 3428 : || (et == ElementType::STRING_TUNINGS)
3688 3372 : || (et == ElementType::JUMP)
3689 3372 : || (et == ElementType::MARKER)
3690 3372 : || (et == ElementType::TEMPO_TEXT)
3691 2412 : || (et == ElementType::VOLTA)
3692 2412 : || (et == ElementType::GRADUAL_TEMPO_CHANGE)
3693 2412 : || (et == ElementType::PLAY_COUNT_TEXT)) {
3694 1228 : writeSystem = (e1->track() == track); // always show these on appropriate staves
3695 : }
3696 : }
3697 13920 : if (e1->track() != track || (e1->systemFlag() && !writeSystem)) {
3698 11281 : continue;
3699 : }
3700 2639 : if (needMove) {
3701 1146 : voiceTagWritten |= writeVoiceMove(xml, ctx, segment, startTick, track, &lastTrackWritten);
3702 1146 : needMove = false;
3703 : }
3704 2639 : TWrite::writeItem(e1, xml, ctx);
3705 : }
3706 :
3707 243080 : if (!e || !ctx.canWrite(e)) {
3708 182703 : continue;
3709 : }
3710 60377 : if (e->generated()) {
3711 21539 : continue;
3712 : }
3713 0 : if (forceTimeSig && track2voice(track) == 0 && segment->segmentType() == SegmentType::ChordRest && !timeSigWritten
3714 38838 : && !crWritten) {
3715 : // Ensure that <voice> tag is open
3716 0 : voiceTagWritten |= writeVoiceMove(xml, ctx, segment, startTick, track, &lastTrackWritten);
3717 : // we will miss a key sig!
3718 0 : if (!keySigWritten) {
3719 0 : Key ck = score->staff(track2staff(track))->concertKey(segment->tick());
3720 0 : Key tk = score->staff(track2staff(track))->key(segment->tick());
3721 0 : KeySig* ks = Factory::createKeySig(score->dummy());
3722 0 : ks->setKey(ck, tk);
3723 0 : TWrite::write(ks, xml, ctx);
3724 0 : ks->masterScore()->eidRegister()->removeItem(ks);
3725 0 : delete ks;
3726 0 : keySigWritten = true;
3727 : }
3728 : // we will miss a time sig!
3729 0 : writeTimeSig(score, segment->tick(), xml, ctx);
3730 0 : timeSigWritten = true;
3731 : }
3732 38838 : if (needMove) {
3733 16575 : voiceTagWritten |= writeVoiceMove(xml, ctx, segment, startTick, track, &lastTrackWritten);
3734 : // needMove = false; //! NOTE Not necessary, because needMove is currently never read again.
3735 : }
3736 38838 : if (e->isChordRest()) {
3737 32986 : ChordRest* cr = toChordRest(e);
3738 32986 : writeTupletStart(cr, xml, ctx);
3739 : }
3740 38838 : TWrite::writeItem(e, xml, ctx);
3741 :
3742 38838 : if (e->isChordRest()) {
3743 32986 : ChordRest* cr = toChordRest(e);
3744 32986 : writeTupletEnd(cr, xml, ctx);
3745 : }
3746 :
3747 38838 : if (!(e->isRest() && toRest(e)->isGap())) {
3748 38788 : TWrite::write(segment, xml, ctx); // write only once
3749 : }
3750 38838 : if (forceTimeSig) {
3751 0 : if (segment->segmentType() == SegmentType::KeySig) {
3752 0 : keySigWritten = true;
3753 : }
3754 0 : if (segment->segmentType() == SegmentType::TimeSig) {
3755 0 : timeSigWritten = true;
3756 : }
3757 0 : if (segment->segmentType() == SegmentType::ChordRest) {
3758 0 : crWritten = true;
3759 : }
3760 : }
3761 : }
3762 :
3763 65815 : if (voiceTagWritten) {
3764 16907 : xml.endElement(); // </voice>
3765 : }
3766 : }
3767 : }
|