LCOV - code coverage report
Current view: top level - src/engraving/rw/write - twrite.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 77.8 % 2342 1822
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : #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              : }
        

Generated by: LCOV version 2.0-1