MuseScore/libmscore/read206.cpp
Joachim Schmitz b7b35643a1 Fix Deprecation warnings with Qt 5.15 (and 5.14)
* fix 383 warnings C4996 (deprecated) with one single line change
* fix 5 'empty' warnings about invalid slots, not sure the remaining
slots serve any purpose though
* disable warnings C4127 and C4996 for thirdparty/google_analytics
* fix 30 warnings C4996 using the suggested alternatives available since
at least Qt 5.9, so won't break building against that
* change as requested in code review plus some more fixes using the same
idea
* fixing yet more warnings
* disable deprecation warnings in qoogle_analytics for MinGW too
although I believe maxOS and Linux may use those too. Easy to extend to
those, if need be.
* Fix qml runtime warnings exactly following the advice given by that
warning, but without really know what I'm doing here or whether that is
still backwards compatible to Qt 5.9.
* Use replacements as suggested, if available and no `endl` neded with
`qDebug()`
* fix 24 more
* 2 more
* 7 more (one only seen in DEBUG mode)
* Fix the `endl` warnings
* maybe more changes this way?
* Add all warnings C5999 or bigger to the ignore list for telemetry
with Qt 5.15 Beta 1 as avoiding only C26439, C26444,C 26452, C26495,
C26498, C26812 isn't possible
* fix 2 deprecation warning new with Qt 5.15 beta 1
* fix 4 new warnings and also Qt 5.12 builds, disable some dead code
* fix deprecation warnings new with Qt 5.15's MSVC 2019 integration and
revert some (`QNetworkReply::networkError()`) that Qt 5.15 beta 2
reverted too
* fix warning reg. obsolete operator < for QVariants
* revert changes needed prior to beta 3 And clarifying some earlier
changes
* mark ToDos
* fix warning reg QString()::null
* fix a 'regression' from a revert of an earlier change
2020-06-03 12:59:19 +02:00

4100 lines
158 KiB
C++

