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 : /**
24 : \file
25 : Implementation of EngravingItem, ElementList
26 : */
27 :
28 : #include "engravingitem.h"
29 :
30 : #include <cmath>
31 :
32 : #include "containers.h"
33 : #include "io/buffer.h"
34 : #include "translation.h"
35 :
36 : #include "iengravingconfiguration.h"
37 : #include "iengravingcontextconfiguration.h"
38 : #include "iengravingfont.h"
39 :
40 : #include "rw/rwregister.h"
41 :
42 : #include "types/typesconv.h"
43 :
44 : #include "rendering/iscorerenderer.h"
45 : #include "rendering/score/autoplace.h"
46 : #include "rendering/score/chordlayout.h"
47 : #include "rendering/score/tlayout.h"
48 :
49 : #ifndef ENGRAVING_NO_ACCESSIBILITY
50 : #include "accessibility/accessibleitem.h"
51 : #include "accessibility/accessibleroot.h"
52 : #endif
53 :
54 : #include "../editing/addremoveelement.h"
55 : #include "../editing/editdata.h"
56 : #include "../editing/editproperty.h"
57 : #include "../editing/elementeditdata.h"
58 : #include "../editing/navigation.h"
59 :
60 : #include "chord.h"
61 : #include "dummyparent.h"
62 : #include "factory.h"
63 : #include "linkedobjects.h"
64 : #include "masterscore.h"
65 : #include "measure.h"
66 : #include "mscore.h"
67 : #include "note.h"
68 : #include "page.h"
69 : #include "parenthesis.h"
70 : #include "part.h"
71 : #include "rootitem.h"
72 : #include "score.h"
73 : #include "segment.h"
74 : #include "shape.h"
75 : #include "sig.h"
76 : #include "staff.h"
77 : #include "stafflines.h"
78 : #include "stafftype.h"
79 : #include "system.h"
80 :
81 : #include "log.h"
82 : #define LOG_PROP() if (0) LOGD()
83 :
84 : using namespace mu;
85 : using namespace muse::io;
86 : using namespace muse::draw;
87 : using namespace mu::engraving;
88 : using namespace mu::engraving::rendering::score;
89 :
90 : namespace mu::engraving {
91 621107 : EngravingItem::EngravingItem(const ElementType& type, EngravingObject* parent, ElementFlags f)
92 621107 : : EngravingObject(type, parent)
93 : {
94 621107 : m_flags = f;
95 621107 : m_color = configuration()->defaultColor();
96 621107 : m_z = -1;
97 621107 : m_minDistance = 0.0_sp;
98 621107 : }
99 :
100 18740 : EngravingItem::EngravingItem(const EngravingItem& e, bool link)
101 18740 : : EngravingObject(e)
102 : {
103 18740 : m_offset = e.m_offset;
104 18740 : m_track = e.m_track;
105 18740 : m_flags = e.m_flags;
106 18740 : setFlag(ElementFlag::SELECTED, false);
107 18740 : m_z = e.m_z;
108 18740 : m_color = e.m_color;
109 18740 : m_minDistance = e.m_minDistance;
110 18740 : m_excludeVerticalAlign = e.m_excludeVerticalAlign;
111 18740 : itemDiscovered = false;
112 :
113 18740 : m_accessibleEnabled = e.m_accessibleEnabled;
114 :
115 18740 : if (e.m_leftParenthesis) {
116 0 : m_leftParenthesis = e.m_leftParenthesis->clone();
117 0 : m_leftParenthesis->setOwnershipParent(this);
118 0 : m_leftParenthesis->setTrack(track());
119 0 : if (link) {
120 0 : score()->undo(new Link(m_leftParenthesis, e.m_leftParenthesis));
121 : }
122 : }
123 18740 : if (e.m_rightParenthesis) {
124 0 : m_rightParenthesis = e.m_rightParenthesis->clone();
125 0 : m_rightParenthesis->setOwnershipParent(this);
126 0 : m_rightParenthesis->setTrack(track());
127 0 : if (link) {
128 0 : score()->undo(new Link(m_rightParenthesis, e.m_rightParenthesis));
129 : }
130 : }
131 18740 : }
132 :
133 596767 : EngravingItem::~EngravingItem()
134 : {
135 596767 : Score::onElementDestruction(this);
136 :
137 596767 : delete m_layoutData;
138 596767 : }
139 :
140 : #ifndef ENGRAVING_NO_ACCESSIBILITY
141 4668 : void EngravingItem::setupAccessible()
142 : {
143 4668 : if (m_accessible) {
144 0 : return;
145 : }
146 :
147 : static const std::set<ElementType> accessibleDisabled = {
148 : ElementType::LEDGER_LINE
149 4698 : };
150 :
151 4668 : if (score() && !score()->isPaletteScore()) {
152 4668 : if (!muse::contains(accessibleDisabled, type())) {
153 4668 : m_accessible = createAccessible();
154 4668 : m_accessible->setup();
155 : }
156 : }
157 : }
158 :
159 : #endif
160 :
161 6457 : bool EngravingItem::accessibleEnabled() const
162 : {
163 6457 : return m_accessibleEnabled;
164 : }
165 :
166 332956 : void EngravingItem::setAccessibleEnabled(bool enabled)
167 : {
168 332956 : m_accessibleEnabled = enabled;
169 332956 : }
170 :
171 634 : EngravingItem* EngravingItem::accessibleParentItem() const
172 : {
173 634 : if (EngravingItem* p = layoutParent()) {
174 623 : return p;
175 : }
176 :
177 11 : if (ownershipParent()) {
178 : // attached to something, but not placed anywhere: in neither tree
179 4 : return nullptr;
180 : }
181 :
182 : // not attached to anything: head of the tree those objects form
183 7 : return score() && score()->dummy() ? score()->dummy()->rootItem() : nullptr;
184 : }
185 :
186 625 : EngravingItemList EngravingItem::accessibleChildren() const
187 : {
188 625 : return childrenItems();
189 : }
190 :
191 7904582 : EngravingItem* EngravingItem::ownershipParentItem() const
192 : {
193 7904582 : EngravingObject* p = ownershipParent();
194 7904582 : if (p && p->isEngravingItem()) {
195 7821163 : return toEngravingItem(p);
196 : }
197 :
198 83419 : return nullptr;
199 : }
200 :
201 7303967 : EngravingItem* EngravingItem::layoutParent() const
202 : {
203 7303967 : return ownershipParentItem();
204 : }
205 :
206 734317 : const std::shared_ptr<IEngravingConfiguration>& EngravingItem::configuration() const
207 : {
208 734317 : return score()->configuration.get();
209 : }
210 :
211 4668 : const muse::modularity::ContextPtr& EngravingItem::iocContext() const
212 : {
213 4668 : return score()->iocContext();
214 : }
215 :
216 1883 : const std::shared_ptr<IEngravingContextConfiguration>& EngravingItem::contextConfiguration() const
217 : {
218 1883 : return score()->contextConfiguration.get();
219 : }
220 :
221 66193 : const std::shared_ptr<rendering::IScoreRenderer>& EngravingItem::renderer() const
222 : {
223 66193 : return score()->renderer.get();
224 : }
225 :
226 : #ifndef ENGRAVING_NO_ACCESSIBILITY
227 0 : AccessibleItemPtr EngravingItem::createAccessible()
228 : {
229 0 : return std::make_shared<AccessibleItem>(this);
230 : }
231 :
232 19810 : void EngravingItem::notifyAboutNameChanged()
233 : {
234 19810 : if (!selected()) {
235 19623 : return;
236 : }
237 :
238 187 : if (m_accessible) {
239 0 : doInitAccessible();
240 0 : m_accessible->accessibleRoot()->notifyAboutFocusedElementNameChanged();
241 : }
242 : }
243 :
244 : #endif
245 :
246 : //---------------------------------------------------------
247 : // spatiumChanged
248 : //---------------------------------------------------------
249 :
250 3705 : void EngravingItem::spatiumChanged(double oldValue, double newValue)
251 : {
252 3705 : if (offsetIsSpatiumDependent()) {
253 3636 : m_offset *= (newValue / oldValue);
254 : }
255 3705 : }
256 :
257 : //---------------------------------------------------------
258 : // localSpatiumChanged
259 : // the scale of a staff changed
260 : //---------------------------------------------------------
261 :
262 0 : void EngravingItem::localSpatiumChanged(double oldValue, double newValue)
263 : {
264 0 : if (offsetIsSpatiumDependent()) {
265 0 : m_offset *= (newValue / oldValue);
266 : }
267 0 : }
268 :
269 : //---------------------------------------------------------
270 : // spatium
271 : //---------------------------------------------------------
272 :
273 4798907 : double EngravingItem::spatium() const
274 : {
275 4798907 : EngravingItem* p = layoutParent();
276 4798907 : if (systemFlag() || (p && p->systemFlag())) {
277 43673 : return style().spatium();
278 : }
279 4755234 : Staff* s = staff();
280 4755234 : return s ? s->spatium(this) : style().spatium();
281 : }
282 :
283 2224866 : double EngravingItem::defaultSpatium() const
284 : {
285 2224866 : return style().defaultSpatium();
286 : }
287 :
288 444 : bool EngravingItem::isInteractionAvailable() const
289 : {
290 444 : if (!getProperty(Pid::VISIBLE).toBool() && (score()->printing() || !score()->isShowInvisible())) {
291 0 : return false;
292 : }
293 :
294 444 : return true;
295 : }
296 :
297 : //---------------------------------------------------------
298 : // offsetIsSpatiumDependent
299 : //---------------------------------------------------------
300 :
301 80841 : bool EngravingItem::offsetIsSpatiumDependent() const
302 : {
303 80841 : return sizeIsSpatiumDependent() || (m_flags & ElementFlag::ON_STAFF);
304 : }
305 :
306 1 : Sid EngravingItem::defaultPosSid() const
307 : {
308 1 : return Sid::NOSTYLE;
309 : }
310 :
311 75823 : PointF EngravingItem::defaultPos() const
312 : {
313 75823 : Sid styleId = defaultPosSid();
314 :
315 75823 : if (styleId == Sid::NOSTYLE) {
316 13 : return PointF();
317 : }
318 :
319 75810 : PointF offsetPos = style().value(styleId).value<PointF>();
320 75810 : if (offsetIsSpatiumDependent()) {
321 63911 : offsetPos *= spatium();
322 : } else {
323 11899 : offsetPos *= DPMM;
324 : }
325 :
326 75810 : return offsetPos;
327 : }
328 :
329 235200 : PlacementV EngravingItem::placement() const
330 : {
331 235200 : return flag(ElementFlag::PLACE_ABOVE) && !isSystemObjectBelowBottomStaff() ? PlacementV::ABOVE : PlacementV::BELOW;
332 : }
333 :
334 : //---------------------------------------------------------
335 : // magS
336 : //---------------------------------------------------------
337 :
338 2035330 : double EngravingItem::magS() const
339 : {
340 2035330 : return mag() * (style().spatium() / defaultSpatium());
341 : }
342 :
343 : //---------------------------------------------------------
344 : // name
345 : //---------------------------------------------------------
346 :
347 0 : TranslatableString EngravingItem::subtypeUserName() const
348 : {
349 0 : return {};
350 : }
351 :
352 16 : String EngravingItem::translatedSubtypeUserName() const
353 : {
354 16 : return subtypeUserName().translated();
355 : }
356 :
357 : //---------------------------------------------------------
358 : // linkedClone
359 : //---------------------------------------------------------
360 :
361 733 : EngravingItem* EngravingItem::linkedClone()
362 : {
363 733 : EngravingItem* e = clone();
364 733 : score()->undo(new Link(e, this));
365 733 : return e;
366 : }
367 :
368 : //---------------------------------------------------------
369 : // deleteLater
370 : //---------------------------------------------------------
371 :
372 133 : void EngravingItem::deleteLater()
373 : {
374 133 : if (selected()) {
375 0 : score()->deselect(this);
376 : }
377 133 : masterScore()->cmdState().deleteLater(this);
378 133 : }
379 :
380 : //---------------------------------------------------------
381 : // scanElements
382 : /// If leaf node, apply `func` on this element (after checking if it is visible).
383 : /// Otherwise, recurse over all children (see ScoreElement::scanElements).
384 : /// Note: This function is overridden in some classes to skip certain children,
385 : /// or to apply `func` even to non-leaf nodes.
386 : //---------------------------------------------------------
387 :
388 11034 : void EngravingItem::scanElements(std::function<void(EngravingItem*)> func)
389 : {
390 11034 : if (m_leftParenthesis) {
391 2 : m_leftParenthesis->scanElements(func);
392 : }
393 :
394 11034 : if (m_rightParenthesis) {
395 2 : m_rightParenthesis->scanElements(func);
396 : }
397 :
398 11034 : func(this);
399 11034 : }
400 :
401 780 : bool EngravingItem::collectForDrawing() const
402 : {
403 780 : if (!visible() && !score()->isShowInvisible()) {
404 0 : return false;
405 : }
406 :
407 780 : return true;
408 : }
409 :
410 : //---------------------------------------------------------
411 : // reset
412 : //---------------------------------------------------------
413 :
414 23 : void EngravingItem::reset()
415 : {
416 23 : undoResetProperty(Pid::AUTOPLACE);
417 23 : undoResetProperty(Pid::PLACEMENT);
418 23 : undoResetProperty(Pid::MIN_DISTANCE);
419 23 : undoResetProperty(Pid::OFFSET);
420 23 : undoResetProperty(Pid::LEADING_SPACE);
421 23 : EngravingObject::reset();
422 23 : }
423 :
424 : //---------------------------------------------------------
425 : // change
426 : //---------------------------------------------------------
427 :
428 115 : void EngravingItem::change(EngravingItem* o, EngravingItem* n)
429 : {
430 115 : remove(o);
431 115 : add(n);
432 115 : }
433 :
434 : //---------------------------------------------------------
435 : // staff
436 : //---------------------------------------------------------
437 :
438 27447056 : Staff* EngravingItem::staff() const
439 : {
440 27447056 : if (!hasStaff() || score()->staves().empty()) {
441 970728 : return nullptr;
442 : }
443 :
444 26476328 : return score()->staff(staffIdx());
445 : }
446 :
447 27447056 : bool EngravingItem::hasStaff() const
448 : {
449 27447056 : return m_track != muse::nidx;
450 : }
451 :
452 : //---------------------------------------------------------
453 : // staffType
454 : //---------------------------------------------------------
455 :
456 2607765 : const StaffType* EngravingItem::staffType() const
457 : {
458 2607765 : Staff* s = staff();
459 2607765 : return s ? s->staffTypeForElement(this) : nullptr;
460 : }
461 :
462 : //---------------------------------------------------------
463 : // onTabStaff
464 : //---------------------------------------------------------
465 :
466 815114 : bool EngravingItem::onTabStaff() const
467 : {
468 815114 : const StaffType* stt = staffType();
469 815114 : return stt ? stt->isTabStaff() : false;
470 : }
471 :
472 0 : bool EngravingItem::hasGrips() const
473 : {
474 0 : return gripsCount() > 0;
475 : }
476 :
477 4964481 : track_idx_t EngravingItem::track() const
478 : {
479 4964481 : return m_track;
480 : }
481 :
482 540238 : void EngravingItem::setTrack(track_idx_t val)
483 : {
484 540238 : IF_ASSERT_FAILED(val < m_score->ntracks() || val == 0 || val == muse::nidx || m_score->isPaletteScore()) {
485 : // Zero and muse::nidx have special use cases.
486 : // In all other cases the track can't be larger than the total track count.
487 0 : return;
488 : }
489 :
490 540238 : m_track = val;
491 :
492 540238 : if (m_leftParenthesis) {
493 2 : m_leftParenthesis->setTrack(val);
494 : }
495 540238 : if (m_rightParenthesis) {
496 2 : m_rightParenthesis->setTrack(val);
497 : }
498 : }
499 :
500 : //---------------------------------------------------------
501 : // z
502 : //---------------------------------------------------------
503 :
504 107022 : int EngravingItem::z() const
505 : {
506 107022 : if (m_z == -1) {
507 88939 : m_z = int(type()) * 100;
508 : }
509 107022 : return m_z;
510 : }
511 :
512 6447 : void EngravingItem::setZ(int val)
513 : {
514 6447 : m_z = val;
515 6447 : }
516 :
517 36139588 : staff_idx_t EngravingItem::staffIdx() const
518 : {
519 36139588 : return track2staff(m_track);
520 : }
521 :
522 4468 : void EngravingItem::setStaffIdx(staff_idx_t val)
523 : {
524 4468 : voice_idx_t voiceIdx = voice();
525 4468 : setTrack(staff2track(val, voiceIdx == muse::nidx ? 0 : voiceIdx));
526 4468 : }
527 :
528 746540 : staff_idx_t EngravingItem::effectiveStaffIdx() const
529 : {
530 746540 : if (!systemFlag()) {
531 726486 : return vStaffIdx();
532 : }
533 :
534 20054 : const System* system = toSystem(findAncestor(ElementType::SYSTEM));
535 20054 : if (!system || system->vbox()) {
536 0 : return vStaffIdx();
537 : }
538 :
539 20054 : if (isSystemObjectBelowBottomStaff()) {
540 0 : return system->lastVisibleStaff();
541 : }
542 :
543 20054 : staff_idx_t originalStaffIdx = staffIdx();
544 20054 : if (originalStaffIdx == muse::nidx) {
545 44 : return muse::nidx;
546 : }
547 :
548 20010 : const Measure* m = findMeasure();
549 :
550 : // TODO - make more system markings ignore empty cutaway measures
551 20628 : auto cutawayMeasureNo =[&](staff_idx_t staffIdx) {
552 20628 : if (!m) {
553 890 : return false;
554 : }
555 19738 : bool measureNo = isMeasureNumber();
556 19738 : bool staffVisible = m->mstaves()[staffIdx]->visible();
557 19738 : bool cutaway = score()->staff(staffIdx)->cutaway() && m->isEmpty(staffIdx);
558 19738 : return (cutaway || !staffVisible) && measureNo;
559 20010 : };
560 :
561 20010 : const std::vector<Staff*>& systemObjectStaves = m_score->systemObjectStaves(); // CAUTION: may not be ordered
562 20010 : if (originalStaffIdx > 0) {
563 630 : staff_idx_t prevSysObjStaffIdx = 0;
564 2216 : for (Staff* sysObjStaff : systemObjectStaves) {
565 1586 : staff_idx_t sysObjStaffIdx = sysObjStaff->idx();
566 1586 : if (sysObjStaffIdx > prevSysObjStaffIdx && sysObjStaffIdx < originalStaffIdx) {
567 430 : prevSysObjStaffIdx = sysObjStaffIdx;
568 : }
569 : }
570 :
571 630 : bool omitObject = true;
572 630 : for (staff_idx_t stfIdx = prevSysObjStaffIdx; stfIdx < originalStaffIdx; ++stfIdx) {
573 630 : if (m_score->staff(stfIdx)->show() && system->staff(stfIdx)->show() && !cutawayMeasureNo(stfIdx)) {
574 630 : omitObject = false;
575 630 : break;
576 : }
577 : }
578 :
579 630 : if (omitObject) {
580 : // If all staves between this and the previous system object are hidden
581 : // we omit this object because it will be replaced by the upper one
582 0 : return muse::nidx;
583 : }
584 : }
585 :
586 20010 : if (m_score->staff(originalStaffIdx)->show() && system->staff(originalStaffIdx)->show() && !cutawayMeasureNo(originalStaffIdx)) {
587 19998 : return originalStaffIdx;
588 : }
589 :
590 12 : return system->nextVisibleStaff(originalStaffIdx);
591 : }
592 :
593 2446 : bool EngravingItem::isTopSystemObject() const
594 : {
595 2446 : return systemFlag() && track() == 0;
596 : }
597 :
598 1531646 : staff_idx_t EngravingItem::vStaffIdx() const
599 : {
600 1531646 : return staffIdx();
601 : }
602 :
603 2501028 : voice_idx_t EngravingItem::voice() const
604 : {
605 2501028 : return track2voice(m_track);
606 : }
607 :
608 56 : void EngravingItem::setVoice(voice_idx_t v)
609 : {
610 56 : setTrack((m_track / VOICES) * VOICES + v);
611 56 : }
612 :
613 : //---------------------------------------------------------
614 : // tick
615 : //---------------------------------------------------------
616 :
617 6946007 : Fraction EngravingItem::tick() const
618 : {
619 : // walks EngravingObject, because the chain leaves EngravingItem at the score
620 8862146 : for (const EngravingObject* e = ownershipParent(); e; e = e->ownershipParent()) {
621 8386078 : if (e->isSegment()) {
622 6229567 : return toSegment(e)->tick();
623 2156511 : } else if (e->isMeasureBase()) {
624 240372 : return toMeasureBase(e)->tick();
625 : }
626 : }
627 476068 : return Fraction(0, 1);
628 : }
629 :
630 : //---------------------------------------------------------
631 : // rtick
632 : //---------------------------------------------------------
633 :
634 1482281 : Fraction EngravingItem::rtick() const
635 : {
636 2647439 : for (const EngravingObject* e = ownershipParent(); e; e = e->ownershipParent()) {
637 2647349 : if (e->isSegment()) {
638 1482191 : return toSegment(e)->rtick();
639 : }
640 : }
641 90 : return Fraction(0, 1);
642 : }
643 :
644 : //---------------------------------------------------------
645 : // beat
646 : //---------------------------------------------------------
647 :
648 0 : Fraction EngravingItem::beat() const
649 : {
650 : // Returns an appropriate fraction of ticks for use as a "Beat" reference
651 : // in the Select All Similar filter.
652 : int bar, beat, ticks;
653 0 : TimeSigMap* tsm = score()->sigmap();
654 0 : tsm->tickValues(tick().ticks(), &bar, &beat, &ticks);
655 0 : int ticksB = ticks_beat(tsm->timesig(tick().ticks()).timesig().denominator());
656 :
657 0 : Fraction complexFraction((++beat * ticksB) + ticks, ticksB);
658 0 : return complexFraction.reduced();
659 : }
660 :
661 : //---------------------------------------------------------
662 : // part
663 : //---------------------------------------------------------
664 :
665 678610 : Part* EngravingItem::part() const
666 : {
667 678610 : Staff* s = staff();
668 678610 : return s ? s->part() : 0;
669 : }
670 :
671 301215 : Part* EngravingItem::originPart() const
672 : {
673 301215 : Part* p = part();
674 301215 : return p ? p->originPart(track(), tick()) : nullptr;
675 : }
676 :
677 298720 : void EngravingItem::setColor(const Color& c)
678 : {
679 298720 : m_color = c;
680 298720 : }
681 :
682 153228 : Color EngravingItem::color() const
683 : {
684 153228 : return m_color;
685 : }
686 :
687 : //---------------------------------------------------------
688 : // curColor
689 : //---------------------------------------------------------
690 :
691 385 : Color EngravingItem::curColor(const rendering::PaintOptions& opt) const
692 : {
693 385 : return curColor(getProperty(Pid::VISIBLE).toBool(), opt);
694 : }
695 :
696 : //---------------------------------------------------------
697 : // curColor
698 : //---------------------------------------------------------
699 :
700 385 : Color EngravingItem::curColor(bool isVisible, const rendering::PaintOptions& opt) const
701 : {
702 385 : return curColor(isVisible, color(), opt);
703 : }
704 :
705 385 : Color EngravingItem::curColor(bool isVisible, Color normalColor, const rendering::PaintOptions& opt) const
706 : {
707 385 : if (opt.isPrinting) {
708 : // For printing, always use the actual color of the item (default black unless overridden by the user)
709 385 : return normalColor;
710 : }
711 :
712 0 : if (flag(ElementFlag::DROP_TARGET)) {
713 0 : return configuration()->highlightSelectionColor(track() == muse::nidx ? 0 : voice());
714 : }
715 :
716 0 : bool marked = false;
717 0 : if (isNote()) {
718 0 : marked = toNote(this)->mark();
719 : }
720 :
721 0 : auto maybeOverrideColor = [&](Color c) -> Color {
722 0 : if (opt.overrideItemColor) {
723 0 : return opt.overrideItemColor(this, c);
724 : }
725 0 : return c;
726 0 : };
727 :
728 0 : if (selected() || marked) {
729 0 : voice_idx_t voiceForColorChoice = track() == muse::nidx ? 0 : voice();
730 0 : if (hasVoiceAssignmentProperties()) {
731 0 : VoiceAssignment voiceAssignment = getProperty(Pid::VOICE_ASSIGNMENT).value<VoiceAssignment>();
732 0 : if (voiceAssignment != VoiceAssignment::CURRENT_VOICE_ONLY) {
733 0 : voiceForColorChoice = VOICES;
734 : }
735 : }
736 : return maybeOverrideColor(
737 0 : configuration()->selectionColor(voiceForColorChoice, isVisible, isUnlinkedFromMaster()));
738 : }
739 :
740 0 : if (!isVisible) {
741 0 : return configuration()->invisibleColor();
742 : }
743 :
744 0 : if (!opt.ignoreDisplayedDefaultColor && normalColor == configuration()->defaultColor()) {
745 0 : return maybeOverrideColor(configuration()->displayedDefaultColor(opt.invertColors));
746 : }
747 :
748 0 : if (opt.invertColors) {
749 0 : return maybeOverrideColor(normalColor.inverted());
750 : }
751 0 : return maybeOverrideColor(normalColor);
752 : }
753 :
754 17705 : PointF EngravingItem::systemPos() const
755 : {
756 : // Returns position in system coordinates. Only applicable to items
757 : // that have a System ancestor.
758 :
759 17705 : IF_ASSERT_FAILED(findAncestor(ElementType::SYSTEM)) {
760 0 : return PointF();
761 : }
762 :
763 17705 : PointF result = pos();
764 17705 : EngravingItem* ancestor = layoutParent();
765 50525 : while (ancestor && !ancestor->isSystem()) {
766 32820 : result += ancestor->pos();
767 32820 : ancestor = ancestor->layoutParent();
768 : }
769 :
770 17705 : return result;
771 : }
772 :
773 : //---------------------------------------------------------
774 : // pagePos
775 : // return position in canvas coordinates
776 : //---------------------------------------------------------
777 :
778 649949 : PointF EngravingItem::pagePos() const
779 : {
780 649949 : PointF p(pos());
781 649949 : EngravingItem* parent = layoutParent();
782 649949 : if (parent == nullptr) {
783 4699 : return p;
784 : }
785 :
786 645250 : staff_idx_t idx = effectiveStaffIdx();
787 :
788 645250 : if (idx == muse::nidx) {
789 12615 : idx = vStaffIdx();
790 : }
791 :
792 271608 : auto measureStaffY = [this](const Measure* measure, staff_idx_t idx) -> double {
793 271608 : if (!measure) {
794 0 : return 0.0;
795 : }
796 271608 : if (measure->mstaves().size() <= idx) {
797 0 : LOGD("staffIdx out of bounds: %s", typeName());
798 0 : return 0.0;
799 : }
800 271608 : return measure->staffLines(idx)->y();
801 645250 : };
802 :
803 507839 : auto systemStaffY = [this](const System* system, staff_idx_t idx) -> double {
804 507839 : if (!system) {
805 366 : return 0.0;
806 : }
807 507473 : if (system->staves().size() <= idx) {
808 0 : LOGD("staffIdx out of bounds: %s", typeName());
809 0 : return 0.0;
810 : }
811 507473 : return system->staffYpage(idx);
812 645250 : };
813 :
814 645250 : if (m_flags & ElementFlag::ON_STAFF) {
815 : // Segments, measures and systems all contain multiple staves
816 : // Find the position of the stave that this item is on
817 512172 : if (parent->isSegment()) {
818 266448 : Segment* segment = toSegment(parent);
819 266448 : Measure* measure = segment ? segment->measure() : nullptr;
820 266448 : System* system = measure ? measure->system() : nullptr;
821 266448 : p.ry() += measureStaffY(measure, idx) + systemStaffY(system, idx);
822 245724 : } else if (parent->isMeasure()) {
823 5160 : Measure* measure = toMeasure(parent);
824 5160 : System* system = measure->system();
825 5160 : p.ry() += measureStaffY(measure, idx) + systemStaffY(system, idx);
826 240564 : } else if (parent->isSystem()) {
827 236231 : System* system = toSystem(parent);
828 236231 : p.ry() += systemStaffY(system, idx);
829 : } else {
830 4333 : return p + parent->pagePos();
831 : }
832 507839 : p.rx() = pageX();
833 : } else {
834 133078 : if (parent->layoutParent()) {
835 123558 : p += parent->pagePos();
836 : }
837 : }
838 640917 : return p;
839 : }
840 :
841 : //---------------------------------------------------------
842 : // canvasPos
843 : //---------------------------------------------------------
844 :
845 76138 : PointF EngravingItem::canvasPos() const
846 : {
847 76138 : PointF p(pos());
848 76138 : EngravingItem* parent = layoutParent();
849 76138 : if (parent == nullptr) {
850 1212 : return p;
851 : }
852 :
853 74926 : staff_idx_t idx = effectiveStaffIdx();
854 :
855 74926 : if (idx == muse::nidx) {
856 1633 : idx = vStaffIdx();
857 : }
858 :
859 38623 : auto measureStaffY = [this](const Measure* measure, staff_idx_t idx) -> double {
860 38623 : if (!measure) {
861 0 : return 0.0;
862 : }
863 38623 : if (measure->mstaves().size() <= idx) {
864 1174 : LOGD("staffIdx out of bounds: %s", typeName());
865 1174 : return 0.0;
866 : }
867 37449 : return measure->staffLines(idx)->y();
868 74926 : };
869 :
870 42373 : auto systemStaffY = [this](const System* system, staff_idx_t idx) -> double {
871 42373 : if (!system) {
872 1118 : return 0.0;
873 : }
874 41255 : if (system->staves().size() <= idx) {
875 74 : LOGD("staffIdx out of bounds: %s", typeName());
876 74 : return 0.0;
877 : }
878 41181 : return system->staffYpage(idx);
879 74926 : };
880 :
881 38623 : auto pageY = [](const Page* page) -> double {
882 38623 : if (!page) {
883 15660 : return 0.0;
884 : }
885 22963 : return page->y();
886 : };
887 :
888 74926 : if (m_flags & ElementFlag::ON_STAFF) {
889 : // Segments, measures and systems all contain multiple staves
890 : // Find the position of the stave that this item is on
891 46719 : if (parent->isSegment()) {
892 38399 : Segment* segment = toSegment(parent);
893 38399 : Measure* measure = segment ? segment->measure() : nullptr;
894 38399 : System* system = measure ? measure->system() : nullptr;
895 38399 : Page* page = system ? system->page() : nullptr;
896 38399 : p.ry() += measureStaffY(measure, idx) + systemStaffY(system, idx) + pageY(page);
897 8320 : } else if (parent->isMeasure()) {
898 224 : Measure* measure = toMeasure(parent)->coveringMMRestOrThis();
899 224 : System* system = measure->system();
900 224 : Page* page = system->page();
901 224 : p.ry() += measureStaffY(measure, idx) + systemStaffY(system, idx) + pageY(page);
902 8096 : } else if (parent->isSystem()) {
903 3750 : System* system = toSystem(parent);
904 3750 : p.ry() += systemStaffY(system, idx);
905 : } else {
906 4346 : return p + parent->canvasPos();
907 : }
908 42373 : p.rx() = canvasX();
909 : } else {
910 28207 : p += parent->canvasPos();
911 : }
912 70580 : return p;
913 : }
914 :
915 : //---------------------------------------------------------
916 : // pageX
917 : //---------------------------------------------------------
918 :
919 546681 : double EngravingItem::pageX() const
920 : {
921 546681 : double xp = x();
922 1228924 : for (EngravingItem* e = layoutParent(); e && e->layoutParent(); e = e->layoutParent()) {
923 682243 : xp += e->x();
924 : }
925 546681 : return xp;
926 : }
927 :
928 : //---------------------------------------------------------
929 : // canvasX
930 : //---------------------------------------------------------
931 :
932 42580 : double EngravingItem::canvasX() const
933 : {
934 42580 : double xp = x();
935 187435 : for (EngravingItem* e = layoutParent(); e; e = e->layoutParent()) {
936 144855 : xp += e->x();
937 : }
938 42580 : return xp;
939 : }
940 :
941 : //---------------------------------------------------------
942 : // contains
943 : // Return true if p is inside the shape of the object.
944 : // Note: p is in page coordinates
945 : //---------------------------------------------------------
946 :
947 0 : bool EngravingItem::contains(const PointF& p) const
948 : {
949 0 : return shape().contains(p - pagePos());
950 : }
951 :
952 : //---------------------------------------------------------
953 : // intersects
954 : // Return true if \a rr intersects bounding box of object.
955 : // Note: \a rr is in page coordinates
956 : //---------------------------------------------------------
957 :
958 0 : bool EngravingItem::intersects(const RectF& rr) const
959 : {
960 0 : return shape().intersects(rr.translated(-pagePos()));
961 : }
962 :
963 0 : bool EngravingItem::hitShapeContains(const PointF& p) const
964 : {
965 0 : return hitShape().contains(p - pagePos());
966 : }
967 :
968 0 : bool EngravingItem::hitShapeIntersects(const RectF& rr) const
969 : {
970 0 : return hitShape().intersects(rr.translated(-pagePos()));
971 : }
972 :
973 : //---------------------------------------------------------
974 : // remove
975 : /// Remove \a el from the list. Return true on success.
976 : //---------------------------------------------------------
977 :
978 85 : bool ElementList::remove(EngravingItem* el)
979 : {
980 85 : auto i = find(begin(), end(), el);
981 85 : if (i == end()) {
982 0 : return false;
983 : }
984 85 : erase(i);
985 85 : return true;
986 : }
987 :
988 : //---------------------------------------------------------
989 : // replace
990 : //---------------------------------------------------------
991 :
992 0 : void ElementList::replace(EngravingItem* o, EngravingItem* n)
993 : {
994 0 : auto i = find(begin(), end(), o);
995 0 : if (i == end()) {
996 0 : LOGD("ElementList::replace: element not found");
997 0 : return;
998 : }
999 0 : *i = n;
1000 : }
1001 :
1002 : //---------------------------------------------------------
1003 : // dump
1004 : //---------------------------------------------------------
1005 :
1006 0 : void EngravingItem::dump() const
1007 : {
1008 0 : const LayoutData* ldata = this->ldata();
1009 0 : LOGD("---EngravingItem: %s, pos(%4.2f,%4.2f)"
1010 : "\n bbox(%g,%g,%g,%g)"
1011 : "\n abox(%g,%g,%g,%g)"
1012 : "\n parent: %p",
1013 0 : typeName(), ldata->pos().x(), ldata->pos().y(),
1014 0 : ldata->bbox().x(), ldata->bbox().y(), ldata->bbox().width(), ldata->bbox().height(),
1015 0 : pageBoundingRect().x(), pageBoundingRect().y(), pageBoundingRect().width(), pageBoundingRect().height(),
1016 : ownershipParent());
1017 0 : }
1018 :
1019 : //---------------------------------------------------------
1020 : // mimeData
1021 : //---------------------------------------------------------
1022 :
1023 16 : muse::ByteArray EngravingItem::mimeData(const PointF& dragOffset) const
1024 : {
1025 16 : auto buffer = Buffer::opened(IODevice::WriteOnly);
1026 16 : XmlWriter xml(&buffer);
1027 :
1028 16 : xml.startElement("EngravingItem");
1029 16 : if (isNote()) {
1030 15 : xml.tagFraction("duration", toNote(this)->chord()->ticks());
1031 : }
1032 16 : if (!dragOffset.isNull()) {
1033 0 : xml.tagPoint("dragOffset", dragOffset);
1034 : }
1035 :
1036 16 : rw::RWRegister::writer()->writeItem(this, xml);
1037 :
1038 16 : xml.endElement();
1039 16 : xml.flush();
1040 16 : buffer.close();
1041 32 : return buffer.data();
1042 16 : }
1043 :
1044 : //---------------------------------------------------------
1045 : // readType
1046 : // return new position of QDomElement in e
1047 : //---------------------------------------------------------
1048 :
1049 16 : ElementType EngravingItem::readType(XmlReader& e, PointF* dragOffset, Fraction* duration)
1050 : {
1051 16 : while (e.readNextStartElement()) {
1052 16 : if (e.name() == "EngravingItem") {
1053 31 : while (e.readNextStartElement()) {
1054 31 : const AsciiStringView tag = e.name();
1055 31 : if (tag == "dragOffset") {
1056 0 : *dragOffset = e.readPoint();
1057 31 : } else if (tag == "duration") {
1058 15 : *duration = e.readFraction();
1059 : } else {
1060 16 : ElementType type = TConv::fromXml(tag, ElementType::INVALID);
1061 16 : if (type == ElementType::INVALID) {
1062 0 : break;
1063 : }
1064 16 : return type;
1065 : }
1066 : }
1067 : } else {
1068 0 : e.unknown();
1069 : }
1070 : }
1071 0 : return ElementType::INVALID;
1072 : }
1073 :
1074 : //---------------------------------------------------------
1075 : // readMimeData
1076 : //---------------------------------------------------------
1077 :
1078 16 : EngravingItem* EngravingItem::readMimeData(Score* score, const muse::ByteArray& data, PointF* dragOffset, Fraction* duration)
1079 : {
1080 16 : XmlReader e(data);
1081 :
1082 16 : const ElementType type = EngravingItem::readType(e, dragOffset, duration);
1083 16 : if (type == ElementType::INVALID) {
1084 0 : LOGD("cannot read type");
1085 0 : return nullptr;
1086 : }
1087 :
1088 16 : EngravingItem* el = Factory::createItem(type, score->dummy(), false);
1089 16 : if (el) {
1090 16 : rw::RWRegister::reader()->readItem(el, e);
1091 : }
1092 :
1093 16 : return el;
1094 16 : }
1095 :
1096 : //---------------------------------------------------------
1097 : // add
1098 : //---------------------------------------------------------
1099 :
1100 40 : void EngravingItem::add(EngravingItem* e)
1101 : {
1102 40 : switch (e->type()) {
1103 40 : case ElementType::PARENTHESIS: {
1104 40 : Parenthesis* p = toParenthesis(e);
1105 40 : p->setVisible(visible());
1106 40 : if (p->direction() == DirectionH::LEFT) {
1107 20 : m_leftParenthesis = p;
1108 20 : } else if (p->direction() == DirectionH::RIGHT) {
1109 20 : m_rightParenthesis = p;
1110 : }
1111 40 : break;
1112 : }
1113 0 : default:
1114 0 : LOGD("EngravingItem: cannot add %s to %s", e->typeName(), typeName());
1115 : }
1116 40 : }
1117 :
1118 : //---------------------------------------------------------
1119 : // remove
1120 : //---------------------------------------------------------
1121 :
1122 0 : void EngravingItem::remove(EngravingItem* e)
1123 : {
1124 0 : switch (e->type()) {
1125 0 : case ElementType::PARENTHESIS: {
1126 0 : if (e == m_leftParenthesis) {
1127 0 : m_leftParenthesis = nullptr;
1128 : }
1129 0 : if (e == m_rightParenthesis) {
1130 0 : m_rightParenthesis = nullptr;
1131 : }
1132 0 : break;
1133 : }
1134 0 : default:
1135 0 : ASSERT_X(String(u"EngravingItem: cannot remove %1 from %2").arg(String::fromAscii(e->typeName()), String::fromAscii(typeName())));
1136 : }
1137 0 : }
1138 :
1139 : //---------------------------------------------------------
1140 : // elementLessThan
1141 : //---------------------------------------------------------
1142 :
1143 2366 : bool elementLessThan(const EngravingItem* const e1, const EngravingItem* const e2)
1144 : {
1145 2366 : if (e1->z() == e2->z()) {
1146 1133 : if (e1->selected() && !e2->selected()) {
1147 0 : return false;
1148 : }
1149 1133 : if (!e1->selected() && e2->selected()) {
1150 0 : return true;
1151 : }
1152 1133 : if (e1->visible() && !e2->visible()) {
1153 0 : return false;
1154 : }
1155 1133 : if (!e1->visible() && e2->visible()) {
1156 0 : return true;
1157 : }
1158 :
1159 1133 : return e1->track() < e2->track();
1160 : }
1161 :
1162 1233 : return e1->z() < e2->z();
1163 : }
1164 :
1165 : //---------------------------------------------------------
1166 : // autoplace
1167 : //---------------------------------------------------------
1168 :
1169 155165 : bool EngravingItem::autoplace() const
1170 : {
1171 155165 : if (!score() || !style().styleB(Sid::autoplaceEnabled)) {
1172 0 : return false;
1173 : }
1174 155165 : return !flag(ElementFlag::NO_AUTOPLACE);
1175 : }
1176 :
1177 : //---------------------------------------------------------
1178 : // getProperty
1179 : //---------------------------------------------------------
1180 :
1181 802791 : PropertyValue EngravingItem::getProperty(Pid propertyId) const
1182 : {
1183 802791 : switch (propertyId) {
1184 47 : case Pid::TICK:
1185 47 : return tick();
1186 3 : case Pid::TRACK:
1187 3 : return track();
1188 0 : case Pid::VOICE:
1189 0 : return voice();
1190 2847 : case Pid::GENERATED:
1191 2847 : return generated();
1192 90018 : case Pid::COLOR:
1193 90018 : return PropertyValue::fromValue(color());
1194 79831 : case Pid::VISIBLE:
1195 79831 : return visible();
1196 0 : case Pid::SELECTED:
1197 0 : return selected();
1198 18340 : case Pid::OFFSET:
1199 18340 : return PropertyValue::fromValue(m_offset);
1200 1729 : case Pid::MIN_DISTANCE:
1201 1729 : return m_minDistance;
1202 75730 : case Pid::PLACEMENT:
1203 75730 : return placement();
1204 95156 : case Pid::AUTOPLACE:
1205 95156 : return autoplace();
1206 94888 : case Pid::Z:
1207 94888 : return z();
1208 8 : case Pid::SYSTEM_FLAG:
1209 8 : return systemFlag();
1210 1237 : case Pid::SIZE_SPATIUM_DEPENDENT:
1211 1237 : return sizeIsSpatiumDependent();
1212 104252 : case Pid::POSITION_LINKED_TO_MASTER:
1213 104252 : return _isPositionLinkedToMaster;
1214 104297 : case Pid::APPEARANCE_LINKED_TO_MASTER:
1215 104297 : return _isAppearanceLinkedToMaster;
1216 77053 : case Pid::EXCLUDE_FROM_OTHER_PARTS:
1217 77053 : return _excludeFromOtherParts;
1218 817 : case Pid::EXCLUDE_VERTICAL_ALIGN:
1219 817 : return m_excludeVerticalAlign;
1220 53411 : case Pid::HAS_PARENTHESES:
1221 53411 : return parenthesesMode();
1222 3127 : default:
1223 3127 : if (ownershipParent()) {
1224 3117 : return ownershipParent()->getProperty(propertyId);
1225 : }
1226 :
1227 10 : return PropertyValue();
1228 : }
1229 : }
1230 :
1231 : //---------------------------------------------------------
1232 : // setProperty
1233 : //---------------------------------------------------------
1234 :
1235 137710 : bool EngravingItem::setProperty(Pid propertyId, const PropertyValue& v)
1236 : {
1237 137710 : switch (propertyId) {
1238 0 : case Pid::TRACK:
1239 0 : setTrack(v.value<track_idx_t>());
1240 0 : break;
1241 0 : case Pid::VOICE:
1242 0 : setVoice(v.value<voice_idx_t>());
1243 0 : break;
1244 75 : case Pid::GENERATED:
1245 75 : setGenerated(v.toBool());
1246 75 : break;
1247 42148 : case Pid::COLOR:
1248 42148 : setColor(v.value<Color>());
1249 42148 : break;
1250 183 : case Pid::VISIBLE:
1251 183 : setVisible(v.toBool());
1252 183 : break;
1253 0 : case Pid::SELECTED:
1254 0 : setSelected(v.toBool());
1255 0 : break;
1256 1497 : case Pid::OFFSET:
1257 1497 : m_offset = v.value<PointF>();
1258 1497 : break;
1259 11116 : case Pid::MIN_DISTANCE:
1260 11116 : setMinDistance(v.value<Spatium>());
1261 11116 : break;
1262 7611 : case Pid::PLACEMENT:
1263 7611 : setPlacement(v.value<PlacementV>());
1264 7611 : break;
1265 21 : case Pid::AUTOPLACE:
1266 21 : setAutoplace(v.toBool());
1267 21 : break;
1268 0 : case Pid::Z:
1269 0 : setZ(v.toInt());
1270 0 : break;
1271 16451 : case Pid::SYSTEM_FLAG:
1272 16451 : setSystemFlag(v.toBool());
1273 16451 : break;
1274 44112 : case Pid::SIZE_SPATIUM_DEPENDENT:
1275 44112 : setSizeIsSpatiumDependent(v.toBool());
1276 44112 : break;
1277 7 : case Pid::POSITION_LINKED_TO_MASTER:
1278 7 : if (_isPositionLinkedToMaster == v.toBool()) {
1279 0 : break;
1280 : }
1281 7 : if (!_isPositionLinkedToMaster) {
1282 1 : relinkPropertiesToMaster(PropertyGroup::POSITION);
1283 : }
1284 7 : setPositionLinkedToMaster(v.toBool());
1285 7 : break;
1286 39 : case Pid::APPEARANCE_LINKED_TO_MASTER:
1287 39 : if (_isAppearanceLinkedToMaster == v.toBool()) {
1288 16 : break;
1289 : }
1290 23 : if (!_isAppearanceLinkedToMaster) {
1291 1 : relinkPropertiesToMaster(PropertyGroup::APPEARANCE);
1292 : }
1293 23 : setAppearanceLinkedToMaster(v.toBool());
1294 23 : break;
1295 20 : case Pid::EXCLUDE_FROM_OTHER_PARTS:
1296 20 : setExcludeFromOtherParts(v.toBool());
1297 20 : break;
1298 0 : case Pid::EXCLUDE_VERTICAL_ALIGN:
1299 0 : setExcludeVerticalAlign(v.toBool());
1300 0 : break;
1301 1 : case Pid::HAS_PARENTHESES:
1302 1 : setParenthesesMode(v.value<ParenthesesMode>());
1303 1 : break;
1304 14429 : default:
1305 14429 : if (ownershipParent()) {
1306 14218 : return ownershipParent()->setProperty(propertyId, v);
1307 : }
1308 :
1309 : LOG_PROP() << typeName() << " unknown <" << propertyName(propertyId) << ">(" << int(propertyId) << "), data: " << v.value<String>();
1310 211 : return false;
1311 : }
1312 123281 : triggerLayout();
1313 123281 : return true;
1314 : }
1315 :
1316 7 : void EngravingItem::manageExclusionFromParts(bool exclude)
1317 : {
1318 7 : if (exclude) {
1319 4 : const std::list<EngravingObject*> links = linkList();
1320 12 : for (EngravingObject* linkedObject : links) {
1321 8 : if (linkedObject->score() == score()) {
1322 4 : continue;
1323 : }
1324 4 : EngravingItem* linkedItem = toEngravingItem(linkedObject);
1325 4 : if (linkedItem->selected()) {
1326 0 : linkedItem->score()->deselect(linkedItem);
1327 : }
1328 4 : linkedItem->score()->undoRemoveElement(linkedItem, false);
1329 4 : linkedItem->undoUnlink();
1330 : }
1331 4 : } else {
1332 3 : score()->undoAddElement(this, /*addToLinkedStaves*/ true, /*ctrlModifier*/ false, this);
1333 : }
1334 7 : }
1335 :
1336 21623 : EngravingItem* EngravingItem::sharedItem() const
1337 : {
1338 21623 : return ldata()->m_sharedItem;
1339 : }
1340 :
1341 22758 : const std::vector<EngravingItem*>& EngravingItem::originItems() const
1342 : {
1343 22758 : return ldata()->m_originItems;
1344 : }
1345 :
1346 889 : void EngravingItem::connectSharedItem(EngravingItem* sharedItem, EngravingItem* originItem)
1347 : {
1348 889 : if (originItem->ldata()->m_sharedItem == sharedItem) {
1349 60 : return;
1350 : }
1351 :
1352 829 : IF_ASSERT_FAILED(sharedItem->type() == originItem->type()) {
1353 0 : return;
1354 : }
1355 :
1356 829 : IF_ASSERT_FAILED(!sharedItem->isChord() && !originItem->isChord()) {
1357 : // Notes are connected, not chords
1358 0 : return;
1359 : }
1360 :
1361 829 : IF_ASSERT_FAILED(sharedItem->ldata()->m_sharedItem == nullptr && originItem->ldata()->m_originItems.empty()) {
1362 0 : return;
1363 : }
1364 :
1365 829 : originItem->mutldata()->m_sharedItem = sharedItem;
1366 :
1367 829 : std::vector<EngravingItem*>& curOriginItems = sharedItem->mutldata()->m_originItems;
1368 829 : auto it = std::lower_bound(curOriginItems.begin(), curOriginItems.end(), originItem, [](EngravingItem* item1, EngravingItem* item2) {
1369 340 : return item1->track() < item2->track();
1370 : });
1371 :
1372 829 : curOriginItems.insert(it, originItem);
1373 : }
1374 :
1375 33 : void EngravingItem::disconnectSharedItem(EngravingItem* sharedItem, EngravingItem* originItem)
1376 : {
1377 33 : IF_ASSERT_FAILED(originItem->m_layoutData->m_sharedItem == sharedItem) {
1378 0 : return;
1379 : }
1380 :
1381 33 : originItem->m_layoutData->m_sharedItem = nullptr;
1382 :
1383 33 : DO_ASSERT(muse::remove(sharedItem->m_layoutData->m_originItems, originItem));
1384 : }
1385 :
1386 16712 : void EngravingItem::disconnectAllOriginItems(EngravingItem* sharedItem)
1387 : {
1388 17205 : for (EngravingItem* originItem : sharedItem->originItems()) {
1389 493 : originItem->m_layoutData->m_sharedItem = nullptr;
1390 : }
1391 :
1392 16712 : sharedItem->m_layoutData->m_originItems.clear();
1393 16712 : }
1394 :
1395 11936 : void EngravingItem::disconnectSharedTree(EngravingItem* item)
1396 : {
1397 15940 : auto disconnect = [](EngravingItem* item) {
1398 15940 : if (EngravingItem* sharedItem = item->sharedItem()) {
1399 33 : disconnectSharedItem(sharedItem, item);
1400 : }
1401 :
1402 15940 : disconnectAllOriginItems(item);
1403 15940 : };
1404 :
1405 11936 : if (item->isSpanner()) {
1406 1028 : disconnect(item);
1407 1954 : for (SpannerSegment* ss : toSpanner(item)->spannerSegments()) {
1408 926 : ss->scanElements(disconnect);
1409 : }
1410 1028 : return;
1411 : }
1412 :
1413 10908 : item->scanElements(disconnect);
1414 : }
1415 :
1416 4963 : bool EngravingItem::isBefore(const EngravingItem* item) const
1417 : {
1418 4963 : if (!item) {
1419 0 : return false;
1420 : }
1421 4963 : if (tick() != item->tick()) {
1422 3758 : return tick() < item->tick();
1423 : }
1424 :
1425 1205 : const Measure* thisMeasure = findMeasure();
1426 1205 : const Measure* otherMeasure = item->findMeasure();
1427 1205 : if (thisMeasure != otherMeasure) {
1428 225 : return thisMeasure->isBefore(otherMeasure);
1429 : }
1430 :
1431 980 : const EngravingItem* thisSeg = findAncestor(ElementType::SEGMENT);
1432 980 : const EngravingItem* otherSeg = item->findAncestor(ElementType::SEGMENT);
1433 980 : if (!thisSeg || !otherSeg || !thisSeg->isSegment() || !otherSeg->isSegment()) {
1434 0 : return false;
1435 : }
1436 :
1437 980 : return toSegment(thisSeg)->goesBefore(toSegment(otherSeg));
1438 : }
1439 :
1440 2651 : const Staff* EngravingItem::staffToCenterAgainst(bool above, const System* system) const
1441 : {
1442 2651 : const Part* thisPart = part();
1443 2651 : const Staff* thisStaff = staff();
1444 2651 : if (!thisPart || !thisStaff) {
1445 0 : return nullptr;
1446 : }
1447 :
1448 2651 : const Fraction itemTick = tick();
1449 2651 : const Instrument* thisPartInstrument = thisPart->instrument(itemTick);
1450 2651 : const bool thisInstrumentIsVocal = thisPartInstrument && thisPartInstrument->isVocalInstrument();
1451 2651 : const bool participatesInStaffCentering = thisPart->nstaves() > 1 || thisInstrumentIsVocal;
1452 2651 : if (!participatesInStaffCentering) {
1453 1249 : return nullptr;
1454 : }
1455 :
1456 1402 : if (!system) {
1457 140 : if (isSpanner()) {
1458 6 : const Segment* startSeg = toSpanner(this)->startSegment();
1459 6 : system = startSeg ? startSeg->measure()->system() : nullptr;
1460 : } else {
1461 134 : system = toSystem(findAncestor(ElementType::SYSTEM));
1462 : }
1463 : }
1464 :
1465 1402 : if (!system) {
1466 0 : return nullptr;
1467 : }
1468 :
1469 1402 : const staff_idx_t thisIdx = thisStaff->idx();
1470 1402 : const staff_idx_t otherIdx = above ? system->prevVisibleStaff(thisIdx) : system->nextVisibleStaff(thisIdx);
1471 1402 : if (otherIdx == muse::nidx) {
1472 515 : return nullptr;
1473 : }
1474 :
1475 887 : const Staff* otherStaff = score()->staff(otherIdx);
1476 887 : if (!otherStaff) {
1477 0 : return nullptr;
1478 : }
1479 :
1480 887 : const Part* otherPart = otherStaff->part();
1481 887 : if (otherPart == thisPart) {
1482 477 : return otherStaff;
1483 : }
1484 :
1485 410 : const Instrument* otherPartInstrument = otherPart->instrument(itemTick);
1486 410 : const bool otherInstrumentIsVocal = otherPartInstrument && otherPartInstrument->isVocalInstrument();
1487 :
1488 : // If the staves are not of the same part, only allow centering if they are both vocal parts:
1489 410 : return thisInstrumentIsVocal && otherInstrumentIsVocal ? otherStaff : nullptr;
1490 : }
1491 :
1492 322 : bool EngravingItem::appliesToAllVoicesInInstrument() const
1493 : {
1494 644 : return hasVoiceAssignmentProperties()
1495 322 : && getProperty(Pid::VOICE_ASSIGNMENT).value<VoiceAssignment>() == VoiceAssignment::ALL_VOICE_IN_INSTRUMENT;
1496 : }
1497 :
1498 0 : void EngravingItem::setInitialTrackAndVoiceAssignment(track_idx_t track, bool curVoiceOnlyOverride)
1499 : {
1500 0 : IF_ASSERT_FAILED(track != muse::nidx) {
1501 0 : return;
1502 : }
1503 :
1504 0 : if (configuration()->dynamicsApplyToAllVoices() && !curVoiceOnlyOverride) {
1505 0 : setTrack(trackZeroVoice(track));
1506 0 : setProperty(Pid::VOICE_ASSIGNMENT, VoiceAssignment::ALL_VOICE_IN_INSTRUMENT);
1507 : } else {
1508 0 : setTrack(track);
1509 0 : setProperty(Pid::VOICE_ASSIGNMENT, VoiceAssignment::CURRENT_VOICE_ONLY);
1510 : }
1511 : }
1512 :
1513 5 : void EngravingItem::checkVoiceAssignmentCompatibleWithTrack()
1514 : {
1515 5 : voice_idx_t currentVoice = voice();
1516 5 : VoiceAssignment voiceAssignment = getProperty(Pid::VOICE_ASSIGNMENT).value<VoiceAssignment>();
1517 :
1518 5 : if (voiceAssignment != VoiceAssignment::CURRENT_VOICE_ONLY && currentVoice != 0) {
1519 0 : setProperty(Pid::TRACK, trackZeroVoice(track()));
1520 : }
1521 5 : }
1522 :
1523 84 : bool EngravingItem::elementAppliesToTrack(const track_idx_t refTrack) const
1524 : {
1525 84 : if (!hasVoiceAssignmentProperties()) {
1526 34 : return refTrack == track();
1527 : }
1528 50 : const VoiceAssignment voiceAssignment = getProperty(Pid::VOICE_ASSIGNMENT).value<VoiceAssignment>();
1529 :
1530 50 : return elementAppliesToTrack(track(), refTrack, voiceAssignment, part());
1531 : }
1532 :
1533 1717 : void EngravingItem::setPlacementBasedOnVoiceAssignment(DirectionV styledDirection)
1534 : {
1535 1717 : PlacementV oldPlacement = placement();
1536 1717 : PlacementV newPlacement = PlacementV::BELOW;
1537 :
1538 1717 : DirectionV internalDirectionProperty = getProperty(Pid::DIRECTION).value<DirectionV>();
1539 1717 : const bool directionIsAuto = internalDirectionProperty == DirectionV::AUTO && styledDirection == DirectionV::AUTO;
1540 1717 : const bool centerBetweenStaves = getProperty(Pid::CENTER_BETWEEN_STAVES).value<AutoOnOff>() == AutoOnOff::ON;
1541 1717 : const bool gapAbove = directionIsAuto && centerBetweenStaves && staffToCenterAgainst(true);
1542 1717 : const bool gapBelow = directionIsAuto && centerBetweenStaves && staffToCenterAgainst(false);
1543 :
1544 1717 : if (internalDirectionProperty != DirectionV::AUTO) {
1545 374 : newPlacement = internalDirectionProperty == DirectionV::UP ? PlacementV::ABOVE : PlacementV::BELOW;
1546 1343 : } else if (styledDirection != DirectionV::AUTO) {
1547 0 : newPlacement = styledDirection == DirectionV::UP ? PlacementV::ABOVE : PlacementV::BELOW;
1548 1343 : } else if (centerBetweenStaves && part()->nstaves() > 1 && (gapAbove || gapBelow)) {
1549 48 : bool isOnLastStaffOfInstrument = staffIdx() == part()->staves().back()->idx();
1550 48 : newPlacement = isOnLastStaffOfInstrument ? PlacementV::ABOVE : PlacementV::BELOW;
1551 : } else {
1552 1295 : VoiceAssignment voiceAssignment = getProperty(Pid::VOICE_ASSIGNMENT).value<VoiceAssignment>();
1553 1295 : if (voiceAssignment == VoiceAssignment::ALL_VOICE_IN_INSTRUMENT || voiceAssignment == VoiceAssignment::ALL_VOICE_IN_STAFF) {
1554 2536 : if (style().styleB(Sid::dynamicsHairpinsAboveForVocalStaves) && originPart()->instrument(tick())->isVocalInstrument()) {
1555 180 : newPlacement = PlacementV::ABOVE;
1556 : } else {
1557 1088 : newPlacement = PlacementV::BELOW;
1558 : }
1559 27 : } else if (voice() == 0) {
1560 : // Put above the staff only in case of multiple voices at this tick (similar to stem directions)
1561 13 : const Measure* measure = score()->tick2measure(tick());
1562 13 : Fraction startTick = Fraction(-1, 1);
1563 13 : Fraction length = Fraction(-1, 1);
1564 13 : if (isSpanner()) {
1565 2 : startTick = tick();
1566 2 : length = toSpanner(this)->ticks();
1567 11 : } else if (const Segment* segment = toSegment(findAncestor(ElementType::SEGMENT))) {
1568 11 : if (segment && segment->isTimeTickType() && segment->measure() != measure) {
1569 : // Edge case: this is a TimeTick segment at the end of previous measure. Happens only
1570 : // when dynamic is anchorToEndOfPrevious. In this case look for preceding segment.
1571 0 : segment = segment->prev1(SegmentType::Duration);
1572 0 : assert(segment);
1573 0 : measure = segment->measure();
1574 : }
1575 11 : startTick = segment->tick();
1576 11 : length = segment->ticks();
1577 0 : } else if (measure) {
1578 0 : startTick = measure->tick();
1579 0 : length = measure->ticks();
1580 : }
1581 13 : if (measure && measure->hasVoices(staffIdx(), startTick, length)) {
1582 7 : newPlacement = PlacementV::ABOVE;
1583 : } else {
1584 6 : newPlacement = PlacementV::BELOW;
1585 : }
1586 : } else {
1587 14 : newPlacement = voice() % 2 ? PlacementV::BELOW : PlacementV::ABOVE;
1588 : }
1589 : }
1590 :
1591 1717 : if (directionIsAuto && centerBetweenStaves) {
1592 : /* If the preferred side has no staff to center against but the other side has one, place
1593 : * the item on that side instead. If neither side has one, leave the placement alone: */
1594 56 : const bool preferAbove = newPlacement == PlacementV::ABOVE;
1595 56 : const bool preferredGap = preferAbove ? gapAbove : gapBelow;
1596 56 : const bool otherGap = preferAbove ? gapBelow : gapAbove;
1597 56 : if (!preferredGap && otherGap) {
1598 5 : newPlacement = preferAbove ? PlacementV::BELOW : PlacementV::ABOVE;
1599 : }
1600 : }
1601 :
1602 1717 : if (newPlacement != oldPlacement) {
1603 170 : setPlacement(newPlacement);
1604 : }
1605 1717 : }
1606 :
1607 2 : void EngravingItem::relinkPropertiesToMaster(PropertyGroup propGroup)
1608 : {
1609 2 : assert(!score()->isMaster());
1610 :
1611 2 : const std::list<EngravingObject*> linkedElements = linkListForPropertyPropagation();
1612 2 : EngravingObject* masterElement = nullptr;
1613 2 : for (EngravingObject* element : linkedElements) {
1614 2 : if (element->score()->isMaster()) {
1615 2 : masterElement = element;
1616 2 : break;
1617 : }
1618 : }
1619 :
1620 2 : if (!masterElement) {
1621 0 : return;
1622 : }
1623 :
1624 798 : for (int i = 0; i < static_cast<int>(Pid::END); ++i) {
1625 796 : const Pid propertyId = static_cast<Pid>(i);
1626 796 : if (propertyGroup(propertyId) != propGroup) {
1627 445 : continue;
1628 : }
1629 351 : const PropertyValue masterValue = masterElement->getProperty(propertyId);
1630 351 : const PropertyFlags masterFlags = masterElement->propertyFlags(propertyId);
1631 351 : if (getProperty(propertyId) != masterValue) {
1632 2 : setProperty(propertyId, masterValue);
1633 : }
1634 351 : if (propertyFlags(propertyId) != masterFlags) {
1635 1 : setPropertyFlags(propertyId, masterFlags);
1636 : }
1637 351 : }
1638 2 : }
1639 :
1640 0 : void EngravingItem::relinkPropertyToMaster(Pid propertyId)
1641 : {
1642 0 : assert(!score()->isMaster());
1643 :
1644 0 : const std::list<EngravingObject*> linkedElements = linkListForPropertyPropagation();
1645 0 : EngravingObject* masterElement = nullptr;
1646 0 : for (EngravingObject* element : linkedElements) {
1647 0 : if (element->score()->isMaster()) {
1648 0 : masterElement = element;
1649 0 : break;
1650 : }
1651 : }
1652 :
1653 0 : if (!masterElement) {
1654 0 : return;
1655 : }
1656 :
1657 0 : setProperty(propertyId, masterElement->getProperty(propertyId));
1658 0 : setPropertyFlags(propertyId, masterElement->propertyFlags(propertyId));
1659 0 : }
1660 :
1661 482 : PropertyPropagation EngravingItem::propertyPropagation(const EngravingItem* destinationItem, Pid propertyId) const
1662 : {
1663 482 : assert(destinationItem != this);
1664 :
1665 482 : if (propertyLink(propertyId)) {
1666 155 : return PropertyPropagation::PROPAGATE; // These properties are always linked, no matter what
1667 : }
1668 :
1669 327 : if (propertyGroup(propertyId) == PropertyGroup::NONE) {
1670 275 : return PropertyPropagation::NONE;
1671 : }
1672 :
1673 52 : const Score* sourceScore = score();
1674 52 : const Score* destinationScore = destinationItem->score();
1675 52 : const Staff* sourceStaff = staff();
1676 52 : const Staff* destinationStaff = destinationItem->staff();
1677 :
1678 52 : if (sourceScore == destinationScore) {
1679 0 : const bool diffStaff = sourceStaff != destinationStaff;
1680 0 : const bool visiblePositionOrColor = propertyId == Pid::VISIBLE || propertyId == Pid::COLOR
1681 0 : || propertyGroup(propertyId) == PropertyGroup::POSITION;
1682 0 : const bool hasParens = propertyId == Pid::HAS_PARENTHESES && isNote() && toNote(this)->ghost()
1683 0 : && !toNote(this)->hideGeneratedParens();
1684 0 : const bool linkSameScore = propertyLinkSameScore(propertyId);
1685 0 : if ((diffStaff && (visiblePositionOrColor || hasParens)) || !linkSameScore) {
1686 : // Allow visibility and position to stay independent
1687 0 : return PropertyPropagation::NONE;
1688 : }
1689 : // Maintain every other property synced
1690 0 : return PropertyPropagation::PROPAGATE;
1691 : }
1692 :
1693 52 : const bool isTextProperty = propertyGroup(propertyId) == PropertyGroup::TEXT;
1694 52 : if (isTextProperty) {
1695 2 : if (sourceScore->isMaster() && destinationItem->isPropertyLinkedToMaster(propertyId)) {
1696 : // From master score - check if destination part follows master
1697 2 : return PropertyPropagation::PROPAGATE;
1698 : }
1699 :
1700 0 : if (!sourceScore->isMaster() && isPropertyLinkedToMaster(propertyId)) {
1701 : // From part - check if source part follows master
1702 0 : return PropertyPropagation::PROPAGATE;
1703 : }
1704 : }
1705 :
1706 50 : if (!sourceScore->isMaster()) {
1707 : // Properties are only propagated when being edited from master. If this is being edited
1708 : // from a part score, we mark it as unlinked so it becomes independent in the part.
1709 34 : return PropertyPropagation::UNLINK;
1710 : }
1711 :
1712 16 : if (systemFlag() && !isTopSystemObject()) {
1713 : // Let only the top system object propagate
1714 0 : return PropertyPropagation::NONE;
1715 : }
1716 :
1717 16 : if (destinationItem->isPropertyLinkedToMaster(propertyId)) {
1718 16 : return PropertyPropagation::PROPAGATE;
1719 : }
1720 :
1721 0 : return PropertyPropagation::NONE;
1722 : }
1723 :
1724 : //---------------------------------------------------------
1725 : // undoChangeProperty
1726 : //---------------------------------------------------------
1727 :
1728 6676 : void EngravingItem::undoChangeProperty(Pid pid, const PropertyValue& val, PropertyFlags ps)
1729 : {
1730 6676 : if (pid == Pid::AUTOPLACE && (val.toBool() == true && !autoplace())) {
1731 : // Switching autoplacement on. Save user-defined
1732 : // placement properties to undo stack.
1733 3 : undoPushProperty(Pid::PLACEMENT);
1734 3 : undoPushProperty(Pid::OFFSET);
1735 : }
1736 6676 : EngravingObject::undoChangeProperty(pid, val, ps);
1737 6676 : }
1738 :
1739 : //---------------------------------------------------------
1740 : // propertyDefault
1741 : //---------------------------------------------------------
1742 :
1743 818878 : PropertyValue EngravingItem::propertyDefault(Pid pid) const
1744 : {
1745 818878 : switch (pid) {
1746 0 : case Pid::GENERATED:
1747 0 : return false;
1748 95093 : case Pid::VISIBLE:
1749 95093 : return true;
1750 88113 : case Pid::COLOR:
1751 88113 : return PropertyValue::fromValue(configuration()->defaultColor());
1752 70710 : case Pid::PLACEMENT: {
1753 70710 : PropertyValue v = EngravingObject::propertyDefault(pid);
1754 70710 : if (v.isValid()) { // if it's a styled property
1755 26 : return v;
1756 : }
1757 70684 : return PlacementV::BELOW;
1758 70710 : }
1759 0 : case Pid::SELECTED:
1760 0 : return false;
1761 19082 : case Pid::OFFSET:
1762 19082 : return PropertyValue::fromValue(PointF());
1763 1683 : case Pid::MIN_DISTANCE: {
1764 1683 : PropertyValue v = EngravingObject::propertyDefault(pid);
1765 1683 : if (v.isValid()) {
1766 372 : return v;
1767 : }
1768 1311 : return 0.0_sp;
1769 1683 : }
1770 95142 : case Pid::AUTOPLACE:
1771 95142 : return true;
1772 94886 : case Pid::Z:
1773 94886 : return int(type()) * 100;
1774 76991 : case Pid::POSITION_LINKED_TO_MASTER:
1775 76991 : return true;
1776 76991 : case Pid::APPEARANCE_LINKED_TO_MASTER:
1777 76991 : return true;
1778 76991 : case Pid::EXCLUDE_FROM_OTHER_PARTS:
1779 76991 : return false;
1780 815 : case Pid::EXCLUDE_VERTICAL_ALIGN:
1781 815 : return false;
1782 76991 : case Pid::HAS_PARENTHESES:
1783 153982 : return ParenthesesMode::NONE;
1784 45390 : default: {
1785 45390 : PropertyValue v = EngravingObject::propertyDefault(pid);
1786 :
1787 45390 : if (v.isValid()) {
1788 40973 : return v;
1789 : }
1790 :
1791 4417 : if (ownershipParent()) {
1792 4063 : return ownershipParent()->propertyDefault(pid);
1793 : }
1794 :
1795 354 : return PropertyValue();
1796 45390 : }
1797 : }
1798 : }
1799 :
1800 : //---------------------------------------------------------
1801 : // custom
1802 : // check if property is != default
1803 : //---------------------------------------------------------
1804 :
1805 0 : bool EngravingItem::custom(Pid id) const
1806 : {
1807 0 : return propertyDefault(id) != getProperty(id);
1808 : }
1809 :
1810 : //---------------------------------------------------------
1811 : // isPrintable
1812 : //---------------------------------------------------------
1813 :
1814 0 : bool EngravingItem::isPrintable() const
1815 : {
1816 0 : switch (type()) {
1817 0 : case ElementType::PAGE:
1818 : case ElementType::SYSTEM:
1819 : case ElementType::MEASURE:
1820 : case ElementType::SEGMENT:
1821 : case ElementType::VBOX:
1822 : case ElementType::HBOX:
1823 : case ElementType::TBOX:
1824 : case ElementType::FBOX:
1825 : case ElementType::SPACER:
1826 : case ElementType::SHADOW_NOTE:
1827 : case ElementType::LASSO:
1828 0 : return false;
1829 0 : default:
1830 0 : return true;
1831 : }
1832 : }
1833 :
1834 6 : bool EngravingItem::isPlayable() const
1835 : {
1836 6 : switch (type()) {
1837 3 : case ElementType::NOTE:
1838 : case ElementType::CHORD:
1839 3 : return true;
1840 0 : case ElementType::HARMONY:
1841 0 : return ownershipParent() && ownershipParent()->isSegment();
1842 3 : default:
1843 3 : return false;
1844 : }
1845 : }
1846 :
1847 80757 : bool EngravingItem::isSystemObjectBelowBottomStaff() const
1848 : {
1849 80757 : return systemFlag() && staff() && staff()->hasSystemObjectsBelowBottomStaff();
1850 : }
1851 :
1852 : //---------------------------------------------------------
1853 : // findAncestor
1854 : //---------------------------------------------------------
1855 :
1856 1300 : EngravingItem* EngravingItem::findAncestor(ElementType t)
1857 : {
1858 1300 : EngravingItem* e = this;
1859 4223 : while (e && e->type() != t) {
1860 2923 : e = e->layoutParent();
1861 : }
1862 1300 : return e;
1863 : }
1864 :
1865 198821 : const EngravingItem* EngravingItem::findAncestor(ElementType t) const
1866 : {
1867 198821 : const EngravingItem* e = this;
1868 619544 : while (e && e->type() != t) {
1869 420723 : e = e->layoutParent();
1870 : }
1871 198821 : return e;
1872 : }
1873 :
1874 : //---------------------------------------------------------
1875 : // findMeasure
1876 : //---------------------------------------------------------
1877 :
1878 550156 : Measure* EngravingItem::findMeasure()
1879 : {
1880 550156 : if (isMeasure()) {
1881 174547 : return toMeasure(this);
1882 375609 : } else if (EngravingItem* p = layoutParent()) {
1883 371660 : return p->findMeasure();
1884 : } else {
1885 3949 : return 0;
1886 : }
1887 : }
1888 :
1889 : //---------------------------------------------------------
1890 : // findMeasure
1891 : //---------------------------------------------------------
1892 :
1893 167331 : const Measure* EngravingItem::findMeasure() const
1894 : {
1895 167331 : EngravingItem* e = const_cast<EngravingItem*>(this);
1896 167331 : return e->findMeasure();
1897 : }
1898 :
1899 : //---------------------------------------------------------
1900 : // findMeasureBase
1901 : //---------------------------------------------------------
1902 :
1903 41347 : MeasureBase* EngravingItem::findMeasureBase()
1904 : {
1905 41347 : if (isMeasureBase()) {
1906 14082 : return toMeasureBase(this);
1907 27265 : } else if (EngravingItem* p = layoutParent()) {
1908 26624 : return p->findMeasureBase();
1909 : } else {
1910 641 : return 0;
1911 : }
1912 : }
1913 :
1914 : //---------------------------------------------------------
1915 : // findMeasureBase
1916 : //---------------------------------------------------------
1917 :
1918 14723 : const MeasureBase* EngravingItem::findMeasureBase() const
1919 : {
1920 14723 : EngravingItem* e = const_cast<EngravingItem*>(this);
1921 14723 : return e->findMeasureBase();
1922 : }
1923 :
1924 : //---------------------------------------------------------
1925 : // undoSetVisible
1926 : //---------------------------------------------------------
1927 :
1928 12 : void EngravingItem::undoSetVisible(bool v)
1929 : {
1930 12 : undoChangeProperty(Pid::VISIBLE, v);
1931 12 : }
1932 :
1933 0 : void EngravingItem::undoAddElement(EngravingItem* element, bool addToLinkedStaves)
1934 : {
1935 0 : score()->undoAddElement(element, addToLinkedStaves);
1936 0 : }
1937 :
1938 : //---------------------------------------------------------
1939 : // drawSymbol
1940 : //---------------------------------------------------------
1941 :
1942 231 : void EngravingItem::drawSymbol(SymId id, Painter* p, const PointF& o, double scale) const
1943 : {
1944 231 : score()->engravingFont()->draw(id, p, magS() * scale, o);
1945 231 : }
1946 :
1947 0 : void EngravingItem::drawSymbols(const SymIdList& symbols, Painter* p, const PointF& o, double scale) const
1948 : {
1949 0 : score()->engravingFont()->draw(symbols, p, magS() * scale, o);
1950 0 : }
1951 :
1952 20 : void EngravingItem::drawSymbols(const SymIdList& symbols, Painter* p, const PointF& o, const SizeF& scale) const
1953 : {
1954 20 : score()->engravingFont()->draw(symbols, p, SizeF(magS() * scale), o);
1955 20 : }
1956 :
1957 : //---------------------------------------------------------
1958 : // symHeight
1959 : //---------------------------------------------------------
1960 :
1961 8218 : double EngravingItem::symHeight(SymId id) const
1962 : {
1963 8218 : return score()->engravingFont()->height(id, magS());
1964 : }
1965 :
1966 : //---------------------------------------------------------
1967 : // symWidth
1968 : //---------------------------------------------------------
1969 :
1970 489259 : double EngravingItem::symWidth(SymId id) const
1971 : {
1972 489259 : return score()->engravingFont()->width(id, magS());
1973 : }
1974 :
1975 0 : double EngravingItem::symWidth(const SymIdList& symbols) const
1976 : {
1977 0 : return score()->engravingFont()->width(symbols, magS());
1978 : }
1979 :
1980 : //---------------------------------------------------------
1981 : // symAdvance
1982 : //---------------------------------------------------------
1983 :
1984 114 : double EngravingItem::symAdvance(SymId id) const
1985 : {
1986 114 : return score()->engravingFont()->advance(id, magS());
1987 : }
1988 :
1989 : //---------------------------------------------------------
1990 : // symBbox
1991 : //---------------------------------------------------------
1992 :
1993 406573 : RectF EngravingItem::symBbox(SymId id) const
1994 : {
1995 406573 : return score()->engravingFont()->bbox(id, magS());
1996 : }
1997 :
1998 2340 : RectF EngravingItem::symBbox(const SymIdList& symbols) const
1999 : {
2000 2340 : return score()->engravingFont()->bbox(symbols, magS());
2001 : }
2002 :
2003 42258 : Shape EngravingItem::symShapeWithCutouts(SymId id) const
2004 : {
2005 42258 : Shape shape = score()->engravingFont()->shapeWithCutouts(id, magS());
2006 174255 : for (ShapeElement& element : shape.elements()) {
2007 131997 : element.setItem(this);
2008 : }
2009 :
2010 42258 : return shape;
2011 : }
2012 :
2013 : //---------------------------------------------------------
2014 : // symSmuflAnchor
2015 : //---------------------------------------------------------
2016 :
2017 730694 : PointF EngravingItem::symSmuflAnchor(SymId symId, SmuflAnchorId anchorId) const
2018 : {
2019 730694 : return score()->engravingFont()->smuflAnchor(symId, anchorId, magS());
2020 : }
2021 :
2022 : //---------------------------------------------------------
2023 : // symIsValid
2024 : //---------------------------------------------------------
2025 :
2026 0 : bool EngravingItem::symIsValid(SymId id) const
2027 : {
2028 0 : return score()->engravingFont()->isValid(id);
2029 : }
2030 :
2031 : //---------------------------------------------------------
2032 : // concertPitch
2033 : //---------------------------------------------------------
2034 :
2035 2163256 : bool EngravingItem::concertPitch() const
2036 : {
2037 2163256 : return style().styleB(Sid::concertPitch);
2038 : }
2039 :
2040 : //---------------------------------------------------------
2041 : // nextElement
2042 : // selects the next score element
2043 : //---------------------------------------------------------
2044 :
2045 1 : EngravingItem* EngravingItem::nextElement()
2046 : {
2047 1 : EngravingItem* e = score()->selection().element();
2048 1 : if (!e && !score()->selection().elements().empty()) {
2049 0 : e = score()->selection().elements().front();
2050 : }
2051 1 : if (e) {
2052 1 : switch (e->type()) {
2053 0 : case ElementType::SEGMENT: {
2054 0 : Segment* s = toSegment(e);
2055 0 : return s->nextElement(staffIdx());
2056 : }
2057 0 : case ElementType::MEASURE: {
2058 0 : Measure* m = toMeasure(e);
2059 0 : return m->nextElementStaff(staffIdx());
2060 : }
2061 1 : case ElementType::CLEF:
2062 : case ElementType::KEYSIG:
2063 : case ElementType::TIMESIG:
2064 : case ElementType::BAR_LINE:
2065 1 : return nextSegmentElement();
2066 0 : default: {
2067 0 : return e->layoutParent()->nextElement();
2068 : }
2069 : }
2070 : }
2071 0 : return nullptr;
2072 : }
2073 :
2074 : //---------------------------------------------------------
2075 : // prevElement
2076 : // selects the previous score element
2077 : //---------------------------------------------------------
2078 :
2079 0 : EngravingItem* EngravingItem::prevElement()
2080 : {
2081 0 : EngravingItem* e = score()->selection().element();
2082 0 : if (!e && !score()->selection().elements().empty()) {
2083 0 : e = score()->selection().elements().back();
2084 : }
2085 0 : if (e) {
2086 0 : switch (e->type()) {
2087 0 : case ElementType::SEGMENT: {
2088 0 : Segment* s = toSegment(e);
2089 0 : return s->prevElement(staffIdx());
2090 : }
2091 0 : case ElementType::MEASURE: {
2092 0 : Measure* m = toMeasure(e);
2093 0 : return m->prevElementStaff(staffIdx());
2094 : }
2095 0 : case ElementType::CLEF:
2096 : case ElementType::KEYSIG:
2097 : case ElementType::TIMESIG:
2098 : case ElementType::BAR_LINE:
2099 0 : return prevSegmentElement();
2100 0 : default: {
2101 0 : return e->layoutParent()->prevElement();
2102 : }
2103 : }
2104 : }
2105 0 : return nullptr;
2106 : }
2107 :
2108 : //------------------------------------------------------------------------------------------
2109 : // nextSegmentElement
2110 : // This function is used in for the next-element command to navigate between main elements
2111 : // of segments. (Note, Rest, Clef, Time Signature, Key Signature, Barline, Ambitus, Breath, etc.)
2112 : // The default implementation is to look for the first such element. After it is found each
2113 : // element knows how to find the next one and overrides this method
2114 : //------------------------------------------------------------------------------------------
2115 :
2116 0 : EngravingItem* EngravingItem::nextSegmentElement()
2117 : {
2118 0 : EngravingItem* p = this;
2119 0 : while (p) {
2120 0 : switch (p->type()) {
2121 0 : case ElementType::NOTE:
2122 0 : if (toNote(p)->chord()->isGrace()) {
2123 0 : break;
2124 : }
2125 0 : return p;
2126 0 : case ElementType::REST:
2127 : case ElementType::MMREST:
2128 0 : return p;
2129 0 : case ElementType::CHORD: {
2130 0 : Chord* c = toChord(p);
2131 0 : if (!c->isGrace()) {
2132 0 : return c->notes().back();
2133 : }
2134 : }
2135 0 : break;
2136 0 : case ElementType::SEGMENT: {
2137 0 : Segment* s = toSegment(p);
2138 0 : return s->firstElementForNavigation(staffIdx());
2139 : }
2140 0 : case ElementType::MEASURE: {
2141 0 : Measure* m = toMeasure(p);
2142 0 : return m->nextElementStaff(staffIdx());
2143 : }
2144 0 : case ElementType::SYSTEM: {
2145 0 : System* sys = toSystem(p);
2146 0 : return sys->nextSegmentElement();
2147 : }
2148 0 : default:
2149 0 : break;
2150 : }
2151 0 : p = p->layoutParent();
2152 : }
2153 0 : return Navigation::firstElement(score());
2154 : }
2155 :
2156 : //------------------------------------------------------------------------------------------
2157 : // prevSegmentElement
2158 : // This function is used in for the prev-element command to navigate between main elements
2159 : // of segments. (Note, Rest, Clef, Time Signature, Key Signature, Barline, Ambitus, Breath, etc.)
2160 : // The default implementation is to look for the first such element. After it is found each
2161 : // element knows how to find the previous one and overrides this method
2162 : //------------------------------------------------------------------------------------------
2163 :
2164 0 : EngravingItem* EngravingItem::prevSegmentElement()
2165 : {
2166 0 : EngravingItem* p = this;
2167 0 : while (p) {
2168 0 : switch (p->type()) {
2169 0 : case ElementType::NOTE:
2170 0 : if (toNote(p)->chord()->isGrace()) {
2171 0 : break;
2172 : }
2173 0 : return p;
2174 0 : case ElementType::REST:
2175 : case ElementType::MMREST:
2176 0 : return p;
2177 0 : case ElementType::CHORD: {
2178 0 : Chord* c = toChord(p);
2179 0 : if (!c->isGrace()) {
2180 0 : return c->notes().front();
2181 : }
2182 : }
2183 0 : break;
2184 0 : case ElementType::SEGMENT: {
2185 0 : Segment* s = toSegment(p);
2186 0 : return s->lastElementForNavigation(staffIdx());
2187 : }
2188 0 : case ElementType::MEASURE: {
2189 0 : Measure* m = toMeasure(p);
2190 0 : return m->prevElementStaff(staffIdx());
2191 : }
2192 0 : case ElementType::SYSTEM: {
2193 0 : System* sys = toSystem(p);
2194 0 : return sys->prevSegmentElement();
2195 : }
2196 0 : default:
2197 0 : break;
2198 : }
2199 0 : p = p->layoutParent();
2200 : }
2201 0 : return Navigation::firstElement(score());
2202 : }
2203 :
2204 : #ifndef ENGRAVING_NO_ACCESSIBILITY
2205 1884 : AccessibleItemPtr EngravingItem::accessible() const
2206 : {
2207 1884 : return m_accessible;
2208 : }
2209 :
2210 : #endif
2211 :
2212 0 : String EngravingItem::accessibleInfo() const
2213 : {
2214 0 : return EngravingItem::translatedTypeUserName();
2215 : }
2216 :
2217 : //---------------------------------------------------------
2218 : // nextGrip
2219 : //---------------------------------------------------------
2220 :
2221 0 : bool EngravingItem::nextGrip(EditData& ed) const
2222 : {
2223 0 : int i = int(ed.curGrip) + 1;
2224 0 : if (i >= ed.grips) {
2225 0 : ed.curGrip = Grip(0);
2226 0 : return false;
2227 : }
2228 0 : ed.curGrip = Grip(i);
2229 0 : return true;
2230 : }
2231 :
2232 : //---------------------------------------------------------
2233 : // prevGrip
2234 : //---------------------------------------------------------
2235 :
2236 0 : bool EngravingItem::prevGrip(EditData& ed) const
2237 : {
2238 0 : int i = int(ed.curGrip) - 1;
2239 0 : if (i < 0) {
2240 0 : ed.curGrip = Grip(ed.grips - 1);
2241 0 : return false;
2242 : }
2243 0 : ed.curGrip = Grip(i);
2244 0 : return true;
2245 : }
2246 :
2247 : //---------------------------------------------------------
2248 : // isUserModified
2249 : // Check if this element was modified by user and
2250 : // therefore must be saved.
2251 : //---------------------------------------------------------
2252 :
2253 17898 : bool EngravingItem::isUserModified() const
2254 : {
2255 34594 : for (const StyledProperty& spp : *styledProperties()) {
2256 16696 : Pid pid = spp.pid;
2257 16696 : PropertyValue val = getProperty(pid);
2258 16696 : PropertyValue defaultValue = propertyDefault(pid);
2259 :
2260 16696 : if (propertyType(pid) == P_TYPE::ABSOLUTE) {
2261 0 : if (std::abs(val.value<double>() - defaultValue.value<double>()) > 0.0001) { // we don’t care spatium diffs that small
2262 0 : return true;
2263 : }
2264 : } else {
2265 16696 : if (getProperty(pid) != propertyDefault(pid)) {
2266 0 : return true;
2267 : }
2268 : }
2269 16696 : }
2270 107374 : for (Pid p : { Pid::VISIBLE, Pid::OFFSET, Pid::COLOR, Pid::Z, Pid::AUTOPLACE }) {
2271 89479 : if (getProperty(p) != propertyDefault(p)) {
2272 3 : return true;
2273 : }
2274 : }
2275 17895 : return false;
2276 : }
2277 :
2278 : //---------------------------------------------------------
2279 : // triggerLayout
2280 : //---------------------------------------------------------
2281 :
2282 1162595 : void EngravingItem::triggerLayout() const
2283 : {
2284 1162595 : if (isInScoreTree()) {
2285 918282 : score()->setLayout(tick(), staffIdx(), this);
2286 : }
2287 1162595 : }
2288 :
2289 : //----------------------------------------------------------------------
2290 : // triggerLayoutAll
2291 : //
2292 : // *************************** CAUTION *******************************
2293 : // This causes a layout of the entire score: extremely expensive and
2294 : // likely unnecessary! Consider overriding triggerLayout() instead.
2295 : //----------------------------------------------------------------------
2296 :
2297 3829 : void EngravingItem::triggerLayoutAll() const
2298 : {
2299 3829 : if (isInScoreTree()) {
2300 2051 : score()->setLayoutAll(staffIdx(), this);
2301 : }
2302 3829 : }
2303 :
2304 975 : void EngravingItem::triggerLayoutToEnd() const
2305 : {
2306 975 : if (isInScoreTree()) {
2307 975 : score()->setLayout(tick(), score()->endTick(), staffIdx(), staffIdx(), this);
2308 : }
2309 975 : }
2310 :
2311 : //---------------------------------------------------------
2312 : // addData
2313 : //---------------------------------------------------------
2314 :
2315 4 : void EditData::addData(std::shared_ptr<ElementEditData> ed)
2316 : {
2317 4 : m_data.push_back(ed);
2318 4 : }
2319 :
2320 : //---------------------------------------------------------
2321 : // startDrag
2322 : //---------------------------------------------------------
2323 :
2324 0 : void EngravingItem::startDrag(EditData& ed)
2325 : {
2326 0 : if (!isMovable()) {
2327 0 : return;
2328 : }
2329 0 : std::shared_ptr<ElementEditData> eed = std::make_shared<ElementEditData>();
2330 0 : eed->e = this;
2331 0 : eed->pushProperty(Pid::OFFSET);
2332 0 : eed->pushProperty(Pid::AUTOPLACE);
2333 0 : ed.addData(eed);
2334 0 : if (ed.modifiers & AltModifier) {
2335 0 : setAutoplace(false);
2336 : }
2337 0 : }
2338 :
2339 : //---------------------------------------------------------
2340 : // drag
2341 : /// Return update Rect relative to canvas.
2342 : //---------------------------------------------------------
2343 :
2344 0 : RectF EngravingItem::drag(EditData& ed)
2345 : {
2346 0 : if (!isMovable()) {
2347 0 : return RectF();
2348 : }
2349 :
2350 0 : const RectF r0(canvasBoundingRect());
2351 :
2352 0 : const PointF offset0 = ed.evtDelta + offset();
2353 0 : double x = offset0.x();
2354 0 : double y = offset0.y();
2355 :
2356 0 : double _spatium = spatium();
2357 0 : if (ed.hRaster) {
2358 0 : double hRaster = _spatium / MScore::hRaster();
2359 0 : int n = lrint(x / hRaster);
2360 0 : x = hRaster * n;
2361 : }
2362 0 : if (ed.vRaster) {
2363 0 : double vRaster = _spatium / MScore::vRaster();
2364 0 : int n = lrint(y / vRaster);
2365 0 : y = vRaster * n;
2366 : }
2367 :
2368 0 : setOffset(PointF(x, y));
2369 : // setGenerated(false);
2370 :
2371 0 : if (isTextBase()) { // TODO: check for other types
2372 : //
2373 : // restrict move to page boundaries
2374 : //
2375 0 : const RectF r(canvasBoundingRect());
2376 0 : Page* p = 0;
2377 0 : EngravingItem* e = this;
2378 0 : while (e) {
2379 0 : if (e->isPage()) {
2380 0 : p = toPage(e);
2381 0 : break;
2382 : }
2383 0 : e = e->layoutParent();
2384 : }
2385 0 : if (p) {
2386 0 : bool move = false;
2387 0 : RectF pr(p->canvasBoundingRect());
2388 0 : if (r.right() > pr.right()) {
2389 0 : x -= r.right() - pr.right();
2390 0 : move = true;
2391 0 : } else if (r.left() < pr.left()) {
2392 0 : x += pr.left() - r.left();
2393 0 : move = true;
2394 : }
2395 0 : if (r.bottom() > pr.bottom()) {
2396 0 : y -= r.bottom() - pr.bottom();
2397 0 : move = true;
2398 0 : } else if (r.top() < pr.top()) {
2399 0 : y += pr.top() - r.top();
2400 0 : move = true;
2401 : }
2402 0 : if (move) {
2403 0 : setOffset(PointF(x, y));
2404 : }
2405 : }
2406 : }
2407 0 : return canvasBoundingRect().united(r0);
2408 : }
2409 :
2410 : //---------------------------------------------------------
2411 : // endDrag
2412 : //---------------------------------------------------------
2413 :
2414 0 : void EngravingItem::endDrag(EditData& ed)
2415 : {
2416 0 : if (!isMovable()) {
2417 0 : return;
2418 : }
2419 0 : ElementEditDataPtr eed = ed.getData(this);
2420 0 : if (!eed) {
2421 0 : return;
2422 : }
2423 0 : for (const auto& [id, v, f] : eed->propertyData) {
2424 0 : setPropertyFlags(id, f); // reset initial property flags state
2425 0 : score()->undoPropertyChanged(this, id, v,
2426 0 : f == PropertyFlags::STYLED ? PropertyFlags::UNSTYLED : f);
2427 0 : setGenerated(false);
2428 : }
2429 0 : score()->hideAnchors();
2430 0 : }
2431 :
2432 : //---------------------------------------------------------
2433 : // genericDragAnchorLines
2434 : //---------------------------------------------------------
2435 :
2436 0 : std::vector<LineF> EngravingItem::genericDragAnchorLines() const
2437 : {
2438 0 : double xp = 0.0;
2439 0 : for (EngravingItem* e = layoutParent(); e; e = e->layoutParent()) {
2440 0 : xp += e->x();
2441 : }
2442 : double yp;
2443 0 : if (ownershipParent()->isSegment() || ownershipParent()->isMeasure()) {
2444 0 : Measure* meas = ownershipParent()->isSegment() ? toSegment(ownershipParent())->measure() : toMeasure(ownershipParent());
2445 0 : System* system = meas->system();
2446 0 : const staff_idx_t stIdx = effectiveStaffIdx();
2447 0 : if (stIdx == muse::nidx) {
2448 0 : return { LineF() };
2449 : }
2450 0 : yp = system ? system->staffCanvasYpage(stIdx) : 0.0;
2451 0 : if (placement() == PlacementV::BELOW) {
2452 0 : yp += system ? system->staff(stIdx)->bbox().height() : 0.0;
2453 : }
2454 : //adjust anchor Y positions to staffType offset
2455 0 : if (staff()) {
2456 0 : yp += staff()->staffTypeForElement(this)->yoffset().val() * spatium();
2457 : }
2458 : } else {
2459 0 : yp = layoutParent()->canvasPos().y();
2460 : }
2461 0 : PointF p1(xp, yp);
2462 0 : LineF anchorLine(p1, canvasPos());
2463 0 : return { anchorLine };
2464 : }
2465 :
2466 : //---------------------------------------------------------
2467 : // updateGrips
2468 : //---------------------------------------------------------
2469 :
2470 0 : void EngravingItem::updateGrips(EditData& ed) const
2471 : {
2472 0 : const auto positions(gripsPositions(ed));
2473 0 : const size_t ngrips = positions.size();
2474 0 : for (int i = 0; i < int(ngrips); ++i) {
2475 0 : ed.grip[i].translate(positions[i]);
2476 : }
2477 0 : }
2478 :
2479 : //---------------------------------------------------------
2480 : // startEdit
2481 : //---------------------------------------------------------
2482 :
2483 0 : void EngravingItem::startEdit(EditData& ed)
2484 : {
2485 0 : std::shared_ptr<ElementEditData> elementData = std::make_shared<ElementEditData>();
2486 0 : elementData->e = this;
2487 0 : ed.addData(elementData);
2488 0 : }
2489 :
2490 : //---------------------------------------------------------
2491 : // isEditAllowed
2492 : //---------------------------------------------------------
2493 :
2494 0 : bool EngravingItem::isEditAllowed(EditData& ed) const
2495 : {
2496 0 : return ed.key == Key_Home;
2497 : }
2498 :
2499 : //---------------------------------------------------------
2500 : // edit
2501 : // return true if event is accepted
2502 : //---------------------------------------------------------
2503 :
2504 0 : bool EngravingItem::edit(EditData& ed)
2505 : {
2506 0 : if (ed.key == Key_Home) {
2507 0 : setOffset(PointF());
2508 0 : return true;
2509 : }
2510 0 : return false;
2511 : }
2512 :
2513 : //---------------------------------------------------------
2514 : // startDragGrip
2515 : //---------------------------------------------------------
2516 :
2517 0 : void EngravingItem::startDragGrip(EditData& ed)
2518 : {
2519 0 : ElementEditDataPtr eed = ed.getData(this);
2520 0 : if (!eed) {
2521 0 : eed = std::make_shared<ElementEditData>();
2522 0 : eed->e = this;
2523 0 : ed.addData(eed);
2524 : }
2525 0 : eed->pushProperty(Pid::OFFSET);
2526 0 : eed->pushProperty(Pid::AUTOPLACE);
2527 0 : if (ed.modifiers & AltModifier) {
2528 0 : setAutoplace(false);
2529 : }
2530 0 : }
2531 :
2532 : //---------------------------------------------------------
2533 : // dragGrip
2534 : //---------------------------------------------------------
2535 :
2536 0 : void EngravingItem::dragGrip(EditData& ed)
2537 : {
2538 0 : IF_ASSERT_FAILED(ed.curGrip != Grip::NO_GRIP) {
2539 0 : return;
2540 : }
2541 :
2542 0 : score()->addRefresh(canvasBoundingRect());
2543 0 : setOffset(offset() + ed.delta);
2544 0 : score()->addRefresh(canvasBoundingRect());
2545 : }
2546 :
2547 : //---------------------------------------------------------
2548 : // endDragGrip
2549 : //---------------------------------------------------------
2550 :
2551 0 : void EngravingItem::endDragGrip(EditData& ed)
2552 : {
2553 0 : ElementEditDataPtr eed = ed.getData(this);
2554 0 : bool changed = false;
2555 0 : if (eed) {
2556 0 : for (const auto& [id, data, flags] : eed->propertyData) {
2557 0 : setPropertyFlags(id, flags); // reset initial property flags state
2558 0 : PropertyFlags f = flags;
2559 0 : if (f == PropertyFlags::STYLED) {
2560 0 : f = PropertyFlags::UNSTYLED;
2561 : }
2562 0 : if (score()->undoPropertyChanged(this, id, data, f)) {
2563 0 : changed = true;
2564 : }
2565 : }
2566 0 : eed->propertyData.clear();
2567 : }
2568 0 : if (changed) {
2569 0 : undoChangeProperty(Pid::GENERATED, false);
2570 : }
2571 0 : score()->hideAnchors();
2572 0 : }
2573 :
2574 : //---------------------------------------------------------
2575 : // endEdit
2576 : //---------------------------------------------------------
2577 :
2578 0 : void EngravingItem::endEdit(EditData&)
2579 : {
2580 0 : score()->hideAnchors();
2581 0 : }
2582 :
2583 : //---------------------------------------------------------
2584 : // styleP
2585 : //---------------------------------------------------------
2586 :
2587 6693 : double EngravingItem::styleP(Sid idx) const
2588 : {
2589 6693 : return style().styleAbsolute(idx);
2590 : }
2591 :
2592 257 : void EngravingItem::setParenthesesMode(const ParenthesesMode& v, bool addToLinked, bool generated)
2593 : {
2594 257 : setHasLeftParenthesis(v & ParenthesesMode::LEFT, addToLinked, generated);
2595 257 : setHasRightParenthesis(v & ParenthesesMode::RIGHT, addToLinked, generated);
2596 257 : }
2597 :
2598 53498 : ParenthesesMode EngravingItem::parenthesesMode() const
2599 : {
2600 53498 : ParenthesesMode p = ParenthesesMode::NONE;
2601 53498 : if (m_leftParenthesis) {
2602 0 : p |= ParenthesesMode::LEFT;
2603 : }
2604 53498 : if (m_rightParenthesis) {
2605 0 : p |= ParenthesesMode::RIGHT;
2606 : }
2607 :
2608 53498 : return p;
2609 : }
2610 :
2611 257 : void EngravingItem::setHasLeftParenthesis(bool v, bool addToLinked, bool generated)
2612 : {
2613 257 : const bool hasGeneratedParen = m_leftParenthesis && m_leftParenthesis->generated();
2614 257 : const bool hasUserParen = m_leftParenthesis && !m_leftParenthesis->generated();
2615 :
2616 257 : if (generated && v == hasGeneratedParen) {
2617 64 : return;
2618 : }
2619 :
2620 193 : if (!generated && v == hasUserParen) {
2621 174 : return;
2622 : }
2623 :
2624 19 : if (v) {
2625 19 : if (!m_leftParenthesis) {
2626 19 : Parenthesis* paren = Factory::createParenthesis(this);
2627 19 : paren->setTrack(track());
2628 19 : paren->setDirection(DirectionH::LEFT);
2629 19 : paren->setGenerated(generated);
2630 :
2631 19 : score()->undoAddElement(paren, addToLinked);
2632 : }
2633 : } else {
2634 0 : score()->undoRemoveElement(m_leftParenthesis, addToLinked);
2635 0 : assert(m_leftParenthesis == nullptr);
2636 : }
2637 : }
2638 :
2639 257 : void EngravingItem::setHasRightParenthesis(bool v, bool addToLinked, bool generated)
2640 : {
2641 257 : const bool hasGeneratedParen = m_rightParenthesis && m_rightParenthesis->generated();
2642 257 : const bool hasUserParen = m_rightParenthesis && !m_rightParenthesis->generated();
2643 :
2644 257 : if (generated && v == hasGeneratedParen) {
2645 64 : return;
2646 : }
2647 :
2648 193 : if (!generated && v == hasUserParen) {
2649 174 : return;
2650 : }
2651 :
2652 19 : if (v) {
2653 19 : if (!m_rightParenthesis) {
2654 19 : Parenthesis* paren = Factory::createParenthesis(this);
2655 19 : paren->setTrack(track());
2656 19 : paren->setDirection(DirectionH::RIGHT);
2657 19 : paren->setGenerated(generated);
2658 :
2659 19 : score()->undoAddElement(paren, addToLinked);
2660 : }
2661 : } else {
2662 0 : score()->undoRemoveElement(m_rightParenthesis, addToLinked);
2663 0 : assert(m_rightParenthesis == nullptr);
2664 : }
2665 : }
2666 :
2667 35 : EngravingItem::BarBeat EngravingItem::barbeat() const
2668 : {
2669 35 : EngravingItem::BarBeat barBeat = { 0, 0, 0.0F };
2670 35 : const EngravingItem* parent = this;
2671 82 : while (parent && !parent->isSegment() && !parent->isMeasure()) {
2672 47 : parent = parent->layoutParent();
2673 : }
2674 :
2675 35 : if (!parent) {
2676 0 : return barBeat;
2677 : }
2678 :
2679 35 : int bar = 0;
2680 35 : int displayedBar = 0;
2681 35 : int beat = 0;
2682 35 : int ticks = 0;
2683 35 : const Measure* measure = nullptr;
2684 :
2685 35 : const TimeSigMap* timeSigMap = score()->sigmap();
2686 35 : int ticksB = ticks_beat(timeSigMap->timesig(0).timesig().denominator());
2687 :
2688 35 : if (parent->isSegment()) {
2689 35 : const Segment* segment = toSegment(parent);
2690 35 : timeSigMap->tickValues(segment->tick().ticks(), &bar, &beat, &ticks);
2691 35 : ticksB = ticks_beat(timeSigMap->timesig(segment->tick().ticks()).timesig().denominator());
2692 35 : measure = segment->findMeasure();
2693 35 : if (measure) {
2694 35 : displayedBar = measure->measureNumber();
2695 : }
2696 0 : } else if (parent->isMeasure()) {
2697 0 : measure = toMeasure(parent);
2698 0 : bar = measure->measureNumber();
2699 0 : displayedBar = bar;
2700 0 : beat = -1;
2701 0 : ticks = 0;
2702 : }
2703 :
2704 35 : barBeat = { bar + 1, displayedBar + 1, beat + 1 + ticks / static_cast<float>(ticksB) };
2705 35 : return barBeat;
2706 : }
2707 :
2708 32 : EngravingItem* EngravingItem::findLinkedInScore(const Score* score) const
2709 : {
2710 32 : if (!score || !staff() || !links() || links()->empty()) {
2711 16 : return nullptr;
2712 : }
2713 :
2714 : // If this is a system element it may not be on the same stave, so just check if linked elements are in the score
2715 16 : if (systemFlag() && track() == 0) {
2716 0 : for (EngravingObject* linked : *links()) {
2717 0 : if (linked != this && linked->score() == score) {
2718 0 : return toEngravingItem(linked);
2719 : }
2720 : }
2721 0 : return nullptr;
2722 : }
2723 :
2724 16 : Staff* linkedStaffInScore = staff()->findLinkedInScore(score);
2725 :
2726 16 : if (!linkedStaffInScore) {
2727 12 : return nullptr;
2728 : }
2729 :
2730 4 : return findLinkedInStaff(linkedStaffInScore);
2731 : }
2732 :
2733 1609 : EngravingItem* EngravingItem::findLinkedInStaff(const Staff* staff) const
2734 : {
2735 1609 : if (!links() || links()->empty()) {
2736 484 : return nullptr;
2737 : }
2738 :
2739 2294 : for (EngravingObject* linked : *links()) {
2740 2293 : if (toEngravingItem(linked)->staff() == staff) {
2741 1124 : return toEngravingItem(linked);
2742 : }
2743 : }
2744 1 : return nullptr;
2745 : }
2746 :
2747 47234 : bool EngravingItem::selected() const
2748 : {
2749 47234 : return flag(ElementFlag::SELECTED);
2750 : }
2751 :
2752 321928 : void EngravingItem::setSelected(bool f)
2753 : {
2754 321928 : setFlag(ElementFlag::SELECTED, f);
2755 321928 : }
2756 :
2757 1252489 : void EngravingItem::setVisible(bool f)
2758 : {
2759 1252489 : setFlag(ElementFlag::INVISIBLE, !f);
2760 1252489 : if (m_leftParenthesis) {
2761 0 : m_leftParenthesis->setVisible(f);
2762 : }
2763 1252489 : if (m_rightParenthesis) {
2764 0 : m_rightParenthesis->setVisible(f);
2765 : }
2766 1252489 : }
2767 :
2768 : #ifndef ENGRAVING_NO_ACCESSIBILITY
2769 1883 : void EngravingItem::initAccessibleIfNeed()
2770 : {
2771 1883 : if (!contextConfiguration() || !contextConfiguration()->isAccessibleEnabled()) {
2772 1883 : return;
2773 : }
2774 :
2775 0 : if (!m_accessibleEnabled) {
2776 0 : return;
2777 : }
2778 :
2779 0 : doInitAccessible();
2780 : }
2781 :
2782 0 : void EngravingItem::doInitAccessible()
2783 : {
2784 0 : EngravingItemList parents;
2785 0 : EngravingItem* parent = accessibleParentItem();
2786 0 : while (parent) {
2787 0 : parents.push_back(parent);
2788 0 : parent = parent->accessibleParentItem();
2789 : }
2790 :
2791 0 : for (auto it = parents.rbegin(); it != parents.rend(); ++it) {
2792 0 : (*it)->setupAccessible();
2793 : }
2794 :
2795 0 : setupAccessible();
2796 0 : }
2797 :
2798 : #endif // ENGRAVING_NO_ACCESSIBILITY
2799 :
2800 0 : String EngravingItem::formatBarsAndBeats() const
2801 : {
2802 0 : String result;
2803 0 : EngravingItem::BarBeat barbeat = this->barbeat();
2804 :
2805 0 : if (barbeat.bar != 0) {
2806 0 : result = muse::mtrc("engraving", "Measure: %1").arg(barbeat.bar);
2807 :
2808 0 : if (barbeat.displayedBar != barbeat.bar) {
2809 0 : result += u"; " + muse::mtrc("engraving", "Displayed measure: %1").arg(barbeat.displayedBar);
2810 : }
2811 :
2812 0 : if (!muse::RealIsNull(barbeat.beat)) {
2813 0 : result += u"; " + muse::mtrc("engraving", "Beat: %1").arg(barbeat.beat);
2814 : }
2815 : }
2816 :
2817 0 : return result;
2818 0 : }
2819 :
2820 19 : bool EngravingItem::isPropertyLinkedToMaster(Pid id) const
2821 : {
2822 19 : if (propertyGroup(id) == PropertyGroup::POSITION) {
2823 0 : return isPositionLinkedToMaster();
2824 : }
2825 :
2826 19 : if (propertyGroup(id) == PropertyGroup::APPEARANCE) {
2827 19 : return isAppearanceLinkedToMaster();
2828 : }
2829 :
2830 0 : return true;
2831 : }
2832 :
2833 0 : bool EngravingItem::isUnlinkedFromMaster() const
2834 : {
2835 0 : return !(getProperty(Pid::POSITION_LINKED_TO_MASTER).toBool()
2836 0 : && getProperty(Pid::APPEARANCE_LINKED_TO_MASTER).toBool());
2837 : }
2838 :
2839 34 : void EngravingItem::unlinkPropertyFromMaster(Pid id)
2840 : {
2841 34 : if (propertyGroup(id) == PropertyGroup::POSITION) {
2842 1 : score()->undo(new ChangeProperty(this, Pid::POSITION_LINKED_TO_MASTER, false));
2843 33 : } else if (propertyGroup(id) == PropertyGroup::APPEARANCE) {
2844 33 : score()->undo(new ChangeProperty(this, Pid::APPEARANCE_LINKED_TO_MASTER, false));
2845 : }
2846 34 : }
2847 :
2848 182805 : EngravingItem::LayoutData* EngravingItem::createLayoutData() const
2849 : {
2850 182805 : return new EngravingItem::LayoutData();
2851 : }
2852 :
2853 32877825 : const EngravingItem::LayoutData* EngravingItem::ldata() const
2854 : {
2855 32877825 : return ldataInternal();
2856 : }
2857 :
2858 5344126 : EngravingItem::LayoutData* EngravingItem::mutldata()
2859 : {
2860 5344126 : return mutldataInternal();
2861 : }
2862 :
2863 32877825 : const EngravingItem::LayoutData* EngravingItem::ldataInternal() const
2864 : {
2865 32877825 : if (!m_layoutData) {
2866 315571 : m_layoutData = createLayoutData();
2867 315571 : m_layoutData->m_item = this;
2868 : }
2869 32877825 : return m_layoutData;
2870 : }
2871 :
2872 5344126 : EngravingItem::LayoutData* EngravingItem::mutldataInternal()
2873 : {
2874 5344126 : if (!m_layoutData) {
2875 311789 : m_layoutData = createLayoutData();
2876 311789 : m_layoutData->m_item = this;
2877 : }
2878 5344126 : return m_layoutData;
2879 : }
2880 :
2881 67 : bool EngravingItem::elementAppliesToTrack(const track_idx_t elementTrack, const track_idx_t refTrack,
2882 : const VoiceAssignment voiceAssignment, const Part* part)
2883 : {
2884 67 : if (voiceAssignment == VoiceAssignment::CURRENT_VOICE_ONLY && elementTrack == refTrack) {
2885 3 : return true;
2886 : }
2887 :
2888 64 : if (voiceAssignment == VoiceAssignment::ALL_VOICE_IN_STAFF && track2staff(elementTrack) == track2staff(refTrack)) {
2889 0 : return true;
2890 : }
2891 :
2892 64 : if (!part) {
2893 0 : return false;
2894 : }
2895 :
2896 64 : if (voiceAssignment == VoiceAssignment::ALL_VOICE_IN_INSTRUMENT) {
2897 49 : const TrackRange range = part->trackRange();
2898 49 : if (range.startTrack <= refTrack && range.endTrack - 1 >= refTrack) {
2899 10 : return true;
2900 : }
2901 : }
2902 54 : return false;
2903 : }
2904 :
2905 1697723 : void EngravingItem::LayoutData::setBbox(const RectF& r)
2906 : {
2907 1697723 : DO_ASSERT(!std::isnan(r.x()) && !std::isinf(r.x()));
2908 1697723 : DO_ASSERT(!std::isnan(r.y()) && !std::isinf(r.y()));
2909 1697723 : DO_ASSERT(!std::isnan(r.width()) && !std::isinf(r.width()));
2910 1697723 : DO_ASSERT(!std::isnan(r.height()) && !std::isinf(r.height()));
2911 :
2912 : //DO_ASSERT(!isShapeComposite());
2913 1697723 : m_shape.set_value(Shape(r, m_item, Shape::Type::Fixed));
2914 1697723 : }
2915 :
2916 137 : void EngravingItem::LayoutData::connectItemSnappedBefore(EngravingItem* itemBefore)
2917 : {
2918 137 : IF_ASSERT_FAILED(itemBefore && itemBefore != m_item && itemBefore != m_itemSnappedAfter
2919 : && itemBefore->ldata()->itemSnappedBefore() != m_item) {
2920 0 : return;
2921 : }
2922 137 : m_itemSnappedBefore = itemBefore;
2923 137 : itemBefore->mutldata()->m_itemSnappedAfter = const_cast<EngravingItem*>(m_item);
2924 : }
2925 :
2926 2880 : void EngravingItem::LayoutData::disconnectItemSnappedBefore()
2927 : {
2928 2880 : if (!m_itemSnappedBefore) {
2929 2863 : return;
2930 : }
2931 17 : m_itemSnappedBefore->mutldata()->m_itemSnappedAfter = nullptr;
2932 17 : m_itemSnappedBefore = nullptr;
2933 : }
2934 :
2935 73 : void EngravingItem::LayoutData::connectItemSnappedAfter(EngravingItem* itemAfter)
2936 : {
2937 73 : IF_ASSERT_FAILED(itemAfter && itemAfter != m_item && itemAfter != m_itemSnappedBefore
2938 : && itemAfter->ldata()->itemSnappedAfter() != m_item) {
2939 0 : return;
2940 : }
2941 73 : m_itemSnappedAfter = itemAfter;
2942 73 : itemAfter->mutldata()->m_itemSnappedBefore = const_cast<EngravingItem*>(m_item);
2943 : }
2944 :
2945 2880 : void EngravingItem::LayoutData::disconnectItemSnappedAfter()
2946 : {
2947 2880 : if (!m_itemSnappedAfter) {
2948 2841 : return;
2949 : }
2950 39 : m_itemSnappedAfter->mutldata()->m_itemSnappedBefore = nullptr;
2951 39 : m_itemSnappedAfter = nullptr;
2952 : }
2953 :
2954 5983 : void EngravingItem::LayoutData::scanConnectedItemsInSnappingChain(const System* system,
2955 : const std::function<void(EngravingItem*)>& func) const
2956 : {
2957 5983 : EngravingItem* item = const_cast<EngravingItem*>(m_item);
2958 5983 : func(item);
2959 :
2960 11966 : std::vector<EngravingItem*> visited = { item }; // To break out of circular snap chains
2961 :
2962 5983 : EngravingItem* snappedBefore = m_itemSnappedBefore;
2963 6172 : while (snappedBefore && snappedBefore->findAncestor(ElementType::SYSTEM) == system) {
2964 189 : IF_ASSERT_FAILED(!muse::contains(visited, snappedBefore)) {
2965 0 : break;
2966 : }
2967 189 : visited.push_back(snappedBefore);
2968 189 : func(snappedBefore);
2969 189 : snappedBefore = snappedBefore->ldata()->itemSnappedBefore();
2970 : }
2971 :
2972 5983 : EngravingItem* snappedAfter = m_itemSnappedAfter;
2973 6386 : while (snappedAfter && snappedAfter->findAncestor(ElementType::SYSTEM) == system) {
2974 403 : IF_ASSERT_FAILED(!muse::contains(visited, snappedAfter)) {
2975 0 : break;
2976 : }
2977 403 : visited.push_back(snappedAfter);
2978 403 : func(snappedAfter);
2979 403 : snappedAfter = snappedAfter->ldata()->itemSnappedAfter();
2980 : }
2981 5983 : }
2982 :
2983 2877748 : const RectF& EngravingItem::LayoutData::bbox(LD_ACCESS mode) const
2984 : {
2985 : //! NOTE Temporary disabled CHECK - a lot of messages
2986 : UNUSED(mode);
2987 2877748 : const Shape& sh = m_shape.value(LD_ACCESS::MAYBE_NOTINITED);
2988 :
2989 : //! NOTE Temporary
2990 : {
2991 : static const RectF _dummy;
2992 :
2993 2877748 : switch (m_item->type()) {
2994 177326 : case ElementType::NOTE:
2995 : case ElementType::ORNAMENT:
2996 177326 : return !sh.elements().empty() ? sh.elements().at(0) : _dummy;
2997 2700422 : default:
2998 2700422 : break;
2999 : }
3000 : }
3001 :
3002 2700422 : return sh.bbox();
3003 : }
3004 :
3005 2931490 : Shape EngravingItem::LayoutData::shape(LD_ACCESS mode) const
3006 : {
3007 : //! NOTE Temporary disabled CHECK - a lot of messages
3008 2931490 : Shape sh = m_shape.value(LD_ACCESS::MAYBE_NOTINITED);
3009 :
3010 2931490 : const Parenthesis* leftParen = m_item->leftParen();
3011 2931490 : if (leftParen && leftParen->addToSkyline()) {
3012 118 : sh.add(leftParen->ldata()->shape().translated(leftParen->pos()));
3013 : }
3014 2931490 : const Parenthesis* rightParen = m_item->rightParen();
3015 2931490 : if (rightParen && rightParen->addToSkyline()) {
3016 118 : sh.add(rightParen->ldata()->shape().translated(rightParen->pos()));
3017 : }
3018 :
3019 : //! NOTE Temporary
3020 : //! Reimplementation: done
3021 : {
3022 2931490 : switch (m_item->type()) {
3023 1133574 : case ElementType::BEAM:
3024 : case ElementType::GRACE_NOTES_GROUP:
3025 : case ElementType::ORNAMENT:
3026 : case ElementType::OTTAVA_SEGMENT:
3027 : case ElementType::VOLTA_SEGMENT:
3028 : case ElementType::PEDAL_SEGMENT:
3029 : case ElementType::TEXTLINE_SEGMENT:
3030 : case ElementType::HARMONIC_MARK_SEGMENT:
3031 : case ElementType::PALM_MUTE_SEGMENT:
3032 : case ElementType::LET_RING_SEGMENT:
3033 : case ElementType::GRADUAL_TEMPO_CHANGE_SEGMENT:
3034 : case ElementType::RASGUEADO_SEGMENT:
3035 : case ElementType::WHAMMY_BAR_SEGMENT:
3036 : case ElementType::SLUR_SEGMENT:
3037 : case ElementType::TIE_SEGMENT:
3038 : case ElementType::LAISSEZ_VIB_SEGMENT:
3039 : case ElementType::PARTIAL_TIE_SEGMENT:
3040 : case ElementType::NOTE:
3041 1133574 : return sh;
3042 447769 : case ElementType::CHORD:
3043 : case ElementType::REST:
3044 : case ElementType::MEASURE_REPEAT:
3045 : case ElementType::MMREST: {
3046 447769 : if (mode == LD_ACCESS::CHECK) {
3047 : //! NOTE Temporary fix
3048 : //! We can remove it the moment we figure out the layout order of the elements
3049 443929 : LayoutContext ctx(m_item->score());
3050 443929 : ChordRest::LayoutData* ldata = static_cast<ChordRest::LayoutData*>(const_cast<LayoutData*>(this));
3051 443929 : ChordLayout::checkAndFillShape(toChordRest(m_item), ldata, ctx.conf());
3052 443929 : }
3053 447769 : return m_shape.value(LD_ACCESS::CHECK);
3054 : } break;
3055 1323 : case ElementType::GUITAR_BEND_SEGMENT: {
3056 : //! NOTE Temporary fix
3057 : //! We can remove it the moment we figure out the layout order of the elements
3058 1323 : TLayout::fillGuitarBendSegmentShape(toGuitarBendSegment(m_item),
3059 : static_cast<GuitarBendSegment::LayoutData*>(const_cast<LayoutData*>(this)));
3060 1323 : return m_shape.value(LD_ACCESS::CHECK);
3061 : } break;
3062 1056 : case ElementType::HAIRPIN_SEGMENT: {
3063 : //! To be removed when we're confident enough...
3064 1056 : IF_ASSERT_FAILED(m_shape.has_value()) {
3065 0 : const_cast<LayoutData*>(this)->setShape(TLayout::recalculateTextLineBaseSegmentShape(toHairpinSegment(m_item)));
3066 : }
3067 1056 : return m_shape.value(LD_ACCESS::CHECK);
3068 : } break;
3069 232 : case ElementType::TRILL_SEGMENT: {
3070 : //! NOTE Temporary fix
3071 : //! We can remove it the moment we figure out the layout order of the elements
3072 232 : LayoutContext ctx(m_item->score());
3073 232 : TLayout::fillTrillSegmentShape(toTrillSegment(m_item),
3074 : static_cast<HairpinSegment::LayoutData*>(const_cast<LayoutData*>(this)),
3075 : ctx.conf());
3076 232 : return m_shape.value(LD_ACCESS::CHECK);
3077 232 : } break;
3078 1941 : case ElementType::TUPLET: {
3079 : //! NOTE Temporary fix
3080 : //! We can remove it the moment we figure out the layout order of the elements
3081 1941 : TLayout::fillTupletShape(toTuplet(m_item), static_cast<Tuplet::LayoutData*>(const_cast<LayoutData*>(this)));
3082 1941 : return m_shape.value(LD_ACCESS::CHECK);
3083 : } break;
3084 1345595 : default:
3085 1345595 : break;
3086 : }
3087 : }
3088 :
3089 1345595 : return sh;
3090 2931490 : }
3091 :
3092 : #ifndef NDEBUG
3093 2538272 : void EngravingItem::LayoutData::doSetPosDebugHook(double x, double y)
3094 : {
3095 : UNUSED(x);
3096 : UNUSED(y);
3097 2538272 : }
3098 :
3099 778731 : void EngravingItem::LayoutData::setWidthDebugHook(double w)
3100 : {
3101 : UNUSED(w);
3102 778731 : }
3103 :
3104 : #endif
3105 :
3106 0 : void EngravingItem::LayoutData::dump(std::stringstream& ss) const
3107 : {
3108 0 : ss << "\n";
3109 0 : ss << m_item->typeName() << " id: " << m_item->eid().toStdString() << "\n";
3110 :
3111 0 : ss << "skip: " << (m_isSkipDraw ? "yes" : "no") << "\n";
3112 0 : ss << "mag: " << m_mag << "\n";
3113 :
3114 0 : ss << "pos: ";
3115 0 : mu::engraving::dump(m_pos, ss);
3116 0 : ss << "\n";
3117 :
3118 0 : ss << "shape: ";
3119 0 : mu::engraving::dump(m_shape, ss);
3120 0 : ss << "\n";
3121 :
3122 0 : supDump(ss);
3123 0 : }
3124 :
3125 4958683 : double EngravingItem::mag() const
3126 : {
3127 4958683 : if (!ldata()) {
3128 : //LOGD() << "no layout data, will be returned default (1.0)";
3129 0 : return 1.0;
3130 : }
3131 4958683 : return ldata()->mag();
3132 : }
3133 :
3134 710510 : PointF EngravingItem::staffOffset() const
3135 : {
3136 710510 : const StaffType* st = staffType();
3137 710510 : const double yOffset = st ? st->yoffset().val() * spatium() : 0.0;
3138 710510 : return PointF(0.0, yOffset);
3139 : }
3140 : }
|