1 //=============================================================================
2 //  MuseScore
3 //  Music Composition & Notation
4 //
5 //  Copyright (C) 2002-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 /**
14  \file
15  Implementation of Element, ElementList
16 */
17 
18 #include "element.h"
19 #include "accidental.h"
20 #include "ambitus.h"
21 #include "arpeggio.h"
22 #include "articulation.h"
23 #include "bagpembell.h"
24 #include "barline.h"
25 #include "bend.h"
26 #include "box.h"
27 #include "bracket.h"
28 #include "breath.h"
29 #include "chord.h"
30 #include "chordline.h"
31 #include "chordrest.h"
32 #include "clef.h"
33 #include "connector.h"
34 #include "dynamic.h"
35 #include "figuredbass.h"
36 #include "fingering.h"
37 #include "fret.h"
38 #include "fraction.h"
39 #include "glissando.h"
40 #include "hairpin.h"
41 #include "harmony.h"
42 #include "icon.h"
43 #include "image.h"
44 #include "iname.h"
45 #include "instrchange.h"
46 #include "jump.h"
47 #include "keysig.h"
48 #include "layoutbreak.h"
49 #include "lyrics.h"
50 #include "marker.h"
51 #include "measure.h"
52 #include "mscore.h"
53 #include "notedot.h"
54 #include "note.h"
55 #include "noteline.h"
56 #include "ossia.h"
57 #include "ottava.h"
58 #include "page.h"
59 #include "pedal.h"
60 #include "rehearsalmark.h"
61 #include "repeat.h"
62 #include "rest.h"
63 #include "score.h"
64 #include "segment.h"
65 #include "slur.h"
66 #include "spacer.h"
67 #include "staff.h"
68 #include "staffstate.h"
69 #include "stafftext.h"
70 #include "systemtext.h"
71 #include "stafftype.h"
72 #include "stem.h"
73 #include "sticking.h"
74 #include "style.h"
75 #include "symbol.h"
76 #include "sym.h"
77 #include "system.h"
78 #include "tempotext.h"
79 #include "textframe.h"
80 #include "text.h"
81 #include "measurenumber.h"
82 #include "mmrestrange.h"
83 #include "textline.h"
84 #include "tie.h"
85 #include "timesig.h"
86 #include "tremolobar.h"
87 #include "tremolo.h"
88 #include "trill.h"
89 #include "undo.h"
90 #include "utils.h"
91 #include "volta.h"
92 #include "xml.h"
93 #include "systemdivider.h"
94 #include "stafftypechange.h"
95 #include "stafflines.h"
96 #include "letring.h"
97 #include "vibrato.h"
98 #include "palmmute.h"
99 #include "fermata.h"
100 #include "shape.h"
101 //#include "musescoreCore.h"
102 
103 namespace Ms {
104 
105 // extern bool showInvisible;
106 
107 //---------------------------------------------------------
108 //   spatiumChanged
109 //---------------------------------------------------------
110 
spatiumChanged(qreal oldValue,qreal newValue)111 void Element::spatiumChanged(qreal oldValue, qreal newValue)
112       {
113       if (offsetIsSpatiumDependent())
114             _offset *= (newValue / oldValue);
115       }
116 
117 //---------------------------------------------------------
118 //   localSpatiumChanged
119 //    the scale of a staff changed
120 //---------------------------------------------------------
121 
localSpatiumChanged(qreal oldValue,qreal newValue)122 void Element::localSpatiumChanged(qreal oldValue, qreal newValue)
123       {
124       if (offsetIsSpatiumDependent())
125             _offset *= (newValue / oldValue);
126       }
127 
128 //---------------------------------------------------------
129 //   spatium
130 //---------------------------------------------------------
131 
spatium() const132 qreal Element::spatium() const
133       {
134       if (systemFlag() || (parent() && parent()->systemFlag())) {
135             return score()->spatium();
136             }
137       else {
138             Staff* s = staff();
139             return s ? s->spatium(this) : score()->spatium();
140             }
141       }
142 
143 //---------------------------------------------------------
144 //   offsetIsSpatiumDependent
145 //---------------------------------------------------------
146 
offsetIsSpatiumDependent() const147 bool Element::offsetIsSpatiumDependent() const
148       {
149       return sizeIsSpatiumDependent() || (_flags & ElementFlag::ON_STAFF);
150       }
151 
152 //---------------------------------------------------------
153 //   magS
154 //---------------------------------------------------------
155 
magS() const156 qreal Element::magS() const
157       {
158       return mag() * (score()->spatium() / SPATIUM20);
159       }
160 
161 //---------------------------------------------------------
162 //   name
163 //---------------------------------------------------------
164 
subtypeName() const165 QString Element::subtypeName() const
166       {
167       return "";
168       }
169 
170 //---------------------------------------------------------
171 //   Element
172 //---------------------------------------------------------
173 
Element(Score * s,ElementFlags f)174 Element::Element(Score* s, ElementFlags f)
175    : ScoreElement(s)
176       {
177       _flags         = f;
178       _track         = -1;
179       _color         = MScore::defaultColor;
180       _mag           = 1.0;
181       _tag           = 1;
182       _z             = -1;
183       _offsetChanged = OffsetChange::NONE;
184       _minDistance   = Spatium(0.0);
185       }
186 
Element(const Element & e)187 Element::Element(const Element& e)
188    : ScoreElement(e)
189       {
190       _parent     = e._parent;
191       _bbox       = e._bbox;
192       _mag        = e._mag;
193       _pos        = e._pos;
194       _offset     = e._offset;
195       _track      = e._track;
196       _flags      = e._flags;
197       setFlag(ElementFlag::SELECTED, false);
198       _tag        = e._tag;
199       _z          = e._z;
200       _color      = e._color;
201       _offsetChanged = e._offsetChanged;
202       _minDistance   = e._minDistance;
203       itemDiscovered = false;
204       }
205 
206 //---------------------------------------------------------
207 //   ~Element
208 //---------------------------------------------------------
209 
~Element()210 Element::~Element()
211       {
212       Score::onElementDestruction(this);
213       }
214 
215 //---------------------------------------------------------
216 //   linkedClone
217 //---------------------------------------------------------
218 
linkedClone()219 Element* Element::linkedClone()
220       {
221       Element* e = clone();
222       e->setAutoplace(true);
223       score()->undo(new Link(e, this));
224       return e;
225       }
226 
227 //---------------------------------------------------------
228 //   deleteLater
229 //---------------------------------------------------------
230 
deleteLater()231 void Element::deleteLater()
232       {
233       if (selected())
234             score()->deselect(this);
235       masterScore()->deleteLater(this);
236       }
237 
238 //---------------------------------------------------------
239 //   scanElements
240 //---------------------------------------------------------
241 
scanElements(void * data,void (* func)(void *,Element *),bool all)242 void Element::scanElements(void* data, void (*func)(void*, Element*), bool all)
243       {
244       if (all || visible() || score()->showInvisible())
245             func(data, this);
246       }
247 
248 //---------------------------------------------------------
249 //   reset
250 //---------------------------------------------------------
251 
reset()252 void Element::reset()
253       {
254       undoResetProperty(Pid::AUTOPLACE);
255       undoResetProperty(Pid::PLACEMENT);
256       undoResetProperty(Pid::MIN_DISTANCE);
257       undoResetProperty(Pid::OFFSET);
258       setOffsetChanged(false);
259       ScoreElement::reset();
260       }
261 
262 //---------------------------------------------------------
263 //   change
264 //---------------------------------------------------------
265 
change(Element * o,Element * n)266 void Element::change(Element* o, Element* n)
267       {
268       remove(o);
269       add(n);
270       }
271 
272 //---------------------------------------------------------
273 //   staff
274 //---------------------------------------------------------
275 
staff() const276 Staff* Element::staff() const
277       {
278       if (_track == -1 || score()->staves().empty())
279             return 0;
280 
281       return score()->staff(_track >> 2);
282       }
283 
284 //---------------------------------------------------------
285 //   staffType
286 //---------------------------------------------------------
287 
staffType() const288 const StaffType* Element::staffType() const
289       {
290       Staff* s = staff();
291       return s ? s->staffTypeForElement(this) : nullptr;
292       }
293 
294 //---------------------------------------------------------
295 //   onTabStaff
296 //---------------------------------------------------------
297 
onTabStaff() const298 bool Element::onTabStaff() const
299       {
300       const StaffType* stt = staffType();
301       return stt ? stt->isTabStaff() : false;
302       }
303 
304 //---------------------------------------------------------
305 //   z
306 //---------------------------------------------------------
307 
z() const308 int Element::z() const
309       {
310       if (_z == -1)
311             _z = int(type()) * 100;
312       return _z;
313       }
314 
315 //---------------------------------------------------------
316 //   tick
317 //---------------------------------------------------------
318 
tick() const319 Fraction Element::tick() const
320       {
321       const Element* e = this;
322       while (e->parent()) {
323             if (e->parent()->isSegment())
324                   return toSegment(e->parent())->tick();
325             else if (e->parent()->isMeasureBase())
326                   return toMeasureBase(e->parent())->tick();
327             e = e->parent();
328             }
329       return Fraction(0, 1);
330       }
331 
332 //---------------------------------------------------------
333 //   rtick
334 //---------------------------------------------------------
335 
rtick() const336 Fraction Element::rtick() const
337       {
338       const Element* e = this;
339       while (e->parent()) {
340             if (e->parent()->isSegment())
341                   return toSegment(e->parent())->rtick();
342             e = e->parent();
343             }
344       return Fraction(0, 1);
345       }
346 
347 //---------------------------------------------------------
348 //   playTick
349 //---------------------------------------------------------
350 
playTick() const351 Fraction Element::playTick() const
352       {
353       // Play from the element's tick position by default.
354       return tick();
355       }
356 
357 
358 //---------------------------------------------------------
359 //   beat
360 //---------------------------------------------------------
361 
beat() const362 Fraction Element::beat() const
363       {
364       // Returns an appropriate fraction of ticks for use as a "Beat" reference
365       // in the Select All Similar filter.
366       int bar, beat, ticks;
367       TimeSigMap* tsm = score()->sigmap();
368       tsm->tickValues(tick().ticks(), &bar, &beat, &ticks);
369       int ticksB = ticks_beat(tsm->timesig(tick().ticks()).timesig().denominator());
370 
371       Fraction complexFraction((++beat * ticksB) + ticks, ticksB);
372       return complexFraction.reduced();
373       }
374 
375 //---------------------------------------------------------
376 //   part
377 //---------------------------------------------------------
378 
part() const379 Part* Element::part() const
380       {
381       Staff* s = staff();
382       return s ? s->part() : 0;
383       }
384 
385 //---------------------------------------------------------
386 //   curColor
387 //---------------------------------------------------------
388 
curColor() const389 QColor Element::curColor() const
390       {
391       return curColor(visible());
392       }
393 
394 //---------------------------------------------------------
395 //   curColor
396 //---------------------------------------------------------
397 
curColor(bool isVisible) const398 QColor Element::curColor(bool isVisible) const
399       {
400       return curColor(isVisible, color());
401       }
402 
curColor(bool isVisible,QColor normalColor) const403 QColor Element::curColor(bool isVisible, QColor normalColor) const
404       {
405       // the default element color is always interpreted as black in
406       // printing
407       if (score() && score()->printing())
408             return (normalColor == MScore::defaultColor) ? Qt::black : normalColor;
409 
410       if (flag(ElementFlag::DROP_TARGET))
411             return MScore::dropColor;
412       bool marked = false;
413       if (isNote()) {
414             //const Note* note = static_cast<const Note*>(this);
415             marked = toNote(this)->mark();
416             }
417       if (selected() || marked ) {
418             QColor originalColor;
419             if (track() == -1)
420                   originalColor = MScore::selectColor[0];
421             else
422                   originalColor = MScore::selectColor[voice()];
423             if (isVisible)
424                   return originalColor;
425             else {
426                   int red = originalColor.red();
427                   int green = originalColor.green();
428                   int blue = originalColor.blue();
429                   float tint = .6f;  // Between 0 and 1. Higher means lighter, lower means darker
430                   return QColor(red + tint * (255 - red), green + tint * (255 - green), blue + tint * (255 - blue));
431                   }
432             }
433       if (!isVisible)
434             return Qt::gray;
435       return normalColor;
436       }
437 
438 //---------------------------------------------------------
439 //   pagePos
440 //    return position in canvas coordinates
441 //---------------------------------------------------------
442 
pagePos() const443 QPointF Element::pagePos() const
444       {
445       QPointF p(pos());
446       if (parent() == 0)
447             return p;
448 
449       if (_flags & ElementFlag::ON_STAFF) {
450             System* system = 0;
451             Measure* measure = 0;
452             if (parent()->isSegment())
453                   measure = toSegment(parent())->measure();
454             else if (parent()->isMeasure())           // used in measure number
455                   measure = toMeasure(parent());
456             else if (parent()->isSystem())
457                   system = toSystem(parent());
458             else if (parent()->isFretDiagram())
459                   return p + parent()->pagePos();
460             else
461                   qFatal("this %s parent %s\n", name(), parent()->name());
462             if (measure) {
463                   system = measure->system();
464                   p.ry() += measure->staffLines(vStaffIdx())->y();
465                   }
466             if (system) {
467                   if (system->staves()->size() <= vStaffIdx()) {
468                         qDebug("staffIdx out of bounds: %s", name());
469                         }
470                   p.ry() += system->staffYpage(vStaffIdx());
471                   }
472             p.rx() = pageX();
473             }
474       else {
475             if (parent()->parent())
476                   p += parent()->pagePos();
477             }
478       return p;
479       }
480 
481 
482 //---------------------------------------------------------
483 //   canvasPos
484 //---------------------------------------------------------
485 
canvasPos() const486 QPointF Element::canvasPos() const
487       {
488       QPointF p(pos());
489       if (parent() == nullptr)
490             return p;
491 
492       if (_flags & ElementFlag::ON_STAFF) {
493             System* system = nullptr;
494             Measure* measure = nullptr;
495             if (parent()->isSegment())
496                   measure = toSegment(parent())->measure();
497             else if (parent()->isMeasure())     // used in measure number
498                   measure = toMeasure(parent());
499                   // system = toMeasure(parent())->system();
500             else if (parent()->isSystem())
501                   system = toSystem(parent());
502             else if (parent()->isChord())       // grace chord
503                   measure = toSegment(parent()->parent())->measure();
504             else if (parent()->isFretDiagram())
505                   return p + parent()->canvasPos() + QPointF(toFretDiagram(parent())->centerX(), 0.0);
506             else
507                   qFatal("this %s parent %s\n", name(), parent()->name());
508             if (measure) {
509                   p.ry() += measure->staffLines(vStaffIdx())->y();
510                   system = measure->system();
511                   if (system) {
512                         Page* page = system->page();
513                         if (page)
514                               p.ry() += page->y();
515                         }
516                   }
517             if (system)
518                   p.ry() += system->staffYpage(vStaffIdx());
519             p.rx() = canvasX();
520             }
521       else
522             p += parent()->canvasPos();
523       return p;
524       }
525 
526 //---------------------------------------------------------
527 //   pageX
528 //---------------------------------------------------------
529 
pageX() const530 qreal Element::pageX() const
531       {
532       qreal xp = x();
533       for (Element* e = parent(); e && e->parent(); e = e->parent())
534             xp += e->x();
535       return xp;
536       }
537 
538 //---------------------------------------------------------
539 //    canvasX
540 //---------------------------------------------------------
541 
canvasX() const542 qreal Element::canvasX() const
543       {
544       qreal xp = x();
545       for (Element* e = parent(); e; e = e->parent())
546             xp += e->x();
547       return xp;
548       }
549 
550 //---------------------------------------------------------
551 //   contains
552 //    Return true if p is inside the shape of the object.
553 //    Note: p is in page coordinates
554 //---------------------------------------------------------
555 
contains(const QPointF & p) const556 bool Element::contains(const QPointF& p) const
557       {
558       return shape().contains(p - pagePos());
559       }
560 
561 //---------------------------------------------------------
562 //  intersects
563 //    Return true if \a rr intersects bounding box of object.
564 //    Note: \a rr is in page coordinates
565 //---------------------------------------------------------
566 
intersects(const QRectF & rr) const567 bool Element::intersects(const QRectF& rr) const
568       {
569       return shape().intersects(rr.translated(-pagePos()));
570       }
571 
572 //---------------------------------------------------------
573 //   writeProperties
574 //---------------------------------------------------------
575 
writeProperties(XmlWriter & xml) const576 void Element::writeProperties(XmlWriter& xml) const
577       {
578       bool autoplaceEnabled = score()->styleB(Sid::autoplaceEnabled);
579       if (!autoplaceEnabled) {
580             score()->setStyleValue(Sid::autoplaceEnabled, true);
581             writeProperty(xml, Pid::AUTOPLACE);
582             score()->setStyleValue(Sid::autoplaceEnabled, autoplaceEnabled);
583             }
584       else {
585             writeProperty(xml, Pid::AUTOPLACE);
586             }
587 
588       // copy paste should not keep links
589       if (_links && (_links->size() > 1) && !xml.clipboardmode()) {
590             if (MScore::debugMode)
591                   xml.tag("lid", _links->lid());
592             Element* me = static_cast<Element*>(_links->mainElement());
593             Q_ASSERT(type() == me->type());
594             Staff* s = staff();
595             if (!s) {
596                   s = score()->staff(xml.curTrack() / VOICES);
597                   if (!s)
598                         qWarning("Element::writeProperties: linked element's staff not found (%s)", name());
599                   }
600             Location loc = Location::positionForElement(this);
601             if (me == this) {
602                   xml.tagE("linkedMain");
603                   xml.setLidLocalIndex(_links->lid(), xml.assignLocalIndex(loc));
604                   }
605             else {
606                   if (s->links()) {
607                         Staff* linkedStaff = toStaff(s->links()->mainElement());
608                         loc.setStaff(linkedStaff->idx());
609                         }
610                   xml.stag("linked");
611                   if (!me->score()->isMaster()) {
612                         if (me->score() == score()) {
613                               xml.tag("score", "same");
614                               }
615                         else {
616                               qWarning("Element::writeProperties: linked elements belong to different scores but none of them is master score: (%s lid=%d)", name(), _links->lid());
617                               }
618                         }
619                   Location mainLoc = Location::positionForElement(me);
620                   const int guessedLocalIndex = xml.assignLocalIndex(mainLoc);
621                   if (loc != mainLoc) {
622                         mainLoc.toRelative(loc);
623                         mainLoc.write(xml);
624                         }
625                   const int indexDiff = xml.lidLocalIndex(_links->lid()) - guessedLocalIndex;
626                   xml.tag("indexDiff", indexDiff, 0);
627                   xml.etag(); // </linked>
628                   }
629             }
630       if ((xml.writeTrack() || track() != xml.curTrack())
631          && (track() != -1) && !isBeam()) {
632             // Writing track number for beams is redundant as it is calculated
633             // during layout.
634             int t = track() + xml.trackDiff();
635             xml.tag("track", t);
636             }
637       if (xml.writePosition())
638             xml.tag(Pid::POSITION, rtick());
639       if (_tag != 0x1) {
640             for (int i = 1; i < MAX_TAGS; i++) {
641                   if (_tag == ((unsigned)1 << i)) {
642                         xml.tag("tag", score()->layerTags()[i]);
643                         break;
644                         }
645                   }
646             }
647       for (Pid pid : { Pid::OFFSET, Pid::COLOR, Pid::VISIBLE, Pid::Z, Pid::PLACEMENT}) {
648             if (propertyFlags(pid) == PropertyFlags::NOSTYLE)
649                   writeProperty(xml, pid);
650             }
651       }
652 
653 //---------------------------------------------------------
654 //   readProperties
655 //---------------------------------------------------------
656 
readProperties(XmlReader & e)657 bool Element::readProperties(XmlReader& e)
658       {
659       const QStringRef& tag(e.name());
660 
661       if (readProperty(tag, e, Pid::SIZE_SPATIUM_DEPENDENT))
662             ;
663       else if (readProperty(tag, e, Pid::OFFSET))
664             ;
665       else if (readProperty(tag, e, Pid::MIN_DISTANCE))
666             ;
667       else if (readProperty(tag, e, Pid::AUTOPLACE))
668             ;
669       else if (tag == "track")
670             setTrack(e.readInt() + e.trackOffset());
671       else if (tag == "color")
672             setColor(e.readColor());
673       else if (tag == "visible")
674             setVisible(e.readInt());
675       else if (tag == "selected") // obsolete
676             e.readInt();
677       else if ((tag == "linked") || (tag == "linkedMain")) {
678             Staff* s = staff();
679             if (!s) {
680                   s = score()->staff(e.track() / VOICES);
681                   if (!s) {
682                         qWarning("Element::readProperties: linked element's staff not found (%s)", name());
683                         e.skipCurrentElement();
684                         return true;
685                         }
686                   }
687             if (tag == "linkedMain") {
688                   _links = new LinkedElements(score());
689                   _links->push_back(this);
690                   e.addLink(s, _links);
691                   e.readNext();
692                   }
693             else {
694                   Staff* ls = s->links() ? toStaff(s->links()->mainElement()) : nullptr;
695                   bool linkedIsMaster = ls ? ls->score()->isMaster() : false;
696                   Location loc = e.location(true);
697                   if (ls)
698                         loc.setStaff(ls->idx());
699                   Location mainLoc = Location::relative();
700                   bool locationRead = false;
701                   int localIndexDiff = 0;
702                   while (e.readNextStartElement()) {
703                         const QStringRef& ntag(e.name());
704 
705                         if (ntag == "score") {
706                               QString val(e.readElementText());
707                               if (val == "same")
708                                     linkedIsMaster = score()->isMaster();
709                               }
710                         else if (ntag == "location") {
711                               mainLoc.read(e);
712                               mainLoc.toAbsolute(loc);
713                               locationRead = true;
714                               }
715                         else if (ntag == "indexDiff")
716                               localIndexDiff = e.readInt();
717                         else
718                               e.unknown();
719                         }
720                   if (!locationRead)
721                         mainLoc = loc;
722                   LinkedElements* link = e.getLink(linkedIsMaster, mainLoc, localIndexDiff);
723                   if (link) {
724                         ScoreElement* linked = link->mainElement();
725                         if (linked->type() == type())
726                               linkTo(linked);
727                         else
728                               qWarning("Element::readProperties: linked elements have different types: %s, %s. Input file corrupted?", name(), linked->name());
729                         }
730                   if (!_links)
731                         qWarning("Element::readProperties: could not link %s at staff %d", name(), mainLoc.staff() + 1);
732                   }
733             }
734       else if (tag == "lid") {
735             if (score()->mscVersion() >= 301) {
736                   e.skipCurrentElement();
737                   return true;
738                   }
739             int id = e.readInt();
740             _links = e.linkIds().value(id);
741             if (!_links) {
742                   if (!score()->isMaster())   // DEBUG
743                         qDebug("---link %d not found (%d)", id, e.linkIds().size());
744                   _links = new LinkedElements(score(), id);
745                   e.linkIds().insert(id, _links);
746                   }
747 #ifndef NDEBUG
748             else {
749                   for (ScoreElement* eee : *_links) {
750                         Element* ee = static_cast<Element*>(eee);
751                         if (ee->type() != type()) {
752                               qFatal("link %s(%d) type mismatch %s linked to %s",
753                                  ee->name(), id, ee->name(), name());
754                               }
755                         }
756                   }
757 #endif
758             Q_ASSERT(!_links->contains(this));
759             _links->append(this);
760             }
761       else if (tag == "tick") {
762             int val = e.readInt();
763             if (val >= 0)
764                   e.setTick(Fraction::fromTicks(score()->fileDivision(val)));       // obsolete
765             }
766       else if (tag == "pos")             // obsolete
767             readProperty(e, Pid::OFFSET);
768       else if (tag == "voice")
769             setTrack((_track/VOICES)*VOICES + e.readInt());
770       else if (tag == "tag") {
771             QString val(e.readElementText());
772             for (int i = 1; i < MAX_TAGS; i++) {
773                   if (score()->layerTags()[i] == val) {
774                         _tag = 1 << i;
775                         break;
776                         }
777                   }
778             }
779       else if (readProperty(tag, e, Pid::PLACEMENT))
780             ;
781       else if (tag == "z")
782             setZ(e.readInt());
783       else
784             return false;
785       return true;
786       }
787 
788 //---------------------------------------------------------
789 //   write
790 //---------------------------------------------------------
791 
write(XmlWriter & xml) const792 void Element::write(XmlWriter& xml) const
793       {
794       xml.stag(this);
795       writeProperties(xml);
796       xml.etag();
797       }
798 
799 //---------------------------------------------------------
800 //   read
801 //---------------------------------------------------------
802 
read(XmlReader & e)803 void Element::read(XmlReader& e)
804       {
805       while (e.readNextStartElement()) {
806             if (!readProperties(e))
807                   e.unknown();
808             }
809       }
810 
811 //---------------------------------------------------------
812 //   remove
813 ///   Remove \a el from the list. Return true on success.
814 //---------------------------------------------------------
815 
remove(Element * el)816 bool ElementList::remove(Element* el)
817       {
818       auto i = find(begin(), end(), el);
819       if (i == end())
820             return false;
821       erase(i);
822       return true;
823       }
824 
825 //---------------------------------------------------------
826 //   replace
827 //---------------------------------------------------------
828 
replace(Element * o,Element * n)829 void ElementList::replace(Element* o, Element* n)
830       {
831       auto i = find(begin(), end(), o);
832       if (i == end()) {
833             qDebug("ElementList::replace: element not found");
834             return;
835             }
836       *i = n;
837       }
838 
839 //---------------------------------------------------------
840 //   write
841 //---------------------------------------------------------
842 
write(XmlWriter & xml) const843 void ElementList::write(XmlWriter& xml) const
844       {
845       for (const Element* e : *this)
846             e->write(xml);
847       }
848 
849 //---------------------------------------------------------
850 //   Compound
851 //---------------------------------------------------------
852 
Compound(Score * s)853 Compound::Compound(Score* s)
854    : Element(s)
855       {
856       }
857 
Compound(const Compound & c)858 Compound::Compound(const Compound& c)
859    : Element(c)
860       {
861       elements.clear();
862       foreach(Element* e, c.elements)
863             elements.append(e->clone());
864       }
865 
866 //---------------------------------------------------------
867 //   draw
868 //---------------------------------------------------------
869 
draw(QPainter * painter) const870 void Compound::draw(QPainter* painter) const
871       {
872       foreach(Element* e, elements) {
873             QPointF pt(e->pos());
874             painter->translate(pt);
875             e->draw(painter);
876             painter->translate(-pt);
877             }
878       }
879 
880 //---------------------------------------------------------
881 //   addElement
882 //---------------------------------------------------------
883 
884 /**
885  offset \a x and \a y are in Point units
886 */
887 
addElement(Element * e,qreal x,qreal y)888 void Compound::addElement(Element* e, qreal x, qreal y)
889       {
890       e->setPos(x, y);
891       e->setParent(this);
892       elements.push_back(e);
893       }
894 
895 //---------------------------------------------------------
896 //   layout
897 //---------------------------------------------------------
898 
layout()899 void Compound::layout()
900       {
901       setbbox(QRectF());
902       for (auto i = elements.begin(); i != elements.end(); ++i) {
903             Element* e = *i;
904             e->layout();
905             addbbox(e->bbox().translated(e->pos()));
906             }
907       }
908 
909 //---------------------------------------------------------
910 //   setSelected
911 //---------------------------------------------------------
912 
setSelected(bool f)913 void Compound::setSelected(bool f)
914       {
915       Element::setSelected(f);
916       for (auto i = elements.begin(); i != elements.end(); ++i)
917             (*i)->setSelected(f);
918       }
919 
920 //---------------------------------------------------------
921 //   setVisible
922 //---------------------------------------------------------
923 
setVisible(bool f)924 void Compound::setVisible(bool f)
925       {
926       Element::setVisible(f);
927       for (auto i = elements.begin(); i != elements.end(); ++i)
928             (*i)->setVisible(f);
929       }
930 
931 //---------------------------------------------------------
932 //   clear
933 //---------------------------------------------------------
934 
clear()935 void Compound::clear()
936       {
937       foreach(Element* e, elements) {
938             if (e->selected())
939                   score()->deselect(e);
940             delete e;
941             }
942       elements.clear();
943       }
944 
945 //---------------------------------------------------------
946 //   dump
947 //---------------------------------------------------------
948 
dump() const949 void Element::dump() const
950       {
951       qDebug("---Element: %s, pos(%4.2f,%4.2f)"
952          "\n   bbox(%g,%g,%g,%g)"
953          "\n   abox(%g,%g,%g,%g)"
954          "\n  parent: %p",
955          name(), ipos().x(), ipos().y(),
956          _bbox.x(), _bbox.y(), _bbox.width(), _bbox.height(),
957          abbox().x(), abbox().y(), abbox().width(), abbox().height(),
958          parent());
959       }
960 
961 //---------------------------------------------------------
962 //   mimeData
963 //---------------------------------------------------------
964 
mimeData(const QPointF & dragOffset) const965 QByteArray Element::mimeData(const QPointF& dragOffset) const
966       {
967       QBuffer buffer;
968       buffer.open(QIODevice::WriteOnly);
969       XmlWriter xml(score(), &buffer);
970       xml.setClipboardmode(true);
971       xml.stag("Element");
972       if (isNote())
973             xml.tag("duration", toNote(this)->chord()->ticks());
974       if (!dragOffset.isNull())
975             xml.tag("dragOffset", dragOffset);
976       write(xml);
977       xml.etag();
978       buffer.close();
979       return buffer.buffer();
980       }
981 
982 //---------------------------------------------------------
983 //   readType
984 //    return new position of QDomElement in e
985 //---------------------------------------------------------
986 
readType(XmlReader & e,QPointF * dragOffset,Fraction * duration)987 ElementType Element::readType(XmlReader& e, QPointF* dragOffset,
988    Fraction* duration)
989       {
990       while (e.readNextStartElement()) {
991             if (e.name() == "Element")
992                   while (e.readNextStartElement()) {
993                         const QStringRef& tag = e.name();
994                         if (tag == "dragOffset")
995                               *dragOffset = e.readPoint();
996                         else if (tag == "duration")
997                               *duration = e.readFraction();
998                         else {
999                               ElementType type = name2type(tag);
1000                               if (type == ElementType::INVALID)
1001                                     break;
1002                               return type;
1003                         }
1004                   }
1005             else
1006                   e.unknown();
1007             }
1008       return ElementType::INVALID;
1009       }
1010 
1011 //---------------------------------------------------------
1012 //   readMimeData
1013 //---------------------------------------------------------
1014 
readMimeData(Score * score,const QByteArray & data,QPointF * dragOffset,Fraction * duration)1015 Element* Element::readMimeData(Score* score, const QByteArray& data, QPointF* dragOffset, Fraction* duration)
1016       {
1017       XmlReader e(data);
1018       const ElementType type = Element::readType(e, dragOffset, duration);
1019       e.setPasteMode(true);
1020 
1021       if (type == ElementType::INVALID) {
1022             qDebug("cannot read type");
1023             return nullptr;
1024             }
1025 
1026       Element* el = Element::create(type, score);
1027       if (el)
1028             el->read(e);
1029 
1030       return el;
1031       }
1032 
1033 //---------------------------------------------------------
1034 //   add
1035 //---------------------------------------------------------
1036 
add(Element * e)1037 void Element::add(Element* e)
1038       {
1039       qDebug("Element: cannot add %s to %s", e->name(), name());
1040       }
1041 
1042 //---------------------------------------------------------
1043 //   remove
1044 //---------------------------------------------------------
1045 
remove(Element * e)1046 void Element::remove(Element* e)
1047       {
1048       qFatal("Element: cannot remove %s from %s", e->name(), name());
1049       }
1050 
1051 //---------------------------------------------------------
1052 //   create
1053 //    Element factory
1054 //---------------------------------------------------------
1055 
create(ElementType type,Score * score)1056 Element* Element::create(ElementType type, Score* score)
1057       {
1058       switch (type) {
1059             case ElementType::VOLTA:             return new Volta(score);
1060             case ElementType::OTTAVA:            return new Ottava(score);
1061             case ElementType::TEXTLINE:          return new TextLine(score);
1062             case ElementType::NOTELINE:          return new NoteLine(score);
1063             case ElementType::TRILL:             return new Trill(score);
1064             case ElementType::LET_RING:          return new LetRing(score);
1065             case ElementType::VIBRATO:           return new Vibrato(score);
1066             case ElementType::PALM_MUTE:         return new PalmMute(score);
1067             case ElementType::PEDAL:             return new Pedal(score);
1068             case ElementType::HAIRPIN:           return new Hairpin(score);
1069             case ElementType::CLEF:              return new Clef(score);
1070             case ElementType::KEYSIG:            return new KeySig(score);
1071             case ElementType::TIMESIG:           return new TimeSig(score);
1072             case ElementType::BAR_LINE:          return new BarLine(score);
1073             case ElementType::SYSTEM_DIVIDER:    return new SystemDivider(score);
1074             case ElementType::ARPEGGIO:          return new Arpeggio(score);
1075             case ElementType::BREATH:            return new Breath(score);
1076             case ElementType::GLISSANDO:         return new Glissando(score);
1077             case ElementType::BRACKET:           return new Bracket(score);
1078             case ElementType::ARTICULATION:      return new Articulation(score);
1079             case ElementType::FERMATA:           return new Fermata(score);
1080             case ElementType::CHORDLINE:         return new ChordLine(score);
1081             case ElementType::ACCIDENTAL:        return new Accidental(score);
1082             case ElementType::DYNAMIC:           return new Dynamic(score);
1083             case ElementType::TEXT:              return new Text(score);
1084             case ElementType::MEASURE_NUMBER:    return new MeasureNumber(score);
1085             case ElementType::MMREST_RANGE:      return new MMRestRange(score);
1086             case ElementType::INSTRUMENT_NAME:   return new InstrumentName(score);
1087             case ElementType::STAFF_TEXT:        return new StaffText(score);
1088             case ElementType::SYSTEM_TEXT:       return new SystemText(score);
1089             case ElementType::REHEARSAL_MARK:    return new RehearsalMark(score);
1090             case ElementType::INSTRUMENT_CHANGE: return new InstrumentChange(score);
1091             case ElementType::STAFFTYPE_CHANGE:  return new StaffTypeChange(score);
1092             case ElementType::NOTEHEAD:          return new NoteHead(score);
1093             case ElementType::NOTEDOT:           return new NoteDot(score);
1094             case ElementType::TREMOLO:           return new Tremolo(score);
1095             case ElementType::LAYOUT_BREAK:      return new LayoutBreak(score);
1096             case ElementType::MARKER:            return new Marker(score);
1097             case ElementType::JUMP:              return new Jump(score);
1098             case ElementType::REPEAT_MEASURE:    return new RepeatMeasure(score);
1099             case ElementType::ICON:              return new Icon(score);
1100             case ElementType::NOTE:              return new Note(score);
1101             case ElementType::SYMBOL:            return new Symbol(score);
1102             case ElementType::FSYMBOL:           return new FSymbol(score);
1103             case ElementType::CHORD:             return new Chord(score);
1104             case ElementType::REST:              return new Rest(score);
1105             case ElementType::SPACER:            return new Spacer(score);
1106             case ElementType::STAFF_STATE:       return new StaffState(score);
1107             case ElementType::TEMPO_TEXT:        return new TempoText(score);
1108             case ElementType::HARMONY:           return new Harmony(score);
1109             case ElementType::FRET_DIAGRAM:      return new FretDiagram(score);
1110             case ElementType::BEND:              return new Bend(score);
1111             case ElementType::TREMOLOBAR:        return new TremoloBar(score);
1112             case ElementType::LYRICS:            return new Lyrics(score);
1113             case ElementType::FIGURED_BASS:      return new FiguredBass(score);
1114             case ElementType::STEM:              return new Stem(score);
1115             case ElementType::SLUR:              return new Slur(score);
1116             case ElementType::TIE:               return new Tie(score);
1117             case ElementType::FINGERING:          return new Fingering(score);
1118             case ElementType::HBOX:              return new HBox(score);
1119             case ElementType::VBOX:              return new VBox(score);
1120             case ElementType::TBOX:              return new TBox(score);
1121             case ElementType::FBOX:              return new FBox(score);
1122             case ElementType::MEASURE:           return new Measure(score);
1123             case ElementType::TAB_DURATION_SYMBOL: return new TabDurationSymbol(score);
1124             case ElementType::OSSIA:               return new Ossia(score);
1125             case ElementType::IMAGE:             return new Image(score);
1126             case ElementType::BAGPIPE_EMBELLISHMENT: return new BagpipeEmbellishment(score);
1127             case ElementType::AMBITUS:           return new Ambitus(score);
1128             case ElementType::STICKING:          return new Sticking(score);
1129 
1130             case ElementType::LYRICSLINE:
1131             case ElementType::TEXTLINE_BASE:
1132             case ElementType::TEXTLINE_SEGMENT:
1133             case ElementType::GLISSANDO_SEGMENT:
1134             case ElementType::SLUR_SEGMENT:
1135             case ElementType::TIE_SEGMENT:
1136             case ElementType::STEM_SLASH:
1137             case ElementType::PAGE:
1138             case ElementType::BEAM:
1139             case ElementType::HOOK:
1140             case ElementType::TUPLET:
1141             case ElementType::HAIRPIN_SEGMENT:
1142             case ElementType::OTTAVA_SEGMENT:
1143             case ElementType::TRILL_SEGMENT:
1144             case ElementType::LET_RING_SEGMENT:
1145             case ElementType::VIBRATO_SEGMENT:
1146             case ElementType::PALM_MUTE_SEGMENT:
1147             case ElementType::VOLTA_SEGMENT:
1148             case ElementType::PEDAL_SEGMENT:
1149             case ElementType::LYRICSLINE_SEGMENT:
1150             case ElementType::LEDGER_LINE:
1151             case ElementType::STAFF_LINES:
1152             case ElementType::SELECTION:
1153             case ElementType::LASSO:
1154             case ElementType::SHADOW_NOTE:
1155             case ElementType::SEGMENT:
1156             case ElementType::SYSTEM:
1157             case ElementType::COMPOUND:
1158             case ElementType::ELEMENT:
1159             case ElementType::ELEMENT_LIST:
1160             case ElementType::STAFF_LIST:
1161             case ElementType::MEASURE_LIST:
1162             case ElementType::MAXTYPE:
1163             case ElementType::INVALID:
1164             case ElementType::PART:
1165             case ElementType::STAFF:
1166             case ElementType::SCORE:
1167             case ElementType::BRACKET_ITEM:
1168                   break;
1169             }
1170       qDebug("cannot create type %d <%s>", int(type), Element::name(type));
1171       return 0;
1172       }
1173 
1174 //---------------------------------------------------------
1175 //   name2Element
1176 //---------------------------------------------------------
1177 
name2Element(const QStringRef & s,Score * sc)1178 Element* Element::name2Element(const QStringRef& s, Score* sc)
1179       {
1180       ElementType type = Element::name2type(s);
1181       if (type == ElementType::INVALID) {
1182             qDebug("invalid <%s>\n", qPrintable(s.toString()));
1183             return 0;
1184             }
1185       return Element::create(type, sc);
1186       }
1187 
1188 //---------------------------------------------------------
1189 //   elementLessThan
1190 //---------------------------------------------------------
1191 
elementLessThan(const Element * const e1,const Element * const e2)1192 bool elementLessThan(const Element* const e1, const Element* const e2)
1193       {
1194       if (e1->z() == e2->z()) {
1195             if (e1->selected())
1196                   return false;
1197             else if (e2->selected())
1198                   return true;
1199             else if (!e1->visible())
1200                   return true;
1201             else if (!e2->visible())
1202                   return false;
1203             else
1204                   return e1->track() > e2->track();
1205             }
1206       return e1->z() <= e2->z();
1207       }
1208 
1209 //---------------------------------------------------------
1210 //   collectElements
1211 //---------------------------------------------------------
1212 
collectElements(void * data,Element * e)1213 void collectElements(void* data, Element* e)
1214       {
1215       QList<Element*>* el = static_cast<QList<Element*>*>(data);
1216       el->append(e);
1217       }
1218 
1219 //---------------------------------------------------------
1220 //   autoplace
1221 //---------------------------------------------------------
1222 
autoplace() const1223 bool Element::autoplace() const
1224       {
1225       if (!score() || !score()->styleB(Sid::autoplaceEnabled))
1226           return false;
1227       return !flag(ElementFlag::NO_AUTOPLACE);
1228       }
1229 
1230 //---------------------------------------------------------
1231 //   getProperty
1232 //---------------------------------------------------------
1233 
getProperty(Pid propertyId) const1234 QVariant Element::getProperty(Pid propertyId) const
1235       {
1236       switch (propertyId) {
1237             case Pid::TICK:
1238                   return tick();
1239             case Pid::TRACK:
1240                   return track();
1241             case Pid::VOICE:
1242                   return voice();
1243             case Pid::POSITION:
1244                   return rtick();
1245             case Pid::GENERATED:
1246                   return generated();
1247             case Pid::COLOR:
1248                   return color();
1249             case Pid::VISIBLE:
1250                   return visible();
1251             case Pid::SELECTED:
1252                   return selected();
1253             case Pid::OFFSET:
1254                   return _offset;
1255             case Pid::MIN_DISTANCE:
1256                   return _minDistance;
1257             case Pid::PLACEMENT:
1258                   return int(placement());
1259             case Pid::AUTOPLACE:
1260                   return autoplace();
1261             case Pid::Z:
1262                   return z();
1263             case Pid::SYSTEM_FLAG:
1264                   return systemFlag();
1265             case Pid::SIZE_SPATIUM_DEPENDENT:
1266                   return sizeIsSpatiumDependent();
1267             default:
1268                   return QVariant();
1269             }
1270       }
1271 
1272 //---------------------------------------------------------
1273 //   setProperty
1274 //---------------------------------------------------------
1275 
setProperty(Pid propertyId,const QVariant & v)1276 bool Element::setProperty(Pid propertyId, const QVariant& v)
1277       {
1278       switch (propertyId) {
1279             case Pid::TRACK:
1280                   setTrack(v.toInt());
1281                   break;
1282             case Pid::VOICE:
1283                   setVoice(v.toInt());
1284                   break;
1285             case Pid::GENERATED:
1286                   setGenerated(v.toBool());
1287                   break;
1288             case Pid::COLOR:
1289                   setColor(v.value<QColor>());
1290                   break;
1291             case Pid::VISIBLE:
1292                   setVisible(v.toBool());
1293                   break;
1294             case Pid::SELECTED:
1295                   setSelected(v.toBool());
1296                   break;
1297             case Pid::OFFSET:
1298                   _offset = v.toPointF();
1299                   break;
1300             case Pid::MIN_DISTANCE:
1301                   setMinDistance(v.value<Spatium>());
1302                   break;
1303             case Pid::PLACEMENT:
1304                   setPlacement(Placement(v.toInt()));
1305                   break;
1306             case Pid::AUTOPLACE:
1307                   setAutoplace(v.toBool());
1308                   break;
1309             case Pid::Z:
1310                   setZ(v.toInt());
1311                   break;
1312             case Pid::SYSTEM_FLAG:
1313                   setSystemFlag(v.toBool());
1314                   break;
1315             case Pid::SIZE_SPATIUM_DEPENDENT:
1316                   setSizeIsSpatiumDependent(v.toBool());
1317                   break;
1318             default:
1319                   qDebug("%s unknown <%s>(%d), data <%s>", name(), propertyName(propertyId), int(propertyId), qPrintable(v.toString()));
1320                   return false;
1321             }
1322       triggerLayout();
1323       return true;
1324       }
1325 
1326 //---------------------------------------------------------
1327 //   undoChangeProperty
1328 //---------------------------------------------------------
1329 
undoChangeProperty(Pid pid,const QVariant & val,PropertyFlags ps)1330 void Element::undoChangeProperty(Pid pid, const QVariant& val, PropertyFlags ps)
1331       {
1332       if (pid == Pid::AUTOPLACE && (val.toBool() == true && !autoplace())) {
1333             // Switching autoplacement on. Save user-defined
1334             // placement properties to undo stack.
1335             undoPushProperty(Pid::PLACEMENT);
1336             undoPushProperty(Pid::OFFSET);
1337             }
1338       ScoreElement::undoChangeProperty(pid, val, ps);
1339       }
1340 
1341 //---------------------------------------------------------
1342 //   propertyDefault
1343 //---------------------------------------------------------
1344 
propertyDefault(Pid pid) const1345 QVariant Element::propertyDefault(Pid pid) const
1346       {
1347       switch (pid) {
1348             case Pid::GENERATED:
1349                   return false;
1350             case Pid::VISIBLE:
1351                   return true;
1352             case Pid::COLOR:
1353                   return MScore::defaultColor;
1354             case Pid::PLACEMENT: {
1355                   QVariant v = ScoreElement::propertyDefault(pid);
1356                   if (v.isValid())        // if it's a styled property
1357                         return v;
1358                   return int(Placement::BELOW);
1359                   }
1360             case Pid::SELECTED:
1361                   return false;
1362             case Pid::OFFSET: {
1363                   QVariant v = ScoreElement::propertyDefault(pid);
1364                   if (v.isValid())        // if it's a styled property
1365                         return v;
1366                   return QPointF();
1367                   }
1368             case Pid::MIN_DISTANCE: {
1369                   QVariant v = ScoreElement::propertyDefault(pid);
1370                   if (v.isValid())
1371                         return v;
1372                   return 0.0;
1373                   }
1374             case Pid::AUTOPLACE:
1375                   return true;
1376             case Pid::Z:
1377                   return int(type()) * 100;
1378             default:
1379                   return ScoreElement::propertyDefault(pid);
1380             }
1381       }
1382 
1383 //---------------------------------------------------------
1384 //   propertyId
1385 //---------------------------------------------------------
1386 
propertyId(const QStringRef & name) const1387 Pid Element::propertyId(const QStringRef& name) const
1388       {
1389       if (name == "pos" || name == "offset")
1390             return Pid::OFFSET;
1391       return ScoreElement::propertyId(name);
1392       }
1393 
1394 //---------------------------------------------------------
1395 //   propertyUserValue
1396 //---------------------------------------------------------
1397 
propertyUserValue(Pid pid) const1398 QString Element::propertyUserValue(Pid pid) const
1399       {
1400       switch(pid) {
1401             case Pid::SUBTYPE:
1402                   return subtypeName();
1403             default:
1404                   return ScoreElement::propertyUserValue(pid);
1405             }
1406       }
1407 
1408 //---------------------------------------------------------
1409 //   custom
1410 //    check if property is != default
1411 //---------------------------------------------------------
1412 
custom(Pid id) const1413 bool Element::custom(Pid id) const
1414       {
1415       return propertyDefault(id) != getProperty(id);
1416       }
1417 
1418 //---------------------------------------------------------
1419 //   isPrintable
1420 //---------------------------------------------------------
1421 
isPrintable() const1422 bool Element::isPrintable() const
1423       {
1424       switch (type()) {
1425             case ElementType::PAGE:
1426             case ElementType::SYSTEM:
1427             case ElementType::MEASURE:
1428             case ElementType::SEGMENT:
1429             case ElementType::VBOX:
1430             case ElementType::HBOX:
1431             case ElementType::TBOX:
1432             case ElementType::FBOX:
1433             case ElementType::SPACER:
1434             case ElementType::SHADOW_NOTE:
1435             case ElementType::LASSO:
1436             case ElementType::ELEMENT_LIST:
1437             case ElementType::STAFF_LIST:
1438             case ElementType::MEASURE_LIST:
1439             case ElementType::SELECTION:
1440                   return false;
1441             default:
1442                   return true;
1443             }
1444       }
1445 
1446 //---------------------------------------------------------
1447 //   findAncestor
1448 //---------------------------------------------------------
1449 
findAncestor(ElementType t)1450 Element* Element::findAncestor(ElementType t)
1451       {
1452       Element* e = this;
1453       while (e && e->type() != t)
1454             e = e->parent();
1455       return e;
1456       }
1457 
findAncestor(ElementType t) const1458 const Element* Element::findAncestor(ElementType t) const
1459       {
1460       const Element* e = this;
1461       while (e && e->type() != t)
1462             e = e->parent();
1463       return e;
1464       }
1465 
1466 //---------------------------------------------------------
1467 //   findMeasure
1468 //---------------------------------------------------------
1469 
findMeasure()1470 Measure* Element::findMeasure()
1471       {
1472       if (isMeasure())
1473             return toMeasure(this);
1474       else if (_parent)
1475             return _parent->findMeasure();
1476       else
1477             return 0;
1478       }
1479 
1480 //---------------------------------------------------------
1481 //   findMeasure
1482 //---------------------------------------------------------
1483 
findMeasure() const1484 const Measure* Element::findMeasure() const
1485       {
1486       Element* e = const_cast<Element*>(this);
1487       return e->findMeasure();
1488       }
1489 
1490 //---------------------------------------------------------
1491 //   findMeasureBase
1492 //---------------------------------------------------------
1493 
findMeasureBase()1494 MeasureBase* Element::findMeasureBase()
1495       {
1496       if (isMeasureBase())
1497             return toMeasureBase(this);
1498       else if (_parent)
1499             return _parent->findMeasureBase();
1500       else
1501             return 0;
1502       }
1503 
1504 //---------------------------------------------------------
1505 //   findMeasureBase
1506 //---------------------------------------------------------
1507 
findMeasureBase() const1508 const MeasureBase* Element::findMeasureBase() const
1509       {
1510       Element* e = const_cast<Element*>(this);
1511       return e->findMeasureBase();
1512       }
1513 
1514 //---------------------------------------------------------
1515 //   undoSetColor
1516 //---------------------------------------------------------
1517 
undoSetColor(const QColor & c)1518 void Element::undoSetColor(const QColor& c)
1519       {
1520       undoChangeProperty(Pid::COLOR, c);
1521       }
1522 
1523 //---------------------------------------------------------
1524 //   undoSetVisible
1525 //---------------------------------------------------------
1526 
undoSetVisible(bool v)1527 void Element::undoSetVisible(bool v)
1528       {
1529       undoChangeProperty(Pid::VISIBLE, v);
1530       }
1531 
1532 //---------------------------------------------------------
1533 //   drawSymbol
1534 //---------------------------------------------------------
1535 
drawSymbol(SymId id,QPainter * p,const QPointF & o,qreal scale) const1536 void Element::drawSymbol(SymId id, QPainter* p, const QPointF& o, qreal scale) const
1537       {
1538       score()->scoreFont()->draw(id, p, magS() * scale, o);
1539       }
1540 
drawSymbol(SymId id,QPainter * p,const QPointF & o,int n) const1541 void Element::drawSymbol(SymId id, QPainter* p, const QPointF& o, int n) const
1542       {
1543       score()->scoreFont()->draw(id, p, magS(), o, n);
1544       }
1545 
drawSymbols(const std::vector<SymId> & s,QPainter * p,const QPointF & o,qreal scale) const1546 void Element::drawSymbols(const std::vector<SymId>& s, QPainter* p, const QPointF& o, qreal scale) const
1547       {
1548       score()->scoreFont()->draw(s, p, magS() * scale, o);
1549       }
1550 
drawSymbols(const std::vector<SymId> & s,QPainter * p,const QPointF & o,const QSizeF & scale) const1551 void Element::drawSymbols(const std::vector<SymId>& s, QPainter* p, const QPointF& o, const QSizeF& scale) const
1552       {
1553       score()->scoreFont()->draw(s, p, magS() * scale, o);
1554       }
1555 
1556 //---------------------------------------------------------
1557 //   symHeight
1558 //---------------------------------------------------------
1559 
symHeight(SymId id) const1560 qreal Element::symHeight(SymId id) const
1561       {
1562       return score()->scoreFont()->height(id, magS());
1563       }
1564 
1565 //---------------------------------------------------------
1566 //   symWidth
1567 //---------------------------------------------------------
1568 
symWidth(SymId id) const1569 qreal Element::symWidth(SymId id) const
1570       {
1571       return score()->scoreFont()->width(id, magS());
1572       }
symWidth(const std::vector<SymId> & s) const1573 qreal Element::symWidth(const std::vector<SymId>& s) const
1574       {
1575       return score()->scoreFont()->width(s, magS());
1576       }
1577 
1578 //---------------------------------------------------------
1579 //   symAdvance
1580 //---------------------------------------------------------
1581 
symAdvance(SymId id) const1582 qreal Element::symAdvance(SymId id) const
1583       {
1584       return score()->scoreFont()->advance(id, magS());
1585       }
1586 
1587 //---------------------------------------------------------
1588 //   symBbox
1589 //---------------------------------------------------------
1590 
symBbox(SymId id) const1591 QRectF Element::symBbox(SymId id) const
1592       {
1593       return score()->scoreFont()->bbox(id, magS());
1594       }
1595 
symBbox(const std::vector<SymId> & s) const1596 QRectF Element::symBbox(const std::vector<SymId>& s) const
1597       {
1598       return score()->scoreFont()->bbox(s, magS());
1599       }
1600 
1601 //---------------------------------------------------------
1602 //   symStemDownNW
1603 //---------------------------------------------------------
1604 
symStemDownNW(SymId id) const1605 QPointF Element::symStemDownNW(SymId id) const
1606       {
1607       return score()->scoreFont()->stemDownNW(id, magS());
1608       }
1609 
1610 //---------------------------------------------------------
1611 //   symStemUpSE
1612 //---------------------------------------------------------
1613 
symStemUpSE(SymId id) const1614 QPointF Element::symStemUpSE(SymId id) const
1615       {
1616       return score()->scoreFont()->stemUpSE(id, magS());
1617       }
1618 
1619 //---------------------------------------------------------
1620 //   symStemDownSW
1621 //---------------------------------------------------------
1622 
symStemDownSW(SymId id) const1623 QPointF Element::symStemDownSW(SymId id) const
1624       {
1625       return score()->scoreFont()->stemDownSW(id, magS());
1626       }
1627 
1628 //---------------------------------------------------------
1629 //   symStemUpNW
1630 //---------------------------------------------------------
1631 
symStemUpNW(SymId id) const1632 QPointF Element::symStemUpNW(SymId id) const
1633       {
1634       return score()->scoreFont()->stemUpNW(id, magS());
1635       }
1636 
1637 //---------------------------------------------------------
1638 //   symCutOutNE / symCutOutNW / symCutOutSE / symCutOutNW
1639 //---------------------------------------------------------
1640 
symCutOutNE(SymId id) const1641 QPointF Element::symCutOutNE(SymId id) const
1642       {
1643       return score()->scoreFont()->cutOutNE(id, magS());
1644       }
1645 
symCutOutNW(SymId id) const1646 QPointF Element::symCutOutNW(SymId id) const
1647       {
1648       return score()->scoreFont()->cutOutNW(id, magS());
1649       }
1650 
symCutOutSE(SymId id) const1651 QPointF Element::symCutOutSE(SymId id) const
1652       {
1653       return score()->scoreFont()->cutOutSE(id, magS());
1654       }
1655 
symCutOutSW(SymId id) const1656 QPointF Element::symCutOutSW(SymId id) const
1657       {
1658       return score()->scoreFont()->cutOutSW(id, magS());
1659       }
1660 
1661 //---------------------------------------------------------
1662 //   symIsValid
1663 //---------------------------------------------------------
1664 
symIsValid(SymId id) const1665 bool Element::symIsValid(SymId id) const
1666       {
1667       return score()->scoreFont()->isValid(id);
1668       }
1669 
1670 //---------------------------------------------------------
1671 //   concertPitch
1672 //---------------------------------------------------------
1673 
concertPitch() const1674 bool Element::concertPitch() const
1675       {
1676       return score()->styleB(Sid::concertPitch);
1677       }
1678 //---------------------------------------------------------
1679 //   nextElement
1680 //   selects the next score element
1681 //---------------------------------------------------------
1682 
nextElement()1683 Element* Element::nextElement()
1684       {
1685       Element* e = score()->selection().element();
1686       if (!e && !score()->selection().elements().isEmpty())
1687             e = score()->selection().elements().first();
1688       if (e) {
1689             switch (e->type()) {
1690                   case ElementType::SEGMENT: {
1691                         Segment* s = toSegment(e);
1692                         return s->nextElement(staffIdx());
1693                         }
1694                   case ElementType::MEASURE: {
1695                         Measure* m = toMeasure(e);
1696                         return m->nextElementStaff(staffIdx());
1697                         }
1698                   case ElementType::CLEF:
1699                   case ElementType::KEYSIG:
1700                   case ElementType::TIMESIG:
1701                   case ElementType::BAR_LINE:
1702                         return nextSegmentElement();
1703                   default: {
1704                         return e->parent()->nextElement();
1705                         }
1706                   }
1707             }
1708       return nullptr;
1709       }
1710 
1711 
1712 //---------------------------------------------------------
1713 //   prevElement
1714 //   selects the previous score element
1715 //---------------------------------------------------------
1716 
prevElement()1717 Element* Element::prevElement()
1718       {
1719       Element* e = score()->selection().element();
1720       if (!e && !score()->selection().elements().isEmpty() )
1721             e = score()->selection().elements().last();
1722       if (e) {
1723             switch(e->type()) {
1724                   case ElementType::SEGMENT: {
1725                         Segment* s = toSegment(e);
1726                         return s->prevElement(staffIdx());
1727                         }
1728                   case ElementType::MEASURE: {
1729                         Measure* m = toMeasure(e);
1730                         return m->prevElementStaff(staffIdx());
1731                         }
1732                   case ElementType::CLEF:
1733                   case ElementType::KEYSIG:
1734                   case ElementType::TIMESIG:
1735                   case ElementType::BAR_LINE:
1736                         return prevSegmentElement();
1737                   default: {
1738                         return e->parent()->prevElement();
1739                         }
1740                   }
1741             }
1742       return nullptr;
1743       }
1744 
1745 
1746 //------------------------------------------------------------------------------------------
1747 //   nextSegmentElement
1748 //   This function is used in for the next-element command to navigate between main elements
1749 //   of segments. (Note, Rest, Clef, Time Signature, Key Signature, Barline, Ambitus, Breath, etc.)
1750 //   The default implementation is to look for the first such element. After it is found each
1751 //   element knows how to find the next one and overrides this method
1752 //------------------------------------------------------------------------------------------
1753 
nextSegmentElement()1754 Element* Element::nextSegmentElement()
1755       {
1756       Element* p = this;
1757       while (p) {
1758             switch (p->type()) {
1759                   case ElementType::NOTE:
1760                         if (toNote(p)->chord()->isGrace())
1761                               break;
1762                         return p;
1763                   case ElementType::REST:
1764                         return p;
1765                   case ElementType::CHORD: {
1766                         Chord* c = toChord(p);
1767                         if (!c->isGrace())
1768                               return c->notes().back();
1769                         }
1770                         break;
1771                   case ElementType::SEGMENT: {
1772                         Segment* s = toSegment(p);
1773                         return s->firstElement(staffIdx());
1774                         }
1775                   case ElementType::MEASURE: {
1776                         Measure* m = toMeasure(p);
1777                         return m->nextElementStaff(staffIdx());
1778                         }
1779                   case ElementType::SYSTEM: {
1780                         System* sys = toSystem(p);
1781                         return sys->nextSegmentElement();
1782                         }
1783                   default:
1784                         break;
1785                   }
1786             p = p->parent();
1787             }
1788       return score()->firstElement();
1789       }
1790 
1791 //------------------------------------------------------------------------------------------
1792 //   prevSegmentElement
1793 //   This function is used in for the prev-element command to navigate between main elements
1794 //   of segments. (Note, Rest, Clef, Time Signature, Key Signature, Barline, Ambitus, Breath, etc.)
1795 //   The default implementation is to look for the first such element. After it is found each
1796 //   element knows how to find the previous one and overrides this method
1797 //------------------------------------------------------------------------------------------
1798 
prevSegmentElement()1799 Element* Element::prevSegmentElement()
1800       {
1801       Element* p = this;
1802       while (p) {
1803             switch (p->type()) {
1804                   case ElementType::NOTE:
1805                         if (toNote(p)->chord()->isGrace())
1806                               break;
1807                         return p;
1808                   case ElementType::REST:
1809                         return p;
1810                   case ElementType::CHORD: {
1811                         Chord* c = toChord(p);
1812                         if (!c->isGrace())
1813                               return c->notes().front();
1814                         }
1815                         break;
1816                   case ElementType::SEGMENT: {
1817                         Segment* s = toSegment(p);
1818                         return s->lastElement(staffIdx());
1819                         }
1820                   case ElementType::MEASURE: {
1821                         Measure* m = toMeasure(p);
1822                         return m->prevElementStaff(staffIdx());
1823                         }
1824                   case ElementType::SYSTEM: {
1825                         System* sys = toSystem(p);
1826                         return sys->prevSegmentElement();
1827                         }
1828                   default:
1829                         break;
1830                   }
1831             p = p->parent();
1832             }
1833       return score()->firstElement();
1834       }
1835 
1836 //---------------------------------------------------------
1837 //   accessibleInfo
1838 //---------------------------------------------------------
1839 
accessibleInfo() const1840 QString Element::accessibleInfo() const
1841       {
1842       return Element::userName();
1843       }
1844 
1845 //---------------------------------------------------------
1846 //   nextGrip
1847 //---------------------------------------------------------
1848 
nextGrip(EditData & ed) const1849 bool Element::nextGrip(EditData& ed) const
1850       {
1851       int i = int(ed.curGrip) + 1;
1852       if (i >= ed.grips) {
1853             ed.curGrip = Grip(0);
1854             return false;
1855             }
1856       ed.curGrip = Grip(i);
1857       return true;
1858       }
1859 
1860 //---------------------------------------------------------
1861 //   prevGrip
1862 //---------------------------------------------------------
1863 
prevGrip(EditData & ed) const1864 bool Element::prevGrip(EditData& ed) const
1865       {
1866       int i = int(ed.curGrip) - 1;
1867       if (i < 0) {
1868             ed.curGrip = Grip(ed.grips - 1);
1869             return false;
1870             }
1871       ed.curGrip = Grip(i);
1872       return true;
1873       }
1874 
1875 //---------------------------------------------------------
1876 //   isUserModified
1877 //    Check if this element was modified by user and
1878 //    therefore must be saved.
1879 //---------------------------------------------------------
1880 
isUserModified() const1881 bool Element::isUserModified() const
1882       {
1883       for (const StyledProperty& spp : *styledProperties()) {
1884             Pid pid               = spp.pid;
1885             QVariant val          = getProperty(pid);
1886             QVariant defaultValue = propertyDefault(pid);
1887 
1888             if (propertyType(pid) == P_TYPE::SP_REAL) {
1889                   if (qAbs(val.toReal() - defaultValue.toReal()) > 0.0001)    // we don’t care spatium diffs that small
1890                         return true;
1891                   }
1892             else  {
1893                   if (getProperty(pid) != propertyDefault(pid))
1894                         return true;
1895                   }
1896             }
1897       for (Pid p : {Pid::VISIBLE, Pid::OFFSET, Pid::COLOR, Pid::Z, Pid::AUTOPLACE}) {
1898             if (getProperty(p) != propertyDefault(p))
1899                   return true;
1900             }
1901       return false;
1902       }
1903 
1904 //---------------------------------------------------------
1905 //   triggerLayout
1906 //---------------------------------------------------------
1907 
triggerLayout() const1908 void Element::triggerLayout() const
1909       {
1910       if (parent())
1911             score()->setLayout(tick(), staffIdx(), this);
1912       }
1913 
1914 //---------------------------------------------------------
1915 //   triggerLayoutAll
1916 //---------------------------------------------------------
1917 
triggerLayoutAll() const1918 void Element::triggerLayoutAll() const
1919       {
1920       if (parent())
1921             score()->setLayoutAll(staffIdx(), this);
1922       }
1923 
1924 //---------------------------------------------------------
1925 //   control
1926 //---------------------------------------------------------
1927 
control(bool textEditing) const1928 bool EditData::control(bool textEditing) const
1929       {
1930       if (textEditing)
1931             return modifiers & CONTROL_MODIFIER;
1932       else
1933             return modifiers & Qt::ControlModifier;
1934       }
1935 
1936 //---------------------------------------------------------
1937 //   clearData
1938 //---------------------------------------------------------
1939 
clearData()1940 void EditData::clearData()
1941       {
1942       qDeleteAll(data);
1943       data.clear();
1944       }
1945 
1946 //---------------------------------------------------------
1947 //   getData
1948 //---------------------------------------------------------
1949 
getData(const Element * e) const1950 ElementEditData* EditData::getData(const Element* e) const
1951       {
1952       for (ElementEditData* ed : data) {
1953             if (ed->e == e)
1954                   return ed;
1955             }
1956       return 0;
1957       }
1958 
1959 //---------------------------------------------------------
1960 //   addData
1961 //---------------------------------------------------------
1962 
addData(ElementEditData * ed)1963 void EditData::addData(ElementEditData* ed)
1964       {
1965       data.push_back(ed);
1966       }
1967 
1968 //---------------------------------------------------------
1969 //   drawEditMode
1970 //---------------------------------------------------------
1971 
drawEditMode(QPainter * p,EditData & ed)1972 void Element::drawEditMode(QPainter* p, EditData& ed)
1973       {
1974       QPen pen(MScore::defaultColor, 0.0);
1975       p->setPen(pen);
1976       for (int i = 0; i < ed.grips; ++i) {
1977             if (Grip(i) == ed.curGrip)
1978                   p->setBrush(MScore::frameMarginColor);
1979             else
1980                   p->setBrush(Qt::NoBrush);
1981             p->drawRect(ed.grip[i]);
1982             }
1983       }
1984 
1985 //---------------------------------------------------------
1986 //   startDrag
1987 //---------------------------------------------------------
1988 
startDrag(EditData & ed)1989 void Element::startDrag(EditData& ed)
1990       {
1991       if (!isMovable())
1992             return;
1993       ElementEditData* eed = new ElementEditData();
1994       eed->e = this;
1995       eed->pushProperty(Pid::OFFSET);
1996       eed->pushProperty(Pid::AUTOPLACE);
1997       eed->initOffset = offset();
1998       ed.addData(eed);
1999       if (ed.modifiers & Qt::AltModifier)
2000             setAutoplace(false);
2001       }
2002 
2003 //---------------------------------------------------------
2004 //   drag
2005 ///   Return update Rect relative to canvas.
2006 //---------------------------------------------------------
2007 
drag(EditData & ed)2008 QRectF Element::drag(EditData& ed)
2009       {
2010       if (!isMovable())
2011             return QRectF();
2012 
2013       const QRectF r0(canvasBoundingRect());
2014 
2015       const ElementEditData* eed = ed.getData(this);
2016 
2017       const QPointF offset0 = ed.moveDelta + eed->initOffset;
2018       qreal x = offset0.x();
2019       qreal y = offset0.y();
2020 
2021       qreal _spatium = spatium();
2022       if (ed.hRaster) {
2023             qreal hRaster = _spatium / MScore::hRaster();
2024             int n = lrint(x / hRaster);
2025             x = hRaster * n;
2026             }
2027       if (ed.vRaster) {
2028             qreal vRaster = _spatium / MScore::vRaster();
2029             int n = lrint(y / vRaster);
2030             y = vRaster * n;
2031             }
2032 
2033       setOffset(QPointF(x, y));
2034       setOffsetChanged(true);
2035 //      setGenerated(false);
2036 
2037       if (isTextBase()) {         // TODO: check for other types
2038             //
2039             // restrict move to page boundaries
2040             //
2041             const QRectF r(canvasBoundingRect());
2042             Page* p = 0;
2043             Element* e = this;
2044             while (e) {
2045                   if (e->isPage()) {
2046                         p = toPage(e);
2047                         break;
2048                         }
2049                   e = e->parent();
2050                   }
2051             if (p) {
2052                   bool move = false;
2053                   QRectF pr(p->canvasBoundingRect());
2054                   if (r.right() > pr.right()) {
2055                         x -= r.right() - pr.right();
2056                         move = true;
2057                         }
2058                   else if (r.left() < pr.left()) {
2059                         x += pr.left() - r.left();
2060                         move = true;
2061                         }
2062                   if (r.bottom() > pr.bottom()) {
2063                         y -= r.bottom() - pr.bottom();
2064                         move = true;
2065                         }
2066                   else if (r.top() < pr.top()) {
2067                         y += pr.top() - r.top();
2068                         move = true;
2069                         }
2070                   if (move)
2071                         setOffset(QPointF(x, y));
2072                   }
2073             }
2074       return canvasBoundingRect() | r0;
2075       }
2076 
2077 //---------------------------------------------------------
2078 //   endDrag
2079 //---------------------------------------------------------
2080 
endDrag(EditData & ed)2081 void Element::endDrag(EditData& ed)
2082       {
2083       if (!isMovable())
2084             return;
2085       ElementEditData* eed = ed.getData(this);
2086       if (!eed)
2087             return;
2088       for (const PropertyData &pd : qAsConst(eed->propertyData)) {
2089             setPropertyFlags(pd.id, pd.f); // reset initial property flags state
2090             PropertyFlags f = pd.f;
2091             if (f == PropertyFlags::STYLED)
2092                   f = PropertyFlags::UNSTYLED;
2093             score()->undoPropertyChanged(this, pd.id, pd.data, f);
2094             setGenerated(false);
2095             }
2096       }
2097 
2098 //---------------------------------------------------------
2099 //   genericDragAnchorLines
2100 //---------------------------------------------------------
2101 
genericDragAnchorLines() const2102 QVector<QLineF> Element::genericDragAnchorLines() const
2103       {
2104       qreal xp = 0.0;
2105       for (Element* e = parent(); e; e = e->parent())
2106             xp += e->x();
2107       qreal yp;
2108       if (parent()->isSegment()) {
2109             System* system = toSegment(parent())->measure()->system();
2110             const int stIdx = staffIdx();
2111             yp = system ? system->staffCanvasYpage(stIdx) : 0.0;
2112             if (placement() == Placement::BELOW)
2113                   yp += system ? system->staff(stIdx)->bbox().height() : 0.0;
2114             //adjust anchor Y positions to staffType offset
2115             if (staff())
2116                 yp += staff()->staffTypeForElement(this)->yoffset().val()* spatium();
2117             }
2118       else
2119             yp = parent()->canvasPos().y();
2120       QPointF p1(xp, yp);
2121       QLineF anchorLine(p1, canvasPos());
2122       return { anchorLine };
2123       }
2124 
2125 //---------------------------------------------------------
2126 //   updateGrips
2127 //---------------------------------------------------------
2128 
updateGrips(EditData & ed) const2129 void Element::updateGrips(EditData& ed) const
2130       {
2131       const auto positions(gripsPositions(ed));
2132       const size_t ngrips = positions.size();
2133       for (int i = 0; i < int(ngrips); ++i)
2134             ed.grip[i].translate(positions[i]);
2135       }
2136 
2137 //---------------------------------------------------------
2138 //   startEdit
2139 //---------------------------------------------------------
2140 
startEdit(EditData & ed)2141 void Element::startEdit(EditData& ed)
2142       {
2143       ElementEditData* elementData = new ElementEditData();
2144       elementData->e = this;
2145       ed.addData(elementData);
2146       }
2147 
2148 //---------------------------------------------------------
2149 //   edit
2150 //    return true if event is accepted
2151 //---------------------------------------------------------
2152 
edit(EditData & ed)2153 bool Element::edit(EditData& ed)
2154       {
2155       if (ed.key ==  Qt::Key_Home) {
2156             setOffset(QPoint());
2157             return true;
2158             }
2159       return false;
2160       }
2161 
2162 //---------------------------------------------------------
2163 //   startEditDrag
2164 //---------------------------------------------------------
2165 
startEditDrag(EditData & ed)2166 void Element::startEditDrag(EditData& ed)
2167       {
2168       ElementEditData* eed = ed.getData(this);
2169       if (!eed) {
2170             eed = new ElementEditData();
2171             eed->e = this;
2172             ed.addData(eed);
2173             }
2174       eed->pushProperty(Pid::OFFSET);
2175       eed->pushProperty(Pid::AUTOPLACE);
2176       if (ed.modifiers & Qt::AltModifier)
2177             setAutoplace(false);
2178       }
2179 
2180 //---------------------------------------------------------
2181 //   editDrag
2182 //---------------------------------------------------------
2183 
editDrag(EditData & ed)2184 void Element::editDrag(EditData& ed)
2185       {
2186       score()->addRefresh(canvasBoundingRect());
2187       setOffset(offset() + ed.delta);
2188       setOffsetChanged(true);
2189       score()->addRefresh(canvasBoundingRect());
2190       }
2191 
2192 //---------------------------------------------------------
2193 //   endEditDrag
2194 //---------------------------------------------------------
2195 
endEditDrag(EditData & ed)2196 void Element::endEditDrag(EditData& ed)
2197       {
2198       ElementEditData* eed = ed.getData(this);
2199       bool changed = false;
2200       if (eed) {
2201             for (const PropertyData &pd : qAsConst(eed->propertyData)) {
2202                   setPropertyFlags(pd.id, pd.f); // reset initial property flags state
2203                   PropertyFlags f = pd.f;
2204                   if (f == PropertyFlags::STYLED)
2205                         f = PropertyFlags::UNSTYLED;
2206                   if (score()->undoPropertyChanged(this, pd.id, pd.data, f))
2207                         changed = true;
2208                   }
2209             eed->propertyData.clear();
2210             }
2211       if (changed) {
2212             undoChangeProperty(Pid::GENERATED, false);
2213             }
2214       }
2215 
2216 //---------------------------------------------------------
2217 //   endEdit
2218 //---------------------------------------------------------
2219 
endEdit(EditData &)2220 void Element::endEdit(EditData&)
2221       {
2222       }
2223 
2224 //---------------------------------------------------------
2225 //   styleP
2226 //---------------------------------------------------------
2227 
styleP(Sid idx) const2228 qreal Element::styleP(Sid idx) const
2229       {
2230       return score()->styleP(idx);
2231       }
2232 
2233 //---------------------------------------------------------
2234 //   autoplaceSegmentElement
2235 //---------------------------------------------------------
2236 
2237 #if 0
2238 void Element::autoplaceSegmentElement(qreal minDistance)
2239       {
2240       if (visible() && autoplace() && parent()) {
2241             setOffset(QPointF());
2242             Segment* s        = toSegment(parent());
2243             Measure* m        = s->measure();
2244             int si            = staffIdx();
2245             Shape s1          = m->staffShape(si);
2246             Shape s2          = shape().translated(s->pos() + pos());
2247 
2248             if (isTextBase()) {
2249                   // look for collisions in next measures
2250                   qreal totalWidth = m->width();
2251                   for (Measure* nm = m->nextMeasure(); nm; nm = nm->nextMeasure()) {
2252                         if (s2.right() > totalWidth) {
2253                               s1.add(nm->staffShape(si).translated(QPointF(totalWidth, 0.0)));
2254                               totalWidth += nm->width();
2255                               }
2256                         else
2257                               break;
2258                         }
2259 
2260                   // look for collisions in previous measures
2261                   totalWidth = 0;
2262                   for (Measure* pm = m->prevMeasure(); pm; pm = pm->prevMeasure()) {
2263                         if (s2.left() > totalWidth) {
2264                               s1.add(pm->staffShape(si).translated(QPointF(-(totalWidth + pm->width()), 0.0)));
2265                               totalWidth += pm->width();
2266                               }
2267                         else
2268                               break;
2269                         }
2270 
2271                   // actually check for collisions
2272                   bool intersection = true;
2273                   qreal totalYOff = 0;
2274                   while (intersection) {
2275                         intersection = false;
2276                         for (ShapeElement se : s1) {
2277                               if (s2.intersects(se)){
2278                                     intersection = true;
2279                                     break;
2280                                     }
2281                               }
2282                         if (! intersection)
2283                               break;
2284                         else {
2285                               qreal yd = -1;
2286                               if (placeAbove())
2287                                     yd = 1;
2288                               totalYOff -= yd;
2289                               s2.translateY(-yd);
2290                               }
2291                         }
2292 
2293                   // margin of 5 to stop slight overlap, hardcoded for now
2294                   qreal textMarginBottom = 5;
2295                   s2.translateY(-textMarginBottom);
2296                   rUserYoffset() = totalYOff - textMarginBottom;
2297 
2298                   m->staffShape(si).add(s2);
2299 
2300                   Shape s3 = s2.translated(QPointF()); // make a copy of s2
2301                   totalWidth = m->width();
2302                   for (Measure* nm = m->nextMeasure(); nm; nm = nm->nextMeasure()) {
2303                         if (s2.right() > totalWidth) {
2304                               s3.translateX(-totalWidth);
2305                               nm->staffShape(staffIdx()).add(s3);
2306                               totalWidth += nm->width();
2307                               s3 = s2.translated(QPointF()); // reset translation
2308                               }
2309                         else
2310                               break;
2311                         }
2312 
2313                   s3 = s2.translated(QPointF());
2314                   totalWidth = 0;
2315                   for (Measure* pm = m->prevMeasure(); pm; pm = pm->prevMeasure()) {
2316                         if (s2.left() > totalWidth) {
2317                               s3.translateX(totalWidth+pm->width());
2318                               pm->staffShape(staffIdx()).add(s3);
2319                               totalWidth += pm->width();
2320                               s3 = s2.translated(QPointF()); // reset translation
2321                               }
2322                         else
2323                               break;
2324                         }
2325 
2326                   }
2327             else {
2328                   // look for collisions in the next measure
2329                   // if necessary
2330                   bool cnm = (s2.right() > m->width()) && m->nextMeasure() && m->nextMeasure()->system() == m->system();
2331                   if (cnm) {
2332                         Measure* nm = m->nextMeasure();
2333                         s1.add(nm->staffShape(si).translated(QPointF(m->width(), 0.0)));
2334                         }
2335                   qreal d = placeAbove() ? s2.minVerticalDistance(s1) : s1.minVerticalDistance(s2);
2336                   if (d > -minDistance) {
2337                         qreal yd = d + minDistance;
2338                         if (placeAbove())
2339                               yd *= -1.0;
2340                         rUserYoffset() = yd;
2341                         s2.translateY(yd);
2342                         }
2343                   m->staffShape(si).add(s2);
2344                   if (cnm) {
2345                         Measure* nm = m->nextMeasure();
2346                         s2.translateX(-m->width());
2347                         nm->staffShape(staffIdx()).add(s2);
2348                         }
2349                   }
2350             }
2351       }
2352 #endif
2353 
2354 //---------------------------------------------------------
2355 //   setOffsetChanged
2356 //---------------------------------------------------------
2357 
setOffsetChanged(bool v,bool absolute,const QPointF & diff)2358 void Element::setOffsetChanged(bool v, bool absolute, const QPointF& diff)
2359       {
2360       if (v)
2361             _offsetChanged = absolute ? OffsetChange::ABSOLUTE_OFFSET : OffsetChange::RELATIVE_OFFSET;
2362       else
2363             _offsetChanged = OffsetChange::NONE;
2364       _changedPos = pos() + diff;
2365       }
2366 
2367 //---------------------------------------------------------
2368 //   rebaseOffset
2369 //    calculates new offset for moved elements
2370 //    for drag & other actions that result in absolute position, apply the new offset
2371 //    for nudge & other actions that result in relative adjustment, return the vertical difference
2372 //---------------------------------------------------------
2373 
rebaseOffset(bool nox)2374 qreal Element::rebaseOffset(bool nox)
2375       {
2376       QPointF off = offset();
2377       QPointF p = _changedPos - pos();
2378       if (nox)
2379             p.rx() = 0.0;
2380       //OffsetChange saveChangedValue = _offsetChanged;
2381 
2382       bool staffRelative = staff() && parent() && !(parent()->isNote() || parent()->isRest());
2383       if (staffRelative && propertyFlags(Pid::PLACEMENT) != PropertyFlags::NOSTYLE) {
2384             // check if flipped
2385             // TODO: elements that support PLACEMENT but not as a styled property (add supportsPlacement() method?)
2386             // TODO: refactor to take advantage of existing cmdFlip() algorithms
2387             // TODO: adjustPlacement() (from read206.cpp) on read for 3.0 as well
2388             QRectF r = bbox().translated(_changedPos);
2389             qreal staffHeight = staff()->height();
2390             Element* e = isSpannerSegment() ? toSpannerSegment(this)->spanner() : this;
2391             bool multi = e->isSpanner() && toSpanner(e)->spannerSegments().size() > 1;
2392             bool above = e->placeAbove();
2393             bool flipped = above ? r.top() > staffHeight : r.bottom() < 0.0;
2394             if (flipped && !multi) {
2395                   off.ry() += above ? -staffHeight : staffHeight;
2396                   undoChangeProperty(Pid::OFFSET, off + p);
2397                   _offsetChanged = OffsetChange::ABSOLUTE_OFFSET;       //saveChangedValue;
2398                   rypos() += above ? staffHeight : -staffHeight;
2399                   PropertyFlags pf = e->propertyFlags(Pid::PLACEMENT);
2400                   if (pf == PropertyFlags::STYLED)
2401                         pf = PropertyFlags::UNSTYLED;
2402                   Placement place = above ? Placement::BELOW : Placement::ABOVE;
2403                   e->undoChangeProperty(Pid::PLACEMENT, int(place), pf);
2404                   undoResetProperty(Pid::MIN_DISTANCE);
2405                   // TODO
2406                   //MuseScoreCore::mscoreCore->updateInspector();
2407                   return 0.0;
2408                   }
2409             }
2410 
2411       if (offsetChanged() == OffsetChange::ABSOLUTE_OFFSET) {
2412             undoChangeProperty(Pid::OFFSET, off + p);
2413             _offsetChanged = OffsetChange::ABSOLUTE_OFFSET;             //saveChangedValue;
2414             // allow autoplace to manage min distance even when not needed
2415             undoResetProperty(Pid::MIN_DISTANCE);
2416             return 0.0;
2417             }
2418 
2419       // allow autoplace to manage min distance even when not needed
2420       undoResetProperty(Pid::MIN_DISTANCE);
2421       return p.y();
2422       }
2423 
2424 //---------------------------------------------------------
2425 //   rebaseMinDistance
2426 //    calculates new minDistance for moved elements
2427 //    if necessary, also rebases offset
2428 //    updates md, yd
2429 //    returns true if shape needs to be rebased
2430 //---------------------------------------------------------
2431 
rebaseMinDistance(qreal & md,qreal & yd,qreal sp,qreal rebase,bool above,bool fix)2432 bool Element::rebaseMinDistance(qreal& md, qreal& yd, qreal sp, qreal rebase, bool above, bool fix)
2433       {
2434       bool rc = false;
2435       PropertyFlags pf = propertyFlags(Pid::MIN_DISTANCE);
2436       if (pf == PropertyFlags::STYLED)
2437             pf = PropertyFlags::UNSTYLED;
2438       qreal adjustedY = pos().y() + yd;
2439       qreal diff = _changedPos.y() - adjustedY;
2440       if (fix) {
2441             undoChangeProperty(Pid::MIN_DISTANCE, -999.0, pf);
2442             yd = 0.0;
2443             }
2444       else if (!isStyled(Pid::MIN_DISTANCE)) {
2445             md = (above ? md + yd : md - yd) / sp;
2446             undoChangeProperty(Pid::MIN_DISTANCE, md, pf);
2447             yd += diff;
2448             }
2449       else {
2450             // min distance still styled
2451             // user apparently moved element into skyline
2452             // but perhaps not really, if performing a relative adjustment
2453             if (_offsetChanged == OffsetChange::RELATIVE_OFFSET) {
2454                   // relative movement (cursor): fix only if moving vertically into direction of skyline
2455                   if ((above && diff > 0.0) || (!above && diff < 0.0)) {
2456                         // rebase offset
2457                         QPointF p = offset();
2458                         p.ry() += rebase;
2459                         undoChangeProperty(Pid::OFFSET, p);
2460                         md = (above ? md - diff : md + diff) / sp;
2461                         undoChangeProperty(Pid::MIN_DISTANCE, md, pf);
2462                         rc = true;
2463                         yd = 0.0;
2464                         }
2465                   }
2466             else {
2467                   // absolute movement (drag): fix unconditionally
2468                   md = (above ? md + yd : md - yd) / sp;
2469                   undoChangeProperty(Pid::MIN_DISTANCE, md, pf);
2470                   yd = 0.0;
2471                   }
2472             }
2473       // TODO
2474       //MuseScoreCore::mscoreCore->updateInspector();
2475       return rc;
2476       }
2477 
2478 //---------------------------------------------------------
2479 //   autoplaceSegmentElement
2480 //---------------------------------------------------------
2481 
autoplaceSegmentElement(bool above,bool add)2482 void Element::autoplaceSegmentElement(bool above, bool add)
2483       {
2484       // rebase vertical offset on drag
2485       qreal rebase = 0.0;
2486       if (offsetChanged() != OffsetChange::NONE)
2487             rebase = rebaseOffset();
2488 
2489       if (autoplace() && parent()) {
2490             Segment* s = toSegment(parent());
2491             Measure* m = s->measure();
2492 
2493             qreal sp = score()->spatium();
2494             int si = staffIdx();
2495             if (systemFlag()) {
2496                   const int firstVis = m->system()->firstVisibleStaff();
2497                   if (firstVis < score()->nstaves())
2498                         si = firstVis;
2499                   }
2500             else {
2501                   qreal mag = staff()->mag(this);
2502                   sp *= mag;
2503                   }
2504             qreal minDistance = _minDistance.val() * sp;
2505 
2506             SysStaff* ss = m->system()->staff(si);
2507             QRectF r = bbox().translated(m->pos() + s->pos() + pos());
2508 
2509             // Adjust bbox Y pos for staffType offset
2510             if (staffType()) {
2511                   qreal stYOffset = staffType()->yoffset().val() * sp;
2512                   r.translate(0.0, stYOffset);
2513                   }
2514 
2515             SkylineLine sk(!above);
2516             qreal d;
2517             if (above) {
2518                   sk.add(r.x(), r.bottom(), r.width());
2519                   d = sk.minDistance(ss->skyline().north());
2520                   }
2521             else {
2522                   sk.add(r.x(), r.top(), r.width());
2523                   d = ss->skyline().south().minDistance(sk);
2524                   }
2525 
2526             if (d > -minDistance) {
2527                   qreal yd = d + minDistance;
2528                   if (above)
2529                         yd *= -1.0;
2530                   if (offsetChanged() != OffsetChange::NONE) {
2531                         // user moved element within the skyline
2532                         // we may need to adjust minDistance, yd, and/or offset
2533                         bool inStaff = above ? r.bottom() + rebase > 0.0 : r.top() + rebase < staff()->height();
2534                         if (rebaseMinDistance(minDistance, yd, sp, rebase, above, inStaff))
2535                               r.translate(0.0, rebase);
2536                         }
2537                   rypos() += yd;
2538                   r.translate(QPointF(0.0, yd));
2539                   }
2540             if (add && addToSkyline())
2541                   ss->skyline().add(r);
2542             }
2543       setOffsetChanged(false);
2544       }
2545 
2546 //---------------------------------------------------------
2547 //   autoplaceMeasureElement
2548 //---------------------------------------------------------
2549 
autoplaceMeasureElement(bool above,bool add)2550 void Element::autoplaceMeasureElement(bool above, bool add)
2551       {
2552       // rebase vertical offset on drag
2553       qreal rebase = 0.0;
2554       if (offsetChanged() != OffsetChange::NONE)
2555             rebase = rebaseOffset();
2556 
2557       if (autoplace() && parent()) {
2558             Measure* m = toMeasure(parent());
2559             int si     = staffIdx();
2560 
2561             qreal sp = score()->spatium();
2562             qreal minDistance = _minDistance.val() * sp;
2563 
2564             SysStaff* ss = m->system()->staff(si);
2565             // shape rather than bbox is good for tuplets especially
2566             Shape sh = shape().translated(m->pos() + pos());
2567 
2568             SkylineLine sk(!above);
2569             qreal d;
2570             if (above) {
2571                   sk.add(sh);
2572                   d = sk.minDistance(ss->skyline().north());
2573                   }
2574             else {
2575                   sk.add(sh);
2576                   d = ss->skyline().south().minDistance(sk);
2577                   }
2578             if (d > -minDistance) {
2579                   qreal yd = d + minDistance;
2580                   if (above)
2581                         yd *= -1.0;
2582                   if (offsetChanged() != OffsetChange::NONE) {
2583                         // user moved element within the skyline
2584                         // we may need to adjust minDistance, yd, and/or offset
2585                         bool inStaff = above ? sh.bottom() + rebase > 0.0 : sh.top() + rebase < staff()->height();
2586                         if (rebaseMinDistance(minDistance, yd, sp, rebase, above, inStaff))
2587                               sh.translateY(rebase);
2588                         }
2589                   rypos() += yd;
2590                   sh.translateY(yd);
2591                   }
2592             if (add && addToSkyline())
2593                   ss->skyline().add(sh);
2594             }
2595       setOffsetChanged(false);
2596       }
2597 
2598 }
2599