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