ea56495436
fix #171036: don't scale system elements with staff size
2213 lines
72 KiB
C++
2213 lines
72 KiB
C++
//=============================================================================
|
||
// MuseScore
|
||
// Music Composition & Notation
|
||
//
|
||
// Copyright (C) 2002-2011 Werner Schweer
|
||
//
|
||
// This program is free software; you can redistribute it and/or modify
|
||
// it under the terms of the GNU General Public License version 2
|
||
// as published by the Free Software Foundation and appearing in
|
||
// the file LICENCE.GPL
|
||
//=============================================================================
|
||
|
||
/**
|
||
\file
|
||
Implementation of Element, ElementList
|
||
*/
|
||
|
||
#include "element.h"
|
||
#include "accidental.h"
|
||
#include "ambitus.h"
|
||
#include "arpeggio.h"
|
||
#include "articulation.h"
|
||
#include "bagpembell.h"
|
||
#include "barline.h"
|
||
#include "bend.h"
|
||
#include "box.h"
|
||
#include "bracket.h"
|
||
#include "breath.h"
|
||
#include "chord.h"
|
||
#include "chordline.h"
|
||
#include "chordrest.h"
|
||
#include "clef.h"
|
||
#include "connector.h"
|
||
#include "dynamic.h"
|
||
#include "figuredbass.h"
|
||
#include "fingering.h"
|
||
#include "fret.h"
|
||
#include "glissando.h"
|
||
#include "hairpin.h"
|
||
#include "harmony.h"
|
||
#include "icon.h"
|
||
#include "image.h"
|
||
#include "iname.h"
|
||
#include "instrchange.h"
|
||
#include "jump.h"
|
||
#include "keysig.h"
|
||
#include "layoutbreak.h"
|
||
#include "lyrics.h"
|
||
#include "marker.h"
|
||
#include "measure.h"
|
||
#include "mscore.h"
|
||
#include "notedot.h"
|
||
#include "note.h"
|
||
#include "noteline.h"
|
||
#include "ossia.h"
|
||
#include "ottava.h"
|
||
#include "page.h"
|
||
#include "pedal.h"
|
||
#include "rehearsalmark.h"
|
||
#include "repeat.h"
|
||
#include "rest.h"
|
||
#include "score.h"
|
||
#include "segment.h"
|
||
#include "slur.h"
|
||
#include "spacer.h"
|
||
#include "staff.h"
|
||
#include "staffstate.h"
|
||
#include "stafftext.h"
|
||
#include "systemtext.h"
|
||
#include "stafftype.h"
|
||
#include "stem.h"
|
||
#include "style.h"
|
||
#include "symbol.h"
|
||
#include "sym.h"
|
||
#include "system.h"
|
||
#include "tempotext.h"
|
||
#include "textframe.h"
|
||
#include "text.h"
|
||
#include "measurenumber.h"
|
||
#include "textline.h"
|
||
#include "tie.h"
|
||
#include "timesig.h"
|
||
#include "tremolobar.h"
|
||
#include "tremolo.h"
|
||
#include "trill.h"
|
||
#include "undo.h"
|
||
#include "utils.h"
|
||
#include "volta.h"
|
||
#include "xml.h"
|
||
#include "systemdivider.h"
|
||
#include "stafftypechange.h"
|
||
#include "stafflines.h"
|
||
#include "letring.h"
|
||
#include "vibrato.h"
|
||
#include "palmmute.h"
|
||
#include "fermata.h"
|
||
#include "shape.h"
|
||
|
||
namespace Ms {
|
||
|
||
// extern bool showInvisible;
|
||
|
||
//---------------------------------------------------------
|
||
// spatiumChanged
|
||
//---------------------------------------------------------
|
||
|
||
void Element::spatiumChanged(qreal oldValue, qreal newValue)
|
||
{
|
||
if (sizeIsSpatiumDependent())
|
||
_offset *= (newValue / oldValue);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// localSpatiumChanged
|
||
// the scale of a staff changed
|
||
//---------------------------------------------------------
|
||
|
||
void Element::localSpatiumChanged(qreal oldValue, qreal newValue)
|
||
{
|
||
if (sizeIsSpatiumDependent())
|
||
_offset *= (newValue / oldValue);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// spatium
|
||
//---------------------------------------------------------
|
||
|
||
qreal Element::spatium() const
|
||
{
|
||
if (systemFlag()) {
|
||
return score()->spatium();
|
||
}
|
||
else {
|
||
Staff* s = staff();
|
||
return s ? s->spatium(tick()) : score()->spatium();
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// magS
|
||
//---------------------------------------------------------
|
||
|
||
qreal Element::magS() const
|
||
{
|
||
return mag() * (score()->spatium() / SPATIUM20);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// name
|
||
//---------------------------------------------------------
|
||
|
||
QString Element::subtypeName() const
|
||
{
|
||
return "";
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// Element
|
||
//---------------------------------------------------------
|
||
|
||
Element::Element(Score* s, ElementFlags f)
|
||
: ScoreElement(s)
|
||
{
|
||
_flags = f;
|
||
_track = -1;
|
||
_color = MScore::defaultColor;
|
||
_mag = 1.0;
|
||
_tag = 1;
|
||
_z = -1;
|
||
}
|
||
|
||
Element::Element(const Element& e)
|
||
: ScoreElement(e)
|
||
{
|
||
_parent = e._parent;
|
||
_bbox = e._bbox;
|
||
_mag = e._mag;
|
||
_pos = e._pos;
|
||
_offset = e._offset;
|
||
_track = e._track;
|
||
_flags = e._flags;
|
||
_tag = e._tag;
|
||
_z = e._z;
|
||
_color = e._color;
|
||
itemDiscovered = false;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// ~Element
|
||
//---------------------------------------------------------
|
||
|
||
Element::~Element()
|
||
{
|
||
Score::onElementDestruction(this);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// linkedClone
|
||
//---------------------------------------------------------
|
||
|
||
Element* Element::linkedClone()
|
||
{
|
||
Element* e = clone();
|
||
e->setAutoplace(true);
|
||
score()->undo(new Link(e, this));
|
||
return e;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// scanElements
|
||
//---------------------------------------------------------
|
||
|
||
void Element::scanElements(void* data, void (*func)(void*, Element*), bool all)
|
||
{
|
||
if (all || visible() || score()->showInvisible())
|
||
func(data, this);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// reset
|
||
//---------------------------------------------------------
|
||
|
||
void Element::reset()
|
||
{
|
||
undoResetProperty(Pid::AUTOPLACE);
|
||
undoResetProperty(Pid::PLACEMENT);
|
||
undoResetProperty(Pid::OFFSET);
|
||
ScoreElement::reset();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// change
|
||
//---------------------------------------------------------
|
||
|
||
void Element::change(Element* o, Element* n)
|
||
{
|
||
remove(o);
|
||
add(n);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// staff
|
||
//---------------------------------------------------------
|
||
|
||
Staff* Element::staff() const
|
||
{
|
||
if (_track == -1 || score()->staves().empty())
|
||
return 0;
|
||
|
||
return score()->staff(_track >> 2);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// staffType
|
||
//---------------------------------------------------------
|
||
|
||
StaffType* Element::staffType() const
|
||
{
|
||
Staff* s = staff();
|
||
return s ? s->staffType(tick()) : 0;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// z
|
||
//---------------------------------------------------------
|
||
|
||
int Element::z() const
|
||
{
|
||
if (_z == -1)
|
||
_z = int(type()) * 100;
|
||
return _z;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// tick
|
||
//---------------------------------------------------------
|
||
|
||
Fraction Element::tick() const
|
||
{
|
||
const Element* e = this;
|
||
while (e->parent()) {
|
||
if (e->parent()->isSegment())
|
||
return toSegment(e->parent())->tick();
|
||
else if (e->parent()->isMeasureBase())
|
||
return toMeasureBase(e->parent())->tick();
|
||
e = e->parent();
|
||
}
|
||
return Fraction(0, 1);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// rtick
|
||
//---------------------------------------------------------
|
||
|
||
Fraction Element::rtick() const
|
||
{
|
||
const Element* e = this;
|
||
while (e->parent()) {
|
||
if (e->parent()->isSegment())
|
||
return toSegment(e->parent())->rtick();
|
||
e = e->parent();
|
||
}
|
||
return Fraction(0, 1);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// part
|
||
//---------------------------------------------------------
|
||
|
||
Part* Element::part() const
|
||
{
|
||
Staff* s = staff();
|
||
return s ? s->part() : 0;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// curColor
|
||
//---------------------------------------------------------
|
||
|
||
QColor Element::curColor() const
|
||
{
|
||
return curColor(visible());
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// curColor
|
||
//---------------------------------------------------------
|
||
|
||
QColor Element::curColor(bool isVisible) const
|
||
{
|
||
return curColor(isVisible, color());
|
||
}
|
||
|
||
QColor Element::curColor(bool isVisible, QColor normalColor) const
|
||
{
|
||
// the default element color is always interpreted as black in
|
||
// printing
|
||
if (score() && score()->printing())
|
||
return (normalColor == MScore::defaultColor) ? Qt::black : normalColor;
|
||
|
||
if (flag(ElementFlag::DROP_TARGET))
|
||
return MScore::dropColor;
|
||
bool marked = false;
|
||
if (isNote()) {
|
||
//const Note* note = static_cast<const Note*>(this);
|
||
marked = toNote(this)->mark();
|
||
}
|
||
if (selected() || marked ) {
|
||
QColor originalColor;
|
||
if (track() == -1)
|
||
originalColor = MScore::selectColor[0];
|
||
else
|
||
originalColor = MScore::selectColor[voice()];
|
||
if (isVisible)
|
||
return originalColor;
|
||
else {
|
||
int red = originalColor.red();
|
||
int green = originalColor.green();
|
||
int blue = originalColor.blue();
|
||
float tint = .6f; // Between 0 and 1. Higher means lighter, lower means darker
|
||
return QColor(red + tint * (255 - red), green + tint * (255 - green), blue + tint * (255 - blue));
|
||
}
|
||
}
|
||
if (!isVisible)
|
||
return Qt::gray;
|
||
return normalColor;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// pagePos
|
||
// return position in canvas coordinates
|
||
//---------------------------------------------------------
|
||
|
||
QPointF Element::pagePos() const
|
||
{
|
||
QPointF p(pos());
|
||
if (parent() == 0)
|
||
return p;
|
||
|
||
if (_flags & ElementFlag::ON_STAFF) {
|
||
System* system = 0;
|
||
Measure* measure = 0;
|
||
if (parent()->isSegment())
|
||
measure = toSegment(parent())->measure();
|
||
else if (parent()->isMeasure()) // used in measure number
|
||
measure = toMeasure(parent());
|
||
else if (parent()->isSystem())
|
||
system = toSystem(parent());
|
||
else if (parent()->isFretDiagram())
|
||
return p + parent()->pagePos();
|
||
else
|
||
qFatal("this %s parent %s\n", name(), parent()->name());
|
||
if (measure) {
|
||
system = measure->system();
|
||
p.ry() += measure->staffLines(vStaffIdx())->y();
|
||
}
|
||
if (system) {
|
||
if (system->staves()->size() <= vStaffIdx()) {
|
||
qDebug("staffIdx out of bounds: %s", name());
|
||
}
|
||
p.ry() += system->staffYpage(vStaffIdx());
|
||
}
|
||
p.rx() = pageX();
|
||
}
|
||
else {
|
||
if (parent()->parent())
|
||
p += parent()->pagePos();
|
||
}
|
||
return p;
|
||
}
|
||
|
||
|
||
//---------------------------------------------------------
|
||
// canvasPos
|
||
//---------------------------------------------------------
|
||
|
||
QPointF Element::canvasPos() const
|
||
{
|
||
QPointF p(pos());
|
||
if (parent() == nullptr)
|
||
return p;
|
||
|
||
if (_flags & ElementFlag::ON_STAFF) {
|
||
System* system = nullptr;
|
||
Measure* measure = nullptr;
|
||
if (parent()->isSegment())
|
||
measure = toSegment(parent())->measure();
|
||
else if (parent()->isMeasure()) // used in measure number
|
||
measure = toMeasure(parent());
|
||
// system = toMeasure(parent())->system();
|
||
else if (parent()->isSystem())
|
||
system = toSystem(parent());
|
||
else if (parent()->isChord()) // grace chord
|
||
measure = toSegment(parent()->parent())->measure();
|
||
else if (parent()->isFretDiagram())
|
||
return p + parent()->canvasPos();
|
||
else
|
||
qFatal("this %s parent %s\n", name(), parent()->name());
|
||
if (measure) {
|
||
p.ry() += measure->staffLines(vStaffIdx())->y();
|
||
system = measure->system();
|
||
if (system) {
|
||
Page* page = system->page();
|
||
if (page)
|
||
p.ry() += page->y();
|
||
}
|
||
}
|
||
if (system)
|
||
p.ry() += system->staffYpage(vStaffIdx());
|
||
p.rx() = canvasX();
|
||
}
|
||
else
|
||
p += parent()->canvasPos();
|
||
return p;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// pageX
|
||
//---------------------------------------------------------
|
||
|
||
qreal Element::pageX() const
|
||
{
|
||
qreal xp = x();
|
||
for (Element* e = parent(); e && e->parent(); e = e->parent())
|
||
xp += e->x();
|
||
return xp;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// canvasX
|
||
//---------------------------------------------------------
|
||
|
||
qreal Element::canvasX() const
|
||
{
|
||
qreal xp = x();
|
||
for (Element* e = parent(); e; e = e->parent())
|
||
xp += e->x();
|
||
return xp;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// contains
|
||
// Return true if p is inside the shape of the object.
|
||
// Note: p is in page coordinates
|
||
//---------------------------------------------------------
|
||
|
||
bool Element::contains(const QPointF& p) const
|
||
{
|
||
return shape().contains(p - pagePos());
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// intersects
|
||
// Return true if \a rr intersects bounding box of object.
|
||
// Note: \a rr is in page coordinates
|
||
//---------------------------------------------------------
|
||
|
||
bool Element::intersects(const QRectF& rr) const
|
||
{
|
||
return shape().intersects(rr.translated(-pagePos()));
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// writeProperties
|
||
//---------------------------------------------------------
|
||
|
||
void Element::writeProperties(XmlWriter& xml) const
|
||
{
|
||
writeProperty(xml, Pid::AUTOPLACE);
|
||
// copy paste should not keep links
|
||
if (_links && (_links->size() > 1) && !xml.clipboardmode()) {
|
||
if (MScore::debugMode)
|
||
xml.tag("lid", _links->lid());
|
||
Element* me = static_cast<Element*>(_links->mainElement());
|
||
Q_ASSERT(type() == me->type());
|
||
Staff* s = staff();
|
||
if (!s) {
|
||
s = score()->staff(xml.curTrack() / VOICES);
|
||
if (!s)
|
||
qWarning("Element::writeProperties: linked element's staff not found (%s)", name());
|
||
}
|
||
Location loc = Location::positionForElement(this);
|
||
if (me == this) {
|
||
xml.tagE("linkedMain");
|
||
xml.setLidLocalIndex(_links->lid(), xml.assignLocalIndex(loc));
|
||
}
|
||
else {
|
||
if (s->links()) {
|
||
Staff* linkedStaff = toStaff(s->links()->mainElement());
|
||
loc.setStaff(linkedStaff->idx());
|
||
}
|
||
xml.stag("linked");
|
||
if (!me->score()->isMaster()) {
|
||
if (me->score() == score()) {
|
||
xml.tag("score", "same");
|
||
}
|
||
else {
|
||
qWarning("Element::writeProperties: linked elements belong to different scores but none of them is master score: (%s lid=%d)", name(), _links->lid());
|
||
}
|
||
}
|
||
Location mainLoc = Location::positionForElement(me);
|
||
const int guessedLocalIndex = xml.assignLocalIndex(mainLoc);
|
||
if (loc != mainLoc) {
|
||
mainLoc.toRelative(loc);
|
||
mainLoc.write(xml);
|
||
}
|
||
const int indexDiff = xml.lidLocalIndex(_links->lid()) - guessedLocalIndex;
|
||
xml.tag("indexDiff", indexDiff, 0);
|
||
xml.etag(); // </linked>
|
||
}
|
||
}
|
||
if ((xml.writeTrack() || track() != xml.curTrack())
|
||
&& (track() != -1) && !isBeam()) {
|
||
// Writing track number for beams is redundant as it is calculated
|
||
// during layout.
|
||
int t = track() + xml.trackDiff();
|
||
xml.tag("track", t);
|
||
}
|
||
if (xml.writePosition())
|
||
xml.tag(Pid::POSITION, rtick());
|
||
if (_tag != 0x1) {
|
||
for (int i = 1; i < MAX_TAGS; i++) {
|
||
if (_tag == ((unsigned)1 << i)) {
|
||
xml.tag("tag", score()->layerTags()[i]);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
for (Pid pid : { Pid::OFFSET, Pid::COLOR, Pid::VISIBLE, Pid::Z, Pid::PLACEMENT}) {
|
||
if (propertyFlags(pid) == PropertyFlags::NOSTYLE)
|
||
writeProperty(xml, pid);
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// readProperties
|
||
//---------------------------------------------------------
|
||
|
||
bool Element::readProperties(XmlReader& e)
|
||
{
|
||
const QStringRef& tag(e.name());
|
||
|
||
if (readProperty(tag, e, Pid::SIZE_SPATIUM_DEPENDENT))
|
||
;
|
||
else if (readProperty(tag, e, Pid::OFFSET))
|
||
;
|
||
else if (readProperty(tag, e, Pid::AUTOPLACE))
|
||
;
|
||
else if (tag == "track")
|
||
setTrack(e.readInt() + e.trackOffset());
|
||
else if (tag == "color")
|
||
setColor(e.readColor());
|
||
else if (tag == "visible")
|
||
setVisible(e.readInt());
|
||
else if (tag == "selected") // obsolete
|
||
e.readInt();
|
||
else if ((tag == "linked") || (tag == "linkedMain")) {
|
||
Staff* s = staff();
|
||
if (!s) {
|
||
s = score()->staff(e.track() / VOICES);
|
||
if (!s) {
|
||
qWarning("Element::readProperties: linked element's staff not found (%s)", name());
|
||
e.skipCurrentElement();
|
||
return true;
|
||
}
|
||
}
|
||
if (tag == "linkedMain") {
|
||
_links = new LinkedElements(score());
|
||
_links->push_back(this);
|
||
e.addLink(s, _links);
|
||
e.readNext();
|
||
}
|
||
else {
|
||
Staff* ls = s->links() ? toStaff(s->links()->mainElement()) : nullptr;
|
||
bool linkedIsMaster = ls ? ls->score()->isMaster() : false;
|
||
Location loc = e.location(true);
|
||
if (ls)
|
||
loc.setStaff(ls->idx());
|
||
Location mainLoc = Location::relative();
|
||
bool locationRead = false;
|
||
int localIndexDiff = 0;
|
||
while (e.readNextStartElement()) {
|
||
const QStringRef& ntag(e.name());
|
||
|
||
if (ntag == "score") {
|
||
QString val(e.readElementText());
|
||
if (val == "same")
|
||
linkedIsMaster = score()->isMaster();
|
||
}
|
||
else if (ntag == "location") {
|
||
mainLoc.read(e);
|
||
mainLoc.toAbsolute(loc);
|
||
locationRead = true;
|
||
}
|
||
else if (ntag == "indexDiff")
|
||
localIndexDiff = e.readInt();
|
||
else
|
||
e.unknown();
|
||
}
|
||
if (!locationRead)
|
||
mainLoc = loc;
|
||
LinkedElements* link = e.getLink(linkedIsMaster, mainLoc, localIndexDiff);
|
||
if (link) {
|
||
ScoreElement* linked = link->mainElement();
|
||
if (linked->type() == type())
|
||
linkTo(linked);
|
||
else
|
||
qWarning("Element::readProperties: linked elements have different types: %s, %s. Input file corrupted?", name(), linked->name());
|
||
}
|
||
if (!_links)
|
||
qWarning("Element::readProperties: could not link %s at staff %d", name(), mainLoc.staff() + 1);
|
||
}
|
||
}
|
||
else if (tag == "lid") {
|
||
if (score()->mscVersion() >= 301) {
|
||
e.skipCurrentElement();
|
||
return true;
|
||
}
|
||
int id = e.readInt();
|
||
_links = e.linkIds().value(id);
|
||
if (!_links) {
|
||
if (!score()->isMaster()) // DEBUG
|
||
qDebug("---link %d not found (%d)", id, e.linkIds().size());
|
||
_links = new LinkedElements(score(), id);
|
||
e.linkIds().insert(id, _links);
|
||
}
|
||
#ifndef NDEBUG
|
||
else {
|
||
for (ScoreElement* eee : *_links) {
|
||
Element* ee = static_cast<Element*>(eee);
|
||
if (ee->type() != type()) {
|
||
qFatal("link %s(%d) type mismatch %s linked to %s",
|
||
ee->name(), id, ee->name(), name());
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
Q_ASSERT(!_links->contains(this));
|
||
_links->append(this);
|
||
}
|
||
else if (tag == "tick") {
|
||
int val = e.readInt();
|
||
if (val >= 0)
|
||
e.setTick(Fraction::fromTicks(score()->fileDivision(val))); // obsolete
|
||
}
|
||
else if (tag == "pos") // obsolete
|
||
readProperty(e, Pid::OFFSET);
|
||
else if (tag == "voice")
|
||
setTrack((_track/VOICES)*VOICES + e.readInt());
|
||
else if (tag == "tag") {
|
||
QString val(e.readElementText());
|
||
for (int i = 1; i < MAX_TAGS; i++) {
|
||
if (score()->layerTags()[i] == val) {
|
||
_tag = 1 << i;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else if (readProperty(tag, e, Pid::PLACEMENT))
|
||
;
|
||
else if (tag == "z")
|
||
setZ(e.readInt());
|
||
else
|
||
return false;
|
||
return true;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// write
|
||
//---------------------------------------------------------
|
||
|
||
void Element::write(XmlWriter& xml) const
|
||
{
|
||
xml.stag(this);
|
||
writeProperties(xml);
|
||
xml.etag();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// read
|
||
//---------------------------------------------------------
|
||
|
||
void Element::read(XmlReader& e)
|
||
{
|
||
while (e.readNextStartElement()) {
|
||
if (!readProperties(e))
|
||
e.unknown();
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// remove
|
||
/// Remove \a el from the list. Return true on success.
|
||
//---------------------------------------------------------
|
||
|
||
bool ElementList::remove(Element* el)
|
||
{
|
||
auto i = find(begin(), end(), el);
|
||
if (i == end())
|
||
return false;
|
||
erase(i);
|
||
return true;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// replace
|
||
//---------------------------------------------------------
|
||
|
||
void ElementList::replace(Element* o, Element* n)
|
||
{
|
||
auto i = find(begin(), end(), o);
|
||
if (i == end()) {
|
||
qDebug("ElementList::replace: element not found");
|
||
return;
|
||
}
|
||
*i = n;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// write
|
||
//---------------------------------------------------------
|
||
|
||
void ElementList::write(XmlWriter& xml) const
|
||
{
|
||
for (const Element* e : *this)
|
||
e->write(xml);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// Compound
|
||
//---------------------------------------------------------
|
||
|
||
Compound::Compound(Score* s)
|
||
: Element(s)
|
||
{
|
||
}
|
||
|
||
Compound::Compound(const Compound& c)
|
||
: Element(c)
|
||
{
|
||
elements.clear();
|
||
foreach(Element* e, c.elements)
|
||
elements.append(e->clone());
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// draw
|
||
//---------------------------------------------------------
|
||
|
||
void Compound::draw(QPainter* painter) const
|
||
{
|
||
foreach(Element* e, elements) {
|
||
QPointF pt(e->pos());
|
||
painter->translate(pt);
|
||
e->draw(painter);
|
||
painter->translate(-pt);
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// addElement
|
||
//---------------------------------------------------------
|
||
|
||
/**
|
||
offset \a x and \a y are in Point units
|
||
*/
|
||
|
||
void Compound::addElement(Element* e, qreal x, qreal y)
|
||
{
|
||
e->setPos(x, y);
|
||
e->setParent(this);
|
||
elements.push_back(e);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// layout
|
||
//---------------------------------------------------------
|
||
|
||
void Compound::layout()
|
||
{
|
||
setbbox(QRectF());
|
||
for (auto i = elements.begin(); i != elements.end(); ++i) {
|
||
Element* e = *i;
|
||
e->layout();
|
||
addbbox(e->bbox().translated(e->pos()));
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// setSelected
|
||
//---------------------------------------------------------
|
||
|
||
void Compound::setSelected(bool f)
|
||
{
|
||
Element::setSelected(f);
|
||
for (auto i = elements.begin(); i != elements.end(); ++i)
|
||
(*i)->setSelected(f);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// setVisible
|
||
//---------------------------------------------------------
|
||
|
||
void Compound::setVisible(bool f)
|
||
{
|
||
Element::setVisible(f);
|
||
for (auto i = elements.begin(); i != elements.end(); ++i)
|
||
(*i)->setVisible(f);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// clear
|
||
//---------------------------------------------------------
|
||
|
||
void Compound::clear()
|
||
{
|
||
foreach(Element* e, elements) {
|
||
if (e->selected())
|
||
score()->deselect(e);
|
||
delete e;
|
||
}
|
||
elements.clear();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// dump
|
||
//---------------------------------------------------------
|
||
|
||
void Element::dump() const
|
||
{
|
||
qDebug("---Element: %s, pos(%4.2f,%4.2f)"
|
||
"\n bbox(%g,%g,%g,%g)"
|
||
"\n abox(%g,%g,%g,%g)"
|
||
"\n parent: %p",
|
||
name(), ipos().x(), ipos().y(),
|
||
_bbox.x(), _bbox.y(), _bbox.width(), _bbox.height(),
|
||
abbox().x(), abbox().y(), abbox().width(), abbox().height(),
|
||
parent());
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// mimeData
|
||
//---------------------------------------------------------
|
||
|
||
QByteArray Element::mimeData(const QPointF& dragOffset) const
|
||
{
|
||
QBuffer buffer;
|
||
buffer.open(QIODevice::WriteOnly);
|
||
XmlWriter xml(score(), &buffer);
|
||
xml.setClipboardmode(true);
|
||
xml.stag("Element");
|
||
if (isNote())
|
||
xml.tag("duration", toNote(this)->chord()->ticks());
|
||
if (!dragOffset.isNull())
|
||
xml.tag("dragOffset", dragOffset);
|
||
write(xml);
|
||
xml.etag();
|
||
buffer.close();
|
||
return buffer.buffer();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// readType
|
||
// return new position of QDomElement in e
|
||
//---------------------------------------------------------
|
||
|
||
ElementType Element::readType(XmlReader& e, QPointF* dragOffset,
|
||
Fraction* duration)
|
||
{
|
||
while (e.readNextStartElement()) {
|
||
if (e.name() == "Element")
|
||
while (e.readNextStartElement()) {
|
||
const QStringRef& tag = e.name();
|
||
if (tag == "dragOffset")
|
||
*dragOffset = e.readPoint();
|
||
else if (tag == "duration")
|
||
*duration = e.readFraction();
|
||
else {
|
||
ElementType type = name2type(tag);
|
||
if (type == ElementType::INVALID)
|
||
break;
|
||
return type;
|
||
}
|
||
}
|
||
else
|
||
e.unknown();
|
||
}
|
||
return ElementType::INVALID;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// add
|
||
//---------------------------------------------------------
|
||
|
||
void Element::add(Element* e)
|
||
{
|
||
qDebug("Element: cannot add %s to %s", e->name(), name());
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// remove
|
||
//---------------------------------------------------------
|
||
|
||
void Element::remove(Element* e)
|
||
{
|
||
qFatal("Element: cannot remove %s from %s", e->name(), name());
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// create
|
||
// Element factory
|
||
//---------------------------------------------------------
|
||
|
||
Element* Element::create(ElementType type, Score* score)
|
||
{
|
||
switch (type) {
|
||
case ElementType::VOLTA: return new Volta(score);
|
||
case ElementType::OTTAVA: return new Ottava(score);
|
||
case ElementType::TEXTLINE: return new TextLine(score);
|
||
case ElementType::NOTELINE: return new NoteLine(score);
|
||
case ElementType::TRILL: return new Trill(score);
|
||
case ElementType::LET_RING: return new LetRing(score);
|
||
case ElementType::VIBRATO: return new Vibrato(score);
|
||
case ElementType::PALM_MUTE: return new PalmMute(score);
|
||
case ElementType::PEDAL: return new Pedal(score);
|
||
case ElementType::HAIRPIN: return new Hairpin(score);
|
||
case ElementType::CLEF: return new Clef(score);
|
||
case ElementType::KEYSIG: return new KeySig(score);
|
||
case ElementType::TIMESIG: return new TimeSig(score);
|
||
case ElementType::BAR_LINE: return new BarLine(score);
|
||
case ElementType::SYSTEM_DIVIDER: return new SystemDivider(score);
|
||
case ElementType::ARPEGGIO: return new Arpeggio(score);
|
||
case ElementType::BREATH: return new Breath(score);
|
||
case ElementType::GLISSANDO: return new Glissando(score);
|
||
case ElementType::BRACKET: return new Bracket(score);
|
||
case ElementType::ARTICULATION: return new Articulation(score);
|
||
case ElementType::FERMATA: return new Fermata(score);
|
||
case ElementType::CHORDLINE: return new ChordLine(score);
|
||
case ElementType::ACCIDENTAL: return new Accidental(score);
|
||
case ElementType::DYNAMIC: return new Dynamic(score);
|
||
case ElementType::TEXT: return new Text(score);
|
||
case ElementType::MEASURE_NUMBER: return new MeasureNumber(score);
|
||
case ElementType::INSTRUMENT_NAME: return new InstrumentName(score);
|
||
case ElementType::STAFF_TEXT: return new StaffText(score);
|
||
case ElementType::SYSTEM_TEXT: return new SystemText(score);
|
||
case ElementType::REHEARSAL_MARK: return new RehearsalMark(score);
|
||
case ElementType::INSTRUMENT_CHANGE: return new InstrumentChange(score);
|
||
case ElementType::STAFFTYPE_CHANGE: return new StaffTypeChange(score);
|
||
case ElementType::NOTEHEAD: return new NoteHead(score);
|
||
case ElementType::NOTEDOT: return new NoteDot(score);
|
||
case ElementType::TREMOLO: return new Tremolo(score);
|
||
case ElementType::LAYOUT_BREAK: return new LayoutBreak(score);
|
||
case ElementType::MARKER: return new Marker(score);
|
||
case ElementType::JUMP: return new Jump(score);
|
||
case ElementType::REPEAT_MEASURE: return new RepeatMeasure(score);
|
||
case ElementType::ICON: return new Icon(score);
|
||
case ElementType::NOTE: return new Note(score);
|
||
case ElementType::SYMBOL: return new Symbol(score);
|
||
case ElementType::FSYMBOL: return new FSymbol(score);
|
||
case ElementType::CHORD: return new Chord(score);
|
||
case ElementType::REST: return new Rest(score);
|
||
case ElementType::SPACER: return new Spacer(score);
|
||
case ElementType::STAFF_STATE: return new StaffState(score);
|
||
case ElementType::TEMPO_TEXT: return new TempoText(score);
|
||
case ElementType::HARMONY: return new Harmony(score);
|
||
case ElementType::FRET_DIAGRAM: return new FretDiagram(score);
|
||
case ElementType::BEND: return new Bend(score);
|
||
case ElementType::TREMOLOBAR: return new TremoloBar(score);
|
||
case ElementType::LYRICS: return new Lyrics(score);
|
||
case ElementType::FIGURED_BASS: return new FiguredBass(score);
|
||
case ElementType::STEM: return new Stem(score);
|
||
case ElementType::SLUR: return new Slur(score);
|
||
case ElementType::TIE: return new Tie(score);
|
||
case ElementType::FINGERING: return new Fingering(score);
|
||
case ElementType::HBOX: return new HBox(score);
|
||
case ElementType::VBOX: return new VBox(score);
|
||
case ElementType::TBOX: return new TBox(score);
|
||
case ElementType::FBOX: return new FBox(score);
|
||
case ElementType::MEASURE: return new Measure(score);
|
||
case ElementType::TAB_DURATION_SYMBOL: return new TabDurationSymbol(score);
|
||
case ElementType::OSSIA: return new Ossia(score);
|
||
case ElementType::IMAGE: return new Image(score);
|
||
case ElementType::BAGPIPE_EMBELLISHMENT: return new BagpipeEmbellishment(score);
|
||
case ElementType::AMBITUS: return new Ambitus(score);
|
||
|
||
case ElementType::LYRICSLINE:
|
||
case ElementType::TEXTLINE_BASE:
|
||
case ElementType::TEXTLINE_SEGMENT:
|
||
case ElementType::GLISSANDO_SEGMENT:
|
||
case ElementType::SLUR_SEGMENT:
|
||
case ElementType::TIE_SEGMENT:
|
||
case ElementType::STEM_SLASH:
|
||
case ElementType::PAGE:
|
||
case ElementType::BEAM:
|
||
case ElementType::HOOK:
|
||
case ElementType::TUPLET:
|
||
case ElementType::HAIRPIN_SEGMENT:
|
||
case ElementType::OTTAVA_SEGMENT:
|
||
case ElementType::TRILL_SEGMENT:
|
||
case ElementType::LET_RING_SEGMENT:
|
||
case ElementType::VIBRATO_SEGMENT:
|
||
case ElementType::PALM_MUTE_SEGMENT:
|
||
case ElementType::VOLTA_SEGMENT:
|
||
case ElementType::PEDAL_SEGMENT:
|
||
case ElementType::LYRICSLINE_SEGMENT:
|
||
case ElementType::LEDGER_LINE:
|
||
case ElementType::STAFF_LINES:
|
||
case ElementType::SELECTION:
|
||
case ElementType::LASSO:
|
||
case ElementType::SHADOW_NOTE:
|
||
case ElementType::SEGMENT:
|
||
case ElementType::SYSTEM:
|
||
case ElementType::COMPOUND:
|
||
case ElementType::ELEMENT:
|
||
case ElementType::ELEMENT_LIST:
|
||
case ElementType::STAFF_LIST:
|
||
case ElementType::MEASURE_LIST:
|
||
case ElementType::MAXTYPE:
|
||
case ElementType::INVALID:
|
||
case ElementType::PART:
|
||
case ElementType::STAFF:
|
||
case ElementType::SCORE:
|
||
case ElementType::BRACKET_ITEM:
|
||
break;
|
||
}
|
||
qDebug("cannot create type %d <%s>", int(type), Element::name(type));
|
||
return 0;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// name2Element
|
||
//---------------------------------------------------------
|
||
|
||
Element* Element::name2Element(const QStringRef& s, Score* sc)
|
||
{
|
||
ElementType type = Element::name2type(s);
|
||
if (type == ElementType::INVALID) {
|
||
qDebug("invalid <%s>\n", qPrintable(s.toString()));
|
||
return 0;
|
||
}
|
||
return Element::create(type, sc);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// elementLessThan
|
||
//---------------------------------------------------------
|
||
|
||
bool elementLessThan(const Element* const e1, const Element* const e2)
|
||
{
|
||
if (e1->z() == e2->z()) {
|
||
if (e1->selected())
|
||
return false;
|
||
else if (e2->selected())
|
||
return true;
|
||
else if (!e1->visible())
|
||
return true;
|
||
else if (!e2->visible())
|
||
return false;
|
||
else
|
||
return e1->track() > e2->track();
|
||
}
|
||
return e1->z() <= e2->z();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// collectElements
|
||
//---------------------------------------------------------
|
||
|
||
void collectElements(void* data, Element* e)
|
||
{
|
||
QList<Element*>* el = static_cast<QList<Element*>*>(data);
|
||
el->append(e);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// getProperty
|
||
//---------------------------------------------------------
|
||
|
||
QVariant Element::getProperty(Pid propertyId) const
|
||
{
|
||
switch (propertyId) {
|
||
case Pid::TICK:
|
||
return tick();
|
||
case Pid::TRACK:
|
||
return track();
|
||
case Pid::VOICE:
|
||
return voice();
|
||
case Pid::POSITION:
|
||
return rtick();
|
||
case Pid::GENERATED:
|
||
return generated();
|
||
case Pid::COLOR:
|
||
return color();
|
||
case Pid::VISIBLE:
|
||
return visible();
|
||
case Pid::SELECTED:
|
||
return selected();
|
||
case Pid::OFFSET:
|
||
return _offset;
|
||
case Pid::PLACEMENT:
|
||
return int(placement());
|
||
case Pid::AUTOPLACE:
|
||
return autoplace();
|
||
case Pid::Z:
|
||
return z();
|
||
case Pid::SYSTEM_FLAG:
|
||
return systemFlag();
|
||
case Pid::SIZE_SPATIUM_DEPENDENT:
|
||
return sizeIsSpatiumDependent();
|
||
default:
|
||
return QVariant();
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// setProperty
|
||
//---------------------------------------------------------
|
||
|
||
bool Element::setProperty(Pid propertyId, const QVariant& v)
|
||
{
|
||
switch (propertyId) {
|
||
case Pid::TRACK:
|
||
setTrack(v.toInt());
|
||
break;
|
||
case Pid::VOICE:
|
||
setVoice(v.toInt());
|
||
break;
|
||
case Pid::GENERATED:
|
||
setGenerated(v.toBool());
|
||
break;
|
||
case Pid::COLOR:
|
||
setColor(v.value<QColor>());
|
||
break;
|
||
case Pid::VISIBLE:
|
||
setVisible(v.toBool());
|
||
break;
|
||
case Pid::SELECTED:
|
||
setSelected(v.toBool());
|
||
break;
|
||
case Pid::OFFSET:
|
||
_offset = v.toPointF();
|
||
break;
|
||
case Pid::PLACEMENT:
|
||
setPlacement(Placement(v.toInt()));
|
||
break;
|
||
case Pid::AUTOPLACE:
|
||
setAutoplace(v.toBool());
|
||
break;
|
||
case Pid::Z:
|
||
setZ(v.toInt());
|
||
break;
|
||
case Pid::SYSTEM_FLAG:
|
||
setSystemFlag(v.toBool());
|
||
break;
|
||
case Pid::SIZE_SPATIUM_DEPENDENT:
|
||
setSizeIsSpatiumDependent(v.toBool());
|
||
break;
|
||
default:
|
||
qDebug("%s unknown <%s>(%d), data <%s>", name(), propertyName(propertyId), int(propertyId), qPrintable(v.toString()));
|
||
return false;
|
||
}
|
||
triggerLayout();
|
||
return true;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// undoChangeProperty
|
||
//---------------------------------------------------------
|
||
|
||
void Element::undoChangeProperty(Pid pid, const QVariant& val, PropertyFlags ps)
|
||
{
|
||
if (pid == Pid::AUTOPLACE && (val.toBool() == true && !autoplace())) {
|
||
// Switching autoplacement on. Save user-defined
|
||
// placement properties to undo stack.
|
||
undoPushProperty(Pid::PLACEMENT);
|
||
undoPushProperty(Pid::OFFSET);
|
||
}
|
||
ScoreElement::undoChangeProperty(pid, val, ps);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// propertyDefault
|
||
//---------------------------------------------------------
|
||
|
||
QVariant Element::propertyDefault(Pid pid) const
|
||
{
|
||
switch (pid) {
|
||
case Pid::GENERATED:
|
||
return false;
|
||
case Pid::VISIBLE:
|
||
return true;
|
||
case Pid::COLOR:
|
||
return MScore::defaultColor;
|
||
case Pid::PLACEMENT:
|
||
return int(Placement::BELOW);
|
||
case Pid::SELECTED:
|
||
return false;
|
||
case Pid::OFFSET: {
|
||
QVariant v = ScoreElement::propertyDefault(pid);
|
||
if (v.isValid()) // if its a styled property
|
||
return v;
|
||
return QPointF();
|
||
}
|
||
case Pid::AUTOPLACE:
|
||
return true;
|
||
case Pid::Z:
|
||
return int(type()) * 100;
|
||
default:
|
||
return ScoreElement::propertyDefault(pid);
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// propertyId
|
||
//---------------------------------------------------------
|
||
|
||
Pid Element::propertyId(const QStringRef& name) const
|
||
{
|
||
if (name == "pos" || name == "offset")
|
||
return Pid::OFFSET;
|
||
return ScoreElement::propertyId(name);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// propertyUserValue
|
||
//---------------------------------------------------------
|
||
|
||
QString Element::propertyUserValue(Pid pid) const
|
||
{
|
||
switch(pid) {
|
||
case Pid::SUBTYPE:
|
||
return subtypeName();
|
||
default:
|
||
return ScoreElement::propertyUserValue(pid);
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// custom
|
||
// check if property is != default
|
||
//---------------------------------------------------------
|
||
|
||
bool Element::custom(Pid id) const
|
||
{
|
||
return propertyDefault(id) != getProperty(id);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// isPrintable
|
||
//---------------------------------------------------------
|
||
|
||
bool Element::isPrintable() const
|
||
{
|
||
switch (type()) {
|
||
case ElementType::PAGE:
|
||
case ElementType::SYSTEM:
|
||
case ElementType::MEASURE:
|
||
case ElementType::SEGMENT:
|
||
case ElementType::VBOX:
|
||
case ElementType::HBOX:
|
||
case ElementType::TBOX:
|
||
case ElementType::FBOX:
|
||
case ElementType::SPACER:
|
||
case ElementType::SHADOW_NOTE:
|
||
case ElementType::LASSO:
|
||
case ElementType::ELEMENT_LIST:
|
||
case ElementType::STAFF_LIST:
|
||
case ElementType::MEASURE_LIST:
|
||
case ElementType::SELECTION:
|
||
return false;
|
||
default:
|
||
return true;
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// findMeasure
|
||
//---------------------------------------------------------
|
||
|
||
Measure* Element::findMeasure()
|
||
{
|
||
if (isMeasure())
|
||
return toMeasure(this);
|
||
else if (_parent)
|
||
return _parent->findMeasure();
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// findMeasure
|
||
//---------------------------------------------------------
|
||
|
||
const Measure* Element::findMeasure() const
|
||
{
|
||
Element* e = const_cast<Element*>(this);
|
||
return e->findMeasure();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// findMeasureBase
|
||
//---------------------------------------------------------
|
||
|
||
MeasureBase* Element::findMeasureBase()
|
||
{
|
||
if (isMeasureBase())
|
||
return toMeasureBase(this);
|
||
else if (_parent)
|
||
return _parent->findMeasureBase();
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// findMeasureBase
|
||
//---------------------------------------------------------
|
||
|
||
const MeasureBase* Element::findMeasureBase() const
|
||
{
|
||
Element* e = const_cast<Element*>(this);
|
||
return e->findMeasureBase();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// undoSetColor
|
||
//---------------------------------------------------------
|
||
|
||
void Element::undoSetColor(const QColor& c)
|
||
{
|
||
undoChangeProperty(Pid::COLOR, c);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// undoSetVisible
|
||
//---------------------------------------------------------
|
||
|
||
void Element::undoSetVisible(bool v)
|
||
{
|
||
undoChangeProperty(Pid::VISIBLE, v);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// drawSymbol
|
||
//---------------------------------------------------------
|
||
|
||
void Element::drawSymbol(SymId id, QPainter* p, const QPointF& o, qreal scale) const
|
||
{
|
||
score()->scoreFont()->draw(id, p, magS() * scale, o);
|
||
}
|
||
|
||
void Element::drawSymbol(SymId id, QPainter* p, const QPointF& o, int n) const
|
||
{
|
||
score()->scoreFont()->draw(id, p, magS(), o, n);
|
||
}
|
||
|
||
void Element::drawSymbols(const std::vector<SymId>& s, QPainter* p, const QPointF& o, qreal scale) const
|
||
{
|
||
score()->scoreFont()->draw(s, p, magS() * scale, o);
|
||
}
|
||
|
||
void Element::drawSymbols(const std::vector<SymId>& s, QPainter* p, const QPointF& o, const QSizeF& scale) const
|
||
{
|
||
score()->scoreFont()->draw(s, p, magS() * scale, o);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// symHeight
|
||
//---------------------------------------------------------
|
||
|
||
qreal Element::symHeight(SymId id) const
|
||
{
|
||
return score()->scoreFont()->height(id, magS());
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// symWidth
|
||
//---------------------------------------------------------
|
||
|
||
qreal Element::symWidth(SymId id) const
|
||
{
|
||
return score()->scoreFont()->width(id, magS());
|
||
}
|
||
qreal Element::symWidth(const std::vector<SymId>& s) const
|
||
{
|
||
return score()->scoreFont()->width(s, magS());
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// symAdvance
|
||
//---------------------------------------------------------
|
||
|
||
qreal Element::symAdvance(SymId id) const
|
||
{
|
||
return score()->scoreFont()->advance(id, magS());
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// symBbox
|
||
//---------------------------------------------------------
|
||
|
||
QRectF Element::symBbox(SymId id) const
|
||
{
|
||
return score()->scoreFont()->bbox(id, magS());
|
||
}
|
||
|
||
QRectF Element::symBbox(const std::vector<SymId>& s) const
|
||
{
|
||
return score()->scoreFont()->bbox(s, magS());
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// symStemDownNW
|
||
//---------------------------------------------------------
|
||
|
||
QPointF Element::symStemDownNW(SymId id) const
|
||
{
|
||
return score()->scoreFont()->stemDownNW(id, magS());
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// symStemUpSE
|
||
//---------------------------------------------------------
|
||
|
||
QPointF Element::symStemUpSE(SymId id) const
|
||
{
|
||
return score()->scoreFont()->stemUpSE(id, magS());
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// symCutOutNE / symCutOutNW / symCutOutSE / symCutOutNW
|
||
//---------------------------------------------------------
|
||
|
||
QPointF Element::symCutOutNE(SymId id) const
|
||
{
|
||
return score()->scoreFont()->cutOutNE(id, magS());
|
||
}
|
||
|
||
QPointF Element::symCutOutNW(SymId id) const
|
||
{
|
||
return score()->scoreFont()->cutOutNW(id, magS());
|
||
}
|
||
|
||
QPointF Element::symCutOutSE(SymId id) const
|
||
{
|
||
return score()->scoreFont()->cutOutSE(id, magS());
|
||
}
|
||
|
||
QPointF Element::symCutOutSW(SymId id) const
|
||
{
|
||
return score()->scoreFont()->cutOutSW(id, magS());
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// symIsValid
|
||
//---------------------------------------------------------
|
||
|
||
bool Element::symIsValid(SymId id) const
|
||
{
|
||
return score()->scoreFont()->isValid(id);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// concertPitch
|
||
//---------------------------------------------------------
|
||
|
||
bool Element::concertPitch() const
|
||
{
|
||
return score()->styleB(Sid::concertPitch);
|
||
}
|
||
//---------------------------------------------------------
|
||
// nextElement
|
||
// selects the next score element
|
||
//---------------------------------------------------------
|
||
|
||
Element* Element::nextElement()
|
||
{
|
||
Element* e = score()->selection().element();
|
||
if (!e && !score()->selection().elements().isEmpty())
|
||
e = score()->selection().elements().first();
|
||
if (e) {
|
||
switch (e->type()) {
|
||
case ElementType::SEGMENT: {
|
||
Segment* s = toSegment(e);
|
||
return s->nextElement(staffIdx());
|
||
}
|
||
case ElementType::MEASURE: {
|
||
Measure* m = toMeasure(e);
|
||
return m->nextElementStaff(staffIdx());
|
||
}
|
||
case ElementType::CLEF:
|
||
case ElementType::KEYSIG:
|
||
case ElementType::TIMESIG:
|
||
case ElementType::BAR_LINE:
|
||
return nextSegmentElement();
|
||
default: {
|
||
return e->parent()->nextElement();
|
||
}
|
||
}
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
|
||
//---------------------------------------------------------
|
||
// prevElement
|
||
// selects the previous score element
|
||
//---------------------------------------------------------
|
||
|
||
Element* Element::prevElement()
|
||
{
|
||
Element* e = score()->selection().element();
|
||
if (!e && !score()->selection().elements().isEmpty() )
|
||
e = score()->selection().elements().last();
|
||
if (e) {
|
||
switch(e->type()) {
|
||
case ElementType::SEGMENT: {
|
||
Segment* s = toSegment(e);
|
||
return s->prevElement(staffIdx());
|
||
}
|
||
case ElementType::MEASURE: {
|
||
Measure* m = toMeasure(e);
|
||
return m->prevElementStaff(staffIdx());
|
||
}
|
||
case ElementType::CLEF:
|
||
case ElementType::KEYSIG:
|
||
case ElementType::TIMESIG:
|
||
case ElementType::BAR_LINE:
|
||
return prevSegmentElement();
|
||
default: {
|
||
return e->parent()->prevElement();
|
||
}
|
||
}
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
|
||
//------------------------------------------------------------------------------------------
|
||
// nextSegmentElement
|
||
// This function is used in for the next-element command to navigate between main elements
|
||
// of segments. (Note, Rest, Clef, Time Signature, Key Signature, Barline, Ambitus, Breath, etc.)
|
||
// The default implementation is to look for the first such element. After it is found each
|
||
// element knows how to find the next one and overrides this method
|
||
//------------------------------------------------------------------------------------------
|
||
|
||
Element* Element::nextSegmentElement()
|
||
{
|
||
Element* p = this;
|
||
while (p) {
|
||
switch (p->type()) {
|
||
case ElementType::NOTE:
|
||
if (toNote(p)->chord()->isGrace())
|
||
break;
|
||
return p;
|
||
case ElementType::REST:
|
||
return p;
|
||
case ElementType::CHORD: {
|
||
Chord* c = toChord(p);
|
||
if (!c->isGrace())
|
||
return c->notes().back();
|
||
}
|
||
break;
|
||
case ElementType::SEGMENT: {
|
||
Segment* s = toSegment(p);
|
||
return s->firstElement(staffIdx());
|
||
}
|
||
case ElementType::MEASURE: {
|
||
Measure* m = toMeasure(p);
|
||
return m->nextElementStaff(staffIdx());
|
||
}
|
||
case ElementType::SYSTEM: {
|
||
System* sys = toSystem(p);
|
||
return sys->nextSegmentElement();
|
||
}
|
||
default:
|
||
break;
|
||
}
|
||
p = p->parent();
|
||
}
|
||
return score()->firstElement();
|
||
}
|
||
|
||
//------------------------------------------------------------------------------------------
|
||
// prevSegmentElement
|
||
// This function is used in for the prev-element command to navigate between main elements
|
||
// of segments. (Note, Rest, Clef, Time Signature, Key Signature, Barline, Ambitus, Breath, etc.)
|
||
// The default implementation is to look for the first such element. After it is found each
|
||
// element knows how to find the previous one and overrides this method
|
||
//------------------------------------------------------------------------------------------
|
||
|
||
Element* Element::prevSegmentElement()
|
||
{
|
||
Element* p = this;
|
||
while (p) {
|
||
switch (p->type()) {
|
||
case ElementType::NOTE:
|
||
if (toNote(p)->chord()->isGrace())
|
||
break;
|
||
return p;
|
||
case ElementType::REST:
|
||
return p;
|
||
case ElementType::CHORD: {
|
||
Chord* c = toChord(p);
|
||
if (!c->isGrace())
|
||
return c->notes().front();
|
||
}
|
||
break;
|
||
case ElementType::SEGMENT: {
|
||
Segment* s = toSegment(p);
|
||
return s->lastElement(staffIdx());
|
||
}
|
||
case ElementType::MEASURE: {
|
||
Measure* m = toMeasure(p);
|
||
return m->prevElementStaff(staffIdx());
|
||
}
|
||
case ElementType::SYSTEM: {
|
||
System* sys = toSystem(p);
|
||
return sys->prevSegmentElement();
|
||
}
|
||
default:
|
||
break;
|
||
}
|
||
p = p->parent();
|
||
}
|
||
return score()->firstElement();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// accessibleInfo
|
||
//---------------------------------------------------------
|
||
|
||
QString Element::accessibleInfo() const
|
||
{
|
||
return Element::userName();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// nextGrip
|
||
//---------------------------------------------------------
|
||
|
||
bool Element::nextGrip(EditData& ed) const
|
||
{
|
||
int i = int(ed.curGrip) + 1;
|
||
if (i >= ed.grips) {
|
||
ed.curGrip = Grip(0);
|
||
return false;
|
||
}
|
||
ed.curGrip = Grip(i);
|
||
return true;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// prevGrip
|
||
//---------------------------------------------------------
|
||
|
||
bool Element::prevGrip(EditData& ed) const
|
||
{
|
||
int i = int(ed.curGrip) - 1;
|
||
if (i < 0) {
|
||
ed.curGrip = Grip(ed.grips - 1);
|
||
return false;
|
||
}
|
||
ed.curGrip = Grip(i);
|
||
return true;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// isUserModified
|
||
// Check if this element was modified by user and
|
||
// therefore must be saved.
|
||
//---------------------------------------------------------
|
||
|
||
bool Element::isUserModified() const
|
||
{
|
||
for (const StyledProperty& spp : *styledProperties()) {
|
||
Pid pid = spp.pid;
|
||
QVariant val = getProperty(pid);
|
||
QVariant defaultValue = propertyDefault(pid);
|
||
|
||
if (propertyType(pid) == P_TYPE::SP_REAL) {
|
||
if (qAbs(val.toReal() - defaultValue.toReal()) > 0.0001) // we don’t care spatium diffs that small
|
||
return true;
|
||
}
|
||
else {
|
||
if (getProperty(pid) != propertyDefault(pid))
|
||
return true;
|
||
}
|
||
}
|
||
return !visible() || !offset().isNull() || (color() != MScore::defaultColor);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// triggerLayout
|
||
//---------------------------------------------------------
|
||
|
||
void Element::triggerLayout() const
|
||
{
|
||
score()->setLayout(tick());
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// init
|
||
//---------------------------------------------------------
|
||
|
||
void EditData::init()
|
||
{
|
||
grip.clear();
|
||
grips = 0;
|
||
curGrip = Grip(0);
|
||
pos = QPointF();
|
||
startMove = QPointF();
|
||
lastPos = QPointF();
|
||
delta = QPointF();
|
||
hRaster = false;
|
||
vRaster = false;
|
||
key = 0;
|
||
modifiers = 0;
|
||
s.clear();
|
||
|
||
dragOffset = QPointF();
|
||
element = 0;
|
||
duration = Fraction(1,4);
|
||
clearData();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// control
|
||
//---------------------------------------------------------
|
||
|
||
bool EditData::control(bool textEditing) const
|
||
{
|
||
if (textEditing)
|
||
return modifiers & CONTROL_MODIFIER;
|
||
else
|
||
return modifiers & Qt::ControlModifier;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// clearData
|
||
//---------------------------------------------------------
|
||
|
||
void EditData::clearData()
|
||
{
|
||
qDeleteAll(data);
|
||
data.clear();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// getData
|
||
//---------------------------------------------------------
|
||
|
||
ElementEditData* EditData::getData(const Element* e) const
|
||
{
|
||
for (ElementEditData* ed : data) {
|
||
if (ed->e == e)
|
||
return ed;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// addData
|
||
//---------------------------------------------------------
|
||
|
||
void EditData::addData(ElementEditData* ed)
|
||
{
|
||
data.push_back(ed);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// drawEditMode
|
||
//---------------------------------------------------------
|
||
|
||
void Element::drawEditMode(QPainter* p, EditData& ed)
|
||
{
|
||
QPen pen(MScore::defaultColor, 0.0);
|
||
p->setPen(pen);
|
||
for (int i = 0; i < ed.grips; ++i) {
|
||
if (Grip(i) == ed.curGrip)
|
||
p->setBrush(MScore::frameMarginColor);
|
||
else
|
||
p->setBrush(Qt::NoBrush);
|
||
p->drawRect(ed.grip[i]);
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// startDrag
|
||
//---------------------------------------------------------
|
||
|
||
void Element::startDrag(EditData& ed)
|
||
{
|
||
if (!isMovable())
|
||
return;
|
||
ElementEditData* eed = new ElementEditData();
|
||
eed->e = this;
|
||
eed->pushProperty(Pid::OFFSET);
|
||
ed.addData(eed);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// drag
|
||
/// Return update Rect relative to canvas.
|
||
//---------------------------------------------------------
|
||
|
||
QRectF Element::drag(EditData& ed)
|
||
{
|
||
if (!isMovable())
|
||
return QRectF();
|
||
|
||
QRectF r(canvasBoundingRect());
|
||
|
||
qreal x = ed.delta.x();
|
||
qreal y = ed.delta.y();
|
||
|
||
qreal _spatium = spatium();
|
||
if (ed.hRaster) {
|
||
qreal hRaster = _spatium / MScore::hRaster();
|
||
int n = lrint(x / hRaster);
|
||
x = hRaster * n;
|
||
}
|
||
if (ed.vRaster) {
|
||
qreal vRaster = _spatium / MScore::vRaster();
|
||
int n = lrint(y / vRaster);
|
||
y = vRaster * n;
|
||
}
|
||
|
||
setOffset(QPointF(x, y));
|
||
// setGenerated(false);
|
||
|
||
if (isTextBase()) { // TODO: check for other types
|
||
//
|
||
// restrict move to page boundaries
|
||
//
|
||
Page* p = 0;
|
||
Element* e = this;
|
||
while (e) {
|
||
if (e->isPage()) {
|
||
p = toPage(e);
|
||
break;
|
||
}
|
||
e = e->parent();
|
||
}
|
||
if (p) {
|
||
bool move = false;
|
||
QRectF pr(p->canvasBoundingRect());
|
||
if (r.right() > pr.right()) {
|
||
x -= r.right() - pr.right();
|
||
move = true;
|
||
}
|
||
else if (r.left() < pr.left()) {
|
||
x += pr.left() - r.left();
|
||
move = true;
|
||
}
|
||
if (r.bottom() > pr.bottom()) {
|
||
y -= r.bottom() - pr.bottom();
|
||
move = true;
|
||
}
|
||
else if (r.top() < pr.top()) {
|
||
y += pr.top() - r.top();
|
||
move = true;
|
||
}
|
||
if (move)
|
||
setOffset(QPointF(x, y));
|
||
}
|
||
}
|
||
return canvasBoundingRect() | r;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// endDrag
|
||
//---------------------------------------------------------
|
||
|
||
void Element::endDrag(EditData& ed)
|
||
{
|
||
if (!isMovable())
|
||
return;
|
||
ElementEditData* eed = ed.getData(this);
|
||
for (PropertyData pd : eed->propertyData) {
|
||
PropertyFlags f = propertyFlags(pd.id);
|
||
if (f == PropertyFlags::STYLED)
|
||
f = PropertyFlags::UNSTYLED;
|
||
score()->undoPropertyChanged(this, pd.id, pd.data, f);
|
||
setGenerated(false);
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// startEdit
|
||
//---------------------------------------------------------
|
||
|
||
void Element::startEdit(EditData& ed)
|
||
{
|
||
ElementEditData* elementData = new ElementEditData();
|
||
elementData->e = this;
|
||
ed.addData(elementData);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// edit
|
||
// return true if event is accepted
|
||
//---------------------------------------------------------
|
||
|
||
bool Element::edit(EditData& ed)
|
||
{
|
||
if (ed.key == Qt::Key_Home) {
|
||
setOffset(QPoint());
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// startEditDrag
|
||
//---------------------------------------------------------
|
||
|
||
void Element::startEditDrag(EditData& ed)
|
||
{
|
||
ElementEditData* eed = ed.getData(this);
|
||
if (!eed) {
|
||
eed = new ElementEditData();
|
||
eed->e = this;
|
||
ed.addData(eed);
|
||
}
|
||
eed->pushProperty(Pid::OFFSET);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// editDrag
|
||
//---------------------------------------------------------
|
||
|
||
void Element::editDrag(EditData& ed)
|
||
{
|
||
score()->addRefresh(canvasBoundingRect());
|
||
setOffset(offset() + ed.delta);
|
||
score()->addRefresh(canvasBoundingRect());
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// endEditDrag
|
||
//---------------------------------------------------------
|
||
|
||
void Element::endEditDrag(EditData& ed)
|
||
{
|
||
ElementEditData* eed = ed.getData(this);
|
||
bool changed = false;
|
||
if (eed) {
|
||
for (PropertyData pd : eed->propertyData) {
|
||
if (score()->undoPropertyChanged(this, pd.id, pd.data))
|
||
changed = true;
|
||
}
|
||
eed->propertyData.clear();
|
||
}
|
||
if (changed) {
|
||
undoChangeProperty(Pid::GENERATED, false);
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// endEdit
|
||
//---------------------------------------------------------
|
||
|
||
void Element::endEdit(EditData&)
|
||
{
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// styleP
|
||
//---------------------------------------------------------
|
||
|
||
qreal Element::styleP(Sid idx) const
|
||
{
|
||
return score()->styleP(idx);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// autoplaceSegmentElement
|
||
//---------------------------------------------------------
|
||
|
||
#if 0
|
||
void Element::autoplaceSegmentElement(qreal minDistance)
|
||
{
|
||
if (visible() && autoplace() && parent()) {
|
||
setOffset(QPointF());
|
||
Segment* s = toSegment(parent());
|
||
Measure* m = s->measure();
|
||
int si = staffIdx();
|
||
Shape s1 = m->staffShape(si);
|
||
Shape s2 = shape().translated(s->pos() + pos());
|
||
|
||
if (isTextBase()) {
|
||
// look for collisions in next measures
|
||
qreal totalWidth = m->width();
|
||
for (Measure* nm = m->nextMeasure(); nm; nm = nm->nextMeasure()) {
|
||
if (s2.right() > totalWidth) {
|
||
s1.add(nm->staffShape(si).translated(QPointF(totalWidth, 0.0)));
|
||
totalWidth += nm->width();
|
||
}
|
||
else
|
||
break;
|
||
}
|
||
|
||
// look for collisions in previous measures
|
||
totalWidth = 0;
|
||
for (Measure* pm = m->prevMeasure(); pm; pm = pm->prevMeasure()) {
|
||
if (s2.left() > totalWidth) {
|
||
s1.add(pm->staffShape(si).translated(QPointF(-(totalWidth + pm->width()), 0.0)));
|
||
totalWidth += pm->width();
|
||
}
|
||
else
|
||
break;
|
||
}
|
||
|
||
// actually check for collisions
|
||
bool intersection = true;
|
||
qreal totalYOff = 0;
|
||
while (intersection) {
|
||
intersection = false;
|
||
for (ShapeElement se : s1) {
|
||
if (s2.intersects(se)){
|
||
intersection = true;
|
||
break;
|
||
}
|
||
}
|
||
if (! intersection)
|
||
break;
|
||
else {
|
||
qreal yd = -1;
|
||
if (placeAbove())
|
||
yd = 1;
|
||
totalYOff -= yd;
|
||
s2.translateY(-yd);
|
||
}
|
||
}
|
||
|
||
// margin of 5 to stop slight overlap, hardcoded for now
|
||
qreal textMarginBottom = 5;
|
||
s2.translateY(-textMarginBottom);
|
||
rUserYoffset() = totalYOff - textMarginBottom;
|
||
|
||
m->staffShape(si).add(s2);
|
||
|
||
Shape s3 = s2.translated(QPointF()); // make a copy of s2
|
||
totalWidth = m->width();
|
||
for (Measure* nm = m->nextMeasure(); nm; nm = nm->nextMeasure()) {
|
||
if (s2.right() > totalWidth) {
|
||
s3.translateX(-totalWidth);
|
||
nm->staffShape(staffIdx()).add(s3);
|
||
totalWidth += nm->width();
|
||
s3 = s2.translated(QPointF()); // reset translation
|
||
}
|
||
else
|
||
break;
|
||
}
|
||
|
||
s3 = s2.translated(QPointF());
|
||
totalWidth = 0;
|
||
for (Measure* pm = m->prevMeasure(); pm; pm = pm->prevMeasure()) {
|
||
if (s2.left() > totalWidth) {
|
||
s3.translateX(totalWidth+pm->width());
|
||
pm->staffShape(staffIdx()).add(s3);
|
||
totalWidth += pm->width();
|
||
s3 = s2.translated(QPointF()); // reset translation
|
||
}
|
||
else
|
||
break;
|
||
}
|
||
|
||
}
|
||
else {
|
||
// look for collisions in the next measure
|
||
// if necessary
|
||
bool cnm = (s2.right() > m->width()) && m->nextMeasure() && m->nextMeasure()->system() == m->system();
|
||
if (cnm) {
|
||
Measure* nm = m->nextMeasure();
|
||
s1.add(nm->staffShape(si).translated(QPointF(m->width(), 0.0)));
|
||
}
|
||
qreal d = placeAbove() ? s2.minVerticalDistance(s1) : s1.minVerticalDistance(s2);
|
||
if (d > -minDistance) {
|
||
qreal yd = d + minDistance;
|
||
if (placeAbove())
|
||
yd *= -1.0;
|
||
rUserYoffset() = yd;
|
||
s2.translateY(yd);
|
||
}
|
||
m->staffShape(si).add(s2);
|
||
if (cnm) {
|
||
Measure* nm = m->nextMeasure();
|
||
s2.translateX(-m->width());
|
||
nm->staffShape(staffIdx()).add(s2);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
//---------------------------------------------------------
|
||
// autoplaceSegmentElement
|
||
//---------------------------------------------------------
|
||
|
||
void Element::autoplaceSegmentElement(qreal minDistance)
|
||
{
|
||
if (visible() && autoplace() && parent()) {
|
||
Segment* s = toSegment(parent());
|
||
Measure* m = s->measure();
|
||
|
||
int si = staffIdx();
|
||
if (systemFlag()) {
|
||
const int firstVis = m->system()->firstVisibleStaff();
|
||
if (firstVis < score()->nstaves())
|
||
si = firstVis;
|
||
}
|
||
|
||
SysStaff* ss = m->system()->staff(si);
|
||
QRectF r = bbox().translated(m->pos() + s->pos() + pos());
|
||
|
||
SkylineLine sk(!placeAbove());
|
||
qreal d;
|
||
if (placeAbove()) {
|
||
sk.add(r.x(), r.bottom(), r.width());
|
||
d = sk.minDistance(ss->skyline().north());
|
||
}
|
||
else {
|
||
sk.add(r.x(), r.top(), r.width());
|
||
d = ss->skyline().south().minDistance(sk);
|
||
}
|
||
|
||
if (d > -minDistance) {
|
||
qreal yd = d + minDistance;
|
||
if (placeAbove())
|
||
yd *= -1.0;
|
||
rypos() += yd;
|
||
r.translate(QPointF(0.0, yd));
|
||
}
|
||
ss->skyline().add(r);
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// autoplaceMeasureElement
|
||
//---------------------------------------------------------
|
||
|
||
void Element::autoplaceMeasureElement(qreal minDistance)
|
||
{
|
||
if (visible() && autoplace() && parent()) {
|
||
Measure* m = toMeasure(parent());
|
||
int si = staffIdx();
|
||
|
||
SysStaff* ss = m->system()->staff(si);
|
||
QRectF r = bbox().translated(m->pos() + pos());
|
||
|
||
SkylineLine sk(!placeAbove());
|
||
qreal d;
|
||
if (placeAbove()) {
|
||
sk.add(r.x(), r.bottom(), r.width());
|
||
d = sk.minDistance(ss->skyline().north());
|
||
}
|
||
else {
|
||
sk.add(r.x(), r.top(), r.width());
|
||
d = ss->skyline().south().minDistance(sk);
|
||
}
|
||
if (d > -minDistance) {
|
||
qreal yd = d + minDistance;
|
||
if (placeAbove())
|
||
yd *= -1.0;
|
||
rypos() += yd;
|
||
r.translate(QPointF(0.0, yd));
|
||
}
|
||
ss->skyline().add(r);
|
||
}
|
||
}
|
||
|
||
}
|