1 //=============================================================================
2 //  MuseScore
3 //  Music Composition & Notation
4 //
5 //  Copyright (C) 2010-2011 Werner Schweer
6 //
7 //  This program is free software; you can redistribute it and/or modify
8 //  it under the terms of the GNU General Public License version 2
9 //  as published by the Free Software Foundation and appearing in
10 //  the file LICENCE.GPL
11 //=============================================================================
12 
13 #include "connector.h"
14 #include "score.h"
15 #include "spanner.h"
16 #include "system.h"
17 #include "chordrest.h"
18 #include "chord.h"
19 #include "segment.h"
20 #include "measure.h"
21 #include "undo.h"
22 #include "staff.h"
23 #include "lyrics.h"
24 #include "musescoreCore.h"
25 
26 namespace Ms {
27 
28 //-----------------------------------------------------------------------------
29 //   @@ SpannerWriter
30 ///   Helper class for writing Spanners
31 //-----------------------------------------------------------------------------
32 
33 class SpannerWriter : public ConnectorInfoWriter {
34    protected:
tagName() const35       const char* tagName() const override { return "Spanner"; }
36    public:
37       SpannerWriter(XmlWriter& xml, const Element* current, const Spanner* spanner, int track, Fraction frac, bool start);
38 
39       static void fillSpannerPosition(Location& l, const MeasureBase* endpoint, const Fraction& tick, bool clipboardmode);
40       };
41 
42 //---------------------------------------------------------
43 //   SpannerSegment
44 //---------------------------------------------------------
45 
SpannerSegment(Spanner * sp,Score * s,ElementFlags f)46 SpannerSegment::SpannerSegment(Spanner* sp, Score* s, ElementFlags f)
47    : Element(s, f)
48       {
49       _spanner = sp;
50       setSpannerSegmentType(SpannerSegmentType::SINGLE);
51       }
52 
SpannerSegment(Score * s,ElementFlags f)53 SpannerSegment::SpannerSegment(Score* s, ElementFlags f)
54    : Element(s, f)
55       {
56       setSpannerSegmentType(SpannerSegmentType::SINGLE);
57       _spanner = 0;
58       }
59 
SpannerSegment(const SpannerSegment & s)60 SpannerSegment::SpannerSegment(const SpannerSegment& s)
61    : Element(s)
62       {
63       _spanner            = s._spanner;
64       _spannerSegmentType = s._spannerSegmentType;
65       _p2                 = s._p2;
66       _offset2            = s._offset2;
67       }
68 
69 //---------------------------------------------------------
70 //   mag
71 //---------------------------------------------------------
72 
mag() const73 qreal SpannerSegment::mag() const
74       {
75       if (spanner()->systemFlag())
76             return 1.0;
77       return staff() ? staff()->mag(spanner()->tick()) : 1.0;
78       }
79 
tick() const80 Fraction SpannerSegment::tick() const
81       {
82       return _spanner ? _spanner->tick() : Fraction(0, 1);
83       }
84 
85 //---------------------------------------------------------
86 //   setSystem
87 //---------------------------------------------------------
88 
setSystem(System * s)89 void SpannerSegment::setSystem(System* s)
90       {
91       if (system() != s) {
92             if (system())
93                   system()->remove(this);
94             if (s)
95                   s->add(this);
96             else
97                   setParent(0);
98             }
99       }
100 
101 //---------------------------------------------------------
102 //   spatiumChanged
103 //---------------------------------------------------------
104 
spatiumChanged(qreal ov,qreal nv)105 void SpannerSegment::spatiumChanged(qreal ov, qreal nv)
106       {
107       Element::spatiumChanged(ov, nv);
108       if (offsetIsSpatiumDependent())
109             _offset2 *= (nv / ov);
110       }
111 
112 //---------------------------------------------------------
113 //   mimeData
114 //---------------------------------------------------------
115 
mimeData(const QPointF & dragOffset) const116 QByteArray SpannerSegment::mimeData(const QPointF& dragOffset) const
117       {
118       if (dragOffset.isNull()) // where is dragOffset used?
119             return spanner()->mimeData(dragOffset);
120       return Element::mimeData(dragOffset);
121       }
122 
123 //---------------------------------------------------------
124 //   propertyDelegate
125 //---------------------------------------------------------
126 
propertyDelegate(Pid pid)127 Element* SpannerSegment::propertyDelegate(Pid pid)
128       {
129       if (pid == Pid::COLOR || pid == Pid::VISIBLE || pid == Pid::PLACEMENT)
130             return spanner();
131       return 0;
132       }
133 
134 //---------------------------------------------------------
135 //   getProperty
136 //---------------------------------------------------------
137 
getProperty(Pid pid) const138 QVariant SpannerSegment::getProperty(Pid pid) const
139       {
140       if (Element* e = const_cast<SpannerSegment*>(this)->propertyDelegate(pid))
141             return e->getProperty(pid);
142       switch (pid) {
143             case Pid::OFFSET2:
144                   return _offset2;
145             default:
146                   return Element::getProperty(pid);
147             }
148       }
149 
150 //---------------------------------------------------------
151 //   setProperty
152 //---------------------------------------------------------
153 
setProperty(Pid pid,const QVariant & v)154 bool SpannerSegment::setProperty(Pid pid, const QVariant& v)
155       {
156       if (Element* e = propertyDelegate(pid))
157             return e->setProperty(pid, v);
158       switch (pid) {
159             case Pid::OFFSET2:
160                   _offset2 = v.toPointF();
161                   triggerLayoutAll();
162                   break;
163             default:
164                   return Element::setProperty(pid, v);
165             }
166       return true;
167       }
168 
169 //---------------------------------------------------------
170 //   propertyDefault
171 //---------------------------------------------------------
172 
propertyDefault(Pid pid) const173 QVariant SpannerSegment::propertyDefault(Pid pid) const
174       {
175       if (Element* e = const_cast<SpannerSegment*>(this)->propertyDelegate(pid))
176             return e->propertyDefault(pid);
177       switch (pid) {
178             case Pid::OFFSET2:
179                   return QVariant();
180             default:
181                   return Element::propertyDefault(pid);
182             }
183       }
184 
185 //---------------------------------------------------------
186 //   getPropertyStyle
187 //---------------------------------------------------------
188 
getPropertyStyle(Pid pid) const189 Sid SpannerSegment::getPropertyStyle(Pid pid) const
190       {
191       if (Element* e = const_cast<SpannerSegment*>(this)->propertyDelegate(pid))
192             return e->getPropertyStyle(pid);
193       return Element::getPropertyStyle(pid);
194       }
195 
196 //---------------------------------------------------------
197 //   propertyFlags
198 //---------------------------------------------------------
199 
propertyFlags(Pid pid) const200 PropertyFlags SpannerSegment::propertyFlags(Pid pid) const
201       {
202       if (Element* e = const_cast<SpannerSegment*>(this)->propertyDelegate(pid))
203             return e->propertyFlags(pid);
204       return Element::propertyFlags(pid);
205       }
206 
207 //---------------------------------------------------------
208 //   resetProperty
209 //---------------------------------------------------------
210 
resetProperty(Pid pid)211 void SpannerSegment::resetProperty(Pid pid)
212       {
213       if (Element* e = propertyDelegate(pid))
214             return e->resetProperty(pid);
215       return Element::resetProperty(pid);
216       }
217 
218 //---------------------------------------------------------
219 //   styleChanged
220 //---------------------------------------------------------
221 
styleChanged()222 void SpannerSegment::styleChanged()
223       {
224       spanner()->styleChanged();
225       }
226 
227 //---------------------------------------------------------
228 //   reset
229 //---------------------------------------------------------
230 
reset()231 void SpannerSegment::reset()
232       {
233       undoChangeProperty(Pid::OFFSET2, QPointF());
234       Element::reset();
235       spanner()->reset();
236       }
237 
238 //---------------------------------------------------------
239 //   undoChangeProperty
240 //---------------------------------------------------------
241 
undoChangeProperty(Pid pid,const QVariant & val,PropertyFlags ps)242 void SpannerSegment::undoChangeProperty(Pid pid, const QVariant& val, PropertyFlags ps)
243       {
244       if (pid == Pid::AUTOPLACE && (val.toBool() == true && !autoplace())) {
245             // Switching autoplacement on. Save user-defined
246             // placement properties to undo stack.
247             undoPushProperty(Pid::OFFSET2);
248             // other will be saved in Element::undoChangeProperty
249             }
250       Element::undoChangeProperty(pid, val, ps);
251       }
252 
253 //---------------------------------------------------------
254 //   setSelected
255 //---------------------------------------------------------
256 
setSelected(bool f)257 void SpannerSegment::setSelected(bool f)
258       {
259       for (SpannerSegment* ss : _spanner->spannerSegments())
260             ss->Element::setSelected(f);
261       _spanner->setSelected(f);
262       }
263 
264 //---------------------------------------------------------
265 //   setVisible
266 //---------------------------------------------------------
267 
setVisible(bool f)268 void SpannerSegment::setVisible(bool f)
269       {
270       if (_spanner) {
271             for (SpannerSegment* ss : _spanner->spannerSegments())
272                   ss->Element::setVisible(f);
273             _spanner->setVisible(f);
274             }
275       else
276             Element::setVisible(f);
277       }
278 
279 //---------------------------------------------------------
280 //   setColor
281 //---------------------------------------------------------
282 
setColor(const QColor & col)283 void SpannerSegment::setColor(const QColor& col)
284       {
285       if (_spanner) {
286             for (SpannerSegment* ss : _spanner->spannerSegments())
287                   ss->_color = col;
288             _spanner->_color = col;
289             }
290       else
291             _color = col;
292       }
293 
294 //---------------------------------------------------------
295 //   nextSegmentElement
296 //---------------------------------------------------------
297 
nextSegmentElement()298 Element* SpannerSegment::nextSegmentElement()
299       {
300       return spanner()->nextSegmentElement();
301       }
302 
303 //---------------------------------------------------------
304 //   prevSegmentElement
305 //---------------------------------------------------------
306 
prevSegmentElement()307 Element* SpannerSegment::prevSegmentElement()
308       {
309       return spanner()->prevSegmentElement();
310       }
311 
312 //---------------------------------------------------------
313 //   accessibleInfo
314 //---------------------------------------------------------
315 
accessibleInfo() const316 QString SpannerSegment::accessibleInfo() const
317       {
318       return spanner()->accessibleInfo();
319       }
320 
321 //---------------------------------------------------------
322 //   triggerLayout
323 //---------------------------------------------------------
324 
triggerLayout() const325 void SpannerSegment::triggerLayout() const
326       {
327       if (_spanner)
328             _spanner->triggerLayout();
329       }
330 
331 //---------------------------------------------------------
332 //   Spanner
333 //---------------------------------------------------------
334 
Spanner(Score * s,ElementFlags f)335 Spanner::Spanner(Score* s, ElementFlags f)
336    : Element(s, f)
337       {
338       }
339 
Spanner(const Spanner & s)340 Spanner::Spanner(const Spanner& s)
341    : Element(s)
342       {
343       _anchor       = s._anchor;
344       _startElement = s._startElement;
345       _endElement   = s._endElement;
346       _tick         = s._tick;
347       _ticks        = s._ticks;
348       _track2       = s._track2;
349       }
350 
~Spanner()351 Spanner::~Spanner()
352       {
353       qDeleteAll(segments);
354       qDeleteAll(unusedSegments);
355       }
356 
357 //---------------------------------------------------------
358 //   mag
359 //---------------------------------------------------------
360 
mag() const361 qreal Spanner::mag() const
362       {
363       if (systemFlag())
364             return 1.0;
365       return staff() ? staff()->mag(tick()) : 1.0;
366       }
367 
368 //---------------------------------------------------------
369 //   add
370 //---------------------------------------------------------
371 
add(Element * e)372 void Spanner::add(Element* e)
373       {
374       SpannerSegment* ls = toSpannerSegment(e);
375       ls->setSpanner(this);
376       ls->setSelected(selected());
377       ls->setTrack(track());
378 //      ls->setAutoplace(autoplace());
379       segments.push_back(ls);
380       }
381 
382 //---------------------------------------------------------
383 //   remove
384 //---------------------------------------------------------
385 
remove(Element * e)386 void Spanner::remove(Element* e)
387       {
388       SpannerSegment* ss = toSpannerSegment(e);
389       if (ss->system())
390             ss->system()->remove(ss);
391       segments.erase(std::remove(segments.begin(), segments.end(), ss), segments.end());
392       }
393 
394 //---------------------------------------------------------
395 //   removeUnmanaged
396 //
397 //    Remove the Spanner and its segments from objects which may know about them
398 //
399 //    This method and the following are used for spanners which are contained within compound elements
400 //    which manage their parts themselves without using the standard management supplied by Score;
401 //    Example can be the LyricsLine within a Lyrics element or the FiguredBassLine within a FiguredBass
402 //    (not implemented yet).
403 //---------------------------------------------------------
404 
removeUnmanaged()405 void Spanner::removeUnmanaged()
406       {
407       for (SpannerSegment* ss : spannerSegments())
408             if (ss->system()) {
409 //                  ss->system()->remove(ss);
410                   ss->setSystem(nullptr);
411                   }
412       score()->removeUnmanagedSpanner(this);
413       }
414 
415 //---------------------------------------------------------
416 //   insertTimeUnmanaged
417 //---------------------------------------------------------
418 
insertTimeUnmanaged(const Fraction & fromTick,const Fraction & len)419 void Spanner::insertTimeUnmanaged(const Fraction& fromTick, const Fraction& len)
420       {
421       Fraction newTick1 = tick();
422       Fraction newTick2 = tick2();
423 
424       // check spanner start and end point
425       if (len > Fraction(0,1)) {          // adding time
426             if (tick() > fromTick)        // start after insertion point: shift start to right
427                   newTick1 += len;
428             if (tick2() > fromTick)       // end after insertion point: shift end to right
429                   newTick2 += len;
430             }
431       if (len < Fraction(0,1)) {          // removing time
432             Fraction toTick = fromTick - len;
433             if (tick() > fromTick) {      // start after beginning of removed time
434                   if (tick() < toTick) {  // start within removed time: bring start at removing point
435                         if (parent()) {
436                               parent()->remove(this);
437                               return;
438                               }
439                         else
440                               newTick1 = fromTick;
441                         }
442                   else                    // start after removed time: shift start to left
443                         newTick1 += len;
444                   }
445             if (tick2() > fromTick) {     // end after start of removed time
446                   if (tick2() < toTick)   // end within removed time: bring end at removing point
447                         newTick2 = fromTick;
448                   else                    // end after removed time: shift end to left
449                         newTick2 += len;
450                   }
451             }
452 
453       // update properties as required
454       if (newTick2 <= newTick1) {               // if no longer any span: remove it
455             if (parent())
456                   parent()->remove(this);
457             }
458       else {                                    // if either TICKS or TICK did change, update property
459             if (newTick2-newTick1 != tick2()- tick())
460                   setProperty(Pid::SPANNER_TICKS, newTick2-newTick1);
461             if (newTick1 != tick())
462                   setProperty(Pid::SPANNER_TICK, newTick1);
463             }
464       }
465 
466 //---------------------------------------------------------
467 //   scanElements
468 //    used in palettes
469 //---------------------------------------------------------
470 
scanElements(void * data,void (* func)(void *,Element *),bool all)471 void Spanner::scanElements(void* data, void (*func)(void*, Element*), bool all)
472       {
473       Q_UNUSED(all);
474       for (SpannerSegment* seg : segments)
475             seg->scanElements(data, func, true);
476       }
477 
478 //---------------------------------------------------------
479 //   setScore
480 //---------------------------------------------------------
481 
setScore(Score * s)482 void Spanner::setScore(Score* s)
483       {
484       Element::setScore(s);
485       foreach(SpannerSegment* seg, segments)
486             seg->setScore(s);
487       }
488 
489 //---------------------------------------------------------
490 //   getProperty
491 //---------------------------------------------------------
492 
getProperty(Pid propertyId) const493 QVariant Spanner::getProperty(Pid propertyId) const
494       {
495       switch (propertyId) {
496             case Pid::SPANNER_TICK:
497                   return _tick;
498             case Pid::SPANNER_TICKS:
499                   return _ticks;
500             case Pid::SPANNER_TRACK2:
501                   return track2();
502             case Pid::ANCHOR:
503                   return int(anchor());
504             case Pid::LOCATION_STAVES:
505                   return (track2() / VOICES) - (track() / VOICES);
506             case Pid::LOCATION_VOICES:
507                   return (track2() % VOICES) - (track() / VOICES);
508             case Pid::LOCATION_FRACTIONS:
509                   return _ticks;
510             case Pid::LOCATION_MEASURES:
511             case Pid::LOCATION_GRACE:
512             case Pid::LOCATION_NOTE:
513                   return Location::getLocationProperty(propertyId, startElement(), endElement());
514             default:
515                   break;
516             }
517       return Element::getProperty(propertyId);
518       }
519 
520 //---------------------------------------------------------
521 //   setProperty
522 //---------------------------------------------------------
523 
setProperty(Pid propertyId,const QVariant & v)524 bool Spanner::setProperty(Pid propertyId, const QVariant& v)
525       {
526       switch (propertyId) {
527             case Pid::SPANNER_TICK:
528                   triggerLayout(); // spanner may have moved to another system
529                   setTick(v.value<Fraction>());
530                   setStartElement(0);     // invalidate
531                   setEndElement(0);       //
532                   if (score() && score()->spannerMap().removeSpanner(this))
533                         score()->addSpanner(this);
534                   break;
535             case Pid::SPANNER_TICKS:
536                   triggerLayout(); // spanner may now span for a smaller number of systems
537                   setTicks(v.value<Fraction>());
538                   setEndElement(0);       // invalidate
539                   break;
540             case Pid::TRACK:
541                   setTrack(v.toInt());
542                   setStartElement(0);     // invalidate
543                   break;
544             case Pid::SPANNER_TRACK2:
545                   setTrack2(v.toInt());
546                   setEndElement(0);       // invalidate
547                   break;
548             case Pid::ANCHOR:
549                   setAnchor(Anchor(v.toInt()));
550                   break;
551             default:
552                   return Element::setProperty(propertyId, v);
553             }
554       triggerLayout();
555       return true;
556       }
557 
558 //---------------------------------------------------------
559 //   propertyDefault
560 //---------------------------------------------------------
561 
propertyDefault(Pid propertyId) const562 QVariant Spanner::propertyDefault(Pid propertyId) const
563       {
564       switch (propertyId) {
565             case Pid::ANCHOR:
566                   return int(Anchor::SEGMENT);
567             default:
568                   break;
569             }
570       return Element::propertyDefault(propertyId);
571       }
572 
573 //---------------------------------------------------------
574 //   computeStartElement
575 //---------------------------------------------------------
576 
computeStartElement()577 void Spanner::computeStartElement()
578       {
579       switch (_anchor) {
580             case Anchor::SEGMENT: {
581                   Segment* seg = score()->tick2segmentMM(tick(), false, SegmentType::ChordRest);
582                   int strack = (track() / VOICES) * VOICES;
583                   int etrack = strack + VOICES;
584                   _startElement = 0;
585                   if (seg) {
586                         for (int t = strack; t < etrack; ++t) {
587                               if (seg->element(t)) {
588                                     _startElement = seg->element(t);
589                                     break;
590                                     }
591                               }
592                         }
593                   }
594                   break;
595 
596             case Anchor::MEASURE:
597                   _startElement = score()->tick2measure(tick());
598                   break;
599 
600             case Anchor::CHORD:
601             case Anchor::NOTE:
602                   return;
603             }
604       }
605 
606 //---------------------------------------------------------
607 //   computeEndElement
608 //---------------------------------------------------------
609 
computeEndElement()610 void Spanner::computeEndElement()
611       {
612       if (score()->isPalette()) {
613             // return immediately to prevent lots of
614             // "no element found" messages from appearing
615             _endElement = nullptr;
616             return;
617             }
618 
619       switch (_anchor) {
620             case Anchor::SEGMENT: {
621                   if (track2() == -1)
622                         setTrack2(track());
623                   if (ticks().isZero() && isTextLine() && parent())   // special case palette
624                         setTicks(score()->lastSegment()->tick() - _tick);
625 
626                   if (isLyricsLine() && toLyricsLine(this)->isEndMelisma()) {
627                         // lyrics endTick should already indicate the segment we want
628                         // except for TEMP_MELISMA_TICKS case
629                         Lyrics* l = toLyricsLine(this)->lyrics();
630                         Fraction tick = (l->ticks().ticks() == Lyrics::TEMP_MELISMA_TICKS) ? l->tick() : l->endTick();
631                         Segment* s = score()->tick2segment(tick, true, SegmentType::ChordRest);
632                         if (!s) {
633                               qDebug("%s no end segment for tick %d", name(), tick.ticks());
634                               return;
635                               }
636                         int t = trackZeroVoice(track2());
637                         // take the first chordrest we can find;
638                         // linePos will substitute one in current voice if available
639                         for (int v = 0; v < VOICES; ++v) {
640                               _endElement = s->element(t + v);
641                               if (_endElement)
642                                     break;
643                               }
644                         }
645                   else {
646                         // find last cr on this staff that ends before tick2
647                         _endElement = score()->findCRinStaff(tick2(), track2() / VOICES);
648                         }
649                   if (!_endElement) {
650                         qDebug("%s no end element for tick %d", name(), tick2().ticks());
651                         return;
652                         }
653 
654                   if (!endCR()->measure()->isMMRest()) {
655                         ChordRest* cr = endCR();
656                         Fraction nticks = cr->tick() + cr->actualTicks() - _tick;
657                         if ((_ticks - nticks).isNotZero()) {
658                               qDebug("%s ticks changed, %d -> %d", name(), _ticks.ticks(), nticks.ticks());
659                               setTicks(nticks);
660                               if (isOttava())
661                                     staff()->updateOttava();
662                               }
663                         }
664                   }
665                   break;
666 
667             case Anchor::MEASURE:
668                   _endElement = score()->tick2measure(tick2() - Fraction(1, 1920));
669                   if (!_endElement) {
670                         qDebug("Spanner::computeEndElement(), measure not found for tick %d\n", tick2().ticks()-1);
671                         _endElement = score()->lastMeasure();
672                         }
673                   break;
674 
675             case Anchor::CHORD:
676             case Anchor::NOTE:
677                   break;
678             }
679       }
680 
681 //---------------------------------------------------------
682 //   startElementFromSpanner
683 //
684 //    Given a Spanner and an end element, determines a start element suitable for the end
685 //    element of a new Spanner, so that it is 'parallel' to the old one.
686 //    Can be used while cloning a linked Spanner, to update the cloned spanner start and end elements
687 //    (Spanner(const Spanner&) copies start and end elements from the original to the copy).
688 //    NOTES:      Only spanners with Anchor::NOTE are currently supported.
689 //                Going back from end to start ensures the 'other' anchor of this is already set up
690 //                      (for instance, while cloning staves)
691 //---------------------------------------------------------
692 
startElementFromSpanner(Spanner * sp,Element * newEnd)693 Note* Spanner::startElementFromSpanner(Spanner* sp, Element* newEnd)
694       {
695       if (sp->anchor() != Anchor::NOTE)
696             return nullptr;
697 
698       Note*  oldStart   = toNote(sp->startElement());
699       Note*  oldEnd     = toNote(sp->endElement());
700       if (oldStart == nullptr || oldEnd == nullptr)
701             return nullptr;
702       Note*  newStart   = nullptr;
703       Score* score      = newEnd->score();
704       // determine the track where to expect the 'parallel' start element
705       int   newTrack    = (newEnd->track() - oldEnd->track()) + oldStart->track();
706       // look in notes linked to oldStart for a note with the
707       // same score as new score and appropriate track
708       for (ScoreElement* newEl : oldStart->linkList())
709             if (toNote(newEl)->score() == score && toNote(newEl)->track() == newTrack) {
710                   newStart = toNote(newEl);
711                   break;
712             }
713       return newStart;
714       }
715 
716 //---------------------------------------------------------
717 //   endElementFromSpanner
718 //
719 //    Given a Spanner and a start element, determines an end element suitable for the start
720 //    element of a new Spanner, so that it is 'parallel' to the old one.
721 //    Can be used while cloning a linked Spanner, to update the cloned spanner start and end elements
722 //    (Spanner(const Spanner&) copies start and end elements from the original to the copy).
723 //    NOTES:      Only spanners with Anchor::NOTE are currently supported.
724 //---------------------------------------------------------
725 
endElementFromSpanner(Spanner * sp,Element * newStart)726 Note* Spanner::endElementFromSpanner(Spanner* sp, Element* newStart)
727       {
728       if (sp->anchor() != Anchor::NOTE)
729             return nullptr;
730 
731       Note*  oldStart   = toNote(sp->startElement());
732       Note*  oldEnd     = toNote(sp->endElement());
733       if (oldStart == nullptr || oldEnd == nullptr)
734             return nullptr;
735       Note*  newEnd     = nullptr;
736       Score* score      = newStart->score();
737       // determine the track where to expect the 'parallel' start element
738       int   newTrack    = newStart->track() + (oldEnd->track() - oldStart->track());
739       // look in notes linked to oldEnd for a note with the
740       // same score as new score and appropriate track
741       for (ScoreElement* newEl : oldEnd->linkList())
742             if (toNote(newEl)->score() == score && toNote(newEl)->track() == newTrack) {
743                   newEnd = toNote(newEl);
744                   break;
745             }
746       return newEnd;
747       }
748 
749 //---------------------------------------------------------
750 //   setNoteSpan
751 //
752 //    Sets up all the variables congruent with given start and end note anchors.
753 //---------------------------------------------------------
754 
setNoteSpan(Note * startNote,Note * endNote)755 void  Spanner::setNoteSpan(Note* startNote, Note* endNote)
756       {
757       if (_anchor != Anchor::NOTE)
758             return;
759 
760       setScore(startNote->score());
761       setParent(startNote);
762       setStartElement(startNote);
763       setEndElement(endNote);
764       setTick(startNote->chord()->tick());
765       setTick2(endNote->chord()->tick());
766       setTrack(startNote->track());
767       setTrack2(endNote->track());
768       }
769 
770 //---------------------------------------------------------
771 //   startChord
772 //---------------------------------------------------------
773 
startChord()774 Chord* Spanner::startChord()
775       {
776       Q_ASSERT(_anchor == Anchor::CHORD);
777       if (!_startElement)
778             _startElement = score()->findCR(tick(), track());
779       return toChord(_startElement);
780       }
781 
782 //---------------------------------------------------------
783 //   endChord
784 //---------------------------------------------------------
785 
endChord()786 Chord* Spanner::endChord()
787       {
788       Q_ASSERT(_anchor == Anchor::CHORD);
789 
790       if (!_endElement && type() == ElementType::SLUR) {
791             Segment* s = score()->tick2segmentMM(tick2(), false, SegmentType::ChordRest);
792             _endElement = s ? toChordRest(s->element(track2())) : nullptr;
793             if (_endElement && !_endElement->isChord())
794                   _endElement = nullptr;
795             }
796       return toChord(_endElement);
797       }
798 
799 //---------------------------------------------------------
800 //   startCR
801 //---------------------------------------------------------
802 
startCR()803 ChordRest* Spanner::startCR()
804       {
805       Q_ASSERT(_anchor == Anchor::SEGMENT || _anchor == Anchor::CHORD);
806       if (!_startElement || _startElement->score() != score())
807             _startElement = score()->findCR(tick(), track());
808       return toChordRest(_startElement);
809       }
810 
811 //---------------------------------------------------------
812 //   endCR
813 //---------------------------------------------------------
814 
endCR()815 ChordRest* Spanner::endCR()
816       {
817       Q_ASSERT(_anchor == Anchor::SEGMENT || _anchor == Anchor::CHORD);
818       if ((!_endElement || _endElement->score() != score())) {
819             Segment* s  = score()->tick2segmentMM(tick2(), false, SegmentType::ChordRest);
820             const int tr2 = effectiveTrack2();
821             _endElement = s ? toChordRest(s->element(tr2)) : nullptr;
822             }
823       return toChordRest(_endElement);
824       }
825 
826 //---------------------------------------------------------
827 //   startSegment
828 //---------------------------------------------------------
829 
startSegment() const830 Segment* Spanner::startSegment() const
831       {
832       Q_ASSERT(score() != NULL);
833       return score()->tick2rightSegment(tick());
834       }
835 
836 //---------------------------------------------------------
837 //   endSegment
838 //---------------------------------------------------------
839 
endSegment() const840 Segment* Spanner::endSegment() const
841       {
842       return score()->tick2leftSegment(tick2());
843       }
844 
845 //---------------------------------------------------------
846 //   startMeasure
847 //---------------------------------------------------------
848 
startMeasure() const849 Measure* Spanner::startMeasure() const
850       {
851       return toMeasure(_startElement);
852       }
853 
854 //---------------------------------------------------------
855 //   endMeasure
856 //---------------------------------------------------------
857 
endMeasure() const858 Measure* Spanner::endMeasure() const
859       {
860       return toMeasure(_endElement);
861       }
862 
863 //---------------------------------------------------------
864 //   setSelected
865 //---------------------------------------------------------
866 
setSelected(bool f)867 void Spanner::setSelected(bool f)
868       {
869       for (SpannerSegment* ss : spannerSegments())
870             ss->Element::setSelected(f);
871       Element::setSelected(f);
872       }
873 
874 //---------------------------------------------------------
875 //   setVisible
876 //---------------------------------------------------------
877 
setVisible(bool f)878 void Spanner::setVisible(bool f)
879       {
880       for (SpannerSegment* ss : spannerSegments())
881             ss->Element::setVisible(f);
882       Element::setVisible(f);
883       }
884 
885 //---------------------------------------------------------
886 //   setAutoplace
887 //---------------------------------------------------------
888 
setAutoplace(bool f)889 void Spanner::setAutoplace(bool f)
890       {
891       for (SpannerSegment* ss : spannerSegments())
892             ss->Element::setAutoplace(f);
893       Element::setAutoplace(f);
894       }
895 
896 //---------------------------------------------------------
897 //   setColor
898 //---------------------------------------------------------
899 
setColor(const QColor & col)900 void Spanner::setColor(const QColor& col)
901       {
902       for (SpannerSegment* ss : spannerSegments())
903             ss->setColor(col);
904       _color = col;
905       }
906 
907 //---------------------------------------------------------
908 //   setStartElement
909 //---------------------------------------------------------
910 
setStartElement(Element * e)911 void Spanner::setStartElement(Element* e)
912       {
913 #ifndef NDEBUG
914       if (_anchor == Anchor::NOTE)
915             Q_ASSERT(!e || e->type() == ElementType::NOTE);
916 #endif
917       _startElement = e;
918       }
919 
920 //---------------------------------------------------------
921 //   setEndElement
922 //---------------------------------------------------------
923 
setEndElement(Element * e)924 void Spanner::setEndElement(Element* e)
925       {
926 #ifndef NDEBUG
927       if (_anchor == Anchor::NOTE)
928             Q_ASSERT(!e || e->type() == ElementType::NOTE);
929 #endif
930       _endElement = e;
931       }
932 
933 //---------------------------------------------------------
934 //   nextSpanner
935 //---------------------------------------------------------
936 
nextSpanner(Element * e,int activeStaff)937 Spanner* Spanner::nextSpanner(Element* e, int activeStaff)
938       {
939     std::multimap<int, Spanner*> mmap = score()->spanner();
940           auto range = mmap.equal_range(tick().ticks());
941           if (range.first != range.second) { // range not empty
942                 for (auto i = range.first; i != range.second; ++i) {
943                       if (i->second == e) {
944                             while (i != range.second) {
945                                   ++i;
946                                   if (i == range.second)
947                                         return nullptr;
948                                   Spanner* s =  i->second;
949                                   Element* st = s->startElement();
950                                   if (!st)
951                                         continue;
952                                   if (s->startSegment() == toSpanner(e)->startSegment()) {
953                                         if (st->staffIdx() == activeStaff)
954                                               return s;
955 #if 1
956                                         else if (st->isMeasure() && activeStaff == 0)
957                                               return s;
958 #else
959                                         // TODO: when navigating system spanners, check firstVisibleStaff()?
960                                         // currently, information about which staves are hidden
961                                         // is not exposed through navigation,
962                                         // so it may make more sense to continue to navigate systems elements
963                                         // only when actually on staff 0
964                                         // see also https://musescore.org/en/node/301496
965                                         // and https://github.com/musescore/MuseScore/pull/5755
966                                         else if (st->isMeasure()) {
967                                               SpannerSegment* ss = s->frontSegment();
968                                               int top = ss && ss->system() ? ss->system()->firstVisibleStaff() : 0;
969                                               if (activeStaff == top)
970                                                     return s;
971                                               }
972 #endif
973                                         }
974                                   //else
975                                         //return nullptr;
976                                   }
977                             break;
978                            /* else {
979                                   break;
980                                   }*/
981                             }
982                       }
983                  }
984           return nullptr;
985       }
986 
987 //---------------------------------------------------------
988 //   prevSpanner
989 //---------------------------------------------------------
990 
prevSpanner(Element * e,int activeStaff)991 Spanner* Spanner::prevSpanner(Element* e, int activeStaff)
992       {
993       std::multimap<int, Spanner*> mmap = score()->spanner();
994       auto range = mmap.equal_range(tick().ticks());
995       if (range.first != range.second) { // range not empty
996             for (auto i = range.first; i != range.second; ++i) {
997                   if (i->second == e) {
998                         if (i == range.first)
999                               return nullptr;
1000                         while (i != range.first) {
1001                               --i;
1002                               Spanner* s =  i->second;
1003                               Element* st = s->startElement();
1004                               if (s->startSegment() == toSpanner(e)->startSegment()) {
1005                                     if (st->staffIdx() == activeStaff)
1006                                           return s;
1007 #if 1
1008                                     else if (st->isMeasure() && activeStaff == 0)
1009                                           return s;
1010 #else
1011                                     // TODO: see nextSpanner()
1012                                     else if (st->isMeasure()) {
1013                                           SpannerSegment* ss = s->frontSegment();
1014                                           int top = ss && ss->system() ? ss->system()->firstVisibleStaff() : 0;
1015                                           if (activeStaff == top)
1016                                                 return s;
1017                                           }
1018 #endif
1019                                     }
1020                               }
1021                         break;
1022                         }
1023                   }
1024             }
1025       return nullptr;
1026       }
1027 
1028 //---------------------------------------------------------
1029 //   nextSegmentElement
1030 //---------------------------------------------------------
1031 
nextSegmentElement()1032 Element* Spanner::nextSegmentElement()
1033       {
1034       Segment* s = startSegment();
1035       if (s)
1036             return s->firstElement(staffIdx());
1037       return score()->lastElement();
1038       }
1039 
1040 //---------------------------------------------------------
1041 //   prevSegmentElement
1042 //---------------------------------------------------------
1043 
prevSegmentElement()1044 Element* Spanner::prevSegmentElement()
1045       {
1046       Segment* s = endSegment();
1047       if (s)
1048             return s->lastElement(staffIdx());
1049       return score()->firstElement();
1050       }
1051 
1052 //---------------------------------------------------------
1053 //   setTick
1054 //---------------------------------------------------------
1055 
setTick(const Fraction & v)1056 void Spanner::setTick(const Fraction& v)
1057       {
1058       _tick = v;
1059       if (score())
1060             score()->spannerMap().setDirty();
1061       }
1062 
1063 //---------------------------------------------------------
1064 //   setTick2
1065 //---------------------------------------------------------
1066 
setTick2(const Fraction & f)1067 void Spanner::setTick2(const Fraction& f)
1068       {
1069       setTicks(f - _tick);
1070       }
1071 
1072 //---------------------------------------------------------
1073 //   setTicks
1074 //---------------------------------------------------------
1075 
setTicks(const Fraction & f)1076 void Spanner::setTicks(const Fraction& f)
1077       {
1078       _ticks = f;
1079       if (score())
1080             score()->spannerMap().setDirty();
1081       }
1082 
1083 //---------------------------------------------------------
1084 //   triggerLayout
1085 //---------------------------------------------------------
1086 
triggerLayout() const1087 void Spanner::triggerLayout() const
1088       {
1089       // Spanners do not have parent even when added to a score, so can't check parent here
1090       const int tr2 = effectiveTrack2();
1091       score()->setLayout(_tick, _tick + _ticks, staffIdx(), track2staff(tr2), this);
1092       }
1093 
triggerLayoutAll() const1094 void Spanner::triggerLayoutAll() const
1095       {
1096       // Spanners do not have parent even when added to a score, so can't check parent here
1097       score()->setLayoutAll(staffIdx(), this);
1098 
1099       const int tr2 = track2();
1100       if (tr2 != -1 && tr2 != track())
1101             score()->setLayoutAll(track2staff(tr2), this);
1102       }
1103 
1104 //---------------------------------------------------------
1105 //   pushUnusedSegment
1106 //---------------------------------------------------------
1107 
pushUnusedSegment(SpannerSegment * seg)1108 void Spanner::pushUnusedSegment(SpannerSegment* seg)
1109       {
1110       if (!seg)
1111             return;
1112       seg->setSystem(nullptr);
1113       unusedSegments.push_back(seg);
1114       }
1115 
1116 //---------------------------------------------------------
1117 //   popUnusedSegment
1118 //    Take the next unused segment for reusing it.
1119 //    If there is no unused segments left returns nullptr.
1120 //---------------------------------------------------------
1121 
popUnusedSegment()1122 SpannerSegment* Spanner::popUnusedSegment()
1123       {
1124       if (unusedSegments.empty())
1125             return nullptr;
1126       SpannerSegment* seg = unusedSegments.front();
1127       unusedSegments.pop_front();
1128       return seg;
1129       }
1130 
1131 //---------------------------------------------------------
1132 //   reuse
1133 //    called when segment from unusedSegments is added
1134 //    back to the spanner.
1135 //---------------------------------------------------------
1136 
reuse(SpannerSegment * seg)1137 void Spanner::reuse(SpannerSegment* seg)
1138       {
1139       add(seg);
1140       }
1141 
1142 //---------------------------------------------------------
1143 //   reuseSegments
1144 //    Adds \p number segments from unusedSegments to this
1145 //    spanner via reuse() call. Returns number of new
1146 //    segments that still need to be created, that is,
1147 //    returns (number - nMovedSegments).
1148 //---------------------------------------------------------
1149 
reuseSegments(int number)1150 int Spanner::reuseSegments(int number)
1151       {
1152       while (number > 0) {
1153             SpannerSegment* seg = popUnusedSegment();
1154             if (!seg)
1155                   break;
1156             reuse(seg);
1157             --number;
1158             }
1159       return number;
1160       }
1161 
1162 //---------------------------------------------------------
1163 //   fixupSegments
1164 //    Makes number of segments match targetNumber.
1165 //    Tries to reuse unused segments. If there are no
1166 //    unused segments left, uses \p createSegment to create
1167 //    the needed segments.
1168 //    Previously unused segments are added via reuse() call
1169 //---------------------------------------------------------
1170 
fixupSegments(unsigned int targetNumber,std::function<SpannerSegment * ()> createSegment)1171 void Spanner::fixupSegments(unsigned int targetNumber, std::function<SpannerSegment*()> createSegment)
1172       {
1173       const int diff = targetNumber - int(nsegments());
1174       if (diff == 0)
1175             return;
1176       if (diff > 0) {
1177             const int ncreate = reuseSegments(diff);
1178             for (int i = 0; i < ncreate; ++i)
1179                   add(createSegment());
1180             }
1181       else { // diff < 0
1182             const int nremove = -diff;
1183             for (int i = 0; i < nremove; ++i) {
1184                   SpannerSegment* seg = segments.back();
1185                   segments.pop_back();
1186                   pushUnusedSegment(seg);
1187                   }
1188             }
1189       }
1190 
1191 //---------------------------------------------------------
1192 //   eraseSpannerSegments
1193 //    Completely erase all spanner segments, both used and
1194 //    unused.
1195 //---------------------------------------------------------
1196 
eraseSpannerSegments()1197 void Spanner::eraseSpannerSegments()
1198       {
1199       qDeleteAll(segments);
1200       qDeleteAll(unusedSegments);
1201       segments.clear();
1202       unusedSegments.clear();
1203       }
1204 
1205 //---------------------------------------------------------
1206 //   layoutSystem
1207 //---------------------------------------------------------
1208 
layoutSystem(System *)1209 SpannerSegment* Spanner::layoutSystem(System*)
1210       {
1211       qDebug(" %s", name());
1212       return 0;
1213       }
1214 
1215 //---------------------------------------------------------
1216 //   getNextLayoutSystemSegment
1217 //---------------------------------------------------------
1218 
getNextLayoutSystemSegment(System * system,std::function<SpannerSegment * ()> createSegment)1219 SpannerSegment* Spanner::getNextLayoutSystemSegment(System* system, std::function<SpannerSegment*()> createSegment)
1220       {
1221       SpannerSegment* seg = nullptr;
1222       for (SpannerSegment* ss : spannerSegments()) {
1223             if (!ss->system()) {
1224                   seg = ss;
1225                   break;
1226                   }
1227             }
1228       if (!seg) {
1229             if ((seg = popUnusedSegment()))
1230                   reuse(seg);
1231             else {
1232                   seg = createSegment();
1233                   Q_ASSERT(seg);
1234                   add(seg);
1235                   }
1236             }
1237       seg->setSystem(system);
1238       seg->setSpanner(this);
1239       seg->setTrack(track());
1240       seg->setVisible(visible());
1241       return seg;
1242       }
1243 
1244 //---------------------------------------------------------
1245 //   layoutSystemsDone
1246 //    Called after layout of all systems is done so precise
1247 //    number of systems for this spanner becomes available.
1248 //---------------------------------------------------------
1249 
layoutSystemsDone()1250 void Spanner::layoutSystemsDone()
1251       {
1252       std::vector<SpannerSegment*> validSegments;
1253       for (SpannerSegment* seg : segments) {
1254             if (seg->system())
1255                   validSegments.push_back(seg);
1256             else // TODO: score()->selection().remove(ss); needed?
1257                   pushUnusedSegment(seg);
1258             }
1259       segments = std::move(validSegments);
1260       }
1261 
1262 //--------------------------------------------------
1263 //   fraction
1264 //---------------------------------------------------------
1265 
fraction(const XmlWriter & xml,const Element * current,const Fraction & t)1266 static Fraction fraction(const XmlWriter& xml, const Element* current, const Fraction& t)
1267       {
1268       Fraction tick(t);
1269       if (!xml.clipboardmode()) {
1270             const Measure* m = toMeasure(current->findMeasure());
1271             if (m)
1272                   tick -= m->tick();
1273             }
1274       return tick;
1275       }
1276 
1277 //---------------------------------------------------------
1278 //   Spanner::readProperties
1279 //---------------------------------------------------------
1280 
readProperties(XmlReader & e)1281 bool Spanner::readProperties(XmlReader& e)
1282       {
1283       const QStringRef tag(e.name());
1284       if (e.pasteMode()) {
1285             if (tag == "ticks_f") {
1286                   setTicks(e.readFraction());
1287                   return true;
1288                   }
1289             }
1290       return Element::readProperties(e);
1291       }
1292 
1293 //---------------------------------------------------------
1294 //   Spanner::writeProperties
1295 //---------------------------------------------------------
1296 
writeProperties(XmlWriter & xml) const1297 void Spanner::writeProperties(XmlWriter& xml) const
1298       {
1299       if (xml.clipboardmode())
1300             xml.tag("ticks_f", ticks());
1301       Element::writeProperties(xml);
1302       }
1303 
1304 //--------------------------------------------------
1305 //   Spanner::writeSpannerStart
1306 //---------------------------------------------------------
1307 
writeSpannerStart(XmlWriter & xml,const Element * current,int track,Fraction tick) const1308 void Spanner::writeSpannerStart(XmlWriter& xml, const Element* current, int track, Fraction tick) const
1309       {
1310       Fraction frac = fraction(xml, current, tick);
1311       SpannerWriter w(xml, current, this, track, frac, true);
1312       w.write();
1313       }
1314 
1315 //--------------------------------------------------
1316 //   Spanner::writeSpannerEnd
1317 //---------------------------------------------------------
1318 
writeSpannerEnd(XmlWriter & xml,const Element * current,int track,Fraction tick) const1319 void Spanner::writeSpannerEnd(XmlWriter& xml, const Element* current, int track, Fraction tick) const
1320       {
1321       Fraction frac = fraction(xml, current, tick);
1322       SpannerWriter w(xml, current, this, track, frac, false);
1323       w.write();
1324       }
1325 
1326 //--------------------------------------------------
1327 //   Spanner::readSpanner
1328 //---------------------------------------------------------
1329 
readSpanner(XmlReader & e,Element * current,int track)1330 void Spanner::readSpanner(XmlReader& e, Element* current, int track)
1331       {
1332       std::unique_ptr<ConnectorInfoReader> info(new ConnectorInfoReader(e, current, track));
1333       ConnectorInfoReader::readConnector(std::move(info), e);
1334       }
1335 
1336 //--------------------------------------------------
1337 //   Spanner::readSpanner
1338 //---------------------------------------------------------
1339 
readSpanner(XmlReader & e,Score * current,int track)1340 void Spanner::readSpanner(XmlReader& e, Score* current, int track)
1341       {
1342       std::unique_ptr<ConnectorInfoReader> info(new ConnectorInfoReader(e, current, track));
1343       ConnectorInfoReader::readConnector(std::move(info), e);
1344       }
1345 
1346 //---------------------------------------------------------
1347 //   SpannerWriter::fillSpannerPosition
1348 //---------------------------------------------------------
1349 
fillSpannerPosition(Location & l,const MeasureBase * m,const Fraction & tick,bool clipboardmode)1350 void SpannerWriter::fillSpannerPosition(Location& l, const MeasureBase* m, const Fraction& tick, bool clipboardmode)
1351       {
1352       if (clipboardmode) {
1353             l.setMeasure(0);
1354             l.setFrac(tick);
1355             }
1356       else {
1357             if (!m) {
1358                   qWarning("fillSpannerPosition: couldn't find spanner's endpoint's measure");
1359                   l.setMeasure(0);
1360                   l.setFrac(tick);
1361                   return;
1362                   }
1363             l.setMeasure(m->measureIndex());
1364             l.setFrac(tick - m->tick());
1365             }
1366       }
1367 
1368 //---------------------------------------------------------
1369 //   SpannerWriter::SpannerWriter
1370 //---------------------------------------------------------
1371 
SpannerWriter(XmlWriter & xml,const Element * current,const Spanner * sp,int track,Fraction frac,bool start)1372 SpannerWriter::SpannerWriter(XmlWriter& xml, const Element* current, const Spanner* sp, int track, Fraction frac, bool start)
1373    : ConnectorInfoWriter(xml, current, sp, track, frac)
1374       {
1375       const bool clipboardmode = xml.clipboardmode();
1376       if (!sp->startElement() || !sp->endElement()) {
1377             qWarning("SpannerWriter: spanner (%s) doesn't have an endpoint!", sp->name());
1378             return;
1379             }
1380       if (current->isMeasure() || current->isSegment() || (sp->startElement()->type() != current->type())) {
1381             // (The latter is the hairpins' case, for example, though they are
1382             // covered by the other checks too.)
1383             // We cannot determine position of the spanner from its start/end
1384             // elements and will try to obtain this info from the spanner itself.
1385             if (!start) {
1386                   _prevLoc.setTrack(sp->track());
1387                   Measure* m = sp->score()->tick2measure(sp->tick());
1388                   fillSpannerPosition(_prevLoc, m, sp->tick(), clipboardmode);
1389                   }
1390             else {
1391                   const int track2 = (sp->track2() != -1) ? sp->track2() : sp->track();
1392                   _nextLoc.setTrack(track2);
1393                   Measure* m = sp->score()->tick2measure(sp->tick2());
1394                   fillSpannerPosition(_nextLoc, m, sp->tick2(), clipboardmode);
1395                   }
1396             }
1397       else {
1398             // We can obtain the spanner position info from its start/end
1399             // elements and will prefer this source of information.
1400             // Reason: some spanners contain no or wrong information (e.g. Ties).
1401             if (!start)
1402                   updateLocation(sp->startElement(), _prevLoc, clipboardmode);
1403             else
1404                   updateLocation(sp->endElement(), _nextLoc, clipboardmode);
1405             }
1406       }
1407 
1408 //---------------------------------------------------------
1409 //   autoplaceSpannerSegment
1410 //---------------------------------------------------------
1411 
autoplaceSpannerSegment()1412 void SpannerSegment::autoplaceSpannerSegment()
1413       {
1414       if (!parent()) {
1415             setOffset(QPointF());
1416             return;
1417             }
1418       if (isStyled(Pid::OFFSET))
1419             setOffset(spanner()->propertyDefault(Pid::OFFSET).toPointF());
1420 
1421       if (spanner()->anchor() == Spanner::Anchor::NOTE)
1422             return;
1423 
1424       // rebase vertical offset on drag
1425       qreal rebase = 0.0;
1426       if (offsetChanged() != OffsetChange::NONE)
1427             rebase = rebaseOffset();
1428 
1429       if (autoplace()) {
1430             qreal sp = score()->spatium();
1431             if (!systemFlag() && !spanner()->systemFlag())
1432                   sp *= staff()->mag(spanner()->tick());
1433             qreal md = minDistance().val() * sp;
1434             bool above = spanner()->placeAbove();
1435             SkylineLine sl(!above);
1436             Shape sh = shape();
1437             sl.add(sh.translated(pos()));
1438             qreal yd = 0.0;
1439             if (above) {
1440                   qreal d  = system()->topDistance(staffIdx(), sl);
1441                   if (d > -md)
1442                         yd = -(d + md);
1443                   }
1444             else {
1445                   qreal d  = system()->bottomDistance(staffIdx(), sl);
1446                   if (d > -md)
1447                         yd = d + md;
1448                   }
1449             if (yd != 0.0) {
1450                   if (offsetChanged() != OffsetChange::NONE) {
1451                         // user moved element within the skyline
1452                         // we may need to adjust minDistance, yd, and/or offset
1453                         qreal adj = pos().y() + rebase;
1454                         bool inStaff = above ? sh.bottom() + adj > 0.0 : sh.top() + adj < staff()->height();
1455                         rebaseMinDistance(md, yd, sp, rebase, above, inStaff);
1456                         }
1457                   rypos() += yd;
1458                   }
1459             }
1460       setOffsetChanged(false);
1461       }
1462 
1463 //---------------------------------------------------------
1464 //   undoChangeProperty
1465 //---------------------------------------------------------
1466 
undoChangeProperty(Pid id,const QVariant & v,PropertyFlags ps)1467 void Spanner::undoChangeProperty(Pid id, const QVariant& v, PropertyFlags ps)
1468       {
1469       if (id == Pid::PLACEMENT) {
1470             ScoreElement::undoChangeProperty(id, v, ps);
1471             // change offset of all segments if styled
1472 
1473             for (SpannerSegment* s : segments) {
1474                   if (s->isStyled(Pid::OFFSET)) {
1475                         s->setOffset(s->propertyDefault(Pid::OFFSET).toPointF());
1476                         s->triggerLayout();
1477                         }
1478                   }
1479             MuseScoreCore::mscoreCore->updateInspector();
1480             return;
1481             }
1482       Element::undoChangeProperty(id, v, ps);
1483       }
1484 
1485 }
1486 
1487