Read and write spanners using the connectors framework
Remove IDs for spanners
This commit is contained in:
parent
2c28195c1c
commit
104898195b
24 changed files with 344 additions and 282 deletions
|
@ -1052,27 +1052,6 @@ bool Chord::readProperties(XmlReader& e)
|
|||
_arpeggio->read(e);
|
||||
_arpeggio->setParent(this);
|
||||
}
|
||||
// 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 = upNote();
|
||||
Glissando* gliss = new Glissando(score());
|
||||
gliss->read(e);
|
||||
gliss->setAnchor(Spanner::Anchor::NOTE);
|
||||
gliss->setStartElement(nullptr);
|
||||
gliss->setEndElement(nullptr);
|
||||
// in TAB, use straight line with no text
|
||||
if (score()->staff(e.track() >> 2)->isTabStaff(tick())) {
|
||||
gliss->setGlissandoType(GlissandoType::STRAIGHT);
|
||||
gliss->setShowText(false);
|
||||
}
|
||||
finalNote->addSpannerBack(gliss);
|
||||
}
|
||||
else if (tag == "Tremolo") {
|
||||
_tremolo = new Tremolo(score());
|
||||
_tremolo->setTrack(track());
|
||||
|
|
|
@ -191,14 +191,10 @@ void ChordRest::writeProperties(XmlWriter& xml) const
|
|||
if (s->generated() || !s->isSlur() || toSlur(s)->broken() || !xml.canWrite(s))
|
||||
continue;
|
||||
|
||||
if (s->startElement() == this) {
|
||||
int id = xml.spannerId(s);
|
||||
xml.tagE(QString("Slur type=\"start\" id=\"%1\"").arg(id));
|
||||
}
|
||||
else if (s->endElement() == this) {
|
||||
int id = xml.spannerId(s);
|
||||
xml.tagE(QString("Slur type=\"stop\" id=\"%1\"").arg(id));
|
||||
}
|
||||
if (s->startElement() == this)
|
||||
s->writeSpannerStart(xml, this, track());
|
||||
else if (s->endElement() == this)
|
||||
s->writeSpannerEnd(xml, this, track());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -299,33 +295,44 @@ bool ChordRest::readProperties(XmlReader& e)
|
|||
setDots(e.readInt());
|
||||
else if (tag == "move")
|
||||
_staffMove = 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"));
|
||||
else if (tag == "Spanner")
|
||||
Spanner::readSpanner(e, this, track());
|
||||
else if (tag == "Lyrics") {
|
||||
Element* element = new Lyrics(score());
|
||||
element->setTrack(e.track());
|
||||
element->read(e);
|
||||
add(element);
|
||||
}
|
||||
else if (tag == "pos") {
|
||||
QPointF pt = e.readPoint();
|
||||
setUserOff(pt * spatium());
|
||||
}
|
||||
else if (tag == "offset")
|
||||
DurationElement::readProperties(e);
|
||||
else if (!DurationElement::readProperties(e))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!spanner) {
|
||||
if (atype == "stop") {
|
||||
SpannerValues sv;
|
||||
sv.spannerId = id;
|
||||
sv.track2 = track();
|
||||
sv.tick2 = e.tick();
|
||||
e.addSpannerValues(sv);
|
||||
}
|
||||
else if (atype == "start")
|
||||
qDebug("spanner: start without spanner");
|
||||
}
|
||||
else {
|
||||
if (atype == "start") {
|
||||
if (spanner->ticks() > 0 && spanner->tick() == -1) // stop has been read first
|
||||
spanner->setTicks(spanner->ticks() - e.tick() - 1);
|
||||
spanner->setTick(e.tick());
|
||||
spanner->setTrack(track());
|
||||
if (spanner->type() == ElementType::SLUR)
|
||||
spanner->setStartElement(this);
|
||||
if (e.pasteMode()) {
|
||||
//---------------------------------------------------------
|
||||
// ChordRest::readAddConnector
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ChordRest::readAddConnector(ConnectorInfoReader* info, bool pasteMode)
|
||||
{
|
||||
const ElementType type = info->type();
|
||||
switch(type) {
|
||||
case ElementType::SLUR:
|
||||
{
|
||||
Spanner* spanner = toSpanner(info->connector());
|
||||
const Location& l = info->location();
|
||||
|
||||
if (info->isStart()) {
|
||||
spanner->setTrack(l.track());
|
||||
spanner->setTick(tick());
|
||||
spanner->setStartElement(this);
|
||||
if (pasteMode) {
|
||||
score()->undoAddElement(spanner);
|
||||
for (ScoreElement* ee : spanner->linkList()) {
|
||||
if (ee == spanner)
|
||||
continue;
|
||||
|
@ -342,16 +349,14 @@ bool ChordRest::readProperties(XmlReader& e)
|
|||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
score()->addSpanner(spanner);
|
||||
}
|
||||
else if (atype == "stop") {
|
||||
spanner->setTick2(e.tick());
|
||||
spanner->setTrack2(track());
|
||||
if (spanner->isSlur())
|
||||
spanner->setEndElement(this);
|
||||
ChordRest* start = toChordRest(spanner->startElement());
|
||||
if (start)
|
||||
spanner->setTrack(start->track());
|
||||
if (e.pasteMode()) {
|
||||
else if (info->isEnd()) {
|
||||
spanner->setTrack2(l.track());
|
||||
spanner->setTick2(tick());
|
||||
spanner->setEndElement(this);
|
||||
if (pasteMode) {
|
||||
for (ScoreElement* ee : spanner->linkList()) {
|
||||
if (ee == spanner)
|
||||
continue;
|
||||
|
@ -370,25 +375,12 @@ bool ChordRest::readProperties(XmlReader& e)
|
|||
}
|
||||
}
|
||||
else
|
||||
qDebug("ChordRest::read(): unknown Slur type <%s>", qPrintable(atype));
|
||||
qDebug("ChordRest::readAddConnector(): Slur end is neither start nor end");
|
||||
}
|
||||
e.readNext();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
else if (tag == "Lyrics") {
|
||||
Element* element = new Lyrics(score());
|
||||
element->setTrack(e.track());
|
||||
element->read(e);
|
||||
add(element);
|
||||
}
|
||||
else if (tag == "pos") {
|
||||
QPointF pt = e.readPoint();
|
||||
setUserOff(pt * spatium());
|
||||
}
|
||||
else if (tag == "offset")
|
||||
DurationElement::readProperties(e);
|
||||
else if (!DurationElement::readProperties(e))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -85,6 +85,7 @@ class ChordRest : public DurationElement {
|
|||
virtual void writeProperties(XmlWriter& xml) const;
|
||||
virtual bool readProperties(XmlReader&);
|
||||
virtual bool readProperties300(XmlReader&);
|
||||
virtual void readAddConnector(ConnectorInfoReader* info, bool pasteMode) override;
|
||||
virtual void scanElements(void* data, void (*func)(void*, Element*), bool all=true) override;
|
||||
|
||||
void setBeamMode(Beam::Mode m) { _beamMode = m; }
|
||||
|
|
|
@ -370,7 +370,7 @@ void Glissando::write(XmlWriter& xml) const
|
|||
{
|
||||
if (!xml.canWrite(this))
|
||||
return;
|
||||
xml.stag(QString("%1 id=\"%2\"").arg(name()).arg(xml.spannerId(this)));
|
||||
xml.stag(name());
|
||||
if (_showText && !_text.isEmpty())
|
||||
xml.tag("text", _text);
|
||||
|
||||
|
@ -389,7 +389,6 @@ void Glissando::read(XmlReader& e)
|
|||
{
|
||||
qDeleteAll(spannerSegments());
|
||||
spannerSegments().clear();
|
||||
e.addSpanner(e.intAttribute("id", -1), this);
|
||||
|
||||
_showText = false;
|
||||
while (e.readNextStartElement()) {
|
||||
|
|
|
@ -495,8 +495,7 @@ void Hairpin::write(XmlWriter& xml) const
|
|||
{
|
||||
if (!xml.canWrite(this))
|
||||
return;
|
||||
int id = xml.spannerId(this);
|
||||
xml.stag(QString("%1 id=\"%2\"").arg(name()).arg(id));
|
||||
xml.stag(name());
|
||||
xml.tag("subtype", int(_hairpinType));
|
||||
writeProperty(xml, Pid::VELO_CHANGE);
|
||||
writeProperty(xml, Pid::HAIRPIN_CIRCLEDTIP);
|
||||
|
@ -521,9 +520,6 @@ void Hairpin::read(XmlReader& e)
|
|||
delete seg;
|
||||
spannerSegments().clear();
|
||||
|
||||
int id = e.intAttribute("id", -1);
|
||||
e.addSpanner(id, this);
|
||||
|
||||
while (e.readNextStartElement()) {
|
||||
const QStringRef& tag(e.name());
|
||||
if (tag == "subtype")
|
||||
|
|
|
@ -90,8 +90,6 @@ LetRing::LetRing(Score* s)
|
|||
|
||||
void LetRing::read(XmlReader& e)
|
||||
{
|
||||
int id = e.intAttribute("id", -1);
|
||||
e.addSpanner(id, this);
|
||||
while (e.readNextStartElement()) {
|
||||
if (!TextLineBase::readProperties(e))
|
||||
e.unknown();
|
||||
|
@ -106,7 +104,7 @@ void LetRing::write(XmlWriter& xml) const
|
|||
{
|
||||
if (!xml.canWrite(this))
|
||||
return;
|
||||
xml.stag(QString("%1 id=\"%2\"").arg(name()).arg(xml.spannerId(this)));
|
||||
xml.stag(name());
|
||||
|
||||
for (const StyledProperty& spp : *styledProperties())
|
||||
writeProperty(xml, spp.pid);
|
||||
|
|
|
@ -1115,8 +1115,7 @@ const QRectF& SLine::bbox() const
|
|||
|
||||
void SLine::write(XmlWriter& xml) const
|
||||
{
|
||||
int id = xml.spannerId(this);
|
||||
xml.stag(QString("%1 id=\"%2\"").arg(name()).arg(id));
|
||||
xml.stag(name());
|
||||
SLine::writeProperties(xml);
|
||||
xml.etag();
|
||||
}
|
||||
|
@ -1130,7 +1129,6 @@ void SLine::read(XmlReader& e)
|
|||
foreach(SpannerSegment* seg, spannerSegments())
|
||||
delete seg;
|
||||
spannerSegments().clear();
|
||||
e.addSpanner(e.intAttribute("id", -1), this);
|
||||
|
||||
while (e.readNextStartElement()) {
|
||||
if (!SLine::readProperties(e))
|
||||
|
|
|
@ -1966,67 +1966,8 @@ void Measure::read(XmlReader& e, int staffIdx)
|
|||
segment = 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());
|
||||
sl->read(e);
|
||||
//
|
||||
// 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 == "LetRing"
|
||||
|| tag == "Vibrato"
|
||||
|| tag == "PalmMute"
|
||||
|| tag == "Volta") {
|
||||
Spanner* sp = toSpanner(Element::name2Element(tag, score()));
|
||||
sp->setTrack(e.track());
|
||||
sp->setTick(e.tick());
|
||||
// ?? sp->setAnchor(Spanner::Anchor::SEGMENT);
|
||||
sp->read(e);
|
||||
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 == "Spanner")
|
||||
Spanner::readSpanner(e, this, e.track());
|
||||
else if (tag == "RepeatMeasure") {
|
||||
RepeatMeasure* rm = new RepeatMeasure(score());
|
||||
rm->setTrack(e.track());
|
||||
|
@ -2295,6 +2236,44 @@ void Measure::read(XmlReader& e, int staffIdx)
|
|||
e.setCurrentMeasure(nullptr);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Measure::readAddConnector
|
||||
//---------------------------------------------------------
|
||||
|
||||
void Measure::readAddConnector(ConnectorInfoReader* info, bool pasteMode)
|
||||
{
|
||||
const ElementType type = info->type();
|
||||
switch(type) {
|
||||
case ElementType::HAIRPIN:
|
||||
case ElementType::PEDAL:
|
||||
case ElementType::OTTAVA:
|
||||
case ElementType::TRILL:
|
||||
case ElementType::TEXTLINE:
|
||||
case ElementType::LET_RING:
|
||||
case ElementType::VIBRATO:
|
||||
case ElementType::PALM_MUTE:
|
||||
case ElementType::VOLTA:
|
||||
{
|
||||
Spanner* sp = toSpanner(info->connector());
|
||||
const Location& l = info->location();
|
||||
const int lTick = l.frac().ticks();
|
||||
const int spTick = pasteMode ? lTick : (tick() + lTick);
|
||||
if (info->isStart()) {
|
||||
sp->setTrack(l.track());
|
||||
sp->setTick(spTick);
|
||||
score()->addSpanner(sp);
|
||||
}
|
||||
else if (info->isEnd()) {
|
||||
sp->setTrack2(l.track());
|
||||
sp->setTick2(spTick);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Measure::read300
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -101,6 +101,7 @@ class Measure final : public MeasureBase {
|
|||
|
||||
void read(XmlReader&, int idx);
|
||||
void read(XmlReader& d) { read(d, 0); }
|
||||
virtual void readAddConnector(ConnectorInfoReader* info, bool pasteMode) override;
|
||||
void read300(XmlReader&, int idx);
|
||||
void read300(XmlReader& d) { read300(d, 0); }
|
||||
virtual void write(XmlWriter& xml) const override { Element::write(xml); }
|
||||
|
|
|
@ -1193,11 +1193,9 @@ void Note::write(XmlWriter& xml) const
|
|||
for (NoteDot* dot : _dots)
|
||||
dot->write(xml);
|
||||
if (_tieFor)
|
||||
_tieFor->write(xml);
|
||||
if (_tieBack) {
|
||||
int id = xml.spannerId(_tieBack);
|
||||
xml.tagE(QString("endSpanner id=\"%1\"").arg(id));
|
||||
}
|
||||
_tieFor->writeSpannerStart(xml, this, track());
|
||||
if (_tieBack)
|
||||
_tieBack->writeSpannerEnd(xml, this, track());
|
||||
if ((chord() == 0 || chord()->playEventType() != PlayEventType::Auto) && !_playEvents.empty()) {
|
||||
xml.stag("Events");
|
||||
for (const NoteEvent& e : _playEvents)
|
||||
|
@ -1212,9 +1210,9 @@ void Note::write(XmlWriter& xml) const
|
|||
}
|
||||
|
||||
for (Spanner* e : _spannerFor)
|
||||
e->write(xml);
|
||||
e->writeSpannerStart(xml, this, track());
|
||||
for (Spanner* e : _spannerBack)
|
||||
xml.tagE(QString("endSpanner id=\"%1\"").arg(xml.spannerId(e)));
|
||||
e->writeSpannerEnd(xml, this, track());
|
||||
|
||||
xml.etag();
|
||||
}
|
||||
|
@ -1321,14 +1319,8 @@ bool Note::readProperties(XmlReader& e)
|
|||
a->read(e);
|
||||
add(a);
|
||||
}
|
||||
else if (tag == "Tie") {
|
||||
Tie* tie = new Tie(score());
|
||||
tie->setParent(this);
|
||||
tie->setTrack(track());
|
||||
tie->read(e);
|
||||
tie->setStartNote(this);
|
||||
_tieFor = tie;
|
||||
}
|
||||
else if (tag == "Spanner")
|
||||
Spanner::readSpanner(e, this, track());
|
||||
else if (tag == "tpc2")
|
||||
_tpc[1] = e.readInt();
|
||||
else if (tag == "small")
|
||||
|
@ -1408,79 +1400,6 @@ bool Note::readProperties(XmlReader& e)
|
|||
if (chord())
|
||||
chord()->setPlayEventType(PlayEventType::User);
|
||||
}
|
||||
else if (tag == "endSpanner") {
|
||||
int id = e.intAttribute("id");
|
||||
Spanner* sp = e.findSpanner(id);
|
||||
if (sp) {
|
||||
sp->setEndElement(this);
|
||||
if (sp->isTie())
|
||||
_tieBack = toTie(sp);
|
||||
else {
|
||||
if (sp->isGlissando() && parent() && parent()->isChord())
|
||||
toChord(parent())->setEndsGlissando(true);
|
||||
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 id = e.intAttribute("id", -1);
|
||||
if (id != -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(score());
|
||||
placeholder->setAnchor(Spanner::Anchor::NOTE);
|
||||
placeholder->setEndElement(this);
|
||||
placeholder->setTrack2(track());
|
||||
placeholder->setTick(0);
|
||||
placeholder->setTick2(e.tick());
|
||||
e.addSpanner(id, placeholder);
|
||||
}
|
||||
}
|
||||
e.readNext();
|
||||
}
|
||||
else if (tag == "TextLine"
|
||||
|| tag == "Glissando") {
|
||||
Spanner* sp = toSpanner(Element::name2Element(tag, 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(track());
|
||||
sp->read(e);
|
||||
// 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(this);
|
||||
sp->setTick(e.tick());
|
||||
addSpannerFor(sp);
|
||||
sp->setParent(this);
|
||||
}
|
||||
}
|
||||
else if (tag == "offset")
|
||||
Element::readProperties(e);
|
||||
else if (Element::readProperties(e))
|
||||
|
@ -1490,6 +1409,67 @@ bool Note::readProperties(XmlReader& e)
|
|||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Note::readAddConnector
|
||||
//---------------------------------------------------------
|
||||
|
||||
void Note::readAddConnector(ConnectorInfoReader* info, bool pasteMode)
|
||||
{
|
||||
const ElementType type = info->type();
|
||||
const Location& l = info->location();
|
||||
switch(type) {
|
||||
case ElementType::TIE:
|
||||
case ElementType::TEXTLINE:
|
||||
case ElementType::GLISSANDO:
|
||||
{
|
||||
Spanner* sp = toSpanner(info->connector());
|
||||
if (info->isStart()) {
|
||||
sp->setTrack(l.track());
|
||||
sp->setTick(tick());
|
||||
if (sp->isTie()) {
|
||||
Tie* tie = toTie(sp);
|
||||
tie->setParent(this);
|
||||
tie->setStartNote(this);
|
||||
_tieFor = tie;
|
||||
}
|
||||
else {
|
||||
// DISABLE pasting of glissandi into staves with other lionked staves
|
||||
// because the glissando is not properly cloned into the linked staves
|
||||
if (pasteMode && staff() && staff()->links() && !staff()->links()->empty()) {
|
||||
// Do nothing. The spanner is no longer needed.
|
||||
info->releaseConnector();
|
||||
delete sp;
|
||||
}
|
||||
else {
|
||||
sp->setAnchor(Spanner::Anchor::NOTE);
|
||||
sp->setStartElement(this);
|
||||
addSpannerFor(sp);
|
||||
sp->setParent(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (info->isEnd()) {
|
||||
// We might have deleted a spanner (see "DISABLE pasting
|
||||
// of glissandi..." note above)
|
||||
if (!sp)
|
||||
break;
|
||||
sp->setTrack2(l.track());
|
||||
sp->setTick2(tick());
|
||||
sp->setEndElement(this);
|
||||
if (sp->isTie())
|
||||
_tieBack = toTie(sp);
|
||||
else {
|
||||
if (sp->isGlissando() && parent() && parent()->isChord())
|
||||
toChord(parent())->setEndsGlissando(true);
|
||||
addSpannerBack(sp);
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// transposition
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -385,6 +385,7 @@ class Note final : public Element {
|
|||
|
||||
virtual void read(XmlReader&) override;
|
||||
virtual bool readProperties(XmlReader&) override;
|
||||
virtual void readAddConnector(ConnectorInfoReader* info, bool pasteMode) override;
|
||||
virtual void read300(XmlReader&) override;
|
||||
virtual bool readProperties300(XmlReader&) override;
|
||||
virtual void write(XmlWriter&) const override;
|
||||
|
|
|
@ -253,7 +253,7 @@ void Ottava::write(XmlWriter& xml) const
|
|||
{
|
||||
if (!xml.canWrite(this))
|
||||
return;
|
||||
xml.stag(QString("%1 id=\"%2\"").arg(name()).arg(xml.spannerId(this)));
|
||||
xml.stag(name());
|
||||
xml.tag("subtype", ottavaDefault[int(ottavaType())].name);
|
||||
|
||||
for (const StyledProperty& spp : *styledProperties())
|
||||
|
@ -271,7 +271,6 @@ void Ottava::read(XmlReader& e)
|
|||
{
|
||||
qDeleteAll(spannerSegments());
|
||||
spannerSegments().clear();
|
||||
e.addSpanner(e.intAttribute("id", -1), this);
|
||||
while (e.readNextStartElement())
|
||||
readProperties(e);
|
||||
updateStyledProperties();
|
||||
|
|
|
@ -90,8 +90,6 @@ PalmMute::PalmMute(Score* s)
|
|||
|
||||
void PalmMute::read(XmlReader& e)
|
||||
{
|
||||
int id = e.intAttribute("id", -1);
|
||||
e.addSpanner(id, this);
|
||||
while (e.readNextStartElement()) {
|
||||
if (!TextLineBase::readProperties(e))
|
||||
e.unknown();
|
||||
|
@ -106,7 +104,7 @@ void PalmMute::write(XmlWriter& xml) const
|
|||
{
|
||||
if (!xml.canWrite(this))
|
||||
return;
|
||||
xml.stag(QString("%1 id=\"%2\"").arg(name()).arg(xml.spannerId(this)));
|
||||
xml.stag(name());
|
||||
|
||||
for (const StyledProperty& spp : *styledProperties())
|
||||
writeProperty(xml, spp.pid);
|
||||
|
|
|
@ -274,20 +274,6 @@ bool Score::pasteStaff(XmlReader& e, Segment* dst, int dstStaff)
|
|||
sp->setTick(e.tick());
|
||||
addSpanner(sp);
|
||||
}
|
||||
else if (tag == "Slur") {
|
||||
Slur* sp = new Slur(this);
|
||||
sp->read(e);
|
||||
sp->setTrack(e.track());
|
||||
sp->setTick(e.tick());
|
||||
// check if we saw endSpanner / stop element already
|
||||
int id = e.spannerId(sp);
|
||||
const SpannerValues* sv = e.spannerValues(id);
|
||||
if (sv) {
|
||||
sp->setTick2(sv->tick2);
|
||||
sp->setTrack2(sv->track2);
|
||||
}
|
||||
undoAddElement(sp);
|
||||
}
|
||||
else if (tag == "endSpanner") {
|
||||
int id = e.intAttribute("id");
|
||||
Spanner* spanner = e.findSpanner(id);
|
||||
|
|
|
@ -110,8 +110,6 @@ Pedal::Pedal(Score* s)
|
|||
|
||||
void Pedal::read(XmlReader& e)
|
||||
{
|
||||
int id = e.intAttribute("id", -1);
|
||||
e.addSpanner(id, this);
|
||||
while (e.readNextStartElement()) {
|
||||
if (!TextLineBase::readProperties(e))
|
||||
e.unknown();
|
||||
|
@ -126,8 +124,7 @@ void Pedal::write(XmlWriter& xml) const
|
|||
{
|
||||
if (!xml.canWrite(this))
|
||||
return;
|
||||
int id = xml.spannerId(this);
|
||||
xml.stag(QString("%1 id=\"%2\"").arg(name()).arg(id));
|
||||
xml.stag(name());
|
||||
|
||||
for (auto i : {
|
||||
Pid::END_HOOK_TYPE,
|
||||
|
|
|
@ -1192,12 +1192,12 @@ void Score::writeSegments(XmlWriter& xml, int strack, int etrack,
|
|||
end = s->tick2() < endTick;
|
||||
else
|
||||
end = s->tick2() <= endTick;
|
||||
if (s->tick() == segment->tick() && (!clip || end)) {
|
||||
if (s->tick() == segment->tick() && (!clip || end) && !s->isSlur()) {
|
||||
if (needTick) {
|
||||
writeMove(xml, segment, track);
|
||||
needTick = false;
|
||||
}
|
||||
s->write(xml);
|
||||
s->writeSpannerStart(xml, segment, track);
|
||||
}
|
||||
}
|
||||
if ((s->tick2() == segment->tick())
|
||||
|
@ -1209,7 +1209,7 @@ void Score::writeSegments(XmlWriter& xml, int strack, int etrack,
|
|||
writeMove(xml, segment, track);
|
||||
needTick = false;
|
||||
}
|
||||
xml.tagE(QString("endSpanner id=\"%1\"").arg(xml.spannerId(s)));
|
||||
s->writeSpannerEnd(xml, segment, track);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1270,7 +1270,7 @@ void Score::writeSegments(XmlWriter& xml, int strack, int etrack,
|
|||
&& (s->track2() == track || (s->track2() == -1 && s->track() == track))
|
||||
&& (!clip || s->tick() >= fs->tick())
|
||||
) {
|
||||
xml.tagE(QString("endSpanner id=\"%1\"").arg(xml.spannerId(s)));
|
||||
s->writeSpannerEnd(xml, lastMeasure(), track, endTick);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1003,7 +1003,7 @@ void Slur::write(XmlWriter& xml) const
|
|||
}
|
||||
if (!xml.canWrite(this))
|
||||
return;
|
||||
xml.stag(QString("Slur id=\"%1\"").arg(xml.spannerId(this)));
|
||||
xml.stag(name());
|
||||
SlurTie::writeProperties(xml);
|
||||
xml.etag();
|
||||
}
|
||||
|
@ -1015,7 +1015,6 @@ void Slur::write(XmlWriter& xml) const
|
|||
void Slur::read(XmlReader& e)
|
||||
{
|
||||
setTrack(e.track()); // set staff
|
||||
e.addSpanner(e.intAttribute("id"), this);
|
||||
while (e.readNextStartElement()) {
|
||||
const QStringRef& tag(e.name());
|
||||
if (tag == "track2")
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
// the file LICENCE.GPL
|
||||
//=============================================================================
|
||||
|
||||
#include "connector.h"
|
||||
#include "score.h"
|
||||
#include "spanner.h"
|
||||
#include "system.h"
|
||||
|
@ -22,6 +23,19 @@
|
|||
|
||||
namespace Ms {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// @@ SpannerWriter
|
||||
/// Helper class for writing Spanners
|
||||
//-----------------------------------------------------------------------------
|
||||
class SpannerWriter : public ConnectorInfoWriter {
|
||||
protected:
|
||||
const char* tagName() const override { return "Spanner"; }
|
||||
public:
|
||||
SpannerWriter(XmlWriter& xml, const Element* current, const Spanner* spanner, int track, Fraction frac, bool start);
|
||||
|
||||
static void fillSpannerPosition(Location& l, const Element* endpoint, int tick, bool clipboardmode);
|
||||
};
|
||||
|
||||
//---------------------------------------------------------
|
||||
// SpannerSegment
|
||||
//---------------------------------------------------------
|
||||
|
@ -954,5 +968,148 @@ SpannerSegment* Spanner::layoutSystem(System*)
|
|||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// Spanner::writeSpannerStart
|
||||
//---------------------------------------------------------
|
||||
|
||||
void Spanner::writeSpannerStart(XmlWriter& xml, const Element* current, int track, Fraction frac) const
|
||||
{
|
||||
SpannerWriter w(xml, current, this, track, frac, true);
|
||||
w.write();
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// Spanner::writeSpannerEnd
|
||||
//---------------------------------------------------------
|
||||
|
||||
void Spanner::writeSpannerEnd(XmlWriter& xml, const Element* current, int track, Fraction frac) const
|
||||
{
|
||||
SpannerWriter w(xml, current, this, track, frac, false);
|
||||
w.write();
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// fraction
|
||||
//---------------------------------------------------------
|
||||
|
||||
static Fraction fraction(const XmlWriter& xml, const Element* current, int tick) {
|
||||
if (!xml.clipboardmode()) {
|
||||
const Measure* m = toMeasure(current->findMeasure());
|
||||
if (m)
|
||||
tick -= m->tick();
|
||||
}
|
||||
return Fraction::fromTicks(tick);
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// Spanner::writeSpannerStart
|
||||
//---------------------------------------------------------
|
||||
|
||||
void Spanner::writeSpannerStart(XmlWriter& xml, const Element* current, int track, int tick) const
|
||||
{
|
||||
writeSpannerStart(xml, current, track, fraction(xml, current, tick));
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// Spanner::writeSpannerEnd
|
||||
//---------------------------------------------------------
|
||||
|
||||
void Spanner::writeSpannerEnd(XmlWriter& xml, const Element* current, int track, int tick) const
|
||||
{
|
||||
writeSpannerEnd(xml, current, track, fraction(xml, current, tick));
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// Spanner::readSpanner
|
||||
//---------------------------------------------------------
|
||||
|
||||
void Spanner::readSpanner(XmlReader& e, Element* current, int track)
|
||||
{
|
||||
ConnectorInfoReader info(e, current, track);
|
||||
ConnectorInfoReader::readConnector(info, e);
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// Spanner::readSpanner
|
||||
//---------------------------------------------------------
|
||||
|
||||
void Spanner::readSpanner(XmlReader& e, Score* current, int track)
|
||||
{
|
||||
ConnectorInfoReader info(e, current, track);
|
||||
ConnectorInfoReader::readConnector(info, e);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// SpannerWriter::fillSpannerPosition
|
||||
//---------------------------------------------------------
|
||||
|
||||
void SpannerWriter::fillSpannerPosition(Location& l, const Element* endpoint, int tick, bool clipboardmode)
|
||||
{
|
||||
if (clipboardmode) {
|
||||
l.setMeasure(0);
|
||||
l.setFrac(Fraction::fromTicks(tick));
|
||||
}
|
||||
else {
|
||||
const MeasureBase* m = toMeasureBase(endpoint->findMeasure());
|
||||
if (!m) {
|
||||
qWarning("fillSpannerPosition: couldn't find spanner's endpoint's measure");
|
||||
l.setMeasure(0);
|
||||
l.setFrac(Fraction::fromTicks(tick));
|
||||
return;
|
||||
}
|
||||
// It may happen (hairpins!) that the spanner's end element is
|
||||
// situated in the end of one measure but its end tick is in the
|
||||
// beginning of the next measure. So we are to correct the found
|
||||
// measure a bit.
|
||||
while (tick >= m->endTick()) {
|
||||
const MeasureBase* next = m->next();
|
||||
if (next)
|
||||
m = next;
|
||||
else
|
||||
break;
|
||||
}
|
||||
l.setMeasure(m->measureIndex());
|
||||
l.setFrac(Fraction::fromTicks(tick - m->tick()));
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// SpannerWriter::SpannerWriter
|
||||
//---------------------------------------------------------
|
||||
|
||||
SpannerWriter::SpannerWriter(XmlWriter& xml, const Element* current, const Spanner* sp, int track, Fraction frac, bool start)
|
||||
: ConnectorInfoWriter(xml, current, sp, track, frac)
|
||||
{
|
||||
const bool clipboardmode = xml.clipboardmode();
|
||||
if (!sp->startElement() || !sp->endElement()) {
|
||||
qWarning("SpannerWriter: spanner (%s) doesn't have an endpoint!", sp->name());
|
||||
return;
|
||||
}
|
||||
if (current->isMeasure() || current->isSegment() || (sp->startElement()->type() != current->type())) {
|
||||
// (The latter is the hairpins' case, for example, though they are
|
||||
// covered by the other checks too.)
|
||||
// We cannot determine position of the spanner from its start/end
|
||||
// elements and will try to obtain this info from the spanner itself.
|
||||
if (!start) {
|
||||
_prevLoc.setTrack(sp->track());
|
||||
fillSpannerPosition(_prevLoc, sp->startElement(), sp->tick(), clipboardmode);
|
||||
}
|
||||
else {
|
||||
const int track2 = (sp->track2() != -1) ? sp->track2() : sp->track();
|
||||
_nextLoc.setTrack(track2);
|
||||
fillSpannerPosition(_nextLoc, sp->endElement(), sp->tick2(), clipboardmode);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// We can obtain the spanner position info from its start/end
|
||||
// elements and will prefer this source of information.
|
||||
// Reason: some spanners contain no or wrong information (e.g. Ties).
|
||||
if (!start)
|
||||
updateLocation(sp->startElement(), _prevLoc, clipboardmode);
|
||||
else
|
||||
updateLocation(sp->endElement(), _nextLoc, clipboardmode);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -149,6 +149,13 @@ class Spanner : public Element {
|
|||
virtual ElementType type() const = 0;
|
||||
virtual void setScore(Score* s) override;
|
||||
|
||||
void writeSpannerStart(XmlWriter& xml, const Element* current, int track, Fraction frac = -1) const;
|
||||
void writeSpannerEnd(XmlWriter& xml, const Element* current, int track, Fraction frac = -1) const;
|
||||
void writeSpannerStart(XmlWriter& xml, const Element* current, int track, int tick) const;
|
||||
void writeSpannerEnd(XmlWriter& xml, const Element* current, int track, int tick) const;
|
||||
static void readSpanner(XmlReader& e, Element* current, int track);
|
||||
static void readSpanner(XmlReader& e, Score* current, int track);
|
||||
|
||||
virtual int tick() const override { return _tick; }
|
||||
int tick2() const { return _tick + _ticks; }
|
||||
int ticks() const { return _ticks; }
|
||||
|
|
|
@ -391,7 +391,7 @@ void TextLineBase::write(XmlWriter& xml) const
|
|||
{
|
||||
if (!xml.canWrite(this))
|
||||
return;
|
||||
xml.stag(QString("%1 id=\"%2\"").arg(name()).arg(xml.spannerId(this)));
|
||||
xml.stag(name());
|
||||
writeProperties(xml);
|
||||
xml.etag();
|
||||
}
|
||||
|
@ -404,7 +404,6 @@ void TextLineBase::read(XmlReader& e)
|
|||
{
|
||||
qDeleteAll(spannerSegments());
|
||||
spannerSegments().clear();
|
||||
e.addSpanner(e.intAttribute("id", -1), this);
|
||||
|
||||
while (e.readNextStartElement()) {
|
||||
if (!readProperties(e))
|
||||
|
|
|
@ -519,7 +519,7 @@ Tie::Tie(Score* s)
|
|||
|
||||
void Tie::write(XmlWriter& xml) const
|
||||
{
|
||||
xml.stag(QString("Tie id=\"%1\"").arg(xml.spannerId(this)));
|
||||
xml.stag(name());
|
||||
SlurTie::writeProperties(xml);
|
||||
xml.etag();
|
||||
}
|
||||
|
@ -530,7 +530,6 @@ void Tie::write(XmlWriter& xml) const
|
|||
|
||||
void Tie::read(XmlReader& e)
|
||||
{
|
||||
e.addSpanner(e.intAttribute("id"), this);
|
||||
while (e.readNextStartElement()) {
|
||||
if (SlurTie::readProperties(e))
|
||||
;
|
||||
|
|
|
@ -327,7 +327,7 @@ void Trill::write(XmlWriter& xml) const
|
|||
{
|
||||
if (!xml.canWrite(this))
|
||||
return;
|
||||
xml.stag(QString("%1 id=\"%2\"").arg(name()).arg(xml.spannerId(this)));
|
||||
xml.stag(name());
|
||||
xml.tag("subtype", trillTypeName());
|
||||
writeProperty(xml, Pid::PLAY);
|
||||
writeProperty(xml, Pid::ORNAMENT_STYLE);
|
||||
|
@ -346,7 +346,6 @@ void Trill::read(XmlReader& e)
|
|||
qDeleteAll(spannerSegments());
|
||||
spannerSegments().clear();
|
||||
|
||||
e.addSpanner(e.intAttribute("id", -1), this);
|
||||
while (e.readNextStartElement()) {
|
||||
const QStringRef& tag(e.name());
|
||||
if (tag == "subtype")
|
||||
|
|
|
@ -216,7 +216,7 @@ void Vibrato::write(XmlWriter& xml) const
|
|||
{
|
||||
if (!xml.canWrite(this))
|
||||
return;
|
||||
xml.stag(QString("%1 id=\"%2\"").arg(name()).arg(xml.spannerId(this)));
|
||||
xml.stag(name());
|
||||
xml.tag("subtype", vibratoTypeName());
|
||||
writeProperty(xml, Pid::PLAY);
|
||||
SLine::writeProperties(xml);
|
||||
|
@ -232,7 +232,6 @@ void Vibrato::read(XmlReader& e)
|
|||
qDeleteAll(spannerSegments());
|
||||
spannerSegments().clear();
|
||||
|
||||
e.addSpanner(e.intAttribute("id", -1), this);
|
||||
while (e.readNextStartElement()) {
|
||||
const QStringRef& tag(e.name());
|
||||
if (tag == "subtype")
|
||||
|
|
|
@ -130,7 +130,6 @@ void Volta::read(XmlReader& e)
|
|||
qDeleteAll(spannerSegments());
|
||||
spannerSegments().clear();
|
||||
|
||||
e.addSpanner(e.intAttribute("id", -1), this);
|
||||
while (e.readNextStartElement()) {
|
||||
const QStringRef& tag(e.name());
|
||||
if (tag == "endings") {
|
||||
|
@ -153,7 +152,7 @@ void Volta::read(XmlReader& e)
|
|||
|
||||
void Volta::write(XmlWriter& xml) const
|
||||
{
|
||||
xml.stag(QString("%1 id=\"%2\"").arg(name()).arg(xml.spannerId(this)));
|
||||
xml.stag(name());
|
||||
TextLineBase::writeProperties(xml);
|
||||
QString s;
|
||||
for (int i : _endings) {
|
||||
|
|
Loading…
Reference in a new issue