Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-Studio-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "articulation.h"
24 :
25 : #include "style/style.h"
26 : #include "types/symnames.h"
27 : #include "types/typesconv.h"
28 : #include "types/translatablestring.h"
29 :
30 : #include "beam.h"
31 : #include "chord.h"
32 : #include "chordrest.h"
33 : #include "measure.h"
34 : #include "page.h"
35 : #include "staff.h"
36 : #include "stafftype.h"
37 : #include "system.h"
38 : #include "note.h"
39 :
40 : #include "log.h"
41 :
42 : using namespace mu;
43 : using namespace mu::engraving;
44 : using namespace muse::draw;
45 :
46 : //---------------------------------------------------------
47 : // articulationStyle
48 : //---------------------------------------------------------
49 :
50 : static const ElementStyle articulationStyle {
51 : { Sid::articulationMinDistance, Pid::MIN_DISTANCE },
52 : { Sid::articulationAnchorDefault, Pid::ARTICULATION_ANCHOR },
53 : };
54 :
55 : //---------------------------------------------------------
56 : // Articulation
57 : //---------------------------------------------------------
58 :
59 1328 : Articulation::Articulation(DummyParentOr<ChordRest> parent, ElementType type)
60 1328 : : EngravingItem(type, parent, ElementFlag::MOVABLE)
61 : {
62 1328 : m_symId = SymId::noSym;
63 1328 : m_anchor = ArticulationAnchor::AUTO;
64 1328 : m_direction = DirectionV::AUTO;
65 1328 : m_ornamentStyle = OrnamentStyle::DEFAULT;
66 1328 : m_playArticulation = true;
67 :
68 1328 : initElementStyle(&articulationStyle);
69 1328 : setupShowOnTabStyles();
70 1328 : }
71 :
72 : //---------------------------------------------------------
73 : // setSymId
74 : //---------------------------------------------------------
75 :
76 1315 : void Articulation::setSymId(SymId id)
77 : {
78 1315 : m_symId = id;
79 1315 : computeCategories();
80 1315 : setupShowOnTabStyles();
81 1315 : m_anchor = ArticulationAnchor(propertyDefault(Pid::ARTICULATION_ANCHOR).toInt());
82 1315 : m_textType = ArticulationTextType::NO_TEXT;
83 1315 : }
84 :
85 : //---------------------------------------------------------
86 : // setTextType
87 : //---------------------------------------------------------
88 :
89 28 : void Articulation::setTextType(ArticulationTextType textType)
90 : {
91 28 : m_textType = textType;
92 28 : }
93 :
94 647 : void Articulation::setSelected(bool f)
95 : {
96 647 : if (m_text) {
97 0 : m_text->setSelected(f);
98 : }
99 :
100 647 : EngravingItem::setSelected(f);
101 647 : }
102 :
103 344 : void Articulation::setVisible(bool f)
104 : {
105 344 : if (m_text) {
106 0 : m_text->setVisible(f);
107 : }
108 :
109 344 : EngravingItem::setVisible(f);
110 344 : }
111 :
112 : //---------------------------------------------------------
113 : // subtype
114 : //---------------------------------------------------------
115 :
116 2123 : int Articulation::subtype() const
117 : {
118 2123 : if (m_textType != ArticulationTextType::NO_TEXT) {
119 16 : return int(m_textType);
120 : }
121 2107 : String s = String::fromAscii(SymNames::nameForSymId(m_symId).ascii());
122 2107 : if (s.endsWith(u"Below")) {
123 589 : return int(SymNames::symIdByName(s.left(s.size() - 5) + u"Above"));
124 1518 : } else if (s.endsWith(u"Turned")) {
125 24 : return int(SymNames::symIdByName(s.left(s.size() - 6)));
126 : }
127 :
128 1494 : return int(m_symId);
129 2107 : }
130 :
131 : //---------------------------------------------------------
132 : // setUp
133 : //---------------------------------------------------------
134 :
135 3469 : void Articulation::setUp(bool val)
136 : {
137 3469 : Articulation::LayoutData* ldata = mutldata();
138 3469 : ldata->up = val;
139 :
140 : //! NOTE member of Articulation m_symId - this is `given` data
141 : //! member of LayoutData m_symId - this is layout data
142 : //! I would not like to change the `given` data here, but they are changing for backward compatibility
143 : //! Even better, I wouldn’t want symId to be `given` data,
144 : //! it would be better if there was some type,
145 : //! and from it we would already figure out how (with what symbol) to display it
146 :
147 3469 : bool dup = m_direction == DirectionV::AUTO ? val : m_direction == DirectionV::UP;
148 3469 : String s = String::fromAscii(SymNames::nameForSymId(m_symId).ascii());
149 3469 : if (s.endsWith(!dup ? u"Above" : u"Below")) {
150 179 : String s2 = s.left(s.size() - 5) + (dup ? u"Above" : u"Below");
151 179 : m_symId = SymNames::symIdByName(s2);
152 3469 : } else if (s.endsWith(u"Turned")) {
153 28 : String s2 = dup ? s.left(s.size() - 6) : s;
154 28 : m_symId = SymNames::symIdByName(s2);
155 3290 : } else if (!dup) {
156 867 : String s2 = s + u"Turned";
157 867 : SymId sym = SymNames::symIdByName(s2);
158 867 : if (sym != SymId::noSym) {
159 12 : m_symId = sym;
160 : }
161 867 : }
162 :
163 3469 : ldata->symId = m_symId;
164 3469 : }
165 :
166 : //---------------------------------------------------------
167 : // typeUserName
168 : //---------------------------------------------------------
169 :
170 0 : muse::TranslatableString Articulation::typeUserName() const
171 : {
172 0 : if (m_textType != ArticulationTextType::NO_TEXT) {
173 0 : return TranslatableString("engraving", "Articulation text");
174 : }
175 :
176 0 : return TranslatableString("engraving", "Articulation");
177 : }
178 :
179 : //---------------------------------------------------------
180 : // subtypeUserName
181 : //---------------------------------------------------------
182 :
183 2 : muse::TranslatableString Articulation::subtypeUserName() const
184 : {
185 2 : if (m_textType != ArticulationTextType::NO_TEXT) {
186 0 : return TConv::userName(m_textType);
187 : }
188 :
189 2 : return SymNames::userNameForSymId(symId());
190 : }
191 :
192 : //---------------------------------------------------------
193 : // chordRest
194 : //---------------------------------------------------------
195 :
196 17861 : ChordRest* Articulation::chordRest() const
197 : {
198 17861 : if (ownershipParent() && ownershipParent()->isChordRest()) {
199 17861 : return toChordRest(ownershipParent());
200 : }
201 0 : return 0;
202 : }
203 :
204 4743 : Segment* Articulation::segment() const
205 : {
206 4743 : ChordRest* cr = chordRest();
207 4743 : if (!cr) {
208 0 : return 0;
209 : }
210 :
211 4743 : Segment* s = 0;
212 4743 : if (cr->isGrace()) {
213 2 : if (cr->ownershipParent()) {
214 2 : s = toSegment(cr->ownershipParent()->ownershipParent());
215 : }
216 : } else {
217 4741 : s = toSegment(cr->ownershipParent());
218 : }
219 :
220 4743 : return s;
221 : }
222 :
223 1339 : Measure* Articulation::measure() const
224 : {
225 1339 : Segment* s = segment();
226 1339 : return toMeasure(s ? s->ownershipParent() : 0);
227 : }
228 :
229 0 : System* Articulation::system() const
230 : {
231 0 : Measure* m = measure();
232 0 : return m ? m->system() : nullptr;
233 : }
234 :
235 0 : Page* Articulation::page() const
236 : {
237 0 : System* s = system();
238 0 : return s ? s->page() : nullptr;
239 : }
240 :
241 3435 : bool Articulation::isHiddenOnTabStaff() const
242 : {
243 3435 : if (m_showOnTabStyles.first == Sid::NOSTYLE || m_showOnTabStyles.second == Sid::NOSTYLE) {
244 1497 : return false;
245 : }
246 :
247 1938 : const StaffType* stType = staffType();
248 :
249 1938 : if (!stType || !stType->isTabStaff()) {
250 1924 : return false;
251 : }
252 :
253 14 : return stType->isHiddenElementOnTab(m_showOnTabStyles.first, m_showOnTabStyles.second);
254 : }
255 :
256 : //---------------------------------------------------------
257 : // layoutCloseToNote
258 : // Needed to figure out the layout policy regarding
259 : // distance to the note and placement in relation to
260 : // slur.
261 : //---------------------------------------------------------
262 :
263 22205 : bool Articulation::layoutCloseToNote() const
264 : {
265 22205 : Staff* s = staff();
266 22205 : if (s && s->staffType()->isTabStaff() && isStaccato()) {
267 84 : return false;
268 : }
269 :
270 22121 : return (isStaccato() || isTenuto()) && !isDouble();
271 : }
272 :
273 : //---------------------------------------------------------
274 : // dragAnchorLines
275 : //---------------------------------------------------------
276 :
277 0 : std::vector<LineF> Articulation::dragAnchorLines() const
278 : {
279 0 : std::vector<LineF> result;
280 0 : result.push_back(LineF(canvasPos(), layoutParent()->canvasPos()));
281 0 : return result;
282 0 : }
283 :
284 : //---------------------------------------------------------
285 : // getProperty
286 : //---------------------------------------------------------
287 :
288 9289 : PropertyValue Articulation::getProperty(Pid propertyId) const
289 : {
290 9289 : switch (propertyId) {
291 0 : case Pid::SYMBOL: return PropertyValue::fromValue(m_symId);
292 761 : case Pid::DIRECTION: return PropertyValue::fromValue<DirectionV>(direction());
293 92 : case Pid::ARTICULATION_ANCHOR: return int(anchor());
294 761 : case Pid::ORNAMENT_STYLE: return ornamentStyle();
295 761 : case Pid::PLAY: return playArticulation();
296 6914 : default:
297 6914 : return EngravingItem::getProperty(propertyId);
298 : }
299 : }
300 :
301 : //---------------------------------------------------------
302 : // setProperty
303 : //---------------------------------------------------------
304 :
305 3241 : bool Articulation::setProperty(Pid propertyId, const PropertyValue& v)
306 : {
307 3241 : switch (propertyId) {
308 0 : case Pid::SYMBOL:
309 0 : setSymId(v.value<SymId>());
310 0 : break;
311 0 : case Pid::DIRECTION:
312 0 : setDirection(v.value<DirectionV>());
313 0 : break;
314 1422 : case Pid::ARTICULATION_ANCHOR:
315 1422 : setAnchor(ArticulationAnchor(v.toInt()));
316 1422 : break;
317 0 : case Pid::PLAY:
318 0 : setPlayArticulation(v.toBool());
319 0 : break;
320 5 : case Pid::ORNAMENT_STYLE:
321 5 : setOrnamentStyle(v.value<OrnamentStyle>());
322 5 : break;
323 1814 : default:
324 1814 : return EngravingItem::setProperty(propertyId, v);
325 : }
326 1427 : triggerLayout();
327 1427 : return true;
328 : }
329 :
330 : //---------------------------------------------------------
331 : // propertyDefault
332 : //---------------------------------------------------------
333 :
334 10232 : PropertyValue Articulation::propertyDefault(Pid propertyId) const
335 : {
336 10232 : switch (propertyId) {
337 761 : case Pid::DIRECTION:
338 761 : return DirectionV::AUTO;
339 :
340 761 : case Pid::ORNAMENT_STYLE:
341 761 : return OrnamentStyle::DEFAULT;
342 :
343 761 : case Pid::PLAY:
344 761 : return true;
345 :
346 7949 : default:
347 7949 : break;
348 : }
349 7949 : return EngravingItem::propertyDefault(propertyId);
350 : }
351 :
352 : //---------------------------------------------------------
353 : // anchorGroup
354 : //---------------------------------------------------------
355 :
356 2473 : Articulation::AnchorGroup Articulation::anchorGroup(SymId symId)
357 : {
358 2473 : switch (symId) {
359 852 : case SymId::articAccentAbove:
360 : case SymId::articAccentBelow:
361 : case SymId::articStaccatoAbove:
362 : case SymId::articStaccatoBelow:
363 : case SymId::articStaccatissimoAbove:
364 : case SymId::articStaccatissimoBelow:
365 : case SymId::articTenutoAbove:
366 : case SymId::articTenutoBelow:
367 : case SymId::articTenutoStaccatoAbove:
368 : case SymId::articTenutoStaccatoBelow:
369 : case SymId::articMarcatoAbove:
370 : case SymId::articMarcatoBelow:
371 :
372 : case SymId::articAccentStaccatoAbove:
373 : case SymId::articAccentStaccatoBelow:
374 : case SymId::articLaissezVibrerAbove:
375 : case SymId::articLaissezVibrerBelow:
376 : case SymId::articMarcatoStaccatoAbove:
377 : case SymId::articMarcatoStaccatoBelow:
378 : case SymId::articMarcatoTenutoAbove:
379 : case SymId::articMarcatoTenutoBelow:
380 : case SymId::articStaccatissimoStrokeAbove:
381 : case SymId::articStaccatissimoStrokeBelow:
382 : case SymId::articStaccatissimoWedgeAbove:
383 : case SymId::articStaccatissimoWedgeBelow:
384 : case SymId::articStressAbove:
385 : case SymId::articStressBelow:
386 : case SymId::articTenutoAccentAbove:
387 : case SymId::articTenutoAccentBelow:
388 : case SymId::articUnstressAbove:
389 : case SymId::articUnstressBelow:
390 :
391 : case SymId::articSoftAccentAbove:
392 : case SymId::articSoftAccentBelow:
393 : case SymId::articSoftAccentStaccatoAbove:
394 : case SymId::articSoftAccentStaccatoBelow:
395 : case SymId::articSoftAccentTenutoAbove:
396 : case SymId::articSoftAccentTenutoBelow:
397 : case SymId::articSoftAccentTenutoStaccatoAbove:
398 : case SymId::articSoftAccentTenutoStaccatoBelow:
399 :
400 : case SymId::wiggleSawtooth:
401 : case SymId::wiggleSawtoothWide:
402 : case SymId::wiggleVibratoLargeFaster:
403 : case SymId::wiggleVibratoLargeSlowest:
404 852 : return AnchorGroup::ARTICULATION;
405 :
406 0 : case SymId::luteFingeringRHThumb:
407 : case SymId::luteFingeringRHFirst:
408 : case SymId::luteFingeringRHSecond:
409 : case SymId::luteFingeringRHThird:
410 0 : return AnchorGroup::LUTE_FINGERING;
411 :
412 0 : case SymId::tremoloDivisiDots2:
413 : case SymId::tremoloDivisiDots3:
414 : case SymId::tremoloDivisiDots4:
415 : case SymId::tremoloDivisiDots6:
416 0 : return AnchorGroup::ARTICULATION;
417 :
418 1621 : default:
419 1621 : break;
420 : }
421 1621 : return AnchorGroup::OTHER;
422 : }
423 :
424 : //---------------------------------------------------------
425 : // symId2ArticulationName
426 : //---------------------------------------------------------
427 :
428 3 : String Articulation::symId2ArticulationName(SymId symId)
429 : {
430 3 : switch (symId) {
431 0 : case SymId::articStaccatissimoAbove:
432 : case SymId::articStaccatissimoBelow:
433 : case SymId::articStaccatissimoStrokeAbove:
434 : case SymId::articStaccatissimoStrokeBelow:
435 : case SymId::articStaccatissimoWedgeAbove:
436 : case SymId::articStaccatissimoWedgeBelow:
437 0 : return u"staccatissimo";
438 :
439 3 : case SymId::articStaccatoAbove:
440 : case SymId::articStaccatoBelow:
441 : case SymId::tremoloDivisiDots2:
442 : case SymId::tremoloDivisiDots3:
443 : case SymId::tremoloDivisiDots4:
444 : case SymId::tremoloDivisiDots6:
445 3 : return u"staccato";
446 :
447 0 : case SymId::articAccentStaccatoAbove:
448 : case SymId::articAccentStaccatoBelow:
449 0 : return u"sforzatoStaccato";
450 :
451 0 : case SymId::articMarcatoStaccatoAbove:
452 : case SymId::articMarcatoStaccatoBelow:
453 0 : return u"marcatoStaccato";
454 :
455 0 : case SymId::articTenutoStaccatoAbove:
456 : case SymId::articTenutoStaccatoBelow:
457 0 : return u"portato";
458 :
459 0 : case SymId::articTenutoAccentAbove:
460 : case SymId::articTenutoAccentBelow:
461 0 : return u"sforzatoTenuto";
462 :
463 0 : case SymId::articMarcatoTenutoAbove:
464 : case SymId::articMarcatoTenutoBelow:
465 0 : return u"marcatoTenuto";
466 :
467 0 : case SymId::articTenutoAbove:
468 : case SymId::articTenutoBelow:
469 0 : return u"tenuto";
470 :
471 0 : case SymId::articMarcatoAbove:
472 : case SymId::articMarcatoBelow:
473 0 : return u"marcato";
474 :
475 0 : case SymId::articAccentAbove:
476 : case SymId::articAccentBelow:
477 0 : return u"accent";
478 :
479 0 : case SymId::dynamicSforzato:
480 : case SymId::dynamicSforzando:
481 0 : return u"sforzato";
482 :
483 0 : case SymId::brassMuteOpen:
484 0 : return u"open";
485 :
486 0 : case SymId::brassMuteClosed:
487 0 : return u"closed";
488 :
489 0 : case SymId::stringsHarmonic:
490 0 : return u"harmonic";
491 :
492 0 : case SymId::ornamentMordent:
493 0 : return u"mordent";
494 :
495 0 : default:
496 0 : return u"---";
497 : }
498 : }
499 :
500 : //---------------------------------------------------------
501 : // articulationName
502 : //---------------------------------------------------------
503 :
504 3 : String Articulation::articulationName() const
505 : {
506 3 : return symId2ArticulationName(m_symId);
507 : }
508 :
509 : //---------------------------------------------------------
510 : // getPropertyStyle
511 : //---------------------------------------------------------
512 :
513 4644 : Sid Articulation::getPropertyStyle(Pid id) const
514 : {
515 4644 : switch (id) {
516 1393 : case Pid::MIN_DISTANCE:
517 1393 : return EngravingItem::getPropertyStyle(id);
518 :
519 2490 : case Pid::ARTICULATION_ANCHOR: {
520 2490 : if (isHandbellsArticulation()) {
521 17 : return Sid::articulationAnchorDefault;
522 : }
523 :
524 2473 : switch (anchorGroup(m_symId)) {
525 852 : case AnchorGroup::ARTICULATION:
526 852 : return Sid::articulationAnchorDefault;
527 0 : case AnchorGroup::LUTE_FINGERING:
528 0 : return Sid::articulationAnchorLuteFingering;
529 1621 : case AnchorGroup::OTHER:
530 1621 : return Sid::articulationAnchorOther;
531 : }
532 : }
533 0 : assert(false); // should never be reached
534 : // fallthrough
535 761 : default:
536 761 : return Sid::NOSTYLE;
537 : }
538 : }
539 :
540 : //---------------------------------------------------------
541 : // resetProperty
542 : //---------------------------------------------------------
543 :
544 0 : void Articulation::resetProperty(Pid id)
545 : {
546 0 : switch (id) {
547 0 : case Pid::DIRECTION:
548 : case Pid::ORNAMENT_STYLE:
549 0 : setProperty(id, propertyDefault(id));
550 0 : return;
551 0 : case Pid::ARTICULATION_ANCHOR:
552 0 : setProperty(id, propertyDefault(id));
553 0 : return;
554 :
555 0 : default:
556 0 : break;
557 : }
558 0 : EngravingItem::resetProperty(id);
559 : }
560 :
561 : //---------------------------------------------------------
562 : // mag
563 : //---------------------------------------------------------
564 :
565 65484 : double Articulation::mag() const
566 : {
567 65484 : return layoutParent() ? layoutParent()->mag() * style().styleD(Sid::articulationMag) : 1.0;
568 : }
569 :
570 1315 : void Articulation::computeCategories()
571 : {
572 1315 : m_categories.setFlag(ArticulationCategory::DOUBLE,
573 1307 : m_symId == SymId::articMarcatoStaccatoAbove || m_symId == SymId::articMarcatoStaccatoBelow
574 1307 : || m_symId == SymId::articTenutoStaccatoAbove || m_symId == SymId::articTenutoStaccatoBelow
575 1276 : || m_symId == SymId::articAccentStaccatoAbove || m_symId == SymId::articAccentStaccatoBelow
576 1267 : || m_symId == SymId::articMarcatoTenutoAbove || m_symId == SymId::articMarcatoTenutoBelow
577 2622 : || m_symId == SymId::articTenutoAccentAbove || m_symId == SymId::articTenutoAccentBelow);
578 :
579 1315 : m_categories.setFlag(ArticulationCategory::TENUTO,
580 1220 : m_symId == SymId::articTenutoAbove || m_symId == SymId::articTenutoBelow
581 1190 : || m_symId == SymId::articMarcatoTenutoAbove || m_symId == SymId::articMarcatoTenutoBelow
582 2535 : || m_symId == SymId::articTenutoAccentAbove || m_symId == SymId::articTenutoAccentBelow);
583 :
584 1315 : m_categories.setFlag(ArticulationCategory::STACCATO,
585 1069 : m_symId == SymId::articStaccatoAbove || m_symId == SymId::articStaccatoBelow
586 1015 : || m_symId == SymId::articMarcatoStaccatoAbove || m_symId == SymId::articMarcatoStaccatoBelow
587 1007 : || m_symId == SymId::articTenutoStaccatoAbove || m_symId == SymId::articTenutoStaccatoBelow
588 976 : || m_symId == SymId::articAccentStaccatoAbove || m_symId == SymId::articAccentStaccatoBelow
589 967 : || m_symId == SymId::tremoloDivisiDots2 || m_symId == SymId::tremoloDivisiDots3
590 2384 : || m_symId == SymId::tremoloDivisiDots4 || m_symId == SymId::tremoloDivisiDots6);
591 :
592 1315 : m_categories.setFlag(ArticulationCategory::ACCENT,
593 1226 : m_symId == SymId::articAccentAbove || m_symId == SymId::articAccentBelow
594 2541 : || m_symId == SymId::articAccentStaccatoAbove || m_symId == SymId::articAccentStaccatoBelow);
595 :
596 1315 : m_categories.setFlag(ArticulationCategory::MARCATO,
597 1258 : m_symId == SymId::articMarcatoAbove || m_symId == SymId::articMarcatoBelow
598 1257 : || m_symId == SymId::articMarcatoStaccatoAbove || m_symId == SymId::articMarcatoStaccatoBelow
599 2573 : || m_symId == SymId::articMarcatoTenutoAbove || m_symId == SymId::articMarcatoTenutoBelow);
600 :
601 1315 : m_categories.setFlag(ArticulationCategory::LUTE_FINGERING,
602 1312 : m_symId == SymId::stringsThumbPosition || m_symId == SymId::luteFingeringRHThumb
603 1312 : || m_symId == SymId::luteFingeringRHFirst || m_symId == SymId::luteFingeringRHSecond
604 2627 : || m_symId == SymId::luteFingeringRHThird);
605 1315 : m_categories.setFlag(ArticulationCategory::LAISSEZ_VIB,
606 1315 : m_symId == SymId::articLaissezVibrerAbove || m_symId == SymId::articLaissezVibrerBelow);
607 :
608 1315 : m_categories.setFlag(ArticulationCategory::HANDBELLS,
609 1315 : (static_cast<int>(m_symId) >= static_cast<int>(SymId::handbellsBelltree)
610 505 : && static_cast<int>(m_symId) <= static_cast<int>(SymId::handbellsTableSingleBell))
611 1820 : || (static_cast<int>(m_textType) >= static_cast<int>(ArticulationTextType::TD)
612 0 : && static_cast<int>(m_textType) <= static_cast<int>(ArticulationTextType::VIB))
613 : );
614 1315 : }
615 :
616 1823 : bool Articulation::isBasicArticulation() const
617 : {
618 : static const std::set<SymId> articulations{
619 : SymId::articAccentAbove, SymId::articAccentBelow,
620 : SymId::articStaccatoAbove, SymId::articStaccatoBelow,
621 : SymId::articTenutoAbove, SymId::articTenutoBelow,
622 : SymId::articMarcatoAbove, SymId::articMarcatoBelow,
623 :
624 : SymId::articAccentStaccatoAbove, SymId::articAccentStaccatoBelow,
625 : SymId::articMarcatoStaccatoAbove, SymId::articMarcatoStaccatoBelow,
626 : SymId::articMarcatoTenutoAbove, SymId::articMarcatoTenutoBelow,
627 : SymId::articTenutoStaccatoAbove, SymId::articTenutoStaccatoBelow,
628 : SymId::articTenutoAccentAbove, SymId::articTenutoAccentBelow,
629 : SymId::articStaccatissimoAbove, SymId::articStaccatissimoBelow,
630 : SymId::articStaccatissimoStrokeAbove, SymId::articStaccatissimoStrokeBelow,
631 : SymId::articStaccatissimoWedgeAbove, SymId::articStaccatissimoWedgeBelow,
632 : SymId::articStressAbove, SymId::articStressBelow
633 1839 : };
634 1823 : SymId symId = static_cast<SymId>(subtype());
635 1823 : return articulations.find(symId) != articulations.end();
636 : }
637 :
638 : //---------------------------------------------------------
639 : // accessibleInfo
640 : //---------------------------------------------------------
641 :
642 0 : String Articulation::accessibleInfo() const
643 : {
644 0 : return String(u"%1: %2").arg(EngravingItem::accessibleInfo(), translatedSubtypeUserName());
645 : }
646 :
647 2643 : void Articulation::setupShowOnTabStyles()
648 : {
649 : /// staccato
650 2643 : if (isStaccato()) {
651 348 : m_showOnTabStyles = { Sid::staccatoShowTabCommon, Sid::staccatoShowTabSimple };
652 : }
653 :
654 : /// accent
655 2643 : if (isAccent() || isMarcato()) {
656 236 : m_showOnTabStyles = { Sid::accentShowTabCommon, Sid::accentShowTabSimple };
657 : }
658 :
659 : /// turn
660 2643 : if (m_symId == SymId::ornamentTurn || m_symId == SymId::ornamentTurnInverted) {
661 65 : m_showOnTabStyles = { Sid::turnShowTabCommon, Sid::turnShowTabSimple };
662 : }
663 :
664 : /// mordent
665 2643 : if (m_symId == SymId::ornamentMordent || m_symId == SymId::ornamentShortTrill) {
666 63 : m_showOnTabStyles = { Sid::mordentShowTabCommon, Sid::mordentShowTabSimple };
667 : }
668 :
669 : /// wah
670 2643 : if (m_symId == SymId::brassMuteOpen || m_symId == SymId::brassMuteClosed) {
671 25 : m_showOnTabStyles = { Sid::wahShowTabCommon, Sid::wahShowTabSimple };
672 : }
673 :
674 : /// golpe
675 2643 : if (m_symId == SymId::guitarGolpe) {
676 0 : m_showOnTabStyles = { Sid::golpeShowTabCommon, Sid::golpeShowTabSimple };
677 : }
678 2643 : }
679 :
680 65 : void Articulation::styleChanged()
681 : {
682 65 : if (m_text) {
683 0 : m_text->styleChanged();
684 : }
685 :
686 65 : bool isGolpeThumb = m_symId == SymId::guitarGolpe && m_anchor == ArticulationAnchor::BOTTOM;
687 65 : EngravingItem::styleChanged();
688 65 : if (isGolpeThumb) {
689 0 : setAnchor(ArticulationAnchor::BOTTOM);
690 : }
691 65 : }
692 :
693 3454 : bool Articulation::isOnCrossBeamSide() const
694 : {
695 3454 : ChordRest* cr = chordRest();
696 3454 : if (!cr || !cr->isChord()) {
697 0 : return false;
698 : }
699 3454 : Chord* chord = toChord(cr);
700 3454 : return chord->beam() && (chord->beam()->cross() || chord->staffMove() != 0) && (up() == chord->up());
701 : }
702 :
703 7192 : staff_idx_t Articulation::vStaffIdx() const
704 : {
705 7192 : ChordRest* cr = chordRest();
706 7192 : if (!cr) {
707 0 : return staffIdx();
708 : }
709 7192 : return cr->vStaffIdx();
710 : }
711 :
712 : struct ArticulationGroup
713 : {
714 : SymId first;
715 : SymId second;
716 :
717 266 : bool operator <(const ArticulationGroup& other) const
718 : {
719 266 : if ((first == other.first && second == other.second)
720 266 : || (first == other.second && second == other.first)) {
721 0 : return false;
722 : }
723 :
724 266 : if (first != other.first) {
725 76 : return first < other.first;
726 : }
727 :
728 190 : return second < other.second;
729 : }
730 : };
731 :
732 : namespace mu::engraving {
733 : static const std::map<SymId, ArticulationGroup> ARTICULATION_ABOVE_SPLIT_GROUPS = {
734 : { SymId::articAccentStaccatoAbove, { SymId::articStaccatoAbove, SymId::articAccentAbove } },
735 : { SymId::articTenutoAccentAbove, { SymId::articTenutoAbove, SymId::articAccentAbove } },
736 : { SymId::articTenutoStaccatoAbove, { SymId::articTenutoAbove, SymId::articStaccatoAbove } },
737 : { SymId::articMarcatoStaccatoAbove, { SymId::articStaccatoAbove, SymId::articMarcatoAbove } },
738 : { SymId::articMarcatoTenutoAbove, { SymId::articTenutoAbove, SymId::articMarcatoAbove } },
739 : };
740 :
741 : static const std::map<SymId, ArticulationGroup> ARTICULATION_BELOW_SPLIT_GROUP = {
742 : { SymId::articAccentStaccatoBelow, { SymId::articStaccatoBelow, SymId::articAccentBelow } },
743 : { SymId::articTenutoAccentBelow, { SymId::articTenutoBelow, SymId::articAccentBelow } },
744 : { SymId::articTenutoStaccatoBelow, { SymId::articTenutoBelow, SymId::articStaccatoBelow } },
745 : { SymId::articMarcatoStaccatoBelow, { SymId::articStaccatoBelow, SymId::articMarcatoBelow } },
746 : { SymId::articMarcatoTenutoBelow, { SymId::articTenutoBelow, SymId::articMarcatoBelow } },
747 : };
748 :
749 : static const std::map<ArticulationGroup, SymId> ARTICULATION_ABOVE_JOIN_GROUPS = {
750 : { { SymId::articStaccatoAbove, SymId::articAccentAbove }, SymId::articAccentStaccatoAbove },
751 : { { SymId::articTenutoAbove, SymId::articAccentAbove }, SymId::articTenutoAccentAbove },
752 : { { SymId::articTenutoAbove, SymId::articStaccatoAbove }, SymId::articTenutoStaccatoAbove },
753 : { { SymId::articStaccatoAbove, SymId::articMarcatoAbove }, SymId::articMarcatoStaccatoAbove },
754 : { { SymId::articTenutoAbove, SymId::articMarcatoAbove }, SymId::articMarcatoTenutoAbove },
755 : };
756 :
757 : static const std::map<SymId, SymId> ARTICULATION_PLACEMENTS = {
758 : { SymId::articStaccatissimoAbove, SymId::articStaccatissimoBelow },
759 : { SymId::articStaccatissimoStrokeAbove, SymId::articStaccatissimoStrokeBelow },
760 : { SymId::articStaccatissimoWedgeAbove, SymId::articStaccatissimoWedgeBelow },
761 : { SymId::articStaccatoAbove, SymId::articStaccatoBelow },
762 : { SymId::articAccentStaccatoAbove, SymId::articAccentStaccatoBelow },
763 : { SymId::articMarcatoStaccatoAbove, SymId::articMarcatoStaccatoBelow },
764 : { SymId::articTenutoStaccatoAbove, SymId::articTenutoStaccatoBelow },
765 : { SymId::articTenutoAccentAbove, SymId::articTenutoAccentBelow },
766 : { SymId::articMarcatoTenutoAbove, SymId::articMarcatoTenutoBelow },
767 : { SymId::articTenutoAbove, SymId::articTenutoBelow },
768 : { SymId::articMarcatoAbove, SymId::articMarcatoBelow },
769 : { SymId::articAccentAbove, SymId::articAccentBelow }
770 : };
771 :
772 307 : std::set<SymId> splitArticulations(const std::set<SymId>& articulationSymbolIds)
773 : {
774 307 : std::set<SymId> result;
775 614 : for (const SymId& articulationSymbolId : articulationSymbolIds) {
776 307 : auto artic = ARTICULATION_ABOVE_SPLIT_GROUPS.find(articulationSymbolId);
777 307 : if (artic != ARTICULATION_ABOVE_SPLIT_GROUPS.end()) {
778 : // check above
779 46 : const ArticulationGroup& group = artic->second;
780 46 : result.insert(group.first);
781 46 : result.insert(group.second);
782 : } else {
783 : // check below
784 261 : artic = ARTICULATION_BELOW_SPLIT_GROUP.find(articulationSymbolId);
785 261 : if (artic != ARTICULATION_BELOW_SPLIT_GROUP.end()) {
786 28 : const ArticulationGroup& group = artic->second;
787 28 : result.insert(group.first);
788 28 : result.insert(group.second);
789 : } else {
790 233 : result.insert(articulationSymbolId);
791 : }
792 : }
793 : }
794 :
795 307 : return result;
796 0 : }
797 :
798 0 : std::set<SymId> joinArticulations(const std::set<SymId>& articulationSymbolIds)
799 : {
800 0 : SymIdList result;
801 :
802 0 : SymIdList vsymbolIds(articulationSymbolIds.begin(), articulationSymbolIds.end());
803 :
804 0 : std::sort(vsymbolIds.begin(), vsymbolIds.end(), [](SymId l, SymId r) {
805 0 : return l > r;
806 : });
807 :
808 0 : std::set<SymId> splittedSymbols;
809 :
810 0 : auto symbolSelected = [&splittedSymbols](const SymId& symbolId) -> bool {
811 0 : return splittedSymbols.find(symbolId) != splittedSymbols.end();
812 0 : };
813 :
814 0 : for (size_t i = 0; i < vsymbolIds.size(); i++) {
815 0 : if (symbolSelected(vsymbolIds[i])) {
816 0 : continue;
817 : }
818 :
819 0 : bool found = false;
820 0 : for (size_t j = i + 1; j < vsymbolIds.size(); j++) {
821 0 : if (symbolSelected(vsymbolIds[i]) || symbolSelected(vsymbolIds[j])) {
822 0 : continue;
823 : }
824 :
825 0 : ArticulationGroup group = { vsymbolIds[i], vsymbolIds[j] };
826 0 : auto joinArticulation = ARTICULATION_ABOVE_JOIN_GROUPS.find(group);
827 0 : if (joinArticulation != ARTICULATION_ABOVE_JOIN_GROUPS.end()) {
828 0 : result.push_back(joinArticulation->second);
829 0 : splittedSymbols.insert(vsymbolIds[i]);
830 0 : splittedSymbols.insert(vsymbolIds[j]);
831 0 : found = true;
832 : }
833 : }
834 :
835 0 : if (!found) {
836 0 : result.push_back(vsymbolIds[i]);
837 0 : splittedSymbols.insert(vsymbolIds[i]);
838 : }
839 : }
840 :
841 0 : std::sort(result.begin(), result.end(), [](SymId l, SymId r) {
842 0 : return l < r;
843 : });
844 :
845 0 : return std::set<SymId>(result.begin(), result.end());
846 0 : }
847 :
848 0 : std::set<SymId> updateArticulations(const std::set<SymId>& articulationSymbolIds, SymId articulationSymbolId,
849 : ArticulationsUpdateMode updateMode)
850 : {
851 0 : std::set<SymId> splittedArticulations = splitArticulations(articulationSymbolIds);
852 :
853 0 : switch (articulationSymbolId) {
854 0 : case SymId::articMarcatoAbove: {
855 0 : if (splittedArticulations.find(SymId::articAccentAbove) != splittedArticulations.end()) {
856 0 : splittedArticulations.erase(SymId::articAccentAbove);
857 0 : splittedArticulations.insert(SymId::articMarcatoAbove);
858 0 : } else if (splittedArticulations.find(SymId::articMarcatoAbove) != splittedArticulations.end()) {
859 0 : if (updateMode == ArticulationsUpdateMode::Remove) {
860 0 : splittedArticulations.erase(SymId::articMarcatoAbove);
861 : }
862 : } else {
863 0 : splittedArticulations.insert(SymId::articMarcatoAbove);
864 : }
865 0 : break;
866 : }
867 0 : case SymId::articAccentAbove: {
868 0 : if (splittedArticulations.find(SymId::articMarcatoAbove) != splittedArticulations.end()) {
869 0 : splittedArticulations.erase(SymId::articMarcatoAbove);
870 0 : splittedArticulations.insert(SymId::articAccentAbove);
871 0 : } else if (splittedArticulations.find(SymId::articAccentAbove) != splittedArticulations.end()) {
872 0 : if (updateMode == ArticulationsUpdateMode::Remove) {
873 0 : splittedArticulations.erase(SymId::articAccentAbove);
874 : }
875 : } else {
876 0 : splittedArticulations.insert(SymId::articAccentAbove);
877 : }
878 0 : break;
879 : }
880 0 : case SymId::articTenutoAbove: {
881 0 : if (splittedArticulations.find(SymId::articTenutoAbove) != splittedArticulations.end()) {
882 0 : if (updateMode == ArticulationsUpdateMode::Remove) {
883 0 : splittedArticulations.erase(SymId::articTenutoAbove);
884 : }
885 : } else {
886 0 : splittedArticulations.insert(SymId::articTenutoAbove);
887 : }
888 0 : break;
889 : }
890 0 : case SymId::articStaccatoAbove: {
891 0 : if (splittedArticulations.find(SymId::articStaccatoAbove) != splittedArticulations.end()) {
892 0 : if (updateMode == ArticulationsUpdateMode::Remove) {
893 0 : splittedArticulations.erase(SymId::articStaccatoAbove);
894 : }
895 : } else {
896 0 : splittedArticulations.insert(SymId::articStaccatoAbove);
897 : }
898 0 : break;
899 : }
900 0 : default:
901 0 : break;
902 : }
903 : // we no longer want to join articulations when adding or removing individual ones. combined articulations can still
904 : // be added by the palette (which doesn't perform any of this splitting)
905 0 : return splittedArticulations;
906 0 : }
907 :
908 0 : std::set<SymId> flipArticulations(const std::set<SymId>& articulationSymbolIds, PlacementV placement)
909 : {
910 0 : std::set<SymId> result;
911 0 : switch (placement) {
912 0 : case PlacementV::ABOVE:
913 0 : for (const SymId& articulationSymbolId: articulationSymbolIds) {
914 0 : bool found = false;
915 0 : for (auto it = ARTICULATION_PLACEMENTS.begin(); it != ARTICULATION_PLACEMENTS.end(); ++it) {
916 0 : if (it->second == articulationSymbolId) {
917 0 : result.insert(it->first);
918 0 : found = true;
919 0 : break;
920 : }
921 : }
922 :
923 0 : if (!found) {
924 0 : result.insert(articulationSymbolId);
925 : }
926 : }
927 0 : break;
928 0 : case PlacementV::BELOW:
929 0 : for (const SymId& articulationSymbolId: articulationSymbolIds) {
930 0 : bool found = false;
931 0 : for (auto it = ARTICULATION_PLACEMENTS.begin(); it != ARTICULATION_PLACEMENTS.end(); ++it) {
932 0 : if (it->first == articulationSymbolId) {
933 0 : result.insert(it->second);
934 0 : found = true;
935 0 : break;
936 : }
937 : }
938 :
939 0 : if (!found) {
940 0 : result.insert(articulationSymbolId);
941 : }
942 : }
943 : }
944 :
945 0 : return result;
946 0 : }
947 :
948 3454 : double Articulation::LayoutData::opticalCenter() const
949 : {
950 3454 : switch (symId.value()) {
951 4 : case SymId::handbellsMartellatoLift:
952 4 : return 0.5 * m_item->symWidth(SymId::handbellsMartellato);
953 8 : case SymId::handbellsMalletLft:
954 8 : return 0.5 * m_item->symWidth(SymId::handbellsMalletBellOnTable);
955 4 : case SymId::handbellsPluckLift:
956 4 : return 0.5 * m_item->symWidth(SymId::articStaccatoAbove);
957 3438 : default:
958 3438 : return 0.5 * bbox().width();
959 : }
960 : }
961 : }
|