    //=============================================================================
    //  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 "note.h"
    #include "part.h"
    #include "score.h"
    #include "segment.h"
    #include "staff.h"
    #include "sym.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;
          }

    //---------------------------------------------------------
    //   startEdit
    //---------------------------------------------------------

    void LineSegment::startEdit(EditData& ed)
          {
          ed.grips   = 3;
          ed.curGrip = Grip::END;
          Element::startEdit(ed);
          }

    //---------------------------------------------------------
    //   readProperties
    //---------------------------------------------------------

    bool LineSegment::readProperties(XmlReader& e)
          {
          const QStringRef& tag(e.name());
          if (tag == "subtype")
                setSpannerSegmentType(SpannerSegmentType(e.readInt()));
          else if (tag == "off2") {
                setUserOff2(e.readPoint() * spatium());
                if (!userOff2().isNull())
                      setAutoplace(false);
                }
          else if (tag == "pos") {
                qreal _spatium = score()->spatium();
                setUserOff(QPointF());
                setReadPos(e.readPoint() * _spatium);
                if (e.pasteMode())      // x position will be wrong
                      setReadPos(QPointF());
                setAutoplace(false);
                }
          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(EditData& ed) const
          {
          QPointF pp(pagePos());
          ed.grip[int(Grip::START)].translate(pp);
          ed.grip[int(Grip::END)].translate(pos2() + pp);
          ed.grip[int(Grip::MIDDLE)].translate(pos2() * .5 + pp);
          }

    //---------------------------------------------------------
    //   gripAnchor
    //    return page coordinates
    //---------------------------------------------------------

    QPointF LineSegment::gripAnchor(Grip grip) const
          {
          // Middle or aperture grip have no anchor
          if (!system() || grip == Grip::MIDDLE || grip == Grip::APERTURE)
                return QPointF(0, 0);
          // note-anchored spanners are relative to the system
          qreal y = spanner()->anchor() == Spanner::Anchor::NOTE ?
                      system()->pos().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:
                            x = 0; // No Anchor
                            y = 0;
                            break;
                      }
                return QPointF(x, y);
                }
          else {
                if ((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();    // to page coordinates
                      return p;
                      }
                }
          }

    //---------------------------------------------------------
    //   startEditDrag
    //---------------------------------------------------------

    void LineSegment::startEditDrag(EditData& ed)
          {
          ElementEditData* eed = ed.getData(this);
          eed->pushProperty(P_ID::USER_OFF);
          eed->pushProperty(P_ID::USER_OFF2);
          }

    //---------------------------------------------------------
    //   edit
    //    return true if event is accepted
    //---------------------------------------------------------

    bool LineSegment::edit(EditData& ed)
          {
          if (!((ed.modifiers & Qt::ShiftModifier)
             && ((spannerSegmentType() == SpannerSegmentType::SINGLE)
                  || (spannerSegmentType() == SpannerSegmentType::BEGIN && ed.curGrip == Grip::START)
                  || (spannerSegmentType() == SpannerSegmentType::END && ed.curGrip == Grip::END))))
                return false;

          LineSegment* ls       = 0;
          SpannerSegmentType st = spannerSegmentType(); // may change later
          SLine* l              = line();
          int track             = l->track();
          int track2            = l->track2();    // assumed to be same as track

          switch (l->anchor()) {
                case 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 (ed.key == Qt::Key_Left) {
                            if (ed.curGrip == Grip::START)
                                  s1 = prevSeg1(s1, track);
                            else if (ed.curGrip == Grip::END || ed.curGrip == Grip::MIDDLE)
                                  s2 = prevSeg1(s2, track2);
                            }
                      else if (ed.key == Qt::Key_Right) {
                            if (ed.curGrip == Grip::START)
                                  s1 = nextSeg1(s1, track);
                            else if (ed.curGrip == Grip::END || ed.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;
                      spanner()->undoChangeProperty(P_ID::SPANNER_TICK, s1->tick());
                      spanner()->undoChangeProperty(P_ID::SPANNER_TICKS, s2->tick() - s1->tick());
                      }
                      break;
                case Spanner::Anchor::NOTE:
                      {
                      Note* note1       = toNote(l->startElement());
                      Note* note2       = toNote(l->endElement());
                      Note* oldNote1    = note1;
                      Note* oldNote2    = note2;
                      if (!note1 && !note2) {
                            qDebug("LineSegment::edit: no start/end note");
                            return true;            // accept the event without doing anything
                            }

                      switch (ed.key) {
                            case Qt::Key_Left:
                                  if (ed.curGrip == Grip::START)
                                        note1 = prevChordNote(note1);
                                  else if (ed.curGrip == Grip::END || ed.curGrip == Grip::MIDDLE)
                                        note2 = prevChordNote(note2);
                                  break;
                            case Qt::Key_Right:
                                  if (ed.curGrip == Grip::START)
                                        note1 = nextChordNote(note1);
                                  else if (ed.curGrip == Grip::END || ed.curGrip == Grip::MIDDLE)
                                        note2 = nextChordNote(note2);
                                  break;
                            case Qt::Key_Up:
                                  if (ed.curGrip == Grip::START)
                                        note1 = toNote(score()->upAlt(note1));
                                  else if (ed.curGrip == Grip::END || ed.curGrip == Grip::MIDDLE)
                                        note2 = toNote(score()->upAlt(note2));
                                  break;
                            case Qt::Key_Down:
                                  if (ed.curGrip == Grip::START)
                                        note1 = toNote(score()->downAlt(note1));
                                  else if (ed.curGrip == Grip::END || ed.curGrip == Grip::MIDDLE)
                                        note2 = toNote(score()->downAlt(note2));
                                  break;
                            default:
                                  return true;
                            }

                      // check prevChordNote() and nextchordNote() didn't return null
                      // OR Score::upAlt() and Score::downAlt() didn't return non-Note (notably rests)
                      // OR spanner duration is > 0
                      // OR note1 and note2 didn't end up in different instruments
                      // if this is the case, accepts the event and return without doing nothing
                      if (!note1 || !note2
                         || !note1->isNote() || !note2->isNote()
                         || note1->chord()->tick() >= note2->chord()->tick()
                         || note1->chord()->staff()->part()->instrument(note1->chord()->tick())
                         != note2->chord()->staff()->part()->instrument(note2->chord()->tick()) )
                            return true;
                      if (note1 != oldNote1 || note2 != oldNote2)
                            spanner()->setNoteSpan(note1, note2);          // set new spanner span
                      }
                      break;
                default:
                      {
                      Measure* m1 = l->startMeasure();
                      Measure* m2 = l->endMeasure();

                      if (ed.key == Qt::Key_Left) {
                            if (ed.curGrip == Grip::START) {
                                  if (m1->prevMeasure())
                                        m1 = m1->prevMeasure();
                                  }
                            else if (ed.curGrip == Grip::END || ed.curGrip == Grip::MIDDLE) {
                                  Measure* m = m2->prevMeasure();
                                  if (m)
                                        m2 = m;
                                  }
                            }
                      else if (ed.key == Qt::Key_Right) {
                            if (ed.curGrip == Grip::START) {
                                  if (m1->nextMeasure())
                                        m1 = m1->nextMeasure();
                                  }
                            else if (ed.curGrip == Grip::END || ed.curGrip == Grip::MIDDLE) {
                                  if (m2->nextMeasure())
                                        m2 = m2->nextMeasure();
                                  }
                            }
                      if (m1->tick() > m2->tick())
                            return true;
                      if (l->startElement() != m1) {
                            spanner()->undoChangeProperty(P_ID::SPANNER_TICK,  m1->tick());
                            spanner()->undoChangeProperty(P_ID::SPANNER_TICKS, m2->endTick() - m1->tick());
                            }
                      else if (l->endElement() != m2) {
                            spanner()->undoChangeProperty(P_ID::SPANNER_TICKS, m2->endTick() - m1->tick());
                            }
                      }
                }
          triggerLayout();
          l->layout();            // recompute segment list, segment type may change

          LineSegment* nls = 0;
          if (st == SpannerSegmentType::SINGLE) {
                if (ed.curGrip == Grip::START)
                      nls = l->frontSegment();
                else if (ed.curGrip == Grip::END)
                      nls = l->backSegment();
                }
          else if (st == SpannerSegmentType::BEGIN)
                nls = l->frontSegment();
          else if (st == SpannerSegmentType::END)
                nls = l->backSegment();
          else
                qDebug("spannerSegmentType %d", int(spannerSegmentType()));

          if (nls && (nls != this))
                ed.view->changeEditElement(nls);
          if (ls)
                score()->undoRemoveElement(ls);

          triggerLayout();
          return true;
          }

    //---------------------------------------------------------
    //   editDrag
    //---------------------------------------------------------

    void LineSegment::editDrag(EditData& ed)
          {
          // Only for resizing according to the diagonal properties
          QPointF deltaResize(ed.delta.x(), line()->diagonal() ? ed.delta.y() : 0.0);

          switch (ed.curGrip) {
                case Grip::START: // Resize the begin of element (left grip)
                      setUserOff(userOff() + deltaResize);
                      _userOff2 -= deltaResize;
                      undoChangeProperty(P_ID::AUTOPLACE, false);
                      break;
                case Grip::END: // Resize the end of element (rigth grip)
                      _userOff2 += deltaResize;
                      undoChangeProperty(P_ID::AUTOPLACE, false);
                      break;
                case Grip::MIDDLE: { // Move the element (middle grip)
                      // Only for moving, no y limitaion
                      QPointF deltaMove(ed.delta.x(), ed.delta.y());
                      setUserOff(userOff() + deltaMove);
                      undoChangeProperty(P_ID::AUTOPLACE, false);
                      }
                      break;
                default:
                      break;
                }
          if (line()->anchor() == Spanner::Anchor::NOTE && ed.isStartEndGrip()) {
                //
                // if we touch a different note, change anchor
                //
                Element* e = ed.view->elementNear(ed.pos);
                if (e && e->isNote()) {
                      SLine* l = line();
                      if (ed.curGrip == Grip::END && e != line()->endElement()) {
                            qDebug("LineSegment: move end anchor");
                            Note* noteOld = toNote(l->endElement());
                            Note* noteNew = toNote(e);
                            Note* sNote   = toNote(l->startElement());
                            // do not change anchor if new note is before start note
                            if (sNote && sNote->chord() && noteNew->chord() && sNote->chord()->tick() < noteNew->chord()->tick()) {
                                  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.)");
                      }
                }
          triggerLayout();
          }

    //---------------------------------------------------------
    //   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:
                case P_ID::DASH_LINE_LEN:
                case P_ID::DASH_GAP_LEN:
                      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:
                case P_ID::DASH_LINE_LEN:
                case P_ID::DASH_GAP_LEN:
                      return line()->setProperty(id, val);
                default:
                      return SpannerSegment::setProperty(id, val);
                }
          }

    //---------------------------------------------------------
    //   propertyDefault
    //---------------------------------------------------------

    QVariant LineSegment::propertyDefault(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:
                case P_ID::DASH_LINE_LEN:
                case P_ID::DASH_GAP_LEN:
                      return line()->propertyDefault(id);
                default:
                      return SpannerSegment::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->staffCanvasYpage(line()->staffIdx()));

          return QLineF(p, canvasPos());
          }

    //---------------------------------------------------------
    //   SLine
    //---------------------------------------------------------

    SLine::SLine(Score* s)
       : Spanner(s)
          {
          setTrack(0);
          }

    SLine::SLine(const SLine& s)
       : Spanner(s)
          {
          _diagonal    = s._diagonal;
          _lineWidth   = s._lineWidth;
          _lineColor   = s._lineColor;
          _lineStyle   = s._lineStyle;
          _dashLineLen = s._dashLineLen;
          _dashGapLen  = s._dashGapLen;
          }

    //---------------------------------------------------------
    //   linePos
    //    Anchor::NOTE: return anchor note position in system coordinates
    //    Other:        return (x position (relative to what?), 0)
    //---------------------------------------------------------

    QPointF SLine::linePos(Grip grip, System** sys) const
          {
          qreal x = 0.0;
          qreal sp = staff()->spatium(tick());
          switch (anchor()) {
                case Spanner::Anchor::SEGMENT:
                      {
                      ChordRest* cr;
                      if (grip == Grip::START) {
                            cr = static_cast<ChordRest*>(startElement());
                            if (cr && type() == ElementType::OTTAVA) {
                                  // some sources say to center the text over the notehead
                                  // others say to start the text just to left of notehead
                                  // some say to include accidental, others don't
                                  // our compromise - left align, but account for accidental
                                  if (cr->durationType() == TDuration::DurationType::V_MEASURE && !cr->measure()->hasVoices(cr->staffIdx()))
                                        x = cr->x();            // center for measure rests
    //TODO                              else if (cr->spaceLw > 0.0)
    //                                    x = -cr->spaceLw;  // account for accidentals, etc
                                  }
                            }
                      else {
                            cr = static_cast<ChordRest*>(endElement());
                            if (type() == ElementType::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

                                        Segment* s = cr->segment();
                                        qreal width = s->staffShape(staffIdx()).right();
                                        x = width + sp;

                                        // extend past chord/rest
                                        // but don't overlap next chord/rest

                                        bool crFound = false;
                                        int n = staffIdx() * VOICES;
                                        Segment* ns = s->next();
                                        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() == ElementType::LYRICSLINE && static_cast<Lyrics*>(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<ChordRest*>(e);
                                        }
                                  // layout to right edge of CR
                                  if (cr) {
                                        qreal maxRight = 0.0;
                                        if (cr->type() == ElementType::CHORD) {
                                              // chord bbox() is unreliable, look at notes
                                              // this also allows us to more easily ignore ledger lines
                                              for (Note* n : static_cast<Chord*>(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() == ElementType::HAIRPIN || type() == ElementType::TRILL
                                        || type() == ElementType::TEXTLINE || type() == ElementType::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() == ElementType::SEGMENT) {
                                        qreal x2 = cr->x() /* TODO + cr->space().rw() */;
                                        Segment* currentSeg = static_cast<Segment*>(endElement()->parent());
                                        Segment* seg = score()->tick2segmentMM(tick2(), false, SegmentType::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(SegmentType::EndBarLine);
                                              if (!seg)
                                                    seg = currentSeg->measure()->last();
                                              // allow lyrics hyphen to extend to barline
                                              // other lines stop 1sp short
                                              qreal gap = (type() == ElementType::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(SegmentType::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();

                            if (score()->styleB(StyleIdx::createMultiMeasureRests)) {
                                  // find the actual measure where the volta should stop
                                  m = startMeasure();
                                  if (m->hasMMRest())
                                        m = m->mmRest();
                                  while (m->nextMeasureMM() && (m->endTick() < tick2()))
                                        m = m->nextMeasureMM();
                                  }
                            else {
                                  m = endMeasure();
                                  }
                            // back up to barline (skip courtesy elements)
                            Segment* seg = m->last();
                            while (seg && seg->segmentType() != SegmentType::EndBarLine)
                                  seg = seg->prev();
                            qreal mwidth = seg ? seg->x() : m->bbox().right();
                            x = m->pos().x() + mwidth;
                            // align to barline
                            if (seg && seg->segmentType() == SegmentType::EndBarLine) {
                                  Element* e = seg->element(0);
                                  if (e && e->type() == ElementType::BAR_LINE) {
                                        BarLineType blt = static_cast<BarLine*>(e)->barLineType();
                                        switch (blt) {
                                              case BarLineType::END_REPEAT:
                                                    // skip dots
                                                    x += symWidth(SymId::repeatDot);
                                                    x += score()->styleS(StyleIdx::endBarDistance).val() * _spatium;
                                                    // fall through
                                              case BarLineType::DOUBLE:
                                                    // center on leftmost (thinner) barline
                                                    x += score()->styleS(StyleIdx::doubleBarWidth).val() * _spatium * 0.5;
                                                    break;
                                              case BarLineType::START_REPEAT:
                                                    // center on leftmost (thicker) barline
                                                    x += score()->styleS(StyleIdx::endBarWidth).val() * _spatium * 0.5;
                                                    break;
                                              default:
                                                    // center on barline
                                                    x += score()->styleS(StyleIdx::barWidth).val() * _spatium * 0.5;
                                                    break;
                                              }
                                        }
                                  }
                            }
                      if (score()->styleB(StyleIdx::createMultiMeasureRests))
                            m = m->mmRest1();
                      Q_ASSERT(m->system());
                      *sys = m->system();
                      }
                      break;

                case Spanner::Anchor::NOTE:
                      {
                      Element* e = grip == Grip::START ? startElement() : endElement();
                      if (!e)
                            return QPointF();
                      System* s = static_cast<Note*>(e)->chord()->segment()->system();
                      *sys = s;
                      // return the position of the anchor note relative to the system
    //                  QPointF     elemPagePos = e->pagePos();                   // DEBUG
    //                  QPointF     systPagePos = s->pagePos();
    //                  qreal       staffYPage  = s->staffYpage(e->staffIdx());
                      return e->pagePos() - s->pagePos();
                      }

                case Spanner::Anchor::CHORD:
                      qFatal("Sline::linePos(): anchor not implemented");
                      break;
                }
          return QPointF(x, 0.0);
          }

    //---------------------------------------------------------
    //   layoutSystem
    //    layout spannersegment for system
    //---------------------------------------------------------

    SpannerSegment* SLine::layoutSystem(System* system)
          {
          int stick = system->firstMeasure()->tick();
          int etick = system->lastMeasure()->endTick();

          LineSegment* lineSegm = 0;
          for (SpannerSegment* ss : segments) {
                if (!ss->system()) {
                      lineSegm = static_cast<LineSegment*>(ss);
                      break;
                      }
                }
          if (!lineSegm) {
                lineSegm = createLineSegment();
                add(lineSegm);
                }
          lineSegm->setSystem(system);
          lineSegm->setSpanner(this);

          SpannerSegmentType sst;
          if (tick() >= stick) {
                //
                // this is the first call to layoutSystem,
                // processing the first line segment
                //
                computeStartElement();
                computeEndElement();
                sst = tick2() <= etick ? SpannerSegmentType::SINGLE : SpannerSegmentType::BEGIN;
                }
          else if (tick() < stick && tick2() > etick) {
                sst = SpannerSegmentType::MIDDLE;
                }
          else {
                //
                // this is the last call to layoutSystem
                // processing the last line segment
                //
                sst = SpannerSegmentType::END;
                }
          lineSegm->setSpannerSegmentType(sst);

          switch (sst) {
                case SpannerSegmentType::SINGLE: {
                      System* s;
                      QPointF p1 = linePos(Grip::START, &s);
                      QPointF p2 = linePos(Grip::END,   &s);
                      qreal len = p2.x() - p1.x();
                      lineSegm->setPos(p1);
                      lineSegm->setPos2(QPointF(len, p2.y() - p1.y()));
                      }
                      break;
                case SpannerSegmentType::BEGIN: {
                      System* s;
                      QPointF p1 = linePos(Grip::START, &s);
                      lineSegm->setPos(p1);
                      qreal x2 = system->bbox().right();
                      lineSegm->setPos2(QPointF(x2 - p1.x(), 0.0));
                      }
                      break;
                case SpannerSegmentType::MIDDLE: {
                      Measure* firstMeasure = system->firstMeasure();
                      Segment* firstCRSeg   = firstMeasure->first(SegmentType::ChordRest);
                      qreal x1              = (firstCRSeg ? firstCRSeg->pos().x() : 0) + firstMeasure->pos().x();
                      qreal x2              = system->bbox().right();
                      System* s;
                      QPointF p1 = linePos(Grip::START, &s);
                      lineSegm->setPos(QPointF(x1, p1.y()));
                      lineSegm->setPos2(QPointF(x2 - x1, 0.0));
                      }
                      break;
                case SpannerSegmentType::END: {
                      qreal offset = 0.0;
                      System* s;
                      QPointF p2 = linePos(Grip::END,   &s);
                      Measure* firstMeas  = system->firstMeasure();
                      Segment* firstCRSeg = firstMeas->first(SegmentType::ChordRest);
                      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();
                            }
                      qreal x1  = (firstCRSeg ? firstCRSeg->pos().x() : 0) + firstMeas->pos().x() + offset;
                      qreal len = p2.x() - x1;
                      lineSegm->setPos(QPointF(p2.x() - len, p2.y()));
                      lineSegm->setPos2(QPointF(len, 0.0));
    #if 1
                      QList<SpannerSegment*> sl;
                      for (SpannerSegment* ss : segments) {
                            if (ss->system())
                                  sl.push_back(ss);
                            else {
                                  qDebug("delete spanner segment %s", ss->name());
                                  score()->selection().remove(ss);
                                  delete ss;
                                  }
                            }
                      segments.swap(sl);
    #endif
                      }
                      break;
                }
          lineSegm->layout();
    #if 0
          QList<SpannerSegment*> sl;
          for (SpannerSegment* ss : segments) {
                if (ss->system())
                      sl.push_back(ss);
                else {
                      qDebug("delete spanner segment %s", ss->name());
                      delete ss;
                      }
                }
          segments.swap(sl);
    #endif
          return lineSegm;
          }

    //---------------------------------------------------------
    //   layout
    //    compute segments from tick1 tick2
    //    (obsolete)
    //---------------------------------------------------------

    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
                //
                setLen(gscore->spatium() * 7);
                if (!spannerSegments().empty()) {
                      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));

          const QList<System*>& 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;  ++i) {
                if (systems.at(i)->vbox())
                      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().empty()) {
                                  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->vbox())
                      continue;
                LineSegment* lineSegm = segmentAt(segIdx++);
                lineSegm->setTrack(track());       // DEBUG
                lineSegm->setSystem(system);

                Measure* firstMeas = system->firstMeasure();
                Segment* firstCRSeg = firstMeas->first(SegmentType::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() != ElementType::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() != ElementType::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(XmlWriter& xml) const
          {
          if (!endElement())
                xml.tag("ticks", ticks());
          Spanner::writeProperties(xml);
          if (_diagonal)
                xml.tag("diagonal", _diagonal);
          writeProperty(xml, P_ID::LINE_WIDTH);
          writeProperty(xml, P_ID::LINE_STYLE);
          writeProperty(xml, P_ID::LINE_COLOR);
          writeProperty(xml, P_ID::ANCHOR);
          writeProperty(xml, P_ID::DASH_LINE_LEN);
          writeProperty(xml, P_ID::DASH_GAP_LEN);
          if (score() == gscore) {
                // when used as icon
                if (!spannerSegments().empty()) {
                      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;
          for (const SpannerSegment* seg : spannerSegments()) {
                if (!seg->autoplace() || !seg->visible()) {
                      modified = true;
                      break;
                      }
                }
          if (!modified)
                return;

          //
          // write user modified layout
          //
          qreal _spatium = spatium();
          for (const SpannerSegment* seg : spannerSegments()) {
                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 == "dashLineLength")
                _dashLineLen = e.readDouble();
          else if (tag == "dashGapLength")
                _dashGapLen = e.readDouble();
          else if (tag == "lineColor")
                _lineColor = e.readColor();
          else if (!Element::readProperties(e))
                return false;
          return true;
          }

    //---------------------------------------------------------
    //   setLen
    //    used to create an element suitable for palette
    //---------------------------------------------------------

    void SLine::setLen(qreal l)
          {
          if (spannerSegments().empty())
                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().empty())
                setbbox(QRectF());
          else
                setbbox(segmentAt(0)->bbox());
          return Element::bbox();
          }

    //---------------------------------------------------------
    //   write
    //---------------------------------------------------------

    void SLine::write(XmlWriter& 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;
                case P_ID::LINE_STYLE:
                      return QVariant(int(_lineStyle));
                case P_ID::DASH_LINE_LEN:
                      return dashLineLen();
                case P_ID::DASH_GAP_LEN:
                      return dashGapLen();
                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<QColor>();
                      break;
                case P_ID::LINE_WIDTH:
                      _lineWidth = v.value<Spatium>();
                      break;
                case P_ID::LINE_STYLE:
                      _lineStyle = Qt::PenStyle(v.toInt());
                      break;
                case P_ID::DASH_LINE_LEN:
                      setDashLineLen(v.toDouble());
                      break;
                case P_ID::DASH_GAP_LEN:
                      setDashGapLen(v.toDouble());
                      break;
                default:
                      return Spanner::setProperty(id, v);
                }
          triggerLayout();
          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 Spatium(0.15);
                case P_ID::LINE_STYLE:
                      return int(Qt::SolidLine);
                case P_ID::DASH_LINE_LEN:
                case P_ID::DASH_GAP_LEN:
                      return 5.0;
                default:
                      return Spanner::propertyDefault(id);
                }
          }

    //---------------------------------------------------------
    //   getPropertyStyle
    //---------------------------------------------------------

    StyleIdx SLine::getPropertyStyle(P_ID id) const
          {
          return Spanner::getPropertyStyle(id);
          }

    }

↑ V547 Expression 'ls' is always false.

↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: lineWidthStyle, lineStyleStyle, lineColorStyle.

↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: lineWidthStyle, lineStyleStyle, lineColorStyle.