    //=============================================================================
    //  MuseScore
    //  Music Composition & Notation
    //
    //  Copyright (C) 2010-2011 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 "fret.h"
    #include "measure.h"
    #include "system.h"
    #include "score.h"
    #include "stringdata.h"
    #include "chord.h"
    #include "note.h"
    #include "segment.h"
    #include "mscore.h"
    #include "harmony.h"

    namespace Ms {

    //    parent() is Segment or Box
    //

    //---------------------------------------------------------
    //   FretDiagram
    //---------------------------------------------------------

    FretDiagram::FretDiagram(Score* score)
       : Element(score)
          {
          setFlags(ElementFlag::MOVABLE | ElementFlag::ON_STAFF | ElementFlag::SELECTABLE);
          font.setFamily("FreeSans");
          font.setPointSize(4.0 * mag());
          }

    FretDiagram::FretDiagram(const FretDiagram& f)
       : Element(f)
          {
          _strings    = f._strings;
          _frets      = f._frets;
          _fretOffset = f._fretOffset;
          _maxFrets   = f._maxFrets;
          maxStrings  = f.maxStrings;
          font        = f.font;
          _barre      = f._barre;
          _userMag    = f._userMag;

          if (f._dots) {
                _dots = new char[_strings];
                memcpy(_dots, f._dots, _strings);
                }
          if (f._marker) {
                _marker = new char[_strings];
                memcpy(_marker, f._marker, _strings);
                }
          if (f._fingering) {
                _fingering = new char[_strings];
                memcpy(_fingering, f._fingering, _strings);
                }
          if (f._harmony)
                _harmony = new Harmony(*f._harmony);
          else
                _harmony = 0;
          }

    //---------------------------------------------------------
    //   FretDiagram
    //---------------------------------------------------------

    FretDiagram::~FretDiagram()
          {
          delete[] _dots;
          delete[] _marker;
          delete[] _fingering;
          }

    //---------------------------------------------------------
    //   fromString
    ///  Create diagram from string like "XO-123"
    ///  Always assume barre on the first visible fret
    //---------------------------------------------------------

    FretDiagram* FretDiagram::fromString(Score* score, const QString &s)
          {
          FretDiagram* fd = new FretDiagram(score);
          fd->setStrings(s.size());
          fd->setFrets(4);
          int offset = 0;
          int barreString = -1;
          for (int i = 0; i < s.size(); i++) {
                QChar c = s.at(i);
                if (c == 'X' or c == 'O')
                      fd->setMarker(i, c.unicode());
                else if (c == '-' && barreString == -1) {
                      fd->setBarre(1);
                      barreString = i;
                      }
                else {
                      int fret = c.digitValue();
                      if (fret != -1) {
                            fd->setDot(i, fret);
                            if (fret - 3 > 0 && offset < fret - 3)
                                offset = fret - 3;
                            }
                      }
                }
          if (offset > 0) {
                fd->setOffset(offset);
                for (int i = 0; i < fd->strings(); i++)
                      if (fd->dot(i))
                            fd->setDot(i, fd->dot(i) - offset);
                }
          if (barreString >= 0)
                fd->setDot(barreString, 1);
          return fd;
          }

    //---------------------------------------------------------
    //   pagePos
    //---------------------------------------------------------

    QPointF FretDiagram::pagePos() const
          {
          if (parent() == 0)
                return pos();
          if (parent()->type() == ElementType::SEGMENT) {
                Measure* m = static_cast<Segment*>(parent())->measure();
                System* system = m->system();
                qreal yp = y();
                if (system)
                      yp += system->staffYpage(staffIdx());
                return QPointF(pageX(), yp);
                }
          else
                return Element::pagePos();
          }

    //---------------------------------------------------------
    //   dragAnchor
    //---------------------------------------------------------

    QLineF FretDiagram::dragAnchor() const
          {
          qreal xp = 0.0;
          for (Element* e = parent(); e; e = e->parent())
                xp += e->x();
          qreal yp;
          if (parent()->type() == ElementType::SEGMENT) {
                System* system = static_cast<Segment*>(parent())->measure()->system();
                yp = system->staffCanvasYpage(staffIdx());
                }
          else
                yp = parent()->canvasPos().y();
          QPointF p1(xp, yp);
          return QLineF(p1, canvasPos());
    #if 0 // TODOxx
          if (parent()->type() == ElementType::SEGMENT) {
                Segment* s     = static_cast<Segment*>(parent());
                Measure* m     = s->measure();
                System* system = m->system();
                qreal yp      = system->staff(staffIdx())->y() + system->y();
                qreal xp      = m->tick2pos(s->tick()) + m->pagePos().x();
                QPointF p1(xp, yp);

                qreal x  = 0.0;
                qreal y  = 0.0;
                qreal tw = width();
                qreal th = height();
                if (_align & Align::BOTTOM)
                      y = th;
                else if (_align & Align::VCENTER)
                      y = (th * .5);
                else if (_align & Align::BASELINE)
                      y = baseLine();
                if (_align & Align::RIGHT)
                      x = tw;
                else if (_align & Align::HCENTER)
                      x = (tw * .5);
                return QLineF(p1, abbox().topLeft() + QPointF(x, y));
                }
          return QLineF(parent()->pagePos(), abbox().topLeft());
    #endif
          }

    //---------------------------------------------------------
    //   setOffset
    //---------------------------------------------------------

    void FretDiagram::setOffset(int offset)
          {
          _fretOffset = offset;
          }

    //---------------------------------------------------------
    //   setStrings
    //---------------------------------------------------------

    void FretDiagram::setStrings(int n)
          {
          if (n <= maxStrings) {
                _strings = n;
                return;
                }
          maxStrings = n;
          if (_dots) {
                char* ndots = new char[n];
                memcpy(ndots, _dots, n);
                for (int i = _strings; i < n; ++i)
                      ndots[i] = 0;
                delete[] _dots;
                _dots = ndots;
                }
          if (_marker) {
                char* nmarker = new char[n];
                memcpy(nmarker, _marker, n);
                for (int i = _strings; i < n; ++i)
                      nmarker[i] = 'O';
                delete[] _marker;
                _marker = nmarker;
                }
          if (_fingering) {
                char* nfingering = new char[n];
                memcpy(nfingering, _fingering, n);
                for (int i = _strings; i < n; ++i)
                      nfingering[i] = 0;
                delete[] _fingering;
                _fingering = nfingering;
                }
          _strings = n;
          }

    //---------------------------------------------------------
    //   init
    //---------------------------------------------------------

    void FretDiagram::init(StringData* stringData, Chord* chord)
          {
          if (!stringData)
                setStrings(6);
          else
                setStrings(stringData->strings());
          if (stringData) {
                for (int string = 0; string < _strings; ++string)
                      _marker[string] = 'X';
                foreach(const Note* note, chord->notes()) {
                      int string;
                      int fret;
                      if (stringData->convertPitch(note->pitch(), chord->staff(), chord->segment()->tick(), &string, &fret))
                            setDot(string, fret);
                      }
                _maxFrets = stringData->frets();
                }
          else
                _maxFrets = 6;
          }

    //---------------------------------------------------------
    //   draw
    //---------------------------------------------------------

    void FretDiagram::draw(QPainter* painter) const
          {
          qreal _spatium = spatium() * _userMag * score()->styleD(StyleIdx::fretMag);
          QPen pen(curColor());
          pen.setWidthF(lw2);
          pen.setCapStyle(Qt::FlatCap);
          painter->setPen(pen);
          painter->setBrush(QBrush(QColor(painter->pen().color())));
          qreal x2 = (_strings-1) * stringDist;
          painter->drawLine(QLineF(-lw1 * .5, 0.0, x2 + lw1 * .5, 0.0));

          pen.setWidthF(lw1);
          painter->setPen(pen);
          qreal y2 = (_frets+1) * fretDist - fretDist * .5;
          for (int i = 0; i < _strings; ++i) {
                qreal x = stringDist * i;
                painter->drawLine(QLineF(x, _fretOffset ? -_spatium * .2 : 0.0, x, y2));
                }
          for (int i = 1; i <= _frets; ++i) {
                qreal y = fretDist * i;
                painter->drawLine(QLineF(0.0, y, x2, y));
                }
          QFont scaledFont(font);
          scaledFont.setPointSizeF(font.pointSize() * _userMag * score()->styleD(StyleIdx::fretMag));
          QFontMetricsF fm(scaledFont, MScore::paintDevice());
          scaledFont.setPointSizeF(scaledFont.pointSizeF() * MScore::pixelRatio);

          painter->setFont(scaledFont);
          qreal dotd = stringDist * .6;

          for (int i = 0; i < _strings; ++i) {
                if (_dots && _dots[i] && _dots[i] != _barre) {
                      int fret = _dots[i] - 1;
                      qreal x = stringDist * i - dotd * .5;
                      qreal y = fretDist * fret + fretDist * .5 - dotd * .5;
                      painter->drawEllipse(QRectF(x, y, dotd, dotd));
                      }
                if (_marker && _marker[i]) {
                      qreal x = stringDist * i;
                      qreal y = -fretDist * .3 - fm.ascent();
                      painter->drawText(QRectF(x, y, .0, .0),
                         Qt::AlignHCenter|Qt::TextDontClip, QChar(_marker[i]));
                      }
                }
          if (_barre) {
                int string = -1;
                for (int i = 0; i < _strings; ++i) {
                      if (_dots && _dots[i] == _barre) {
                            string = i;
                            break;
                            }
                      }
                if (string != -1) {
                      qreal x1   = stringDist * string;
                      qreal x2   = stringDist * (_strings-1);
                      qreal y    = fretDist * (_barre-1) + fretDist * .5;
                      pen.setWidthF((dotd + lw2 * .5) * score()->styleD(StyleIdx::barreLineWidth));
                      pen.setCapStyle(Qt::RoundCap);
                      painter->setPen(pen);
                      painter->drawLine(QLineF(x1, y, x2, y));
                      }
                }
          if (_fretOffset > 0) {
                qreal fretNumMag = score()->styleD(StyleIdx::fretNumMag);
                QFont scaledFont(font);
                scaledFont.setPointSizeF(font.pointSize() * fretNumMag * _userMag * score()->styleD(StyleIdx::fretMag) * MScore::pixelRatio);
                painter->setFont(scaledFont);
                if (score()->styleI(StyleIdx::fretNumPos) == 0)
                      painter->drawText(QRectF(-stringDist *.4, .0, .0, fretDist),
                         Qt::AlignVCenter|Qt::AlignRight|Qt::TextDontClip,
                         QString("%1").arg(_fretOffset+1));
                else
                      painter->drawText(QRectF(x2 + (stringDist * 0.4), .0, .0, fretDist),
                         Qt::AlignVCenter|Qt::AlignLeft|Qt::TextDontClip,
                         QString("%1").arg(_fretOffset+1));
                painter->setFont(font);
                }
          }

    //---------------------------------------------------------
    //   layout
    //---------------------------------------------------------

    void FretDiagram::layout()
          {
          qreal _spatium = spatium() * _userMag * score()->styleD(StyleIdx::fretMag);
          lw1             = _spatium * 0.08;
          lw2             = _fretOffset ? lw1 : _spatium * 0.2;
          stringDist      = _spatium * .7;
          fretDist        = _spatium * .8;

          qreal w = stringDist * (_strings - 1);
          qreal h = _frets * fretDist + fretDist * .5;
          qreal y = 0.0;
          qreal dotd = stringDist * .6;
          qreal x = -((dotd+lw1) * .5);
          w += dotd + lw1;
          if (_marker) {
                QFont scaledFont(font);
                scaledFont.setPointSize(font.pointSize() * _userMag);
                QFontMetricsF fm(scaledFont, MScore::paintDevice());
                y = -(fretDist * .1 + fm.height());
                h -= y;
                }
          bbox().setRect(x, y, w, h);

          setPos(-_spatium, -h - score()->styleP(StyleIdx::fretY) + _spatium );
          adjustReadPos();

          if (_harmony)
                _harmony->layout();

          if (!parent() || !parent()->isSegment()) {
                setPos(QPointF());
                return;
                }
    //      Measure* m     = toSegment(parent())->measure();
    //      int idx        = staffIdx();
    //      MStaff* mstaff = m->mstaff(idx);
    //      qreal dist = -(bbox().top());
    //      mstaff->distanceUp = qMax(mstaff->distanceUp, dist + _spatium * 2);
          }

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

    void FretDiagram::write(XmlWriter& xml) const
          {
          if (!xml.canWrite(this))
                return;
          xml.stag("FretDiagram");
          Element::writeProperties(xml);

          writeProperty(xml, P_ID::FRET_STRINGS);
          writeProperty(xml, P_ID::FRET_FRETS);
          writeProperty(xml, P_ID::FRET_OFFSET);
          for (int i = 0; i < _strings; ++i) {
                if ((_dots && _dots[i]) || (_marker && _marker[i]) || (_fingering && _fingering[i])) {
                      xml.stag(QString("string no=\"%1\"").arg(i));
                      if (_dots && _dots[i])
                            xml.tag("dot", _dots[i]);
                      if (_marker && _marker[i])
                            xml.tag("marker", _marker[i]);
                      if (_fingering && _fingering[i])
                            xml.tag("fingering", _fingering[i]);
                      xml.etag();
                      }
                }
          writeProperty(xml, P_ID::FRET_BARRE);
          writeProperty(xml, P_ID::MAG);
          if (_harmony)
                _harmony->write(xml);
          xml.etag();
          }

    //---------------------------------------------------------
    //   read
    //---------------------------------------------------------

    void FretDiagram::read(XmlReader& e)
          {
          while (e.readNextStartElement()) {
                const QStringRef& tag(e.name());
                if (tag == "strings")
                      _strings = e.readInt();
                else if (tag == "frets")
                      _frets = e.readInt();
                else if (tag == "fretOffset")
                      _fretOffset = e.readInt();
                else if (tag == "string") {
                      int no = e.intAttribute("no");
                      while (e.readNextStartElement()) {
                            const QStringRef& tag(e.name());
                            if (tag == "dot")
                                  setDot(no, e.readInt());
                            else if (tag == "marker")
                                  setMarker(no, e.readInt());
                            else if (tag == "fingering")
                                  setFingering(no, e.readInt());
                            else
                                  e.unknown();
                            }
                      }
                else if (tag == "barre")
                      setBarre(e.readInt());
                else if (tag == "mag")
                      _userMag = e.readDouble(0.1, 10.0);
                else if (tag == "Harmony") {
                      Harmony* h = new Harmony(score());
                      h->read(e);
                      add(h);
                      }
                else if (!Element::readProperties(e))
                      e.unknown();
                }
          }

    //---------------------------------------------------------
    //   setDot
    //---------------------------------------------------------

    void FretDiagram::setDot(int string, int fret)
          {
          if (_dots == 0) {
                _dots = new char[_strings];
                memset(_dots, 0, _strings);
                }
          if (0 <= string && string < _strings) {
                _dots[string] = fret;
                setMarker(string, 0);
                }
          }

    //---------------------------------------------------------
    //   setMarker
    //---------------------------------------------------------

    void FretDiagram::setMarker(int string, int m)
          {
          if (_marker == 0) {
                _marker = new char[_strings];
                memset(_marker, 0, _strings);
                }
          if (0 <= string && string < _strings)
                _marker[string] = m;
          }

    //---------------------------------------------------------
    //   setFingering
    //---------------------------------------------------------

    void FretDiagram::setFingering(int string, int finger)
          {
          if (_fingering == 0) {
                _fingering = new char[_strings];
                memset(_fingering, 0, _strings);
                }
          _fingering[string] = finger;
          }

    //---------------------------------------------------------
    //   add
    //---------------------------------------------------------

    void FretDiagram::add(Element* e)
          {
          e->setParent(this);
          if (e->type() == ElementType::HARMONY) {
                _harmony = static_cast<Harmony*>(e);
                _harmony->setTrack(track());
                }
          else
                qWarning("FretDiagram: cannot add <%s>\n", e->name());
          }

    //---------------------------------------------------------
    //   remove
    //---------------------------------------------------------

    void FretDiagram::remove(Element* e)
          {
          if (e == _harmony)
                _harmony = 0;
          else
                qWarning("FretDiagram: cannot remove <%s>\n", e->name());
          }

    //---------------------------------------------------------
    //   acceptDrop
    //---------------------------------------------------------

    bool FretDiagram::acceptDrop(EditData& data) const
          {
          return data.element->type() == ElementType::HARMONY;
          }

    //---------------------------------------------------------
    //   drop
    //---------------------------------------------------------

    Element* FretDiagram::drop(EditData& data)
          {
          Element* e = data.element;
          if (e->type() == ElementType::HARMONY) {
                Harmony* h = static_cast<Harmony*>(e);
                h->setParent(parent());
                h->setTrack(track());
                score()->undoAddElement(h);
                }
          else {
                qWarning("FretDiagram: cannot drop <%s>\n", e->name());
                delete e;
                e = 0;
                }
          return e;
          }

    //---------------------------------------------------------
    //   scanElements
    //---------------------------------------------------------

    void FretDiagram::scanElements(void* data, void (*func)(void*, Element*), bool all)
          {
          Q_UNUSED(all);
          func(data, this);
          if (_harmony)
                func(data, _harmony);
          }

    #if 0
    //---------------------------------------------------------
    //   Read MusicXML
    //
    // Set the FretDiagram state based on the MusicXML <figure> node de.
    //---------------------------------------------------------

    void FretDiagram::readMusicXML(XmlReader& e)
          {
          qDebug("FretDiagram::readMusicXML");

          while (e.readNextStartElement()) {
                const QStringRef& tag(e.name());
                if (tag == "frame-frets") {
                      int val = e.readInt();
                      if (val > 0)
                            setFrets(val);
                      else
                            qDebug("FretDiagram::readMusicXML: illegal frame-fret %d", val);
                      }
                else if (tag == "frame-note") {
                      int fret   = -1;
                      int string = -1;
                      while (e.readNextStartElement()) {
                            const QStringRef& tag(e.name());
                            int val = e.readInt();
                            if (tag == "fret")
                                  fret = val;
                            else if (tag == "string")
                                  string = val;
                            else
                                  e.unknown();
                            }
                      qDebug("FretDiagram::readMusicXML string %d fret %d", string, fret);
                      if (string > 0) {
                            if (fret == 0)
                                  setMarker(strings() - string, 79 /* ??? */);
                            else if (fret > 0)
                                  setDot(strings() - string, fret);
                            }
                      }
                else if (tag == "frame-strings") {
                      int val = e.readInt();
                      if (val > 0) {
                            setStrings(val);
                            for (int i = 0; i < val; ++i)
                                  setMarker(i, 88 /* ??? */);
                            }
                      else
                            qDebug("FretDiagram::readMusicXML: illegal frame-strings %d", val);
                      }
                else
                      e.unknown();
                }
          }
    #endif

    //---------------------------------------------------------
    //   Write MusicXML
    //---------------------------------------------------------

    void FretDiagram::writeMusicXML(XmlWriter& xml) const
          {
          qDebug("FretDiagram::writeMusicXML() this %p harmony %p", this, _harmony);
          int _strings = strings();
          xml.stag("frame");
          xml.tag("frame-strings", _strings);
          xml.tag("frame-frets", frets());
          QString strDots = "'";
          QString strMarker = "'";
          QString strFingering = "'";
          for (int i = 0; i < _strings; ++i) {
                // TODO print frame note
                if (_dots) strDots += QString("%1'").arg(static_cast<int>(_dots[i]));
                if (_marker) strMarker += QString("%1'").arg(static_cast<int>(_marker[i]));
                if (_fingering) strFingering += QString("%1'").arg(static_cast<int>(_fingering[i]));
                if (_marker[i] != 88) {
                      xml.stag("frame-note");
                      xml.tag("string", _strings - i);
                      if (_dots)
                            xml.tag("fret", _dots[i]);
                      else
                            xml.tag("fret", "0");
                      xml.etag();
                      }
                }
          qDebug("FretDiagram::writeMusicXML() this %p dots %s marker %s fingering %s",
                 this, qPrintable(strDots), qPrintable(strMarker), qPrintable(strFingering));
          /*
          xml.tag("root-step", tpc2stepName(rootTpc));
          int alter = tpc2alter(rootTpc);
          if (alter)
                xml.tag("root-alter", alter);
          */
          xml.etag();
          }

    #ifdef SCRIPT_INTERFACE

    //---------------------------------------------------------
    //   undoSetUserMag
    //---------------------------------------------------------

    void FretDiagram::undoSetUserMag(qreal val)
          {
          undoChangeProperty(P_ID::MAG, val);
          }

    //---------------------------------------------------------
    //   undoSetStrings
    //---------------------------------------------------------

    void FretDiagram::undoSetStrings(int val)
          {
          undoChangeProperty(P_ID::FRET_STRINGS, val);
          }

    //---------------------------------------------------------
    //   undoSetFrets
    //---------------------------------------------------------

    void FretDiagram::undoSetFrets(int val)
          {
          undoChangeProperty(P_ID::FRET_FRETS, val);
          }

    //---------------------------------------------------------
    //   undoSetBarre
    //---------------------------------------------------------

    void FretDiagram::undoSetBarre(int val)
          {
          undoChangeProperty(P_ID::FRET_BARRE, val);
          }

    //---------------------------------------------------------
    //   undoSetFretOffset
    //---------------------------------------------------------

    void FretDiagram::undoSetFretOffset(int val)
          {
          undoChangeProperty(P_ID::FRET_OFFSET, val);
          }

    #endif // SCRIPT_INTERFACE

    //---------------------------------------------------------
    //   getProperty
    //---------------------------------------------------------

    QVariant FretDiagram::getProperty(P_ID propertyId) const
          {
          switch (propertyId) {
                case P_ID::MAG:            return userMag();
                case P_ID::FRET_STRINGS:         return strings();
                case P_ID::FRET_FRETS:           return frets();
                case P_ID::FRET_BARRE:           return barre();
                case P_ID::FRET_OFFSET:          return fretOffset();
                default:
                      return Element::getProperty(propertyId);
                }
          }

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

    QVariant FretDiagram::propertyDefault(P_ID propertyId) const
          {
          switch (propertyId) {
                case P_ID::MAG:            return 1.0;
                case P_ID::FRET_STRINGS:         return DEFAULT_STRINGS;
                case P_ID::FRET_FRETS:           return DEFAULT_FRETS;
                case P_ID::FRET_BARRE:           return 0;
                case P_ID::FRET_OFFSET:          return 0;
                default:
                      return Element::propertyDefault(propertyId);
                }
          }

    //---------------------------------------------------------
    //   setProperty
    //---------------------------------------------------------

    bool FretDiagram::setProperty(P_ID propertyId, const QVariant& v)
          {
          switch (propertyId) {
                case P_ID::MAG:
                      setUserMag(v.toDouble());
                      break;
                case P_ID::FRET_STRINGS:
                      setStrings(v.toInt());
                      break;
                case P_ID::FRET_FRETS:
                      setFrets(v.toInt());
                      break;
                case P_ID::FRET_BARRE:
                      setBarre(v.toInt());
                      break;
                case P_ID::FRET_OFFSET:
                      setOffset(v.toInt());
                      break;
                default:
                      return Element::setProperty(propertyId, v);
                }
          score()->setLayoutAll();
          return true;
          }

    }

↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: lw1, lw2, stringDist, fretDist.

↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: lw1, lw2, stringDist, fretDist.