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