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