1 //=============================================================================
2 // MuseScore
3 // Music Composition & Notation
4 //
5 // Copyright (C) 2002-2013 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 "articulation.h"
14 #include "score.h"
15 #include "chordrest.h"
16 #include "system.h"
17 #include "measure.h"
18 #include "staff.h"
19 #include "stafftype.h"
20 #include "undo.h"
21 #include "page.h"
22 #include "barline.h"
23 #include "sym.h"
24 #include "xml.h"
25
26 namespace Ms {
27
28 //---------------------------------------------------------
29 // articulationStyle
30 //---------------------------------------------------------
31
32 static const ElementStyle articulationStyle {
33 { Sid::articulationMinDistance, Pid::MIN_DISTANCE },
34 // { Sid::articulationOffset, Pid::OFFSET },
35 { Sid::articulationAnchorDefault, Pid::ARTICULATION_ANCHOR },
36 };
37
38 //---------------------------------------------------------
39 // Articulation
40 //---------------------------------------------------------
41
Articulation(Score * s)42 Articulation::Articulation(Score* s)
43 : Element(s, ElementFlag::MOVABLE)
44 {
45 _symId = SymId::noSym;
46 _anchor = ArticulationAnchor::TOP_STAFF;
47 _direction = Direction::AUTO;
48 _up = true;
49 _ornamentStyle = MScore::OrnamentStyle::DEFAULT;
50 setPlayArticulation(true);
51 initElementStyle(&articulationStyle);
52 }
53
Articulation(SymId id,Score * s)54 Articulation::Articulation(SymId id, Score* s)
55 : Articulation(s)
56 {
57 setSymId(id);
58 }
59
60 //---------------------------------------------------------
61 // setSymId
62 //---------------------------------------------------------
63
setSymId(SymId id)64 void Articulation::setSymId(SymId id)
65 {
66 _symId = id;
67 _anchor = ArticulationAnchor(propertyDefault(Pid::ARTICULATION_ANCHOR).toInt());
68 }
69
70 //---------------------------------------------------------
71 // subtype
72 //---------------------------------------------------------
73
subtype() const74 int Articulation::subtype() const
75 {
76 QString s = Sym::id2name(_symId);
77 if (s.endsWith("Below"))
78 return int(Sym::name2id(s.left(s.size() - 5) + "Above"));
79 else if (s.endsWith("Turned"))
80 return int(Sym::name2id(s.left(s.size() - 6)));
81 else
82 return int(_symId);
83 }
84
85 //---------------------------------------------------------
86 // setUp
87 //---------------------------------------------------------
88
setUp(bool val)89 void Articulation::setUp(bool val)
90 {
91 _up = val;
92 bool dup = _direction == Direction::AUTO ? val : _direction == Direction::UP;
93 QString s = Sym::id2name(_symId);
94 if (s.endsWith(!dup ? "Above" : "Below")) {
95 QString s2 = s.left(s.size() - 5) + (dup ? "Above" : "Below");
96 _symId = Sym::name2id(s2);
97 }
98 else if (s.endsWith("Turned")) {
99 QString s2 = dup ? s.left(s.size() - 6) : s;
100 _symId = Sym::name2id(s2);
101 }
102 else if (!dup) {
103 QString s2 = s + "Turned";
104 SymId sym = Sym::name2id(s2);
105 if (sym != SymId::noSym)
106 _symId = sym;
107 }
108 }
109
110 //---------------------------------------------------------
111 // read
112 //---------------------------------------------------------
113
read(XmlReader & e)114 void Articulation::read(XmlReader& e)
115 {
116 while (e.readNextStartElement()) {
117 if (!readProperties(e))
118 e.unknown();
119 }
120 }
121
122 extern SymId oldArticulationNames2SymId(const QString&);
123
124 //---------------------------------------------------------
125 // readProperties
126 //---------------------------------------------------------
127
readProperties(XmlReader & e)128 bool Articulation::readProperties(XmlReader& e)
129 {
130 const QStringRef& tag(e.name());
131
132 if (tag == "subtype") {
133 QString s = e.readElementText();
134 SymId id = Sym::name2id(s);
135 if (id == SymId::noSym)
136 id = oldArticulationNames2SymId(s); // compatibility hack for "old" 3.0 scores
137 if (id == SymId::noSym || s == "ornamentMordentInverted") // SMuFL < 1.30
138 id = SymId::ornamentMordent;
139
140 QString programVersion = masterScore()->mscoreVersion();
141 if (!programVersion.isEmpty() && programVersion < "3.6") {
142 if (id == SymId::noSym || s == "ornamentMordent") // SMuFL < 1.30 and MuseScore < 3.6
143 id = SymId::ornamentShortTrill;
144 }
145 setSymId(id);
146 }
147 else if (tag == "channel") {
148 _channelName = e.attribute("name");
149 e.readNext();
150 }
151 else if (readProperty(tag, e, Pid::ARTICULATION_ANCHOR))
152 ;
153 else if (tag == "direction")
154 readProperty(e, Pid::DIRECTION);
155 else if ( tag == "ornamentStyle")
156 readProperty(e, Pid::ORNAMENT_STYLE);
157 else if ( tag == "play")
158 setPlayArticulation(e.readBool());
159 else if (tag == "offset") {
160 if (score()->mscVersion() > 114)
161 Element::readProperties(e);
162 else
163 e.skipCurrentElement(); // ignore manual layout in older scores
164 }
165 else if (Element::readProperties(e))
166 ;
167 else
168 return false;
169 return true;
170 }
171
172 //---------------------------------------------------------
173 // write
174 //---------------------------------------------------------
175
write(XmlWriter & xml) const176 void Articulation::write(XmlWriter& xml) const
177 {
178 if (!xml.canWrite(this))
179 return;
180 xml.stag(this);
181 if (!_channelName.isEmpty())
182 xml.tagE(QString("channel name=\"%1\"").arg(_channelName));
183 writeProperty(xml, Pid::DIRECTION);
184 xml.tag("subtype", Sym::id2name(_symId));
185 writeProperty(xml, Pid::PLAY);
186 writeProperty(xml, Pid::ORNAMENT_STYLE);
187 for (const StyledProperty& spp : *styledProperties())
188 writeProperty(xml, spp.pid);
189 Element::writeProperties(xml);
190 xml.etag();
191 }
192
193 //---------------------------------------------------------
194 // userName
195 //---------------------------------------------------------
196
userName() const197 QString Articulation::userName() const
198 {
199 return Sym::id2userName(symId());
200 }
201
202 //---------------------------------------------------------
203 // Symbol::draw
204 //---------------------------------------------------------
205
draw(QPainter * painter) const206 void Articulation::draw(QPainter* painter) const
207 {
208 #if 0 //TODO
209 SymId sym = symId();
210 ArticulationShowIn flags = articulationList[int(articulationType())].flags;
211 if (staff()) {
212 if (staff()->staffGroup() == StaffGroup::TAB) {
213 if (!(flags & ArticulationShowIn::TABLATURE))
214 return;
215 }
216 else {
217 if (!(flags & ArticulationShowIn::PITCHED_STAFF))
218 return;
219 }
220 }
221 #endif
222 painter->setPen(curColor());
223 drawSymbol(_symId, painter, QPointF(-0.5 * width(), 0.0));
224 }
225
226 //---------------------------------------------------------
227 // chordRest
228 //---------------------------------------------------------
229
chordRest() const230 ChordRest* Articulation::chordRest() const
231 {
232 if (parent() && parent()->isChordRest())
233 return toChordRest(parent());
234 return 0;
235 }
236
segment() const237 Segment* Articulation::segment() const
238 {
239 ChordRest* cr = chordRest();
240 if (!cr)
241 return 0;
242
243 Segment* s = 0;
244 if (cr->isGrace()) {
245 if (cr->parent())
246 s = toSegment(cr->parent()->parent());
247 }
248 else
249 s = toSegment(cr->parent());
250
251 return s;
252 }
253
measure() const254 Measure* Articulation::measure() const
255 {
256 Segment* s = segment();
257 return toMeasure(s ? s->parent() : 0);
258 }
259
system() const260 System* Articulation::system() const
261 {
262 Measure* m = measure();
263 return toSystem(m ? m->parent() : 0);
264 }
265
page() const266 Page* Articulation::page() const
267 {
268 System* s = system();
269 return toPage(s ? s->parent() : 0);
270 }
271
272 //---------------------------------------------------------
273 // layout
274 //---------------------------------------------------------
275
layout()276 void Articulation::layout()
277 {
278 QRectF b(symBbox(_symId));
279 setbbox(b.translated(-0.5 * b.width(), 0.0));
280 }
281
282 //---------------------------------------------------------
283 // layoutCloseToNote
284 // Needed to figure out the layout policy regarding
285 // distance to the note and placement in relation to
286 // slur.
287 //---------------------------------------------------------
288
layoutCloseToNote() const289 bool Articulation::layoutCloseToNote() const
290 {
291 return (isStaccato() || isTenuto()) && !isDouble();
292 }
293
294 //---------------------------------------------------------
295 // dragAnchorLines
296 //---------------------------------------------------------
297
dragAnchorLines() const298 QVector<QLineF> Articulation::dragAnchorLines() const
299 {
300 QVector<QLineF> result;
301 result << QLineF(canvasPos(), parent()->canvasPos());
302 return result;
303 }
304
305 //---------------------------------------------------------
306 // getProperty
307 //---------------------------------------------------------
308
getProperty(Pid propertyId) const309 QVariant Articulation::getProperty(Pid propertyId) const
310 {
311 switch (propertyId) {
312 case Pid::SYMBOL: return QVariant::fromValue(_symId);
313 case Pid::DIRECTION: return QVariant::fromValue<Direction>(direction());
314 case Pid::ARTICULATION_ANCHOR: return int(anchor());
315 case Pid::ORNAMENT_STYLE: return int(ornamentStyle());
316 case Pid::PLAY: return bool(playArticulation());
317 default:
318 return Element::getProperty(propertyId);
319 }
320 }
321
322 //---------------------------------------------------------
323 // setProperty
324 //---------------------------------------------------------
325
setProperty(Pid propertyId,const QVariant & v)326 bool Articulation::setProperty(Pid propertyId, const QVariant& v)
327 {
328 switch (propertyId) {
329 case Pid::SYMBOL:
330 setSymId(v.value<SymId>());
331 break;
332 case Pid::DIRECTION:
333 setDirection(v.value<Direction>());
334 break;
335 case Pid::ARTICULATION_ANCHOR:
336 setAnchor(ArticulationAnchor(v.toInt()));
337 break;
338 case Pid::PLAY:
339 setPlayArticulation(v.toBool());
340 break;
341 case Pid::ORNAMENT_STYLE:
342 setOrnamentStyle(MScore::OrnamentStyle(v.toInt()));
343 break;
344 default:
345 return Element::setProperty(propertyId, v);
346 }
347 triggerLayout();
348 return true;
349 }
350
351 //---------------------------------------------------------
352 // propertyDefault
353 //---------------------------------------------------------
354
propertyDefault(Pid propertyId) const355 QVariant Articulation::propertyDefault(Pid propertyId) const
356 {
357 switch (propertyId) {
358 case Pid::DIRECTION:
359 return QVariant::fromValue<Direction>(Direction::AUTO);
360
361 case Pid::ORNAMENT_STYLE:
362 //return int(score()->style()->ornamentStyle(_ornamentStyle));
363 return int(MScore::OrnamentStyle::DEFAULT);
364
365 case Pid::PLAY:
366 return true;
367
368 default:
369 break;
370 }
371 return Element::propertyDefault(propertyId);
372 }
373
374 //---------------------------------------------------------
375 // anchorGroup
376 //---------------------------------------------------------
377
anchorGroup(SymId symId)378 Articulation::AnchorGroup Articulation::anchorGroup(SymId symId)
379 {
380 switch (symId) {
381 case SymId::articAccentAbove:
382 case SymId::articAccentBelow:
383 case SymId::articStaccatoAbove:
384 case SymId::articStaccatoBelow:
385 case SymId::articStaccatissimoAbove:
386 case SymId::articStaccatissimoBelow:
387 case SymId::articTenutoAbove:
388 case SymId::articTenutoBelow:
389 case SymId::articTenutoStaccatoAbove:
390 case SymId::articTenutoStaccatoBelow:
391 case SymId::articMarcatoAbove:
392 case SymId::articMarcatoBelow:
393
394 case SymId::articAccentStaccatoAbove:
395 case SymId::articAccentStaccatoBelow:
396 case SymId::articLaissezVibrerAbove:
397 case SymId::articLaissezVibrerBelow:
398 case SymId::articMarcatoStaccatoAbove:
399 case SymId::articMarcatoStaccatoBelow:
400 case SymId::articMarcatoTenutoAbove:
401 case SymId::articMarcatoTenutoBelow:
402 case SymId::articStaccatissimoStrokeAbove:
403 case SymId::articStaccatissimoStrokeBelow:
404 case SymId::articStaccatissimoWedgeAbove:
405 case SymId::articStaccatissimoWedgeBelow:
406 case SymId::articStressAbove:
407 case SymId::articStressBelow:
408 case SymId::articTenutoAccentAbove:
409 case SymId::articTenutoAccentBelow:
410 case SymId::articUnstressAbove:
411 case SymId::articUnstressBelow:
412
413 case SymId::articSoftAccentAbove:
414 case SymId::articSoftAccentBelow:
415 case SymId::articSoftAccentStaccatoAbove:
416 case SymId::articSoftAccentStaccatoBelow:
417 case SymId::articSoftAccentTenutoAbove:
418 case SymId::articSoftAccentTenutoBelow:
419 case SymId::articSoftAccentTenutoStaccatoAbove:
420 case SymId::articSoftAccentTenutoStaccatoBelow:
421
422 case SymId::guitarFadeIn:
423 case SymId::guitarFadeOut:
424 case SymId::guitarVolumeSwell:
425 case SymId::wiggleSawtooth:
426 case SymId::wiggleSawtoothWide:
427 case SymId::wiggleVibratoLargeFaster:
428 case SymId::wiggleVibratoLargeSlowest:
429 return AnchorGroup::ARTICULATION;
430
431 case SymId::luteFingeringRHThumb:
432 case SymId::luteFingeringRHFirst:
433 case SymId::luteFingeringRHSecond:
434 case SymId::luteFingeringRHThird:
435 return AnchorGroup::LUTE_FINGERING;
436
437 default:
438 break;
439 }
440 return AnchorGroup::OTHER;
441 }
442
443 //---------------------------------------------------------
444 // symId2ArticulationName
445 //---------------------------------------------------------
446
symId2ArticulationName(SymId symId)447 const char* Articulation::symId2ArticulationName(SymId symId)
448 {
449 switch (symId) {
450 case SymId::articStaccatissimoAbove:
451 case SymId::articStaccatissimoBelow:
452 case SymId::articStaccatissimoStrokeAbove:
453 case SymId::articStaccatissimoStrokeBelow:
454 case SymId::articStaccatissimoWedgeAbove:
455 case SymId::articStaccatissimoWedgeBelow:
456 return "staccatissimo";
457
458 case SymId::articStaccatoAbove:
459 case SymId::articStaccatoBelow:
460 return "staccato";
461
462 case SymId::articAccentStaccatoAbove:
463 case SymId::articAccentStaccatoBelow:
464 return "sforzatoStaccato";
465
466 case SymId::articMarcatoStaccatoAbove:
467 case SymId::articMarcatoStaccatoBelow:
468 return "marcatoStaccato";
469
470 case SymId::articTenutoStaccatoAbove:
471 case SymId::articTenutoStaccatoBelow:
472 return "portato";
473
474 case SymId::articMarcatoTenutoAbove:
475 case SymId::articMarcatoTenutoBelow:
476 return "marcatoTenuto";
477
478 case SymId::articTenutoAbove:
479 case SymId::articTenutoBelow:
480 return "tenuto";
481
482 case SymId::articMarcatoAbove:
483 case SymId::articMarcatoBelow:
484 return "marcato";
485
486 case SymId::articAccentAbove:
487 case SymId::articAccentBelow:
488 return "sforzato";
489
490 case SymId::brassMuteOpen:
491 return "open";
492
493 case SymId::brassMuteClosed:
494 return "closed";
495
496 case SymId::stringsHarmonic:
497 return "harmonic";
498
499 case SymId::ornamentMordent:
500 return "mordent";
501
502 default:
503 return "---";
504 }
505 }
506
507 //---------------------------------------------------------
508 // propertyId
509 //---------------------------------------------------------
510
propertyId(const QStringRef & xmlName) const511 Pid Articulation::propertyId(const QStringRef& xmlName) const
512 {
513 if (xmlName == "subtype")
514 return Pid::SYMBOL;
515 return Element::propertyId(xmlName);
516 }
517
518 //---------------------------------------------------------
519 // articulationName
520 //---------------------------------------------------------
521
articulationName() const522 const char* Articulation::articulationName() const
523 {
524 return symId2ArticulationName(_symId);
525 }
526
527 //---------------------------------------------------------
528 // getPropertyStyle
529 //---------------------------------------------------------
530
getPropertyStyle(Pid id) const531 Sid Articulation::getPropertyStyle(Pid id) const
532 {
533 switch (id) {
534 case Pid::MIN_DISTANCE:
535 return Element::getPropertyStyle(id);
536
537 case Pid::ARTICULATION_ANCHOR: {
538 switch (anchorGroup(_symId)) {
539 case AnchorGroup::ARTICULATION:
540 return Sid::articulationAnchorDefault;
541 case AnchorGroup::LUTE_FINGERING:
542 return Sid::articulationAnchorLuteFingering;
543 case AnchorGroup::OTHER:
544 return Sid::articulationAnchorOther;
545 }
546 }
547 Q_ASSERT(false); // should never be reached
548 Q_FALLTHROUGH();
549 default:
550 return Sid::NOSTYLE;
551 }
552 }
553
554 //---------------------------------------------------------
555 // resetProperty
556 //---------------------------------------------------------
557
resetProperty(Pid id)558 void Articulation::resetProperty(Pid id)
559 {
560 switch (id) {
561 case Pid::DIRECTION:
562 case Pid::ORNAMENT_STYLE:
563 setProperty(id, propertyDefault(id));
564 return;
565 case Pid::ARTICULATION_ANCHOR:
566 setProperty(id, propertyDefault(id));
567 return;
568
569 default:
570 break;
571 }
572 Element::resetProperty(id);
573 }
574
575 //---------------------------------------------------------
576 // mag
577 //---------------------------------------------------------
578
mag() const579 qreal Articulation::mag() const
580 {
581 return parent() ? parent()->mag() * score()->styleD(Sid::articulationMag) : 1.0;
582 }
583
isTenuto() const584 bool Articulation::isTenuto() const
585 {
586 return _symId == SymId::articTenutoAbove || _symId == SymId::articTenutoBelow;
587 }
588
isStaccato() const589 bool Articulation::isStaccato() const
590 {
591 return _symId == SymId::articStaccatoAbove || _symId == SymId::articStaccatoBelow
592 || _symId == SymId::articMarcatoStaccatoAbove || _symId == SymId::articMarcatoStaccatoBelow
593 || _symId == SymId::articAccentStaccatoAbove || _symId == SymId::articAccentStaccatoBelow;
594 }
595
isAccent() const596 bool Articulation::isAccent() const
597 {
598 return _symId == SymId::articAccentAbove || _symId == SymId::articAccentBelow
599 || _symId == SymId::articAccentStaccatoAbove || _symId == SymId::articAccentStaccatoBelow;
600 }
601
isMarcato() const602 bool Articulation::isMarcato() const
603 {
604 return _symId == SymId::articMarcatoAbove || _symId == SymId::articMarcatoBelow
605 || _symId == SymId::articMarcatoStaccatoAbove || _symId == SymId::articMarcatoStaccatoBelow
606 || _symId == SymId::articMarcatoTenutoAbove || _symId == SymId::articMarcatoTenutoBelow;
607 }
608
isDouble() const609 bool Articulation::isDouble() const {
610 return _symId == SymId::articMarcatoStaccatoAbove || _symId == SymId::articMarcatoStaccatoBelow
611 || _symId == SymId::articAccentStaccatoAbove || _symId == SymId::articAccentStaccatoBelow
612 || _symId == SymId::articMarcatoTenutoAbove || _symId == SymId::articMarcatoTenutoBelow;
613 }
614
615 //---------------------------------------------------------
616 // isLuteFingering
617 //---------------------------------------------------------
618
isLuteFingering() const619 bool Articulation::isLuteFingering() const
620 {
621 return _symId == SymId::stringsThumbPosition
622 || _symId == SymId::luteFingeringRHThumb
623 || _symId == SymId::luteFingeringRHFirst
624 || _symId == SymId::luteFingeringRHSecond
625 || _symId == SymId::luteFingeringRHThird;
626 }
627
628 //---------------------------------------------------------
629 // isOrnament
630 //---------------------------------------------------------
631
isOrnament() const632 bool Articulation::isOrnament() const
633 {
634 return _symId == SymId::ornamentTurn
635 || _symId == SymId::ornamentTurnInverted
636 || _symId == SymId::ornamentTurnSlash
637 || _symId == SymId::ornamentTrill
638 || _symId == SymId::brassMuteClosed
639 || _symId == SymId::ornamentMordent
640 || _symId == SymId::ornamentShortTrill
641 || _symId == SymId::ornamentTremblement
642 || _symId == SymId::ornamentPrallMordent
643 || _symId == SymId::ornamentLinePrall
644 || _symId == SymId::ornamentUpPrall
645 || _symId == SymId::ornamentUpMordent
646 || _symId == SymId::ornamentPrecompMordentUpperPrefix
647 || _symId == SymId::ornamentDownMordent
648 || _symId == SymId::ornamentPrallUp
649 || _symId == SymId::ornamentPrallDown
650 || _symId == SymId::ornamentPrecompSlide;
651 }
652
653 //---------------------------------------------------------
654 // accessibleInfo
655 //---------------------------------------------------------
656
accessibleInfo() const657 QString Articulation::accessibleInfo() const
658 {
659 return QString("%1: %2").arg(Element::accessibleInfo(), userName());
660 }
661
662 //---------------------------------------------------------
663 // doAutoplace
664 // check for collisions
665 //---------------------------------------------------------
666
doAutoplace()667 void Articulation::doAutoplace()
668 {
669 // rebase vertical offset on drag
670 qreal rebase = 0.0;
671 if (offsetChanged() != OffsetChange::NONE)
672 rebase = rebaseOffset();
673
674 if (autoplace() && parent()) {
675 Segment* s = segment();
676 Measure* m = measure();
677 int si = staffIdx();
678
679 qreal sp = score()->spatium();
680 qreal md = minDistance().val() * sp;
681
682 SysStaff* ss = m->system()->staff(si);
683 QRectF r = bbox().translated(chordRest()->pos() + m->pos() + s->pos() + pos());
684
685 qreal d;
686 bool above = up(); // (anchor() == ArticulationAnchor::TOP_STAFF || anchor() == ArticulationAnchor::TOP_CHORD);
687 SkylineLine sk(!above);
688 if (above) {
689 sk.add(r.x(), r.bottom(), r.width());
690 d = sk.minDistance(ss->skyline().north());
691 }
692 else {
693 sk.add(r.x(), r.top(), r.width());
694 d = ss->skyline().south().minDistance(sk);
695 }
696
697 if (d > -md) {
698 qreal yd = d + md;
699 if (above)
700 yd *= -1.0;
701 if (offsetChanged() != OffsetChange::NONE) {
702 // user moved element within the skyline
703 // we may need to adjust minDistance, yd, and/or offset
704 //bool inStaff = placeAbove() ? r.bottom() + rebase > 0.0 : r.top() + rebase < staff()->height();
705 if (rebaseMinDistance(md, yd, sp, rebase, above, true))
706 r.translate(0.0, rebase);
707 }
708 rypos() += yd;
709 r.translate(QPointF(0.0, yd));
710 }
711 }
712 setOffsetChanged(false);
713 }
714
715 }
716