1 //=============================================================================
2 //  MuseScore
3 //  Music Composition & Notation
4 //
5 //  Copyright (C) 2002-2011 Werner Schweer
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 "tremolo.h"
14 #include "score.h"
15 #include "staff.h"
16 #include "style.h"
17 #include "chord.h"
18 #include "note.h"
19 #include "measure.h"
20 #include "segment.h"
21 #include "stem.h"
22 #include "sym.h"
23 #include "xml.h"
24 
25 namespace Ms {
26 
27 //---------------------------------------------------------
28 //   tremoloStyle
29 //---------------------------------------------------------
30 
31 static const ElementStyle tremoloStyle {
32       { Sid::tremoloStyle, Pid::TREMOLO_STYLE }
33       };
34 
35 //---------------------------------------------------------
36 //   Tremolo
37 //---------------------------------------------------------
38 
39 static const char* tremoloName[] = {
40       QT_TRANSLATE_NOOP("Tremolo", "Eighth through stem"),
41       QT_TRANSLATE_NOOP("Tremolo", "16th through stem"),
42       QT_TRANSLATE_NOOP("Tremolo", "32nd through stem"),
43       QT_TRANSLATE_NOOP("Tremolo", "64th through stem"),
44       QT_TRANSLATE_NOOP("Tremolo", "Buzz roll"),
45       QT_TRANSLATE_NOOP("Tremolo", "Eighth between notes"),
46       QT_TRANSLATE_NOOP("Tremolo", "16th between notes"),
47       QT_TRANSLATE_NOOP("Tremolo", "32nd between notes"),
48       QT_TRANSLATE_NOOP("Tremolo", "64th between notes")
49       };
50 
Tremolo(Score * score)51 Tremolo::Tremolo(Score* score)
52    : Element(score, ElementFlag::MOVABLE)
53       {
54       initElementStyle(&tremoloStyle);
55       }
56 
Tremolo(const Tremolo & t)57 Tremolo::Tremolo(const Tremolo& t)
58    : Element(t)
59       {
60       setTremoloType(t.tremoloType());
61       _chord1       = t.chord1();
62       _chord2       = t.chord2();
63       _durationType = t._durationType;
64       }
65 
66 //---------------------------------------------------------
67 //   chordMag
68 //---------------------------------------------------------
69 
chordMag() const70 qreal Tremolo::chordMag() const
71       {
72       return parent() ? toChord(parent())->chordMag() : 1.0;
73       }
74 
75 //---------------------------------------------------------
76 //   mag
77 //---------------------------------------------------------
78 
mag() const79 qreal Tremolo::mag() const
80       {
81       return parent() ? parent()->mag() : 1.0;
82       }
83 
84 //---------------------------------------------------------
85 //   minHeight
86 //---------------------------------------------------------
87 
minHeight() const88 qreal Tremolo::minHeight() const
89       {
90       const qreal sw = score()->styleS(Sid::tremoloStrokeWidth).val() * chordMag();
91       const qreal td = score()->styleS(Sid::tremoloDistance).val() * chordMag();
92       return (lines() - 1) * td + sw;
93       }
94 
95 //---------------------------------------------------------
96 //   draw
97 //---------------------------------------------------------
98 
draw(QPainter * painter) const99 void Tremolo::draw(QPainter* painter) const
100       {
101       if (isBuzzRoll()) {
102             painter->setPen(curColor());
103             drawSymbol(SymId::buzzRoll, painter);
104             }
105       else {
106             painter->setBrush(QBrush(curColor()));
107             painter->setPen(Qt::NoPen);
108             painter->drawPath(path);
109             }
110       // for palette
111       if (!parent() && !twoNotes()) {
112             qreal x = 0.0; // bbox().width() * .25;
113             QPen pen(curColor(), point(score()->styleS(Sid::stemWidth)));
114             painter->setPen(pen);
115             const qreal sp = spatium();
116             if (isBuzzRoll())
117                   painter->drawLine(QLineF(x, -sp, x, bbox().bottom() + sp));
118             else
119                   painter->drawLine(QLineF(x, -sp * .5, x, path.boundingRect().height() + sp));
120             }
121       }
122 
123 //---------------------------------------------------------
124 //   setTremoloType
125 //---------------------------------------------------------
126 
setTremoloType(TremoloType t)127 void Tremolo::setTremoloType(TremoloType t)
128       {
129       _tremoloType = t;
130       switch (tremoloType()) {
131             case TremoloType::R16:
132             case TremoloType::C16:
133                   _lines = 2;
134                   break;
135             case TremoloType::R32:
136             case TremoloType::C32:
137                   _lines = 3;
138                   break;
139             case TremoloType::R64:
140             case TremoloType::C64:
141                   _lines = 4;
142                   break;
143             default:
144                   _lines = 1;
145                   break;
146             }
147       computeShape();
148       }
149 
150 //---------------------------------------------------------
151 //   spatiumChanged
152 //---------------------------------------------------------
153 
spatiumChanged(qreal oldValue,qreal newValue)154 void Tremolo::spatiumChanged(qreal oldValue, qreal newValue)
155       {
156       Element::spatiumChanged(oldValue, newValue);
157       computeShape();
158       }
159 
160 //---------------------------------------------------------
161 //   localSpatiumChanged
162 //    the scale of a staff changed
163 //---------------------------------------------------------
164 
localSpatiumChanged(qreal oldValue,qreal newValue)165 void Tremolo::localSpatiumChanged(qreal oldValue, qreal newValue)
166       {
167       Element::localSpatiumChanged(oldValue, newValue);
168       computeShape();
169       }
170 
171 //---------------------------------------------------------
172 //   styleChanged
173 //    the scale of a staff changed
174 //---------------------------------------------------------
175 
styleChanged()176 void Tremolo::styleChanged()
177       {
178       Element::styleChanged();
179       computeShape();
180       }
181 
182 //---------------------------------------------------------
183 //   basePath
184 //---------------------------------------------------------
185 
basePath() const186 QPainterPath Tremolo::basePath() const
187       {
188       if (isBuzzRoll())
189             return QPainterPath();
190 
191       const qreal sp = spatium() * chordMag();
192 
193       // overall width of two-note tremolos should not be changed if chordMag() isn't 1.0
194       qreal w2  = sp * score()->styleS(Sid::tremoloWidth).val() * .5 / (twoNotes() ? chordMag() : 1.0);
195       qreal nw2 = w2 * score()->styleD(Sid::tremoloStrokeLengthMultiplier);
196       qreal lw  = sp * score()->styleS(Sid::tremoloStrokeWidth).val();
197       qreal td  = sp * score()->styleS(Sid::tremoloDistance).val();
198 
199       QPainterPath ppath;
200 
201       // first line
202       if (parent() && twoNotes() && (_style == TremoloStyle::DEFAULT))
203             ppath.addRect(-nw2, 0.0, 2.0 * nw2, lw);
204       else
205             ppath.addRect(-w2, 0.0, 2.0 * w2, lw);
206 
207       qreal ty = td;
208 
209       // other lines
210       for (int i = 1; i < _lines; i++) {
211             if (parent() && twoNotes() && (_style != TremoloStyle::TRADITIONAL))
212                   ppath.addRect(-nw2, ty, 2.0 * nw2, lw);
213             else
214                   ppath.addRect(-w2, ty, 2.0 * w2, lw);
215             ty += td;
216             }
217 
218       if (!parent() || !twoNotes()) {
219             // for the palette or for one-note tremolos
220             QTransform shearTransform;
221             shearTransform.shear(0.0, -(lw / 2.0) / w2);
222             ppath = shearTransform.map(ppath);
223             }
224 
225       return ppath;
226       }
227 
228 //---------------------------------------------------------
229 //   computeShape
230 //---------------------------------------------------------
231 
computeShape()232 void Tremolo::computeShape()
233       {
234       if (parent() && twoNotes())
235             return; // cannot compute shape here, should be done at layout stage
236 
237       if (isBuzzRoll())
238             setbbox(symBbox(SymId::buzzRoll));
239       else {
240             path = basePath();
241             setbbox(path.boundingRect());
242             }
243       }
244 
245 //---------------------------------------------------------
246 //   layoutOneNoteTremolo
247 //---------------------------------------------------------
248 
layoutOneNoteTremolo(qreal x,qreal y,qreal spatium)249 void Tremolo::layoutOneNoteTremolo(qreal x, qreal y, qreal spatium)
250       {
251       Q_ASSERT(!twoNotes());
252 
253       bool up = chord()->up();
254       int line = up ? chord()->upLine() : chord()->downLine();
255 
256       qreal t = 0.0;
257       // nearest distance between note and tremolo stroke should be no less than 3.0
258       if (chord()->hook() || chord()->beam()) {
259             t = up ? -3.0 * chordMag() - 2.0 * minHeight() : 3.0 * chordMag();
260             }
261       else {
262             const qreal offset = 2.0 * score()->styleS(Sid::tremoloStrokeWidth).val();
263 
264             if (!up && !(line & 1)) // stem is down; even line
265                   t = qMax((4.0 + offset) * chordMag() - 2.0 * minHeight(), 3.0 * chordMag());
266             else if (!up && (line & 1)) // stem is down; odd line
267                   t = qMax(5.0 * chordMag() - 2.0 * minHeight(), 3.0 * chordMag());
268             else if (up && !(line & 1)) // stem is up; even line
269                   t = qMin(-3.0 * chordMag() - 2.0 * minHeight(), (-4.0 - offset) * chordMag());
270             else /*if ( up &&  (line & 1))*/ // stem is up; odd line
271                   t = qMin(-3.0 * chordMag() - 2.0 * minHeight(), -5.0 * chordMag());
272             }
273 
274       qreal yLine = line + t;
275       // prevent stroke from going out of staff at the top while stem direction is down
276       if (!chord()->up()) {
277             yLine = qMax(yLine, 0.0);
278             }
279       // prevent stroke from going out of staff at the bottom while stem direction is up
280       else {
281             qreal height = isBuzzRoll() ? 0 : minHeight();
282             yLine = qMin(yLine, (staff()->lines(tick()) - 1) * 2 - 2.0 * height);
283             }
284 
285       y = yLine * .5 * spatium;
286 
287       setPos(x, y);
288       }
289 
290 extern std::pair<qreal, qreal> extendedStemLenWithTwoNoteTremolo(Tremolo*, qreal, qreal);
291 
292 //---------------------------------------------------------
293 //   layoutTwoNotesTremolo
294 //---------------------------------------------------------
295 
layoutTwoNotesTremolo(qreal x,qreal y,qreal h,qreal spatium)296 void Tremolo::layoutTwoNotesTremolo(qreal x, qreal y, qreal h, qreal spatium)
297       {
298       const bool defaultStyle = (!customStyleApplicable()) || (_style == TremoloStyle::DEFAULT);
299       const bool isTraditionalAlternate = (_style == TremoloStyle::TRADITIONAL_ALTERNATE);
300 
301       //---------------------------------------------------
302       //   Step 1: Calculate the position of the tremolo (x, y)
303       //---------------------------------------------------
304 
305       y += (h - bbox().height()) * .5;
306 
307 #if 0 // Needs to be done earlier, see connectTremolo in layout.cpp
308       Segment* s = _chord1->segment()->next();
309       while (s) {
310             if (s->element(track()) && (s->element(track())->isChord()))
311                   break;
312             s = s->next();
313             }
314       if (s == 0) {
315             qDebug("no second note of tremolo found");
316             return;
317             }
318 
319       _chord2 = toChord(s->element(track()));
320       _chord2->setTremolo(this);
321 #endif
322 
323       Stem* stem1 = _chord1->stem();
324       Stem* stem2 = _chord2->stem();
325 
326       // compute the y coordinates of the tips of the stems
327       qreal y1, y2;
328       qreal firstChordStaffY;
329 
330       if (stem2 && stem1) {
331             // stemPageYOffset variable is used for the case when the first
332             // chord is cross-staff
333             firstChordStaffY = stem1->pagePos().y() - stem1->y();  // y coordinate of the staff of the first chord
334             y1 = stem1->y() + stem1->p2().y();
335             y2 = stem2->pagePos().y() - firstChordStaffY + stem2->p2().y();  // ->p2().y() is better than ->stemLen()
336             }
337       else {
338             firstChordStaffY = _chord1->pagePos().y() - _chord1->y();  // y coordinate of the staff of the first chord
339             const std::pair<qreal, qreal> extendedLen
340                = extendedStemLenWithTwoNoteTremolo(this, _chord1->defaultStemLength(), _chord2->defaultStemLength());
341             y1 = _chord1->stemPos().y() - firstChordStaffY + extendedLen.first;
342             y2 = _chord2->stemPos().y() - firstChordStaffY + extendedLen.second;
343             }
344 
345       qreal lw = spatium * score()->styleS(Sid::tremoloStrokeWidth).val();
346       if (_chord1->beams() == 0 && _chord2->beams() == 0) {
347             // improve the case when one stem is up and another is down
348             if (defaultStyle && _chord1->up() != _chord2->up() && !crossStaffBeamBetween()) {
349                   qreal meanNote1Y = .5 * (_chord1->upNote()->pagePos().y() - firstChordStaffY + _chord1->downNote()->pagePos().y() - firstChordStaffY);
350                   qreal meanNote2Y = .5 * (_chord2->upNote()->pagePos().y() - firstChordStaffY + _chord2->downNote()->pagePos().y() - firstChordStaffY);
351                   y1 = .5 * (y1 + meanNote1Y);
352                   y2 = .5 * (y2 + meanNote2Y);
353                   }
354             if (!defaultStyle && _chord1->up() == _chord2->up()) {
355                   y1 += _chord1->up() ? -lw / 2.0 : lw / 2.0;
356                   y2 += _chord1->up() ? -lw / 2.0 : lw / 2.0;
357                   }
358             }
359 
360       y = (y1 + y2) * .5;
361       if (!_chord1->up()) {
362             y -= isTraditionalAlternate ? lw * .5 : path.boundingRect().height() * .5;
363             }
364       if (!_chord2->up()) {
365             y -= isTraditionalAlternate ? lw * .5 : path.boundingRect().height() * .5;
366             }
367 
368       // compute the x coordinates of
369       // the inner edge of the stems (default beam style)
370       // the outer edge of the stems (non-default beam style)
371       qreal x2 = _chord2->stemPosBeam().x();
372       if (stem2) {
373             if (defaultStyle && _chord2->up())
374                   x2 -= stem2->lineWidthMag();
375             else if (!defaultStyle && !_chord2->up())
376                   x2 += stem2->lineWidthMag();
377             }
378       qreal x1 = _chord1->stemPosBeam().x();
379       if (stem1) {
380             if (defaultStyle && !_chord1->up())
381                   x1 += stem1->lineWidthMag();
382             else if (!defaultStyle && _chord1->up())
383                   x1 -= stem1->lineWidthMag();
384             }
385 
386       x = (x1 + x2) * .5 - _chord1->pagePos().x();
387 
388       //---------------------------------------------------
389       //   Step 2: Stretch the tremolo strokes horizontally
390       //    from the form of a one-note tremolo (done in basePath())
391       //    to that of a two-note tremolo according to the distance between the two chords
392       //---------------------------------------------------
393 
394       QTransform xScaleTransform;
395       const qreal H_MULTIPLIER = score()->styleD(Sid::tremoloStrokeLengthMultiplier);
396       // TODO const qreal MAX_H_LENGTH = spatium * score()->styleS(Sid::tremoloBeamLengthMultiplier).val();
397       const qreal MAX_H_LENGTH = spatium * 12.0;
398 
399       const qreal defaultLength = qMin(H_MULTIPLIER * (x2 - x1), MAX_H_LENGTH);
400       qreal xScaleFactor = defaultStyle ? defaultLength / H_MULTIPLIER : (x2 - x1);
401       const qreal w2 = spatium * score()->styleS(Sid::tremoloWidth).val() * .5;
402       xScaleFactor /= (2.0 * w2);
403 
404       xScaleTransform.scale(xScaleFactor, 1.0);
405       path = xScaleTransform.map(path);
406 
407       //---------------------------------------------------
408       //   Step 3: Calculate the adjustment of the position of the tremolo
409       //    if the chords are connected by a beam so as not to collide with it
410       //---------------------------------------------------
411 
412       qreal beamYOffset = 0.0;
413 
414       if (_chord1->beams() == _chord2->beams() && _chord1->beam()) {
415             int beams = _chord1->beams();
416             qreal beamHalfLineWidth = point(score()->styleS(Sid::beamWidth)) * .5 * chordMag();
417             beamYOffset = beams * _chord1->beam()->beamDist() - beamHalfLineWidth;
418             if (_chord1->up() != _chord2->up()) {  // cross-staff
419                   beamYOffset = 2 * beamYOffset + beamHalfLineWidth;
420                   }
421             else if (!_chord1->up() && !_chord2->up()) {
422                   beamYOffset = -beamYOffset;
423                   }
424             }
425 
426       //---------------------------------------------------
427       //   Step 4: Tilt the tremolo strokes according to the stems of the chords
428       //---------------------------------------------------
429 
430       QTransform shearTransform;
431       qreal dy = y2 - y1;
432       qreal dx = x2 - x1;
433       if (_chord1->beams() == 0 && _chord2->beams() == 0) {
434             if (_chord1->up() && !_chord2->up()) {
435                   dy -= isTraditionalAlternate ? lw : path.boundingRect().height();
436                   if (!defaultStyle)
437                         dy += lw;
438                   }
439             else if (!_chord1->up() && _chord2->up()) {
440                   dy += isTraditionalAlternate ? lw : path.boundingRect().height();
441                   if (!defaultStyle)
442                         dy -= lw;
443                   }
444             }
445       // Make tremolo strokes less steep if two chords have the opposite stem directions,
446       // except for two cases:
447       // 1. The tremolo doesn't have the default style.
448       // In this case tremolo strokes should attach to the ends of both stems, so no adjustment needed;
449       // 2. The chords are on different staves and the tremolo is between them.
450       // The layout should be improved by extending both stems, so changes are not needed here.
451       if (_chord1->up() != _chord2->up() && defaultStyle && !crossStaffBeamBetween())
452             dy = qMin(qMax(dy, -1.0 * spatium / defaultLength * dx), 1.0 * spatium / defaultLength * dx);
453       qreal ds = dy / dx;
454       shearTransform.shear(0.0, ds);
455       path = shearTransform.map(path);
456 
457       //---------------------------------------------------
458       //   Step 5: Flip the tremolo strokes if necessary
459       //    By default, a TRADITIONAL_ALTERNATE tremolo has its attached-to-stem stroke be above other strokes,
460       //    see basePath().
461       //    But if both chords have stems facing down,
462       //    the tremolo should be flipped to have the attached-to-stem stroke be below other strokes.
463       //---------------------------------------------------
464 
465       if (isTraditionalAlternate && !_chord1->up() && !_chord2->up()) {
466             QTransform rotateTransform;
467             rotateTransform.translate(0.0, lw * .5);
468             rotateTransform.rotate(180);
469             rotateTransform.translate(0.0, -lw * .5);
470             path = rotateTransform.map(path);
471             }
472 
473       setbbox(path.boundingRect());
474       setPos(x, y + beamYOffset);
475       }
476 
477 
478 //---------------------------------------------------------
479 //   layout
480 //---------------------------------------------------------
481 
layout()482 void Tremolo::layout()
483       {
484       path = basePath();
485 
486       _chord1 = toChord(parent());
487       if (!_chord1) {
488             // palette
489             if (!isBuzzRoll()) {
490                   const QRectF box = path.boundingRect();
491                   addbbox(QRectF(box.x(), box.bottom(), box.width(), spatium()));
492                   }
493             return;
494             }
495 
496       Note* anchor1 = _chord1->upNote();
497       Stem* stem    = _chord1->stem();
498       qreal x, y, h;
499       if (stem) {
500             x  = stem->pos().x();
501             y  = stem->pos().y();
502             h  = stem->stemLen();
503             }
504       else {
505             // center tremolo above note
506             x = anchor1->x() + anchor1->headWidth() * .5;
507             y = anchor1->y();
508             h = 2.0 * spatium() + bbox().height();
509             if (anchor1->line() > 4)
510                   h *= -1;
511             }
512 
513       if (twoNotes())
514             layoutTwoNotesTremolo(x, y, h, spatium());
515       else
516             layoutOneNoteTremolo(x, y, spatium());
517       }
518 
519 //---------------------------------------------------------
520 //   crossStaffBeamBetween
521 //    Return true if tremolo is two-note cross-staff and beams between staves
522 //---------------------------------------------------------
523 
crossStaffBeamBetween() const524 bool Tremolo::crossStaffBeamBetween() const
525       {
526       if (!twoNotes())
527             return false;
528 
529       return ((_chord1->staffMove() > _chord2->staffMove()) && _chord1->up() && !_chord2->up())
530          ||  ((_chord1->staffMove() < _chord2->staffMove()) && !_chord1->up() && _chord2->up());
531       }
532 
533 //---------------------------------------------------------
534 //   write
535 //---------------------------------------------------------
536 
write(XmlWriter & xml) const537 void Tremolo::write(XmlWriter& xml) const
538       {
539       if (!xml.canWrite(this))
540             return;
541       xml.stag(this);
542       writeProperty(xml, Pid::TREMOLO_TYPE);
543       writeProperty(xml, Pid::TREMOLO_STYLE);
544       Element::writeProperties(xml);
545       xml.etag();
546       }
547 
548 //---------------------------------------------------------
549 //   read
550 //---------------------------------------------------------
551 
read(XmlReader & e)552 void Tremolo::read(XmlReader& e)
553       {
554       while (e.readNextStartElement()) {
555             const QStringRef& tag(e.name());
556             if (tag == "subtype")
557                   setTremoloType(e.readElementText());
558             // Style needs special handling other than readStyledProperty()
559             // to avoid calling customStyleApplicable() in setProperty(),
560             // which cannot be called now because durationType() isn't defined yet.
561             else if (tag == "strokeStyle") {
562                   setStyle(TremoloStyle(e.readInt()));
563                   setPropertyFlags(Pid::TREMOLO_STYLE, PropertyFlags::UNSTYLED);
564                   }
565             else if (readStyledProperty(e, tag))
566                   ;
567             else if (!Element::readProperties(e))
568                   e.unknown();
569             }
570       }
571 
572 //---------------------------------------------------------
573 //   tremoloTypeName
574 //---------------------------------------------------------
575 
tremoloTypeName() const576 QString Tremolo::tremoloTypeName() const
577       {
578       return type2name(tremoloType());
579       }
580 
581 //---------------------------------------------------------
582 //   setTremoloType
583 //---------------------------------------------------------
584 
setTremoloType(const QString & s)585 void Tremolo::setTremoloType(const QString& s)
586       {
587       setTremoloType(name2Type(s));
588       }
589 
590 //---------------------------------------------------------
591 //   type2Name
592 //---------------------------------------------------------
593 
type2name(TremoloType t)594 QString Tremolo::type2name(TremoloType t)
595       {
596       switch(t) {
597             case TremoloType::R8:  return QString("r8");
598             case TremoloType::R16: return QString("r16");
599             case TremoloType::R32: return QString("r32");
600             case TremoloType::R64: return QString("r64");
601             case TremoloType::C8:  return QString("c8");
602             case TremoloType::C16: return QString("c16");
603             case TremoloType::C32: return QString("c32");
604             case TremoloType::C64: return QString("c64");
605             case TremoloType::BUZZ_ROLL: return QString("buzzroll");
606             default:
607                   break;
608             }
609       return QString("??");
610       }
611 
612 
613 //---------------------------------------------------------
614 //   nameToType
615 //---------------------------------------------------------
616 
name2Type(const QString & s)617 TremoloType Tremolo::name2Type(const QString& s)
618       {
619       TremoloType t = TremoloType::INVALID_TREMOLO;
620       if (s == "r8")
621             t = TremoloType::R8;
622       else if (s == "r16")
623             t = TremoloType::R16;
624       else if (s == "r32")
625             t = TremoloType::R32;
626       else if (s == "r64")
627             t = TremoloType::R64;
628       else if (s == "c8")
629             t = TremoloType::C8;
630       else if (s == "c16")
631             t = TremoloType::C16;
632       else if (s == "c32")
633             t = TremoloType::C32;
634       else if (s == "c64")
635             t = TremoloType::C64;
636       else if (s == "buzzroll")
637             t = TremoloType::BUZZ_ROLL;
638       return t;
639       }
640 
641 //---------------------------------------------------------
642 //   tremoloLen
643 //---------------------------------------------------------
644 
tremoloLen() const645 Fraction Tremolo::tremoloLen() const
646       {
647       Fraction f;
648       switch(lines()) {
649             case 1: f.set(1,8); break;
650             case 2: f.set(1,16); break;
651             case 3: f.set(1,32); break;
652             case 4: f.set(1,64); break;
653             }
654       return f;
655       }
656 
657 //---------------------------------------------------------
658 //   subtypeName
659 //---------------------------------------------------------
660 
subtypeName() const661 QString Tremolo::subtypeName() const
662       {
663       return qApp->translate("Tremolo", tremoloName[subtype()]);
664       }
665 
666 //---------------------------------------------------------
667 //   accessibleInfo
668 //---------------------------------------------------------
669 
accessibleInfo() const670 QString Tremolo::accessibleInfo() const
671       {
672       return QString("%1: %2").arg(Element::accessibleInfo(), subtypeName());
673       }
674 
675 //---------------------------------------------------------
676 //   customStyleApplicable
677 //---------------------------------------------------------
678 
customStyleApplicable() const679 bool Tremolo::customStyleApplicable() const
680       {
681       return twoNotes()
682          && (durationType().type() == TDuration::DurationType::V_HALF)
683          && (staffType()->group() != StaffGroup::TAB);
684       }
685 
686 //---------------------------------------------------------
687 //   getProperty
688 //---------------------------------------------------------
689 
getProperty(Pid propertyId) const690 QVariant Tremolo::getProperty(Pid propertyId) const
691       {
692       switch (propertyId) {
693             case Pid::TREMOLO_TYPE:
694                   return int(_tremoloType);
695             case Pid::TREMOLO_STYLE:
696                   return int(_style);
697             default:
698                   break;
699             }
700       return Element::getProperty(propertyId);
701       }
702 
703 //---------------------------------------------------------
704 //   setProperty
705 //---------------------------------------------------------
706 
setProperty(Pid propertyId,const QVariant & val)707 bool Tremolo::setProperty(Pid propertyId, const QVariant& val)
708       {
709       switch (propertyId) {
710             case Pid::TREMOLO_TYPE:
711                   setTremoloType(TremoloType(val.toInt()));
712                   break;
713             case Pid::TREMOLO_STYLE:
714                   if (customStyleApplicable())
715                         setStyle(TremoloStyle(val.toInt()));
716                   break;
717             default:
718                   return Element::setProperty(propertyId, val);
719             }
720       triggerLayout();
721       return true;
722       }
723 
724 //---------------------------------------------------------
725 //   propertyDefault
726 //---------------------------------------------------------
727 
propertyDefault(Pid propertyId) const728 QVariant Tremolo::propertyDefault(Pid propertyId) const
729       {
730       switch (propertyId) {
731             case Pid::TREMOLO_STYLE:
732                   return score()->styleI(Sid::tremoloStyle);
733             default:
734                   return Element::propertyDefault(propertyId);
735             }
736       }
737 
738 //---------------------------------------------------------
739 //   propertyId
740 //---------------------------------------------------------
741 
propertyId(const QStringRef & name) const742 Pid Tremolo::propertyId(const QStringRef& name) const
743       {
744       if (name == "subtype")
745             return Pid::TREMOLO_TYPE;
746       return Element::propertyId(name);
747       }
748 
749 //---------------------------------------------------------
750 //   propertyUserValue
751 //---------------------------------------------------------
752 
propertyUserValue(Pid pid) const753 QString Tremolo::propertyUserValue(Pid pid) const
754       {
755       switch(pid) {
756             case Pid::TREMOLO_TYPE:
757                   return subtypeName();
758             default:
759                   break;
760             }
761       return Element::propertyUserValue(pid);
762       }
763 }
764