//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2016 Werner Schweer and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation and appearing in
// the file LICENSE.GPL
//=============================================================================
#include "xml.h"
#include "score.h"
#include "staff.h"
#include "revisions.h"
#include "part.h"
#include "page.h"
#include "style.h"
#include "sym.h"
#include "arpeggio.h"
#include "audio.h"
#include "sig.h"
#include "barline.h"
#include "measure.h"
#include "ambitus.h"
#include "bend.h"
#include "chordline.h"
#include "hook.h"
#include "tuplet.h"
#include "systemdivider.h"
#include "spacer.h"
#include "keysig.h"
#include "stafftext.h"
#include "dynamic.h"
#include "drumset.h"
#include "timesig.h"
#include "slur.h"
#include "tie.h"
#include "chord.h"
#include "rest.h"
#include "breath.h"
#include "repeat.h"
#include "utils.h"
#include "read206.h"
#include "excerpt.h"
#include "articulation.h"
#include "volta.h"
#include "pedal.h"
#include "hairpin.h"
#include "glissando.h"
#include "ottava.h"
#include "trill.h"
#include "rehearsalmark.h"
#include "box.h"
#include "textframe.h"
#include "textline.h"
#include "fingering.h"
#include "fermata.h"
#include "image.h"
#include "stem.h"
#include "stemslash.h"
#include "undo.h"
#include "lyrics.h"
#include "tempotext.h"
#include "measurenumber.h"
#include "marker.h"
#ifdef OMR
#include "omr/omr.h"
#include "omr/omrpage.h"
#endif
namespace Ms {
static void readText206(XmlReader& e, TextBase* t, Element* be);
//---------------------------------------------------------
// StyleVal206
// this is a list of default style values which are
// different in 3.x
//
// TODO: remove style values which are equal
//---------------------------------------------------------
struct StyleVal2 {
Sid idx;
QVariant val;
};
static const StyleVal2 style206[] = {
{ Sid::staffUpperBorder, Spatium(7.0) },
{ Sid::staffLowerBorder, Spatium(7.0) },
{ Sid::staffDistance, Spatium(6.5) },
{ Sid::akkoladeDistance, Spatium(6.5) },
{ Sid::minSystemDistance, Spatium(8.5) },
{ Sid::maxSystemDistance, Spatium(15.0) },
// { Sid::lyricsMinBottomDistance, Spatium(4.0) }, // no longer makes sense
{ Sid::lyricsLineHeight, QVariant(1.0) },
{ Sid::lyricsDashForce, QVariant(false) },
{ Sid::figuredBassFontFamily, QVariant(QString("MScoreBC")) },
{ Sid::figuredBassFontSize, QVariant(8.0) },
{ Sid::figuredBassYOffset, QVariant(6.0) },
{ Sid::figuredBassLineHeight, QVariant(1.0) },
{ Sid::figuredBassAlignment, QVariant(0) },
{ Sid::figuredBassStyle, QVariant(0) },
{ Sid::systemFrameDistance, Spatium(7.0) },
{ Sid::frameSystemDistance, Spatium(7.0) },
{ Sid::minMeasureWidth, Spatium(5.0) },
{ Sid::barWidth, Spatium(0.16) }, // 0.1875
{ Sid::doubleBarWidth, Spatium(0.16) },
{ Sid::endBarWidth, Spatium(0.5) }, // 0.5
{ Sid::doubleBarDistance, Spatium(0.46) }, // 0.3 + doubleBarWidth
{ Sid::endBarDistance, Spatium(0.65) }, // 0.3
{ Sid::repeatBarTips, QVariant(false) },
{ Sid::startBarlineSingle, QVariant(false) },
{ Sid::startBarlineMultiple, QVariant(true) },
{ Sid::bracketWidth, Spatium(0.45) },
{ Sid::bracketDistance, Spatium(0.1) },
{ Sid::akkoladeWidth, Spatium(1.6) },
{ Sid::akkoladeBarDistance, Spatium(.4) },
{ Sid::clefLeftMargin, Spatium(0.64) },
{ Sid::keysigLeftMargin, Spatium(0.5) },
{ Sid::timesigLeftMargin, Spatium(0.5) },
{ Sid::clefKeyRightMargin, Spatium(1.75) },
{ Sid::clefBarlineDistance, Spatium(0.5) },
{ Sid::stemWidth, Spatium(0.13) }, // 0.09375
{ Sid::shortenStem, QVariant(true) },
{ Sid::shortStemProgression, Spatium(0.25) },
{ Sid::shortestStem, Spatium(2.25) },
{ Sid::beginRepeatLeftMargin, Spatium(1.0) },
{ Sid::minNoteDistance, Spatium(0.25) }, // 0.4
{ Sid::barNoteDistance, Spatium(1.2) },
{ Sid::barAccidentalDistance, Spatium(.3) },
{ Sid::multiMeasureRestMargin, Spatium(1.2) },
{ Sid::noteBarDistance, Spatium(1.0) },
{ Sid::measureSpacing, QVariant(1.2) },
{ Sid::staffLineWidth, Spatium(0.08) }, // 0.09375
{ Sid::ledgerLineWidth, Spatium(0.16) }, // 0.1875
{ Sid::ledgerLineLength, Spatium(.6) }, // notehead width + this value
{ Sid::accidentalDistance, Spatium(0.22) },
{ Sid::accidentalNoteDistance, Spatium(0.22) },
{ Sid::beamWidth, Spatium(0.5) }, // was 0.48
{ Sid::beamDistance, QVariant(0.5) }, // 0.25sp
{ Sid::beamMinLen, QVariant(1.32) }, // 1.316178 exactly notehead width
{ Sid::beamNoSlope, QVariant(false) },
{ Sid::dotMag, QVariant(1.0) },
{ Sid::dotNoteDistance, QVariant(0.35) },
{ Sid::dotRestDistance, QVariant(0.25) },
{ Sid::dotDotDistance, QVariant(0.5) },
{ Sid::propertyDistanceHead, QVariant(1.0) },
{ Sid::propertyDistanceStem, QVariant(1.8) },
{ Sid::propertyDistance, QVariant(1.0) },
{ Sid::articulationMag, QVariant(1.0) },
{ Sid::lastSystemFillLimit, QVariant(0.3) },
{ Sid::hairpinPosBelow, QPointF(0.0, 3.5) },
{ Sid::hairpinHeight, QVariant(1.2) },
{ Sid::hairpinContHeight, QVariant(0.5) },
{ Sid::hairpinLineWidth, QVariant(0.13) },
{ Sid::pedalPosBelow, QPointF(0.0, 4) },
{ Sid::pedalLineWidth, QVariant(.15) },
{ Sid::pedalLineStyle, QVariant(int(Qt::SolidLine)) },
{ Sid::trillPosAbove, QPointF(0.0, -1) },
{ Sid::harmonyFretDist, QVariant(0.5) },
{ Sid::minHarmonyDistance, QVariant(0.5) },
{ Sid::maxHarmonyBarDistance, QVariant(3.0) },
{ Sid::capoPosition, QVariant(0) },
{ Sid::fretNumMag, QVariant(2.0) },
{ Sid::fretNumPos, QVariant(0) },
{ Sid::fretY, QVariant(1.0) },
{ Sid::showPageNumber, QVariant(true) },
{ Sid::showPageNumberOne, QVariant(false) },
{ Sid::pageNumberOddEven, QVariant(true) },
{ Sid::showMeasureNumber, QVariant(true) },
{ Sid::showMeasureNumberOne, QVariant(false) },
{ Sid::measureNumberInterval, QVariant(5) },
{ Sid::measureNumberSystem, QVariant(true) },
{ Sid::measureNumberAllStaves, QVariant(false) },
{ Sid::smallNoteMag, QVariant(.7) },
{ Sid::graceNoteMag, QVariant(0.7) },
{ Sid::smallStaffMag, QVariant(0.7) },
{ Sid::smallClefMag, QVariant(0.8) },
{ Sid::genClef, QVariant(true) },
{ Sid::genKeysig, QVariant(true) },
{ Sid::genCourtesyTimesig, QVariant(true) },
{ Sid::genCourtesyKeysig, QVariant(true) },
{ Sid::genCourtesyClef, QVariant(true) },
{ Sid::swingRatio, QVariant(60) },
{ Sid::swingUnit, QVariant(QString("")) },
{ Sid::useStandardNoteNames, QVariant(true) },
{ Sid::useGermanNoteNames, QVariant(false) },
{ Sid::useFullGermanNoteNames, QVariant(false) },
{ Sid::useSolfeggioNoteNames, QVariant(false) },
{ Sid::useFrenchNoteNames, QVariant(false) },
{ Sid::automaticCapitalization, QVariant(true) },
{ Sid::lowerCaseMinorChords, QVariant(false) },
{ Sid::lowerCaseBassNotes, QVariant(false) },
{ Sid::allCapsNoteNames, QVariant(false) },
{ Sid::chordStyle, QVariant(QString("std")) },
{ Sid::chordsXmlFile, QVariant(false) },
{ Sid::chordDescriptionFile, QVariant(QString("chords_std.xml")) },
{ Sid::concertPitch, QVariant(false) },
{ Sid::createMultiMeasureRests, QVariant(false) },
{ Sid::minEmptyMeasures, QVariant(2) },
{ Sid::minMMRestWidth, Spatium(4) },
{ Sid::hideEmptyStaves, QVariant(false) },
{ Sid::dontHideStavesInFirstSystem, QVariant(true) },
{ Sid::hideInstrumentNameIfOneInstrument, QVariant(true) },
{ Sid::gateTime, QVariant(100) },
{ Sid::tenutoGateTime, QVariant(100) },
{ Sid::staccatoGateTime, QVariant(50) },
{ Sid::slurGateTime, QVariant(100) },
{ Sid::ArpeggioNoteDistance, QVariant(.5) },
{ Sid::ArpeggioLineWidth, QVariant(.18) },
{ Sid::ArpeggioHookLen, QVariant(.8) },
{ Sid::SlurEndWidth, QVariant(.07) },
{ Sid::SlurMidWidth, QVariant(.15) },
{ Sid::SlurDottedWidth, QVariant(.1) },
{ Sid::MinTieLength, QVariant(1.0) },
{ Sid::SectionPause, QVariant(qreal(3.0)) },
{ Sid::MusicalSymbolFont, QVariant(QString("Emmentaler")) },
{ Sid::MusicalTextFont, QVariant(QString("MScore Text")) },
{ Sid::showHeader, QVariant(false) },
{ Sid::headerFirstPage, QVariant(false) },
{ Sid::headerOddEven, QVariant(true) },
{ Sid::evenHeaderL, QVariant(QString()) },
{ Sid::evenHeaderC, QVariant(QString()) },
{ Sid::evenHeaderR, QVariant(QString()) },
{ Sid::oddHeaderL, QVariant(QString()) },
{ Sid::oddHeaderC, QVariant(QString()) },
{ Sid::oddHeaderR, QVariant(QString()) },
{ Sid::showFooter, QVariant(true) },
{ Sid::footerFirstPage, QVariant(true) },
{ Sid::footerOddEven, QVariant(true) },
{ Sid::evenFooterL, QVariant(QString("$p")) },
{ Sid::evenFooterC, QVariant(QString("$:copyright:")) },
{ Sid::evenFooterR, QVariant(QString()) },
{ Sid::oddFooterL, QVariant(QString()) },
{ Sid::oddFooterC, QVariant(QString("$:copyright:")) },
{ Sid::oddFooterR, QVariant(QString("$p")) },
{ Sid::voltaPosAbove, QPointF(0.0, -3.0) },
{ Sid::voltaHook, QVariant(1.9) },
{ Sid::voltaLineWidth, QVariant(.1) },
{ Sid::voltaLineStyle, QVariant(int(Qt::SolidLine)) },
{ Sid::ottavaPosAbove, QPointF(0.0, -3.0) },
{ Sid::ottavaHookAbove, QVariant(1.9) },
{ Sid::ottavaHookBelow, QVariant(-1.9) },
{ Sid::ottavaLineWidth, QVariant(.1) },
{ Sid::ottavaLineStyle, QVariant(int(Qt::DashLine)) },
{ Sid::ottavaNumbersOnly, true },
{ Sid::tabClef, QVariant(int(ClefType::TAB)) },
{ Sid::tremoloWidth, QVariant(1.2) }, // tremolo stroke width: notehead width
{ Sid::tremoloBoxHeight, QVariant(0.65) },
{ Sid::tremoloStrokeWidth, QVariant(0.5) }, // was 0.35
{ Sid::tremoloDistance, QVariant(0.8) },
// TODO { Sid::tremoloBeamLengthMultiplier, QVariant(0.62) },
// TODO { Sid::tremoloMaxBeamLength, QVariant(12.0) },
{ Sid::linearStretch, QVariant(qreal(1.5)) },
{ Sid::crossMeasureValues, QVariant(false) },
{ Sid::keySigNaturals, QVariant(int(KeySigNatural::NONE)) },
{ Sid::tupletMaxSlope, QVariant(qreal(0.5)) },
{ Sid::tupletOufOfStaff, QVariant(true) },
{ Sid::tupletVHeadDistance, QVariant(.5) },
{ Sid::tupletVStemDistance, QVariant(.25) },
{ Sid::tupletStemLeftDistance, QVariant(.5) },
{ Sid::tupletStemRightDistance, QVariant(.5) },
{ Sid::tupletNoteLeftDistance, QVariant(0.0) },
{ Sid::tupletNoteRightDistance, QVariant(0.0) },
{ Sid::barreLineWidth, QVariant(1.0) },
{ Sid::fretMag, QVariant(1.0) },
{ Sid::scaleBarlines, QVariant(true) },
{ Sid::barGraceDistance, QVariant(.6) },
{ Sid::rehearsalMarkFrameRound, QVariant(20) },
{ Sid::dynamicsFontStyle, int(FontStyle::Italic) },
// { Sid::staffTextFontFace, "FreeSerif" },
// { Sid::staffTextFontSize, 10.0 },
// { Sid::staffTextFontBold, false },
// { Sid::staffTextFontItalic, false },
// { Sid::staffTextFontUnderline, false },
{ Sid::staffTextAlign, QVariant::fromValue(Align::LEFT | Align::TOP) }, // different from 3.x
// { Sid::staffTextOffsetType, int(OffsetType::SPATIUM) },
// { Sid::staffTextPlacement, int(Placement::ABOVE) },
{ Sid::staffTextPosAbove, QPointF(.0, -4.0) },
// { Sid::staffTextMinDistance, Spatium(0.5) },
// { Sid::staffTextFrameType, int(FrameType::NO_FRAME) },
// { Sid::staffTextFramePadding, 0.2 },
// { Sid::staffTextFrameWidth, 0.1 },
// { Sid::staffTextFrameRound, 0 },
// { Sid::staffTextFrameFgColor, QColor(0, 0, 0, 255) },
// { Sid::staffTextFrameBgColor, QColor(255, 255, 255, 0) },
{ Sid::defaultFrameRound, QVariant(25) },
{ Sid::defaultFrameWidth, Spatium(0.2) },
{ Sid::defaultFramePadding, Spatium(0.5) },
{ Sid::titleFrameRound, QVariant(25) },
{ Sid::titleFrameWidth, Spatium(0.2) },
{ Sid::titleFramePadding, Spatium(0.5) },
{ Sid::subTitleFrameRound, QVariant(25) },
{ Sid::subTitleFrameWidth, Spatium(0.2) },
{ Sid::subTitleFramePadding, Spatium(0.5) },
{ Sid::composerFrameRound, QVariant(25) },
{ Sid::composerFrameWidth, Spatium(0.2) },
{ Sid::composerFramePadding, Spatium(0.5) },
{ Sid::lyricistFrameRound, QVariant(25) },
{ Sid::lyricistFrameWidth, Spatium(0.2) },
{ Sid::lyricistFramePadding, Spatium(0.5) },
{ Sid::fingeringFrameRound, QVariant(25) },
{ Sid::fingeringFrameWidth, Spatium(0.2) },
{ Sid::fingeringFramePadding, Spatium(0.5) },
{ Sid::lhGuitarFingeringFrameRound, QVariant(25) },
{ Sid::lhGuitarFingeringFrameWidth, Spatium(0.2) },
{ Sid::lhGuitarFingeringFramePadding, Spatium(0.5) },
{ Sid::rhGuitarFingeringFrameRound, QVariant(25) },
{ Sid::rhGuitarFingeringFrameWidth, Spatium(0.2) },
{ Sid::rhGuitarFingeringFramePadding, Spatium(0.5) },
{ Sid::partInstrumentFrameRound, QVariant(25) },
{ Sid::partInstrumentFrameWidth, Spatium(0.2) },
{ Sid::partInstrumentFramePadding, Spatium(0.5) },
{ Sid::tempoFrameRound, QVariant(0) },
{ Sid::tempoFrameWidth, Spatium(0.2) },
{ Sid::tempoFramePadding, Spatium(0.5) },
{ Sid::tempoFrameRound, QVariant(25) },
{ Sid::tempoFrameWidth, Spatium(0.2) },
{ Sid::tempoFramePadding, Spatium(0.5) },
{ Sid::systemTextFrameRound, QVariant(25) },
{ Sid::systemTextFrameWidth, Spatium(0.2) },
{ Sid::systemTextFramePadding, Spatium(0.5) },
{ Sid::staffTextFrameRound, QVariant(25) },
{ Sid::staffTextFrameWidth, Spatium(0.2) },
{ Sid::staffTextFramePadding, Spatium(0.5) },
{ Sid::rehearsalMarkFrameRound, QVariant(20) },
{ Sid::rehearsalMarkFrameWidth, Spatium(0.2) },
{ Sid::rehearsalMarkFramePadding, Spatium(0.5) },
{ Sid::repeatLeftFrameRound, QVariant(25) },
{ Sid::repeatLeftFrameWidth, Spatium(0.2) },
{ Sid::repeatLeftFramePadding, Spatium(0.5) },
{ Sid::repeatRightFrameRound, QVariant(25) },
{ Sid::repeatRightFrameWidth, Spatium(0.2) },
{ Sid::repeatRightFramePadding, Spatium(0.5) },
};
//---------------------------------------------------------
// excessTextStyles206
// The first map has the name of the style as the string
// The second map has the mapping of each Sid that the style identifies
// to the default value for that sid.
//---------------------------------------------------------
static std::map<QString, std::map<Sid, QVariant> > excessTextStyles206;
//---------------------------------------------------------
// setPageFormat
// set Style from PageFormat
//---------------------------------------------------------
void setPageFormat(MStyle* style, const PageFormat& pf)
{
style->set(Sid::pageWidth, pf.size().width());
style->set(Sid::pageHeight, pf.size().height());
style->set(Sid::pagePrintableWidth, pf.printableWidth());
style->set(Sid::pageEvenLeftMargin, pf.evenLeftMargin());
style->set(Sid::pageOddLeftMargin, pf.oddLeftMargin());
style->set(Sid::pageEvenTopMargin, pf.evenTopMargin());
style->set(Sid::pageEvenBottomMargin, pf.evenBottomMargin());
style->set(Sid::pageOddTopMargin, pf.oddTopMargin());
style->set(Sid::pageOddBottomMargin, pf.oddBottomMargin());
style->set(Sid::pageTwosided, pf.twosided());
}
//---------------------------------------------------------
// initPageFormat
// initialize PageFormat from Style
//---------------------------------------------------------
void initPageFormat(MStyle* style, PageFormat* pf)
{
QSizeF sz;
sz.setWidth(style->value(Sid::pageWidth).toReal());
sz.setHeight(style->value(Sid::pageHeight).toReal());
pf->setSize(sz);
pf->setPrintableWidth(style->value(Sid::pagePrintableWidth).toReal());
pf->setEvenLeftMargin(style->value(Sid::pageEvenLeftMargin).toReal());
pf->setOddLeftMargin(style->value(Sid::pageOddLeftMargin).toReal());
pf->setEvenTopMargin(style->value(Sid::pageEvenTopMargin).toReal());
pf->setEvenBottomMargin(style->value(Sid::pageEvenBottomMargin).toReal());
pf->setOddTopMargin(style->value(Sid::pageOddTopMargin).toReal());
pf->setOddBottomMargin(style->value(Sid::pageOddBottomMargin).toReal());
pf->setTwosided(style->value(Sid::pageTwosided).toBool());
}
//---------------------------------------------------------
// readPageFormat
//---------------------------------------------------------
void readPageFormat(MStyle* style, XmlReader& e)
{
PageFormat pf;
initPageFormat(style, &pf);
pf.read(e);
setPageFormat(style, pf);
}
//---------------------------------------------------------
// readTextStyle206
//---------------------------------------------------------
void readTextStyle206(MStyle* style, XmlReader& e, std::map<QString, std::map<Sid, QVariant> >& excessStyles)
{
QString family = "FreeSerif";
double size = 10;
bool sizeIsSpatiumDependent = false;
FontStyle fontStyle = FontStyle::Normal;
Align align = Align::LEFT;
QPointF offset;
OffsetType offsetType = OffsetType::SPATIUM;
FrameType frameType = FrameType::NO_FRAME;
Spatium paddingWidth(0.0);
Spatium frameWidth(0.0);
QColor foregroundColor = QColor(0, 0, 0, 255);
QColor backgroundColor = QColor(255, 255, 255, 0);
Placement placement = Placement::ABOVE;
bool placementValid = false;
QString name = e.attribute("name");
QColor frameColor = QColor(0, 0, 0, 255);
bool systemFlag = false;
qreal lineWidth = -1.0;
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "name") {
name = e.readElementText();
} else if (tag == "family") {
family = e.readElementText();
} else if (tag == "size") {
size = e.readDouble();
} else if (tag == "bold") {
if (e.readInt()) {
fontStyle = fontStyle + FontStyle::Bold;
}
} else if (tag == "italic") {
if (e.readInt()) {
fontStyle = fontStyle + FontStyle::Italic;
}
} else if (tag == "underline") {
if (e.readInt()) {
fontStyle = fontStyle + FontStyle::Underline;
}
} else if (tag == "align") {
align = Align(e.readInt());
} else if (tag == "anchor") { // obsolete
e.skipCurrentElement();
} else if (tag == "halign") {
const QString& val(e.readElementText());
if (val == "center") {
align = align | Align::HCENTER;
} else if (val == "right") {
align = align | Align::RIGHT;
} else if (val == "left") {
} else {
qDebug("Text::readProperties: unknown alignment: <%s>", qPrintable(val));
}
} else if (tag == "valign") {
const QString& val(e.readElementText());
if (val == "center") {
align = align | Align::VCENTER;
} else if (val == "bottom") {
align = align | Align::BOTTOM;
} else if (val == "baseline") {
align = align | Align::BASELINE;
} else if (val == "top") {
} else {
qDebug("Text::readProperties: unknown alignment: <%s>", qPrintable(val));
}
} else if (tag == "xoffset") {
qreal xo = e.readDouble();
if (offsetType == OffsetType::ABS) {
xo /= INCH;
}
offset.setX(xo);
} else if (tag == "yoffset") {
qreal yo = e.readDouble();
if (offsetType == OffsetType::ABS) {
yo /= INCH;
}
offset.setY(yo);
} else if (tag == "rxoffset" || tag == "ryoffset") { // obsolete
e.readDouble();
} else if (tag == "offsetType") {
const QString& val(e.readElementText());
OffsetType ot = OffsetType::ABS;
if (val == "spatium" || val == "1") {
ot = OffsetType::SPATIUM;
}
if (ot != offsetType) {
offsetType = ot;
if (ot == OffsetType::ABS) {
offset /= INCH; // convert spatium -> inch
} else {
offset *= INCH; // convert inch -> spatium
}
}
} else if (tag == "sizeIsSpatiumDependent" || tag == "spatiumSizeDependent") {
sizeIsSpatiumDependent = e.readInt();
} else if (tag == "frameWidth") { // obsolete
frameType = FrameType::SQUARE;
/*frameWidthMM =*/ e.readDouble();
} else if (tag == "frameWidthS") {
frameType = FrameType::SQUARE;
frameWidth = Spatium(e.readDouble());
} else if (tag == "frame") {
frameType = e.readInt() ? FrameType::SQUARE : FrameType::NO_FRAME;
} else if (tag == "paddingWidth") { // obsolete
/*paddingWidthMM =*/
e.readDouble();
} else if (tag == "paddingWidthS") {
paddingWidth = Spatium(e.readDouble());
} else if (tag == "frameRound") {
e.readInt();
} else if (tag == "frameColor") {
frameColor = e.readColor();
} else if (tag == "foregroundColor") {
foregroundColor = e.readColor();
} else if (tag == "backgroundColor") {
backgroundColor = e.readColor();
} else if (tag == "circle") {
frameType = e.readInt() ? FrameType::CIRCLE : FrameType::NO_FRAME;
} else if (tag == "systemFlag") {
systemFlag = e.readInt();
} else if (tag == "placement") {
QString value(e.readElementText());
if (value == "above") {
placement = Placement::ABOVE;
} else if (value == "below") {
placement = Placement::BELOW;
}
placementValid = true;
} else if (tag == "lineWidth") {
lineWidth = e.readDouble();
} else {
e.unknown();
}
}
if (family == "MuseJazz") {
family = "MuseJazz Text";
}
struct StyleTable {
const char* name;
Tid ss;
} styleTable[] = {
{ "", Tid::DEFAULT },
{ "Title", Tid::TITLE },
{ "Subtitle", Tid::SUBTITLE },
{ "Composer", Tid::COMPOSER },
{ "Lyricist", Tid::POET },
{ "Lyrics Odd Lines", Tid::LYRICS_ODD },
{ "Lyrics Even Lines", Tid::LYRICS_EVEN },
{ "Fingering", Tid::FINGERING },
{ "LH Guitar Fingering", Tid::LH_GUITAR_FINGERING },
{ "RH Guitar Fingering", Tid::RH_GUITAR_FINGERING },
{ "String Number", Tid::STRING_NUMBER },
{ "Instrument Name (Long)", Tid::INSTRUMENT_LONG },
{ "Instrument Name (Short)", Tid::INSTRUMENT_SHORT },
{ "Instrument Name (Part)", Tid::INSTRUMENT_EXCERPT },
{ "Dynamics", Tid::DYNAMICS },
{ "Technique", Tid::EXPRESSION },
{ "Tempo", Tid::TEMPO },
{ "Metronome", Tid::METRONOME },
{ "Measure Number", Tid::MEASURE_NUMBER },
{ "Translator", Tid::TRANSLATOR },
{ "Tuplet", Tid::TUPLET },
{ "System", Tid::SYSTEM },
{ "Staff", Tid::STAFF },
{ "Chord Symbol", Tid::HARMONY_A },
{ "Rehearsal Mark", Tid::REHEARSAL_MARK },
{ "Repeat Text Left", Tid::REPEAT_LEFT },
{ "Repeat Text Right", Tid::REPEAT_RIGHT },
{ "Frame", Tid::FRAME },
{ "Text Line", Tid::TEXTLINE },
{ "Glissando", Tid::GLISSANDO },
{ "Ottava", Tid::OTTAVA },
{ "Pedal", Tid::PEDAL },
{ "Hairpin", Tid::HAIRPIN },
{ "Bend", Tid::BEND },
{ "Header", Tid::HEADER },
{ "Footer", Tid::FOOTER },
{ "Instrument Change", Tid::INSTRUMENT_CHANGE },
{ "Repeat Text", Tid::IGNORED_STYLES, }, // Repeat Text style no longer exists
{ "Figured Bass", Tid::IGNORED_STYLES, }, // F.B. data are in style properties
{ "Volta", Tid::VOLTA },
};
Tid ss = Tid::TEXT_STYLES;
for (const auto& i : styleTable) {
if (name == i.name) {
ss = i.ss;
break;
}
}
if (ss == Tid::IGNORED_STYLES) {
return;
}
bool isExcessStyle = false;
if (ss == Tid::TEXT_STYLES) {
ss = e.addUserTextStyle(name);
if (ss == Tid::TEXT_STYLES) {
qDebug("unhandled substyle <%s>", qPrintable(name));
isExcessStyle = true;
} else {
int idx = int(ss) - int(Tid::USER1);
if ((int(ss) < int(Tid::USER1)) || (int(ss) > int(Tid::USER12))) {
qDebug("User style index %d outside of range.", idx);
return;
}
Sid sid[]
= { Sid::user1Name, Sid::user2Name, Sid::user3Name, Sid::user4Name, Sid::user5Name, Sid::user6Name,
Sid::user7Name, Sid::user8Name, Sid::user9Name, Sid::user10Name, Sid::user11Name,
Sid::user12Name };
style->set(sid[idx], name);
}
}
std::map<Sid, QVariant> excessPairs;
const TextStyle* ts;
if (isExcessStyle) {
ts = textStyle("User-1");
} else {
ts = textStyle(ss);
}
for (const auto& i : *ts) {
QVariant value;
if (i.sid == Sid::NOSTYLE) {
break;
}
switch (i.pid) {
case Pid::SUB_STYLE:
value = int(ss);
break;
case Pid::BEGIN_FONT_FACE:
case Pid::CONTINUE_FONT_FACE:
case Pid::END_FONT_FACE:
case Pid::FONT_FACE:
value = family;
break;
case Pid::BEGIN_FONT_SIZE:
case Pid::CONTINUE_FONT_SIZE:
case Pid::END_FONT_SIZE:
case Pid::FONT_SIZE:
value = size;
break;
case Pid::BEGIN_FONT_STYLE:
case Pid::CONTINUE_FONT_STYLE:
case Pid::END_FONT_STYLE:
case Pid::FONT_STYLE:
value = int(fontStyle);
break;
case Pid::FRAME_TYPE:
value = int(frameType);
break;
case Pid::FRAME_WIDTH:
value = frameWidth;
break;
case Pid::FRAME_PADDING:
value = paddingWidth;
break;
case Pid::FRAME_FG_COLOR:
value = frameColor;
break;
case Pid::FRAME_BG_COLOR:
value = backgroundColor;
break;
case Pid::SIZE_SPATIUM_DEPENDENT:
value = sizeIsSpatiumDependent;
break;
case Pid::BEGIN_TEXT_ALIGN:
case Pid::CONTINUE_TEXT_ALIGN:
case Pid::END_TEXT_ALIGN:
case Pid::ALIGN:
value = QVariant::fromValue(align);
break;
#if 0 //TODO-offset
case Pid::OFFSET:
if (offsetValid) {
if (ss == Tid::TEMPO) {
style->set(Sid::tempoPosAbove, Spatium(offset.y()));
offset = QPointF();
} else if (ss == Tid::STAFF) {
style->set(Sid::staffTextPosAbove, Spatium(offset.y()));
offset = QPointF();
} else if (ss == Tid::REHEARSAL_MARK) {
style->set(Sid::rehearsalMarkPosAbove, Spatium(offset.y()));
offset = QPointF();
}
value = offset;
}
break;
case Pid::OFFSET_TYPE:
value = int(offsetType);
break;
#endif
case Pid::SYSTEM_FLAG:
value = systemFlag;
break;
case Pid::BEGIN_HOOK_HEIGHT:
case Pid::END_HOOK_HEIGHT:
value = QVariant();
break;
case Pid::PLACEMENT:
if (placementValid) {
value = int(placement);
}
break;
case Pid::LINE_WIDTH:
if (lineWidth != -1.0) {
value = lineWidth;
}
break;
default:
// qDebug("unhandled property <%s>%d", propertyName(i.pid), int (i.pid));
break;
}
if (value.isValid()) {
if (isExcessStyle) {
excessPairs[i.sid] = value;
} else {
style->set(i.sid, value);
}
}
// else
// qDebug("invalid style value <%s> pid<%s>", MStyle::valueName(i.sid), propertyName(i.pid));
}
if (isExcessStyle && excessPairs.size() > 0) {
excessStyles[name] = excessPairs;
}
}
//---------------------------------------------------------
// readAccidental206
//---------------------------------------------------------
void readAccidental206(Accidental* a, XmlReader& e)
{
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "bracket") {
int i = e.readInt();
if (i == 0 || i == 1) {
a->setBracket(AccidentalBracket(i));
}
} else if (tag == "subtype") {
QString text = e.readElementText();
const static std::map<QString, AccidentalType> accMap = {
{ "none", AccidentalType::NONE },
{ "sharp", AccidentalType::SHARP },
{ "flat", AccidentalType::FLAT },
{ "natural", AccidentalType::NATURAL },
{ "double sharp", AccidentalType::SHARP2 },
{ "double flat", AccidentalType::FLAT2 },
{ "flat-slash", AccidentalType::FLAT_SLASH },
{ "flat-slash2", AccidentalType::FLAT_SLASH2 },
{ "mirrored-flat2", AccidentalType::MIRRORED_FLAT2 },
{ "mirrored-flat", AccidentalType::MIRRORED_FLAT },
{ "sharp-slash", AccidentalType::SHARP_SLASH },
{ "sharp-slash2", AccidentalType::SHARP_SLASH2 },
{ "sharp-slash3", AccidentalType::SHARP_SLASH3 },
{ "sharp-slash4", AccidentalType::SHARP_SLASH4 },
{ "sharp arrow up", AccidentalType::SHARP_ARROW_UP },
{ "sharp arrow down", AccidentalType::SHARP_ARROW_DOWN },
{ "flat arrow up", AccidentalType::FLAT_ARROW_UP },
{ "flat arrow down", AccidentalType::FLAT_ARROW_DOWN },
{ "natural arrow up", AccidentalType::NATURAL_ARROW_UP },
{ "natural arrow down", AccidentalType::NATURAL_ARROW_DOWN },
{ "sori", AccidentalType::SORI },
{ "koron", AccidentalType::KORON }
};
auto it = accMap.find(text);
if (it == accMap.end()) {
qDebug("invalid type %s", qPrintable(text));
a->setAccidentalType(AccidentalType::NONE);
} else {
a->setAccidentalType(it->second);
}
} else if (tag == "role") {
AccidentalRole r = AccidentalRole(e.readInt());
if (r == AccidentalRole::AUTO || r == AccidentalRole::USER) {
a->setRole(r);
}
} else if (tag == "small") {
a->setSmall(e.readInt());
} else if (a->Element::readProperties(e)) {
} else {
e.unknown();
}
}
}
static NoteHead::Group convertHeadGroup(int i)
{
NoteHead::Group val;
switch (i) {
case 1:
val = NoteHead::Group::HEAD_CROSS;
break;
case 2:
val = NoteHead::Group::HEAD_DIAMOND;
break;
case 3:
val = NoteHead::Group::HEAD_TRIANGLE_DOWN;
break;
case 4:
val = NoteHead::Group::HEAD_MI;
break;
case 5:
val = NoteHead::Group::HEAD_SLASH;
break;
case 6:
val = NoteHead::Group::HEAD_XCIRCLE;
break;
case 7:
val = NoteHead::Group::HEAD_DO;
break;
case 8:
val = NoteHead::Group::HEAD_RE;
break;
case 9:
val = NoteHead::Group::HEAD_FA;
break;
case 10:
val = NoteHead::Group::HEAD_LA;
break;
case 11:
val = NoteHead::Group::HEAD_TI;
break;
case 12:
val = NoteHead::Group::HEAD_SOL;
break;
case 13:
val = NoteHead::Group::HEAD_BREVIS_ALT;
break;
case 0:
default:
val = NoteHead::Group::HEAD_NORMAL;
}
return val;
}
static NoteHead::Type convertHeadType(int i)
{
NoteHead::Type val;
switch (i) {
case 0:
val = NoteHead::Type::HEAD_WHOLE;
break;
case 1:
val = NoteHead::Type::HEAD_HALF;
break;
case 2:
val = NoteHead::Type::HEAD_QUARTER;
break;
case 3:
val = NoteHead::Type::HEAD_BREVIS;
break;
default:
val = NoteHead::Type::HEAD_AUTO;
}
return val;
}
//---------------------------------------------------------
// ArticulationNames
//---------------------------------------------------------
static struct ArticulationNames {
SymId id;
const char* name;
} articulationNames[] = {
{ SymId::fermataAbove, "fermata", },
{ SymId::fermataShortAbove, "shortfermata", },
{ SymId::fermataLongAbove, "longfermata", },
{ SymId::fermataVeryLongAbove, "verylongfermata", },
{ SymId::articAccentAbove, "sforzato", },
{ SymId::articStaccatoAbove, "staccato", },
{ SymId::articStaccatissimoAbove, "staccatissimo", },
{ SymId::articTenutoAbove, "tenuto", },
{ SymId::articTenutoStaccatoAbove, "portato", },
{ SymId::articMarcatoAbove, "marcato", },
{ SymId::guitarFadeIn, "fadein", },
{ SymId::guitarFadeOut, "fadeout", },
{ SymId::guitarVolumeSwell, "volumeswell", },
{ SymId::wiggleSawtooth, "wigglesawtooth", },
{ SymId::wiggleSawtoothWide, "wigglesawtoothwide", },
{ SymId::wiggleVibratoLargeFaster, "wigglevibratolargefaster", },
{ SymId::wiggleVibratoLargeSlowest, "wigglevibratolargeslowest", },
{ SymId::brassMuteOpen, "ouvert", },
{ SymId::brassMuteClosed, "plusstop", },
{ SymId::stringsUpBow, "upbow", },
{ SymId::stringsDownBow, "downbow", },
{ SymId::ornamentTurnInverted, "reverseturn", },
{ SymId::ornamentTurn, "turn", },
{ SymId::ornamentTrill, "trill", },
{ SymId::ornamentMordent, "prall", },
{ SymId::ornamentMordentInverted, "mordent", },
{ SymId::ornamentTremblement, "prallprall", },
{ SymId::ornamentPrallMordent, "prallmordent", },
{ SymId::ornamentUpPrall, "upprall", },
{ SymId::ornamentUpMordent, "upmordent", },
{ SymId::ornamentDownMordent, "downmordent", },
{ SymId::ornamentPrallDown, "pralldown", },
{ SymId::ornamentPrallUp, "prallup", },
{ SymId::ornamentLinePrall, "lineprall", },
{ SymId::ornamentPrecompSlide, "schleifer", },
{ SymId::pluckedSnapPizzicatoAbove, "snappizzicato", },
{ SymId::stringsThumbPosition, "thumb", },
{ SymId::luteFingeringRHThumb, "lutefingeringthumb", },
{ SymId::luteFingeringRHFirst, "lutefingering1st", },
{ SymId::luteFingeringRHSecond, "lutefingering2nd", },
{ SymId::luteFingeringRHThird, "lutefingering3rd", },
{ SymId::ornamentPrecompMordentUpperPrefix, "downprall" },
{ SymId::ornamentPrecompMordentUpperPrefix, "ornamentDownPrall" },
};
//---------------------------------------------------------
// oldArticulationNames2SymId
//---------------------------------------------------------
SymId oldArticulationNames2SymId(const QString& s)
{
for (auto i : articulationNames) {
if (i.name == s) {
return i.id;
}
}
return SymId::noSym;
}
//---------------------------------------------------------
// readDrumset
//---------------------------------------------------------
static void readDrumset(Drumset* ds, XmlReader& e)
{
int pitch = e.intAttribute("pitch", -1);
if (pitch < 0 || pitch > 127) {
qDebug("load drumset: invalid pitch %d", pitch);
return;
}
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "head") {
ds->drum(pitch).notehead = convertHeadGroup(e.readInt());
} else if (tag == "variants") {
while (e.readNextStartElement()) {
const QStringRef& tagv(e.name());
if (tagv == "variant") {
DrumInstrumentVariant div;
div.pitch = e.attribute("pitch").toInt();
while (e.readNextStartElement()) {
const QStringRef& taga(e.name());
if (taga == "articulation") {
QString oldArticulationName = e.readElementText();
SymId oldId = oldArticulationNames2SymId(oldArticulationName);
div.articulationName = Articulation::symId2ArticulationName(oldId);
} else if (taga == "tremolo") {
div.tremolo = Tremolo::name2Type(e.readElementText());
}
}
ds->drum(pitch).addVariant(div);
}
}
} else if (ds->readProperties(e, pitch)) {
} else {
e.unknown();
}
}
}
//---------------------------------------------------------
// readInstrument
//---------------------------------------------------------
static void readInstrument(Instrument* i, Part* p, XmlReader& e)
{
int program = -1;
int bank = 0;
int volume = 100;
int pan = 60;
int chorus = 30;
int reverb = 30;
bool customDrumset = false;
i->clearChannels(); // remove default channel
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "Drum") {
// if we see one of this tags, a custom drumset will
// be created
if (!i->drumset()) {
i->setDrumset(new Drumset(*smDrumset));
}
if (!customDrumset) {
i->drumset()->clear();
customDrumset = true;
}
readDrumset(i->drumset(), e);
} else if (i->readProperties(e, p, &customDrumset)) {
} else {
e.unknown();
}
}
// Read single-note dynamics from template
i->setSingleNoteDynamicsFromTemplate();
if (i->channel().empty()) { // for backward compatibility
Channel* a = new Channel;
a->setName(Channel::DEFAULT_NAME);
a->setProgram(program);
a->setBank(bank);
a->setVolume(volume);
a->setPan(pan);
a->setReverb(reverb);
a->setChorus(chorus);
i->appendChannel(a);
}
if (i->useDrumset()) {
if (i->channel()[0]->bank() == 0) {
i->channel()[0]->setBank(128);
}
}
}
//---------------------------------------------------------
// readStaff
//---------------------------------------------------------
static void readStaff(Staff* staff, XmlReader& e)
{
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "type") { // obsolete
int staffTypeIdx = e.readInt();
qDebug("obsolete: Staff::read staffTypeIdx %d", staffTypeIdx);
} else if (tag == "neverHide") {
bool v = e.readInt();
if (v) {
staff->setHideWhenEmpty(Staff::HideMode::NEVER);
}
} else if (tag == "barLineSpan") {
staff->setBarLineFrom(e.intAttribute("from", 0));
staff->setBarLineTo(e.intAttribute("to", 0));
int span = e.readInt();
staff->setBarLineSpan(span - 1);
} else if (staff->readProperties(e)) {
} else {
e.unknown();
}
}
}
//---------------------------------------------------------
// readPart
//---------------------------------------------------------
void readPart206(Part* part, XmlReader& e)
{
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "Instrument") {
Instrument* i = part->_instruments.instrument(/* tick */ -1);
readInstrument(i, part, e);
Drumset* ds = i->drumset();
Staff* s = part->staff(0);
int lld = s ? qRound(s->lineDistance(Fraction(0,1))) : 1;
if (ds && s && lld > 1) {
for (int j = 0; j < DRUM_INSTRUMENTS; ++j) {
ds->drum(j).line /= lld;
}
}
} else if (tag == "Staff") {
Staff* staff = new Staff(part->score());
staff->setPart(part);
part->score()->staves().push_back(staff);
part->staves()->push_back(staff);
readStaff(staff, e);
} else if (part->readProperties(e)) {
} else {
e.unknown();
}
}
}
//---------------------------------------------------------
// readAmbitus
//---------------------------------------------------------
static void readAmbitus(Ambitus* ambitus, XmlReader& e)
{
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "head") {
ambitus->setNoteHeadGroup(convertHeadGroup(e.readInt()));
} else if (tag == "headType") {
ambitus->setNoteHeadType(convertHeadType(e.readInt()));
} else if (ambitus->readProperties(e)) {
} else {
e.unknown();
}
}
}
//---------------------------------------------------------
// readNote
//---------------------------------------------------------
static void readNote(Note* note, XmlReader& e)
{
note->setTpc1(Tpc::TPC_INVALID);
note->setTpc2(Tpc::TPC_INVALID);
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "Accidental") {
Accidental* a = new Accidental(note->score());
a->setTrack(note->track());
readAccidental206(a, e);
note->add(a);
} else if (tag == "head") {
int i = e.readInt();
NoteHead::Group val = convertHeadGroup(i);
note->setHeadGroup(val);
} else if (tag == "headType") {
int i = e.readInt();
NoteHead::Type val = convertHeadType(i);
note->setHeadType(val);
} else if (readNoteProperties206(note, e)) {
} else {
e.unknown();
}
}
// ensure sane values:
note->setPitch(limit(note->pitch(), 0, 127));
if (!tpcIsValid(note->tpc1()) && !tpcIsValid(note->tpc2())) {
Key key = (note->staff() && note->chord()) ? note->staff()->key(note->chord()->tick()) : Key::C;
int tpc = pitch2tpc(note->pitch(), key, Prefer::NEAREST);
if (note->concertPitch()) {
note->setTpc1(tpc);
} else {
note->setTpc2(tpc);
}
}
if (!(tpcIsValid(note->tpc1()) && tpcIsValid(note->tpc2()))) {
Fraction tick = note->chord() ? note->chord()->tick() : Fraction(-1,1);
Interval v = note->staff() ? note->part()->instrument(tick)->transpose() : Interval();
if (tpcIsValid(note->tpc1())) {
v.flip();
if (v.isZero()) {
note->setTpc2(note->tpc1());
} else {
note->setTpc2(Ms::transposeTpc(note->tpc1(), v, true));
}
} else {
if (v.isZero()) {
note->setTpc1(note->tpc2());
} else {
note->setTpc1(Ms::transposeTpc(note->tpc2(), v, true));
}
}
}
#if 0
// TODO - adapt this code
// check consistency of pitch, tpc1, tpc2, and transposition
// see note in InstrumentChange::read() about a known case of tpc corruption produced in 2.0.x
// but since there are other causes of tpc corruption (eg, https://musescore.org/en/node/74746)
// including perhaps some we don't know about yet,
// we will attempt to fix some problems here regardless of version
if (staff() && !staff()->isDrumStaff(e.tick()) && !e.pasteMode() && !MScore::testMode) {
int tpc1Pitch = (tpc2pitch(_tpc[0]) + 12) % 12;
int tpc2Pitch = (tpc2pitch(_tpc[1]) + 12) % 12;
int soundingPitch = _pitch % 12;
if (tpc1Pitch != soundingPitch) {
qDebug("bad tpc1 - soundingPitch = %d, tpc1 = %d", soundingPitch, tpc1Pitch);
_pitch += tpc1Pitch - soundingPitch;
}
if (staff()) {
Interval v = staff()->part()->instrument(e.tick())->transpose();
int writtenPitch = (_pitch - v.chromatic) % 12;
if (tpc2Pitch != writtenPitch) {
qDebug("bad tpc2 - writtenPitch = %d, tpc2 = %d", writtenPitch, tpc2Pitch);
if (concertPitch()) {
// assume we want to keep sounding pitch
// so fix written pitch (tpc only)
v.flip();
_tpc[1] = Ms::transposeTpc(_tpc[0], v, true);
} else {
// assume we want to keep written pitch
// so fix sounding pitch (both tpc and pitch)
_tpc[0] = Ms::transposeTpc(_tpc[1], v, true);
_pitch += tpc2Pitch - writtenPitch;
}
}
}
}
#endif
}
//---------------------------------------------------------
// adjustPlacement
//---------------------------------------------------------
static void adjustPlacement(Element* e)
{
if (!e || !e->staff()) {
return;
}
// element to use to determine placement
// for spanners, choose first segment
Element* ee;
Spanner* spanner;
if (e->isSpanner()) {
spanner = toSpanner(e);
if (spanner->spannerSegments().empty()) {
return;
}
ee = spanner->spannerSegments().front();
if (!ee) {
return;
}
} else {
spanner = nullptr;
ee = e;
}
// determine placement based on offset
// anything below staff will be set to below
qreal staffHeight = e->staff()->height();
qreal threshold = staffHeight;
qreal offsetAdjust = 0.0;
Placement defaultPlacement = Placement(e->propertyDefault(Pid::PLACEMENT).toInt());
Placement newPlacement;
// most offsets will be recorded as relative to top staff line
// exceptions are styled offsets on elements with default placement below
qreal normalize;
if (defaultPlacement == Placement::BELOW && ee->propertyFlags(Pid::OFFSET) == PropertyFlags::STYLED) {
normalize = staffHeight;
} else {
normalize = 0.0;
}
qreal ypos = ee->offset().y() + normalize;
if (ypos >= threshold) {
newPlacement = Placement::BELOW;
offsetAdjust -= staffHeight;
} else {
newPlacement = Placement::ABOVE;
}
// set placement
e->setProperty(Pid::PLACEMENT, int(newPlacement));
if (newPlacement != defaultPlacement) {
e->setPropertyFlags(Pid::PLACEMENT, PropertyFlags::UNSTYLED);
}
// adjust offset
if (spanner) {
// adjust segments individually
for (auto a : spanner->spannerSegments()) {
// spanner segments share the placement setting of the spanner
// just adjust offset
if (defaultPlacement == Placement::BELOW && a->propertyFlags(Pid::OFFSET) == PropertyFlags::STYLED) {
normalize = staffHeight;
} else {
normalize = 0.0;
}
qreal yp = a->offset().y() + normalize;
a->ryoffset() += normalize + offsetAdjust;
// if any segments are offset to opposite side of staff from placement,
// or if they are within staff,
// disable autoplace
bool disableAutoplace;
if (yp + a->height() <= 0.0) {
disableAutoplace = (newPlacement == Placement::BELOW);
} else if (yp > staffHeight) {
disableAutoplace = (newPlacement == Placement::ABOVE);
} else {
disableAutoplace = true;
}
if (disableAutoplace) {
a->setAutoplace(false);
}
// needed for https://musescore.org/en/node/281312
// ideally we would rebase and calculate new offset
// but this may not be possible
// since original offset is relative to system
a->rxoffset() = 0;
}
} else {
e->ryoffset() += normalize + offsetAdjust;
// if within staff, disable autoplace
if (ypos + e->height() > 0.0 && ypos <= staffHeight) {
e->setAutoplace(false);
}
}
}
//---------------------------------------------------------
// readNoteProperties206
//---------------------------------------------------------
bool readNoteProperties206(Note* note, XmlReader& e)
{
const QStringRef& tag(e.name());
if (tag == "pitch") {
note->setPitch(e.readInt());
} else if (tag == "tpc") {
const int tpc = e.readInt();
note->setTpc1(tpc);
note->setTpc2(tpc);
} else if (tag == "track") { // for performance
note->setTrack(e.readInt());
} else if (tag == "Accidental") {
Accidental* a = new Accidental(note->score());
a->setTrack(note->track());
a->read(e);
note->add(a);
} else if (tag == "Tie") {
Tie* tie = new Tie(note->score());
tie->setParent(note);
tie->setTrack(note->track());
readTie206(e, tie);
tie->setStartNote(note);
note->setTieFor(tie);
} else if (tag == "tpc2") {
note->setTpc2(e.readInt());
} else if (tag == "small") {
note->setSmall(e.readInt());
} else if (tag == "mirror") {
note->readProperty(e, Pid::MIRROR_HEAD);
} else if (tag == "dotPosition") {
note->readProperty(e, Pid::DOT_POSITION);
} else if (tag == "fixed") {
note->setFixed(e.readBool());
} else if (tag == "fixedLine") {
note->setFixedLine(e.readInt());
} else if (tag == "head") {
note->readProperty(e, Pid::HEAD_GROUP);
} else if (tag == "velocity") {
note->setVeloOffset(e.readInt());
} else if (tag == "play") {
note->setPlay(e.readInt());
} else if (tag == "tuning") {
note->setTuning(e.readDouble());
} else if (tag == "fret") {
note->setFret(e.readInt());
} else if (tag == "string") {
note->setString(e.readInt());
} else if (tag == "ghost") {
note->setGhost(e.readInt());
} else if (tag == "headType") {
note->readProperty(e, Pid::HEAD_TYPE);
} else if (tag == "veloType") {
note->readProperty(e, Pid::VELO_TYPE);
} else if (tag == "line") {
note->setLine(e.readInt());
} else if (tag == "Fingering") {
Fingering* f = new Fingering(note->score());
f->setTrack(note->track());
readText206(e, f, note);
note->add(f);
} else if (tag == "Symbol") {
Symbol* s = new Symbol(note->score());
s->setTrack(note->track());
s->read(e);
note->add(s);
} else if (tag == "Image") {
if (MScore::noImages) {
e.skipCurrentElement();
} else {
Image* image = new Image(note->score());
image->setTrack(note->track());
image->read(e);
note->add(image);
}
} else if (tag == "Bend") {
Bend* b = new Bend(note->score());
b->setTrack(note->track());
b->read(e);
note->add(b);
} else if (tag == "NoteDot") {
NoteDot* dot = new NoteDot(note->score());
dot->read(e);
note->add(dot);
} else if (tag == "Events") {
note->playEvents().clear(); // remove default event
while (e.readNextStartElement()) {
const QStringRef& etag(e.name());
if (etag == "Event") {
NoteEvent ne;
ne.read(e);
note->playEvents().append(ne);
} else {
e.unknown();
}
}
if (Chord* ch = note->chord()) {
ch->setPlayEventType(PlayEventType::User);
}
} else if (tag == "endSpanner") {
int id = e.intAttribute("id");
Spanner* sp = e.findSpanner(id);
if (sp) {
sp->setEndElement(note);
if (sp->isTie()) {
note->setTieBack(toTie(sp));
} else {
if (sp->isGlissando() && note->parent() && note->parent()->isChord()) {
toChord(note->parent())->setEndsGlissando(true);
}
note->addSpannerBack(sp);
}
e.removeSpanner(sp);
} else {
// End of a spanner whose start element will appear later;
// may happen for cross-staff spanner from a lower to a higher staff
// (for instance a glissando from bass to treble staff of piano).
// Create a place-holder spanner with end data
// (a TextLine is used only because both Spanner or SLine are abstract,
// the actual class does not matter, as long as it is derived from Spanner)
int id1 = e.intAttribute("id", -1);
Staff* staff = note->staff();
if (id1 != -1
&& // DISABLE if pasting into a staff with linked staves
// because the glissando is not properly cloned into the linked staves
staff && (!e.pasteMode() || !staff->links() || staff->links()->empty())) {
Spanner* placeholder = new TextLine(note->score());
placeholder->setAnchor(Spanner::Anchor::NOTE);
placeholder->setEndElement(note);
placeholder->setTrack2(note->track());
placeholder->setTick(Fraction(0,1));
placeholder->setTick2(e.tick());
e.addSpanner(id1, placeholder);
}
}
e.readNext();
} else if (tag == "TextLine"
|| tag == "Glissando") {
Spanner* sp = toSpanner(Element::name2Element(tag, note->score()));
// check this is not a lower-to-higher cross-staff spanner we already got
int id = e.intAttribute("id");
Spanner* placeholder = e.findSpanner(id);
if (placeholder && placeholder->endElement()) {
// if it is, fill end data from place-holder
sp->setAnchor(Spanner::Anchor::NOTE); // make sure we can set a Note as end element
sp->setEndElement(placeholder->endElement());
sp->setTrack2(placeholder->track2());
sp->setTick(e.tick()); // make sure tick2 will be correct
sp->setTick2(placeholder->tick2());
toNote(placeholder->endElement())->addSpannerBack(sp);
// remove no longer needed place-holder before reading the new spanner,
// as reading it also adds it to XML reader list of spanners,
// which would overwrite the place-holder
e.removeSpanner(placeholder);
delete placeholder;
}
sp->setTrack(note->track());
sp->read(e);
Staff* staff = note->staff();
// DISABLE pasting of glissandi into staves with other lionked staves
// because the glissando is not properly cloned into the linked staves
if (e.pasteMode() && staff && staff->links() && !staff->links()->empty()) {
e.removeSpanner(sp); // read() added the element to the XMLReader: remove it
delete sp;
} else {
sp->setAnchor(Spanner::Anchor::NOTE);
sp->setStartElement(note);
sp->setTick(e.tick());
note->addSpannerFor(sp);
sp->setParent(note);
}
adjustPlacement(sp);
} else if (tag == "offset") {
note->Element::readProperties(e);
} else if (note->Element::readProperties(e)) {
} else {
return false;
}
return true;
}
//---------------------------------------------------------
// ReadStyleName206
// Retrieve the content of the "style" tag from the
// QString with the content of the whole text tag
//---------------------------------------------------------
static QString ReadStyleName206(QString xmlTag)
{
QString s;
if (xmlTag.contains("<style>")) {
QRegExp re("<style>([^<]+)</style>");
if (re.indexIn(xmlTag) > -1) {
s = re.cap(1);
}
}
return s;
}
//---------------------------------------------------------
// readTextPropertyStyle206
// This reads only the 'style' tag, so that it can be read
// before setting anything else.
//---------------------------------------------------------
static bool readTextPropertyStyle206(QString xmlTag, const XmlReader& e, TextBase* t, Element* be)
{
QString s = ReadStyleName206(xmlTag);
if (s.isEmpty()) {
return false;
}
if (!be->isTuplet()) { // Hack
if (excessTextStyles206.find(s) != excessTextStyles206.end()) {
// Init the text with a style that can't be stored as a user style
// due to the limit on the number of user styles possible.
// Use User-1, since it has all the possible user style pids
t->initTid(Tid::DEFAULT);
std::map<Sid, QVariant> styleVals = excessTextStyles206[s];
for (const StyledProperty& p : *textStyle("User-1")) {
if (t->getProperty(p.pid) == t->propertyDefault(p.pid) && styleVals.find(p.sid) != styleVals.end()) {
t->setProperty(p.pid, styleVals[p.sid]);
}
}
} else {
Tid ss;
ss = e.lookupUserTextStyle(s);
if (ss == Tid::TEXT_STYLES) {
ss = textStyleFromName(s);
}
if (ss != Tid::TEXT_STYLES) {
t->initTid(ss);
}
}
}
return true;
}
//---------------------------------------------------------
// readTextProperties206
//---------------------------------------------------------
static bool readTextProperties206(XmlReader& e, TextBase* t)
{
const QStringRef& tag(e.name());
if (tag == "style") {
e.skipCurrentElement(); // read in readTextPropertyStyle206
} else if (tag == "foregroundColor") { // same as "color" ?
e.skipCurrentElement();
} else if (tag == "frame") {
t->setFrameType(e.readBool() ? FrameType::SQUARE : FrameType::NO_FRAME);
t->setPropertyFlags(Pid::FRAME_TYPE, PropertyFlags::UNSTYLED);
} else if (tag == "frameRound") {
t->readProperty(e, Pid::FRAME_ROUND);
} else if (tag == "circle") {
if (e.readBool()) {
t->setFrameType(FrameType::CIRCLE);
} else {
if (t->circle()) {
t->setFrameType(FrameType::SQUARE);
}
}
t->setPropertyFlags(Pid::FRAME_TYPE, PropertyFlags::UNSTYLED);
} else if (tag == "paddingWidthS") {
t->readProperty(e, Pid::FRAME_PADDING);
} else if (tag == "frameWidthS") {
t->readProperty(e, Pid::FRAME_WIDTH);
} else if (tag == "frameColor") {
t->readProperty(e, Pid::FRAME_FG_COLOR);
} else if (tag == "backgroundColor") {
t->readProperty(e, Pid::FRAME_BG_COLOR);
} else if (tag == "halign") {
Align align = Align(int(t->align()) & int(~Align::HMASK));
const QString& val(e.readElementText());
if (val == "center") {
align = align | Align::HCENTER;
} else if (val == "right") {
align = align | Align::RIGHT;
} else if (val == "left") {
} else {
qDebug("unknown alignment: <%s>", qPrintable(val));
}
t->setAlign(align);
t->setPropertyFlags(Pid::ALIGN, PropertyFlags::UNSTYLED);
} else if (tag == "valign") {
Align align = Align(int(t->align()) & int(~Align::VMASK));
const QString& val(e.readElementText());
if (val == "center") {
align = align | Align::VCENTER;
} else if (val == "bottom") {
align = align | Align::BOTTOM;
} else if (val == "baseline") {
align = align | Align::BASELINE;
} else if (val == "top") {
} else {
qDebug("unknown alignment: <%s>", qPrintable(val));
}
t->setAlign(align);
t->setPropertyFlags(Pid::ALIGN, PropertyFlags::UNSTYLED);
} else if (tag == "pos") {
t->readProperty(e, Pid::OFFSET);
if ((char(t->align()) & char(Align::VMASK)) == char(Align::TOP)) {
t->ryoffset() += .5 * t->score()->spatium(); // HACK: bbox is different in 2.x
}
adjustPlacement(t);
} else if (!t->readProperties(e)) {
return false;
}
return true;
}
//---------------------------------------------------------
// TextReaderContext206
// For 2.x files, the style tag could be in a different
// position with respect to 3.x files. Since seek
// position is not reliable for readline in QIODevices (for
// example because of non-single-byte characters in at least
// one of the fields; some two-byte characters are counted as
// two single-byte characters and thus the reading could
// start at the wrong position), a copy of the text tag
// is created and read in a separate XmlReader, while
// the text style is extracted from a QString containing
// the whole text xml tag.
// TextReaderContext206 takes care of this process
//---------------------------------------------------------
class TextReaderContext206
{
XmlReader& origReader;
XmlReader tagReader;
QString xmlTag;
public:
TextReaderContext206(XmlReader& e)
: origReader(e), tagReader(QString())
{
// Create a new xml document containing only the (text) xml chunk
QString name = origReader.name().toString();
qint64 additionalLines = origReader.lineNumber() - 2; // Subtracting the 2 new lines that will be added
xmlTag = origReader.readXml();
xmlTag.prepend("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<" + name + ">");
xmlTag.append("</" + name + ">\n");
tagReader.addData(xmlTag); // Add the xml data to the XmlReader
// the additional lines are needed to output the correct line number
// of the original file in case of error
tagReader.setOffsetLines(additionalLines);
copyProperties(origReader, tagReader);
tagReader.readNextStartElement(); // read up to the first "name" tag
}
// Disable copying the TextReaderContext206
TextReaderContext206(const TextReaderContext206&) = delete;
TextReaderContext206& operator=(const TextReaderContext206&) = delete;
~TextReaderContext206()
{
// Ensure to copy back the potentially changed properties
// to the original XmlReader before destruction
copyProperties(tagReader, origReader);
}
XmlReader& reader() { return tagReader; }
const QString& tag() { return xmlTag; }
private:
void copyProperties(XmlReader& original, XmlReader& derived);
};
//---------------------------------------------------------
// copyProperties
// Copy some of the XmlReader properties of an original
// XmlReader object to a "derived" XmlReader object.
// This is used for example when using the derived XmlReader
// to read the element properties of a 2.x text, or to
// update the base XmlReader object (for example, its
// link list) after reading the properties of a 2.x text
//---------------------------------------------------------
void TextReaderContext206::copyProperties(XmlReader& original, XmlReader& derived)
{
derived.setDocName(original.getDocName());
derived.setTrackOffset(original.trackOffset());
derived.setTrack(original.track() - original.trackOffset());
derived.setTickOffset(original.tickOffset());
derived.setTick(original.tick() - original.tickOffset());
derived.setLastMeasure(original.lastMeasure());
derived.setCurrentMeasure(original.currentMeasure());
derived.setCurrentMeasureIndex(original.currentMeasureIndex());
derived.linkIds() = original.linkIds();
derived.staffLinkedElements() = original.staffLinkedElements();
}
//---------------------------------------------------------
// readText206
//---------------------------------------------------------
static void readText206(XmlReader& e, TextBase* t, Element* be)
{
TextReaderContext206 ctx(e);
readTextPropertyStyle206(ctx.tag(), e, t, be);
while (ctx.reader().readNextStartElement()) {
if (!readTextProperties206(ctx.reader(), t)) {
ctx.reader().unknown();
}
}
}
//---------------------------------------------------------
// read
//---------------------------------------------------------
static void readTempoText(TempoText* t, XmlReader& e)
{
TextReaderContext206 ctx(e);
readTextPropertyStyle206(ctx.tag(), e, t, t);
while (ctx.reader().readNextStartElement()) {
const QStringRef& tag(ctx.reader().name());
if (tag == "tempo") {
t->setTempo(ctx.reader().readDouble());
} else if (tag == "followText") {
t->setFollowText(ctx.reader().readInt());
} else if (!readTextProperties206(ctx.reader(), t)) {
ctx.reader().unknown();
}
}
// check sanity
if (t->xmlText().isEmpty()) {
t->setXmlText(QString("<sym>metNoteQuarterUp</sym> = %1").arg(lrint(60 * t->tempo())));
t->setVisible(false);
} else {
t->setXmlText(t->xmlText().replace("<sym>unicode", "<sym>met"));
}
}
//---------------------------------------------------------
// readMarker
//---------------------------------------------------------
static void readMarker(Marker* m, XmlReader& e)
{
TextReaderContext206 ctx(e);
readTextPropertyStyle206(ctx.tag(), e, m, m);
Marker::Type mt = Marker::Type::SEGNO;
while (ctx.reader().readNextStartElement()) {
const QStringRef& tag(ctx.reader().name());
if (tag == "label") {
QString s(ctx.reader().readElementText());
m->setLabel(s);
mt = m->markerType(s);
} else if (!readTextProperties206(ctx.reader(), m)) {
ctx.reader().unknown();
}
}
m->setMarkerType(mt);
}
//---------------------------------------------------------
// readDynamic
//---------------------------------------------------------
static void readDynamic(Dynamic* d, XmlReader& e)
{
TextReaderContext206 ctx(e);
readTextPropertyStyle206(ctx.tag(), e, d, d);
while (ctx.reader().readNextStartElement()) {
const QStringRef& tag = ctx.reader().name();
if (tag == "subtype") {
d->setDynamicType(ctx.reader().readElementText());
} else if (tag == "velocity") {
d->setVelocity(ctx.reader().readInt());
} else if (tag == "dynType") {
d->setDynRange(Dynamic::Range(ctx.reader().readInt()));
} else if (!readTextProperties206(ctx.reader(), d)) {
ctx.reader().unknown();
}
}
}
//---------------------------------------------------------
// readTuplet
//---------------------------------------------------------
static void readTuplet(Tuplet* tuplet, XmlReader& e)
{
tuplet->setId(e.intAttribute("id", 0));
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "Number") {
Text* _number = new Text(tuplet->score());
_number->setParent(tuplet);
_number->setComposition(true);
tuplet->setNumber(_number);
// _number reads property defaults from parent tuplet as "composition" is set:
tuplet->resetNumberProperty();
readText206(e, _number, tuplet);
_number->setVisible(tuplet->visible()); //?? override saved property
_number->setTrack(tuplet->track());
// move property flags from _number
for (auto p :
{ Pid::FONT_FACE, Pid::FONT_SIZE, Pid::FONT_STYLE, Pid::ALIGN, Pid::SIZE_SPATIUM_DEPENDENT }) {
tuplet->setPropertyFlags(p, _number->propertyFlags(p));
}
} else if (!readTupletProperties206(e, tuplet)) {
e.unknown();
}
}
Fraction r = (tuplet->ratio() == Fraction(1,1)) ? tuplet->ratio() : tuplet->ratio().reduced();
Fraction f(r.denominator(), tuplet->baseLen().fraction().denominator());
tuplet->setTicks(f.reduced());
}
//---------------------------------------------------------
// readLyrics
//---------------------------------------------------------
static void readLyrics(Lyrics* lyrics, XmlReader& e)
{
int iEndTick = 0; // used for backward compatibility
Text* _verseNumber = 0;
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "endTick") {
// store <endTick> tag value until a <ticks> tag has been read
// which positions this lyrics element in the score
iEndTick = e.readInt();
} else if (tag == "Number") {
_verseNumber = new Text(lyrics->score());
readText206(e, _verseNumber, lyrics);
_verseNumber->setParent(lyrics);
} else if (tag == "style") {
e.readElementText(); // ignore style
} else if (!lyrics->readProperties(e)) {
e.unknown();
}
}
// if any endTick, make it relative to current tick
if (iEndTick) {
lyrics->setTicks(Fraction::fromTicks(iEndTick) - e.tick());
}
if (_verseNumber) {
// TODO: add text to main text
delete _verseNumber;
}
lyrics->setAutoplace(true);
if (!lyrics->isStyled(Pid::OFFSET) && !e.pasteMode()) {
// fix offset for pre-3.1 scores
// 2.x and earlier: y offset was relative to staff; x offset was relative to center of notehead
lyrics->rxoffset() -= lyrics->symWidth(SymId::noteheadBlack) * 0.5;
//lyrics->ryoffset() -= lyrics->placeBelow() && lyrics->staff() ? lyrics->staff()->height() : 0.0;
// temporarily set placement to above, since the original offset is relative to top of staff
// depend on adjustPlacement() to change the placement if appropriate
lyrics->setPlacement(Placement::ABOVE);
adjustPlacement(lyrics);
}
}
//---------------------------------------------------------
// readDurationProperties206
//---------------------------------------------------------
bool readDurationProperties206(XmlReader& e, DurationElement* de)
{
if (e.name() == "Tuplet") {
int i = e.readInt();
Tuplet* t = e.findTuplet(i);
if (!t) {
qDebug("readDurationProperties206(): Tuplet id %d not found", i);
t = de->score()->searchTuplet(e, i);
if (t) {
qDebug(" ...found outside measure, input file corrupted?");
e.addTuplet(t);
}
}
if (t) {
de->setTuplet(t);
if (!de->score()->undoStack()->active()) { // HACK, also added in Undo::AddElement()
t->add(de);
}
}
return true;
} else if (de->Element::readProperties(e)) {
return true;
}
return false;
}
//---------------------------------------------------------
// readTupletProperties206
//---------------------------------------------------------
bool readTupletProperties206(XmlReader& e, Tuplet* de)
{
const QStringRef& tag(e.name());
if (de->readStyledProperty(e, tag)) {
} else if (tag == "normalNotes") {
de->setProperty(Pid::NORMAL_NOTES, e.readInt());
} else if (tag == "actualNotes") {
de->setProperty(Pid::ACTUAL_NOTES, e.readInt());
} else if (tag == "p1") {
de->setProperty(Pid::P1, e.readPoint() * de->score()->spatium());
} else if (tag == "p2") {
de->setProperty(Pid::P2, e.readPoint() * de->score()->spatium());
} else if (tag == "baseNote") {
de->setBaseLen(TDuration(e.readElementText()));
} else if (tag == "Number") {
Text* _number = new Text(de->score());
de->setNumber(_number);
_number->setComposition(true);
_number->setParent(de);
// _number->setSubStyleId(SubStyleId::TUPLET);
// initSubStyle(SubStyleId::TUPLET); // hack: initialize number
for (auto p : { Pid::FONT_FACE, Pid::FONT_SIZE, Pid::FONT_STYLE, Pid::ALIGN }) {
_number->resetProperty(p);
}
readText206(e, _number, de);
_number->setVisible(de->visible()); //?? override saved property
_number->setTrack(de->track());
// move property flags from _number
for (auto p : { Pid::FONT_FACE, Pid::FONT_SIZE, Pid::FONT_STYLE, Pid::ALIGN }) {
de->setPropertyFlags(p, _number->propertyFlags(p));
}
} else if (!readDurationProperties206(e, de)) {
return false;
}
return true;
}
//---------------------------------------------------------
// readChordRestProperties206
//---------------------------------------------------------
bool readChordRestProperties206(XmlReader& e, ChordRest* ch)
{
const QStringRef& tag(e.name());
if (tag == "durationType") {
ch->setDurationType(e.readElementText());
if (ch->actualDurationType().type() != TDuration::DurationType::V_MEASURE) {
if (ch->score()->mscVersion() < 112 && (ch->type() == ElementType::REST)
&& // for backward compatibility, convert V_WHOLE rests to V_MEASURE
// if long enough to fill a measure.
// OTOH, freshly created (un-initialized) rests have numerator == 0 (< 4/4)
// (see Fraction() constructor in fraction.h; this happens for instance
// when pasting selection from clipboard): they should not be converted
ch->ticks().numerator() != 0
&& // rest durations are initialized to full measure duration when
// created upon reading the <Rest> tag (see Measure::read() )
// so a V_WHOLE rest in a measure of 4/4 or less => V_MEASURE
(ch->actualDurationType() == TDuration::DurationType::V_WHOLE && ch->ticks() <= Fraction(4, 4))) {
// old pre 2.0 scores: convert
ch->setDurationType(TDuration::DurationType::V_MEASURE);
} else { // not from old score: set duration fraction from duration type
ch->setTicks(ch->actualDurationType().fraction());
}
} else {
if (ch->score()->mscVersion() <= 114) {
SigEvent event = ch->score()->sigmap()->timesig(e.tick());
ch->setTicks(event.timesig());
}
}
} else if (tag == "BeamMode") {
QString val(e.readElementText());
Beam::Mode bm = Beam::Mode::AUTO;
if (val == "auto") {
bm = Beam::Mode::AUTO;
} else if (val == "begin") {
bm = Beam::Mode::BEGIN;
} else if (val == "mid") {
bm = Beam::Mode::MID;
} else if (val == "end") {
bm = Beam::Mode::END;
} else if (val == "no") {
bm = Beam::Mode::NONE;
} else if (val == "begin32") {
bm = Beam::Mode::BEGIN32;
} else if (val == "begin64") {
bm = Beam::Mode::BEGIN64;
} else {
bm = Beam::Mode(val.toInt());
}
ch->setBeamMode(bm);
} else if (tag == "Articulation") {
Element* el = readArticulation(ch, e);
if (el->isFermata()) {
ch->segment()->add(el);
} else {
ch->add(el);
}
} else if (tag == "leadingSpace" || tag == "trailingSpace") {
qDebug("ChordRest: %s obsolete", tag.toLocal8Bit().data());
e.skipCurrentElement();
} else if (tag == "Beam") {
int id = e.readInt();
Beam* beam = e.findBeam(id);
if (beam) {
beam->add(ch); // also calls ch->setBeam(beam)
} else {
qDebug("Beam id %d not found", id);
}
} else if (tag == "small") {
ch->setSmall(e.readInt());
} else if (tag == "duration") {
ch->setTicks(e.readFraction());
} else if (tag == "ticklen") { // obsolete (version < 1.12)
int mticks = ch->score()->sigmap()->timesig(e.tick()).timesig().ticks();
int i = e.readInt();
if (i == 0) {
i = mticks;
}
if ((ch->type() == ElementType::REST) && (mticks == i)) {
ch->setDurationType(TDuration::DurationType::V_MEASURE);
ch->setTicks(Fraction::fromTicks(i));
} else {
Fraction f = Fraction::fromTicks(i);
ch->setTicks(f);
ch->setDurationType(TDuration(f));
}
} else if (tag == "dots") {
ch->setDots(e.readInt());
} else if (tag == "move") {
ch->setStaffMove(e.readInt());
} else if (tag == "Slur") {
int id = e.intAttribute("id");
if (id == 0) {
id = e.intAttribute("number"); // obsolete
}
Spanner* spanner = e.findSpanner(id);
QString atype(e.attribute("type"));
if (!spanner) {
if (atype == "stop") {
SpannerValues sv;
sv.spannerId = id;
sv.track2 = ch->track();
sv.tick2 = e.tick();
e.addSpannerValues(sv);
} else if (atype == "start") {
qDebug("spanner: start without spanner");
}
} else {
if (atype == "start") {
if (spanner->ticks() > Fraction(0,1) && spanner->tick() == Fraction(-1,1)) { // stop has been read first
spanner->setTicks(spanner->ticks() - e.tick() - Fraction::fromTicks(1));
}
spanner->setTick(e.tick());
spanner->setTrack(ch->track());
if (spanner->type() == ElementType::SLUR) {
spanner->setStartElement(ch);
}
if (e.pasteMode()) {
for (ScoreElement* el : spanner->linkList()) {
if (el == spanner) {
continue;
}
Spanner* ls = static_cast<Spanner*>(el);
ls->setTick(spanner->tick());
for (ScoreElement* ee : ch->linkList()) {
ChordRest* cr = toChordRest(ee);
if (cr->score() == ee->score() && cr->staffIdx() == ls->staffIdx()) {
ls->setTrack(cr->track());
if (ls->type() == ElementType::SLUR) {
ls->setStartElement(cr);
}
break;
}
}
}
}
} else if (atype == "stop") {
spanner->setTick2(e.tick());
spanner->setTrack2(ch->track());
if (spanner->isSlur()) {
spanner->setEndElement(ch);
}
ChordRest* start = toChordRest(spanner->startElement());
if (start) {
spanner->setTrack(start->track());
}
if (e.pasteMode()) {
for (ScoreElement* el : spanner->linkList()) {
if (el == spanner) {
continue;
}
Spanner* ls = static_cast<Spanner*>(el);
ls->setTick2(spanner->tick2());
for (ScoreElement* ee : ch->linkList()) {
ChordRest* cr = toChordRest(ee);
if (cr->score() == ee->score() && cr->staffIdx() == ls->staffIdx()) {
ls->setTrack2(cr->track());
if (ls->type() == ElementType::SLUR) {
ls->setEndElement(cr);
}
break;
}
}
}
}
} else {
qDebug("readChordRestProperties206(): unknown Slur type <%s>", qPrintable(atype));
}
}
e.readNext();
} else if (tag == "Lyrics") {
Lyrics* l = new Lyrics(ch->score());
l->setTrack(e.track());
readLyrics(l, e);
ch->add(l);
} else if (tag == "pos") {
QPointF pt = e.readPoint();
ch->setOffset(pt * ch->spatium());
} else if (ch->isRest() && tag == "Image") {
if (MScore::noImages) {
e.skipCurrentElement();
} else {
Image* image = new Image(ch->score());
image->setTrack(e.track());
image->read(e);
ch->add(image);
}
} else if (!readDurationProperties206(e, ch)) {
return false;
}
return true;
}
//---------------------------------------------------------
// readChordProperties206
//---------------------------------------------------------
bool readChordProperties206(XmlReader& e, Chord* ch)
{
const QStringRef& tag(e.name());
if (tag == "Note") {
Note* note = new Note(ch->score());
// the note needs to know the properties of the track it belongs to
note->setTrack(ch->track());
note->setChord(ch);
readNote(note, e);
ch->add(note);
} else if (readChordRestProperties206(e, ch)) {
} else if (tag == "Stem") {
Stem* s = new Stem(ch->score());
s->read(e);
ch->add(s);
} else if (tag == "Hook") {
Hook* hook = new Hook(ch->score());
hook->read(e);
ch->add(hook);
} else if (tag == "appoggiatura") {
ch->setNoteType(NoteType::APPOGGIATURA);
e.readNext();
} else if (tag == "acciaccatura") {
ch->setNoteType(NoteType::ACCIACCATURA);
e.readNext();
} else if (tag == "grace4") {
ch->setNoteType(NoteType::GRACE4);
e.readNext();
} else if (tag == "grace16") {
ch->setNoteType(NoteType::GRACE16);
e.readNext();
} else if (tag == "grace32") {
ch->setNoteType(NoteType::GRACE32);
e.readNext();
} else if (tag == "grace8after") {
ch->setNoteType(NoteType::GRACE8_AFTER);
e.readNext();
} else if (tag == "grace16after") {
ch->setNoteType(NoteType::GRACE16_AFTER);
e.readNext();
} else if (tag == "grace32after") {
ch->setNoteType(NoteType::GRACE32_AFTER);
e.readNext();
} else if (tag == "StemSlash") {
StemSlash* ss = new StemSlash(ch->score());
ss->read(e);
ch->add(ss);
} else if (ch->readProperty(tag, e, Pid::STEM_DIRECTION)) {
} else if (tag == "noStem") {
ch->setNoStem(e.readInt());
} else if (tag == "Arpeggio") {
Arpeggio* arpeggio = new Arpeggio(ch->score());
arpeggio->setTrack(ch->track());
arpeggio->read(e);
arpeggio->setParent(ch);
ch->add(arpeggio);
}
// old glissando format, chord-to-chord, attached to its final chord
else if (tag == "Glissando") {
// the measure we are reading is not inserted in the score yet
// as well as, possibly, the glissando intended initial chord;
// then we cannot fully link the glissando right now;
// temporarily attach the glissando to its final note as a back spanner;
// after the whole score is read, Score::connectTies() will look for
// the suitable initial note
Note* finalNote = ch->upNote();
Glissando* gliss = new Glissando(ch->score());
gliss->read(e);
gliss->setAnchor(Spanner::Anchor::NOTE);
gliss->setStartElement(nullptr);
gliss->setEndElement(nullptr);
// in TAB, use straight line with no text
if (ch->score()->staff(e.track() >> 2)->isTabStaff(ch->tick())) {
gliss->setGlissandoType(GlissandoType::STRAIGHT);
gliss->setShowText(false);
}
finalNote->addSpannerBack(gliss);
} else if (tag == "Tremolo") {
Tremolo* tremolo = new Tremolo(ch->score());
tremolo->setTrack(ch->track());
tremolo->read(e);
tremolo->setParent(ch);
tremolo->setDurationType(ch->durationType());
ch->setTremolo(tremolo);
} else if (tag == "tickOffset") { // obsolete
} else if (tag == "ChordLine") {
ChordLine* cl = new ChordLine(ch->score());
cl->read(e);
QPointF o = cl->offset();
cl->setOffset(0.0, 0.0);
ch->add(cl);
e.fixOffsets().append({ cl, o });
} else {
return false;
}
return true;
}
//---------------------------------------------------------
// convertDoubleArticulations
// Replace double articulations with proper SMuFL
// symbols which were not available for use prior to 3.0
//---------------------------------------------------------
static void convertDoubleArticulations(Chord* chord, XmlReader& e)
{
std::vector<Articulation*> pairableArticulations;
for (Articulation* a : chord->articulations()) {
if (a->isStaccato() || a->isTenuto()
|| a->isAccent() || a->isMarcato()) {
pairableArticulations.push_back(a);
}
}
if (pairableArticulations.size() != 2) {
// Do not replace triple articulation if this happens
return;
}
SymId newSymId = SymId::noSym;
for (int i = 0; i < 2; ++i) {
if (newSymId != SymId::noSym) {
break;
}
Articulation* ai = pairableArticulations[i];
Articulation* aj = pairableArticulations[(i == 0) ? 1 : 0];
if (ai->isStaccato()) {
if (aj->isAccent()) {
newSymId = SymId::articAccentStaccatoAbove;
} else if (aj->isMarcato()) {
newSymId = SymId::articMarcatoStaccatoAbove;
} else if (aj->isTenuto()) {
newSymId = SymId::articTenutoStaccatoAbove;
}
} else if (ai->isTenuto()) {
if (aj->isAccent()) {
newSymId = SymId::articTenutoAccentAbove;
} else if (aj->isMarcato()) {
newSymId = SymId::articMarcatoTenutoAbove;
}
}
}
if (newSymId != SymId::noSym) {
// We reuse old articulation and change symbol ID
// rather than constructing a new articulation
// in order to preserve its other properties.
Articulation* newArtic = pairableArticulations[0];
for (Articulation* a : pairableArticulations) {
chord->remove(a);
if (a != newArtic) {
if (LinkedElements* link = a->links()) {
e.linkIds().remove(link->lid());
}
delete a;
}
}
ArticulationAnchor anchor = newArtic->anchor();
newArtic->setSymId(newSymId);
newArtic->setAnchor(anchor);
chord->add(newArtic);
}
}
//---------------------------------------------------------
// readChord
//---------------------------------------------------------
static void readChord(Chord* chord, XmlReader& e)
{
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "Note") {
Note* note = new Note(chord->score());
// the note needs to know the properties of the track it belongs to
note->setTrack(chord->track());
note->setChord(chord);
readNote(note, e);
chord->add(note);
} else if (tag == "Stem") {
Stem* stem = new Stem(chord->score());
while (e.readNextStartElement()) {
const QStringRef& t(e.name());
if (t == "subtype") { // obsolete
e.skipCurrentElement();
} else if (!stem->readProperties(e)) {
e.unknown();
}
}
chord->add(stem);
} else if (tag == "Lyrics") {
Lyrics* lyrics = new Lyrics(chord->score());
lyrics->setTrack(e.track());
readLyrics(lyrics, e);
chord->add(lyrics);
} else if (readChordProperties206(e, chord)) {
} else {
e.unknown();
}
}
convertDoubleArticulations(chord, e);
}
//---------------------------------------------------------
// readRest
//---------------------------------------------------------
static void readRest(Rest* rest, XmlReader& e)
{
while (e.readNextStartElement()) {
if (!readChordRestProperties206(e, rest)) {
e.unknown();
}
}
}
//---------------------------------------------------------
// readTextLineProperties
//---------------------------------------------------------
static bool readTextLineProperties(XmlReader& e, TextLineBase* tl)
{
const QStringRef& tag(e.name());
if (tag == "beginText") {
Text* text = new Text(tl->score());
readText206(e, text, tl);
tl->setBeginText(text->xmlText());
delete text;
} else if (tag == "continueText") {
Text* text = new Text(tl->score());
readText206(e, text, tl);
tl->setContinueText(text->xmlText());
delete text;
} else if (tag == "endText") {
Text* text = new Text(tl->score());
readText206(e, text, tl);
tl->setEndText(text->xmlText());
delete text;
} else if (tag == "beginHook") {
tl->setBeginHookType(e.readBool() ? HookType::HOOK_90 : HookType::NONE);
} else if (tag == "endHook") {
tl->setEndHookType(e.readBool() ? HookType::HOOK_90 : HookType::NONE);
} else if (tag == "beginHookType") {
tl->setBeginHookType(e.readInt() == 0 ? HookType::HOOK_90 : HookType::HOOK_45);
} else if (tag == "endHookType") {
tl->setEndHookType(e.readInt() == 0 ? HookType::HOOK_90 : HookType::HOOK_45);
} else if (tl->readProperties(e)) {
return true;
}
return true;
}
//---------------------------------------------------------
// readVolta206
//---------------------------------------------------------
static void readVolta206(XmlReader& e, Volta* volta)
{
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "endings") {
QString s = e.readElementText();
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QStringList sl = s.split(",", Qt::SkipEmptyParts);
#else
QStringList sl = s.split(",", QString::SkipEmptyParts);
#endif
volta->endings().clear();
for (const QString& l : sl) {
int i = l.simplified().toInt();
volta->endings().append(i);
}
} else if (tag == "lineWidth") {
volta->setLineWidth(e.readDouble() * volta->spatium());
// TODO lineWidthStyle = PropertyStyle::UNSTYLED;
} else if (!readTextLineProperties(e, volta)) {
e.unknown();
}
}
adjustPlacement(volta);
}
//---------------------------------------------------------
// readPedal
//---------------------------------------------------------
static void readPedal(XmlReader& e, Pedal* pedal)
{
while (e.readNextStartElement()) {
if (!readTextLineProperties(e, pedal)) {
e.unknown();
}
}
adjustPlacement(pedal);
}
//---------------------------------------------------------
// readOttava
//---------------------------------------------------------
static void readOttava(XmlReader& e, Ottava* ottava)
{
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "subtype") {
QString s = e.readElementText();
bool ok;
int idx = s.toInt(&ok);
if (!ok) {
idx = 0; // OttavaType::OTTAVA_8VA;
int i = 0;
for (auto p : { "8va","8vb","15ma","15mb","22ma","22mb" }) {
if (p == s) {
idx = i;
break;
}
++i;
}
}
ottava->setOttavaType(OttavaType(idx));
} else if (tag == "numbersOnly") {
ottava->setNumbersOnly(e.readBool());
//TODO numbersOnlyStyle = PropertyFlags::UNSTYLED;
} else if (!readTextLineProperties(e, ottava)) {
e.unknown();
}
}
ottava->styleChanged();
adjustPlacement(ottava);
}
//---------------------------------------------------------
// readHairpin206
//---------------------------------------------------------
void readHairpin206(XmlReader& e, Hairpin* h)
{
bool useText = false;
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "subtype") {
h->setHairpinType(HairpinType(e.readInt()));
} else if (tag == "lineWidth") {
h->setLineWidth(e.readDouble() * h->spatium());
// lineWidthStyle = PropertyFlags::UNSTYLED;
} else if (tag == "hairpinHeight") {
h->setHairpinHeight(Spatium(e.readDouble()));
// hairpinHeightStyle = PropertyFlags::UNSTYLED;
} else if (tag == "hairpinContHeight") {
h->setHairpinContHeight(Spatium(e.readDouble()));
// hairpinContHeightStyle = PropertyFlags::UNSTYLED;
} else if (tag == "hairpinCircledTip") {
h->setHairpinCircledTip(e.readInt());
} else if (tag == "veloChange") {
h->setVeloChange(e.readInt());
} else if (tag == "dynType") {
h->setDynRange(Dynamic::Range(e.readInt()));
} else if (tag == "useTextLine") { // < 206
e.readInt();
if (h->hairpinType() == HairpinType::CRESC_HAIRPIN) {
h->setHairpinType(HairpinType::CRESC_LINE);
} else if (h->hairpinType() == HairpinType::DECRESC_HAIRPIN) {
h->setHairpinType(HairpinType::DECRESC_LINE);
}
useText = true;
} else if (!readTextLineProperties(e, h)) {
e.unknown();
}
}
if (!useText) {
h->setBeginText("");
h->setContinueText("");
h->setEndText("");
}
adjustPlacement(h);
}
//---------------------------------------------------------
// readTrill206
//---------------------------------------------------------
void readTrill206(XmlReader& e, Trill* t)
{
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "subtype") {
t->setTrillType(e.readElementText());
} else if (tag == "Accidental") {
Accidental* _accidental = new Accidental(t->score());
readAccidental206(_accidental, e);
_accidental->setParent(t);
t->setAccidental(_accidental);
} else if (tag == "ornamentStyle") {
t->readProperty(e, Pid::ORNAMENT_STYLE);
} else if (tag == "play") {
t->setPlayArticulation(e.readBool());
} else if (!t->SLine::readProperties(e)) {
e.unknown();
}
}
adjustPlacement(t);
}
//---------------------------------------------------------
// readTextLine206
//---------------------------------------------------------
void readTextLine206(XmlReader& e, TextLineBase* tlb)
{
while (e.readNextStartElement()) {
if (!readTextLineProperties(e, tlb)) {
e.unknown();
}
}
adjustPlacement(tlb);
}
//---------------------------------------------------------
// setFermataPlacement
// set fermata placement from old ArticulationAnchor
// for backwards compatibility
//---------------------------------------------------------
static void setFermataPlacement(Element* el, ArticulationAnchor anchor, Direction direction)
{
if (direction == Direction::UP) {
el->setPlacement(Placement::ABOVE);
} else if (direction == Direction::DOWN) {
el->setPlacement(Placement::BELOW);
} else {
switch (anchor) {
case ArticulationAnchor::TOP_STAFF:
case ArticulationAnchor::TOP_CHORD:
el->setPlacement(Placement::ABOVE);
break;
case ArticulationAnchor::BOTTOM_STAFF:
case ArticulationAnchor::BOTTOM_CHORD:
el->setPlacement(Placement::BELOW);
break;
case ArticulationAnchor::CHORD:
break;
default:
break;
}
}
}
//---------------------------------------------------------
// readArticulation
//---------------------------------------------------------
Element* readArticulation(Element* parent, XmlReader& e)
{
Element* el = 0;
SymId sym = SymId::fermataAbove; // default -- backward compatibility (no type = ufermata in 1.2)
ArticulationAnchor anchor = ArticulationAnchor::TOP_STAFF;
Direction direction = Direction::AUTO;
Score* score = parent->score();
int track = parent->track();
double timeStretch = 0.0;
bool useDefaultPlacement = true;
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "subtype") {
QString s = e.readElementText();
if (s[0].isDigit()) {
int oldType = s.toInt();
sym = articulationNames[oldType].id;
} else {
sym = oldArticulationNames2SymId(s);
if (sym == SymId::noSym) {
struct {
const char* name;
bool up;
SymId id;
} al[] =
{
{ "fadein", true, SymId::guitarFadeIn },
{ "fadeout", true, SymId::guitarFadeOut },
{ "volumeswell", true, SymId::guitarVolumeSwell },
{ "wigglesawtooth", true, SymId::wiggleSawtooth },
{ "wigglesawtoothwide", true, SymId::wiggleSawtoothWide },
{ "wigglevibratolargefaster", true, SymId::wiggleVibratoLargeFaster },
{ "wigglevibratolargeslowest", true, SymId::wiggleVibratoLargeSlowest },
{ "umarcato", true, SymId::articMarcatoAbove },
{ "dmarcato", false, SymId::articMarcatoBelow },
{ "ufermata", true, SymId::fermataAbove },
{ "dfermata", false, SymId::fermataBelow },
{ "ushortfermata", true, SymId::fermataShortAbove },
{ "dshortfermata", false, SymId::fermataShortBelow },
{ "ulongfermata", true, SymId::fermataLongAbove },
{ "dlongfermata", false, SymId::fermataLongBelow },
{ "uverylongfermata", true, SymId::fermataVeryLongAbove },
{ "dverylongfermata", false, SymId::fermataVeryLongBelow },
// watch out, bug in 1.2 uportato and dportato are reversed
{ "dportato", true, SymId::articTenutoStaccatoAbove },
{ "uportato", false, SymId::articTenutoStaccatoBelow },
{ "ustaccatissimo", true, SymId::articStaccatissimoAbove },
{ "dstaccatissimo", false, SymId::articStaccatissimoBelow }
};
int i;
int n = sizeof(al) / sizeof(*al);
for (i = 0; i < n; ++i) {
if (s == al[i].name) {
sym = al[i].id;
bool up = al[i].up;
direction = up ? Direction::UP : Direction::DOWN;
if ((direction == Direction::DOWN) != (track & 1)) {
useDefaultPlacement = false;
}
break;
}
}
if (i == n) {
sym = Sym::name2id(s);
if (sym == SymId::noSym) {
qDebug("Articulation: unknown type <%s>", qPrintable(s));
}
}
}
}
switch (sym) {
case SymId::fermataAbove:
case SymId::fermataBelow:
case SymId::fermataShortAbove:
case SymId::fermataShortBelow:
case SymId::fermataLongAbove:
case SymId::fermataLongBelow:
case SymId::fermataVeryLongAbove:
case SymId::fermataVeryLongBelow:
el = new Fermata(sym, score);
break;
default:
el = new Articulation(sym, score);
toArticulation(el)->setDirection(direction);
break;
}
} else if (tag == "anchor") {
useDefaultPlacement = false;
if (!el || el->isFermata()) {
anchor = ArticulationAnchor(e.readInt());
} else {
el->readProperties(e);
}
} else if (tag == "direction") {
useDefaultPlacement = false;
if (!el || el->isFermata()) {
direction = toDirection(e.readElementText());
} else {
el->readProperties(e);
}
} else if (tag == "timeStretch") {
timeStretch = e.readDouble();
} else {
if (!el) {
qDebug("not handled <%s>", qPrintable(tag.toString()));
}
if (!el || !el->readProperties(e)) {
e.unknown();
}
}
}
// Special case for "no type" = ufermata, with missing subtype tag
if (!el) {
el = new Fermata(sym, score);
}
if (el->isFermata()) {
if (timeStretch != 0.0) {
el->setProperty(Pid::TIME_STRETCH, timeStretch);
}
if (useDefaultPlacement) {
el->setPlacement(track & 1 ? Placement::BELOW : Placement::ABOVE);
} else {
setFermataPlacement(el, anchor, direction);
}
}
el->setTrack(track);
return el;
}
//---------------------------------------------------------
// readSlurTieProperties
//---------------------------------------------------------
static bool readSlurTieProperties(XmlReader& e, SlurTie* st)
{
const QStringRef& tag(e.name());
if (st->readProperty(tag, e, Pid::SLUR_DIRECTION)) {
} else if (tag == "lineType") {
st->setLineType(e.readInt());
} else if (tag == "SlurSegment") {
SlurTieSegment* s = st->newSlurTieSegment();
s->read(e);
st->add(s);
} else if (!st->Element::readProperties(e)) {
return false;
}
return true;
}
//---------------------------------------------------------
// readSlur206
//---------------------------------------------------------
void readSlur206(XmlReader& e, Slur* s)
{
s->setTrack(e.track()); // set staff
e.addSpanner(e.intAttribute("id"), s);
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "track2") {
s->setTrack2(e.readInt());
} else if (tag == "startTrack") { // obsolete
s->setTrack(e.readInt());
} else if (tag == "endTrack") { // obsolete
e.readInt();
} else if (!readSlurTieProperties(e, s)) {
e.unknown();
}
}
if (s->track2() == -1) {
s->setTrack2(s->track());
}
}
//---------------------------------------------------------
// readTie206
//---------------------------------------------------------
void readTie206(XmlReader& e, Tie* t)
{
e.addSpanner(e.intAttribute("id"), t);
while (e.readNextStartElement()) {
if (readSlurTieProperties(e, t)) {
} else {
e.unknown();
}
}
if (t->score()->mscVersion() <= 114 && t->spannerSegments().size() == 1) {
// ignore manual adjustments to single-segment ties in older scores
TieSegment* ss = t->frontSegment();
QPointF zeroP;
ss->ups(Grip::START).off = zeroP;
ss->ups(Grip::BEZIER1).off = zeroP;
ss->ups(Grip::BEZIER2).off = zeroP;
ss->ups(Grip::END).off = zeroP;
ss->setOffset(zeroP);
ss->setUserOff2(zeroP);
}
}
//---------------------------------------------------------
// readMeasure
//---------------------------------------------------------
static void readMeasure(Measure* m, int staffIdx, XmlReader& e)
{
Segment* segment = 0;
qreal _spatium = m->spatium();
Score* score = m->score();
QList<Chord*> graceNotes;
e.tuplets().clear();
e.setTrack(staffIdx * VOICES);
m->createStaves(staffIdx);
// tick is obsolete
if (e.hasAttribute("tick")) {
e.setTick(Fraction::fromTicks(score->fileDivision(e.intAttribute("tick"))));
}
bool irregular;
if (e.hasAttribute("len")) {
QStringList sl = e.attribute("len").split('/');
if (sl.size() == 2) {
m->setTicks(Fraction(sl[0].toInt(), sl[1].toInt()));
} else {
qDebug("illegal measure size <%s>", qPrintable(e.attribute("len")));
}
irregular = true;
score->sigmap()->add(m->tick().ticks(), SigEvent(m->ticks(), m->timesig()));
score->sigmap()->add(m->endTick().ticks(), SigEvent(m->timesig()));
} else {
irregular = false;
}
Staff* staff = score->staff(staffIdx);
Fraction timeStretch(staff->timeStretch(m->tick()));
// keep track of tick of previous element
// this allows markings that need to apply to previous element to do so
// even though we may have already advanced to next tick position
Fraction lastTick = e.tick();
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "move") {
e.setTick(e.readFraction() + m->tick());
} else if (tag == "tick") {
e.setTick(Fraction::fromTicks(score->fileDivision(e.readInt())));
lastTick = e.tick();
} else if (tag == "BarLine") {
Fermata* fermataAbove = nullptr;
Fermata* fermataBelow = nullptr;
BarLine* bl = new BarLine(score);
bl->setTrack(e.track());
while (e.readNextStartElement()) {
const QStringRef& t(e.name());
if (t == "subtype") {
bl->setBarLineType(e.readElementText());
} else if (t == "customSubtype") { // obsolete
e.readInt();
} else if (t == "span") {
//TODO bl->setSpanFrom(e.intAttribute("from", bl->spanFrom())); // obsolete
// bl->setSpanTo(e.intAttribute("to", bl->spanTo())); // obsolete
int span = e.readInt();
if (span) {
span--;
}
bl->setSpanStaff(span);
} else if (t == "spanFromOffset") {
bl->setSpanFrom(e.readInt());
} else if (t == "spanToOffset") {
bl->setSpanTo(e.readInt());
} else if (t == "Articulation") {
Element* el = readArticulation(bl, e);
if (el->isFermata()) {
if (el->placement() == Placement::ABOVE) {
fermataAbove = toFermata(el);
} else {
fermataBelow = toFermata(el);
fermataBelow->setTrack((bl->staffIdx() + bl->spanStaff()) * VOICES);
}
} else {
bl->add(el);
}
} else if (!bl->Element::readProperties(e)) {
e.unknown();
}
}
//
// StartRepeatBarLine: always at the beginning tick of a measure, always BarLineType::START_REPEAT
// BarLine: in the middle of a measure, has no semantic
// EndBarLine: at the end tick of a measure
// BeginBarLine: first segment of a measure
SegmentType st;
if ((e.tick() != m->tick()) && (e.tick() != m->endTick())) {
st = SegmentType::BarLine;
} else if (bl->barLineType() == BarLineType::START_REPEAT && e.tick() == m->tick()) {
st = SegmentType::StartRepeatBarLine;
} else if (e.tick() == m->tick() && segment == 0) {
st = SegmentType::BeginBarLine;
} else {
st = SegmentType::EndBarLine;
}
segment = m->getSegment(st, e.tick());
segment->add(bl);
bl->layout();
if (fermataAbove) {
segment->add(fermataAbove);
}
if (fermataBelow) {
segment->add(fermataBelow);
}
} else if (tag == "Chord") {
Chord* chord = new Chord(score);
chord->setTrack(e.track());
segment = m->getSegment(SegmentType::ChordRest, e.tick());
chord->setParent(segment);
readChord(chord, e);
if (chord->noteType() != NoteType::NORMAL) {
graceNotes.push_back(chord);
} else {
segment->add(chord);
for (int i = 0; i < graceNotes.size(); ++i) {
Chord* gc = graceNotes[i];
gc->setGraceIndex(i);
chord->add(gc);
}
graceNotes.clear();
Fraction crticks = chord->actualTicks();
lastTick = e.tick();
e.incTick(crticks);
}
} else if (tag == "Rest") {
Rest* rest = new Rest(score);
rest->setDurationType(TDuration::DurationType::V_MEASURE);
rest->setTicks(m->timesig() / timeStretch);
rest->setTrack(e.track());
segment = m->getSegment(SegmentType::ChordRest, e.tick());
rest->setParent(segment);
readRest(rest, e);
segment->add(rest);
if (!rest->ticks().isValid()) { // hack
rest->setTicks(m->timesig() / timeStretch);
}
lastTick = e.tick();
e.incTick(rest->actualTicks());
} else if (tag == "Breath") {
Breath* breath = new Breath(score);
breath->setTrack(e.track());
Fraction tick = e.tick();
breath->read(e);
// older scores placed the breath segment right after the chord to which it applies
// rather than before the next chordrest segment with an element for the staff
// result would be layout too far left if there are other segments due to notes in other staves
// we need to find tick of chord to which this applies, and add its duration
Fraction prevTick;
if (e.tick() < tick) {
prevTick = e.tick(); // use our own tick if we explicitly reset to earlier position
} else {
prevTick = lastTick; // otherwise use tick of previous tick/chord/rest tag
}
// find segment
Segment* prev = m->findSegment(SegmentType::ChordRest, prevTick);
if (prev) {
// find chordrest
ChordRest* lastCR = toChordRest(prev->element(e.track()));
if (lastCR) {
tick = prevTick + lastCR->actualTicks();
}
}
segment = m->getSegment(SegmentType::Breath, tick);
segment->add(breath);
} else if (tag == "endSpanner") {
int id = e.attribute("id").toInt();
Spanner* spanner = e.findSpanner(id);
if (spanner) {
spanner->setTicks(e.tick() - spanner->tick());
// if (spanner->track2() == -1)
// the absence of a track tag [?] means the
// track is the same as the beginning of the slur
if (spanner->track2() == -1) {
spanner->setTrack2(spanner->track() ? spanner->track() : e.track());
}
} else {
// remember "endSpanner" values
SpannerValues sv;
sv.spannerId = id;
sv.track2 = e.track();
sv.tick2 = e.tick();
e.addSpannerValues(sv);
}
e.readNext();
} else if (tag == "Slur") {
Slur* sl = new Slur(score);
sl->setTick(e.tick());
readSlur206(e, sl);
//
// check if we already saw "endSpanner"
//
int id = e.spannerId(sl);
const SpannerValues* sv = e.spannerValues(id);
if (sv) {
sl->setTick2(sv->tick2);
sl->setTrack2(sv->track2);
}
score->addSpanner(sl);
} else if (tag == "HairPin"
|| tag == "Pedal"
|| tag == "Ottava"
|| tag == "Trill"
|| tag == "TextLine"
|| tag == "Volta") {
Spanner* sp = toSpanner(Element::name2Element(tag, score));
sp->setTrack(e.track());
sp->setTick(e.tick());
sp->eraseSpannerSegments();
e.addSpanner(e.intAttribute("id", -1), sp);
if (tag == "Volta") {
readVolta206(e, toVolta(sp));
} else if (tag == "Pedal") {
readPedal(e, toPedal(sp));
} else if (tag == "Ottava") {
readOttava(e, toOttava(sp));
} else if (tag == "HairPin") {
readHairpin206(e, toHairpin(sp));
} else if (tag == "Trill") {
readTrill206(e, toTrill(sp));
} else {
readTextLine206(e, toTextLineBase(sp));
}
score->addSpanner(sp);
//
// check if we already saw "endSpanner"
//
int id = e.spannerId(sp);
const SpannerValues* sv = e.spannerValues(id);
if (sv) {
sp->setTicks(sv->tick2 - sp->tick());
sp->setTrack2(sv->track2);
}
} else if (tag == "RepeatMeasure") {
RepeatMeasure* rm = new RepeatMeasure(score);
rm->setTrack(e.track());
readRest(rm, e);
segment = m->getSegment(SegmentType::ChordRest, e.tick());
segment->add(rm);
lastTick = e.tick();
e.incTick(m->ticks());
} else if (tag == "Clef") {
Clef* clef = new Clef(score);
clef->setTrack(e.track());
clef->read(e);
clef->setGenerated(false);
if (e.tick().isZero()) {
if (score->staff(staffIdx)->clef(Fraction(0,1)) != clef->clefType()) {
score->staff(staffIdx)->setDefaultClefType(clef->clefType());
}
if (clef->links() && clef->links()->size() == 1) {
e.linkIds().remove(clef->links()->lid());
qDebug("remove link %d", clef->links()->lid());
}
delete clef;
continue;
}
// there may be more than one clef segment for same tick position
if (!segment) {
// this is the first segment of measure
segment = m->getSegment(SegmentType::Clef, e.tick());
} else {
bool firstSegment = false;
// the first clef may be missing and is added later in layout
for (Segment* s = m->segments().first(); s && s->tick() == e.tick(); s = s->next()) {
if (s->segmentType() == SegmentType::Clef
// hack: there may be other segment types which should
// generate a clef at current position
|| s->segmentType() == SegmentType::StartRepeatBarLine
) {
firstSegment = true;
break;
}
}
if (firstSegment) {
Segment* ns = 0;
if (segment->next()) {
ns = segment->next();
while (ns && ns->tick() < e.tick()) {
ns = ns->next();
}
}
segment = 0;
for (Segment* s = ns; s && s->tick() == e.tick(); s = s->next()) {
if (s->segmentType() == SegmentType::Clef) {
segment = s;
break;
}
}
if (!segment) {
segment = new Segment(m, SegmentType::Clef, e.tick() - m->tick());
m->segments().insert(segment, ns);
}
} else {
// this is the first clef: move to left
segment = m->getSegment(SegmentType::Clef, e.tick());
}
}
if (e.tick() != m->tick()) {
clef->setSmall(true); // TODO: layout does this ?
}
segment->add(clef);
} else if (tag == "TimeSig") {
TimeSig* ts = new TimeSig(score);
ts->setTrack(e.track());
ts->read(e);
// if time sig not at beginning of measure => courtesy time sig
Fraction currTick = e.tick();
bool courtesySig = (currTick > m->tick());
if (courtesySig) {
// if courtesy sig., just add it without map processing
segment = m->getSegment(SegmentType::TimeSigAnnounce, currTick);
segment->add(ts);
} else {
// if 'real' time sig., do full process
segment = m->getSegment(SegmentType::TimeSig, currTick);
segment->add(ts);
timeStretch = ts->stretch().reduced();
m->setTimesig(ts->sig() / timeStretch);
if (irregular) {
score->sigmap()->add(m->tick().ticks(), SigEvent(m->ticks(), m->timesig()));
score->sigmap()->add(m->endTick().ticks(), SigEvent(m->timesig()));
} else {
m->setTicks(m->timesig());
score->sigmap()->add(m->tick().ticks(), SigEvent(m->timesig()));
}
}
} else if (tag == "KeySig") {
KeySig* ks = new KeySig(score);
ks->setTrack(e.track());
ks->read(e);
Fraction curTick = e.tick();
if (!ks->isCustom() && !ks->isAtonal() && ks->key() == Key::C && curTick.isZero()) {
// ignore empty key signature
qDebug("remove keysig c at tick 0");
if (ks->links()) {
if (ks->links()->size() == 1) {
e.linkIds().remove(ks->links()->lid());
}
}
delete ks;
} else {
// if key sig not at beginning of measure => courtesy key sig
bool courtesySig = (curTick == m->endTick());
segment = m->getSegment(courtesySig ? SegmentType::KeySigAnnounce : SegmentType::KeySig, curTick);
segment->add(ks);
if (!courtesySig) {
staff->setKey(curTick, ks->keySigEvent());
}
}
} else if (tag == "Text" || tag == "StaffText") {
// MuseScore 3 has different types for system text and
// staff text while MuseScore 2 didn't.
// We need to decide first which one we should create.
TextReaderContext206 ctx(e);
QString styleName = ReadStyleName206(ctx.tag());
StaffTextBase* t;
if (styleName == "System" || styleName == "Tempo"
|| styleName == "Marker" || styleName == "Jump"
|| styleName == "Volta") { // TODO: is it possible to get it from style?
t = new SystemText(score);
} else {
t = new StaffText(score);
}
t->setTrack(e.track());
readTextPropertyStyle206(ctx.tag(), e, t, t);
while (ctx.reader().readNextStartElement()) {
if (!readTextProperties206(ctx.reader(), t)) {
ctx.reader().unknown();
}
}
if (t->empty()) {
if (t->links()) {
if (t->links()->size() == 1) {
qDebug("reading empty text: deleted lid = %d", t->links()->lid());
ctx.reader().linkIds().remove(t->links()->lid());
delete t;
}
}
} else {
#if 0
// This code was added at commit ed5b615
// but it seems to no longer be appropriate.
// autoplace is usually true,
// exception is text within staff,
// and in this case offset is already correct without further adjustment.
if (!t->autoplace()) {
// adjust position
qreal userY = t->offset().y() / t->spatium();
qreal yo = -(-2.0 - userY) * t->spatium();
t->layout();
t->setAlign(Align::LEFT | Align::TOP);
t->ryoffset() = yo;
}
#endif
segment = m->getSegment(SegmentType::ChordRest, ctx.reader().tick());
segment->add(t);
}
}
//----------------------------------------------------
// Annotation
else if (tag == "Dynamic") {
Dynamic* dyn = new Dynamic(score);
dyn->setTrack(e.track());
readDynamic(dyn, e);
segment = m->getSegment(SegmentType::ChordRest, e.tick());
segment->add(dyn);
} else if (tag == "RehearsalMark") {
RehearsalMark* el = new RehearsalMark(score);
el->setTrack(e.track());
readText206(e, el, el);
// el->setOffset(el->offset() - el->score()->styleValue(Pid::OFFSET, Sid::rehearsalMarkPosAbove).toPointF());
// if (el->offset().isNull())
// el->setAutoplace(true);
segment = m->getSegment(SegmentType::ChordRest, e.tick());
segment->add(el);
}
#if 0
else if (tag == "StaffText") {
StaffText* el = new StaffText(score);
el->setTrack(e.track());
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "foregroundColor") {
e.skipCurrentElement();
} else if (!el->readProperties(e)) {
e.unknown();
}
}
TextBase* tt = static_cast<TextBase*>(el);
tt->setXmlText(tt->xmlText().replace("<sym>unicode", "<sym>met"));
segment = m->getSegment(SegmentType::ChordRest, e.tick());
segment->add(el);
}
#endif
else if (tag == "Harmony"
|| tag == "FretDiagram"
|| tag == "TremoloBar"
|| tag == "Symbol"
|| tag == "InstrumentChange"
|| tag == "StaffState"
|| tag == "FiguredBass"
) {
Element* el = Element::name2Element(tag, score);
// hack - needed because tick tags are unreliable in 1.3 scores
// for symbols attached to anything but a measure
el->setTrack(e.track());
el->read(e);
if (el->staff() && (el->isHarmony() || el->isFretDiagram() || el->isInstrumentChange())) {
adjustPlacement(el);
}
segment = m->getSegment(SegmentType::ChordRest, e.tick());
segment->add(el);
} else if (tag == "Tempo") {
TempoText* tt = new TempoText(score);
// hack - needed because tick tags are unreliable in 1.3 scores
// for symbols attached to anything but a measure
tt->setTrack(e.track());
readTempoText(tt, e);
segment = m->getSegment(SegmentType::ChordRest, e.tick());
segment->add(tt);
} else if (tag == "Marker" || tag == "Jump") {
Element* el = Element::name2Element(tag, score);
el->setTrack(e.track());
if (tag == "Marker") {
Marker* ma = toMarker(el);
readMarker(ma, e);
Element* markerEl = toElement(ma);
m->add(markerEl);
} else {
el->read(e);
m->add(el);
}
} else if (tag == "Image") {
if (MScore::noImages) {
e.skipCurrentElement();
} else {
Element* el = Element::name2Element(tag, score);
el->setTrack(e.track());
el->read(e);
m->add(el);
}
}
//----------------------------------------------------
else if (tag == "stretch") {
double val = e.readDouble();
if (val < 0.0) {
val = 0;
}
m->setUserStretch(val);
} else if (tag == "noOffset") {
m->setNoOffset(e.readInt());
} else if (tag == "measureNumberMode") {
m->setMeasureNumberMode(MeasureNumberMode(e.readInt()));
} else if (tag == "irregular") {
m->setIrregular(e.readBool());
} else if (tag == "breakMultiMeasureRest") {
m->setBreakMultiMeasureRest(e.readBool());
} else if (tag == "sysInitBarLineType") {
const QString& val(e.readElementText());
BarLine* barLine = new BarLine(score);
barLine->setTrack(e.track());
barLine->setBarLineType(val);
segment = m->getSegment(SegmentType::BeginBarLine, m->tick());
segment->add(barLine);
} else if (tag == "Tuplet") {
Tuplet* tuplet = new Tuplet(score);
tuplet->setTrack(e.track());
tuplet->setTick(e.tick());
tuplet->setParent(m);
readTuplet(tuplet, e);
e.addTuplet(tuplet);
} else if (tag == "startRepeat") {
m->setRepeatStart(true);
e.readNext();
} else if (tag == "endRepeat") {
m->setRepeatCount(e.readInt());
m->setRepeatEnd(true);
} else if (tag == "vspacer" || tag == "vspacerDown") {
if (!m->vspacerDown(staffIdx)) {
Spacer* spacer = new Spacer(score);
spacer->setSpacerType(SpacerType::DOWN);
spacer->setTrack(staffIdx * VOICES);
m->add(spacer);
}
m->vspacerDown(staffIdx)->setGap(e.readDouble() * _spatium);
} else if (tag == "vspacer" || tag == "vspacerUp") {
if (!m->vspacerUp(staffIdx)) {
Spacer* spacer = new Spacer(score);
spacer->setSpacerType(SpacerType::UP);
spacer->setTrack(staffIdx * VOICES);
m->add(spacer);
}
m->vspacerUp(staffIdx)->setGap(e.readDouble() * _spatium);
} else if (tag == "visible") {
m->setStaffVisible(staffIdx, e.readInt());
} else if (tag == "slashStyle") {
m->setStaffStemless(staffIdx, e.readInt());
} else if (tag == "Beam") {
Beam* beam = new Beam(score);
beam->setTrack(e.track());
beam->read(e);
beam->setParent(0);
e.addBeam(beam);
} else if (tag == "Segment") {
if (segment) {
segment->read(e);
} else {
e.unknown();
}
} else if (tag == "MeasureNumber") {
MeasureNumber* noText = new MeasureNumber(score);
readText206(e, noText, m);
noText->setTrack(e.track());
noText->setParent(m);
m->setNoText(noText->staffIdx(), noText);
} else if (tag == "SystemDivider") {
SystemDivider* sd = new SystemDivider(score);
sd->read(e);
m->add(sd);
} else if (tag == "Ambitus") {
Ambitus* range = new Ambitus(score);
readAmbitus(range, e);
segment = m->getSegment(SegmentType::Ambitus, e.tick());
range->setParent(segment); // a parent segment is needed for setTrack() to work
range->setTrack(trackZeroVoice(e.track()));
segment->add(range);
} else if (tag == "multiMeasureRest") {
m->setMMRestCount(e.readInt());
// set tick to previous measure
m->setTick(e.lastMeasure()->tick());
e.setTick(e.lastMeasure()->tick());
} else if (m->MeasureBase::readProperties(e)) {
} else {
e.unknown();
}
}
e.checkTuplets();
m->connectTremolo();
}
//---------------------------------------------------------
// readBox
//---------------------------------------------------------
static void readBox(Box* b, XmlReader& e)
{
b->setLeftMargin(0.0);
b->setRightMargin(0.0);
b->setTopMargin(0.0);
b->setBottomMargin(0.0);
b->setTopGap(0.0);
b->setBottomGap(0.0);
b->setPropertyFlags(Pid::TOP_GAP, PropertyFlags::UNSTYLED);
b->setPropertyFlags(Pid::BOTTOM_GAP, PropertyFlags::UNSTYLED);
b->setBoxHeight(Spatium(0)); // override default set in constructor
b->setBoxWidth(Spatium(0));
bool keepMargins = false; // whether original margins have to be kept when reading old file
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "HBox") {
HBox* hb = new HBox(b->score());
hb->read(e);
b->add(hb);
keepMargins = true; // in old file, box nesting used outer box margins
} else if (tag == "VBox") {
VBox* vb = new VBox(b->score());
vb->read(e);
b->add(vb);
keepMargins = true; // in old file, box nesting used outer box margins
} else if (tag == "Text") {
Text* t;
if (b->isTBox()) {
t = toTBox(b)->text();
readText206(e, t, t);
} else {
t = new Text(b->score());
readText206(e, t, t);
if (t->empty()) {
qDebug("read empty text");
} else {
b->add(t);
}
}
} else if (!b->readProperties(e)) {
e.unknown();
}
}
// with .msc versions prior to 1.17, box margins were only used when nesting another box inside this box:
// for backward compatibility set them to 0 in all other cases
if (b->score()->mscVersion() <= 114 && (b->isHBox() || b->isVBox()) && !keepMargins) {
b->setLeftMargin(0.0);
b->setRightMargin(0.0);
b->setTopMargin(0.0);
b->setBottomMargin(0.0);
}
}
//---------------------------------------------------------
// readStaffContent
//---------------------------------------------------------
static void readStaffContent(Score* score, XmlReader& e)
{
int staff = e.intAttribute("id", 1) - 1;
e.setTick(Fraction(0,1));
e.setTrack(staff * VOICES);
Box* lastReadBox = nullptr;
bool readMeasureLast = false;
if (staff == 0) {
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "Measure") {
if (lastReadBox) {
lastReadBox->setBottomGap(lastReadBox->bottomGap() + lastReadBox->propertyDefault(
Pid::BOTTOM_GAP).toReal());
lastReadBox = nullptr;
}
readMeasureLast = true;
Measure* measure = 0;
measure = new Measure(score);
measure->setTick(e.tick());
//
// inherit timesig from previous measure
//
Measure* m = e.lastMeasure(); // measure->prevMeasure();
Fraction f(m ? m->timesig() : Fraction(4,4));
measure->setTicks(f);
measure->setTimesig(f);
readMeasure(measure, staff, e);
measure->checkMeasure(staff);
if (!measure->isMMRest()) {
score->measures()->add(measure);
e.setLastMeasure(measure);
e.setTick(measure->endTick());
} else {
// this is a multi measure rest
// always preceded by the first measure it replaces
Measure* lm = e.lastMeasure();
if (lm) {
lm->setMMRest(measure);
measure->setTick(lm->tick());
}
}
} else if (tag == "HBox" || tag == "VBox" || tag == "TBox" || tag == "FBox") {
Box* b = toBox(Element::name2Element(tag, score));
readBox(b, e);
b->setTick(e.tick());
score->measures()->add(b);
// If it's the first box, and comes before any measures, reset to
// 301 default.
if (!readMeasureLast && !lastReadBox) {
b->setTopGap(b->propertyDefault(Pid::TOP_GAP).toReal());
b->setPropertyFlags(Pid::TOP_GAP, PropertyFlags::STYLED);
} else if (readMeasureLast) {
b->setTopGap(b->topGap() + b->propertyDefault(Pid::TOP_GAP).toReal());
}
lastReadBox = b;
readMeasureLast = false;
} else if (tag == "tick") {
e.setTick(Fraction::fromTicks(score->fileDivision(e.readInt())));
} else {
e.unknown();
}
}
} else {
Measure* measure = score->firstMeasure();
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "Measure") {
if (measure == 0) {
qDebug("Score::readStaff(): missing measure!");
measure = new Measure(score);
measure->setTick(e.tick());
score->measures()->add(measure);
}
e.setTick(measure->tick());
readMeasure(measure, staff, e);
measure->checkMeasure(staff);
if (measure->isMMRest()) {
measure = e.lastMeasure()->nextMeasure();
} else {
e.setLastMeasure(measure);
if (measure->mmRest()) {
measure = measure->mmRest();
} else {
measure = measure->nextMeasure();
}
}
} else if (tag == "tick") {
e.setTick(Fraction::fromTicks(score->fileDivision(e.readInt())));
} else {
e.unknown();
}
}
}
}
//---------------------------------------------------------
// readStyle
//---------------------------------------------------------
static void readStyle(MStyle* style, XmlReader& e)
{
QString oldChordDescriptionFile = style->value(Sid::chordDescriptionFile).toString();
bool chordListTag = false;
excessTextStyles206.clear();
while (e.readNextStartElement()) {
QString tag = e.name().toString();
if (tag == "TextStyle") {
readTextStyle206(style, e, excessTextStyles206);
} else if (tag == "Spatium") {
style->set(Sid::spatium, e.readDouble() * DPMM);
} else if (tag == "page-layout") {
readPageFormat(style, e);
} else if (tag == "displayInConcertPitch") {
style->set(Sid::concertPitch, QVariant(bool(e.readInt())));
} else if (tag == "pedalY") {
qreal y = e.readDouble();
style->set(Sid::pedalPosBelow, QPointF(0.0, y));
} else if (tag == "lyricsDistance") {
qreal y = e.readDouble();
style->set(Sid::lyricsPosBelow, QPointF(0.0, y));
} else if (tag == "lyricsMinBottomDistance") {
// no longer meaningful since it is now measured from skyline rather than staff
//style->set(Sid::lyricsMinBottomDistance, QPointF(0.0, y));
e.skipCurrentElement();
} else if (tag == "ottavaHook") {
qreal y = qAbs(e.readDouble());
style->set(Sid::ottavaHookAbove, y);
style->set(Sid::ottavaHookBelow, -y);
} else if (tag == "endBarDistance") {
double d = e.readDouble();
d += style->value(Sid::barWidth).toDouble();
d += style->value(Sid::endBarWidth).toDouble();
style->set(Sid::endBarDistance, QVariant(d));
} else if (tag == "ChordList") {
style->chordList()->clear();
style->chordList()->read(e);
style->setCustomChordList(true);
for (ChordFont f : style->chordList()->fonts) {
if (f.family == "MuseJazz") {
f.family = "MuseJazz Text";
}
}
chordListTag = true;
} else if (tag == "harmonyY") {
qreal val = -e.readDouble();
if (val > 0.0) {
style->set(Sid::harmonyPlacement, int(Placement::BELOW));
style->set(Sid::chordSymbolAPosBelow, QPointF(.0, val));
} else {
style->set(Sid::harmonyPlacement, int(Placement::ABOVE));
style->set(Sid::chordSymbolAPosBelow, QPointF(.0, val));
}
} else {
if (!style->readProperties(e)) {
e.skipCurrentElement();
}
}
}
// if we just specified a new chord description file
// and didn't encounter a ChordList tag
// then load the chord description file
QString newChordDescriptionFile = style->value(Sid::chordDescriptionFile).toString();
if (newChordDescriptionFile != oldChordDescriptionFile && !chordListTag) {
if (!newChordDescriptionFile.startsWith("chords_") && style->value(Sid::chordStyle).toString() == "std") {
// should not normally happen,
// but treat as "old" (114) score just in case
style->set(Sid::chordStyle, QVariant(QString("custom")));
style->set(Sid::chordsXmlFile, QVariant(true));
qDebug("StyleData::load: custom chord description file %s with chordStyle == std",
qPrintable(newChordDescriptionFile));
}
if (style->value(Sid::chordStyle).toString() == "custom") {
style->setCustomChordList(true);
} else {
style->setCustomChordList(false);
}
style->chordList()->unload();
}
// make sure we have a chordlist
if (!chordListTag) {
style->checkChordList();
}
}
//---------------------------------------------------------
// readScore
//---------------------------------------------------------
static bool readScore(Score* score, XmlReader& e)
{
while (e.readNextStartElement()) {
e.setTrack(-1);
const QStringRef& tag(e.name());
if (tag == "Staff") {
readStaffContent(score, e);
} else if (tag == "siglist") {
score->sigmap()->read(e, score->fileDivision());
} else if (tag == "Omr") {
#ifdef OMR
score->masterScore()->setOmr(new Omr(score));
score->masterScore()->omr()->read(e);
#else
e.skipCurrentElement();
#endif
} else if (tag == "Audio") {
score->setAudio(new Audio);
score->audio()->read(e);
} else if (tag == "showOmr") {
score->masterScore()->setShowOmr(e.readInt());
} else if (tag == "playMode") {
score->setPlayMode(PlayMode(e.readInt()));
} else if (tag == "LayerTag") {
int id = e.intAttribute("id");
const QString& t = e.attribute("tag");
QString val(e.readElementText());
if (id >= 0 && id < 32) {
score->layerTags()[id] = t;
score->layerTagComments()[id] = val;
}
} else if (tag == "Layer") {
Layer layer;
layer.name = e.attribute("name");
layer.tags = e.attribute("mask").toUInt();
score->layer().append(layer);
e.readNext();
} else if (tag == "currentLayer") {
score->setCurrentLayer(e.readInt());
} else if (tag == "Synthesizer") {
score->synthesizerState().read(e);
} else if (tag == "page-offset") {
score->setPageNumberOffset(e.readInt());
} else if (tag == "Division") {
score->setFileDivision(e.readInt());
} else if (tag == "showInvisible") {
score->setShowInvisible(e.readInt());
} else if (tag == "showUnprintable") {
score->setShowUnprintable(e.readInt());
} else if (tag == "showFrames") {
score->setShowFrames(e.readInt());
} else if (tag == "showMargins") {
score->setShowPageborders(e.readInt());
} else if (tag == "Style") {
qreal sp = score->style().value(Sid::spatium).toDouble();
readStyle(&score->style(), e);
if (score->style().value(Sid::MusicalTextFont).toString() == "MuseJazz") {
score->style().set(Sid::MusicalTextFont, "MuseJazz Text");
}
// if (_layoutMode == LayoutMode::FLOAT || _layoutMode == LayoutMode::SYSTEM) {
if (score->layoutMode() == LayoutMode::FLOAT) {
// style should not change spatium in
// float mode
score->style().set(Sid::spatium, sp);
}
score->setScoreFont(ScoreFont::fontFactory(score->style().value(Sid::MusicalSymbolFont).toString()));
} else if (tag == "copyright" || tag == "rights") {
Text* text = new Text(score);
readText206(e, text, text);
score->setMetaTag("copyright", text->xmlText());
delete text;
} else if (tag == "movement-number") {
score->setMetaTag("movementNumber", e.readElementText());
} else if (tag == "movement-title") {
score->setMetaTag("movementTitle", e.readElementText());
} else if (tag == "work-number") {
score->setMetaTag("workNumber", e.readElementText());
} else if (tag == "work-title") {
score->setMetaTag("workTitle", e.readElementText());
} else if (tag == "source") {
score->setMetaTag("source", e.readElementText());
} else if (tag == "metaTag") {
QString name = e.attribute("name");
score->setMetaTag(name, e.readElementText());
} else if (tag == "Part") {
Part* part = new Part(score);
readPart206(part, e);
score->parts().push_back(part);
} else if ((tag == "HairPin") // TODO: do this elements exist here?
|| (tag == "Ottava")
|| (tag == "TextLine")
|| (tag == "Volta")
|| (tag == "Trill")
|| (tag == "Slur")
|| (tag == "Pedal")) {
Spanner* s = toSpanner(Element::name2Element(tag, score));
if (tag == "HairPin") {
readHairpin206(e, toHairpin(s));
} else if (tag == "Ottava") {
readOttava(e, toOttava(s));
} else if (tag == "TextLine") {
readTextLine206(e, toTextLine(s));
} else if (tag == "Volta") {
readVolta206(e, toVolta(s));
} else if (tag == "Trill") {
readTrill206(e, toTrill(s));
} else if (tag == "Slur") {
readSlur206(e, toSlur(s));
} else {
Q_ASSERT(tag == "Pedal");
readPedal(e, toPedal(s));
}
score->addSpanner(s);
} else if (tag == "Excerpt") {
if (MScore::noExcerpts) {
e.skipCurrentElement();
} else {
if (score->isMaster()) {
Excerpt* ex = new Excerpt(static_cast<MasterScore*>(score));
ex->read(e);
score->excerpts().append(ex);
} else {
qDebug("read206: readScore(): part cannot have parts");
e.skipCurrentElement();
}
}
} else if (tag == "Score") { // recursion
if (MScore::noExcerpts) {
e.skipCurrentElement();
} else {
e.tracks().clear();
MasterScore* m = score->masterScore();
Score* s = new Score(m, MScore::baseStyle());
Excerpt* ex = new Excerpt(m);
ex->setPartScore(s);
e.setLastMeasure(nullptr);
readScore(s, e);
ex->setTracks(e.tracks());
m->addExcerpt(ex);
}
} else if (tag == "PageList") {
e.skipCurrentElement();
} else if (tag == "name") {
QString n = e.readElementText();
if (!score->isMaster()) { //ignore the name if it's not a child score
score->excerpt()->setTitle(n);
}
} else if (tag == "layoutMode") {
QString s = e.readElementText();
if (s == "line") {
score->setLayoutMode(LayoutMode::LINE);
} else if (s == "system") {
score->setLayoutMode(LayoutMode::SYSTEM);
} else {
qDebug("layoutMode: %s", qPrintable(s));
}
} else {
e.unknown();
}
}
if (e.error() != QXmlStreamReader::NoError) {
qDebug("%s: xml read error at line %lld col %lld: %s",
qPrintable(e.getDocName()), e.lineNumber(), e.columnNumber(),
e.name().toUtf8().data());
MScore::lastError = QObject::tr("XML read error at line %1, column %2: %3").arg(e.lineNumber()).arg(
e.columnNumber()).arg(e.name().toString());
return false;
}
score->connectTies();
score->setFileDivision(MScore::division);
//
// sanity check for barLineSpan
//
#if 0 // TODO:barline
for (Staff* st : score->staves()) {
int barLineSpan = st->barLineSpan();
int idx = st->idx();
int n = score->nstaves();
if (idx + barLineSpan > n) {
qDebug("bad span: idx %d span %d staves %d", idx, barLineSpan, n);
// span until last staff
barLineSpan = n - idx;
st->setBarLineSpan(barLineSpan);
} else if (idx == 0 && barLineSpan == 0) {
qDebug("bad span: idx %d span %d staves %d", idx, barLineSpan, n);
// span from the first staff until the start of the next span
barLineSpan = 1;
for (int i = 1; i < n; ++i) {
if (score->staff(i)->barLineSpan() == 0) {
++barLineSpan;
} else {
break;
}
}
st->setBarLineSpan(barLineSpan);
}
// check spanFrom
int minBarLineFrom = st->lines(0) == 1 ? BARLINE_SPAN_1LINESTAFF_FROM : MIN_BARLINE_SPAN_FROMTO;
if (st->barLineFrom() < minBarLineFrom) {
st->setBarLineFrom(minBarLineFrom);
}
if (st->barLineFrom() > st->lines(0) * 2) {
st->setBarLineFrom(st->lines(0) * 2);
}
// check spanTo
Staff* stTo = st->barLineSpan() <= 1 ? st : score->staff(idx + st->barLineSpan() - 1);
// 1-line staves have special bar line spans
int maxBarLineTo = stTo->lines(0) == 1 ? BARLINE_SPAN_1LINESTAFF_TO : stTo->lines(0) * 2;
int defaultBarLineTo = stTo->lines(0) == 1 ? BARLINE_SPAN_1LINESTAFF_TO : (stTo->lines(0) - 1) * 2;
if (st->barLineTo() == UNKNOWN_BARLINE_TO) {
st->setBarLineTo(defaultBarLineTo);
}
if (st->barLineTo() < MIN_BARLINE_SPAN_FROMTO) {
st->setBarLineTo(MIN_BARLINE_SPAN_FROMTO);
}
if (st->barLineTo() > maxBarLineTo) {
st->setBarLineTo(maxBarLineTo);
}
// on single staff span, check spanFrom and spanTo are distant enough
if (st->barLineSpan() == 1) {
if (st->barLineTo() - st->barLineFrom() < MIN_BARLINE_FROMTO_DIST) {
st->setBarLineFrom(0);
st->setBarLineTo(defaultBarLineTo);
}
}
}
#endif
score->fixTicks();
for (Part* p : score->parts()) {
p->updateHarmonyChannels(false);
}
if (score->isMaster()) {
MasterScore* ms = static_cast<MasterScore*>(score);
if (!ms->omr()) {
ms->setShowOmr(false);
}
ms->rebuildMidiMapping();
ms->updateChannel();
// ms->createPlayEvents();
}
return true;
}
//---------------------------------------------------------
// read
// <page-layout>
// <page-height>
// <page-width>
// <landscape>1</landscape>
// <page-margins type="both">
// <left-margin>28.3465</left-margin>
// <right-margin>28.3465</right-margin>
// <top-margin>28.3465</top-margin>
// <bottom-margin>56.6929</bottom-margin>
// </page-margins>
// </page-layout>
//---------------------------------------------------------
void PageFormat::read(XmlReader& e)
{
qreal _oddRightMargin = 0.0;
qreal _evenRightMargin = 0.0;
QString type;
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "page-margins") {
type = e.attribute("type","both");
qreal lm = 0.0, rm = 0.0, tm = 0.0, bm = 0.0;
while (e.readNextStartElement()) {
const QStringRef& t(e.name());
qreal val = e.readDouble() * 0.5 / PPI;
if (t == "left-margin") {
lm = val;
} else if (t == "right-margin") {
rm = val;
} else if (t == "top-margin") {
tm = val;
} else if (t == "bottom-margin") {
bm = val;
} else {
e.unknown();
}
}
_twosided = type == "odd" || type == "even";
if (type == "odd" || type == "both") {
_oddLeftMargin = lm;
_oddRightMargin = rm;
_oddTopMargin = tm;
_oddBottomMargin = bm;
}
if (type == "even" || type == "both") {
_evenLeftMargin = lm;
_evenRightMargin = rm;
_evenTopMargin = tm;
_evenBottomMargin = bm;
}
} else if (tag == "page-height") {
_size.rheight() = e.readDouble() * 0.5 / PPI;
} else if (tag == "page-width") {
_size.rwidth() = e.readDouble() * .5 / PPI;
} else {
e.unknown();
}
}
qreal w1 = _size.width() - _oddLeftMargin - _oddRightMargin;
qreal w2 = _size.width() - _evenLeftMargin - _evenRightMargin;
_printableWidth = qMin(w1, w2); // silently adjust right margins
}
//---------------------------------------------------------
// read206
// import old version > 1.3 and < 3.x files
//---------------------------------------------------------
Score::FileError MasterScore::read206(XmlReader& e)
{
for (unsigned int i = 0; i < sizeof(style206) / sizeof(*style206); ++i) {
style().set(style206[i].idx, style206[i].val);
}
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "programVersion") {
setMscoreVersion(e.readElementText());
parseVersion(mscoreVersion());
} else if (tag == "programRevision") {
setMscoreRevision(e.readIntHex());
} else if (tag == "Score") {
if (!readScore(this, e)) {
return FileError::FILE_BAD_FORMAT;
}
} else if (tag == "Revision") {
Revision* revision = new Revision;
revision->read(e);
revisions()->add(revision);
}
}
int id = 1;
for (LinkedElements* le : e.linkIds()) {
le->setLid(this, id++);
}
for (Staff* s : staves()) {
s->updateOttava();
}
// fix segment span
SegmentType st = SegmentType::BarLineType;
for (Segment* s = firstSegment(st); s; s = s->next1(st)) {
for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
BarLine* b = toBarLine(s->element(staffIdx * VOICES));
if (!b) {
continue;
}
int sp = b->spanStaff();
if (sp <= 0) {
continue;
}
for (int span = 1; span <= sp; ++span) {
BarLine* nb = toBarLine(s->element((staffIdx + span) * VOICES));
if (!nb) {
nb = b->clone();
nb->setTrack((staffIdx + span) * VOICES);
s->add(nb);
}
nb->setSpanStaff(sp - span);
}
staffIdx += sp;
}
}
for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
Staff* s = staff(staffIdx);
int sp = s->barLineSpan();
if (sp <= 0) {
continue;
}
for (int span = 1; span <= sp; ++span) {
Staff* ns = staff(staffIdx + span);
ns->setBarLineSpan(sp - span);
}
staffIdx += sp;
}
// fix positions
// offset = saved offset - layout position
doLayout();
for (auto i : e.fixOffsets()) {
i.first->setOffset(i.second - i.first->pos());
}
// treat reading a 2.06 file as import
// on save warn if old file will be overwritten
setCreated(true);
// don't autosave (as long as there's no change to the score)
setAutosaveDirty(false);
return FileError::FILE_NO_ERROR;
}
}