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