MuseScore/libmscore/element.cpp

1976 lines
68 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, StaffLines.
*/
#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 "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 "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 "textline.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"
namespace Ms {
// extern bool showInvisible;
//
// list has to be synchronized with Element::Type enum
//
static const ElementName elementNames[] = {
ElementName("invalid", QT_TRANSLATE_NOOP("elementName", "invalid")),
ElementName("Symbol", QT_TRANSLATE_NOOP("elementName", "Symbol")),
ElementName("Text", QT_TRANSLATE_NOOP("elementName", "Text")),
ElementName("InstrumentName", QT_TRANSLATE_NOOP("elementName", "Instrument Name")),
ElementName("SlurSegment", QT_TRANSLATE_NOOP("elementName", "Slur Segment")),
ElementName("StaffLines", QT_TRANSLATE_NOOP("elementName", "Staff Lines")),
ElementName("BarLine", QT_TRANSLATE_NOOP("elementName", "Bar Line")),
ElementName("StemSlash", QT_TRANSLATE_NOOP("elementName", "Stem Slash")),
ElementName("Line", QT_TRANSLATE_NOOP("elementName", "Line")),
ElementName("Arpeggio", QT_TRANSLATE_NOOP("elementName", "Arpeggio")),
ElementName("Accidental", QT_TRANSLATE_NOOP("elementName", "Accidental")),
ElementName("Stem", QT_TRANSLATE_NOOP("elementName", "Stem")),
ElementName("Note", QT_TRANSLATE_NOOP("elementName", "Note")),
ElementName("Clef", QT_TRANSLATE_NOOP("elementName", "Clef")),
ElementName("KeySig", QT_TRANSLATE_NOOP("elementName", "Key Signature")),
ElementName("Ambitus", QT_TRANSLATE_NOOP("elementName", "Ambitus")),
ElementName("TimeSig", QT_TRANSLATE_NOOP("elementName", "Time Signature")),
ElementName("Rest", QT_TRANSLATE_NOOP("elementName", "Rest")),
ElementName("Breath", QT_TRANSLATE_NOOP("elementName", "Breath")),
ElementName("RepeatMeasure", QT_TRANSLATE_NOOP("elementName", "Repeat Measure")),
ElementName("Image", QT_TRANSLATE_NOOP("elementName", "Image")),
ElementName("Tie", QT_TRANSLATE_NOOP("elementName", "Tie")),
ElementName("Articulation", QT_TRANSLATE_NOOP("elementName", "Articulation")),
ElementName("ChordLine", QT_TRANSLATE_NOOP("elementName", "Chord Line")),
ElementName("Dynamic", QT_TRANSLATE_NOOP("elementName", "Dynamic")),
ElementName("Beam", QT_TRANSLATE_NOOP("elementName", "Beam")),
ElementName("Hook", QT_TRANSLATE_NOOP("elementName", "Hook")),
ElementName("Lyrics", QT_TRANSLATE_NOOP("elementName", "Lyrics")),
ElementName("FiguredBass", QT_TRANSLATE_NOOP("elementName", "Figured Bass")),
ElementName("Marker", QT_TRANSLATE_NOOP("elementName", "Marker")),
ElementName("Jump", QT_TRANSLATE_NOOP("elementName", "Jump")),
ElementName("Fingering", QT_TRANSLATE_NOOP("elementName", "Fingering")),
ElementName("Tuplet", QT_TRANSLATE_NOOP("elementName", "Tuplet")),
ElementName("Tempo", QT_TRANSLATE_NOOP("elementName", "Tempo")),
ElementName("StaffText", QT_TRANSLATE_NOOP("elementName", "Staff Text")),
ElementName("RehearsalMark", QT_TRANSLATE_NOOP("elementName", "Rehearsal Mark")),
ElementName("InstrumentChange", QT_TRANSLATE_NOOP("elementName", "Instrument Change")),
ElementName("Harmony", QT_TRANSLATE_NOOP("elementName", "Chord Symbol")),
ElementName("FretDiagram", QT_TRANSLATE_NOOP("elementName", "Fretboard Diagram")),
ElementName("Bend", QT_TRANSLATE_NOOP("elementName", "Bend")),
ElementName("TremoloBar", QT_TRANSLATE_NOOP("elementName", "Tremolo Bar")),
ElementName("Volta", QT_TRANSLATE_NOOP("elementName", "Volta")),
ElementName("HairpinSegment", QT_TRANSLATE_NOOP("elementName", "Hairpin Segment")),
ElementName("OttavaSegment", QT_TRANSLATE_NOOP("elementName", "Ottava Segment")),
ElementName("TrillSegment", QT_TRANSLATE_NOOP("elementName", "Trill Segment")),
ElementName("TextLineSegment", QT_TRANSLATE_NOOP("elementName", "Text Line Segment")),
ElementName("VoltaSegment", QT_TRANSLATE_NOOP("elementName", "Volta Segment")),
ElementName("PedalSegment", QT_TRANSLATE_NOOP("elementName", "Pedal Segment")),
ElementName("LyricsLineSegment", QT_TRANSLATE_NOOP("elementName", "Melisma Line Segment")),
ElementName("GlissandoSegment", QT_TRANSLATE_NOOP("elementName", "Glissando Segment")),
ElementName("LayoutBreak", QT_TRANSLATE_NOOP("elementName", "Layout Break")),
ElementName("Spacer", QT_TRANSLATE_NOOP("elementName", "Spacer")),
ElementName("StaffState", QT_TRANSLATE_NOOP("elementName", "Staff State")),
ElementName("LedgerLine", QT_TRANSLATE_NOOP("elementName", "Ledger Line")),
ElementName("NoteHead", QT_TRANSLATE_NOOP("elementName", "Note Head")),
ElementName("NoteDot", QT_TRANSLATE_NOOP("elementName", "Note Dot")),
ElementName("Tremolo", QT_TRANSLATE_NOOP("elementName", "Tremolo")),
ElementName("Measure", QT_TRANSLATE_NOOP("elementName", "Measure")),
ElementName("Selection", QT_TRANSLATE_NOOP("elementName", "Selection")),
ElementName("Lasso", QT_TRANSLATE_NOOP("elementName", "Lasso")),
ElementName("ShadowNote", QT_TRANSLATE_NOOP("elementName", "Shadow Note")),
ElementName("TabDurationSymbol", QT_TRANSLATE_NOOP("elementName", "Tab Duration Symbol")),
ElementName("FSymbol", QT_TRANSLATE_NOOP("elementName", "Font Symbol")),
ElementName("Page", QT_TRANSLATE_NOOP("elementName", "Page")),
ElementName("HairPin", QT_TRANSLATE_NOOP("elementName", "Hairpin")),
ElementName("Ottava", QT_TRANSLATE_NOOP("elementName", "Ottava")),
ElementName("Pedal", QT_TRANSLATE_NOOP("elementName", "Pedal")),
ElementName("Trill", QT_TRANSLATE_NOOP("elementName", "Trill")),
ElementName("TextLine", QT_TRANSLATE_NOOP("elementName", "Text Line")),
ElementName("NoteLine", QT_TRANSLATE_NOOP("elementName", "Note Line")),
ElementName("LyricsLine", QT_TRANSLATE_NOOP("elementName", "Melisma Line")),
ElementName("Glissando", QT_TRANSLATE_NOOP("elementName", "Glissando")),
ElementName("Bracket", QT_TRANSLATE_NOOP("elementName", "Bracket")),
ElementName("Segment", QT_TRANSLATE_NOOP("elementName", "Segment")),
ElementName("System", QT_TRANSLATE_NOOP("elementName", "System")),
ElementName("Compound", QT_TRANSLATE_NOOP("elementName", "Compound")),
ElementName("Chord", QT_TRANSLATE_NOOP("elementName", "Chord")),
ElementName("Slur", QT_TRANSLATE_NOOP("elementName", "Slur")),
ElementName("Element", QT_TRANSLATE_NOOP("elementName", "Element")),
ElementName("ElementList", QT_TRANSLATE_NOOP("elementName", "Element List")),
ElementName("StaffList", QT_TRANSLATE_NOOP("elementName", "Staff List")),
ElementName("MeasureList", QT_TRANSLATE_NOOP("elementName", "Measure List")),
ElementName("HBox", QT_TRANSLATE_NOOP("elementName", "Horizontal Frame")),
ElementName("VBox", QT_TRANSLATE_NOOP("elementName", "Vertical Frame")),
ElementName("TBox", QT_TRANSLATE_NOOP("elementName", "Text Frame")),
ElementName("FBox", QT_TRANSLATE_NOOP("elementName", "Fretboard Diagram Frame")),
ElementName("Icon", QT_TRANSLATE_NOOP("elementName", "Icon")),
ElementName("Ossia", QT_TRANSLATE_NOOP("elementName", "Ossia")),
ElementName("BagpipeEmbellishment", QT_TRANSLATE_NOOP("elementName", "Bagpipe Embellishment"))
};
//---------------------------------------------------------
// DropData
//---------------------------------------------------------
DropData::DropData()
{
view = 0;
element = 0;
duration = Fraction(1,4);
modifiers = 0;
}
//---------------------------------------------------------
// spatiumChanged
//---------------------------------------------------------
void Element::spatiumChanged(qreal oldValue, qreal newValue)
{
_userOff *= (newValue / oldValue);
_readPos *= (newValue / oldValue);
}
//---------------------------------------------------------
// localSpatiumChanged
// the scale of a staff changed
//---------------------------------------------------------
void Element::localSpatiumChanged(qreal oldValue, qreal newValue)
{
_userOff *= (newValue / oldValue);
}
//---------------------------------------------------------
// spatium
//---------------------------------------------------------
qreal Element::spatium() const
{
Staff* s = staff();
return s ? s->spatium() : _score->spatium();
}
//---------------------------------------------------------
// magS
//---------------------------------------------------------
qreal Element::magS() const
{
return mag() * (_score->spatium() / (MScore::DPI * SPATIUM20));
}
//---------------------------------------------------------
// name
//---------------------------------------------------------
const char* Element::name() const
{
return name(type());
}
//---------------------------------------------------------
// name
//---------------------------------------------------------
QString Element::subtypeName() const
{
return "";
}
//---------------------------------------------------------
// userName
//---------------------------------------------------------
QString Element::userName() const
{
return qApp->translate("elementName", elementNames[int(type())].userName);
}
//---------------------------------------------------------
// ~Element
//---------------------------------------------------------
Element::~Element()
{
if (_links) {
_links->removeOne(this);
if (_links->isEmpty()) {
//DEBUG:
score()->links().remove(_links->lid());
//
delete _links;
}
}
}
//---------------------------------------------------------
// Element
//---------------------------------------------------------
Element::Element(Score* s) :
QObject(0), ScoreElement(s)
{
_selected = false;
_generated = false;
_visible = true;
_placement = Placement::BELOW;
_flags = ElementFlag::SELECTABLE;
_track = -1;
_color = MScore::defaultColor;
_mag = 1.0;
_tag = 1;
itemDiscovered = false;
}
Element::Element(const Element& e)
: QObject(0), ScoreElement(e)
{
_parent = e._parent;
_selected = e._selected;
_generated = e._generated;
_visible = e._visible;
_placement = e._placement;
_flags = e._flags;
_track = e._track;
_color = e._color;
_mag = e._mag;
_pos = e._pos;
_userOff = e._userOff;
_readPos = e._readPos;
_bbox = e._bbox;
_tag = e._tag;
itemDiscovered = false;
}
//---------------------------------------------------------
// linkedClone
//---------------------------------------------------------
Element* Element::linkedClone()
{
Element* e = clone();
score()->undo(new Link(this, e));
return e;
}
//---------------------------------------------------------
// adjustReadPos
//---------------------------------------------------------
void Element::adjustReadPos()
{
if (!_readPos.isNull()) {
_userOff = _readPos - _pos;
_readPos = QPointF();
}
}
//---------------------------------------------------------
// scanElements
//---------------------------------------------------------
void Element::scanElements(void* data, void (*func)(void*, Element*), bool all)
{
if (all || _visible || score()->showInvisible())
func(data, this);
}
//---------------------------------------------------------
// reset
//---------------------------------------------------------
void Element::reset()
{
if (!_userOff.isNull())
score()->undoChangeProperty(this, P_ID::USER_OFF, QPointF());
}
//---------------------------------------------------------
// change
//---------------------------------------------------------
void Element::change(Element* o, Element* n)
{
remove(o);
add(n);
}
//---------------------------------------------------------
// staff
//---------------------------------------------------------
Staff* Element::staff() const
{
if (_track == -1 || score()->staves().isEmpty())
return 0;
return score()->staff(staffIdx());
}
//---------------------------------------------------------
// part
//---------------------------------------------------------
Part* Element::part() const
{
Staff* s = staff();
return s ? s->part() : 0;
}
//---------------------------------------------------------
// curColor
//---------------------------------------------------------
QColor Element::curColor() const
{
return curColor(this);
}
//---------------------------------------------------------
// curColor
//---------------------------------------------------------
QColor Element::curColor(const Element* proxy) const
{
// the default element color is always interpreted as black in
// printing
if (score() && score()->printing())
return (proxy->color() == MScore::defaultColor) ? Qt::black : proxy->color();
if (flag(ElementFlag::DROP_TARGET))
return MScore::dropColor;
bool marked = false;
if (type() == Element::Type::NOTE) {
const Note* note = static_cast<const Note*>(this);
marked = note->mark();
}
if (proxy->selected() || marked ) {
if (track() == -1)
return MScore::selectColor[0];
else
return MScore::selectColor[voice()];
}
if (!proxy->visible())
return Qt::gray;
return proxy->color();
}
//---------------------------------------------------------
// drag
/// Return update Rect relative to canvas.
//---------------------------------------------------------
QRectF Element::drag(EditData* data)
{
QRectF r(canvasBoundingRect());
qreal x = data->delta.x();
qreal y = data->delta.y();
qreal _spatium = spatium();
if (data->hRaster) {
qreal hRaster = _spatium / MScore::hRaster();
int n = lrint(x / hRaster);
x = hRaster * n;
}
if (data->vRaster) {
qreal vRaster = _spatium / MScore::vRaster();
int n = lrint(y / vRaster);
y = vRaster * n;
}
setUserOff(QPointF(x, y));
setGenerated(false);
if (type() == Type::TEXT) { // TODO: check for other types
//
// restrict move to page boundaries
//
QRectF r(canvasBoundingRect());
Page* p = 0;
Element* e = this;
while (e) {
if (e->type() == Element::Type::PAGE) {
p = static_cast<Page*>(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)
setUserOff(QPointF(x, y));
}
}
return canvasBoundingRect() | r;
}
//---------------------------------------------------------
// 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 = nullptr;
if (parent()->type() == Element::Type::SEGMENT)
system = static_cast<Segment*>(parent())->measure()->system();
else if (parent()->type() == Element::Type::MEASURE) // used in measure number
system = static_cast<Measure*>(parent())->system();
else if (parent()->type() == Element::Type::SYSTEM)
system = static_cast<System*>(parent());
else
Q_ASSERT(false);
if (system) {
int si = staffIdx();
if (type() == Element::Type::CHORD || type() == Element::Type::REST)
si += static_cast<const ChordRest*>(this)->staffMove();
p.ry() += system->staffYpage(si); // system->staff(si)->y() + system->y();
}
p.rx() = pageX();
}
else {
if (parent()->parent())
p += parent()->pagePos();
}
return p;
}
//---------------------------------------------------------
// canvasPos
//---------------------------------------------------------
QPointF Element::canvasPos() const
{
QPointF p(pos());
if (parent() == 0)
return p;
if (_flags & ElementFlag::ON_STAFF) {
System* system = nullptr;
if (parent()->type() == Element::Type::SEGMENT)
system = static_cast<Segment*>(parent())->system();
else if (parent()->type() == Element::Type::MEASURE) // used in measure number
system = static_cast<Measure*>(parent())->system();
else if (parent()->type() == Element::Type::SYSTEM)
system = static_cast<System*>(parent());
else
Q_ASSERT(false);
if (system) {
int si = staffIdx();
if (type() == Element::Type::CHORD || type() == Element::Type::REST)
si += static_cast<const ChordRest*>(this)->staffMove();
p.ry() += system->staffYpage(si); // system->staff(si)->y() + system->y();
Page* page = system->page();
if (page)
p.ry() += page->y();
}
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 \a p is inside the shape of the object.
Note: \a p is in page coordinates
*/
bool Element::contains(const QPointF& p) const
{
return shape().contains(p - pagePos());
}
//---------------------------------------------------------
// shape
//---------------------------------------------------------
/**
Returns the shape of this element as a QPainterPath in local
coordinates. The shape is used for collision detection and
hit tests (contains())
The default implementation calls bbox() to return a simple rectangular
shape, but subclasses can reimplement this function to return a more
accurate shape for non-rectangular elements.
*/
QPainterPath Element::shape() const
{
QPainterPath pp;
pp.addRect(bbox());
return pp;
}
//---------------------------------------------------------
// 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(Xml& xml) const
{
//copy paste should not keep links
if (_links && (_links->size() > 1) && !xml.clipboardmode)
xml.tag("lid", _links->lid());
if (!userOff().isNull()) {
if (type() == Element::Type::VOLTA_SEGMENT
|| type() == Element::Type::GLISSANDO_SEGMENT || isChordRest())
xml.tag("offset", userOff() / spatium());
else
xml.tag("pos", pos() / score()->spatium());
}
if (((track() != xml.curTrack) || (type() == Element::Type::SLUR)) && (track() != -1)) {
int t;
t = track() + xml.trackDiff;
xml.tag("track", t);
}
if (_tag != 0x1) {
for (int i = 1; i < MAX_TAGS; i++) {
if (_tag == ((unsigned)1 << i)) {
xml.tag("tag", score()->layerTags()[i]);
break;
}
}
}
writeProperty(xml, P_ID::COLOR);
writeProperty(xml, P_ID::VISIBLE);
writeProperty(xml, P_ID::PLACEMENT);
}
//---------------------------------------------------------
// readProperties
//---------------------------------------------------------
bool Element::readProperties(XmlReader& e)
{
const QStringRef& tag(e.name());
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 == "userOff")
_userOff = e.readPoint();
else if (tag == "lid") {
int id = e.readInt();
_links = score()->links().value(id);
if (!_links) {
if (score()->parentScore()) // DEBUG
qDebug("---link %d not found (%d)", id, score()->links().size());
_links = new LinkedElements(score(), id);
score()->links().insert(id, _links);
}
#ifndef NDEBUG
else {
foreach(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();
// certain elements should not be allowed to reset tick
// these include any elements that occur within context of a Chord in a 1.X score
if (val >= 0) {
// if tick is valid, we should honor it
// but there are certain cases where we cannot
// - in 1.X scores, copy & paste of gliss resulted in invalid tick value on the new copy (#21211)
// the tick is not needed for glissandi anyhow, so we can ignore it
// - another bug allowed text items attached to notes or chords to also have invalid tick values (#25616)
// the text might be of any type, but we are now converting any text elements within notes into FINGERING
// - another bug allowed copy & paste of symbols attached to notes to produce invalid tick values (#56146)
// we can't ignore tick for all symbols, because it is needed for correct positioning of symbols attached to measures
// and it also can be relied upon by subsequent elements (http://musescore.org/en/node/25572)
// so honor tick only for elements attached to measures
// symbols attached to notes or other elements don't need the tick anyhow
if (score()->mscVersion() <= 114 && type() == Element::Type::SYMBOL) {
if (!parent() || parent()->type() != Element::Type::MEASURE)
val = -1;
}
if (score()->mscVersion() > 114 || (type() != Element::Type::GLISSANDO && type() != Element::Type::FINGERING && val >= 0))
e.initTick(score()->fileDivision(val));
}
}
else if (tag == "offset") {
setUserOff(e.readPoint() * spatium());
}
else if (tag == "pos") {
QPointF pt = e.readPoint();
if (score()->mscVersion() > 114)
_readPos = pt * score()->spatium();
}
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 (tag == "placement")
_placement = Placement(Ms::getProperty(P_ID::PLACEMENT, e).toInt());
else
return false;
return true;
}
//---------------------------------------------------------
// write
//---------------------------------------------------------
void Element::write(Xml& xml) const
{
xml.stag(name());
writeProperties(xml);
xml.etag();
}
//---------------------------------------------------------
// read
//---------------------------------------------------------
void Element::read(XmlReader& e)
{
while (e.readNextStartElement()) {
if (!readProperties(e))
e.unknown();
}
}
//---------------------------------------------------------
// startEdit
//---------------------------------------------------------
void Element::startEdit(MuseScoreView*, const QPointF&)
{
undoPushProperty(P_ID::USER_OFF);
}
//---------------------------------------------------------
// 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(Xml& xml) const
{
for (const Element* e : *this)
e->write(xml);
}
//---------------------------------------------------------
// StaffLines
//---------------------------------------------------------
StaffLines::StaffLines(Score* s)
: Element(s)
{
setWidth(1.0); // dummy
lines = 5;
setSelectable(false);
}
//---------------------------------------------------------
// pagePos
//---------------------------------------------------------
QPointF StaffLines::pagePos() const
{
System* system = measure()->system();
return QPointF(measure()->x() + system->x(),
system->staff(staffIdx())->y() + system->y());
}
//---------------------------------------------------------
// canvasPos
//---------------------------------------------------------
QPointF StaffLines::canvasPos() const
{
QPointF p(pagePos());
Element* e = parent();
while (e) {
if (e->type() == Element::Type::PAGE) {
p += e->pos();
break;
}
e = e->parent();
}
return p;
}
//---------------------------------------------------------
// layout
//---------------------------------------------------------
void StaffLines::layout()
{
StaffType* st = staff() ? staff()->staffType() : 0;
qreal _spatium = spatium();
if (st) {
dist = st->lineDistance().val() * _spatium;
lines = st->lines();
}
else {
dist = _spatium;
lines = 5;
}
// qDebug("StaffLines::layout:: dist %f st %p", dist, st);
setColor(staff() ? staff()->color() : MScore::defaultColor);
lw = score()->styleS(StyleIdx::staffLineWidth).val() * _spatium;
bbox().setRect(0.0, -lw*.5, width(), lines * dist + lw);
}
//---------------------------------------------------------
// draw
//---------------------------------------------------------
void StaffLines::draw(QPainter* painter) const
{
QPointF _pos(0.0, 0.0);
qreal x1 = _pos.x();
qreal x2 = x1 + width();
QVector<QLineF> ll(lines);
qreal y = _pos.y();
for (int i = 0; i < lines; ++i) {
ll[i].setLine(x1, y, x2, y);
y += dist;
}
if (MScore::debugMode) {
painter->setPen(QPen(Qt::lightGray, lw, Qt::SolidLine, Qt::FlatCap));
y = _pos.y() - 3 * dist;
painter->drawLine(QLineF(x1, y, x2, y));
y = _pos.y() - 2 * dist;
painter->drawLine(QLineF(x1, y, x2, y));
y = _pos.y() - dist;
painter->drawLine(QLineF(x1, y, x2, y));
y = _pos.y() + lines * dist;
painter->drawLine(QLineF(x1, y, x2, y));
y = _pos.y() + (lines+1) * dist;
painter->drawLine(QLineF(x1, y, x2, y));
y = _pos.y() + (lines+2) * dist;
painter->drawLine(QLineF(x1, y, x2, y));
y = _pos.y() + (lines+3) * dist;
painter->drawLine(QLineF(x1, y, x2, y));
y = _pos.y() + (lines+4) * dist;
painter->drawLine(QLineF(x1, y, x2, y));
}
painter->setPen(QPen(curColor(), lw, Qt::SolidLine, Qt::FlatCap));
painter->drawLines(ll);
}
//---------------------------------------------------------
// y1
//---------------------------------------------------------
qreal StaffLines::y1() const
{
System* system = measure()->system();
if (system == 0)
return 0.0;
return system->staff(staffIdx())->y() + ipos().y();
}
//---------------------------------------------------------
// Line
//---------------------------------------------------------
Line::Line(Score* s, bool v)
: Element(s)
{
vertical = v;
_z = int(Element::Type::LINE) * 100;
}
//---------------------------------------------------------
// dump
//---------------------------------------------------------
void Line::dump() const
{
qDebug(" width:%g height:%g vert:%d", point(_width), point(_len), vertical);
}
//---------------------------------------------------------
// setLen
//---------------------------------------------------------
void Line::setLen(Spatium l)
{
_len = l;
}
//---------------------------------------------------------
// setLineWidth
//---------------------------------------------------------
void Line::setLineWidth(Spatium w)
{
_width = w;
}
//---------------------------------------------------------
// layout
//---------------------------------------------------------
void Line::layout()
{
qreal sp = spatium();
qreal w = _width.val() * sp;
qreal l = _len.val() * sp;
qreal w2 = w * .5;
if (vertical)
bbox().setRect(-w2, -w2, w, l + w);
else
bbox().setRect(-w2, -w2, l + w, w);
}
//---------------------------------------------------------
// draw
//---------------------------------------------------------
void Line::draw(QPainter* painter) const
{
qreal sp = spatium();
painter->setPen(QPen(curColor(), _width.val() * sp));
qreal l = _len.val() * sp;
if (vertical)
painter->drawLine(QLineF(0.0, 0.0, 0.0, l));
else
painter->drawLine(QLineF(0.0, 0.0, l, 0.0));
}
//---------------------------------------------------------
// writeProperties
//---------------------------------------------------------
void Line::writeProperties(Xml& xml) const
{
xml.tag("lineWidth", _width.val());
xml.tag("lineLen", _len.val());
if (!vertical)
xml.tag("vertical", vertical);
}
//---------------------------------------------------------
// readProperties
//---------------------------------------------------------
bool Line::readProperties(XmlReader& e)
{
const QStringRef& tag(e.name());
if (tag == "lineWidth")
_width = Spatium(e.readDouble());
else if (tag == "lineLen")
_len = Spatium(e.readDouble());
else if (tag == "vertical")
vertical = e.readInt();
else
return false;
return true;
}
//---------------------------------------------------------
// 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);
Xml xml(&buffer);
xml.clipboardmode = true;
xml.stag("Element");
if (type() == Element::Type::NOTE)
xml.fTag("duration", static_cast<const Note*>(this)->chord()->duration());
if (!dragOffset.isNull())
xml.tag("dragOffset", dragOffset);
write(xml);
xml.etag();
buffer.close();
return buffer.buffer();
}
//---------------------------------------------------------
// readType
// return new position of QDomElement in e
//---------------------------------------------------------
Element::Type 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 {
Element::Type type = name2type(tag);
if (type == Element::Type::INVALID)
break;
return type;
}
}
else
e.unknown();
}
return Element::Type::INVALID;
}
//---------------------------------------------------------
// editDrag
//---------------------------------------------------------
void Element::editDrag(const EditData& ed)
{
score()->addRefresh(canvasBoundingRect());
setUserOff(userOff() + ed.delta);
score()->addRefresh(canvasBoundingRect());
}
//---------------------------------------------------------
// edit
// return true if event is accepted
//---------------------------------------------------------
bool Element::edit(MuseScoreView*, Grip, int key, Qt::KeyboardModifiers, const QString&)
{
if (key == Qt::Key_Home) {
setUserOff(QPoint());
return true;
}
return false;
}
//---------------------------------------------------------
// 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(Element::Type type, Score* score)
{
switch(type) {
case Element::Type::VOLTA: return new Volta(score);
case Element::Type::OTTAVA: return new Ottava(score);
case Element::Type::TEXTLINE: return new TextLine(score);
case Element::Type::NOTELINE: return new NoteLine(score);
case Element::Type::LYRICSLINE: return new LyricsLine(score);
case Element::Type::TRILL: return new Trill(score);
case Element::Type::PEDAL: return new Pedal(score);
case Element::Type::HAIRPIN: return new Hairpin(score);
case Element::Type::CLEF: return new Clef(score);
case Element::Type::KEYSIG: return new KeySig(score);
case Element::Type::TIMESIG: return new TimeSig(score);
case Element::Type::BAR_LINE: return new BarLine(score);
case Element::Type::ARPEGGIO: return new Arpeggio(score);
case Element::Type::BREATH: return new Breath(score);
case Element::Type::GLISSANDO: return new Glissando(score);
case Element::Type::BRACKET: return new Bracket(score);
case Element::Type::ARTICULATION: return new Articulation(score);
case Element::Type::CHORDLINE: return new ChordLine(score);
case Element::Type::ACCIDENTAL: return new Accidental(score);
case Element::Type::DYNAMIC: return new Dynamic(score);
case Element::Type::TEXT: return new Text(score);
case Element::Type::INSTRUMENT_NAME: return new InstrumentName(score);
case Element::Type::STAFF_TEXT: return new StaffText(score);
case Element::Type::REHEARSAL_MARK: return new RehearsalMark(score);
case Element::Type::INSTRUMENT_CHANGE: return new InstrumentChange(score);
case Element::Type::NOTEHEAD: return new NoteHead(score);
case Element::Type::NOTEDOT: return new NoteDot(score);
case Element::Type::TREMOLO: return new Tremolo(score);
case Element::Type::LAYOUT_BREAK: return new LayoutBreak(score);
case Element::Type::MARKER: return new Marker(score);
case Element::Type::JUMP: return new Jump(score);
case Element::Type::REPEAT_MEASURE: return new RepeatMeasure(score);
case Element::Type::ICON: return new Icon(score);
case Element::Type::NOTE: return new Note(score);
case Element::Type::SYMBOL: return new Symbol(score);
case Element::Type::FSYMBOL: return new FSymbol(score);
case Element::Type::CHORD: return new Chord(score);
case Element::Type::REST: return new Rest(score);
case Element::Type::SPACER: return new Spacer(score);
case Element::Type::STAFF_STATE: return new StaffState(score);
case Element::Type::TEMPO_TEXT: return new TempoText(score);
case Element::Type::HARMONY: return new Harmony(score);
case Element::Type::FRET_DIAGRAM: return new FretDiagram(score);
case Element::Type::BEND: return new Bend(score);
case Element::Type::TREMOLOBAR: return new TremoloBar(score);
case Element::Type::LYRICS: return new Lyrics(score);
case Element::Type::FIGURED_BASS: return new FiguredBass(score);
case Element::Type::STEM: return new Stem(score);
case Element::Type::SLUR: return new Slur(score);
case Element::Type::FINGERING: return new Fingering(score);
case Element::Type::HBOX: return new HBox(score);
case Element::Type::VBOX: return new VBox(score);
case Element::Type::TBOX: return new TBox(score);
case Element::Type::FBOX: return new FBox(score);
case Element::Type::MEASURE: return new Measure(score);
case Element::Type::TAB_DURATION_SYMBOL: return new TabDurationSymbol(score);
case Element::Type::OSSIA: return new Ossia(score);
case Element::Type::IMAGE: return new Image(score);
case Element::Type::BAGPIPE_EMBELLISHMENT: return new BagpipeEmbellishment(score);
case Element::Type::AMBITUS: return new Ambitus(score);
case Element::Type::TEXTLINE_SEGMENT: // return new TextLineSegment(score);
case Element::Type::GLISSANDO_SEGMENT:
case Element::Type::SLUR_SEGMENT:
case Element::Type::STEM_SLASH:
case Element::Type::LINE:
case Element::Type::TIE:
case Element::Type::PAGE:
case Element::Type::BEAM:
case Element::Type::HOOK:
case Element::Type::TUPLET:
case Element::Type::HAIRPIN_SEGMENT:
case Element::Type::OTTAVA_SEGMENT:
case Element::Type::TRILL_SEGMENT:
case Element::Type::VOLTA_SEGMENT:
case Element::Type::PEDAL_SEGMENT:
case Element::Type::LYRICSLINE_SEGMENT:
case Element::Type::LEDGER_LINE:
case Element::Type::STAFF_LINES:
case Element::Type::SELECTION:
case Element::Type::LASSO:
case Element::Type::SHADOW_NOTE:
case Element::Type::SEGMENT:
case Element::Type::SYSTEM:
case Element::Type::COMPOUND:
case Element::Type::ELEMENT:
case Element::Type::ELEMENT_LIST:
case Element::Type::STAFF_LIST:
case Element::Type::MEASURE_LIST:
case Element::Type::MAXTYPE:
case Element::Type::INVALID: break;
}
qDebug("cannot create type %d <%s>", int(type), Element::name(type));
return 0;
}
//---------------------------------------------------------
// name
//---------------------------------------------------------
const char* Element::name(Element::Type type)
{
return elementNames[int(type)].name;
}
//---------------------------------------------------------
// name2type
//---------------------------------------------------------
Element::Type Element::name2type(const QStringRef& s)
{
for (int i = 0; i < int(Element::Type::MAXTYPE); ++i) {
if (s == elementNames[i].name)
return Element::Type(i);
}
qDebug("name2type: invalid type <%s>", s.toUtf8().data());
return Element::Type::INVALID;
}
//---------------------------------------------------------
// name2Element
//---------------------------------------------------------
Element* Element::name2Element(const QStringRef& s, Score* sc)
{
Element::Type type = Element::name2type(s);
if (type == Element::Type::INVALID)
return 0;
return Element::create(type, sc);
}
//---------------------------------------------------------
// elementLessThan
//---------------------------------------------------------
bool elementLessThan(const Element* const e1, const Element* const e2)
{
return e1->z() <= e2->z();
}
//---------------------------------------------------------
// getGrip
//---------------------------------------------------------
QPointF Element::getGrip(Grip) const
{
qreal _spatium = score()->spatium();
return QPointF(userOff().x() / _spatium, userOff().y() / _spatium);
}
//---------------------------------------------------------
// setGrip
//---------------------------------------------------------
void Element::setGrip(Grip, const QPointF& pt)
{
qreal _spatium = score()->spatium();
setUserOff(QPointF(pt.x() * _spatium, pt.y() * _spatium));
}
//---------------------------------------------------------
// collectElements
//---------------------------------------------------------
void collectElements(void* data, Element* e)
{
QList<Element*>* el = static_cast<QList<Element*>*>(data);
el->append(e);
}
//---------------------------------------------------------
// undoSetPlacement
//---------------------------------------------------------
void Element::undoSetPlacement(Placement v)
{
score()->undoChangeProperty(this, P_ID::PLACEMENT, int(v));
}
//---------------------------------------------------------
// getProperty
//---------------------------------------------------------
QVariant Element::getProperty(P_ID propertyId) const
{
switch (propertyId) {
case P_ID::TRACK: return track();
case P_ID::GENERATED: return _generated;
case P_ID::COLOR: return color();
case P_ID::VISIBLE: return _visible;
case P_ID::SELECTED: return _selected;
case P_ID::USER_OFF: return _userOff;
case P_ID::PLACEMENT: return int(_placement);
default:
return QVariant();
}
}
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
bool Element::setProperty(P_ID propertyId, const QVariant& v)
{
switch (propertyId) {
case P_ID::TRACK:
setTrack(v.toInt());
break;
case P_ID::GENERATED:
_generated = v.toBool();
break;
case P_ID::COLOR:
setColor(v.value<QColor>());
break;
case P_ID::VISIBLE:
setVisible(v.toBool());
break;
case P_ID::SELECTED:
setSelected(v.toBool());
break;
case P_ID::USER_OFF:
score()->addRefresh(canvasBoundingRect());
_userOff = v.toPointF();
break;
case P_ID::PLACEMENT:
_placement = Placement(v.toInt());
break;
default:
qFatal("Element::setProperty: unknown <%s>(%hhd), data <%s>",
propertyName(propertyId), propertyId, qPrintable(v.toString()));
return false;
}
setGenerated(false);
score()->addRefresh(canvasBoundingRect());
return true;
}
//---------------------------------------------------------
// propertyDefault
//---------------------------------------------------------
QVariant Element::propertyDefault(P_ID id) const
{
switch(id) {
case P_ID::GENERATED:
return false;
case P_ID::VISIBLE:
return true;
case P_ID::COLOR:
return MScore::defaultColor;
case P_ID::PLACEMENT:
return int(Placement::BELOW);
case P_ID::SELECTED:
return false;
case P_ID::USER_OFF:
return QPointF();
default: // not all properties have a default
break;
}
return QVariant();
}
//---------------------------------------------------------
// isSLine
//---------------------------------------------------------
bool Element::isSLine() const
{
return type() == Element::Type::HAIRPIN || type() == Element::Type::OTTAVA || type() == Element::Type::PEDAL
|| type() == Element::Type::TRILL || type() == Element::Type::VOLTA || type() == Element::Type::TEXTLINE || type() == Element::Type::NOTELINE;
}
//---------------------------------------------------------
// isText
//---------------------------------------------------------
bool Element::isText() const
{
return type() == Element::Type::TEXT
|| type() == Element::Type::LYRICS
|| type() == Element::Type::DYNAMIC
|| type() == Element::Type::FINGERING
|| type() == Element::Type::HARMONY
|| type() == Element::Type::MARKER
|| type() == Element::Type::JUMP
|| type() == Element::Type::STAFF_TEXT
|| type() == Element::Type::REHEARSAL_MARK
|| type() == Element::Type::INSTRUMENT_CHANGE
|| type() == Element::Type::FIGURED_BASS
|| type() == Element::Type::TEMPO_TEXT
|| type() == Element::Type::INSTRUMENT_NAME
;
}
//---------------------------------------------------------
// isPrintable
//---------------------------------------------------------
bool Element::isPrintable() const
{
switch (type()) {
case Element::Type::PAGE:
case Element::Type::SYSTEM:
case Element::Type::MEASURE:
case Element::Type::SEGMENT:
case Element::Type::VBOX:
case Element::Type::HBOX:
case Element::Type::TBOX:
case Element::Type::FBOX:
case Element::Type::SPACER:
case Element::Type::SHADOW_NOTE:
case Element::Type::LASSO:
case Element::Type::ELEMENT_LIST:
case Element::Type::STAFF_LIST:
case Element::Type::MEASURE_LIST:
case Element::Type::SELECTION:
return false;
default:
return true;
}
}
//---------------------------------------------------------
// findMeasure
//---------------------------------------------------------
Element* Element::findMeasure()
{
if (type() == Element::Type::MEASURE)
return this;
else if (_parent)
return _parent->findMeasure();
else
return 0;
}
//---------------------------------------------------------
// undoSetColor
//---------------------------------------------------------
void Element::undoSetColor(const QColor& c)
{
score()->undoChangeProperty(this, P_ID::COLOR, c);
}
//---------------------------------------------------------
// undoSetVisible
//---------------------------------------------------------
void Element::undoSetVisible(bool v)
{
score()->undoChangeProperty(this, P_ID::VISIBLE, v);
}
//---------------------------------------------------------
// bbox() function for scripts
//
// use spatium units rather than raster units
//---------------------------------------------------------
QRectF Element::scriptBbox() const
{
qreal _sp = spatium();
QRectF _bbox = bbox();
return QRectF(_bbox.x() / _sp, _bbox.y() / _sp, _bbox.width() / _sp, _bbox.height() / _sp);
}
//---------------------------------------------------------
// positioning functions for scripts
//
// use spatium units rather than raster units
// are undoable
// route pos changes to usefOff
//---------------------------------------------------------
QPointF Element::scriptPagePos() const
{
return pagePos() / spatium();
}
QPointF Element::scriptPos() const
{
return (_pos + _userOff) / spatium();
}
void Element::scriptSetPos(const QPointF& p)
{
score()->undoChangeProperty(this, P_ID::USER_OFF, p*spatium() - ipos());
}
QPointF Element::scriptUserOff() const
{
return _userOff / spatium();
}
void Element::scriptSetUserOff(const QPointF& o)
{
score()->undoChangeProperty(this, P_ID::USER_OFF, o * spatium());
}
//void Element::draw(SymId id, QPainter* p) const { score()->scoreFont()->draw(id, p, magS()); }
//---------------------------------------------------------
// drawSymbol
//---------------------------------------------------------
void Element::drawSymbol(SymId id, QPainter* p, const QPointF& o) const
{
score()->scoreFont()->draw(id, p, magS(), 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 QList<SymId>& s, QPainter* p, const QPointF& o) const
{
score()->scoreFont()->draw(s, p, magS(), 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 QList<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 QList<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);
}
//---------------------------------------------------------
// toTimeSigString
//---------------------------------------------------------
QList<SymId> Element::toTimeSigString(const QString& s) const
{
QList<SymId> d;
for (int i = 0; i < s.size(); ++i) {
switch (s[i].toLatin1()) {
case '+': d += SymId::timeSigPlusSmall; break;
case '0': d += SymId::timeSig0; break;
case '1': d += SymId::timeSig1; break;
case '2': d += SymId::timeSig2; break;
case '3': d += SymId::timeSig3; break;
case '4': d += SymId::timeSig4; break;
case '5': d += SymId::timeSig5; break;
case '6': d += SymId::timeSig6; break;
case '7': d += SymId::timeSig7; break;
case '8': d += SymId::timeSig8; break;
case '9': d += SymId::timeSig9; break;
case 'C': d += SymId::timeSigCommon; break;
case 'O': d += SymId::mensuralProlation2; break;
case '(': d += SymId::timeSigParensLeftSmall; break;
case ')': d += SymId::timeSigParensRightSmall; break;
case '\xA2': d += SymId::timeSigCutCommon; break; // '¢'
case '\xD8': d += SymId::mensuralProlation3; break; // 'Ø'
default: break; // d += s[i]; break;
}
}
return d;
}
//---------------------------------------------------------
// concertPitch
//---------------------------------------------------------
bool Element::concertPitch() const
{
return score()->styleB(StyleIdx::concertPitch);
}
//------------------------------------------------------------------------------------------
// nextElement
// 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::nextElement()
{
Element* p = this;
while (p) {
switch (p->type()) {
case Element::Type::NOTE:
if(static_cast<Note*>(p)->chord()->isGrace())
break;
return p;
case Element::Type::REST:
return p;
case Element::Type::CHORD: {
Chord* c = static_cast<Chord*>(p);
if (!c->isGrace())
return c->notes().back();
}
break;
case Element::Type::SEGMENT: {
Segment* s = static_cast<Segment*>(p);
return s->firstElement(staffIdx());
}
case Element::Type::MEASURE: {
Measure* m = static_cast<Measure*>(p);
return m->nextElementStaff(staffIdx());
}
case Element::Type::SYSTEM: {
System* sys = static_cast<System*>(p);
return sys->nextElement();
}
default:
break;
}
p = p->parent();
}
return score()->firstElement();
}
//------------------------------------------------------------------------------------------
// prevElement
// 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::prevElement()
{
Element* p = this;
while (p) {
switch (p->type()) {
case Element::Type::NOTE:
if(static_cast<Note*>(p)->chord()->isGrace())
break;
return p;
case Element::Type::REST:
return p;
case Element::Type::CHORD: {
Chord* c = static_cast<Chord*>(p);
if (!c->isGrace())
return c->notes().first();
}
break;
case Element::Type::SEGMENT: {
Segment* s = static_cast<Segment*>(p);
return s->lastElement(staffIdx());
}
case Element::Type::MEASURE: {
Measure* m = static_cast<Measure*>(p);
return m->prevElementStaff(staffIdx());
}
case Element::Type::SYSTEM: {
System* sys = static_cast<System*>(p);
return sys->prevElement();
}
default:
break;
}
p = p->parent();
}
return score()->firstElement();
}
//---------------------------------------------------------
// accessibleInfo
//---------------------------------------------------------
QString Element::accessibleInfo()
{
return userName();
}
//---------------------------------------------------------
// nextGrip
//---------------------------------------------------------
bool Element::nextGrip(Grip* grip) const
{
int i = int(*grip) + 1;
if (i >= grips()) {
*grip = Grip(0);
return false;
}
*grip = Grip(i);
return true;
}
//---------------------------------------------------------
// prevGrip
//---------------------------------------------------------
bool Element::prevGrip(Grip* grip) const
{
int i = int(*grip) - 1;
if (i < 0) {
*grip = Grip(grips() - 1);
return false;
}
*grip = Grip(i);
return true;
}
//---------------------------------------------------------
// isUserModified
// Check if this element was modified by user and
// therefore must be saved.
//---------------------------------------------------------
bool Element::isUserModified() const
{
return !visible() || !userOff().isNull() || (color() != MScore::defaultColor);
}
}