Read and write spanners using the connectors framework

Remove IDs for spanners
This commit is contained in:
Dmitri Ovodok 2018-05-24 00:27:23 +03:00
parent 2c28195c1c
commit 104898195b
24 changed files with 344 additions and 282 deletions

View file

@ -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());

View file

@ -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;
}
//---------------------------------------------------------

View file

@ -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; }

View file

@ -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()) {

View file

@ -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")

View file

@ -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);

View file

@ -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))

View file

@ -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
//---------------------------------------------------------

View file

@ -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); }

View file

@ -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
//---------------------------------------------------------

View file

@ -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;

View file

@ -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();

View file

@ -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);

View file

@ -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);

View file

@ -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,

View file

@ -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);
}
}
}

View file

@ -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")

View file

@ -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);
}
}
}

View file

@ -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; }

View file

@ -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))

View file

@ -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))
;

View file

@ -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")

View file

@ -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")

View file

@ -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) {