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 : #include "spanner.h"
23 :
24 : #include "translation.h"
25 :
26 : #include "../editing/navigation.h"
27 :
28 : #include "anchors.h"
29 : #include "chord.h"
30 : #include "chordrest.h"
31 : #include "location.h"
32 : #include "lyrics.h"
33 : #include "measure.h"
34 : #include "note.h"
35 : #include "part.h"
36 : #include "score.h"
37 : #include "segment.h"
38 : #include "staff.h"
39 : #include "system.h"
40 : #include "types/typesconv.h"
41 :
42 : #include "log.h"
43 :
44 : using namespace mu;
45 : using namespace mu::engraving;
46 :
47 : namespace mu::engraving {
48 : //---------------------------------------------------------
49 : // SpannerSegment
50 : //---------------------------------------------------------
51 :
52 4193 : SpannerSegment::SpannerSegment(const ElementType& type, Spanner* sp, ElementFlags f)
53 4193 : : EngravingItem(type, sp, f)
54 : {
55 4193 : setSpannerSegmentType(SpannerSegmentType::SINGLE);
56 4193 : }
57 :
58 589 : SpannerSegment::SpannerSegment(const SpannerSegment& s)
59 589 : : EngravingItem(s)
60 : {
61 589 : m_spannerSegmentType = s.m_spannerSegmentType;
62 589 : m_p2 = s.m_p2;
63 589 : m_offset2 = s.m_offset2;
64 589 : }
65 :
66 4067 : SpannerSegment::~SpannerSegment()
67 : {
68 : // A system only places a segment, so it holds a plain back-reference to it: take
69 : // that reference with us, rather than relying on every deletion site to do it.
70 4067 : moveToSystem(nullptr);
71 4067 : }
72 :
73 7532 : void SpannerSegment::setSpanner(Spanner* val)
74 : {
75 7532 : setOwnershipParent(val);
76 7532 : }
77 :
78 7532 : void SpannerSegment::setOwnershipParent(Spanner* spanner)
79 : {
80 7532 : EngravingItem::setOwnershipParent(spanner);
81 7532 : }
82 :
83 63246 : EngravingItem* SpannerSegment::layoutParent() const
84 : {
85 63246 : return m_system;
86 : }
87 :
88 25 : EngravingItem* SpannerSegment::accessibleParentItem() const
89 : {
90 : // Not placed on a system yet, so it is the spanner that lists it;
91 : // see Spanner::accessibleChildren().
92 25 : if (!m_system) {
93 1 : if (EngravingItem* owner = ownershipParentItem()) {
94 1 : return owner;
95 : }
96 : }
97 :
98 24 : return EngravingItem::accessibleParentItem();
99 : }
100 :
101 : //---------------------------------------------------------
102 : // mag
103 : //---------------------------------------------------------
104 :
105 53133 : double SpannerSegment::mag() const
106 : {
107 53133 : if (spanner()->systemFlag()) {
108 756 : return 1.0;
109 : }
110 52377 : return staff() ? staff()->staffMag(spanner()->tick()) : 1.0;
111 : }
112 :
113 17783 : Fraction SpannerSegment::tick() const
114 : {
115 17783 : const Spanner* sp = spanner();
116 17783 : return sp ? sp->tick() : Fraction(0, 1);
117 : }
118 :
119 : //---------------------------------------------------------
120 : // moveToSystem
121 : //---------------------------------------------------------
122 :
123 15207 : void SpannerSegment::moveToSystem(System* s)
124 : {
125 15207 : if (m_system == s) {
126 7590 : return;
127 : }
128 7617 : if (m_system) {
129 1490 : m_system->remove(this);
130 : }
131 7617 : m_system = s;
132 7617 : if (s) {
133 6210 : s->add(this);
134 : }
135 : }
136 :
137 : //---------------------------------------------------------
138 : // spatiumChanged
139 : //---------------------------------------------------------
140 :
141 0 : void SpannerSegment::spatiumChanged(double ov, double nv)
142 : {
143 0 : EngravingItem::spatiumChanged(ov, nv);
144 0 : if (offsetIsSpatiumDependent()) {
145 0 : m_offset2 *= (nv / ov);
146 : }
147 0 : }
148 :
149 : //---------------------------------------------------------
150 : // mimeData
151 : //---------------------------------------------------------
152 :
153 0 : muse::ByteArray SpannerSegment::mimeData(const PointF& dragOffset) const
154 : {
155 0 : if (dragOffset.isNull()) { // where is dragOffset used?
156 0 : return spanner()->mimeData(dragOffset);
157 : }
158 0 : return EngravingItem::mimeData(dragOffset);
159 : }
160 :
161 : //---------------------------------------------------------
162 : // propertyDelegate
163 : //---------------------------------------------------------
164 :
165 15248 : EngravingObject* SpannerSegment::propertyDelegate(Pid pid) const
166 : {
167 15248 : switch (pid) {
168 2606 : case Pid::PLAY:
169 : case Pid::COLOR:
170 : case Pid::Z:
171 : case Pid::VISIBLE:
172 : case Pid::PLACEMENT:
173 : case Pid::EXCLUDE_FROM_OTHER_PARTS:
174 : case Pid::POSITION_LINKED_TO_MASTER:
175 : case Pid::APPEARANCE_LINKED_TO_MASTER:
176 : case Pid::SPANNER_TICK:
177 : case Pid::SPANNER_TICKS:
178 : case Pid::SPANNER_TRACK2:
179 2606 : return spanner();
180 12642 : default: break;
181 : }
182 :
183 12642 : return nullptr;
184 : }
185 :
186 : //---------------------------------------------------------
187 : // getProperty
188 : //---------------------------------------------------------
189 :
190 3440 : engraving::PropertyValue SpannerSegment::getProperty(Pid pid) const
191 : {
192 3440 : if (EngravingObject* e = const_cast<SpannerSegment*>(this)->propertyDelegate(pid)) {
193 1579 : return e->getProperty(pid);
194 : }
195 1861 : switch (pid) {
196 6 : case Pid::OFFSET2:
197 6 : return m_offset2;
198 1855 : default:
199 1855 : return EngravingItem::getProperty(pid);
200 : }
201 : }
202 :
203 : //---------------------------------------------------------
204 : // setProperty
205 : //---------------------------------------------------------
206 :
207 1465 : bool SpannerSegment::setProperty(Pid pid, const PropertyValue& v)
208 : {
209 1465 : if (EngravingObject* e = propertyDelegate(pid)) {
210 0 : return e->setProperty(pid, v);
211 : }
212 1465 : switch (pid) {
213 3 : case Pid::OFFSET2:
214 3 : m_offset2 = v.value<PointF>();
215 3 : triggerLayout();
216 3 : break;
217 1462 : default:
218 1462 : return EngravingItem::setProperty(pid, v);
219 : }
220 3 : return true;
221 : }
222 :
223 : //---------------------------------------------------------
224 : // propertyDefault
225 : //---------------------------------------------------------
226 :
227 3383 : PropertyValue SpannerSegment::propertyDefault(Pid pid) const
228 : {
229 3383 : if (EngravingObject* e = const_cast<SpannerSegment*>(this)->propertyDelegate(pid)) {
230 1566 : return e->propertyDefault(pid);
231 : }
232 1817 : switch (pid) {
233 0 : case Pid::OFFSET2:
234 0 : return PropertyValue();
235 1817 : default:
236 1817 : return EngravingItem::propertyDefault(pid);
237 : }
238 : }
239 :
240 169 : PointF SpannerSegment::defaultPos() const
241 : {
242 169 : return spanner() ? spanner()->defaultPos() : PointF();
243 : }
244 :
245 : //---------------------------------------------------------
246 : // getPropertyStyle
247 : //---------------------------------------------------------
248 :
249 2588 : Sid SpannerSegment::getPropertyStyle(Pid pid) const
250 : {
251 2588 : if (EngravingObject* e = const_cast<SpannerSegment*>(this)->propertyDelegate(pid)) {
252 0 : return e->getPropertyStyle(pid);
253 : }
254 2588 : return EngravingItem::getPropertyStyle(pid);
255 : }
256 :
257 : //---------------------------------------------------------
258 : // resetProperty
259 : //---------------------------------------------------------
260 :
261 0 : void SpannerSegment::resetProperty(Pid pid)
262 : {
263 0 : if (EngravingObject* e = propertyDelegate(pid)) {
264 0 : return e->resetProperty(pid);
265 : }
266 0 : return EngravingItem::resetProperty(pid);
267 : }
268 :
269 : //---------------------------------------------------------
270 : // styleChanged
271 : //---------------------------------------------------------
272 :
273 53 : void SpannerSegment::styleChanged()
274 : {
275 53 : spanner()->styleChanged();
276 53 : }
277 :
278 : //---------------------------------------------------------
279 : // reset
280 : //---------------------------------------------------------
281 :
282 0 : void SpannerSegment::reset()
283 : {
284 0 : undoChangeProperty(Pid::OFFSET2, PointF());
285 0 : EngravingItem::reset();
286 0 : spanner()->reset();
287 0 : }
288 :
289 : //---------------------------------------------------------
290 : // undoChangeProperty
291 : //---------------------------------------------------------
292 :
293 30 : void SpannerSegment::undoChangeProperty(Pid pid, const PropertyValue& val, PropertyFlags ps)
294 : {
295 30 : if (pid == Pid::AUTOPLACE && (val.toBool() == true && !autoplace())) {
296 : // Switching autoplacement on. Save user-defined
297 : // placement properties to undo stack.
298 3 : undoPushProperty(Pid::OFFSET2);
299 : // other will be saved in EngravingItem::undoChangeProperty
300 : }
301 30 : EngravingItem::undoChangeProperty(pid, val, ps);
302 30 : }
303 :
304 : //---------------------------------------------------------
305 : // setSelected
306 : //---------------------------------------------------------
307 :
308 8810 : void SpannerSegment::setSelected(bool f)
309 : {
310 8810 : EngravingItem::setSelected(f);
311 8810 : if (spanner()->selected() != f) {
312 3792 : spanner()->setSelected(f);
313 : }
314 8810 : }
315 :
316 : //---------------------------------------------------------
317 : // setVisible
318 : //---------------------------------------------------------
319 :
320 2658 : void SpannerSegment::setVisible(bool f)
321 : {
322 2658 : if (Spanner* sp = spanner()) {
323 2658 : sp->setVisible(f);
324 : }
325 2658 : EngravingItem::setVisible(f);
326 2658 : }
327 :
328 : //---------------------------------------------------------
329 : // setColor
330 : //---------------------------------------------------------
331 :
332 900 : void SpannerSegment::setColor(const Color& col)
333 : {
334 900 : if (Spanner* sp = spanner()) {
335 900 : sp->setColor(col);
336 : }
337 900 : EngravingItem::setColor(col);
338 900 : }
339 :
340 : //---------------------------------------------------------
341 : // setZ
342 : //---------------------------------------------------------
343 :
344 0 : void SpannerSegment::setZ(int val)
345 : {
346 0 : if (Spanner* sp = spanner()) {
347 0 : sp->setZ(val);
348 : }
349 0 : EngravingItem::setZ(val);
350 0 : }
351 :
352 : //---------------------------------------------------------
353 : // nextSegmentElement
354 : //---------------------------------------------------------
355 :
356 0 : EngravingItem* SpannerSegment::nextSegmentElement()
357 : {
358 0 : return spanner()->nextSegmentElement();
359 : }
360 :
361 : //---------------------------------------------------------
362 : // prevSegmentElement
363 : //---------------------------------------------------------
364 :
365 0 : EngravingItem* SpannerSegment::prevSegmentElement()
366 : {
367 0 : return spanner()->prevSegmentElement();
368 : }
369 :
370 : //---------------------------------------------------------
371 : // accessibleInfo
372 : //---------------------------------------------------------
373 :
374 0 : String SpannerSegment::accessibleInfo() const
375 : {
376 0 : return spanner()->accessibleInfo();
377 : }
378 :
379 : //---------------------------------------------------------
380 : // triggerLayout
381 : //---------------------------------------------------------
382 :
383 1009 : void SpannerSegment::triggerLayout() const
384 : {
385 1009 : if (Spanner* sp = spanner()) {
386 1009 : sp->triggerLayout();
387 : }
388 1009 : }
389 :
390 10 : std::list<EngravingObject*> SpannerSegment::linkListForPropertyPropagation() const
391 : {
392 10 : std::list<EngravingObject*> result;
393 10 : result.push_back(const_cast<SpannerSegment*>(this));
394 :
395 10 : if (isMiddleType()) {
396 0 : return result;
397 : }
398 :
399 10 : const Spanner* sp = spanner();
400 20 : for (const EngravingObject* linkedSpanner : sp->linkList()) {
401 10 : if (linkedSpanner == sp || toSpanner(linkedSpanner)->placement() != sp->placement()) {
402 10 : continue;
403 : }
404 0 : const std::vector<SpannerSegment*>& linkedSegments = toSpanner(linkedSpanner)->spannerSegments();
405 0 : if (linkedSegments.empty()) {
406 0 : continue;
407 : }
408 0 : if (isSingleBeginType()) {
409 0 : result.push_back(linkedSegments.front());
410 0 : } else if (isSingleEndType()) {
411 0 : result.push_back(linkedSegments.back());
412 : }
413 10 : }
414 :
415 10 : return result;
416 0 : }
417 :
418 0 : bool SpannerSegment::isPropertyLinkedToMaster(Pid id) const
419 : {
420 0 : bool linkedForSpannerSegment = EngravingItem::isPropertyLinkedToMaster(id);
421 0 : if (!linkedForSpannerSegment) {
422 0 : return false;
423 : }
424 :
425 : // The property is linked for the spanner segment, but may be unlinked for the spanner, in which case we consider it unlinked
426 0 : return spanner()->isPropertyLinkedToMaster(id);
427 : }
428 :
429 1681 : bool SpannerSegment::isUserModified() const
430 : {
431 1678 : bool modified = !autoplace() || !visible()
432 1678 : || (propertyFlags(Pid::MIN_DISTANCE) == PropertyFlags::UNSTYLED
433 3358 : || getProperty(Pid::MIN_DISTANCE) != propertyDefault(Pid::MIN_DISTANCE))
434 3359 : || (!offset().isNull() || !userOff2().isNull());
435 :
436 1681 : return modified;
437 : }
438 :
439 0 : bool SpannerSegment::allowTimeAnchor() const
440 : {
441 0 : return spanner()->allowTimeAnchor();
442 : }
443 :
444 0 : int SpannerSegment::subtype() const
445 : {
446 0 : return spanner()->subtype();
447 : }
448 :
449 0 : TranslatableString SpannerSegment::subtypeUserName() const
450 : {
451 0 : return spanner()->subtypeUserName();
452 : }
453 :
454 : //---------------------------------------------------------
455 : // Spanner
456 : //---------------------------------------------------------
457 :
458 4772 : Spanner::Spanner(const ElementType& type, EngravingObject* parent, ElementFlags f)
459 4772 : : EngravingItem(type, parent, f)
460 : {
461 4772 : }
462 :
463 787 : Spanner::Spanner(const Spanner& s)
464 787 : : EngravingItem(s)
465 : {
466 787 : m_playSpanner = s.m_playSpanner;
467 787 : m_startElement = s.m_startElement;
468 787 : m_endElement = s.m_endElement;
469 787 : m_tick = s.m_tick;
470 787 : m_ticks = s.m_ticks;
471 787 : m_track2 = s.m_track2;
472 :
473 1376 : for (auto* segment : s.m_segments) {
474 589 : SpannerSegment* newSegment = toSpannerSegment(segment->clone());
475 589 : add(newSegment); // reparents the clone to this spanner
476 : }
477 787 : }
478 :
479 4870 : Spanner::~Spanner()
480 : {
481 : // A spanner owns its segments (they are its DOM children), so it has to free
482 : // them. Without this they would only be unparented onto the dummy, where they
483 : // would sit until the score is destroyed - with a dangling spanner() pointer.
484 4870 : eraseSpannerSegments();
485 4870 : }
486 :
487 : //---------------------------------------------------------
488 : // mag
489 : //---------------------------------------------------------
490 :
491 140 : double Spanner::mag() const
492 : {
493 140 : if (systemFlag()) {
494 140 : return 1.0;
495 : }
496 0 : return staff() ? staff()->staffMag(tick()) : 1.0;
497 : }
498 :
499 : //---------------------------------------------------------
500 : // add
501 : //---------------------------------------------------------
502 :
503 4923 : void Spanner::add(EngravingItem* e)
504 : {
505 4923 : SpannerSegment* ls = toSpannerSegment(e);
506 4923 : ls->setSpanner(this);
507 4923 : ls->setSelected(selected());
508 4923 : ls->setTrack(track());
509 : // ls->setAutoplace(autoplace());
510 4923 : ls->EngravingItem::setZ(z());
511 4923 : ls->EngravingItem::setVisible(visible());
512 4923 : m_segments.push_back(ls);
513 4923 : e->added();
514 4923 : }
515 :
516 : //---------------------------------------------------------
517 : // remove
518 : //---------------------------------------------------------
519 :
520 0 : void Spanner::remove(EngravingItem* e)
521 : {
522 0 : SpannerSegment* ss = toSpannerSegment(e);
523 0 : if (ss->system()) {
524 0 : ss->system()->remove(ss);
525 : }
526 0 : m_segments.erase(std::remove(m_segments.begin(), m_segments.end(), ss), m_segments.end());
527 0 : }
528 :
529 1108 : void Spanner::removed()
530 : {
531 2037 : for (SpannerSegment* seg : m_segments) {
532 929 : seg->moveToSystem(nullptr);
533 : }
534 :
535 1137 : for (SpannerSegment* seg : m_unusedSegments) {
536 29 : seg->moveToSystem(nullptr);
537 : }
538 1108 : }
539 :
540 12 : EngravingItemList Spanner::accessibleChildren() const
541 : {
542 : // A segment that is placed on a system belongs to that system in the accessibility
543 : // tree, even though this spanner is what owns it.
544 12 : EngravingItemList children = EngravingItem::accessibleChildren();
545 12 : muse::remove_if(children, [](EngravingItem* item) {
546 13 : return item->isSpannerSegment() && toSpannerSegment(item)->system();
547 : });
548 :
549 12 : return children;
550 0 : }
551 :
552 : //---------------------------------------------------------
553 : // removeUnmanaged
554 : //
555 : // Remove the Spanner and its segments from objects which may know about them
556 : //
557 : // This method and the following are used for spanners which are contained within compound elements
558 : // which manage their parts themselves without using the standard management supplied by Score;
559 : // Example can be the LyricsLine within a Lyrics element or the FiguredBassLine within a FiguredBass
560 : // (not implemented yet).
561 : //---------------------------------------------------------
562 :
563 235 : void Spanner::removeUnmanaged()
564 : {
565 477 : for (SpannerSegment* ss : spannerSegments()) {
566 242 : if (ss->system()) {
567 7 : ss->moveToSystem(nullptr);
568 : }
569 : }
570 235 : score()->removeUnmanagedSpanner(this);
571 235 : }
572 :
573 : //---------------------------------------------------------
574 : // insertTimeUnmanaged
575 : //---------------------------------------------------------
576 :
577 19 : void Spanner::insertTimeUnmanaged(const Fraction& fromTick, const Fraction& len)
578 : {
579 19 : Fraction newTick1 = tick();
580 19 : Fraction newTick2 = tick2();
581 :
582 : // check spanner start and end point
583 19 : if (len > Fraction(0, 1)) { // adding time
584 18 : if (tick() >= fromTick) { // start after insertion point: shift start to right
585 0 : newTick1 += len;
586 : }
587 18 : if (tick2() > fromTick) { // end after insertion point: shift end to right
588 1 : newTick2 += len;
589 : }
590 : }
591 19 : if (len < Fraction(0, 1)) { // removing time
592 1 : Fraction toTick = fromTick - len;
593 1 : if (tick() > fromTick) { // start after beginning of removed time
594 0 : if (tick() < toTick) { // start within removed time: bring start at removing point
595 0 : if (ownershipParent()) {
596 0 : ownershipParentItem()->remove(this);
597 0 : return;
598 : } else {
599 0 : newTick1 = fromTick;
600 : }
601 : } else { // start after removed time: shift start to left
602 0 : newTick1 += len;
603 : }
604 : }
605 1 : if (tick2() > fromTick) { // end after start of removed time
606 1 : if (tick2() < toTick) { // end within removed time: bring end at removing point
607 0 : newTick2 = fromTick;
608 : } else { // end after removed time: shift end to left
609 1 : newTick2 += len;
610 : }
611 : }
612 : }
613 :
614 : // update properties as required
615 19 : if (newTick2 <= newTick1) { // if no longer any span: remove it
616 0 : if (ownershipParent()) {
617 0 : ownershipParentItem()->remove(this);
618 : }
619 : } else { // if either TICKS or TICK did change, update property
620 19 : if (newTick2 - newTick1 != tick2() - tick()) {
621 2 : setProperty(Pid::SPANNER_TICKS, newTick2 - newTick1);
622 : }
623 19 : if (newTick1 != tick()) {
624 0 : setProperty(Pid::SPANNER_TICK, newTick1);
625 : }
626 : }
627 : }
628 :
629 : //---------------------------------------------------------
630 : // isVisibleCR
631 : //---------------------------------------------------------
632 :
633 0 : static bool isVisibleCR(EngravingItem* e)
634 : {
635 0 : if (!e || !e->isChordRest()) {
636 0 : return true; // assume visible
637 : }
638 0 : ChordRest* cr = toChordRest(e);
639 0 : return cr->measure()->visible(cr->staffIdx());
640 : }
641 :
642 : //---------------------------------------------------------
643 : // eitherEndVisible
644 : //---------------------------------------------------------
645 :
646 0 : bool Spanner::eitherEndVisible() const
647 : {
648 0 : if (isVolta()) {
649 0 : return true;
650 : }
651 0 : if (!score()->staff(staffIdx())->show()) {
652 0 : return false;
653 : }
654 0 : return isVisibleCR(startElement()) || isVisibleCR(endElement());
655 : }
656 :
657 : //---------------------------------------------------------
658 : // setScore
659 : //---------------------------------------------------------
660 :
661 231 : void Spanner::setScore(Score* s)
662 : {
663 231 : EngravingItem::setScore(s);
664 369 : for (SpannerSegment* seg : m_segments) {
665 138 : seg->setScore(s);
666 : }
667 231 : }
668 :
669 : //---------------------------------------------------------
670 : // getProperty
671 : //---------------------------------------------------------
672 :
673 21168 : PropertyValue Spanner::getProperty(Pid propertyId) const
674 : {
675 21168 : switch (propertyId) {
676 2049 : case Pid::PLAY:
677 2049 : return m_playSpanner;
678 168 : case Pid::SPANNER_TICK:
679 168 : return m_tick;
680 99 : case Pid::SPANNER_TICKS:
681 99 : return m_ticks;
682 3 : case Pid::SPANNER_TRACK2:
683 3 : return track2();
684 0 : case Pid::LOCATION_STAVES:
685 0 : return (track2() / VOICES) - (track() / VOICES);
686 0 : case Pid::LOCATION_VOICES:
687 0 : return (track2() % VOICES) - (track() / VOICES);
688 0 : case Pid::LOCATION_FRACTIONS:
689 0 : return m_ticks;
690 0 : case Pid::LOCATION_MEASURES:
691 : case Pid::LOCATION_GRACE:
692 : case Pid::LOCATION_NOTE:
693 0 : return Location::getLocationProperty(propertyId, startElement(), endElement());
694 18849 : default:
695 18849 : break;
696 : }
697 18849 : return EngravingItem::getProperty(propertyId);
698 : }
699 :
700 : //---------------------------------------------------------
701 : // setProperty
702 : //---------------------------------------------------------
703 :
704 2005 : bool Spanner::setProperty(Pid propertyId, const PropertyValue& v)
705 : {
706 2005 : switch (propertyId) {
707 0 : case Pid::PLAY:
708 0 : setPlaySpanner(v.toBool());
709 0 : break;
710 70 : case Pid::SPANNER_TICK:
711 70 : triggerLayout(); // spanner may have moved to another system
712 70 : setTick(v.value<Fraction>());
713 70 : if (score() && isInSpannerMap() && score()->spannerMap().removeSpanner(this)) {
714 70 : score()->addSpanner(this, /*computeStartEnd =*/ false);
715 : }
716 70 : break;
717 44 : case Pid::SPANNER_TICKS:
718 44 : triggerLayout(); // spanner may now span for a smaller number of systems
719 44 : setTicks(v.value<Fraction>());
720 44 : setEndElement(0); // invalidate
721 44 : break;
722 1 : case Pid::TRACK:
723 1 : setTrack(v.value<track_idx_t>());
724 1 : setStartElement(0); // invalidate
725 1 : break;
726 1 : case Pid::SPANNER_TRACK2:
727 1 : setTrack2(v.value<track_idx_t>());
728 1 : setEndElement(0); // invalidate
729 1 : break;
730 0 : case Pid::POSITION_LINKED_TO_MASTER:
731 0 : setPositionLinkedToMaster(v.toBool());
732 0 : if (isPositionLinkedToMaster()) {
733 0 : for (SpannerSegment* seg : spannerSegments()) {
734 0 : seg->relinkPropertiesToMaster(PropertyGroup::POSITION);
735 : }
736 0 : relinkPropertiesToMaster(PropertyGroup::POSITION);
737 : }
738 0 : break;
739 0 : case Pid::APPEARANCE_LINKED_TO_MASTER:
740 0 : setAppearanceLinkedToMaster(v.toBool());
741 0 : if (isAppearanceLinkedToMaster()) {
742 0 : for (SpannerSegment* seg : spannerSegments()) {
743 0 : seg->relinkPropertiesToMaster(PropertyGroup::APPEARANCE);
744 : }
745 0 : relinkPropertiesToMaster(PropertyGroup::APPEARANCE);
746 : }
747 0 : break;
748 1889 : default:
749 1889 : return EngravingItem::setProperty(propertyId, v);
750 : }
751 116 : triggerLayout();
752 116 : return true;
753 : }
754 :
755 : //---------------------------------------------------------
756 : // propertyDefault
757 : //---------------------------------------------------------
758 :
759 21869 : PropertyValue Spanner::propertyDefault(Pid propertyId) const
760 : {
761 21869 : switch (propertyId) {
762 2049 : case Pid::PLAY:
763 2049 : return true;
764 19820 : default:
765 19820 : break;
766 : }
767 19820 : return EngravingItem::propertyDefault(propertyId);
768 : }
769 :
770 : //---------------------------------------------------------
771 : // computeStartElement
772 : //---------------------------------------------------------
773 :
774 5516 : void Spanner::computeStartElement()
775 : {
776 5516 : EngravingItem* oldStartElement = m_startElement;
777 :
778 5516 : doComputeStartElement();
779 :
780 5516 : if (oldStartElement && oldStartElement->isChord()) {
781 3167 : toChord(oldStartElement)->removeStartingSpanner(this);
782 : }
783 :
784 5516 : Chord* startChord = m_startElement && m_startElement->isChord() ? toChord(m_startElement) : nullptr;
785 5516 : if (startChord) {
786 3895 : startChord->addStartingSpanner(this);
787 : }
788 5516 : }
789 :
790 5516 : void Spanner::doComputeStartElement()
791 : {
792 5516 : switch (anchor()) {
793 3711 : case Anchor::SEGMENT: {
794 3711 : Segment* startSeg = startSegment();
795 3711 : if (!startSeg) {
796 139 : return;
797 : }
798 3572 : if (systemFlag()) {
799 63 : m_startElement = startSegment();
800 : } else {
801 3509 : EngravingItem* startEl = startSeg->element(track());
802 3509 : if (startEl) {
803 3508 : m_startElement = startEl;
804 : } else {
805 1 : m_startElement = startSeg->firstElement(track2staff(track()));
806 : }
807 : }
808 : }
809 3572 : break;
810 :
811 789 : case Anchor::MEASURE:
812 789 : m_startElement = score()->tick2measure(tick());
813 789 : break;
814 :
815 556 : case Anchor::CHORDREST:
816 556 : m_startElement = startCR();
817 556 : break;
818 460 : case Anchor::NOTE:
819 460 : break;
820 : }
821 : }
822 :
823 : //---------------------------------------------------------
824 : // computeEndElement
825 : //---------------------------------------------------------
826 :
827 5514 : void Spanner::computeEndElement()
828 : {
829 5514 : if (score()->isPaletteScore()) {
830 0 : m_endElement = nullptr;
831 0 : return;
832 : }
833 :
834 5514 : EngravingItem* oldEndElement = m_endElement;
835 :
836 5514 : doComputeEndElement();
837 :
838 5514 : if (oldEndElement && oldEndElement->isChord()) {
839 2591 : toChord(oldEndElement)->removeEndingSpanner(this);
840 : }
841 :
842 5514 : Chord* endChord = m_endElement && m_endElement->isChord() ? toChord(m_endElement) : nullptr;
843 5514 : if (endChord) {
844 3400 : endChord->addEndingSpanner(this);
845 : }
846 : }
847 :
848 4717 : void Spanner::doComputeEndElement()
849 : {
850 4717 : switch (anchor()) {
851 2912 : case Anchor::SEGMENT: {
852 2912 : Segment* endSeg = endSegment();
853 2912 : if (!endSeg) {
854 124 : return;
855 : }
856 2788 : if (systemFlag()) {
857 62 : m_endElement = endSeg;
858 : } else {
859 2726 : track_idx_t trackIdx = effectiveTrack2();
860 2726 : EngravingItem* endEl = endSeg->element(trackIdx);
861 2726 : if (endEl) {
862 2716 : m_endElement = endEl;
863 : } else {
864 10 : m_endElement = endSeg->firstElement(track2staff(trackIdx));
865 : }
866 : }
867 : }
868 2788 : break;
869 :
870 789 : case Anchor::MEASURE:
871 789 : m_endElement = score()->tick2measure(tick2() - Fraction(1, 1920));
872 789 : break;
873 :
874 460 : case Anchor::NOTE:
875 460 : break;
876 556 : case Anchor::CHORDREST:
877 556 : m_endElement = endCR();
878 556 : break;
879 : }
880 : }
881 :
882 37072 : bool Spanner::canBeCrossStaff() const
883 : {
884 37072 : switch (type()) {
885 15494 : case ElementType::SLUR:
886 : case ElementType::TIE:
887 : case ElementType::ARPEGGIO:
888 : case ElementType::CHORD_BRACKET:
889 : case ElementType::GLISSANDO:
890 : case ElementType::NOTELINE:
891 15494 : return true;
892 21578 : default:
893 21578 : return false;
894 : }
895 : }
896 :
897 : //---------------------------------------------------------
898 : // startElementFromSpanner
899 : //
900 : // Given a Spanner and an end element, determines a start element suitable for the end
901 : // element of a new Spanner, so that it is 'parallel' to the old one.
902 : // Can be used while cloning a linked Spanner, to update the cloned spanner start and end elements
903 : // (Spanner(const Spanner&) copies start and end elements from the original to the copy).
904 : // NOTES: Only spanners with Anchor::NOTE are currently supported.
905 : // Going back from end to start ensures the 'other' anchor of this is already set up
906 : // (for instance, while cloning staves)
907 : //---------------------------------------------------------
908 :
909 10 : Note* Spanner::startElementFromSpanner(Spanner* sp, EngravingItem* newEnd)
910 : {
911 10 : if (sp->anchor() != Anchor::NOTE) {
912 0 : return nullptr;
913 : }
914 :
915 10 : Note* oldStart = toNote(sp->startElement());
916 10 : Note* oldEnd = toNote(sp->endElement());
917 10 : if (oldStart == nullptr || oldEnd == nullptr) {
918 0 : return nullptr;
919 : }
920 10 : Note* newStart = nullptr;
921 10 : Score* score = newEnd->score();
922 : // determine the track where to expect the 'parallel' start element
923 10 : track_idx_t newTrack = (newEnd->track() - oldEnd->track()) + oldStart->track();
924 : // look in notes linked to oldStart for a note with the
925 : // same score as new score and appropriate track
926 20 : for (EngravingObject* newEl : oldStart->linkList()) {
927 20 : if (toNote(newEl)->score() == score && toNote(newEl)->track() == newTrack) {
928 10 : newStart = toNote(newEl);
929 10 : break;
930 : }
931 10 : }
932 10 : return newStart;
933 : }
934 :
935 : //---------------------------------------------------------
936 : // endElementFromSpanner
937 : //
938 : // Given a Spanner and a start element, determines an end element suitable for the start
939 : // element of a new Spanner, so that it is 'parallel' to the old one.
940 : // Can be used while cloning a linked Spanner, to update the cloned spanner start and end elements
941 : // (Spanner(const Spanner&) copies start and end elements from the original to the copy).
942 : // NOTES: Only spanners with Anchor::NOTE are currently supported.
943 : //---------------------------------------------------------
944 :
945 2 : Note* Spanner::endElementFromSpanner(Spanner* sp, EngravingItem* newStart)
946 : {
947 2 : if (sp->anchor() != Anchor::NOTE) {
948 0 : return nullptr;
949 : }
950 :
951 2 : Note* oldStart = toNote(sp->startElement());
952 2 : Note* oldEnd = toNote(sp->endElement());
953 2 : if (oldStart == nullptr || oldEnd == nullptr) {
954 0 : return nullptr;
955 : }
956 2 : Note* newEnd = nullptr;
957 2 : Score* score = newStart->score();
958 : // determine the track where to expect the 'parallel' start element
959 2 : track_idx_t newTrack = newStart->track() + (oldEnd->track() - oldStart->track());
960 : // look in notes linked to oldEnd for a note with the
961 : // same score as new score and appropriate track
962 4 : for (EngravingObject* newEl : oldEnd->linkList()) {
963 4 : if (toNote(newEl)->score() == score && toNote(newEl)->track() == newTrack) {
964 2 : newEnd = toNote(newEl);
965 2 : break;
966 : }
967 2 : }
968 2 : return newEnd;
969 : }
970 :
971 : //---------------------------------------------------------
972 : // setNoteSpan
973 : //
974 : // Sets up all the variables congruent with given start and end note anchors.
975 : //---------------------------------------------------------
976 :
977 54 : void Spanner::setNoteSpan(Note* startNote, Note* endNote)
978 : {
979 54 : if (anchor() != Anchor::NOTE) {
980 0 : return;
981 : }
982 :
983 54 : Score* score = startNote ? startNote->score() : endNote->score();
984 54 : Note* parent = startNote ? startNote : endNote;
985 54 : Fraction tick = startNote ? startNote->tick() : endNote->tick();
986 54 : Fraction endTick = endNote ? endNote->tick() : startNote->tick();
987 54 : track_idx_t track = startNote ? startNote->track() : endNote->track();
988 54 : track_idx_t endTrack = endNote ? endNote->track() : startNote->track();
989 :
990 54 : setScore(score);
991 54 : setOwnershipParent(parent);
992 54 : setStartElement(startNote);
993 54 : setEndElement(endNote);
994 54 : setTick(tick);
995 54 : setTick2(endTick);
996 54 : setTrack(track);
997 54 : setTrack2(endTrack);
998 : }
999 :
1000 : //---------------------------------------------------------
1001 : // startChord
1002 : //---------------------------------------------------------
1003 :
1004 2846 : Chord* Spanner::startChord()
1005 : {
1006 2846 : if (anchor() != Anchor::CHORDREST) {
1007 8 : return nullptr;
1008 : }
1009 2838 : if (!m_startElement) {
1010 0 : m_startElement = findStartChord();
1011 : }
1012 :
1013 2838 : if (m_startElement && m_startElement->isChord()) {
1014 2838 : return toChord(m_startElement);
1015 : }
1016 :
1017 0 : return nullptr;
1018 : }
1019 :
1020 : //---------------------------------------------------------
1021 : // endChord
1022 : //---------------------------------------------------------
1023 :
1024 4503 : Chord* Spanner::endChord()
1025 : {
1026 4503 : if (anchor() != Anchor::CHORDREST) {
1027 8 : return nullptr;
1028 : }
1029 4495 : if (!m_endElement && type() == ElementType::SLUR) {
1030 0 : m_endElement = findEndChord();
1031 : }
1032 :
1033 4495 : if (m_endElement && m_endElement->isChord()) {
1034 4480 : return toChord(m_endElement);
1035 : }
1036 :
1037 15 : return nullptr;
1038 : }
1039 :
1040 : //---------------------------------------------------------
1041 : // startCR
1042 : //---------------------------------------------------------
1043 :
1044 43586 : ChordRest* Spanner::startCR()
1045 : {
1046 43586 : assert(anchor() == Anchor::SEGMENT || anchor() == Anchor::CHORDREST);
1047 43586 : if (!m_startElement || m_startElement->score() != score()) {
1048 : // TODO: This is a bit weird and prevents this method from being const...
1049 34 : m_startElement = findStartCR();
1050 : }
1051 43586 : return (m_startElement && m_startElement->isChordRest()) ? toChordRest(m_startElement) : nullptr;
1052 : }
1053 :
1054 : //---------------------------------------------------------
1055 : // endCR
1056 : //---------------------------------------------------------
1057 :
1058 48340 : ChordRest* Spanner::endCR()
1059 : {
1060 48340 : assert(anchor() == Anchor::SEGMENT || anchor() == Anchor::CHORDREST);
1061 48340 : if ((!m_endElement || m_endElement->score() != score())) {
1062 : // TODO: This is a bit weird and prevents this method from being const...
1063 360 : m_endElement = findEndCR();
1064 : }
1065 48340 : return (m_endElement && m_endElement->isChordRest()) ? toChordRest(m_endElement) : nullptr;
1066 : }
1067 :
1068 : //---------------------------------------------------------
1069 : // findStartChord
1070 : //---------------------------------------------------------
1071 :
1072 0 : Chord* Spanner::findStartChord() const
1073 : {
1074 0 : assert(anchor() == Anchor::CHORDREST);
1075 0 : ChordRest* cr = score()->findCR(tick(), track());
1076 0 : return cr && cr->isChord() ? toChord(cr) : nullptr;
1077 : }
1078 :
1079 : //---------------------------------------------------------
1080 : // findEndChord
1081 : //---------------------------------------------------------
1082 :
1083 0 : Chord* Spanner::findEndChord() const
1084 : {
1085 0 : assert(anchor() == Anchor::CHORDREST);
1086 0 : Segment* s = score()->tick2segmentMM(tick2(), false, SegmentType::ChordRest);
1087 0 : ChordRest* endCR = s ? toChordRest(s->element(track2())) : nullptr;
1088 0 : if (endCR && !endCR->isChord()) {
1089 0 : endCR = nullptr;
1090 : }
1091 0 : return toChord(endCR);
1092 : }
1093 :
1094 : //---------------------------------------------------------
1095 : // findStartCR
1096 : //---------------------------------------------------------
1097 :
1098 35 : ChordRest* Spanner::findStartCR() const
1099 : {
1100 35 : assert(anchor() == Anchor::SEGMENT || anchor() == Anchor::CHORDREST);
1101 35 : return score()->findCR(tick(), track());
1102 : }
1103 :
1104 : //---------------------------------------------------------
1105 : // findEndCR
1106 : //---------------------------------------------------------
1107 :
1108 361 : ChordRest* Spanner::findEndCR() const
1109 : {
1110 361 : assert(anchor() == Anchor::SEGMENT || anchor() == Anchor::CHORDREST);
1111 361 : Segment* s = score()->tick2segmentMM(tick2(), false, SegmentType::ChordRest);
1112 361 : const track_idx_t tr2 = effectiveTrack2();
1113 361 : ChordRest* endCR = s ? toChordRest(s->element(tr2)) : nullptr;
1114 361 : return endCR;
1115 : }
1116 :
1117 : //---------------------------------------------------------
1118 : // startSegment
1119 : //---------------------------------------------------------
1120 :
1121 7349 : Segment* Spanner::startSegment() const
1122 : {
1123 7349 : assert(score() != NULL);
1124 :
1125 7349 : bool mmRest = style().styleB(Sid::createMultiMeasureRests);
1126 7349 : Fraction startTick = tick();
1127 7349 : track_idx_t trackIdx = track();
1128 7349 : staff_idx_t staffIdx = track2staff(trackIdx);
1129 :
1130 7349 : Segment* startSeg = score()->tick2segment(startTick, true, SegmentType::ChordRest, mmRest);
1131 :
1132 7349 : if (!startSeg || !startSeg->hasElements(staffIdx) || (isVoiceSpecific() && !startSeg->element(trackIdx))) {
1133 262 : startSeg = score()->tick2segment(startTick, true, SegmentType::TimeTick, mmRest);
1134 : }
1135 :
1136 7349 : if (!startSeg && startTick < score()->endTick()) {
1137 8 : Measure* measure = mmRest ? score()->tick2measureMM(startTick) : score()->tick2measure(startTick);
1138 8 : if (measure) {
1139 8 : TimeTickAnchor* anchor = EditTimeTickAnchors::createTimeTickAnchor(measure, startTick - measure->tick(), track2staff(trackIdx));
1140 8 : IF_ASSERT_FAILED(anchor) {
1141 0 : return nullptr;
1142 : }
1143 8 : EditTimeTickAnchors::updateLayout(measure);
1144 8 : return anchor->segment();
1145 : }
1146 : }
1147 :
1148 7341 : if (!startSeg) {
1149 139 : startSeg = score()->tick2rightSegment(startTick, mmRest);
1150 : }
1151 :
1152 7341 : return startSeg;
1153 : }
1154 :
1155 : //---------------------------------------------------------
1156 : // endSegment
1157 : //---------------------------------------------------------
1158 :
1159 10358 : Segment* Spanner::endSegment() const
1160 : {
1161 10358 : assert(score() != NULL);
1162 :
1163 10358 : bool mmRest = style().styleB(Sid::createMultiMeasureRests);
1164 10358 : Fraction endTick = tick2();
1165 10358 : track_idx_t trackIdx = effectiveTrack2();
1166 10358 : staff_idx_t staffIdx = track2staff(trackIdx);
1167 :
1168 10358 : Segment* endSeg = score()->tick2segment(endTick, true, SegmentType::ChordRest, mmRest);
1169 :
1170 10358 : if (!endSeg || !endSeg->hasElements(staffIdx) || (isVoiceSpecific() && !endSeg->element(trackIdx))) {
1171 505 : endSeg = score()->tick2segment(endTick, true, SegmentType::TimeTick, mmRest);
1172 : }
1173 :
1174 10358 : if (!endSeg && !endTick.isZero()) {
1175 277 : Measure* measure = mmRest ? score()->tick2measureMM(endTick) : score()->tick2measure(endTick);
1176 277 : if (measure) {
1177 156 : TimeTickAnchor* anchor = EditTimeTickAnchors::createTimeTickAnchor(measure, endTick - measure->tick(), track2staff(trackIdx));
1178 156 : EditTimeTickAnchors::updateLayout(measure);
1179 156 : return anchor->segment();
1180 : }
1181 : }
1182 :
1183 10202 : if (!endSeg) {
1184 124 : endSeg = score()->tick2leftSegment(endTick, mmRest);
1185 : }
1186 :
1187 10202 : return endSeg;
1188 : }
1189 :
1190 13 : bool Spanner::elementAppliesToTrack(const track_idx_t refTrack) const
1191 : {
1192 13 : if (!hasVoiceAssignmentProperties()) {
1193 4 : return refTrack == track() || refTrack == track2();
1194 : }
1195 :
1196 9 : const VoiceAssignment voiceAssignment = getProperty(Pid::VOICE_ASSIGNMENT).value<VoiceAssignment>();
1197 :
1198 17 : return EngravingItem::elementAppliesToTrack(track(), refTrack, voiceAssignment, part()) || EngravingItem::elementAppliesToTrack(
1199 17 : track2(), refTrack, voiceAssignment, part());
1200 : }
1201 :
1202 : //---------------------------------------------------------
1203 : // startMeasure
1204 : //---------------------------------------------------------
1205 :
1206 1035 : Measure* Spanner::startMeasure() const
1207 : {
1208 1035 : return toMeasure(m_startElement);
1209 : }
1210 :
1211 : //---------------------------------------------------------
1212 : // endMeasure
1213 : //---------------------------------------------------------
1214 :
1215 838 : Measure* Spanner::endMeasure() const
1216 : {
1217 838 : assert(anchor() == Spanner::Anchor::MEASURE);
1218 838 : return toMeasure(m_endElement);
1219 : }
1220 :
1221 15 : Measure* Spanner::findStartMeasure() const
1222 : {
1223 15 : if (!m_startElement) {
1224 0 : return nullptr;
1225 : }
1226 :
1227 15 : return toMeasure(m_startElement->findAncestor(ElementType::MEASURE));
1228 : }
1229 :
1230 3 : Measure* Spanner::findEndMeasure() const
1231 : {
1232 3 : if (!m_endElement) {
1233 0 : return nullptr;
1234 : }
1235 :
1236 3 : return toMeasure(m_endElement->findAncestor(ElementType::MEASURE));
1237 : }
1238 :
1239 : //---------------------------------------------------------
1240 : // setSelected
1241 : //---------------------------------------------------------
1242 :
1243 3879 : void Spanner::setSelected(bool f)
1244 : {
1245 3879 : EngravingItem::setSelected(f);
1246 :
1247 7740 : for (SpannerSegment* ss : spannerSegments()) {
1248 3861 : if (ss->selected() != f) {
1249 68 : ss->setSelected(f);
1250 : }
1251 : }
1252 3879 : }
1253 :
1254 : //---------------------------------------------------------
1255 : // setVisible
1256 : //---------------------------------------------------------
1257 :
1258 3205 : void Spanner::setVisible(bool f)
1259 : {
1260 5956 : for (SpannerSegment* ss : spannerSegments()) {
1261 2751 : ss->EngravingItem::setVisible(f);
1262 : }
1263 3205 : EngravingItem::setVisible(f);
1264 3205 : }
1265 :
1266 : //---------------------------------------------------------
1267 : // setAutoplace
1268 : //---------------------------------------------------------
1269 :
1270 2 : void Spanner::setAutoplace(bool f)
1271 : {
1272 3 : for (SpannerSegment* ss : spannerSegments()) {
1273 1 : ss->EngravingItem::setAutoplace(f);
1274 : }
1275 2 : EngravingItem::setAutoplace(f);
1276 2 : }
1277 :
1278 : //---------------------------------------------------------
1279 : // setColor
1280 : //---------------------------------------------------------
1281 :
1282 908 : void Spanner::setColor(const Color& col)
1283 : {
1284 920 : for (SpannerSegment* ss : spannerSegments()) {
1285 12 : ss->EngravingItem::setColor(col);
1286 : }
1287 908 : EngravingItem::setColor(col);
1288 908 : }
1289 :
1290 : //---------------------------------------------------------
1291 : // setZ
1292 : //---------------------------------------------------------
1293 :
1294 0 : void Spanner::setZ(int val)
1295 : {
1296 0 : for (SpannerSegment* ss : spannerSegments()) {
1297 0 : ss->EngravingItem::setZ(val);
1298 : }
1299 0 : EngravingItem::setZ(val);
1300 0 : }
1301 :
1302 : //---------------------------------------------------------
1303 : // setStartElement
1304 : //---------------------------------------------------------
1305 :
1306 4459 : void Spanner::setStartElement(EngravingItem* e)
1307 : {
1308 : #ifndef NDEBUG
1309 4459 : if (anchor() == Anchor::NOTE) {
1310 3551 : assert(!e || e->isNote());
1311 : }
1312 : #endif
1313 4459 : m_startElement = e;
1314 4459 : }
1315 :
1316 : //---------------------------------------------------------
1317 : // setEndElement
1318 : //---------------------------------------------------------
1319 :
1320 5152 : void Spanner::setEndElement(EngravingItem* e)
1321 : {
1322 : #ifndef NDEBUG
1323 5152 : if (anchor() == Anchor::NOTE) {
1324 3280 : assert(!e || e->isNote());
1325 : }
1326 : #endif
1327 5152 : m_endElement = e;
1328 5152 : }
1329 :
1330 : //---------------------------------------------------------
1331 : // nextSpanner
1332 : //---------------------------------------------------------
1333 :
1334 0 : Spanner* Spanner::nextSpanner(EngravingItem* e, staff_idx_t activeStaff)
1335 : {
1336 0 : std::multimap<int, Spanner*> mmap = score()->spanner();
1337 0 : auto range = mmap.equal_range(tick().ticks());
1338 0 : if (range.first != range.second) { // range not empty
1339 0 : for (auto i = range.first; i != range.second; ++i) {
1340 0 : if (i->second == e) {
1341 0 : while (i != range.second) {
1342 0 : ++i;
1343 0 : if (i == range.second) {
1344 0 : return nullptr;
1345 : }
1346 0 : Spanner* s = i->second;
1347 0 : EngravingItem* st = s->startElement();
1348 0 : if (!st) {
1349 0 : continue;
1350 : }
1351 0 : if (s->startSegment() == toSpanner(e)->startSegment()) {
1352 0 : if (st->staffIdx() == activeStaff) {
1353 0 : return s;
1354 0 : } else if (st->isMeasure() && activeStaff == 0) {
1355 0 : return s;
1356 : }
1357 : }
1358 : }
1359 0 : break;
1360 : }
1361 : }
1362 : }
1363 0 : return nullptr;
1364 0 : }
1365 :
1366 : //---------------------------------------------------------
1367 : // prevSpanner
1368 : //---------------------------------------------------------
1369 :
1370 0 : Spanner* Spanner::prevSpanner(EngravingItem* e, staff_idx_t activeStaff)
1371 : {
1372 0 : std::multimap<int, Spanner*> mmap = score()->spanner();
1373 0 : auto range = mmap.equal_range(tick().ticks());
1374 0 : if (range.first != range.second) { // range not empty
1375 0 : for (auto i = range.first; i != range.second; ++i) {
1376 0 : if (i->second == e) {
1377 0 : if (i == range.first) {
1378 0 : return nullptr;
1379 : }
1380 0 : while (i != range.first) {
1381 0 : --i;
1382 0 : Spanner* s = i->second;
1383 0 : EngravingItem* st = s->startElement();
1384 0 : if (s->startSegment() == toSpanner(e)->startSegment()) {
1385 0 : if (st->staffIdx() == activeStaff) {
1386 0 : return s;
1387 0 : } else if (st->isMeasure() && activeStaff == 0) {
1388 0 : return s;
1389 : }
1390 : }
1391 : }
1392 0 : break;
1393 : }
1394 : }
1395 : }
1396 0 : return nullptr;
1397 0 : }
1398 :
1399 : //---------------------------------------------------------
1400 : // nextSegmentElement
1401 : //---------------------------------------------------------
1402 :
1403 0 : EngravingItem* Spanner::nextSegmentElement()
1404 : {
1405 0 : Segment* s = startSegment();
1406 0 : if (s) {
1407 0 : return s->firstElementForNavigation(staffIdx());
1408 : }
1409 0 : return Navigation::lastElement(score());
1410 : }
1411 :
1412 : //---------------------------------------------------------
1413 : // prevSegmentElement
1414 : //---------------------------------------------------------
1415 :
1416 0 : EngravingItem* Spanner::prevSegmentElement()
1417 : {
1418 0 : Segment* s = endSegment();
1419 0 : if (s) {
1420 0 : return s->lastElementForNavigation(staffIdx());
1421 : }
1422 0 : return Navigation::firstElement(score());
1423 : }
1424 :
1425 : //---------------------------------------------------------
1426 : // setTick
1427 : //---------------------------------------------------------
1428 :
1429 11984 : void Spanner::setTick(const Fraction& v)
1430 : {
1431 11984 : if (m_tick == v) {
1432 7843 : return;
1433 : }
1434 :
1435 4141 : m_tick = v;
1436 :
1437 4141 : Score* score = this->score();
1438 :
1439 4141 : if (score && isInSpannerMap()) {
1440 1444 : score->spannerMap().setDirty();
1441 : }
1442 : }
1443 :
1444 : //---------------------------------------------------------
1445 : // setTick2
1446 : //---------------------------------------------------------
1447 :
1448 5296 : void Spanner::setTick2(const Fraction& f)
1449 : {
1450 5296 : Fraction ticks = f - m_tick;
1451 5296 : IF_ASSERT_FAILED(f >= m_tick) {
1452 0 : ticks = m_tick - f;
1453 0 : m_tick = f;
1454 : }
1455 5296 : setTicks(ticks);
1456 5296 : }
1457 :
1458 : //---------------------------------------------------------
1459 : // setTicks
1460 : //---------------------------------------------------------
1461 :
1462 5766 : void Spanner::setTicks(const Fraction& f)
1463 : {
1464 5766 : if (m_ticks == f) {
1465 2198 : return;
1466 : }
1467 :
1468 3568 : IF_ASSERT_FAILED(f.positive()) {
1469 0 : m_ticks = -f;
1470 : } else {
1471 3568 : m_ticks = f;
1472 : }
1473 :
1474 3568 : Score* score = this->score();
1475 :
1476 3568 : if (score && isInSpannerMap()) {
1477 1515 : score->spannerMap().setDirty();
1478 : }
1479 : }
1480 :
1481 16999 : bool Spanner::isVoiceSpecific() const
1482 : {
1483 : static const std::unordered_set<ElementType> VOICE_SPECIFIC_SPANNERS {
1484 : ElementType::TRILL,
1485 : ElementType::HAIRPIN,
1486 : ElementType::LET_RING,
1487 17021 : };
1488 :
1489 16999 : return VOICE_SPECIFIC_SPANNERS.find(type()) != VOICE_SPECIFIC_SPANNERS.end();
1490 : }
1491 :
1492 5054 : track_idx_t Spanner::track2() const
1493 : {
1494 5054 : return canBeCrossStaff() ? m_track2 : m_track;
1495 : }
1496 :
1497 5338 : void Spanner::setTrack2(track_idx_t v)
1498 : {
1499 5338 : if (!canBeCrossStaff()) {
1500 2921 : return;
1501 : }
1502 :
1503 2417 : m_track2 = v;
1504 : }
1505 :
1506 26680 : track_idx_t Spanner::effectiveTrack2() const
1507 : {
1508 26680 : return canBeCrossStaff() && m_track2 != muse::nidx ? m_track2 : m_track;
1509 : }
1510 :
1511 : //---------------------------------------------------------
1512 : // triggerLayout
1513 : //---------------------------------------------------------
1514 :
1515 129541 : void Spanner::triggerLayout() const
1516 : {
1517 : // Don't trigger layout until the tick is correctly set, otherwise is triggers layout of the entire score
1518 129541 : if (m_tick.numerator() < 0) {
1519 116725 : return;
1520 : }
1521 :
1522 : // Spanners do not have parent even when added to a score, so can't check parent here
1523 12816 : const track_idx_t tr2 = effectiveTrack2();
1524 12816 : score()->setLayout(m_tick, m_tick + m_ticks, staffIdx(), track2staff(tr2), this);
1525 : }
1526 :
1527 : //---------------------------------------------------------
1528 : // pushUnusedSegment
1529 : //---------------------------------------------------------
1530 :
1531 471 : void Spanner::pushUnusedSegment(SpannerSegment* seg)
1532 : {
1533 471 : if (!seg) {
1534 0 : return;
1535 : }
1536 471 : seg->moveToSystem(nullptr);
1537 471 : m_unusedSegments.push_back(seg);
1538 : }
1539 :
1540 : //---------------------------------------------------------
1541 : // popUnusedSegment
1542 : // Take the next unused segment for reusing it.
1543 : // If there is no unused segments left returns nullptr.
1544 : //---------------------------------------------------------
1545 :
1546 3892 : SpannerSegment* Spanner::popUnusedSegment()
1547 : {
1548 3892 : if (m_unusedSegments.empty()) {
1549 3749 : return nullptr;
1550 : }
1551 143 : SpannerSegment* seg = m_unusedSegments.front();
1552 143 : m_unusedSegments.pop_front();
1553 143 : return seg;
1554 : }
1555 :
1556 : //---------------------------------------------------------
1557 : // reuse
1558 : // called when segment from unusedSegments is added
1559 : // back to the spanner.
1560 : //---------------------------------------------------------
1561 :
1562 143 : void Spanner::reuse(SpannerSegment* seg)
1563 : {
1564 143 : add(seg);
1565 143 : }
1566 :
1567 : //---------------------------------------------------------
1568 : // reuseSegments
1569 : // Adds \p number segments from unusedSegments to this
1570 : // spanner via reuse() call. Returns number of new
1571 : // segments that still need to be created, that is,
1572 : // returns (number - nMovedSegments).
1573 : //---------------------------------------------------------
1574 :
1575 2348 : int Spanner::reuseSegments(int number)
1576 : {
1577 2491 : while (number > 0) {
1578 2348 : SpannerSegment* seg = popUnusedSegment();
1579 2348 : if (!seg) {
1580 2205 : break;
1581 : }
1582 143 : reuse(seg);
1583 143 : --number;
1584 : }
1585 2348 : return number;
1586 : }
1587 :
1588 : //---------------------------------------------------------
1589 : // fixupSegments
1590 : // Makes number of segments match targetNumber.
1591 : // Tries to reuse unused segments. If there are no
1592 : // unused segments left, uses \p createSegment to create
1593 : // the needed segments.
1594 : // Previously unused segments are added via reuse() call
1595 : //---------------------------------------------------------
1596 :
1597 5905 : void Spanner::fixupSegments(unsigned int targetNumber, std::function<SpannerSegment* ()> createSegment)
1598 : {
1599 5905 : const int diff = targetNumber - int(nsegments());
1600 5905 : if (diff == 0) {
1601 3086 : return;
1602 : }
1603 2819 : if (diff > 0) {
1604 2348 : const int ncreate = reuseSegments(diff);
1605 4881 : for (int i = 0; i < ncreate; ++i) {
1606 2533 : add(createSegment());
1607 : }
1608 : } else { // diff < 0
1609 471 : const int nremove = -diff;
1610 942 : for (int i = 0; i < nremove; ++i) {
1611 471 : SpannerSegment* seg = m_segments.back();
1612 471 : m_segments.pop_back();
1613 471 : pushUnusedSegment(seg);
1614 : }
1615 : }
1616 : }
1617 :
1618 1130 : bool Spanner::isUserModified() const
1619 : {
1620 1868 : for (SpannerSegment* seg : m_segments) {
1621 741 : if (seg->isUserModified()) {
1622 3 : return true;
1623 : }
1624 : }
1625 :
1626 1127 : return false;
1627 : }
1628 :
1629 : //---------------------------------------------------------
1630 : // eraseSpannerSegments
1631 : // Completely erase all spanner segments, both used and
1632 : // unused.
1633 : //---------------------------------------------------------
1634 :
1635 5987 : void Spanner::eraseSpannerSegments()
1636 : {
1637 : // Unplace the used segments explicitly, so that System::remove() calls removed()
1638 : // on a fully alive segment. ~SpannerSegment would unplace them too, but from there
1639 : // the virtual call no longer reaches overrides below SpannerSegment.
1640 : // Unused segments were already unplaced on their way into m_unusedSegments.
1641 9766 : for (SpannerSegment* seg : m_segments) {
1642 3779 : if (System* system = seg->system()) {
1643 13 : system->remove(seg);
1644 : }
1645 : }
1646 :
1647 5987 : muse::DeleteAll(m_segments);
1648 5987 : muse::DeleteAll(m_unusedSegments);
1649 5987 : m_segments.clear();
1650 5987 : m_unusedSegments.clear();
1651 5987 : }
1652 :
1653 0 : String SpannerSegment::formatBarsAndBeats() const
1654 : {
1655 0 : const Spanner* spanner = this->spanner();
1656 :
1657 0 : if (!spanner) {
1658 0 : return EngravingItem::formatBarsAndBeats();
1659 : }
1660 :
1661 0 : const Segment* endSegment = spanner->endSegment();
1662 :
1663 0 : if (!endSegment) {
1664 0 : endSegment = score()->lastSegment()->prev1MM(SegmentType::ChordRest);
1665 : }
1666 :
1667 0 : if (endSegment->tick() > Fraction(0, 1)
1668 0 : && endSegment->tick() != score()->lastSegment()->prev1MM(SegmentType::ChordRest)->tick()
1669 0 : && spanner->type() != ElementType::SLUR
1670 0 : && spanner->type() != ElementType::HAMMER_ON_PULL_OFF
1671 0 : && spanner->type() != ElementType::TIE) {
1672 0 : endSegment = endSegment->prev1MM(SegmentType::ChordRest);
1673 : }
1674 :
1675 0 : IF_ASSERT_FAILED(endSegment) {
1676 0 : return EngravingItem::formatBarsAndBeats();
1677 : }
1678 :
1679 0 : return formatStartBarsAndBeats(spanner->startSegment()) + u' ' + formatEndBarsAndBeats(endSegment);
1680 : }
1681 :
1682 0 : String SpannerSegment::formatStartBarsAndBeats(const Segment* segment) const
1683 : {
1684 0 : EngravingItem::BarBeat barbeat = segment->barbeat();
1685 0 : String result = muse::mtrc("engraving", "Start measure: %1").arg(String::number(barbeat.bar));
1686 :
1687 0 : if (barbeat.displayedBar != barbeat.bar) {
1688 0 : result += u"; " + muse::mtrc("engraving", "Start displayed measure: %1").arg(barbeat.displayedBar);
1689 : }
1690 :
1691 0 : result += u"; " + muse::mtrc("engraving", "Start beat: %1").arg(barbeat.beat);
1692 0 : return result;
1693 0 : }
1694 :
1695 0 : String SpannerSegment::formatEndBarsAndBeats(const Segment* segment) const
1696 : {
1697 0 : EngravingItem::BarBeat barbeat = segment->barbeat();
1698 0 : String result = muse::mtrc("engraving", "End measure: %1").arg(String::number(barbeat.bar));
1699 :
1700 0 : if (barbeat.displayedBar != barbeat.bar) {
1701 0 : result += u"; " + muse::mtrc("engraving", "End displayed measure: %1").arg(barbeat.displayedBar);
1702 : }
1703 :
1704 0 : result += u"; " + muse::mtrc("engraving", "End beat: %1").arg(barbeat.beat);
1705 0 : return result;
1706 0 : }
1707 :
1708 0 : bool SpannerSegment::collectForDrawing() const
1709 : {
1710 0 : return EngravingItem::collectForDrawing() && (spanner()->eitherEndVisible() || systemFlag());
1711 : }
1712 : }
|