1 //=============================================================================
2 //  MuseScore
3 //  Music Composition & Notation
4 //
5 //  Copyright (C) 2010-2015 Werner Schweer & others
6 //
7 //  This program is free software; you can redistribute it and/or modify
8 //  it under the terms of the GNU General Public License version 2
9 //  as published by the Free Software Foundation and appearing in
10 //  the file LICENCE.GPL
11 //=============================================================================
12 
13 #include "stafftype.h"
14 
15 #include "chord.h"
16 #include "measure.h"
17 #include "mscore.h"
18 #include "navigate.h"
19 #include "staff.h"
20 #include "xml.h"
21 #include "score.h"
22 
23 #define TAB_DEFAULT_LINE_SP   (1.5)
24 #define TAB_RESTSYMBDISPL     2.0
25 
26 namespace Ms {
27 
28 //---------------------------------------------------------
29 //   StaffTypeTablature
30 //---------------------------------------------------------
31 
32 #define TAB_DEFAULT_DUR_YOFFS (-1.0)
33 
34 QList<TablatureFretFont>     StaffType::_fretFonts      = QList<TablatureFretFont>();
35 QList<TablatureDurationFont> StaffType::_durationFonts  = QList<TablatureDurationFont>();
36 
37 const char StaffType::groupNames[STAFF_GROUP_MAX][STAFF_GROUP_NAME_MAX_LENGTH] = {
38       QT_TRANSLATE_NOOP("Staff type group name", "Standard"),
39       QT_TRANSLATE_NOOP("Staff type group name", "Percussion"),
40       QT_TRANSLATE_NOOP("Staff type group name", "Tablature")
41       };
42 
43 const QString StaffType::fileGroupNames[STAFF_GROUP_MAX] = { "pitched", "percussion", "tablature" };
44 
45 //---------------------------------------------------------
46 //   StaffType
47 //---------------------------------------------------------
48 
StaffType()49 StaffType::StaffType()
50       {
51       // set reasonable defaults for type-specific members */
52       _symRepeat = TablatureSymbolRepeat::NEVER;
53       setDurationFontName(_durationFonts[0].displayName);
54       setFretFontName(_fretFonts[0].displayName);
55       }
56 
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,bool invisible,const QColor & color)57 StaffType::StaffType(StaffGroup sg, const QString& xml, const QString& name, int lines, int stpOff, qreal lineDist,
58    bool genClef, bool showBarLines, bool stemless, bool genTimeSig, bool genKeySig, bool showLedgerLines, bool invisible, const QColor& color) :
59    _group(sg), _xmlName(xml), _name(name),
60    _invisible(invisible),
61    _color(color),
62    _lines(lines),
63    _stepOffset(stpOff),
64    _lineDistance(Spatium(lineDist)),
65    _showBarlines(showBarLines),
66    _showLedgerLines(showLedgerLines),
67    _stemless(stemless),
68    _genClef(genClef),
69    _genTimesig(genTimeSig),
70    _genKeysig(genKeySig)
71       {
72       }
73 
StaffType(StaffGroup sg,const QString & xml,const QString & name,int lines,int stpOff,qreal lineDist,bool genClef,bool showBarLines,bool stemless,bool genTimesig,bool invisible,const QColor & color,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)74 StaffType::StaffType(StaffGroup sg, const QString& xml, const QString& name, int lines, int stpOff, qreal lineDist,
75    bool genClef,
76    bool showBarLines, bool stemless, bool genTimesig, bool invisible, const QColor& color,
77    const QString& durFontName, qreal durFontSize, qreal durFontUserY, qreal genDur,
78    const QString& fretFontName, qreal fretFontSize, qreal fretFontUserY,
79    TablatureSymbolRepeat symRepeat, bool linesThrough, TablatureMinimStyle minimStyle, bool onLines,
80    bool showRests, bool stemsDown, bool stemThrough, bool upsideDown, bool showTabFingering, bool useNumbers, bool showBackTied)
81       {
82       Q_UNUSED(invisible);
83       Q_UNUSED(color);
84       _group   = sg;
85       _xmlName = xml;
86       _name    = name;
87       setLines(lines);
88       setStepOffset(stpOff);
89       setLineDistance(Spatium(lineDist));
90       setGenClef(genClef);
91       setShowBarlines(showBarLines);
92       setStemless(stemless);
93       setGenTimesig(genTimesig);
94       setGenKeysig(sg != StaffGroup::TAB);
95       setDurationFontName(durFontName);
96       setDurationFontSize(durFontSize);
97       setDurationFontUserY(durFontUserY);
98       setGenDurations(genDur);
99       setFretFontName(fretFontName);
100       setFretFontSize(fretFontSize);
101       setFretFontUserY(fretFontUserY);
102       setSymbolRepeat(symRepeat);
103       setLinesThrough(linesThrough);
104       setMinimStyle(minimStyle);
105       setOnLines(onLines);
106       setShowRests(showRests);
107       setStemsDown(stemsDown);
108       setStemsThrough(stemThrough);
109       setUpsideDown(upsideDown);
110       setShowTabFingering(showTabFingering);
111       setUseNumbers(useNumbers);
112       setShowBackTied(showBackTied);
113       }
114 
115 
116 //---------------------------------------------------------
117 //   groupName
118 //---------------------------------------------------------
119 
groupName() const120 const char* StaffType::groupName() const
121       {
122       return groupName(_group);
123       }
124 
groupName(StaffGroup r)125 const char* StaffType::groupName(StaffGroup r)
126       {
127       if (r < StaffGroup::STANDARD || (int)r >= STAFF_GROUP_MAX)
128             r = StaffGroup::STANDARD;
129       return groupNames[(int)r];
130       }
131 
132 //---------------------------------------------------------
133 //   operator==
134 //---------------------------------------------------------
135 
operator ==(const StaffType & st) const136 bool StaffType::operator==(const StaffType& st) const
137       {
138       if (!isSameStructure(st) || st._xmlName != _xmlName) {        // common to all type groups
139             return false;
140             }
141       if (_group == StaffGroup::TAB) {                      // TAB-specific
142             bool v = st._durationFontIdx  == _durationFontIdx
143                && st._durationFontSize  == _durationFontSize
144                && st._durationFontUserY == _durationFontUserY
145                && st._fretFontIdx       == _fretFontIdx
146                && st._fretFontSize      == _fretFontSize
147                && st._fretFontUserY     == _fretFontUserY
148                ;
149             return v;
150             }
151       return true;
152       }
153 
154 //---------------------------------------------------------
155 //   isSameStructure
156 //
157 //    same as operator==, but ignores names and fonts
158 //---------------------------------------------------------
159 
isSameStructure(const StaffType & st) const160 bool StaffType::isSameStructure(const StaffType& st) const
161       {
162       if (st.group()         != group()                     // common to all type groups
163          || st._lines        != _lines
164          || st._stepOffset   != _stepOffset
165          || st._lineDistance != _lineDistance
166          || st._genClef      != _genClef
167          || st._showBarlines != _showBarlines
168          || st._stemless     != _stemless
169          || st._genTimesig   != _genTimesig)
170             return false;
171       if (_group == StaffGroup::STANDARD)                   // standard specific
172             if (st._noteHeadScheme != _noteHeadScheme)
173                   return false;
174       if (_group != StaffGroup::TAB) {                      // common to pitched and percussion
175             return st._genKeysig      == _genKeysig
176                && st._showLedgerLines == _showLedgerLines
177                ;
178             }
179       else {                                                // TAB-specific
180             return st._genDurations == _genDurations
181                && st._symRepeat     == _symRepeat
182                && st._linesThrough  == _linesThrough
183                && st._minimStyle    == _minimStyle
184                && st._onLines       == _onLines
185                && st._showBackTied  == _showBackTied
186                && st._showRests     == _showRests
187                && st._stemsDown     == _stemsDown
188                && st._stemsThrough  == _stemsThrough
189                && st._upsideDown    == _upsideDown
190                && st._showTabFingering    == _showTabFingering
191                && st._useNumbers    == _useNumbers
192                ;
193             }
194       }
195 
196 //---------------------------------------------------------
197 //   write
198 //---------------------------------------------------------
199 
write(XmlWriter & xml) const200 void StaffType::write(XmlWriter& xml) const
201       {
202       xml.stag(QString("StaffType group=\"%1\"").arg(fileGroupNames[(int)_group]));
203       if (!_xmlName.isEmpty())
204             xml.tag("name", _xmlName);
205       if (_lines != 5)
206             xml.tag("lines", _lines);
207       if (_lineDistance.val() != 1.0)
208             xml.tag("lineDistance", _lineDistance.val());
209       if (_yoffset.val() != 0.0)
210             xml.tag("yoffset", _yoffset.val());
211       if (_userMag != 1.0)
212             xml.tag("mag", _userMag);
213       if (_small)
214             xml.tag("small", _small);
215       if (_stepOffset)
216             xml.tag("stepOffset", _stepOffset);
217       if (!_genClef)
218             xml.tag("clef", _genClef);
219       if (_stemless) {
220             xml.tag("slashStyle", _stemless); // for backwards compatibility
221             xml.tag("stemless", _stemless);
222             }
223       if (!_showBarlines)
224             xml.tag("barlines", _showBarlines);
225       if (!_genTimesig)
226             xml.tag("timesig", _genTimesig);
227       if (_invisible)
228             xml.tag("invisible", _invisible);
229       if (_color != QColor(Qt::black))
230             xml.tag("color", _color);
231       if (_group == StaffGroup::STANDARD) {
232             xml.tag("noteheadScheme", NoteHead::scheme2name(_noteHeadScheme), NoteHead::scheme2name(NoteHead::Scheme::HEAD_NORMAL));
233             }
234       if (_group == StaffGroup::STANDARD || _group == StaffGroup::PERCUSSION) {
235             if (!_genKeysig)
236                   xml.tag("keysig", _genKeysig);
237             if (!_showLedgerLines)
238                   xml.tag("ledgerlines", _showLedgerLines);
239             }
240       else {
241             xml.tag("durations",        _genDurations);
242             xml.tag("durationFontName", _durationFonts[_durationFontIdx].displayName); // write font names anyway for backward compatibility
243             xml.tag("durationFontSize", _durationFontSize);
244             xml.tag("durationFontY",    _durationFontUserY);
245             xml.tag("fretFontName",     _fretFonts[_fretFontIdx].displayName);
246             xml.tag("fretFontSize",     _fretFontSize);
247             xml.tag("fretFontY",        _fretFontUserY);
248             if (_symRepeat != TablatureSymbolRepeat::NEVER)
249                   xml.tag("symbolRepeat", int(_symRepeat));
250             xml.tag("linesThrough",     _linesThrough);
251             xml.tag("minimStyle",       int(_minimStyle));
252             xml.tag("onLines",          _onLines);
253             xml.tag("showRests",        _showRests);
254             xml.tag("stemsDown",        _stemsDown);
255             xml.tag("stemsThrough",     _stemsThrough);
256             xml.tag("upsideDown",       _upsideDown);
257             xml.tag("showTabFingering", _showTabFingering, false);
258             xml.tag("useNumbers",       _useNumbers);
259             // only output "showBackTied" if different from !"stemless"
260             // to match the behaviour in 2.0.2 scores (or older)
261             if (_showBackTied != !_stemless)
262                   xml.tag("showBackTied",  _showBackTied);
263             }
264       xml.etag();
265       }
266 
267 //---------------------------------------------------------
268 //   read
269 //---------------------------------------------------------
270 
read(XmlReader & e)271 void StaffType::read(XmlReader& e)
272       {
273       QString group = e.attribute("group", fileGroupNames[(int)StaffGroup::STANDARD]);
274       if (group == fileGroupNames[(int)StaffGroup::TAB])
275             _group = StaffGroup::TAB;
276       else if (group == fileGroupNames[(int)StaffGroup::PERCUSSION])
277             _group = StaffGroup::PERCUSSION;
278       else if (group == fileGroupNames[(int)StaffGroup::STANDARD])
279             _group = StaffGroup::STANDARD;
280       else {
281             qDebug("StaffType::read: unknown group: %s", qPrintable(group));
282             _group = StaffGroup::STANDARD;
283             }
284 
285       if (_group == StaffGroup::TAB)
286             setGenKeysig(false);
287 
288       while (e.readNextStartElement()) {
289             const QStringRef& tag(e.name());
290             if (tag == "name")
291                   setXmlName(e.readElementText());
292             else if (tag == "lines")
293                   setLines(e.readInt());
294             else if (tag == "lineDistance")
295                   setLineDistance(Spatium(e.readDouble()));
296             else if (tag == "yoffset")
297                   _yoffset = Spatium(e.readDouble());
298             else if (tag == "mag")
299                   _userMag = e.readDouble();
300             else if (tag == "small")
301                   _small = e.readBool();
302             else if (tag == "stepOffset")
303                   _stepOffset = e.readInt();
304             else if (tag == "clef")
305                   setGenClef(e.readInt());
306             else if ((tag == "slashStyle") || (tag == "stemless")) {
307                   bool val = e.readInt() != 0;
308                   setStemless(val);
309                   setShowBackTied(!val);  // for compatibility with 2.0.2 scores where this prop
310                   }                       // was lacking and controlled by "slashStyle" instead
311             else if (tag == "barlines")
312                   setShowBarlines(e.readInt());
313             else if (tag == "timesig")
314                   setGenTimesig(e.readInt());
315             else if (tag == "noteheadScheme")
316                   setNoteHeadScheme(NoteHead::name2scheme(e.readElementText()));
317             else if (tag == "keysig")
318                   _genKeysig = e.readInt();
319             else if (tag == "ledgerlines")
320                   _showLedgerLines = e.readInt();
321             else if (tag == "invisible")
322                   _invisible = e.readInt();
323             else if (tag == "color")
324                   _color = e.readColor();
325             else if (tag == "durations")
326                   setGenDurations(e.readBool());
327             else if (tag == "durationFontName")
328                   setDurationFontName(e.readElementText());
329             else if (tag == "durationFontSize")
330                   setDurationFontSize(e.readDouble());
331             else if (tag == "durationFontY")
332                   setDurationFontUserY(e.readDouble());
333             else if (tag == "fretFontName")
334                   setFretFontName(e.readElementText());
335             else if (tag == "fretFontSize")
336                   setFretFontSize(e.readDouble());
337             else if (tag == "fretFontY")
338                   setFretFontUserY(e.readDouble());
339             else if (tag == "symbolRepeat")
340                   setSymbolRepeat( (TablatureSymbolRepeat) e.readInt() );
341             else if (tag == "linesThrough")
342                   setLinesThrough(e.readBool());
343             else if (tag == "minimStyle")
344                   setMinimStyle( (TablatureMinimStyle) e.readInt() );
345             else if (tag == "onLines")
346                   setOnLines(e.readBool());
347             else if (tag == "showRests")
348                   setShowRests(e.readBool());
349             else if (tag == "stemsDown")
350                   setStemsDown(e.readBool());
351             else if (tag == "stemsThrough")
352                   setStemsThrough(e.readBool());
353             else if (tag == "upsideDown")
354                   setUpsideDown(e.readBool());
355             else if (tag == "showTabFingering")
356                   setShowTabFingering(e.readBool());
357             else if (tag == "useNumbers")
358                   setUseNumbers(e.readBool());
359             else if (tag == "showBackTied")           // must be after reading "slashStyle"/"stemless" prop, as in older
360                   setShowBackTied(e.readBool());      // scores, this prop was lacking and controlled by "slashStyle"
361             else
362                   e.unknown();
363             }
364       }
365 
366 //---------------------------------------------------------
367 //   doty1
368 //    get y dot position of first repeat barline dot
369 //---------------------------------------------------------
370 
doty1() const371 qreal StaffType::doty1() const
372       {
373       return _lineDistance.val() * (static_cast<qreal>((_lines - 1)/2) - 0.5);
374       }
375 
376 //---------------------------------------------------------
377 //   doty2
378 //    get y dot position of second repeat barline dot
379 //---------------------------------------------------------
380 
doty2() const381 qreal StaffType::doty2() const
382       {
383       return _lineDistance.val() * (static_cast<qreal>(_lines/2) + 0.5);
384       }
385 
386 //---------------------------------------------------------
387 //   setOnLines
388 //---------------------------------------------------------
389 
setOnLines(bool val)390 void StaffType::setOnLines(bool val)
391       {
392       _onLines = val;
393       _durationMetricsValid = _fretMetricsValid = false;
394       }
395 
396 //---------------------------------------------------------
397 //   setDurationMetrics
398 //    checks whether the internally computed metrics are is still valid and re-computes them, if not
399 //---------------------------------------------------------
400 
setDurationMetrics() const401 void StaffType::setDurationMetrics() const
402       {
403       if (_durationMetricsValid && _refDPI == DPI)           // metrics are still valid
404             return;
405 
406 // QFontMetrics[F]() returns results unreliably rounded to integral pixels;
407 // use a scaled up font and then scale computed values down
408 //      QFontMetricsF fm(durationFont());
409       QFont font(durationFont());
410       font.setPointSizeF(_durationFontSize);
411       QFontMetricsF fm(font, MScore::paintDevice());
412       QString txt(_durationFonts[_durationFontIdx].displayValue, int(TabVal::NUM_OF));
413       QRectF bb( fm.tightBoundingRect(txt) );
414       // raise symbols by a default margin and, if marks are above lines, by half the line distance
415       // (converted from spatium units to raster units)
416       _durationGridYOffset = ( TAB_DEFAULT_DUR_YOFFS - (_onLines ? 0.0 : lineDistance().val()*0.5) ) * SPATIUM20;
417       // this is the bottomest point of any duration sign
418       _durationYOffset        = _durationGridYOffset;
419       // move symbols so that the lowest margin 'sits' on the base line:
420       // move down by the whole part above (negative) the base line
421       // ( -bb.y() ) then up by the whole height ( -bb.height() )
422       _durationYOffset        -= (bb.height() + bb.y()) / 100.0;
423       _durationBoxH           = bb.height() / 100.0;
424       _durationBoxY           = _durationGridYOffset - bb.height() / 100.0;
425       // keep track of the conditions under which metrics have been computed
426       _refDPI = DPI;
427       _durationMetricsValid = true;
428       }
429 
setFretMetrics() const430 void StaffType::setFretMetrics() const
431       {
432       if (_fretMetricsValid && _refDPI == DPI)
433             return;
434 
435       QFontMetricsF fm(fretFont(), MScore::paintDevice());
436       QRectF bb;
437       // compute vertical displacement
438       if (_useNumbers) {
439             // compute total height of used characters
440             QString txt = QString();
441             for (int idx = 0; idx < 10; idx++)  // use only first 10 digits
442                   txt.append(_fretFonts[_fretFontIdx].displayDigit[idx]);
443             bb = fm.tightBoundingRect(txt);
444             // for numbers: centre on '0': move down by the whole part above (negative)
445             // the base line ( -bb.y() ) then up by half the whole height ( -bb.height()/2 )
446             QRectF bx( fm.tightBoundingRect(_fretFonts[_fretFontIdx].displayDigit[0]) );
447             _fretYOffset = -(bx.y() + bx.height()/2.0);
448             // _fretYOffset = -(bb.y() + bb.height()/2.0);  // <- using bbox of all chars
449             }
450       else {
451             // compute total height of used characters
452             QString txt(_fretFonts[_fretFontIdx].displayLetter, NUM_OF_LETTERFRETS);
453             bb = fm.tightBoundingRect(txt);
454             // for letters: centre on the 'a' ascender, by moving down half of the part above the base line in bx
455             QRectF bx( fm.tightBoundingRect(_fretFonts[_fretFontIdx].displayLetter[0]) );
456             _fretYOffset = -bx.y() / 2.0;
457             }
458       // if on string, we are done; if between strings, raise by half line distance
459       if (!_onLines)
460             _fretYOffset -= lineDistance().val() * SPATIUM20 * 0.5;
461 
462       // from _fretYOffset, compute _fretBoxH and _fretBoxY
463       _fretBoxH = bb.height();
464       _fretBoxY = bb.y()  + _fretYOffset;
465 
466       // keep track of the conditions under which metrics have been computed
467       _refDPI = DPI;
468       _fretMetricsValid = true;
469       }
470 
471 //---------------------------------------------------------
472 //   setDurationFontName / setFretFontName
473 //---------------------------------------------------------
474 
setDurationFontName(const QString & name)475 void StaffType::setDurationFontName(const QString& name)
476       {
477       int idx;
478       for (idx = 0; idx < _durationFonts.size(); idx++)
479             if (_durationFonts[idx].displayName == name)
480                   break;
481       if (idx >= _durationFonts.size())
482             idx = 0;          // if name not found, use first font
483       _durationFont.setFamily(_durationFonts[idx].family);
484       _durationFontIdx = idx;
485       _durationMetricsValid = false;
486       }
487 
setFretFontName(const QString & name)488 void StaffType::setFretFontName(const QString& name)
489       {
490       int idx;
491       QString locName = name;
492       // convert old names for two built-in fonts which have changed of name
493       if (name == "MuseScore Tab Late Renaiss")
494             locName = "MuseScore Phalèse";
495       for (idx = 0; idx < _fretFonts.size(); idx++)
496             if (_fretFonts[idx].displayName == locName)
497                   break;
498       if (idx >= _fretFonts.size())
499             idx = 0;          // if name not found, use first font
500       _fretFont.setFamily(_fretFonts[idx].family);
501       _fretFontIdx = idx;
502       _fretMetricsValid = false;
503       }
504 
505 //---------------------------------------------------------
506 //   durationBoxH / durationBoxY
507 //---------------------------------------------------------
508 
durationBoxH() const509 qreal StaffType::durationBoxH() const
510       {
511       if (!_genDurations && !_stemless)
512             return 0.0;
513       setDurationMetrics();
514       return _durationBoxH;
515       }
516 
durationBoxY() const517 qreal StaffType::durationBoxY() const
518       {
519       if (!_genDurations && !_stemless)
520             return 0.0;
521       setDurationMetrics();
522       return _durationBoxY + _durationFontUserY * SPATIUM20;
523       }
524 
525 //---------------------------------------------------------
526 //   setDurationFontSize / setFretFontSize
527 //---------------------------------------------------------
528 
setDurationFontSize(qreal val)529 void StaffType::setDurationFontSize(qreal val)
530       {
531       _durationFontSize = val;
532       _durationFont.setPointSizeF(val);
533       _durationMetricsValid = false;
534       }
535 
setFretFontSize(qreal val)536 void StaffType::setFretFontSize(qreal val)
537       {
538       _fretFontSize = val;
539       _fretFont.setPointSizeF(val);
540       _fretMetricsValid = false;
541       }
542 
543 //---------------------------------------------------------
544 //   chordRestStemPosY / chordStemPos / chordStemPosBeam / chordStemLength
545 //
546 //    computes the stem data for the given chord, according to TAB settings
547 //    NOTE: unit: spatium, position: relative to chord (DIFFERENT from Chord own functions)
548 //
549 //   chordRestStemPosY
550 //          returns the vertical position of stem start point
551 //---------------------------------------------------------
552 
chordRestStemPosY(const ChordRest * chordRest) const553 qreal StaffType::chordRestStemPosY(const ChordRest *chordRest) const
554       {
555       if (stemThrough())            // does not make sense for "stems through staves" setting; just return top line vert. position
556             return 0.0;
557 
558       // if stems beside staff, position are fixed, but take into account delta for half notes
559       qreal delta =                             // displacement for half note stems (if used)
560             // if half notes have not a short stem OR not a half note => 0
561             (minimStyle() != TablatureMinimStyle::SHORTER || chordRest->durationType().type() != TDuration::DurationType::V_HALF) ?
562                   0.0 :
563                   // if stem is up, displace of half stem length down (positive)
564                   // if stem is down, displace of half stem length up (negative)
565                   (chordRest->up() ?
566                         -STAFFTYPE_TAB_DEFAULTSTEMLEN_UP : STAFFTYPE_TAB_DEFAULTSTEMLEN_DN) * 0.5;
567       // if fret marks above lines and chordRest is up, move half a line distance up
568       if (!onLines() && chordRest->up())
569             delta -= _lineDistance.val() *0.5;
570       qreal y = (chordRest->up() ? STAFFTYPE_TAB_DEFAULTSTEMPOSY_UP : (_lines-1)*_lineDistance.val() + STAFFTYPE_TAB_DEFAULTSTEMPOSY_DN)
571             + delta;
572       return y;
573       }
574 
575 //---------------------------------------------------------
576 //   chordStemPos
577 //    return position of note at other side of beam
578 //---------------------------------------------------------
579 
chordStemPos(const Chord * chord) const580 QPointF StaffType::chordStemPos(const Chord* chord) const
581       {
582       qreal y;
583       if (stemThrough())
584             // if stems are through staff, stem goes from fartest note string
585             y = (chord->up() ? chord->downString() : chord->upString()) * _lineDistance.val();
586       else
587             // if stems are beside staff, stem start point has a fixed vertical position,
588             // according to TAB parameters and stem up/down
589             y = chordRestStemPosY(chord);
590       return QPointF(chordStemPosX(chord), y);
591       }
592 
593 //---------------------------------------------------------
594 //   chordStemPosBeam
595 //          return position of note at beam side of stem
596 //---------------------------------------------------------
597 
chordStemPosBeam(const Chord * chord) const598 QPointF StaffType::chordStemPosBeam(const Chord* chord) const
599       {
600       qreal y = ( stemsDown() ? chord->downString() : chord->upString() ) * _lineDistance.val();
601 
602       return QPointF(chordStemPosX(chord), y);
603       }
604 
605 //---------------------------------------------------------
606 //   chordStemLength
607 //          return length of stem
608 //---------------------------------------------------------
609 
chordStemLength(const Chord * chord) const610 qreal StaffType::chordStemLength(const Chord* chord) const
611       {
612       qreal    stemLen;
613       // if stems are through staff, length should be computed by relevant chord algorithm;
614       // here, just return default length (= 1 'octave' = 3.5 line spaces)
615       if (stemThrough())
616             return STAFFTYPE_TAB_DEFAULTSTEMLEN_THRU * _lineDistance.val();
617       // if stems beside staff, length is fixed, but take into account shorter half note stems
618       else {
619             bool shrt = (minimStyle() == TablatureMinimStyle::SHORTER) && (chord->durationType().type() == TDuration::DurationType::V_HALF);
620             stemLen = (stemsDown() ? STAFFTYPE_TAB_DEFAULTSTEMLEN_DN : STAFFTYPE_TAB_DEFAULTSTEMLEN_UP)
621                         * (shrt ? STAFFTYPE_TAB_SHORTSTEMRATIO : 1.0);
622             }
623       // scale length by scale of parent chord, but relative to scale of context staff
624       return stemLen * chord->mag() / chord->staff()->mag(chord->tick());
625       }
626 
627 //---------------------------------------------------------
628 //   fretString / durationString
629 //
630 //    construct the text string for a given fret / duration
631 //---------------------------------------------------------
632 
633 static const QString unknownFret = QString("?");
634 
fretString(int fret,int string,bool ghost) const635 QString StaffType::fretString(int fret, int string, bool ghost) const
636       {
637       if (fret == FRET_NONE)
638             return unknownFret;
639       if (ghost)
640             return _fretFonts[_fretFontIdx].ghostChar;
641       else {
642             bool        hasFret;
643             QString     text  = tabBassStringPrefix(string, &hasFret);
644             if (!hasFret)           // if the notation does not allow to fret this string,
645                   return text;      // return the prefix only
646             // otherwise, add to prefix the relevant digit/letter string
647             return text +
648                   (_useNumbers ?
649                         (fret >= NUM_OF_DIGITFRETS  ? unknownFret : _fretFonts[_fretFontIdx].displayDigit[fret]) :
650                         (fret >= NUM_OF_LETTERFRETS ? unknownFret : _fretFonts[_fretFontIdx].displayLetter[fret]) );
651            }
652       }
653 
durationString(TDuration::DurationType type,int dots) const654 QString StaffType::durationString(TDuration::DurationType type, int dots) const
655       {
656       QString s = _durationFonts[_durationFontIdx].displayValue[int(type)];
657       for(int count=0; count < dots; count++)
658             s.append(_durationFonts[_durationFontIdx].displayDot);
659       return s;
660       }
661 
662 //---------------------------------------------------------
663 //    tabBassStringPrefix
664 //
665 //    returns a QString (possibly empty) with the prefix identifying a bass string in TAB's;
666 //    can deal with non-bass strings (i.e. regular TAB lines).
667 //
668 //    Implements the specifics of historic notations for bass lines (i.e. strings outside
669 //    the lines of the tab), both Italian and French.
670 //
671 //    strg   the instrument physical string ordinal (0 = topmost string, may exceed the number
672 //                of lines actually present in the TAB to reference a bass string)
673 //    bool   pntr to a bool receiving the info if notation allows to express a fret number or not
674 //                (this is potentially different from the fact that the instrument string itself can be fretted or not)
675 //---------------------------------------------------------
676 
tabBassStringPrefix(int strg,bool * hasFret) const677 QString StaffType::tabBassStringPrefix(int strg, bool* hasFret) const
678       {
679       *hasFret    = true;           // assume notation allows to fret this string
680       int bassStrgIdx  = (strg >= _lines ? strg - _lines + 1 : 0);
681       if (_useNumbers) {
682             // if above the max bass string which can be fretted with number notation
683             // return a number with the string index
684             if (bassStrgIdx > NUM_OF_BASSSTRINGS_WITH_NUMBER) {
685                   *hasFret    = false;
686                   return _fretFonts[_fretFontIdx].displayDigit[strg+1];
687                   }
688             // if a frettable bass string, return an empty string
689             return QString();
690             }
691      else {
692             // bass string notation
693             // if above the max bass string which can be fretted with letter notation
694             // return a number with the bass string index itself
695             if (bassStrgIdx > NUM_OF_BASSSTRINGS_WITH_LETTER) {
696                   *hasFret    = false;
697                   return _fretFonts[_fretFontIdx].displayDigit[bassStrgIdx-1];
698                   }
699             // if a frettable bass string, return a character with the relevant num. of slashes;
700             // note that the number of slashes is bassStrgIdx-1 (1st bass has no slash)
701             // and slashChar[] is 0-based (slashChar[0] => 1 slash, ...), whence the -2
702             QString prefix    = bassStrgIdx > 1 ?
703                         QString(_fretFonts[_fretFontIdx].slashChar[bassStrgIdx - 2]) : QString();
704             return prefix;
705             }
706       }
707 
708 //---------------------------------------------------------
709 //   drawInputStringMarks
710 //
711 //    in TAB's, draws the marks within the input 'blue cursor' required to identify the current target input string.
712 //
713 //    Implements the specific of historic TAB styles for instruments with more strings than TAB lines.
714 //    For strings normally represented by TAB lines, no mark is required.
715 //    For strings not represented by TAB lines (e.g. bass strings in lutes and similar),
716 //    either a sequence of slashes OR some ledger line-like lines OR the ordinal of the string
717 //    are used, according to the TAB style (French or Italian) and the string position.
718 //
719 //    Note: assumes the string parameter is within legal bounds, i.e.:
720 //    0 <= string <= [instrument strings] - 1
721 //
722 //    p       the QPainter to draw into
723 //    string  the instrument physical string for which to draw the mark (0 = top string)
724 //    voice   the current input voice (affects mark colour)
725 //    rect    the rect of the 'blue rectangle' showing the input position
726 //---------------------------------------------------------
727 
728 static const qreal      LEDGER_LINE_THICKNESS   = 0.15;     // in sp
729 static const qreal      LEDGER_LINE_LEFTX       = 0.25;     // in % of cursor rectangle width
730 static const qreal      LEDGER_LINE_RIGHTX      = 0.75;     // in % of cursor rectangle width
731 
drawInputStringMarks(QPainter * p,int string,int voice,QRectF rect) const732 void StaffType::drawInputStringMarks(QPainter *p, int string, int voice, QRectF rect) const
733       {
734       if (_group != StaffGroup::TAB)
735             return;
736       qreal       spatium     = SPATIUM20;
737       qreal       lineDist    = _lineDistance.val() * spatium;
738       bool        hasFret;
739       QString     text        = tabBassStringPrefix(string, &hasFret);
740 //    qreal       lw          = point(score()->styleS(Sid::ledgerLineWidth));  // no access to score form here
741       qreal       lw          = LEDGER_LINE_THICKNESS * spatium;                    // use a fixed width
742       QPen        pen(MScore::selectColor[voice].lighter(SHADOW_NOTE_LIGHT), lw);
743       p->setPen(pen);
744       // draw conventional 'ledger lines', if required
745       int         numOfLedgerLines  = numOfTabLedgerLines(string);
746       qreal       x1                = rect.x() + rect.width() * LEDGER_LINE_LEFTX;
747       qreal       x2                = rect.x() + rect.width() * LEDGER_LINE_RIGHTX;
748       // cursor rect is 1 line dist. high, and it is:
749       // centred on the line for "frets on strings"    => lower top ledger line 1/2 line dist.
750       // sitting on the line for "frets above strings" => lower top ledger line 1 full line dist
751       qreal y     = rect.top() + lineDist * (_onLines ? 0.5 : 1.0);
752       for (int i = 0; i < numOfLedgerLines; i++) {
753             p->drawLine(QLineF(x1, y, x2, y));
754             y += lineDist / numOfLedgerLines; // insert other lines between top line and tab body
755             }
756       // draw the text, if any
757       if (!text.isEmpty()) {
758             QFont f = fretFont();
759             f.setPointSizeF(f.pointSizeF() * MScore::pixelRatio);
760             p->setFont(f);
761             p->drawText(QPointF(rect.left(), rect.top() + lineDist), text);
762             }
763       }
764 
765 //---------------------------------------------------------
766 //   numOfLedgerLines
767 //
768 //    in TAB's, returns the number of ledgerlines needed by bass lines in some TAB styles.
769 //
770 //    Returns 0 if staff is not a TAB, if a TAB but style does not use ledger lines
771 //    or ledger lines do not apply to the given string.
772 //---------------------------------------------------------
773 
numOfTabLedgerLines(int string) const774 int StaffType::numOfTabLedgerLines(int string) const
775       {
776       if (_group != StaffGroup::TAB || !_useNumbers)
777             return 0;
778 
779       int   numOfLedgers= string < 0 ? -string : string - _lines + 1;
780       return (numOfLedgers >= 1 && numOfLedgers <= NUM_OF_BASSSTRINGS_WITH_NUMBER ? numOfLedgers : 0);
781       }
782 
783 //---------------------------------------------------------
784 //   physStringToVisual / visualStringToPhys
785 //
786 //    returns the string ordinal in visual order (top to down) from a string ordinal in physical order
787 //    or viceversa: manages upsideDown
788 //---------------------------------------------------------
789 
physStringToVisual(int strg) const790 int StaffType::physStringToVisual(int strg) const
791       {
792       if (strg < 0)                       // if above top string, return top string
793             strg = 0;
794 //      // NO: bass strings may exist, which are in addition to tab string lines
795 //      if (strg >= _lines)                 // if physical string has no visual representation,
796 //            strg = _lines - 1;            // reduce to nearest visual line
797       // if TAB upside down, flip around top line
798       return (_upsideDown ? _lines - 1 - strg : strg);
799       }
800 
visualStringToPhys(int line) const801 int StaffType::visualStringToPhys(int line) const
802       {
803       // if TAB upside down, reverse string number
804       line = (_upsideDown ? _lines - 1 - line : line);
805 
806       if (line < 0)           // if above top string, reduce to top string
807             line = 0;
808 // NO: bass strings may exist, which are in addition to tab string lines
809 //      if (line >= _lines)
810 //            line = _lines - 1;
811       return line;
812       }
813 
814 //---------------------------------------------------------
815 //   physStringToYOffset
816 //
817 //    returns the string Y offset from a string ordinal in physical order:
818 //    manages upsideDown and extra bass strings.
819 //
820 //    The returned values is in sp. and is relative to the staff top line.
821 //
822 //    Note: the difference with physStringToVisual() is that this function takes into account
823 //          peculiarities of bass string notations.
824 //---------------------------------------------------------
825 
physStringToYOffset(int strg) const826 qreal StaffType::physStringToYOffset(int strg) const
827       {
828       qreal yOffset = strg;                     // the y offset of the visual string, as a multiple of line distance
829       if (yOffset < 0)                          // if above top physical string, limit to top string
830             yOffset = 0;
831       if (yOffset >= _lines) {                  // if physical string 'below' tab lines,
832             yOffset = _lines;                   // reduce to first string 'below' tab body
833             if (!_useNumbers)                   // with letters, add some space for the slashes ascender
834                   yOffset = _onLines ? _lines : _lines + STAFFTYPE_TAB_BASSSLASH_YOFFSET;
835             }
836       // if TAB upside down, flip around top line
837       yOffset = _upsideDown ? (qreal)(_lines - 1) - yOffset : yOffset;
838       return yOffset * _lineDistance.val();
839       }
840 
841 //---------------------------------------------------------
842 //   TabDurationSymbol
843 //---------------------------------------------------------
844 
TabDurationSymbol(Score * s)845 TabDurationSymbol::TabDurationSymbol(Score* s)
846    : Element(s, ElementFlag::NOT_SELECTABLE)
847       {
848       setGenerated(true);
849       _beamGrid   = TabBeamGrid::NONE;
850       _beamLength = 0.0;
851       _tab        = 0;
852       _text       = QString();
853       }
854 
TabDurationSymbol(Score * s,const StaffType * tab,TDuration::DurationType type,int dots)855 TabDurationSymbol::TabDurationSymbol(Score* s, const StaffType* tab, TDuration::DurationType type, int dots)
856    : Element(s, ElementFlag::NOT_SELECTABLE)
857       {
858       setGenerated(true);
859       _beamGrid   = TabBeamGrid::NONE;
860       _beamLength = 0.0;
861       setDuration(type, dots, tab);
862       }
863 
TabDurationSymbol(const TabDurationSymbol & e)864 TabDurationSymbol::TabDurationSymbol(const TabDurationSymbol& e)
865    : Element(e)
866       {
867       _tab = e._tab;
868       _text = e._text;
869       }
870 
871 //---------------------------------------------------------
872 //   layout
873 //---------------------------------------------------------
874 
layout()875 void TabDurationSymbol::layout()
876       {
877       if(!_tab) {
878             setbbox(QRectF());
879             return;
880             }
881       qreal _spatium    = spatium();
882       qreal hbb, wbb, xbb, ybb;     // bbox sizes
883       qreal xpos, ypos;             // position coords
884 
885       _beamGrid = TabBeamGrid::NONE;
886       Chord* chord = parent() && parent()->isChord() ? toChord(parent()) : nullptr;
887       // if no chord (shouldn't happens...) or not a special beam mode, layout regular symbol
888       if (!chord || !chord->isChord() ||
889             (chord->beamMode() != Beam::Mode::BEGIN && chord->beamMode() != Beam::Mode::MID &&
890                   chord->beamMode() != Beam::Mode::END) ) {
891             QFontMetricsF fm(_tab->durationFont(), MScore::paintDevice());
892             hbb   = _tab->durationBoxH();
893             wbb   = fm.width(_text);
894             xbb   = 0.0;
895             xpos  = 0.0;
896             ypos  = _tab->durationFontYOffset();
897             ybb   = _tab->durationBoxY() - ypos;
898             // with rests, move symbol down by half its displacement from staff
899             if (parent() && parent()->isRest()) {
900                   ybb  += TAB_RESTSYMBDISPL * _spatium;
901                   ypos += TAB_RESTSYMBDISPL * _spatium;
902                   }
903             }
904       // if on a chord with special beam mode, layout an 'English'-style duration grid
905       else {
906             TablatureDurationFont font = _tab->_durationFonts[_tab->_durationFontIdx];
907             hbb   = font.gridStemHeight * _spatium;         // bbox height is stem height
908             wbb   = font.gridStemWidth  * _spatium;         // bbox width is stem width
909             xbb   = -wbb * 0.5;                             // bbox is half at left and half at right of stem centre
910             ybb   = -hbb;                                   // bbox top is at top of stem height
911             xpos  = 0.75 * _spatium;                        // conventional centring of stem on fret marks
912             ypos  = _tab->durationGridYOffset();            // stem start is at bottom
913             if (chord->beamMode() == Beam::Mode::BEGIN) {
914                   _beamGrid   = TabBeamGrid::INITIAL;
915                   _beamLength = 0.0;
916                   }
917             else if (chord->beamMode() == Beam::Mode::MID || chord->beamMode() == Beam::Mode::END) {
918                   _beamLevel  = static_cast<int>(chord->durationType().type()) - static_cast<int>(font.zeroBeamLevel);
919                   _beamGrid   = (_beamLevel < 1 ? TabBeamGrid::INITIAL : TabBeamGrid::MEDIALFINAL);
920                   // _beamLength and bbox x and width will be set in layout2(),
921                   // once horiz. positions of chords are known
922                   }
923             }
924       // set this' mag from parent chord mag (include staff mag)
925       qreal mag = chord != nullptr ? chord->mag() : 1.0;
926       setMag(mag);
927       mag = magS();           // local mag * score mag
928       // set magnified bbox and position
929       bbox().setRect(xbb * mag, ybb * mag, wbb * mag, hbb * mag);
930       setPos(xpos*mag, ypos*mag);
931       }
932 
933 //---------------------------------------------------------
934 //   layout2
935 //
936 //    Second step: after horizontal positions of elements involved are defined,
937 //    compute width of 'grid beams'
938 //---------------------------------------------------------
939 
layout2()940 void TabDurationSymbol::layout2()
941       {
942       // if not within a TAB or not a MEDIALFINAL grid element, do nothing
943       if(!_tab || _beamGrid != TabBeamGrid::MEDIALFINAL)
944             return;
945 
946       // get 'grid' beam length from page positions of this' chord and previous chord
947       Chord*      chord       = toChord(parent());
948       ChordRest*  prevChord   = prevChordRest(chord, true);
949       if (chord == nullptr || prevChord == nullptr)
950             return;
951       qreal       mags        = magS();
952       qreal       beamLen     = prevChord->pagePos().x() - chord->pagePos().x();    // negative
953       // page pos. difference already includes any magnification in effect:
954       // scale it down, as it will be magnified again during drawing
955       _beamLength = beamLen / mags;
956       // update bbox x and w, but keep current y and h
957       bbox().setX(beamLen);
958       // set bbox width to half a stem width (magnified) plus beam length (already magnified)
959       bbox().setWidth(_tab->_durationFonts[_tab->_durationFontIdx].gridStemWidth * spatium() * 0.5 * mags - beamLen);
960       }
961 
962 //---------------------------------------------------------
963 //   draw
964 //---------------------------------------------------------
965 
draw(QPainter * painter) const966 void TabDurationSymbol::draw(QPainter* painter) const
967       {
968       if (!_tab)
969             return;
970 
971       if (_repeat && (_tab->symRepeat() == TablatureSymbolRepeat::SYSTEM)) {
972             Chord* chord = toChord(parent());
973             ChordRest* prevCR = prevChordRest(chord);
974             if (prevCR && (chord->measure()->system() == prevCR->measure()->system()))
975                   return;
976             }
977 
978       qreal mag = magS();
979       qreal imag = 1.0 / mag;
980 
981       QPen  pen(curColor());
982       painter->setPen(pen);
983       painter->scale(mag, mag);
984       if (_beamGrid == TabBeamGrid::NONE) {
985             // if no beam grid, draw symbol
986             QFont f(_tab->durationFont());
987             f.setPointSizeF(f.pointSizeF() * MScore::pixelRatio);
988             painter->setFont(f);
989             painter->drawText(QPointF(0.0, 0.0), _text);
990             }
991       else {
992             // if beam grid, draw stem line
993             TablatureDurationFont& font = _tab->_durationFonts[_tab->_durationFontIdx];
994             qreal _spatium = spatium();
995             pen.setCapStyle(Qt::FlatCap);
996             pen.setWidthF(font.gridStemWidth * _spatium);
997             painter->setPen(pen);
998             // take stem height from bbox, but de-magnify it, as drawing is already magnified
999             qreal h     = bbox().y() / mag;
1000             painter->drawLine(QPointF(0.0, h), QPointF(0.0, 0.0) );
1001             // if beam grid is medial/final, draw beam lines too: lines go from mid of
1002             // previous stem (delta x stored in _beamLength) to mid of this' stem (0.0)
1003             if (_beamGrid == TabBeamGrid::MEDIALFINAL) {
1004                   pen.setWidthF(font.gridBeamWidth * _spatium);
1005                   painter->setPen(pen);
1006                   // lower height available to beams by half a beam width,
1007                   // so that top beam upper border aligns with stem top
1008                   h += (font.gridBeamWidth * _spatium) * 0.5;
1009                   // draw beams equally spaced within the stem height (this is
1010                   // different from modern engraving, but common in historic prints)
1011                   qreal step  = -h / _beamLevel;
1012                   qreal y     = h;
1013                   for (int i = 0; i < _beamLevel; i++, y += step)
1014                         painter->drawLine(QPointF(_beamLength, y), QPointF(0.0, y) );
1015                   }
1016             }
1017       painter->scale(imag, imag);
1018       }
1019 
1020 //---------------------------------------------------------
1021 //   STATIC FUNCTIONS FOR FONT CONFIGURATION MANAGEMENT
1022 //---------------------------------------------------------
1023 
read(XmlReader & e)1024 bool TablatureFretFont::read(XmlReader& e)
1025       {
1026       defPitch    = 9.0;
1027       defYOffset  = 0.0;
1028       while (e.readNextStartElement()) {
1029             const QStringRef& tag(e.name());
1030 
1031             int val = e.intAttribute("value");
1032 
1033             if (tag == "family")
1034                   family = e.readElementText();
1035             else if (tag == "displayName")
1036                   displayName = e.readElementText();
1037             else if (tag == "defaultPitch")
1038                   defPitch = e.readDouble();
1039             else if (tag == "defaultYOffset")
1040                   defYOffset = e.readDouble();
1041             else if (tag == "mark") {
1042                   QString     sval = e.attribute("value");
1043                   int         num  = e.intAttribute("number", 1);
1044                   QString     txt(e.readElementText());
1045                   if (sval.size() < 1)
1046                         return false;
1047                   if (sval == "x")
1048                         xChar = txt[0];
1049                   else if (sval == "ghost")
1050                         ghostChar = txt[0];
1051                   else if (sval == "slash") {
1052                         // limit within legal range
1053                         if (num < 1)
1054                               num = 1;
1055                         if (num > NUM_OF_BASSSTRING_SLASHES)
1056                               num = NUM_OF_BASSSTRING_SLASHES;
1057                         slashChar[num-1] = txt;
1058                         }
1059                   }
1060             else if (tag == "fret") {
1061                   bool bLetter = e.intAttribute("letter");
1062                   QString txt(e.readElementText());
1063                   if (bLetter) {
1064                         if (val >= 0 && val < NUM_OF_LETTERFRETS)
1065                               displayLetter[val] = txt[0];
1066                         }
1067                   else {
1068                         if (val >= 0 && val < NUM_OF_DIGITFRETS)
1069                               displayDigit[val] = txt;
1070                         }
1071                   }
1072             else {
1073                   e.unknown();
1074                   return false;
1075                   }
1076             }
1077       return true;
1078       }
1079 
read(XmlReader & e)1080 bool TablatureDurationFont::read(XmlReader& e)
1081       {
1082       while (e.readNextStartElement()) {
1083             const QStringRef& tag(e.name());
1084 
1085             if (tag == "family")
1086                   family = e.readElementText();
1087             else if (tag == "displayName")
1088                   displayName = e.readElementText();
1089             else if (tag == "defaultPitch")
1090                   defPitch = e.readDouble();
1091             else if (tag == "defaultYOffset")
1092                   defYOffset = e.readDouble();
1093             else if (tag == "beamWidth")
1094                   gridBeamWidth = e.readDouble();
1095             else if (tag == "stemHeight")
1096                   gridStemHeight = e.readDouble();
1097             else if (tag == "stemWidth")
1098                   gridStemWidth = e.readDouble();
1099             else if (tag == "zeroBeamValue") {
1100                   QString val(e.readElementText());
1101                   if (val == "longa")
1102                         zeroBeamLevel = TDuration::DurationType::V_LONG;
1103                   else if (val == "brevis")
1104                         zeroBeamLevel = TDuration::DurationType::V_BREVE;
1105                   else if (val == "semibrevis")
1106                         zeroBeamLevel = TDuration::DurationType::V_WHOLE;
1107                   else if (val == "minima")
1108                         zeroBeamLevel = TDuration::DurationType::V_HALF;
1109                   else if (val == "semiminima")
1110                         zeroBeamLevel = TDuration::DurationType::V_QUARTER;
1111                   else if (val == "fusa")
1112                         zeroBeamLevel = TDuration::DurationType::V_EIGHTH;
1113                   else if (val == "semifusa")
1114                         zeroBeamLevel = TDuration::DurationType::V_16TH;
1115                   else if (val == "32")
1116                         zeroBeamLevel = TDuration::DurationType::V_32ND;
1117                   else if (val == "64")
1118                         zeroBeamLevel = TDuration::DurationType::V_64TH;
1119                   else if (val == "128")
1120                         zeroBeamLevel = TDuration::DurationType::V_128TH;
1121                   else if (val == "256")
1122                         zeroBeamLevel = TDuration::DurationType::V_256TH;
1123                   else if (val == "512")
1124                         zeroBeamLevel = TDuration::DurationType::V_512TH;
1125                   else if (val == "1024")
1126                         zeroBeamLevel = TDuration::DurationType::V_1024TH;
1127                   else
1128                         e.unknown();
1129                   }
1130             else if (tag == "duration") {
1131                   QString val = e.attribute("value");
1132                   QString txt(e.readElementText());
1133                   QChar chr = txt[0];
1134                   if (val == "longa")
1135                         displayValue[int(TabVal::VAL_LONGA)] = chr;
1136                   else if (val == "brevis")
1137                         displayValue[int(TabVal::VAL_BREVIS)] = chr;
1138                   else if (val == "semibrevis")
1139                         displayValue[int(TabVal::VAL_SEMIBREVIS)] = chr;
1140                   else if (val == "minima")
1141                         displayValue[int(TabVal::VAL_MINIMA)] = chr;
1142                   else if (val == "semiminima")
1143                         displayValue[int(TabVal::VAL_SEMIMINIMA)] = chr;
1144                   else if (val == "fusa")
1145                         displayValue[int(TabVal::VAL_FUSA)] = chr;
1146                   else if (val == "semifusa")
1147                         displayValue[int(TabVal::VAL_SEMIFUSA)] = chr;
1148                   else if (val == "32")
1149                         displayValue[int(TabVal::VAL_32)] = chr;
1150                   else if (val == "64")
1151                         displayValue[int(TabVal::VAL_64)] = chr;
1152                   else if (val == "128")
1153                         displayValue[int(TabVal::VAL_128)] = chr;
1154                   else if (val == "256")
1155                         displayValue[int(TabVal::VAL_256)] = chr;
1156                   else if (val == "512")
1157                         displayValue[int(TabVal::VAL_512)] = chr;
1158                   else if (val == "1024")
1159                         displayValue[int(TabVal::VAL_1024)] = chr;
1160                   else if (val == "dot")
1161                         displayDot = chr;
1162                   else
1163                         e.unknown();
1164                   }
1165             else {
1166                   e.unknown();
1167                   return false;
1168                   }
1169             }
1170       return true;
1171       }
1172 
1173 //---------------------------------------------------------
1174 //   Read Configuration File
1175 //
1176 //    reads a configuration and appends read data to g_TABFonts
1177 //    resets everything and reads the built-in config file if fileName is null or empty
1178 //---------------------------------------------------------
1179 
readConfigFile(const QString & fileName)1180 bool StaffType::readConfigFile(const QString& fileName)
1181       {
1182       QString path;
1183 
1184       if (fileName == 0 || fileName.isEmpty()) {       // defaults to built-in xml
1185 #ifdef Q_OS_IOS
1186             {
1187             extern QString resourcePath();
1188             QString rpath = resourcePath();
1189             path = rpath + QString("/fonts_tablature.xml");
1190             }
1191 #else
1192             path = ":/fonts/fonts_tablature.xml";
1193 #endif
1194             _durationFonts.clear();
1195             _fretFonts.clear();
1196             }
1197       else
1198             path = fileName;
1199 
1200       QFileInfo fi(path);
1201       QFile f(path);
1202 
1203       if (!fi.exists() || !f.open(QIODevice::ReadOnly)) {
1204             MScore::lastError = QObject::tr("Cannot open tablature font description:\n%1\n%2").arg(f.fileName(), f.errorString());
1205             qDebug("StaffTypeTablature::readConfigFile failed: <%s>", qPrintable(path));
1206             return false;
1207             }
1208 
1209       XmlReader e(&f);
1210       while (e.readNextStartElement()) {
1211             if (e.name() == "museScore") {
1212                   while (e.readNextStartElement()) {
1213                         const QStringRef& tag(e.name());
1214                         if (tag == "fretFont") {
1215                               TablatureFretFont ff;
1216                               if (ff.read(e))
1217                                     _fretFonts.append(ff);
1218                               else
1219                                     continue;
1220                               }
1221                         else if (tag == "durationFont") {
1222                               TablatureDurationFont df;
1223                               if (df.read(e))
1224                                     _durationFonts.append(df);
1225                               else
1226                                     continue;
1227                               }
1228                         else
1229                               e.unknown();
1230                         }
1231                   return true;
1232                   }
1233             }
1234       return false;
1235       }
1236 
1237 //---------------------------------------------------------
1238 //   fontNames
1239 //
1240 //    returns a list of display names for the fonts  configured to work with Tablatures;
1241 //    the index of a name in the list can be used to retrieve the font data with fontData()
1242 //---------------------------------------------------------
1243 
fontNames(bool bDuration)1244 QList<QString> StaffType::fontNames(bool bDuration)
1245       {
1246       QList<QString> names;
1247       if(bDuration)
1248             foreach(const TablatureDurationFont& f, _durationFonts)
1249                   names.append(f.displayName);
1250       else
1251             foreach(const TablatureFretFont& f, _fretFonts)
1252                   names.append(f.displayName);
1253       return names;
1254       }
1255 
1256 //---------------------------------------------------------
1257 //   fontData
1258 //
1259 //    retrieves data about a Tablature font.
1260 //    returns: true if idx is valid | false if it is not
1261 // any of the pointer parameter can be null, if that datum is not needed
1262 //---------------------------------------------------------
1263 
fontData(bool bDuration,int nIdx,QString * pFamily,QString * pDisplayName,qreal * pSize,qreal * pYOff)1264 bool StaffType::fontData(bool bDuration, int nIdx, QString* pFamily, QString* pDisplayName,
1265    qreal* pSize, qreal* pYOff)
1266       {
1267       if (bDuration) {
1268             if (nIdx >= 0 && nIdx < _durationFonts.size()) {
1269                   TablatureDurationFont f = _durationFonts.at(nIdx);
1270                   if (pFamily)      *pFamily          = f.family;
1271                   if (pDisplayName) *pDisplayName     = f.displayName;
1272                   if (pSize)        *pSize            = f.defPitch;
1273                   if (pYOff)        *pYOff            = f.defYOffset;
1274                   return true;
1275                   }
1276             }
1277       else {
1278             if (nIdx >= 0 && nIdx < _fretFonts.size()) {
1279                   TablatureFretFont f = _fretFonts.at(nIdx);
1280                   if (pFamily)      *pFamily          = f.family;
1281                   if (pDisplayName) *pDisplayName     = f.displayName;
1282                   if (pSize)        *pSize            = f.defPitch;
1283                   if (pYOff)        *pYOff            = f.defYOffset;
1284                   return true;
1285                   }
1286             }
1287       return false;
1288       }
1289 
1290 //=========================================================
1291 //
1292 //   BUILT-IN STAFF TYPES and STAFF TYPE PRESETS
1293 //
1294 //=========================================================
1295 
1296 static const int _defaultPreset[STAFF_GROUP_MAX] =
1297       { 0,              // default pitched preset is "stdNormal"
1298         3,              // default percussion preset is "perc5lines"
1299         5               // default tab preset is "tab6StrCommon"
1300       };
1301 
1302 static const QString _emptyString = QString();
1303 
1304 //---------------------------------------------------------
1305 //   Static functions for StaffType presets
1306 //---------------------------------------------------------
1307 
preset(StaffTypes idx)1308 const StaffType* StaffType::preset(StaffTypes idx)
1309       {
1310       if (int(idx) < 0 || int(idx) >= int(_presets.size()))
1311             return &_presets[0];
1312       return &_presets[int(idx)];
1313       }
1314 
presetFromXmlName(QString & xmlName)1315 const StaffType* StaffType::presetFromXmlName(QString& xmlName)
1316       {
1317       for (size_t i = 0; i < _presets.size(); ++i) {
1318             if (_presets[i].xmlName() == xmlName)
1319                   return &_presets[i];
1320             }
1321       return 0;
1322       }
1323 #if 0
1324 const StaffType* StaffType::presetFromName(QString& name)
1325       {
1326       for (size_t i = 0; i < _presets.size(); ++i) {
1327             if (_presets[i].name() == name)
1328                   return &_presets[i];
1329             }
1330       return 0;
1331       }
1332 #endif
getDefaultPreset(StaffGroup grp)1333 const StaffType* StaffType::getDefaultPreset(StaffGroup grp)
1334       {
1335       int _idx = _defaultPreset[int(grp)];
1336       return &_presets[_idx];
1337       }
1338 
1339 //---------------------------------------------------------
1340 //   initStaffTypes
1341 //---------------------------------------------------------
1342 
1343 std::vector<StaffType> StaffType::_presets;
1344 
initStaffTypes()1345 void StaffType::initStaffTypes()
1346       {
1347       readConfigFile(0);          // get TAB font config, before initStaffTypes()
1348 
1349       // keep in sync with enum class StaffTypes
1350       _presets = {
1351 //                       group,              xml-name,  human-readable-name,          lin stpOff  dist clef   bars stmless time  key    ledger invis     color
1352          StaffType(StaffGroup::STANDARD,   "stdNormal", QObject::tr("Standard"),        5, 0,     1,   true,  true, false, true, true,  true,  false,  Qt::black),
1353 //       StaffType(StaffGroup::PERCUSSION, "perc1Line", QObject::tr("Perc. 1 line"),    1, -4,    1,   true,  true, false, true, false, true,  false,  Qt::black),
1354          StaffType(StaffGroup::PERCUSSION, "perc1Line", QObject::tr("Perc. 1 line"),    1, 0,     1,   true,  true, false, true, false, true,  false,  Qt::black),
1355          StaffType(StaffGroup::PERCUSSION, "perc3Line", QObject::tr("Perc. 3 lines"),   3, 0,     2,   true,  true, false, true, false, true,  false,  Qt::black),
1356          StaffType(StaffGroup::PERCUSSION, "perc5Line", QObject::tr("Perc. 5 lines"),   5, 0,     1,   true,  true, false, true, false, true,  false,  Qt::black),
1357 //                 group            xml-name,     human-readable-name                  lin stpOff dist clef   bars stemless time  invis     color        duration font     size off genDur     fret font          size off  duration symbol repeat      thru       minim style              onLin  rests  stmDn  stmThr upsDn  sTFing nums  bkTied
1358 //       StaffType(StaffGroup::TAB, "tab6StrSimple", QObject::tr("Tab. 6-str. simple"), 6, 2,     1.5, true,  true, true,  false, false,  Qt::black, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Sans",    9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::NONE,   true,  false, true,  false, false, false, true, false),
1359 //       StaffType(StaffGroup::TAB, "tab6StrCommon", QObject::tr("Tab. 6-str. common"), 6, 2,     1.5, true,  true, false, false, false,  Qt::black, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SHORTER,true,  false, true,  false, false, false, true, true),
1360 //       StaffType(StaffGroup::TAB, "tab6StrFull",   QObject::tr("Tab. 6-str. full"),   6, 2,     1.5, true,  true, false, true,  false,  Qt::black, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SLASHED,true,  true,  true,  true,  false, false, true, true),
1361          StaffType(StaffGroup::TAB, "tab6StrSimple", QObject::tr("Tab. 6-str. simple"), 6, 0,     1.5, true,  true, true,  false, false,  Qt::black, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Sans",    9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::NONE,   true,  false, true,  false, false, false, true, false),
1362          StaffType(StaffGroup::TAB, "tab6StrCommon", QObject::tr("Tab. 6-str. common"), 6, 0,     1.5, true,  true, false, false, false,  Qt::black, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SHORTER,true,  false, true,  false, false, false, true, true),
1363          StaffType(StaffGroup::TAB, "tab6StrFull",   QObject::tr("Tab. 6-str. full"),   6, 0,     1.5, true,  true, false, true,  false,  Qt::black, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SLASHED,true,  true,  true,  true,  false, false, true, true),
1364          StaffType(StaffGroup::TAB, "tab4StrSimple", QObject::tr("Tab. 4-str. simple"), 4, 0,     1.5, true,  true, true,  false, false,  Qt::black, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Sans",    9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::NONE,   true,  false, true,  false, false, false, true, false),
1365          StaffType(StaffGroup::TAB, "tab4StrCommon", QObject::tr("Tab. 4-str. common"), 4, 0,     1.5, true,  true, false, false, false,  Qt::black, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SHORTER,true,  false, true,  false, false, false, true, true),
1366          StaffType(StaffGroup::TAB, "tab4StrFull",   QObject::tr("Tab. 4-str. full"),   4, 0,     1.5, true,  true, false, false, false,  Qt::black, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SLASHED,true,  true,  true,  true,  false, false, true, true),
1367          StaffType(StaffGroup::TAB, "tab5StrSimple", QObject::tr("Tab. 5-str. simple"), 5, 0,     1.5, true,  true, true,  false, false,  Qt::black, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Sans",    9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::NONE,   true,  false, true,  false, false, false, true, false),
1368          StaffType(StaffGroup::TAB, "tab5StrCommon", QObject::tr("Tab. 5-str. common"), 5, 0,     1.5, true,  true, false, false, false,  Qt::black, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SHORTER,true,  false, true,  false, false, false, true, true),
1369          StaffType(StaffGroup::TAB, "tab5StrFull",   QObject::tr("Tab. 5-str. full"),   5, 0,     1.5, true,  true, false, false, false,  Qt::black, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SLASHED,true,  true,  true,  true,  false, false, true, true),
1370          StaffType(StaffGroup::TAB, "tabUkulele",    QObject::tr("Tab. ukulele"),       4, 0,     1.5, true,  true, false, false, false,  Qt::black, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SHORTER,true,  true,  true,  false, false, false, true, true),
1371          StaffType(StaffGroup::TAB, "tabBalajka",    QObject::tr("Tab. balalaika"),     3, 0,     1.5, true,  true, false, false, false,  Qt::black, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SHORTER,true,  true,  true,  false, false, false, true, true),
1372          StaffType(StaffGroup::TAB, "tabDulcimer",   QObject::tr("Tab. dulcimer"),      3, 0,     1.5, true,  true, false, false, false,  Qt::black, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SHORTER,true,  true,  true,  false, true,  false, true, true),
1373 //       StaffType(StaffGroup::TAB, "tab6StrItalian",QObject::tr("Tab. 6-str. Italian"),6, 2,     1.5, false, true, true,  true,  false,  Qt::black, "MuseScore Tab Italian",15, 0, true,  "MuseScore Tab Renaiss",10, 0, TablatureSymbolRepeat::NEVER, true,  TablatureMinimStyle::NONE,   true,  true,  false, false, true,  false, true, false),
1374 //       StaffType(StaffGroup::TAB, "tab6StrFrench", QObject::tr("Tab. 6-str. French"), 6, 2,     1.5, false, true, true,  true,  false,  Qt::black, "MuseScore Tab French", 15, 0, true,  "MuseScore Tab Renaiss",10, 0, TablatureSymbolRepeat::NEVER, true,  TablatureMinimStyle::NONE,   false, false, false, false, false, false, false,false)
1375          StaffType(StaffGroup::TAB, "tab6StrItalian",QObject::tr("Tab. 6-str. Italian"),6, 0,     1.5, false, true, true,  true,  false,  Qt::black, "MuseScore Tab Italian",15, 0, true,  "MuseScore Tab Renaiss",10, 0, TablatureSymbolRepeat::NEVER, true,  TablatureMinimStyle::NONE,   true,  true,  false, false, true,  false, true, false),
1376          StaffType(StaffGroup::TAB, "tab6StrFrench", QObject::tr("Tab. 6-str. French"), 6, 0,     1.5, false, true, true,  true,  false,  Qt::black, "MuseScore Tab French", 15, 0, true,  "MuseScore Tab Renaiss",10, 0, TablatureSymbolRepeat::NEVER, true,  TablatureMinimStyle::NONE,   false, false, false, false, false, false, false,false),
1377          StaffType(StaffGroup::TAB, "tab7StrCommon", QObject::tr("Tab. 7-str. common"), 7, 0,     1.5, true,  true, false, false, false,  Qt::black, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SHORTER,true,  false, true,  false, false, false, true, true),
1378          StaffType(StaffGroup::TAB, "tab8StrCommon", QObject::tr("Tab. 8-str. common"), 8, 0,     1.5, true,  true, false, false, false,  Qt::black, "MuseScore Tab Modern", 15, 0, false, "MuseScore Tab Serif",   9, 0, TablatureSymbolRepeat::NEVER, false, TablatureMinimStyle::SHORTER,true,  false, true,  false, false, false, true, true),
1379          };
1380       }
1381 
1382 //---------------------------------------------------------
1383 //   spatium
1384 //---------------------------------------------------------
1385 
spatium(Score * score) const1386 qreal StaffType::spatium(Score* score) const
1387       {
1388       return score->spatium() * (small() ? score->styleD(Sid::smallStaffMag) : 1.0) * userMag();
1389       }
1390 
1391 } // namespace Ms
1392 
1393