    //=============================================================================
    //  MuseScore
    //  Music Composition & Notation
    //
    //  Copyright (C) 2010-2015 Werner Schweer & others
    //
    //  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 "stafftype.h"

    #include "chord.h"
    #include "mscore.h"
    #include "navigate.h"
    #include "staff.h"
    #include "xml.h"
    #include "score.h"

    #define TAB_DEFAULT_LINE_SP   (1.5)
    #define TAB_RESTSYMBDISPL     2.0

    namespace Ms {

    //---------------------------------------------------------
    //   StaffTypeTablature
    //---------------------------------------------------------

    #define TAB_DEFAULT_DUR_YOFFS (-1.0)

    QList<TablatureFretFont>     StaffType::_fretFonts      = QList<TablatureFretFont>();
    QList<TablatureDurationFont> StaffType::_durationFonts  = QList<TablatureDurationFont>();

    const char StaffType::groupNames[STAFF_GROUP_MAX][STAFF_GROUP_NAME_MAX_LENGTH] = {
          QT_TRANSLATE_NOOP("Staff type group name", "Standard"),
          QT_TRANSLATE_NOOP("Staff type group name", "Percussion"),
          QT_TRANSLATE_NOOP("Staff type group name", "Tablature")
          };

    const QString StaffType::fileGroupNames[STAFF_GROUP_MAX] = { "pitched", "percussion", "tablature" };

    //---------------------------------------------------------
    //   StaffType
    //---------------------------------------------------------

    StaffType::StaffType()
          {
          // set reasonable defaults for type-specific members */
          _symRepeat = TablatureSymbolRepeat::NEVER;
          setDurationFontName(_durationFonts[0].displayName);
          setFretFontName(_fretFonts[0].displayName);
          }

    StaffType::StaffType(StaffGroup sg, const QString& xml, const QString& name, int lines, int stpOff, qreal lineDist,
       bool genClef, bool showBarLines, bool stemless, bool genTimeSig, bool genKeySig, bool showLedgerLines) :
       _group(sg), _xmlName(xml), _name(name),
       _lines(lines),
       _stepOffset(stpOff),
       _lineDistance(Spatium(lineDist)),
       _showBarlines(showBarLines),
       _showLedgerLines(showLedgerLines),
       _slashStyle(stemless),
       _genClef(genClef),
       _genTimesig(genTimeSig),
       _genKeysig(genKeySig)
          {
          }

    StaffType::StaffType(StaffGroup sg, const QString& xml, const QString& name, int lines, int stpOff, qreal lineDist,
       bool genClef,
       bool showBarLines, bool stemless, bool genTimesig,
       const QString& durFontName, qreal durFontSize, qreal durFontUserY, qreal genDur,
       const QString& fretFontName, qreal fretFontSize, qreal fretFontUserY,
       TablatureSymbolRepeat symRepeat, bool linesThrough, TablatureMinimStyle minimStyle, bool onLines,
       bool showRests, bool stemsDown, bool stemThrough, bool upsideDown, bool showTabFingering, bool useNumbers, bool showBackTied)
          {
          _group   = sg;
          _xmlName = xml;
          _name    = name;
          setLines(lines);
          setStepOffset(stpOff);
          setLineDistance(Spatium(lineDist));
          setGenClef(genClef);
          setShowBarlines(showBarLines);
          setSlashStyle(stemless);
          setGenTimesig(genTimesig);
          setDurationFontName(durFontName);
          setDurationFontSize(durFontSize);
          setDurationFontUserY(durFontUserY);
          setGenDurations(genDur);
          setFretFontName(fretFontName);
          setFretFontSize(fretFontSize);
          setFretFontUserY(fretFontUserY);
          setSymbolRepeat(symRepeat);
          setLinesThrough(linesThrough);
          setMinimStyle(minimStyle);
          setOnLines(onLines);
          setShowRests(showRests);
          setStemsDown(stemsDown);
          setStemsThrough(stemThrough);
          setUpsideDown(upsideDown);
          setShowTabFingering(showTabFingering);
          setUseNumbers(useNumbers);
          setShowBackTied(showBackTied);
          }

    //---------------------------------------------------------
    //   groupName
    //---------------------------------------------------------

    const char* StaffType::groupName() const
          {
          return groupName(_group);
          }

    const char* StaffType::groupName(StaffGroup r)
          {
          if (r < StaffGroup::STANDARD || (int)r >= STAFF_GROUP_MAX)
                r = StaffGroup::STANDARD;
          return groupNames[(int)r];
          }

    //---------------------------------------------------------
    //   operator==
    //---------------------------------------------------------

    bool StaffType::operator==(const StaffType& st) const
          {
          if (!isSameStructure(st) || st._xmlName != _xmlName) {        // common to all type groups
                return false;
                }
          if (_group == StaffGroup::TAB) {                      // TAB-specific
                bool v = st._durationFontIdx  == _durationFontIdx
                   && st._durationFontSize  == _durationFontSize
                   && st._durationFontUserY == _durationFontUserY
                   && st._fretFontIdx       == _fretFontIdx
                   && st._fretFontSize      == _fretFontSize
                   && st._fretFontUserY     == _fretFontUserY
                   ;
                return v;
                }
          return true;
          }

    //---------------------------------------------------------
    //   isSameStructure
    //
    //    same as operator==, but ignores names and fonts
    //---------------------------------------------------------

    bool StaffType::isSameStructure(const StaffType& st) const
          {
          if (st.group()         != group()                     // common to all type groups
             || st._lines        != _lines
             || st._stepOffset   != _stepOffset
             || st._lineDistance != _lineDistance
             || st._genClef      != _genClef
             || st._showBarlines != _showBarlines
             || st._slashStyle   != _slashStyle
             || st._genTimesig   != _genTimesig)
                return false;
          if (_group == StaffGroup::STANDARD)                   // standard specific
                if (st._noteHeadScheme != _noteHeadScheme)
                      return false;
          if (_group != StaffGroup::TAB) {                      // common to pitched and percussion
                return st._genKeysig      == _genKeysig
                   && st._showLedgerLines == _showLedgerLines
                   ;
                }
          else {                                                // TAB-specific
                return st._genDurations == _genDurations
                   && st._symRepeat     == _symRepeat
                   && st._linesThrough  == _linesThrough
                   && st._minimStyle    == _minimStyle
                   && st._onLines       == _onLines
                   && st._showBackTied  == _showBackTied
                   && st._showRests     == _showRests
                   && st._stemsDown     == _stemsDown
                   && st._stemsThrough  == _stemsThrough
                   && st._upsideDown    == _upsideDown
                   && st._showTabFingering    == _showTabFingering
                   && st._useNumbers    == _useNumbers
                   ;
                }
          }

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

    void StaffType::write(XmlWriter& xml) const
          {
          xml.stag(QString("StaffType group=\"%1\"").arg(fileGroupNames[(int)_group]));
          if (!_xmlName.isEmpty())
                xml.tag("name", _xmlName);
          if (_lines != 5)
                xml.tag("lines", _lines);
          if (_lineDistance.val() != 1.0)
                xml.tag("lineDistance", _lineDistance.val());
          if (_yoffset.val() != 0.0)
                xml.tag("yoffset", _yoffset.val());
          if (_userMag != 1.0)
                xml.tag("mag", _userMag);
          if (_small)
                xml.tag("small", _small);
          if (_stepOffset)
                xml.tag("stepOffset", _stepOffset);
          if (!_genClef)
                xml.tag("clef", _genClef);
          if (_slashStyle)
                xml.tag("slashStyle", _slashStyle);
          if (!_showBarlines)
                xml.tag("barlines", _showBarlines);
          if (!_genTimesig)
                xml.tag("timesig", _genTimesig);
          if (_group == StaffGroup::STANDARD) {
                xml.tag("noteheadScheme", StaffType::scheme2name(_noteHeadScheme), StaffType::scheme2name(NoteHeadScheme::HEAD_NORMAL));
                }
          if (_group == StaffGroup::STANDARD || _group == StaffGroup::PERCUSSION) {
                if (!_genKeysig)
                      xml.tag("keysig", _genKeysig);
                if (!_showLedgerLines)
                      xml.tag("ledgerlines", _showLedgerLines);
                }
          else {
                xml.tag("durations",        _genDurations);
                xml.tag("durationFontName", _durationFonts[_durationFontIdx].displayName); // write font names anyway for backward compatibility
                xml.tag("durationFontSize", _durationFontSize);
                xml.tag("durationFontY",    _durationFontUserY);
                xml.tag("fretFontName",     _fretFonts[_fretFontIdx].displayName);
                xml.tag("fretFontSize",     _fretFontSize);
                xml.tag("fretFontY",        _fretFontUserY);
                if (_symRepeat != TablatureSymbolRepeat::NEVER)
                      xml.tag("symbolRepeat", int(_symRepeat));
                xml.tag("linesThrough",     _linesThrough);
                xml.tag("minimStyle",       int(_minimStyle));
                xml.tag("onLines",          _onLines);
                xml.tag("showRests",        _showRests);
                xml.tag("stemsDown",        _stemsDown);
                xml.tag("stemsThrough",     _stemsThrough);
                xml.tag("upsideDown",       _upsideDown);
                xml.tag("showTabFingering", _showTabFingering, false);
                xml.tag("useNumbers",       _useNumbers);
                // only output "showBackTied" if different from !"slashStyle"
                // to match the behaviour in 2.0.2 scores (or older)
                if (_showBackTied != !_slashStyle)
                      xml.tag("showBackTied",  _showBackTied);
                }
          xml.etag();
          }

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

    void StaffType::read(XmlReader& e)
          {
          QString group = e.attribute("group", fileGroupNames[(int)StaffGroup::STANDARD]);
          if (group == fileGroupNames[(int)StaffGroup::TAB])
                _group = StaffGroup::TAB;
          else if (group == fileGroupNames[(int)StaffGroup::PERCUSSION])
                _group = StaffGroup::PERCUSSION;
          else if (group == fileGroupNames[(int)StaffGroup::STANDARD])
                _group = StaffGroup::STANDARD;
          else {
                qDebug("StaffType::read: unknown group: %s", qPrintable(group));
                _group = StaffGroup::STANDARD;
                }

          while (e.readNextStartElement()) {
                const QStringRef& tag(e.name());
                if (tag == "name")
                      setXmlName(e.readElementText());
                else if (tag == "lines")
                      setLines(e.readInt());
                else if (tag == "lineDistance")
                      setLineDistance(Spatium(e.readDouble()));
                else if (tag == "clef")
                      setGenClef(e.readInt());
                else if (tag == "slashStyle") {
                      bool val = e.readInt() != 0;
                      setSlashStyle(val);
                      setShowBackTied(!val);  // for compatibility with 2.0.2 scores where this prop
                      }                       // was lacking and controlled by "slashStyle" instead
                else if (tag == "yoffset")
                      _yoffset = Spatium(e.readDouble());
                else if (tag == "mag")
                      _userMag = e.readDouble();
                else if (tag == "small")
                      _small = e.readBool();
                else if (tag == "barlines")
                      setShowBarlines(e.readInt());
                else if (tag == "timesig")
                      setGenTimesig(e.readInt());
                else if (tag == "noteheadScheme")
                      setNoteHeadScheme(StaffType::name2scheme(e.readElementText()));
                else if (tag == "keysig")
                      _genKeysig = e.readInt();
                else if (tag == "ledgerlines")
                      _showLedgerLines = e.readInt();
                else if (tag == "durations")
                      setGenDurations(e.readBool());
                else if (tag == "durationFontName")
                      setDurationFontName(e.readElementText());
                else if (tag == "durationFontSize")
                      setDurationFontSize(e.readDouble());
                else if (tag == "durationFontY")
                      setDurationFontUserY(e.readDouble());
               else if (tag == "fretFontName")
                      setFretFontName(e.readElementText());
                else if (tag == "fretFontSize")
                      setFretFontSize(e.readDouble());
                else if (tag == "fretFontY")
                      setFretFontUserY(e.readDouble());
                else if (tag == "symbolRepeat")
                      setSymbolRepeat( (TablatureSymbolRepeat) e.readInt() );
                else if (tag == "linesThrough")
                      setLinesThrough(e.readBool());
                else if (tag == "minimStyle")
                      setMinimStyle( (TablatureMinimStyle) e.readInt() );
                else if (tag == "onLines")
                      setOnLines(e.readBool());
                else if (tag == "showRests")
                      setShowRests(e.readBool());
                else if (tag == "stemsDown")
                      setStemsDown(e.readBool());
                else if (tag == "stemsThrough")
                      setStemsThrough(e.readBool());
                else if (tag == "upsideDown")
                      setUpsideDown(e.readBool());
                else if (tag == "showTabFingering")
                      setShowTabFingering(e.readBool());
                else if (tag == "useNumbers")
                      setUseNumbers(e.readBool());
                else if (tag == "showBackTied")           // must be after reading "slashStyle" prop, as in older
                      setShowBackTied(e.readBool());      // scores, this prop was lacking and controlled by "slashStyle"
                else
                      e.unknown();
                }
          }

    //---------------------------------------------------------
    //   doty1
    //    get y dot position of first repeat barline dot
    //---------------------------------------------------------

    qreal StaffType::doty1() const
          {
          switch(_lines) {
                case 1:
                      return -_lineDistance.val() * .5;
                case 2:
                      return -_lineDistance.val() * .5;
                case 3:
                      return _lineDistance.val() * .5;
                case 4:
                      return _lineDistance.val() * .5;
                case 5:
                      return _lineDistance.val() * 1.5;
                case 6:
                      return _lineDistance.val() * 1.5;
                default:
                      qDebug("StaffType::doty1(): lines %d unsupported", _lines);
                      break;
                }
          return 0.0;
          }

    //---------------------------------------------------------
    //   doty2
    //    get y dot position of second repeat barline dot
    //---------------------------------------------------------

    qreal StaffType::doty2() const
          {
          switch(_lines) {
                case 1:
                      return _lineDistance.val() * .5;
                case 2:
                      return _lineDistance.val() * 1.5;
                case 3:
                      return _lineDistance.val() * 1.5;
                case 4:
                      return _lineDistance.val() * 2.5;
                case 5:
                      return _lineDistance.val() * 2.5;
                case 6:
                      return _lineDistance.val() * 3.5;
                default:
                      qDebug("StaffType::doty2(): lines %d unsupported", _lines);
                      break;
                }
          return 0.0;
          }

    //---------------------------------------------------------
    //   setOnLines
    //---------------------------------------------------------

    void StaffType::setOnLines(bool val)
          {
          _onLines = val;
          _durationMetricsValid = _fretMetricsValid = false;
          }

    //---------------------------------------------------------
    //   setDurationMetrics
    //    checks whether the internally computed metrics are is still valid and re-computes them, if not
    //---------------------------------------------------------

    void StaffType::setDurationMetrics()
          {
          if (_durationMetricsValid && _refDPI == DPI)           // metrics are still valid
                return;

    // QFontMetrics[F]() returns results unreliably rounded to integral pixels;
    // use a scaled up font and then scale computed values down
    //      QFontMetricsF fm(durationFont());
          QFont font(durationFont());
          font.setPointSizeF(_durationFontSize);
          QFontMetricsF fm(font, MScore::paintDevice());
          QString txt(_durationFonts[_durationFontIdx].displayValue, int(TabVal::NUM_OF));
          QRectF bb( fm.tightBoundingRect(txt) );
          // raise symbols by a default margin and, if marks are above lines, by half the line distance
          // (converted from spatium units to raster units)
          _durationGridYOffset = ( TAB_DEFAULT_DUR_YOFFS - (_onLines ? 0.0 : lineDistance().val()*0.5) ) * SPATIUM20;
          // this is the bottomest point of any duration sign
          _durationYOffset        = _durationGridYOffset;
          // move symbols so that the lowest margin 'sits' on the base line:
          // move down by the whole part above (negative) the base line
          // ( -bb.y() ) then up by the whole height ( -bb.height() )
          _durationYOffset        -= (bb.height() + bb.y()) / 100.0;
          _durationBoxH           = bb.height() / 100.0;
          _durationBoxY           = _durationGridYOffset - bb.height() / 100.0;
          // keep track of the conditions under which metrics have been computed
          _refDPI = DPI;
          _durationMetricsValid = true;
          }

    void StaffType::setFretMetrics()
          {
          if (_fretMetricsValid && _refDPI == DPI)
                return;

          QFontMetricsF fm(fretFont(), MScore::paintDevice());
          QRectF bb;
          // compute vertical displacement
          if (_useNumbers) {
                // compute total height of used characters
                QString txt = QString();
                for (int idx = 0; idx < 10; idx++)  // use only first 10 digits
                      txt.append(_fretFonts[_fretFontIdx].displayDigit[idx]);
                bb = fm.tightBoundingRect(txt);
                // for numbers: centre on '0': move down by the whole part above (negative)
                // the base line ( -bb.y() ) then up by half the whole height ( -bb.height()/2 )
                QRectF bx( fm.tightBoundingRect(_fretFonts[_fretFontIdx].displayDigit[0]) );
                _fretYOffset = -(bx.y() + bx.height()/2.0);
                // _fretYOffset = -(bb.y() + bb.height()/2.0);  // <- using bbox of all chars
                }
          else {
                // compute total height of used characters
                QString txt(_fretFonts[_fretFontIdx].displayLetter, NUM_OF_LETTERFRETS);
                bb = fm.tightBoundingRect(txt);
                // for letters: centre on the 'a' ascender, by moving down half of the part above the base line in bx
                QRectF bx( fm.tightBoundingRect(_fretFonts[_fretFontIdx].displayLetter[0]) );
                _fretYOffset = -bx.y() / 2.0;
                }
          // if on string, we are done; if between strings, raise by half line distance
          if (!_onLines)
                _fretYOffset -= lineDistance().val() * SPATIUM20 * 0.5;

          // from _fretYOffset, compute _fretBoxH and _fretBoxY
          _fretBoxH = bb.height();
          _fretBoxY = bb.y()  + _fretYOffset;

          // keep track of the conditions under which metrics have been computed
          _refDPI = DPI;
          _fretMetricsValid = true;
          }

    //---------------------------------------------------------
    //   setDurationFontName / setFretFontName
    //---------------------------------------------------------

    void StaffType::setDurationFontName(const QString& name)
          {
          int idx;
          for (idx = 0; idx < _durationFonts.size(); idx++)
                if (_durationFonts[idx].displayName == name)
                      break;
          if (idx >= _durationFonts.size())
                idx = 0;          // if name not found, use first font
          _durationFont.setFamily(_durationFonts[idx].family);
          _durationFontIdx = idx;
          _durationMetricsValid = false;
          }

    void StaffType::setFretFontName(const QString& name)
          {
          int idx;
          QString locName = name;
          // convert old names for two built-in fonts which have changed of name
          if (name == "MuseScore Tab Late Renaiss")
                locName = "MuseScore Phalèse";
          for (idx = 0; idx < _fretFonts.size(); idx++)
                if (_fretFonts[idx].displayName == locName)
                      break;
          if (idx >= _fretFonts.size())
                idx = 0;          // if name not found, use first font
          _fretFont.setFamily(_fretFonts[idx].family);
          _fretFontIdx = idx;
          _fretMetricsValid = false;
          }

    //---------------------------------------------------------
    //   durationBoxH / durationBoxY
    //---------------------------------------------------------

    qreal StaffType::durationBoxH()
          {
          if (!_genDurations && !_slashStyle)
                return 0.0;
          setDurationMetrics();
          return _durationBoxH;
          }

    qreal StaffType::durationBoxY()
          {
          if (!_genDurations && !_slashStyle)
                return 0.0;
          setDurationMetrics();
          return _durationBoxY + _durationFontUserY * SPATIUM20;
          }

    //---------------------------------------------------------
    //   setDurationFontSize / setFretFontSize
    //---------------------------------------------------------

    void StaffType::setDurationFontSize(qreal val)
          {
          _durationFontSize = val;
          _durationFont.setPointSizeF(val);
          _durationMetricsValid = false;
          }

    void StaffType::setFretFontSize(qreal val)
          {
          _fretFontSize = val;
          _fretFont.setPointSizeF(val);
          _fretMetricsValid = false;
          }

    //---------------------------------------------------------
    //   chordRestStemPosY / chordStemPos / chordStemPosBeam / chordStemLength
    //
    //    computes the stem data for the given chord, according to TAB settings
    //    NOTE: unit: spatium, position: relative to chord (DIFFERENT from Chord own functions)
    //
    //   chordRestStemPosY
    //          returns the vertical position of stem start point
    //---------------------------------------------------------

    qreal StaffType::chordRestStemPosY(const ChordRest *chordRest) const
          {
          if (stemThrough())            // does not make sense for "stems through staves" setting; just return top line vert. position
                return 0.0;

          // if stems beside staff, position are fixed, but take into account delta for half notes
          qreal delta =                             // displacement for half note stems (if used)
                // if half notes have not a short stem OR not a half note => 0
                (minimStyle() != TablatureMinimStyle::SHORTER || chordRest->durationType().type() != TDuration::DurationType::V_HALF) ?
                      0.0 :
                      // if stem is up, displace of half stem length down (positive)
                      // if stem is down, displace of half stem length up (negative)
                      (chordRest->up() ?
                            -STAFFTYPE_TAB_DEFAULTSTEMLEN_UP : STAFFTYPE_TAB_DEFAULTSTEMLEN_DN) * 0.5;
          // if fret marks above lines and chordRest is up, move half a line distance up
          if (!onLines() && chordRest->up())
                delta -= _lineDistance.val() *0.5;
          qreal y = (chordRest->up() ? STAFFTYPE_TAB_DEFAULTSTEMPOSY_UP : (_lines-1)*_lineDistance.val() + STAFFTYPE_TAB_DEFAULTSTEMPOSY_DN)
                + delta;
          return y;
          }

    //---------------------------------------------------------
    //   chordStemPos
    //    return position of note at other side of beam
    //---------------------------------------------------------

    QPointF StaffType::chordStemPos(const Chord* chord) const
          {
          qreal y;
          if (stemThrough())
                // if stems are through staff, stem goes from fartest note string
                y = (chord->up() ? chord->downString() : chord->upString()) * _lineDistance.val();
          else
                // if stems are beside staff, stem start point has a fixed vertical position,
                // according to TAB parameters and stem up/down
                y = chordRestStemPosY(chord);
          return QPointF(chordStemPosX(chord), y);
          }

    //---------------------------------------------------------
    //   chordStemPosBeam
    //          return position of note at beam side of stem
    //---------------------------------------------------------

    QPointF StaffType::chordStemPosBeam(const Chord* chord) const
          {
          qreal y = ( stemsDown() ? chord->downString() : chord->upString() ) * _lineDistance.val();

          return QPointF(chordStemPosX(chord), y);
          }

    //---------------------------------------------------------
    //   chordStemLength
    //          return length of stem
    //---------------------------------------------------------

    qreal StaffType::chordStemLength(const Chord* chord) const
          {
          qreal    stemLen;
          // if stems are through staff, length should be computed by relevant chord algorithm;
          // here, just return default length (= 1 'octave' = 3.5 line spaces)
          if (stemThrough())
                return STAFFTYPE_TAB_DEFAULTSTEMLEN_THRU * _lineDistance.val();
          // if stems beside staff, length is fixed, but take into account shorter half note stems
          else {
                bool shrt = (minimStyle() == TablatureMinimStyle::SHORTER) && (chord->durationType().type() == TDuration::DurationType::V_HALF);
                stemLen = (stemsDown() ? STAFFTYPE_TAB_DEFAULTSTEMLEN_DN : STAFFTYPE_TAB_DEFAULTSTEMLEN_UP)
                            * (shrt ? STAFFTYPE_TAB_SHORTSTEMRATIO : 1.0);
                }
          // scale length by scale of parent chord, but relative to scale of context staff
          return stemLen * chord->mag() / chord->staff()->mag(chord->tick());
          }

    //---------------------------------------------------------
    //   fretString / durationString
    //
    //    construct the text string for a given fret / duration
    //---------------------------------------------------------

    static const QString unknownFret = QString("?");

    QString StaffType::fretString(int fret, int string, bool ghost) const
          {
          if (fret == FRET_NONE)
                return unknownFret;
          if (ghost)
                return _fretFonts[_fretFontIdx].ghostChar;
          else {
                bool        hasFret;
                QString     text  = tabBassStringPrefix(string, &hasFret);
                if (!hasFret)           // if the notation does not allow to fret this string,
                      return text;      // return the prefix only
                // otherwise, add to prefix the relevant digit/letter string
                return text +
                      (_useNumbers ?
                            (fret >= NUM_OF_DIGITFRETS  ? unknownFret : _fretFonts[_fretFontIdx].displayDigit[fret]) :
                            (fret >= NUM_OF_LETTERFRETS ? unknownFret : _fretFonts[_fretFontIdx].displayLetter[fret]) );
               }
          }

    QString StaffType::durationString(TDuration::DurationType type, int dots) const
          {
          QString s = _durationFonts[_durationFontIdx].displayValue[int(type)];
          for(int count=0; count < dots; count++)
                s.append(_durationFonts[_durationFontIdx].displayDot);
          return s;
          }

    //---------------------------------------------------------
    //    tabBassStringPrefix
    //
    //    returns a QString (possibly empty) with the prefix identifying a bass string in TAB's;
    //    can deal with non-bass strings (i.e. regular TAB lines).
    //
    //    Implements the specifics of historic notations for bass lines (i.e. strings outside
    //    the lines of the tab), both Italian and French.
    //
    //    strg   the instrument physical string ordinal (0 = topmost string, may exceed the number
    //                of lines actually present in the TAB to reference a bass string)
    //    bool   pntr to a bool receiving the info if notation allows to express a fret number or not
    //                (this is potentially different from the fact that the instrument string itself can be fretted or not)
    //---------------------------------------------------------

    QString StaffType::tabBassStringPrefix(int strg, bool* hasFret) const
          {
          *hasFret    = true;           // assume notation allows to fret this string
          int bassStrgIdx  = (strg >= _lines ? strg - _lines + 1 : 0);
          if (_useNumbers) {
                // if above the max bass string which can be fretted with number notation
                // return a number with the string index
                if (bassStrgIdx > NUM_OF_BASSSTRINGS_WITH_NUMBER) {
                      *hasFret    = false;
                      return _fretFonts[_fretFontIdx].displayDigit[strg+1];
                      }
                // if a frettable bass string, return an empty string
                return QString();
                }
         else {
                // bass string notation
                // if above the max bass string which can be fretted with letter notation
                // return a number with the bass string index itself
                if (bassStrgIdx > NUM_OF_BASSSTRINGS_WITH_LETTER) {
                      *hasFret    = false;
                      return _fretFonts[_fretFontIdx].displayDigit[bassStrgIdx-1];
                      }
                // if a frettable bass string, return a character with the relevant num. of slashes;
                // note that the number of slashes is bassStrgIdx-1 (1st bass has no slash)
                // and slashChar[] is 0-based (slashChar[0] => 1 slash, ...), whence the -2
                QString prefix    = bassStrgIdx > 1 ?
                            QString(_fretFonts[_fretFontIdx].slashChar[bassStrgIdx - 2]) : QString();
                return prefix;
                }
          }

    //---------------------------------------------------------
    //   drawInputStringMarks
    //
    //    in TAB's, draws the marks within the input 'blue cursor' required to identify the current target input string.
    //
    //    Implements the specific of historic TAB styles for instruments with more strings than TAB lines.
    //    For strings normally represented by TAB lines, no mark is required.
    //    For strings not represented by TAB lines (e.g. bass strings in lutes and similar),
    //    either a sequence of slashes OR some ledger line-like lines OR the ordinal of the string
    //    are used, according to the TAB style (French or Italian) and the string position.
    //
    //    Note: assumes the string parameter is within legal bounds, i.e.:
    //    0 <= string <= [instrument strings] - 1
    //
    //    p       the QPainter to draw into
    //    string  the instrument physical string for which to draw the mark (0 = top string)
    //    voice   the current input voice (affects mark colour)
    //    rect    the rect of the 'blue rectangle' showing the input position
    //---------------------------------------------------------

    static const qreal      LEDGER_LINE_THICKNESS   = 0.15;     // in sp
    static const qreal      LEDGER_LINE_LEFTX       = 0.25;     // in % of cursor rectangle width
    static const qreal      LEDGER_LINE_RIGHTX      = 0.75;     // in % of cursor rectangle width

    void StaffType::drawInputStringMarks(QPainter *p, int string, int voice, QRectF rect) const
          {
          if (_group != StaffGroup::TAB)
                return;
          qreal       spatium     = SPATIUM20;
          qreal       lineDist    = _lineDistance.val() * spatium;
          bool        hasFret;
          QString     text        = tabBassStringPrefix(string, &hasFret);
    //    qreal       lw          = point(score()->styleS(StyleIdx::ledgerLineWidth));  // no access to score form here
          qreal       lw          = LEDGER_LINE_THICKNESS * spatium;                    // use a fixed width
          QPen        pen(MScore::selectColor[voice].lighter(SHADOW_NOTE_LIGHT), lw);
          p->setPen(pen);
          // draw conventional 'ledger lines', if required
          int         numOfLedgerLines  = numOfTabLedgerLines(string);
          qreal       x1                = rect.x() + rect.width() * LEDGER_LINE_LEFTX;
          qreal       x2                = rect.x() + rect.width() * LEDGER_LINE_RIGHTX;
          // cursor rect is 1 line dist. high, and it is:
          // centred on the line for "frets on strings"    => lower top ledger line 1/2 line dist.
          // sitting on the line for "frets above strings" => lower top ledger line 1 full line dist
          qreal y     = rect.top() + lineDist * (_onLines ? 0.5 : 1.0);
          for (int i = 0; i < numOfLedgerLines; i++) {
                p->drawLine(QLineF(x1, y, x2, y));
                y += lineDist / numOfLedgerLines; // insert other lines between top line and tab body
                }
          // draw the text, if any
          if (!text.isEmpty()) {
                p->setFont(fretFont());
                p->drawText(QPointF(rect.left(), rect.top() + lineDist), text);
                }
          }

    //---------------------------------------------------------
    //   numOfLedgerLines
    //
    //    in TAB's, returns the number of ledgerlines needed by bass lines in some TAB styles.
    //
    //    Returns 0 if staff is not a TAB, if a TAB but style does not use ledger lines
    //    or ledger lines do not apply to the given string.
    //---------------------------------------------------------

    int StaffType::numOfTabLedgerLines(int string) const
          {
          if (_group != StaffGroup::TAB || !_useNumbers)
                return 0;

          int   numOfLedgers= string < 0 ? -string : string - _lines + 1;
          return (numOfLedgers >= 1 && numOfLedgers <= NUM_OF_BASSSTRINGS_WITH_NUMBER ? numOfLedgers : 0);
          }

    //---------------------------------------------------------
    //   physStringToVisual / visualStringToPhys
    //
    //    returns the string ordinal in visual order (top to down) from a string ordinal in physical order
    //    or viceversa: manages upsideDown
    //---------------------------------------------------------

    int StaffType::physStringToVisual(int strg) const
          {
          if (strg < 0)                       // if above top string, return top string
                strg = 0;
    //      // NO: bass strings may exist, which are in addition to tab string lines
    //      if (strg >= _lines)                 // if physical string has no visual representation,
    //            strg = _lines - 1;            // reduce to nearest visual line
          // if TAB upside down, flip around top line
          return (_upsideDown ? _lines - 1 - strg : strg);
          }

    int StaffType::visualStringToPhys(int line) const
          {
          // if TAB upside down, reverse string number
          line = (_upsideDown ? _lines - 1 - line : line);

          if (line < 0)           // if above top string, reduce to top string
                line = 0;
    // NO: bass strings may exist, which are in addition to tab string lines
    //      if (line >= _lines)
    //            line = _lines - 1;
          return line;
          }

    //---------------------------------------------------------
    //   physStringToYOffset
    //
    //    returns the string Y offset from a string ordinal in physical order:
    //    manages upsideDown and extra bass strings.
    //
    //    The returned values is in sp. and is relative to the staff top line.
    //
    //    Note: the difference with physStringToVisual() is that this function takes into account
    //          peculiarities of bass string notations.
    //---------------------------------------------------------

    qreal StaffType::physStringToYOffset(int strg) const
          {
          qreal yOffset = strg;                     // the y offset of the visual string, as a multiple of line distance
          if (yOffset < 0)                          // if above top physical string, limit to top string
                yOffset = 0;
          if (yOffset >= _lines) {                  // if physical string 'below' tab lines,
                yOffset = _lines;                   // reduce to first string 'below' tab body
                if (!_useNumbers)                   // with letters, add some space for the slashes ascender
                      yOffset = _lines + STAFFTYPE_TAB_BASSSLASH_YOFFSET;
                }
          // if TAB upside down, flip around top line
          yOffset = _upsideDown ? (qreal)(_lines - 1) - yOffset : yOffset;
          return yOffset * _lineDistance.val();
          }

    //---------------------------------------------------------
    //   TabDurationSymbol
    //---------------------------------------------------------

    TabDurationSymbol::TabDurationSymbol(Score* s)
       : Element(s)
          {
          clearFlags(ElementFlag::MOVABLE | ElementFlag::SELECTABLE);
          setGenerated(true);
          _beamGrid   = TabBeamGrid::NONE;
          _beamLength = 0.0;
          _tab        = 0;
          _text       = QString();
          }

    TabDurationSymbol::TabDurationSymbol(Score* s, StaffType* tab, TDuration::DurationType type, int dots)
       : Element(s)
          {
          clearFlags(ElementFlag::MOVABLE | ElementFlag::SELECTABLE);
          setGenerated(true);
          _beamGrid   = TabBeamGrid::NONE;
          _beamLength = 0.0;
          setDuration(type, dots, tab);
          }

    TabDurationSymbol::TabDurationSymbol(const TabDurationSymbol& e)
       : Element(e)
          {
          _tab = e._tab;
          _text = e._text;
          }

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

    void TabDurationSymbol::layout()
          {
          if(!_tab) {
                setbbox(QRectF());
                return;
                }
          qreal _spatium    = spatium();
          qreal hbb, wbb, xbb, ybb;     // bbox sizes
          qreal xpos, ypos;             // position coords

          _beamGrid = TabBeamGrid::NONE;
          Chord* chord = static_cast<Chord*>(parent());
          // if no chord (shouldn't happens...) or not a special beam mode, layout regular symbol
          if (!chord || !chord->isChord() ||
                (chord->beamMode() != Beam::Mode::BEGIN && chord->beamMode() != Beam::Mode::MID &&
                      chord->beamMode() != Beam::Mode::END) ) {
                QFontMetricsF fm(_tab->durationFont(), MScore::paintDevice());
                hbb   = _tab->durationBoxH();
                wbb   = fm.width(_text);
                xbb   = 0.0;
                xpos  = 0.0;
                ypos  = _tab->durationFontYOffset();
                ybb   = _tab->durationBoxY() - ypos;
                // with rests, move symbol down by half its displacement from staff
                if (parent() && parent()->isRest()) {
                      ybb  += TAB_RESTSYMBDISPL * _spatium;
                      ypos += TAB_RESTSYMBDISPL * _spatium;
                      }
                }
          // if on a chord with special beam mode, layout an 'English'-style duration grid
          else {
                TablatureDurationFont font = _tab->_durationFonts[_tab->_durationFontIdx];
                hbb   = font.gridStemHeight * _spatium;         // bbox height is stem height
                wbb   = font.gridStemWidth  * _spatium;         // bbox width is stem width
                xbb   = -wbb * 0.5;                             // bbox is half at left and half at right of stem centre
                ybb   = -hbb;                                   // bbox top is at top of stem height
                xpos  = 0.75 * _spatium;                        // conventional centring of stem on fret marks
                ypos  = _tab->durationGridYOffset();            // stem start is at bottom
                if (chord->beamMode() == Beam::Mode::BEGIN) {
                      _beamGrid   = TabBeamGrid::INITIAL;
                      _beamLength = 0.0;
                      }
                else if (chord->beamMode() == Beam::Mode::MID || chord->beamMode() == Beam::Mode::END) {
                      _beamLevel  = (int)(chord->durationType().type()) - (int)(font.zeroBeamLevel);
                      _beamGrid   = (_beamLevel < 1 ? TabBeamGrid::INITIAL : TabBeamGrid::MEDIALFINAL);
                      // _beamLength and bbox x and width will be set in layout2(),
                      // once horiz. positions of chords are known
                      }
                }
          // set this' mag from parent chord mag (include staff mag)
          qreal mag = chord != nullptr ? chord->mag() : 1.0;
          setMag(mag);
          mag = magS();           // local mag * score mag
          // set magnified bbox and position
          bbox().setRect(xbb * mag, ybb * mag, wbb * mag, hbb * mag);
          setPos(xpos*mag, ypos*mag);
          }

    //---------------------------------------------------------
    //   layout2
    //
    //    Second step: after horizontal positions of elements involved are defined,
    //    compute width of 'grid beams'
    //---------------------------------------------------------

    void TabDurationSymbol::layout2()
          {
          // if not within a TAB or not a MEDIALFINAL grid element, do nothing
          if(!_tab || _beamGrid != TabBeamGrid::MEDIALFINAL)
                return;

          // get 'grid' beam length from page positions of this' chord and previous chord
          Chord*      chord       = static_cast<Chord*>(parent());
          ChordRest*  prevChord   = prevChordRest(chord, true);
          if (chord == nullptr || prevChord == nullptr)
                return;
          qreal       mags        = magS();
          qreal       beamLen     = prevChord->pagePos().x() - chord->pagePos().x();    // negative
          // page pos. difference already includes any magnification in effect:
          // scale it down, as it will be magnified again during drawing
          _beamLength = beamLen / mags;
          // update bbox x and w, but keep current y and h
          bbox().setX(beamLen);
          // set bbox width to half a stem width (magnified) plus beam length (already magnified)
          bbox().setWidth(_tab->_durationFonts[_tab->_durationFontIdx].gridStemWidth * spatium() * 0.5 * mags - beamLen);
          }

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

    void TabDurationSymbol::draw(QPainter* painter) const
          {
          if (!_tab)
                return;
          qreal mag = magS();
          qreal imag = 1.0 / mag;

          QPen  pen(curColor());
          painter->setPen(pen);
          painter->scale(mag, mag);
          if (_beamGrid == TabBeamGrid::NONE) {
                // if no beam grid, draw symbol
                QFont f(_tab->durationFont());
                f.setPointSizeF(f.pointSizeF() * MScore::pixelRatio);
                painter->setFont(f);
                painter->drawText(QPointF(0.0, 0.0), _text);
                }
          else {
                // if beam grid, draw stem line
                TablatureDurationFont& font = _tab->_durationFonts[_tab->_durationFontIdx];
                qreal _spatium = spatium();
                pen.setCapStyle(Qt::FlatCap);
                pen.setWidthF(font.gridStemWidth * _spatium);
                painter->setPen(pen);
                // take stem height from bbox, but de-magnify it, as drawing is already magnified
                qreal h     = bbox().y() / mag;
                painter->drawLine(QPointF(0.0, h), QPointF(0.0, 0.0) );
                // if beam grid is medial/final, draw beam lines too: lines go from mid of
                // previous stem (delta x stored in _beamLength) to mid of this' stem (0.0)
                if (_beamGrid == TabBeamGrid::MEDIALFINAL) {
                      pen.setWidthF(font.gridBeamWidth * _spatium);
                      painter->setPen(pen);
                      // lower heigth available to beams by half a beam width,
                      // so that top beam upper border aligns with stem top
                      h += (font.gridBeamWidth * _spatium) * 0.5;
                      // draw beams equally spaced within the stem height (this is
                      // different from modern engraving, but common in historic prints)
                      qreal step  = -h / _beamLevel;
                      qreal y     = h;
                      for (int i = 0; i < _beamLevel; i++, y += step)
                            painter->drawLine(QPointF(_beamLength, y), QPointF(0.0, y) );
                      }
                }
          painter->scale(imag, imag);
          }

    //---------------------------------------------------------
    //   STATIC FUNCTIONS FOR FONT CONFIGURATION MANAGEMENT
    //---------------------------------------------------------

    bool TablatureFretFont::read(XmlReader& e)
          {
          defPitch    = 9.0;
          defYOffset  = 0.0;
          while (e.readNextStartElement()) {
                const QStringRef& tag(e.name());

                int val = e.intAttribute("value");

                if (tag == "family")
                      family = e.readElementText();
                else if (tag == "displayName")
                      displayName = e.readElementText();
                else if (tag == "defaultPitch")
                      defPitch = e.readDouble();
                else if (tag == "defaultYOffset")
                      defYOffset = e.readDouble();
                else if (tag == "mark") {
                      QString     val = e.attribute("value");
                      int         num = e.intAttribute("number", 1);
                      QString     txt(e.readElementText());
                      if (val.size() < 1)
                            return false;
                      if (val == "x")
                            xChar = txt[0];
                      else if (val == "ghost")
                            ghostChar = txt[0];
                      else if (val == "slash") {
                            // limit within legal range
                            if (num < 1)
                                  num = 1;
                            if (num > NUM_OF_BASSSTRING_SLASHES)
                                  num = NUM_OF_BASSSTRING_SLASHES;
                            slashChar[num-1] = txt;
                            }
                      }
                else if (tag == "fret") {
                      bool bLetter = e.intAttribute("letter");
                      QString txt(e.readElementText());
                      if (bLetter) {
                            if (val >= 0 && val < NUM_OF_LETTERFRETS)
                                  displayLetter[val] = txt[0];
                            }
                      else {
                            if (val >= 0 && val < NUM_OF_DIGITFRETS)
                                  displayDigit[val] = txt;
                            }
                      }
                else {
                      e.unknown();
                      return false;
                      }
                }
          return true;
          }

    bool TablatureDurationFont::read(XmlReader& e)
          {
          while (e.readNextStartElement()) {
                const QStringRef& tag(e.name());

                if (tag == "family")
                      family = e.readElementText();
                else if (tag == "displayName")
                      displayName = e.readElementText();
                else if (tag == "defaultPitch")
                      defPitch = e.readDouble();
                else if (tag == "defaultYOffset")
                      defYOffset = e.readDouble();
                else if (tag == "beamWidth")
                      gridBeamWidth = e.readDouble();
                else if (tag == "stemHeight")
                      gridStemHeight = e.readDouble();
                else if (tag == "stemWidth")
                      gridStemWidth = e.readDouble();
                else if (tag == "zeroBeamValue") {
                      QString val(e.readElementText());
                      if (val == "longa")
                            zeroBeamLevel = TDuration::DurationType::V_LONG;
                      else if (val == "brevis")
                            zeroBeamLevel = TDuration::DurationType::V_BREVE;
                      else if (val == "semibrevis")
                            zeroBeamLevel = TDuration::DurationType::V_WHOLE;
                      else if (val == "minima")
                            zeroBeamLevel = TDuration::DurationType::V_HALF;
                      else if (val == "semiminima")
                            zeroBeamLevel = TDuration::DurationType::V_QUARTER;
                      else if (val == "fusa")
                            zeroBeamLevel = TDuration::DurationType::V_EIGHTH;
                      else if (val == "semifusa")
                            zeroBeamLevel = TDuration::DurationType::V_16TH;
                      else if (val == "32")
                            zeroBeamLevel = TDuration::DurationType::V_32ND;
                      else if (val == "64")
                            zeroBeamLevel = TDuration::DurationType::V_64TH;
                      else if (val == "128")
                            zeroBeamLevel = TDuration::DurationType::V_128TH;
                      else if (val == "256")
                            zeroBeamLevel = TDuration::DurationType::V_256TH;
                      else
                            e.unknown();
                      }
                else if (tag == "duration") {
                      QString val = e.attribute("value");
                      QString txt(e.readElementText());
                      QChar chr = txt[0];
                      if (val == "longa")
                            displayValue[int(TabVal::VAL_LONGA)] = chr;
                      else if (val == "brevis")
                            displayValue[int(TabVal::VAL_BREVIS)] = chr;
                      else if (val == "semibrevis")
                            displayValue[int(TabVal::VAL_SEMIBREVIS)] = chr;
                      else if (val == "minima")
                            displayValue[int(TabVal::VAL_MINIMA)] = chr;
                      else if (val == "semiminima")
                            displayValue[int(TabVal::VAL_SEMIMINIMA)] = chr;
                      else if (val == "fusa")
                            displayValue[int(TabVal::VAL_FUSA)] = chr;
                      else if (val == "semifusa")
                            displayValue[int(TabVal::VAL_SEMIFUSA)] = chr;
                      else if (val == "32")
                            displayValue[int(TabVal::VAL_32)] = chr;
                      else if (val == "64")
                            displayValue[int(TabVal::VAL_64)] = chr;
                      else if (val == "128")
                            displayValue[int(TabVal::VAL_128)] = chr;
                      else if (val == "256")
                            displayValue[int(TabVal::VAL_256)] = chr;
                      else if (val == "dot")
                            displayDot = chr;
                      else
                            e.unknown();
                      }
                else {
                      e.unknown();
                      return false;
                      }
                }
          return true;
          }

    //---------------------------------------------------------
    //   Read Configuration File
    //
    //    reads a configuration and appends read data to g_TABFonts
    //    resets everythings and reads the built-in config file if fileName is null or empty
    //---------------------------------------------------------

    bool StaffType::readConfigFile(const QString& fileName)
          {
          QString path;

          if (fileName == 0 || fileName.isEmpty()) {       // defaults to built-in xml
    #ifdef Q_OS_IOS
                {
                extern QString resourcePath();
                QString rpath = resourcePath();
                path = rpath + QString("/fonts_tablature.xml");
                }
    #else
                path = ":/fonts/fonts_tablature.xml";
    #endif
                _durationFonts.clear();
                _fretFonts.clear();
                }
          else
                path = fileName;

          QFileInfo fi(path);
          QFile f(path);

          if (!fi.exists() || !f.open(QIODevice::ReadOnly)) {
                MScore::lastError = QObject::tr("Cannot open tablature font description:\n%1\n%2").arg(f.fileName()).arg(f.errorString());
                qDebug("StaffTypeTablature::readConfigFile failed: <%s>", qPrintable(path));
                return false;
                }

          XmlReader e(0, &f);
          while (e.readNextStartElement()) {
                if (e.name() == "museScore") {
                      while (e.readNextStartElement()) {
                            const QStringRef& tag(e.name());
                            if (tag == "fretFont") {
                                  TablatureFretFont f;
                                  if (f.read(e))
                                        _fretFonts.append(f);
                                  else
                                        continue;
                                  }
                            else if (tag == "durationFont") {
                                  TablatureDurationFont f;
                                  if (f.read(e))
                                        _durationFonts.append(f);
                                  else
                                        continue;
                                  }
                            else
                                  e.unknown();
                            }
                      return true;
                      }
                }
          return false;
          }

    //---------------------------------------------------------
    //   fontNames
    //
    //    returns a list of display names for the fonts  configured to work with Tablatures;
    //    the index of a name in the list can be used to retrieve the font data with fontData()
    //---------------------------------------------------------

    QList<QString> StaffType::fontNames(bool bDuration)
          {
          QList<QString> names;
          if(bDuration)
                foreach(const TablatureDurationFont& f, _durationFonts)
                      names.append(f.displayName);
          else
                foreach(const TablatureFretFont& f, _fretFonts)
                      names.append(f.displayName);
          return names;
          }

    //---------------------------------------------------------
    //   fontData
    //
    //    retrieves data about a Tablature font.
    //    returns: true if idx is valid | false if it is not
    // any of the pointer parameter can be null, if that datum is not needed
    //---------------------------------------------------------

    bool StaffType::fontData(bool bDuration, int nIdx, QString* pFamily, QString* pDisplayName,
       qreal* pSize, qreal* pYOff)
          {
          if (bDuration) {
                if (nIdx >= 0 && nIdx < _durationFonts.size()) {
                      TablatureDurationFont f = _durationFonts.at(nIdx);
                      if (pFamily)      *pFamily          = f.family;
                      if (pDisplayName) *pDisplayName     = f.displayName;
                      if (pSize)        *pSize            = f.defPitch;
                      if (pYOff)        *pYOff            = f.defYOffset;
                      return true;
                      }
                }
          else {
                if (nIdx >= 0 && nIdx < _fretFonts.size()) {
                      TablatureFretFont f = _fretFonts.at(nIdx);
                      if (pFamily)      *pFamily          = f.family;
                      if (pDisplayName) *pDisplayName     = f.displayName;
                      if (pSize)        *pSize            = f.defPitch;
                      if (pYOff)        *pYOff            = f.defYOffset;
                      return true;
                      }
                }
          return false;
          }

    //=========================================================
    //
    //   BUILT-IN STAFF TYPES and STAFF TYPE PRESETS
    //
    //=========================================================

    static const int _defaultPreset[STAFF_GROUP_MAX] =
          { 0,              // default pitched preset is "stdNormal"
            3,              // default percussion preset is "perc5lines"
            5               // default tab preset is "tab6StrCommon"
          };

    static const QString _emptyString = QString();

    //---------------------------------------------------------
    //   Static functions for StaffType presets
    //---------------------------------------------------------

    const StaffType* StaffType::preset(StaffTypes idx)
          {
          if (int(idx) < 0 || int(idx) >= int(_presets.size()))
                return &_presets[0];
          return &_presets[int(idx)];
          }

    const StaffType* StaffType::presetFromXmlName(QString& xmlName)
          {
          for (int i = 0; i < int(_presets.size()); ++i) {
                if (_presets[i].xmlName() == xmlName)
                      return &_presets[i];
                }
          return 0;
          }
    #if 0
    const StaffType* StaffType::presetFromName(QString& name)
          {
          for (int i = 0; i < (int)_presets.size(); ++i) {
                if (_presets[i].name() == name)
                      return &_presets[i];
                }
          return 0;
          }
    #endif
    const StaffType* StaffType::getDefaultPreset(StaffGroup grp)
          {
          int _idx = _defaultPreset[int(grp)];
          return &_presets[_idx];
          }

    //---------------------------------------------------------
    //   NoteHeadScheme utils
    //---------------------------------------------------------

    struct NoteHeadSchemeName {
          const char* name;
          const char* username;
          };

    static NoteHeadSchemeName noteHeadSchemeNames[] = {
          {"normal",              QT_TRANSLATE_NOOP("noteheadschemes", "Normal") },
          {"name-pitch",          QT_TRANSLATE_NOOP("noteheadschemes", "Pitch Name") },
          {"name-pitch-german",   QT_TRANSLATE_NOOP("noteheadschemes", "German Pitch Name") },
          {"solfege-movable",     QT_TRANSLATE_NOOP("noteheadschemes", "Solfège Movable Do") },
          {"solfege-fixed",       QT_TRANSLATE_NOOP("noteheadschemes", "Solfège Fixed Do") },
          {"shape-4",             QT_TRANSLATE_NOOP("noteheadschemes", "4-shape (Walker)") },
          {"shape-7-aikin",       QT_TRANSLATE_NOOP("noteheadschemes", "7-shape (Aikin)") },
          {"shape-7-funk",        QT_TRANSLATE_NOOP("noteheadschemes", "7-shape (Funk)") },
          {"shape-7-walker",      QT_TRANSLATE_NOOP("noteheadschemes", "7-shape (Walker)") }
          };

    QString StaffType::scheme2userName(NoteHeadScheme ns)
          {
          return qApp->translate("noteheadschemes", noteHeadSchemeNames[int(ns)].username);
          }

    QString StaffType::scheme2name(NoteHeadScheme ns)
          {
          return noteHeadSchemeNames[int(ns)].name;
          }

    NoteHeadScheme StaffType::name2scheme(QString name)
          {
          for (int i = 0; i < int(NoteHeadScheme::HEAD_SCHEMES); ++i) {
                if (noteHeadSchemeNames[i].name == name)
                      return NoteHeadScheme(i);
                }
          return NoteHeadScheme::HEAD_NORMAL;
          }

    //---------------------------------------------------------
    //   initStaffTypes
    //---------------------------------------------------------

    std::vector<StaffType> StaffType::_presets;

    void StaffType::initStaffTypes()
          {
          readConfigFile(0);          // get TAB font config, before initStaffTypes()

          _presets = {
    //                       group,              xml-name,  human-readable-name,          lin stpOff, dst  clef  bars stmless time  key  ledger
             StaffType(StaffGroup::STANDARD,   "stdNormal", QObject::tr("Standard"),        5, 0,     1,   true,  true, false, true, true,  true),
             StaffType(StaffGroup::PERCUSSION, "perc1Line", QObject::tr("Perc. 1 line"),    1, -4,    1,   true,  true, false, true, false, true),
             StaffType(StaffGroup::PERCUSSION, "perc3Line", QObject::tr("Perc. 3 lines"),   3, 0,     2,   true,  true, false, true, false, true),
             StaffType(StaffGroup::PERCUSSION, "perc5Line", QObject::tr("Perc. 5 lines"),   5, 0,     1,   true,  true, false, true, false, true),
    //                 group            xml-name,     human-readable-name                  lin dist clef   bars stemless time      duration font     size off genDur     fret font          size off  duration symbol repeat        thru  minim style                 onLin  rests  stmDn  stmThr upsDn  sTFing   nums bkTied
    //         StaffType(StaffGroup::TAB, "tab6StrSimple", QObject::tr("Tab. 6-str. simple"), 6, 2,     1.5, true,  true, true,  false, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Sans",    9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::NONE,   true,  false, true,  false, false, false, true, false),
    //         StaffType(StaffGroup::TAB, "tab6StrCommon", QObject::tr("Tab. 6-str. common"), 6, 2,     1.5, true,  true, false, false, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SHORTER,true,  false, true,  false, false, false, true, true),
    //         StaffType(StaffGroup::TAB, "tab6StrFull",   QObject::tr("Tab. 6-str. full"),   6, 2,     1.5, true,  true, false, true,  "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SLASHED,true,  true,  true,  true,  false, false, true, true),
             StaffType(StaffGroup::TAB, "tab6StrSimple", QObject::tr("Tab. 6-str. simple"), 6, 0,     1.5, true,  true, true,  false, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Sans",    9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::NONE,   true,  false, true,  false, false, false, true, false),
             StaffType(StaffGroup::TAB, "tab6StrCommon", QObject::tr("Tab. 6-str. common"), 6, 0,     1.5, true,  true, false, false, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SHORTER,true,  false, true,  false, false, false, true, true),
             StaffType(StaffGroup::TAB, "tab6StrFull",   QObject::tr("Tab. 6-str. full"),   6, 0,     1.5, true,  true, false, true,  "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SLASHED,true,  true,  true,  true,  false, false, true, true),
             StaffType(StaffGroup::TAB, "tab4StrSimple", QObject::tr("Tab. 4-str. simple"), 4, 0,     1.5, true,  true, true,  false, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Sans",    9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::NONE,   true,  false, true,  false, false, false, true, false),
             StaffType(StaffGroup::TAB, "tab4StrCommon", QObject::tr("Tab. 4-str. common"), 4, 0,     1.5, true,  true, false, false, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SHORTER,true,  false, true,  false, false, false, true, true),
             StaffType(StaffGroup::TAB, "tab4StrFull",   QObject::tr("Tab. 4-str. full"),   4, 0,     1.5, true,  true, false, false, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SLASHED,true,  true,  true,  true,  false, false, true, true),
             StaffType(StaffGroup::TAB, "tab5StrSimple", QObject::tr("Tab. 5-str. simple"), 5, 0,     1.5, true,  true, true,  false, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Sans",    9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::NONE,   true,  false, true,  false, false, false, true, false),
             StaffType(StaffGroup::TAB, "tab5StrCommon", QObject::tr("Tab. 5-str. common"), 5, 0,     1.5, true,  true, false, false, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SHORTER,true,  false, true,  false, false, false, true, true),
             StaffType(StaffGroup::TAB, "tab5StrFull",   QObject::tr("Tab. 5-str. full"),   5, 0,     1.5, true,  true, false, false, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SLASHED,true,  true,  true,  true,  false, false, true, true),
             StaffType(StaffGroup::TAB, "tabUkulele",    QObject::tr("Tab. ukulele"),       4, 0,     1.5, true,  true, false, false, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SHORTER,true,  true,  true,  false, false, false, true, true),
             StaffType(StaffGroup::TAB, "tabBalajka",    QObject::tr("Tab. balalaika"),     3, -2,    1.5, true,  true, false, false, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SHORTER,true,  true,  true,  false, false, false, true, true),
    //         StaffType(StaffGroup::TAB, "tab6StrItalian",QObject::tr("Tab. 6-str. Italian"),6, 2,     1.5, false, true, true,  true,  "MuseScore Tab Italian",15, 0, true,  "MuseScore Tab Renaiss",10, 0, TablatureSymbolRepeat::NEVER, true,  TablatureMinimStyle::NONE,   true,  true,  false, false, true,  false, true, false),
    //         StaffType(StaffGroup::TAB, "tab6StrFrench", QObject::tr("Tab. 6-str. French"), 6, 2,     1.5, false, true, true,  true,  "MuseScore Tab French", 15, 0, true,  "MuseScore Tab Renaiss",10, 0, TablatureSymbolRepeat::NEVER, true,  TablatureMinimStyle::NONE,   false, false, false, false, false, false, false,false)
             StaffType(StaffGroup::TAB, "tab6StrItalian",QObject::tr("Tab. 6-str. Italian"),6, 0,     1.5, false, true, true,  true,  "MuseScore Tab Italian",15, 0, true,  "MuseScore Tab Renaiss",10, 0, TablatureSymbolRepeat::NEVER, true,  TablatureMinimStyle::NONE,   true,  true,  false, false, true,  false, true, false),
             StaffType(StaffGroup::TAB, "tab6StrFrench", QObject::tr("Tab. 6-str. French"), 6, 0,     1.5, false, true, true,  true,  "MuseScore Tab French", 15, 0, true,  "MuseScore Tab Renaiss",10, 0, TablatureSymbolRepeat::NEVER, true,  TablatureMinimStyle::NONE,   false, false, false, false, false, false, false,false)
             };
          }

    //---------------------------------------------------------
    //   spatium
    //---------------------------------------------------------

    qreal StaffType::spatium(Score* score) const
          {
          return score->spatium() * (small() ? score->styleD(StyleIdx::smallStaffMag) : 1.0) * userMag();
          }

    } // namespace Ms

↑ V601 The bool type is implicitly cast to the double type. Inspect the 14 argument.

↑ V601 The bool type is implicitly cast to the double type. Inspect the 14 argument.

↑ V601 The bool type is implicitly cast to the double type. Inspect the 14 argument.

↑ V601 The bool type is implicitly cast to the double type. Inspect the 14 argument.

↑ V601 The bool type is implicitly cast to the double type. Inspect the 14 argument.

↑ V601 The bool type is implicitly cast to the double type. Inspect the 14 argument.

↑ V601 The bool type is implicitly cast to the double type. Inspect the 14 argument.

↑ V601 The bool type is implicitly cast to the double type. Inspect the 14 argument.

↑ V601 The bool type is implicitly cast to the double type. Inspect the 14 argument.

↑ V601 The bool type is implicitly cast to the double type. Inspect the 14 argument.

↑ V601 The bool type is implicitly cast to the double type. Inspect the 14 argument.

↑ V601 The bool type is implicitly cast to the double type. Inspect the 14 argument.

↑ V601 The bool type is implicitly cast to the double type. Inspect the 14 argument.

↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: _beamLevel.

↑ V730 It is possible that not all members of a class are initialized inside the constructor. Consider inspecting: _beamLevel.

↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: _beamLength, _beamLevel, _beamGrid.