//============================================================================= // MuseScore // Music Composition & Notation // // Copyright (C) 2002-2012 Werner Schweer // // 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 LICENCE.GPL //============================================================================= #include "line.h" #include "barline.h" #include "chord.h" #include "lyrics.h" #include "measure.h" #include "score.h" #include "segment.h" #include "staff.h" #include "system.h" #include "textline.h" #include "utils.h" #include "xml.h" namespace Ms { //--------------------------------------------------------- // LineSegment //--------------------------------------------------------- LineSegment::LineSegment(const LineSegment& s) : SpannerSegment(s) { _p2 = s._p2; _userOff2 = s._userOff2; } //--------------------------------------------------------- // readProperties //--------------------------------------------------------- bool LineSegment::readProperties(XmlReader& e) { const QStringRef& tag(e.name()); if (tag == "subtype") setSpannerSegmentType(SpannerSegmentType(e.readInt())); else if (tag == "off1") // off1 is obsolete setUserOff(e.readPoint() * spatium()); else if (tag == "off2") setUserOff2(e.readPoint() * spatium()); else if (tag == "pos") { if (score()->mscVersion() > 114) { qreal _spatium = score()->spatium(); setUserOff(QPointF()); setReadPos(e.readPoint() * _spatium); if (e.pasteMode()) // x position will be wrong setReadPos(QPointF()); } else e.readNext(); } else if (!SpannerSegment::readProperties(e)) { e.unknown(); return false; } return true; } //--------------------------------------------------------- // read //--------------------------------------------------------- void LineSegment::read(XmlReader& e) { while (e.readNextStartElement()) readProperties(e); } //--------------------------------------------------------- // updateGrips //--------------------------------------------------------- void LineSegment::updateGrips(Grip* defaultGrip, QVector& grip) const { *defaultGrip = Grip::END; QPointF pp(pagePos()); grip[int(Grip::START)].translate(pp); grip[int(Grip::END)].translate(pos2() + pp); grip[int(Grip::MIDDLE)].translate(pos2() * .5 + pp); } //--------------------------------------------------------- // setGrip //--------------------------------------------------------- void LineSegment::setGrip(Grip grip, const QPointF& p) { QPointF pt(p * spatium()); switch (grip) { case Grip::START: { QPointF delta(pt - userOff()); setUserOff(pt); setUserOff2(userOff2() - delta); } break; case Grip::END: setUserOff2(pt); break; case Grip::MIDDLE: setUserOff(pt); break; case Grip::APERTURE: default: break; } layout(); // needed? } //--------------------------------------------------------- // getGrip //--------------------------------------------------------- QPointF LineSegment::getGrip(Grip grip) const { QPointF p; switch (grip) { case Grip::START: p = userOff(); break; case Grip::END: p = userOff2(); break; case Grip::MIDDLE: p = userOff(); break; case Grip::APERTURE: default: break; } p /= spatium(); return p; } //--------------------------------------------------------- // gripAnchor // return page coordinates //--------------------------------------------------------- QPointF LineSegment::gripAnchor(Grip grip) const { qreal y = system()->staffYpage(staffIdx()); if (spannerSegmentType() == SpannerSegmentType::MIDDLE) { qreal x; switch (grip) { case Grip::START: x = system()->firstMeasure()->abbox().left(); break; case Grip::END: x = system()->lastMeasure()->abbox().right(); break; default: case Grip::MIDDLE: case Grip::APERTURE: x = 0; // No Anchor y = 0; break; } return QPointF(x, y); } else { if ((grip == Grip::MIDDLE || grip == Grip::APERTURE) // center grip or aperture grip || (grip == Grip::END && spannerSegmentType() == SpannerSegmentType::BEGIN) || (grip == Grip::START && spannerSegmentType() == SpannerSegmentType::END) ) return QPointF(0, 0); else { System* s; QPointF p(line()->linePos(grip, &s)); p.ry() += y - system()->pos().y(); if (s) p += s->pos(); return p; } } } //--------------------------------------------------------- // edit // return true if event is accepted //--------------------------------------------------------- bool LineSegment::edit(MuseScoreView* sv, Grip curGrip, int key, Qt::KeyboardModifiers modifiers, const QString&) { if (!((modifiers & Qt::ShiftModifier) && ((spannerSegmentType() == SpannerSegmentType::SINGLE) || (spannerSegmentType() == SpannerSegmentType::BEGIN && curGrip == Grip::START) || (spannerSegmentType() == SpannerSegmentType::END && curGrip == Grip::END)))) return false; LineSegment* ls = 0; SLine* l = line(); SpannerSegmentType st = spannerSegmentType(); int track = l->track(); int track2 = l->track2(); // assumed to be same as track if (l->anchor() == Spanner::Anchor::SEGMENT) { Segment* s1 = spanner()->startSegment(); Segment* s2 = spanner()->endSegment(); // check for line going to end of score if (spanner()->tick2() >= score()->lastSegment()->tick()) { // endSegment calculated above will be the last chord/rest of score // but that is not correct - it should be an imaginary note *after* the end of the score // best we can do is set s2 to lastSegment (probably the end barline) s2 = score()->lastSegment(); } if (!s1 && !s2) { qDebug("LineSegment::edit: no start/end segment"); return true; } if (key == Qt::Key_Left) { if (curGrip == Grip::START) s1 = prevSeg1(s1, track); else if (curGrip == Grip::END || curGrip == Grip::MIDDLE) s2 = prevSeg1(s2, track2); } else if (key == Qt::Key_Right) { if (curGrip == Grip::START) s1 = nextSeg1(s1, track); else if (curGrip == Grip::END || curGrip == Grip::MIDDLE) { Segment* ns2 = nextSeg1(s2, track2); if (ns2) s2 = ns2; else s2 = score()->lastSegment(); } } if (s1 == 0 || s2 == 0 || s1->tick() >= s2->tick()) return true; if (s1->tick() != spanner()->tick()) spanner()->setTick(s1->tick()); if (s2->tick() != spanner()->tick2()) spanner()->setTick2(s2->tick()); } else { Measure* m1 = l->startMeasure(); Measure* m2 = l->endMeasure(); if (key == Qt::Key_Left) { if (curGrip == Grip::START) { if (m1->prevMeasure()) m1 = m1->prevMeasure(); } else if (curGrip == Grip::END || curGrip == Grip::MIDDLE) { Measure* m = m2->prevMeasure(); if (m) m2 = m; } } else if (key == Qt::Key_Right) { if (curGrip == Grip::START) { if (m1->nextMeasure()) m1 = m1->nextMeasure(); } else if (curGrip == Grip::END || curGrip == Grip::MIDDLE) { if (m2->nextMeasure()) m2 = m2->nextMeasure(); } } if (m1->tick() > m2->tick()) return true; if (l->startElement() != m1) { l->setTick(m1->tick()); l->setTicks(m2->endTick() - m1->tick()); } else if (l->endElement() != m2) { l->setTicks(m2->endTick() - m1->tick()); } } _score->doLayout(); // needed to compute multi measure rests // l->layout(); LineSegment* nls = 0; if (st == SpannerSegmentType::SINGLE) { if (curGrip == Grip::START) nls = l->frontSegment(); else if (curGrip == Grip::END) nls = l->backSegment(); } else if (st == SpannerSegmentType::BEGIN) nls = l->frontSegment(); else if (st == SpannerSegmentType::END) nls = l->backSegment(); if (nls && (nls != this)) sv->changeEditElement(nls); if (ls) _score->undoRemoveElement(ls); _score->setLayoutAll(true); return true; } //--------------------------------------------------------- // editDrag //--------------------------------------------------------- void LineSegment::editDrag(const EditData& ed) { // Only for resizing according to the diagonal properties QPointF deltaResize(ed.delta.x(), line()->diagonal() ? ed.delta.y() : 0.0); // Only for moving, no y limitaion QPointF deltaMove(ed.delta.x(), ed.delta.y()); switch (ed.curGrip) { case Grip::START: // Resize the begin of element (left grip) setUserOff(userOff() + deltaResize); _userOff2 -= deltaResize; break; case Grip::END: // Resize the end of element (rigth grip) _userOff2 += deltaResize; break; case Grip::MIDDLE: // Move the element (middle grip) setUserOff(userOff() + deltaMove); break; default: break; } if ((line()->anchor() == Spanner::Anchor::NOTE) && (ed.curGrip == Grip::START || ed.curGrip == Grip::END)) { // // if we touch a different note, change anchor // Element* e = ed.view->elementNear(ed.pos); if (e && e->type() == Element::Type::NOTE) { SLine* l = line(); if (ed.curGrip == Grip::END && e != line()->endElement()) { qDebug("LineSegment: move end anchor"); Note* noteOld = static_cast(l->endElement()); Note* noteNew = static_cast(e); noteOld->removeSpannerBack(l); noteNew->addSpannerBack(l); l->setEndElement(noteNew); _userOff2 += noteOld->canvasPos() - noteNew->canvasPos(); } else if (ed.curGrip == Grip::START && e != l->startElement()) { qDebug("LineSegment: move start anchor (not impl.)"); } } } line()->layout(); } //--------------------------------------------------------- // spatiumChanged //--------------------------------------------------------- void LineSegment::spatiumChanged(qreal ov, qreal nv) { Element::spatiumChanged(ov, nv); _userOff2 *= nv / ov; } //--------------------------------------------------------- // localSpatiumChanged //--------------------------------------------------------- void LineSegment::localSpatiumChanged(qreal ov, qreal nv) { Element::localSpatiumChanged(ov, nv); _userOff2 *= nv / ov; } //--------------------------------------------------------- // getProperty //--------------------------------------------------------- QVariant LineSegment::getProperty(P_ID id) const { switch (id) { case P_ID::DIAGONAL: case P_ID::LINE_COLOR: case P_ID::LINE_WIDTH: case P_ID::LINE_STYLE: return line()->getProperty(id); default: return SpannerSegment::getProperty(id); } } //--------------------------------------------------------- // setProperty //--------------------------------------------------------- bool LineSegment::setProperty(P_ID id, const QVariant& val) { switch (id) { case P_ID::DIAGONAL: case P_ID::LINE_COLOR: case P_ID::LINE_WIDTH: case P_ID::LINE_STYLE: return line()->setProperty(id, val); default: return SpannerSegment::setProperty(id, val); } } //--------------------------------------------------------- // propertyDefault //--------------------------------------------------------- QVariant LineSegment::propertyDefault(P_ID id) const { return line()->propertyDefault(id); } //--------------------------------------------------------- // dragAnchor //--------------------------------------------------------- QLineF LineSegment::dragAnchor() const { if (spannerSegmentType() != SpannerSegmentType::SINGLE && spannerSegmentType() != SpannerSegmentType::BEGIN) return QLineF(); System* s; QPointF p = line()->linePos(Grip::START, &s); p += QPointF(s->canvasPos().x(), s->staffYpage(line()->staffIdx())); return QLineF(p, canvasPos()); } //--------------------------------------------------------- // SLine //--------------------------------------------------------- SLine::SLine(Score* s) : Spanner(s) { _diagonal = false; _lineColor = MScore::defaultColor; _lineWidth = Spatium(0.15); _lineStyle = Qt::SolidLine; setTrack(0); } SLine::SLine(const SLine& s) : Spanner(s) { _diagonal = s._diagonal; _lineWidth = s._lineWidth; _lineColor = s._lineColor; _lineStyle = s._lineStyle; } //--------------------------------------------------------- // linePos // return System/Staff coordinates //--------------------------------------------------------- QPointF SLine::linePos(Grip grip, System** sys) const { qreal x = 0.0; qreal sp = staff()->spatium(); switch (anchor()) { case Spanner::Anchor::SEGMENT: { ChordRest* cr; if (grip == Grip::START) { cr = static_cast(startElement()); if (cr) { // some sources say to center the text over the note head // some say to start the text just to left of notehead // our simple compromise - left align if (cr->durationType() == TDuration::DurationType::V_MEASURE && type() == Element::Type::OTTAVA) x = cr->x(); // center for measure rests else if (cr->space().lw() > 0.0) x = -cr->space().lw(); // account for accidentals, etc } } else { cr = static_cast(endElement()); if (type() == Element::Type::OTTAVA) { if (cr && cr->durationType() == TDuration::DurationType::V_MEASURE) { x = cr->x() + cr->width() + sp; } else if (cr) { // lay out just past right edge of all notes for this segment on this staff // note: the cheap solution is to simply use segment width, // but that would account for notes in other staves unnecessarily // and in any event, segment bboxes seem unreliable qreal width = 0; Segment* s = cr->segment(); int n = staffIdx() * VOICES; for (int i = 0; i < VOICES; ++i) { ChordRest* vcr = static_cast(s->element(n + i)); if (vcr) width = qMax(width, vcr->space().rw()); } // extend past chord/rest x = width + sp; // but don't overlap next chord/rest Segment* ns = s->next(); bool crFound = false; while (ns) { for (int i = 0; i < VOICES; ++i) { if (ns->element(n + i)) { crFound = true; break; } } if (crFound) break; ns = ns->next(); } if (crFound) { qreal nextNoteDistance = ns->x() - s->x() + lineWidth().val() * sp; if (x > nextNoteDistance) x = qMax(width, nextNoteDistance); } } } else if (type() == Element::Type::LYRICSLINE && static_cast(parent())->ticks() > 0) { // melisma line // it is possible CR won't be in correct track // prefer element in current track if available if (!cr) qDebug("no end for lyricsline segment - start %d, ticks %d", tick(), ticks()); else if (cr->track() != track()) { Element* e = cr->segment()->element(track()); if (e) cr = static_cast(e); } // layout to right edge of CR if (cr) { qreal maxRight = 0.0; if (cr->type() == Element::Type::CHORD) { // chord bbox() is unreliable, look at notes // this also allows us to more easily ignore ledger lines for (Note* n : static_cast(cr)->notes()) maxRight = qMax(maxRight, cr->x() + n->x() + n->headWidth()); } else { // rest - won't normally happen maxRight = cr->x() + cr->width(); } x = maxRight; // cr->width() } } else if (type() == Element::Type::HAIRPIN || type() == Element::Type::TRILL || type() == Element::Type::TEXTLINE || type() == Element::Type::LYRICSLINE) { // (for LYRICSLINE, this is hyphen; melisma line is handled above) // lay out to just before next chordrest on this staff, or barline // tick2 actually tells us the right chordrest to look for if (cr && endElement()->parent() && endElement()->parent()->type() == Element::Type::SEGMENT) { qreal x2 = cr->x() + cr->space().rw(); Segment* currentSeg = static_cast(endElement()->parent()); Segment* seg = score()->tick2segmentMM(tick2(), false, Segment::Type::ChordRest); if (!seg) { // no end segment found, use measure width x2 = endElement()->parent()->parent()->width() - sp; } else if (currentSeg->measure() == seg->measure()) { // next chordrest found in same measure; // end line 1sp to left x2 = qMax(x2, seg->x() - sp); } else { // next chordrest is in next measure // lay out to end (barline) of current measure instead seg = currentSeg->next(Segment::Type::EndBarLine); if (!seg) seg = currentSeg->measure()->last(); // allow lyrics hyphen to extend to barline // other lines stop 1sp short qreal gap = (type() == Element::Type::LYRICSLINE) ? 0.0 : sp; x2 = qMax(x2, seg->x() - gap); } x = x2 - endElement()->parent()->x(); } } } int t = grip == Grip::START ? tick() : tick2(); Measure* m = cr ? cr->measure() : score()->tick2measure(t); if (m) { x += cr ? cr->segment()->pos().x() + m->pos().x() : m->tick2pos(t); *sys = m->system(); } else *sys = 0; } break; case Spanner::Anchor::MEASURE: { // anchor() == Anchor::MEASURE const Measure* m; if (grip == Grip::START) { m = startMeasure(); // start after clef/key qreal offset = 0.0; Segment* s = m->first(Segment::Type::ChordRest); if (s) { s = s->prev(); if (s) { offset = s->x(); Element* e = s->element(staffIdx() * VOICES); if (e) offset += e->width(); } } x = m->pos().x() + offset; if (score()->styleB(StyleIdx::createMultiMeasureRests) && m->hasMMRest()) { x = m->mmRest()->pos().x(); } } else { qreal _spatium = spatium(); m = endMeasure(); x = m->pos().x() + m->bbox().right(); if (score()->styleB(StyleIdx::createMultiMeasureRests)) { //find the actual measure where the volta should stop Measure* sm = startMeasure(); Measure* m = sm; if (sm->hasMMRest()) m = sm->mmRest(); while (m->nextMeasureMM() && (m->endTick() < tick2())) m = m->nextMeasureMM(); x = m->pos().x() + m->bbox().right(); } Segment* seg = m->last(); if (seg->segmentType() == Segment::Type::EndBarLine) { Element* e = seg->element(0); if (e && e->type() == Element::Type::BAR_LINE) { if (static_cast(e)->barLineType() == BarLineType::START_REPEAT) x -= e->width() - _spatium * .5; else x -= _spatium * .5; } } } if (score()->styleB(StyleIdx::createMultiMeasureRests)) m = m->mmRest1(); Q_ASSERT(m->system()); *sys = m->system(); } break; case Spanner::Anchor::NOTE: { // System* s = static_cast(startElement())->chord()->segment()->system(); // *sys = s; Element* e = grip == Grip::START ? startElement() : endElement(); System* s = static_cast(e)->chord()->segment()->system(); *sys = s; // for GLISSANDO returns the position of the anchor note relative to the system // for others, returns the position of the anchor note relative to the staff // QPointF elemPagePos = e->pagePos(); // DEBUG // QPointF systPagePos = s->pagePos(); // qreal staffYPage = s->staffYpage(e->staffIdx()); return e->pagePos() - (type() == Element::Type::GLISSANDO ? s->pagePos() : QPointF(s->pagePos().x(), s->staffYpage(e->staffIdx())) ); } case Spanner::Anchor::CHORD: qFatal("Sline::linePos(): anchor not implemented"); break; } return QPointF(x, 0.0); } //--------------------------------------------------------- // layout // compute segments from tick1 tick2 //--------------------------------------------------------- void SLine::layout() { if (score() == gscore || tick() == -1 || tick2() == 1) { // // when used in a palette or while dragging from palette, // SLine has no parent and // tick and tick2 has no meaning so no layout is // possible and needed // if (!spannerSegments().isEmpty()) { LineSegment* lineSegm = frontSegment(); lineSegm->layout(); setbbox(lineSegm->bbox()); } return; } computeStartElement(); computeEndElement(); System* s1; System* s2; QPointF p1(linePos(Grip::START, &s1)); QPointF p2(linePos(Grip::END, &s2)); QList* systems = score()->systems(); int sysIdx1 = systems->indexOf(s1); int sysIdx2 = systems->indexOf(s2); int segmentsNeeded = 0; if (sysIdx1 == -1 || sysIdx2 == -1) return; for (int i = sysIdx1; i < sysIdx2+1; ++i) { if (systems->at(i)->isVbox()) continue; ++segmentsNeeded; } int segCount = spannerSegments().size(); if (segmentsNeeded != segCount) { if (segmentsNeeded > segCount) { int n = segmentsNeeded - segCount; for (int i = 0; i < n; ++i) { LineSegment* lineSegm = createLineSegment(); add(lineSegm); // set user offset to previous segment's offset if (segCount > 0) lineSegm->setUserOff(QPointF(0, segmentAt(segCount+i-1)->userOff().y())); else lineSegm->setUserOff(QPointF(0, userOff().y())); } } else { int n = segCount - segmentsNeeded; // qDebug("SLine: segments %d needed %d, remove %d", segCount, segmentsNeeded, n); for (int i = 0; i < n; ++i) { if (spannerSegments().isEmpty()) { qDebug("SLine::layout(): no segment %d, %d expected", i, n); break; } else { /*LineSegment* lineSegm =*/ takeLastSegment(); // delete lineSegm; } } } } int segIdx = 0; for (int i = sysIdx1; i <= sysIdx2; ++i) { System* system = systems->at(i); if (system->isVbox()) continue; LineSegment* lineSegm = segmentAt(segIdx++); lineSegm->setTrack(track()); // DEBUG lineSegm->setSystem(system); Measure* firstMeas = system->firstMeasure(); Segment* firstCRSeg = firstMeas->first(Segment::Type::ChordRest); if (sysIdx1 == sysIdx2) { // single segment lineSegm->setSpannerSegmentType(SpannerSegmentType::SINGLE); qreal len = p2.x() - p1.x(); // enforcing a minimum length would be possible but inadvisable // the line length calculations are tuned well enough that this should not be needed //if (anchor() == Anchor::SEGMENT && type() != Element::Type::PEDAL) // len = qMax(1.0 * spatium(), len); lineSegm->setPos(p1); lineSegm->setPos2(QPointF(len, p2.y() - p1.y())); } else if (i == sysIdx1) { // start segment lineSegm->setSpannerSegmentType(SpannerSegmentType::BEGIN); lineSegm->setPos(p1); qreal x2 = system->bbox().right(); lineSegm->setPos2(QPointF(x2 - p1.x(), 0.0)); } else if (i > 0 && i != sysIdx2) { // middle segment lineSegm->setSpannerSegmentType(SpannerSegmentType::MIDDLE); qreal x1 = (firstCRSeg ? firstCRSeg->pos().x() : 0) + firstMeas->pos().x(); qreal x2 = system->bbox().right(); lineSegm->setPos(QPointF(x1, p1.y())); lineSegm->setPos2(QPointF(x2 - x1, 0.0)); } else if (i == sysIdx2) { // end segment qreal offset = 0.0; qreal minLen = 0.0; if (anchor() == Anchor::SEGMENT || anchor() == Anchor::MEASURE) { // start line just after previous element (eg, key signature) firstCRSeg = firstCRSeg->prev(); Element* e = firstCRSeg ? firstCRSeg->element(staffIdx() * VOICES) : nullptr; if (e) offset = e->width(); // enforcing a minimum length would be possible but inadvisable // the line length calculations are tuned well enough that this should not be needed //if (type() != Element::Type::PEDAL) // minLen = 1.0 * spatium(); } // qreal firstCRSegX = firstCRSeg ? firstCRSeg->pos().x() : 0; // DEBUG // qreal firstMeasX = firstMeas ? firstMeas->pos().x() : 0; qreal x1 = (firstCRSeg ? firstCRSeg->pos().x() : 0) + firstMeas->pos().x() + offset; qreal len = qMax(minLen, p2.x() - x1); lineSegm->setSpannerSegmentType(SpannerSegmentType::END); lineSegm->setPos(QPointF(p2.x() - len, p2.y())); lineSegm->setPos2(QPointF(len, 0.0)); } lineSegm->layout(); } adjustReadPos(); } //--------------------------------------------------------- // writeProperties // write properties different from prototype //--------------------------------------------------------- void SLine::writeProperties(Xml& xml) const { if (!endElement()) { xml.tag("ticks", ticks()); } Spanner::writeProperties(xml); if (_diagonal) xml.tag("diagonal", _diagonal); if (propertyStyle(P_ID::LINE_WIDTH) != PropertyStyle::STYLED) xml.tag("lineWidth", lineWidth().val()); if (propertyStyle(P_ID::LINE_STYLE) == PropertyStyle::UNSTYLED || (lineStyle() != Qt::SolidLine)) if (propertyStyle(P_ID::LINE_STYLE) != PropertyStyle::STYLED) xml.tag("lineStyle", int(lineStyle())); if (propertyStyle(P_ID::LINE_COLOR) == PropertyStyle::UNSTYLED || (lineColor() != MScore::defaultColor)) xml.tag("lineColor", lineColor()); writeProperty(xml, P_ID::ANCHOR); if (score() == gscore) { // when used as icon if (!spannerSegments().isEmpty()) { LineSegment* s = frontSegment(); xml.tag("length", s->pos2().x()); } else xml.tag("length", spatium() * 4); return; } // // check if user has modified the default layout // bool modified = false; int n = spannerSegments().size(); for (int i = 0; i < n; ++i) { const LineSegment* seg = segmentAt(i); if (!seg->userOff().isNull() || !seg->userOff2().isNull() || !seg->visible()) { modified = true; break; } } if (!modified) return; // // write user modified layout // qreal _spatium = spatium(); for (int i = 0; i < n; ++i) { const LineSegment* seg = segmentAt(i); xml.stag("Segment"); xml.tag("subtype", int(seg->spannerSegmentType())); xml.tag("off2", seg->userOff2() / _spatium); seg->Element::writeProperties(xml); xml.etag(); } } //--------------------------------------------------------- // readProperties //--------------------------------------------------------- bool SLine::readProperties(XmlReader& e) { const QStringRef& tag(e.name()); if (tag == "tick2") { // obsolete if (tick() == -1) // not necessarily set (for first note of score?) #30151 setTick(e.tick()); setTick2(e.readInt()); } else if (tag == "tick") // obsolete setTick(e.readInt()); else if (tag == "ticks") setTicks(e.readInt()); else if (tag == "Segment") { LineSegment* ls = createLineSegment(); ls->setTrack(track()); // needed in read to get the right staff mag ls->read(e); add(ls); // in v1.x "visible" is a property of the segment only; // we must ensure that it propagates also to the parent element. // That's why the visibility is set after adding the segment // to the corresponding spanner if (score()->mscVersion() <= 114) ls->setVisible(ls->visible()); else ls->setVisible(visible()); } else if (tag == "length") setLen(e.readDouble()); else if (tag == "diagonal") setDiagonal(e.readInt()); else if (tag == "anchor") setAnchor(Anchor(e.readInt())); else if (tag == "lineWidth") _lineWidth = Spatium(e.readDouble()); else if (tag == "lineStyle") _lineStyle = Qt::PenStyle(e.readInt()); else if (tag == "lineColor") _lineColor = e.readColor(); else if (Element::readProperties(e)) ; else return false; return true; } //--------------------------------------------------------- // setLen // used to create an element suitable for palette //--------------------------------------------------------- void SLine::setLen(qreal l) { if (spannerSegments().isEmpty()) add(createLineSegment()); LineSegment* s = frontSegment(); s->setPos(QPointF()); s->setPos2(QPointF(l, 0)); } //--------------------------------------------------------- // bbox // used by palette: only one segment //--------------------------------------------------------- const QRectF& SLine::bbox() const { if (spannerSegments().isEmpty()) setbbox(QRectF()); else setbbox(segmentAt(0)->bbox()); return Element::bbox(); } //--------------------------------------------------------- // write //--------------------------------------------------------- void SLine::write(Xml& xml) const { int id = xml.spannerId(this); xml.stag(QString("%1 id=\"%2\"").arg(name()).arg(id)); SLine::writeProperties(xml); xml.etag(); } //--------------------------------------------------------- // read //--------------------------------------------------------- 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)) e.unknown(); } } //--------------------------------------------------------- // getProperty //--------------------------------------------------------- QVariant SLine::getProperty(P_ID id) const { switch (id) { case P_ID::DIAGONAL: return _diagonal; case P_ID::LINE_COLOR: return _lineColor; case P_ID::LINE_WIDTH: return _lineWidth.val(); case P_ID::LINE_STYLE: return QVariant(int(_lineStyle)); default: return Spanner::getProperty(id); } } //--------------------------------------------------------- // setProperty //--------------------------------------------------------- bool SLine::setProperty(P_ID id, const QVariant& v) { switch (id) { case P_ID::DIAGONAL: _diagonal = v.toBool(); break; case P_ID::LINE_COLOR: _lineColor = v.value(); break; case P_ID::LINE_WIDTH: _lineWidth = Spatium(v.toDouble()); break; case P_ID::LINE_STYLE: _lineStyle = Qt::PenStyle(v.toInt()); break; default: return Spanner::setProperty(id, v); } return true; } //--------------------------------------------------------- // propertyDefault //--------------------------------------------------------- QVariant SLine::propertyDefault(P_ID id) const { switch (id) { case P_ID::DIAGONAL: return false; case P_ID::LINE_COLOR: return MScore::defaultColor; case P_ID::LINE_WIDTH: return 0.15; case P_ID::LINE_STYLE: return int(Qt::SolidLine); default: return Spanner::propertyDefault(id); } } }