MuseScore/libmscore/chordrest.cpp

1304 lines
48 KiB
C++
Raw Normal View History

2012-05-26 14:26:10 +02:00
//=============================================================================
// 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
//=============================================================================
#include "chordrest.h"
#include "chord.h"
#include "xml.h"
#include "style.h"
#include "system.h"
#include "measure.h"
#include "staff.h"
#include "tuplet.h"
#include "score.h"
#include "sym.h"
#include "slur.h"
#include "beam.h"
#include "breath.h"
#include "barline.h"
#include "articulation.h"
#include "tempo.h"
#include "tempotext.h"
#include "note.h"
#include "arpeggio.h"
#include "dynamic.h"
#include "stafftext.h"
#include "sig.h"
#include "clef.h"
#include "lyrics.h"
#include "segment.h"
#include "stafftype.h"
#include "undo.h"
#include "stem.h"
#include "harmony.h"
#include "figuredbass.h"
#include "icon.h"
2013-05-13 18:49:17 +02:00
namespace Ms {
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// hasArticulation
//---------------------------------------------------------
Articulation* ChordRest::hasArticulation(const Articulation* aa)
{
ArticulationType idx = aa->articulationType();
2012-11-20 20:51:18 +01:00
foreach(Articulation* a, _articulations) {
if (idx == a->articulationType())
2012-05-26 14:26:10 +02:00
return a;
}
return 0;
}
//---------------------------------------------------------
// ChordRest
//---------------------------------------------------------
ChordRest::ChordRest(Score* s)
: DurationElement(s)
{
2013-07-11 14:44:35 +02:00
_staffMove = 0;
_beam = 0;
2013-07-11 14:44:35 +02:00
_tabDur = 0;
_beamMode = Beam::Mode::AUTO;
_up = true;
2013-07-11 14:44:35 +02:00
_small = false;
_crossMeasure = CrossMeasure::UNKNOWN;
2012-05-26 14:26:10 +02:00
}
2014-06-27 13:41:49 +02:00
ChordRest::ChordRest(const ChordRest& cr, bool link)
2012-05-26 14:26:10 +02:00
: DurationElement(cr)
{
_durationType = cr._durationType;
_staffMove = cr._staffMove;
2013-07-11 14:44:35 +02:00
_beam = 0;
2012-05-26 14:26:10 +02:00
_tabDur = 0; // tab sur. symb. depends upon context: can't be
// simply copied from another CR
2014-06-27 13:41:49 +02:00
for (Articulation* a : cr._articulations) { // make deep copy
2012-05-26 14:26:10 +02:00
Articulation* na = new Articulation(*a);
2014-06-27 13:41:49 +02:00
if (link)
na->linkTo(a);
2012-05-26 14:26:10 +02:00
na->setParent(this);
na->setTrack(track());
2012-11-20 20:51:18 +01:00
_articulations.append(na);
2012-05-26 14:26:10 +02:00
}
2013-07-11 14:44:35 +02:00
_beamMode = cr._beamMode;
_up = cr._up;
_small = cr._small;
_crossMeasure = cr._crossMeasure;
_space = cr._space;
2012-05-26 14:26:10 +02:00
2014-07-09 18:16:16 +02:00
for (Lyrics* l : cr._lyricsList) { // make deep copy
if (l == 0)
continue;
2012-05-26 14:26:10 +02:00
Lyrics* nl = new Lyrics(*l);
2014-06-27 13:41:49 +02:00
if (link)
nl->linkTo(l);
2012-05-26 14:26:10 +02:00
nl->setParent(this);
nl->setTrack(track());
_lyricsList.append(nl);
}
}
2014-08-07 10:55:36 +02:00
//---------------------------------------------------------
// undoUnlink
//---------------------------------------------------------
void ChordRest::undoUnlink()
{
DurationElement::undoUnlink();
for (Articulation* a : _articulations)
a->undoUnlink();
for (Lyrics* l : _lyricsList)
l->undoUnlink();
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// ChordRest
//---------------------------------------------------------
ChordRest::~ChordRest()
{
2013-06-10 11:03:34 +02:00
foreach (Articulation* a, _articulations)
2012-05-26 14:26:10 +02:00
delete a;
2013-06-10 11:03:34 +02:00
foreach (Lyrics* l, _lyricsList)
2012-05-26 14:26:10 +02:00
delete l;
2013-06-10 11:03:34 +02:00
if (_tabDur)
delete _tabDur;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// scanElements
//---------------------------------------------------------
void ChordRest::scanElements(void* data, void (*func)(void*, Element*), bool all)
{
if (_beam && (_beam->elements().front() == this)
&& !measure()->slashStyle(staffIdx()))
2012-05-26 14:26:10 +02:00
_beam->scanElements(data, func, all);
2012-11-20 20:51:18 +01:00
foreach(Articulation* a, _articulations)
2012-05-26 14:26:10 +02:00
func(data, a);
foreach(Lyrics* l, _lyricsList) {
if (l)
l->scanElements(data, func, all);
}
DurationElement* de = this;
while (de->tuplet() && de->tuplet()->elements().front() == de) {
func(data, de->tuplet());
de = de->tuplet();
}
if (_tabDur)
func(data, _tabDur);
}
//---------------------------------------------------------
// writeProperties
//---------------------------------------------------------
void ChordRest::writeProperties(Xml& xml) const
{
DurationElement::writeProperties(xml);
//
// Beam::Mode default:
// REST - Beam::Mode::NONE
// CHORD - Beam::Mode::AUTO
2012-05-26 14:26:10 +02:00
//
if ((type() == Element::Type::REST && _beamMode != Beam::Mode::NONE)
|| (type() == Element::Type::CHORD && _beamMode != Beam::Mode::AUTO)) {
2012-05-26 14:26:10 +02:00
QString s;
switch(_beamMode) {
case Beam::Mode::AUTO: s = "auto"; break;
case Beam::Mode::BEGIN: s = "begin"; break;
case Beam::Mode::MID: s = "mid"; break;
case Beam::Mode::END: s = "end"; break;
case Beam::Mode::NONE: s = "no"; break;
case Beam::Mode::BEGIN32: s = "begin32"; break;
case Beam::Mode::BEGIN64: s = "begin64"; break;
case Beam::Mode::INVALID: s = "?"; break;
2012-05-26 14:26:10 +02:00
}
xml.tag("BeamMode", s);
}
2014-05-26 18:18:01 +02:00
writeProperty(xml, P_ID::SMALL);
2013-05-12 12:51:42 +02:00
if (actualDurationType().dots())
xml.tag("dots", actualDurationType().dots());
2014-08-20 09:50:42 +02:00
writeProperty(xml, P_ID::STAFF_MOVE);
2013-05-12 12:51:42 +02:00
if (actualDurationType().isValid())
xml.tag("durationType", actualDurationType().name());
2012-05-26 14:26:10 +02:00
2013-05-12 12:51:42 +02:00
if (!duration().isZero() && (!actualDurationType().fraction().isValid()
|| (actualDurationType().fraction() != duration())))
2012-05-26 14:26:10 +02:00
xml.fTag("duration", duration());
2014-10-10 14:24:37 +02:00
2012-11-20 20:51:18 +01:00
foreach(const Articulation* a, _articulations)
2012-05-26 14:26:10 +02:00
a->write(xml);
#ifndef NDEBUG
if (_beam && (MScore::testMode || !_beam->generated()))
2012-05-26 14:26:10 +02:00
xml.tag("Beam", _beam->id());
#else
if (_beam && !_beam->generated())
2012-05-26 14:26:10 +02:00
xml.tag("Beam", _beam->id());
#endif
foreach(Lyrics* lyrics, _lyricsList) {
if (lyrics)
lyrics->write(xml);
}
2013-06-19 16:25:29 +02:00
if (!isGrace()) {
Fraction t(globalDuration());
if (staff())
t *= staff()->timeStretch(xml.curTick);
xml.curTick += t.ticks();
}
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// readProperties
//---------------------------------------------------------
2013-01-11 18:10:18 +01:00
bool ChordRest::readProperties(XmlReader& e)
2012-05-26 14:26:10 +02:00
{
2013-01-11 18:10:18 +01:00
const QStringRef& tag(e.name());
2012-05-26 14:26:10 +02:00
2013-05-29 10:31:26 +02:00
if (tag == "durationType") {
setDurationType(e.readElementText());
if (actualDurationType().type() != TDuration::DurationType::V_MEASURE) {
if ((type() == Element::Type::REST) &&
2013-05-29 10:31:26 +02:00
// for backward compatibility, convert V_WHOLE rests to V_MEASURE
// if long enough to fill a measure.
// OTOH, freshly created (un-initialized) rests have numerator == 0 (< 4/4)
// (see Fraction() constructor in fraction.h; this happens for instance
// when pasting selection from clipboard): they should not be converted
duration().numerator() != 0 &&
// rest durations are initialized to full measure duration when
// created upon reading the <Rest> tag (see Measure::read() )
// so a V_WHOLE rest in a measure of 4/4 or less => V_MEASURE
(actualDurationType()==TDuration::DurationType::V_WHOLE && duration() <= Fraction(4, 4)) ) {
2013-05-29 10:31:26 +02:00
// old pre 2.0 scores: convert
setDurationType(TDuration::DurationType::V_MEASURE);
2013-05-29 10:31:26 +02:00
}
else // not from old score: set duration fraction from duration type
setDuration(actualDurationType().fraction());
}
else {
if (score()->mscVersion() < 115) {
SigEvent event = score()->sigmap()->timesig(e.tick());
setDuration(event.timesig());
}
}
}
else if (tag == "BeamMode") {
2013-01-11 18:10:18 +01:00
QString val(e.readElementText());
Beam::Mode bm = Beam::Mode::AUTO;
2012-05-26 14:26:10 +02:00
if (val == "auto")
bm = Beam::Mode::AUTO;
2012-05-26 14:26:10 +02:00
else if (val == "begin")
bm = Beam::Mode::BEGIN;
2012-05-26 14:26:10 +02:00
else if (val == "mid")
bm = Beam::Mode::MID;
2012-05-26 14:26:10 +02:00
else if (val == "end")
bm = Beam::Mode::END;
2012-05-26 14:26:10 +02:00
else if (val == "no")
bm = Beam::Mode::NONE;
2012-05-26 14:26:10 +02:00
else if (val == "begin32")
bm = Beam::Mode::BEGIN32;
2012-05-26 14:26:10 +02:00
else if (val == "begin64")
bm = Beam::Mode::BEGIN64;
2012-05-26 14:26:10 +02:00
else
bm = Beam::Mode(val.toInt());
_beamMode = Beam::Mode(bm);
2012-05-26 14:26:10 +02:00
}
else if (tag == "Attribute" || tag == "Articulation") { // obsolete: "Attribute"
Articulation* atr = new Articulation(score());
atr->setTrack(track());
2012-05-26 14:26:10 +02:00
atr->read(e);
add(atr);
}
else if (tag == "leadingSpace") {
2013-01-11 18:10:18 +01:00
qDebug("ChordRest: leadingSpace obsolete"); // _extraLeadingSpace = Spatium(val.toDouble());
2013-01-22 21:53:45 +01:00
e.skipCurrentElement();
2012-05-26 14:26:10 +02:00
}
else if (tag == "trailingSpace") {
2013-01-11 18:10:18 +01:00
qDebug("ChordRest: trailingSpace obsolete"); // _extraTrailingSpace = Spatium(val.toDouble());
2013-01-22 21:53:45 +01:00
e.skipCurrentElement();
2012-05-26 14:26:10 +02:00
}
else if (tag == "Beam") {
2013-01-11 18:10:18 +01:00
int id = e.readInt();
2013-01-21 20:21:41 +01:00
Beam* beam = e.findBeam(id);
2012-05-26 14:26:10 +02:00
if (beam)
beam->add(this); // also calls this->setBeam(beam)
else
2013-01-11 18:10:18 +01:00
qDebug("Beam id %d not found", id);
2012-05-26 14:26:10 +02:00
}
else if (tag == "small")
2013-01-11 18:10:18 +01:00
_small = e.readInt();
2012-05-26 14:26:10 +02:00
else if (tag == "duration")
2013-01-11 18:10:18 +01:00
setDuration(e.readFraction());
2012-05-26 14:26:10 +02:00
else if (tag == "ticklen") { // obsolete (version < 1.12)
2013-01-21 20:21:41 +01:00
int mticks = score()->sigmap()->timesig(e.tick()).timesig().ticks();
2013-01-11 18:10:18 +01:00
int i = e.readInt();
2012-05-26 14:26:10 +02:00
if (i == 0)
i = mticks;
if ((type() == Element::Type::REST) && (mticks == i)) {
setDurationType(TDuration::DurationType::V_MEASURE);
2012-05-26 14:26:10 +02:00
setDuration(Fraction::fromTicks(i));
}
else {
Fraction f = Fraction::fromTicks(i);
setDuration(f);
setDurationType(TDuration(f));
}
}
else if (tag == "dots")
2013-01-11 18:10:18 +01:00
setDots(e.readInt());
2012-05-26 14:26:10 +02:00
else if (tag == "move")
2013-01-11 18:10:18 +01:00
_staffMove = e.readInt();
2014-07-18 18:14:46 +02:00
else if (tag == "Slur") {
int id = e.intAttribute("id");
if (id == 0)
id = e.intAttribute("number"); // obsolete
2014-07-21 13:24:21 +02:00
Spanner* spanner = e.findSpanner(id);
2014-07-18 18:14:46 +02:00
if (!spanner)
qDebug("ChordRest::read(): Slur id %d not found", id);
else {
QString atype(e.attribute("type"));
if (atype == "start") {
2014-08-07 10:18:50 +02:00
spanner->setTick(e.tick());
2014-08-23 12:31:15 +02:00
if (spanner->ticks() > 0) // stop has been read first, ticks is tick2 - (-1)
spanner->setTick2(spanner->ticks() - 1);
2014-08-07 10:18:50 +02:00
spanner->setTrack(track());
if (spanner->type() == Element::Type::SLUR)
spanner->setStartElement(this);
2014-07-30 12:34:56 +02:00
if (e.pasteMode()) {
2014-08-07 10:18:50 +02:00
for (Element* e : spanner->linkList()) {
if (e == spanner)
2014-07-30 12:34:56 +02:00
continue;
2014-08-07 10:18:50 +02:00
Spanner* ls = static_cast<Spanner*>(e);
ls->setTick(spanner->tick());
2014-07-30 12:34:56 +02:00
for (Element* ee : linkList()) {
ChordRest* cr = static_cast<ChordRest*>(ee);
if (cr->score() == ee->score() && cr->staffIdx() == ls->staffIdx()) {
ls->setTrack(cr->track());
2014-08-07 10:18:50 +02:00
if (ls->type() == Element::Type::SLUR)
ls->setStartElement(cr);
2014-07-30 12:34:56 +02:00
break;
}
}
}
}
2014-07-18 18:14:46 +02:00
}
else if (atype == "stop") {
2014-08-07 10:18:50 +02:00
spanner->setTick2(e.tick());
spanner->setTrack2(track());
if (spanner->type() == Element::Type::SLUR)
spanner->setEndElement(this);
Chord* start = static_cast<Chord*>(spanner->startElement());
2014-07-18 18:14:46 +02:00
if (start)
2014-08-07 10:18:50 +02:00
spanner->setTrack(start->track());
2014-07-30 12:34:56 +02:00
if (e.pasteMode()) {
2014-08-07 10:18:50 +02:00
for (Element* e : spanner->linkList()) {
if (e == spanner)
2014-07-30 12:34:56 +02:00
continue;
2014-08-07 10:18:50 +02:00
Spanner* ls = static_cast<Spanner*>(e);
ls->setTick2(spanner->tick2());
2014-07-30 12:34:56 +02:00
for (Element* ee : linkList()) {
ChordRest* cr = static_cast<ChordRest*>(ee);
if (cr->score() == ee->score() && cr->staffIdx() == ls->staffIdx()) {
ls->setTrack2(cr->track());
2014-08-07 10:18:50 +02:00
if (ls->type() == Element::Type::SLUR)
ls->setEndElement(cr);
2014-07-30 12:34:56 +02:00
break;
}
}
}
}
2014-07-18 18:14:46 +02:00
}
else
qDebug("ChordRest::read(): unknown Slur type <%s>", qPrintable(atype));
}
e.readNext();
}
2012-05-26 14:26:10 +02:00
else if (tag == "Lyrics" /*|| tag == "FiguredBass"*/) {
2013-01-18 10:55:52 +01:00
Element* element = Element::name2Element(tag, score());
2013-01-21 20:21:41 +01:00
element->setTrack(e.track());
2012-05-26 14:26:10 +02:00
element->read(e);
add(element);
}
else if (tag == "pos") {
QPointF pt = e.readPoint();
if (score()->mscVersion() > 114)
setUserOff(pt * spatium());
}
2014-05-06 15:47:57 +02:00
else if (tag == "offset") {
if (score()->mscVersion() > 114) // || voice() >= 2) {
2014-05-06 15:47:57 +02:00
DurationElement::readProperties(e);
else if (type() == Element::Type::REST) {
2014-05-06 15:47:57 +02:00
DurationElement::readProperties(e);
setUserXoffset(0.0); // honor Y offset but not X for rests in older scores
}
else
2014-05-06 15:47:57 +02:00
e.skipCurrentElement(); // ignore manual layout otherwise
}
2013-05-21 09:44:08 +02:00
else if (DurationElement::readProperties(e))
return true;
2012-05-26 14:26:10 +02:00
else
return false;
return true;
}
//---------------------------------------------------------
// setSmall
//---------------------------------------------------------
void ChordRest::setSmall(bool val)
{
2013-05-28 15:42:02 +02:00
_small = val;
}
//---------------------------------------------------------
// undoSetSmall
//---------------------------------------------------------
void ChordRest::undoSetSmall(bool val)
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::SMALL, val);
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// layoutArticulations
// called from chord()->layout()
//---------------------------------------------------------
void ChordRest::layoutArticulations()
{
2012-11-20 20:51:18 +01:00
if (parent() == 0 || _articulations.isEmpty())
2012-05-26 14:26:10 +02:00
return;
qreal _spatium = spatium();
qreal _spStaff = _spatium * staff()->lineDistance(); // scaled to staff line distance for vert. pos. within a staff
if (type() == Element::Type::CHORD) {
2012-11-20 20:51:18 +01:00
if (_articulations.size() == 1) {
static_cast<Chord*>(this)->layoutArticulation(_articulations[0]);
2012-05-26 14:26:10 +02:00
return;
}
2012-11-20 20:51:18 +01:00
if (_articulations.size() == 2) {
2012-05-26 14:26:10 +02:00
//
// staccato | tenuto + marcato
//
2012-11-20 20:51:18 +01:00
Articulation* a1 = _articulations[0];
Articulation* a2 = _articulations[1];
ArticulationType st1 = a1->articulationType();
ArticulationType st2 = a2->articulationType();
2012-05-26 14:26:10 +02:00
if ((st2 == ArticulationType::Tenuto || st2 == ArticulationType::Staccato)
&& (st1 == ArticulationType::Marcato)) {
2012-05-26 14:26:10 +02:00
qSwap(a1, a2);
qSwap(st1, st2);
}
if ((st1 == ArticulationType::Tenuto || st1 == ArticulationType::Staccato)
&& (st2 == ArticulationType::Marcato)) {
2012-05-26 14:26:10 +02:00
QPointF pt = static_cast<Chord*>(this)->layoutArticulation(a1);
pt.ry() += a1->up() ? -_spStaff * .5 : _spStaff * .5;
2012-07-30 16:22:56 +02:00
a2->layout();
2012-05-26 14:26:10 +02:00
a2->setUp(a1->up());
a2->setPos(pt);
a2->adjustReadPos();
return;
}
//
// staccato | tenuto + sforzato
//
if ((st2 == ArticulationType::Tenuto || st2 == ArticulationType::Staccato)
&& (st1 == ArticulationType::Sforzatoaccent)) {
2012-05-26 14:26:10 +02:00
qSwap(a1, a2);
qSwap(st1, st2);
}
if ((st1 == ArticulationType::Tenuto || st1 == ArticulationType::Staccato)
&& (st2 == ArticulationType::Sforzatoaccent)) {
2012-05-26 14:26:10 +02:00
QPointF pt = static_cast<Chord*>(this)->layoutArticulation(a1);
pt.ry() += a1->up() ? -_spStaff * .7 : _spStaff * .7;
2012-07-30 16:22:56 +02:00
a2->layout();
2012-05-26 14:26:10 +02:00
a2->setUp(a1->up());
a2->setPos(pt);
a2->adjustReadPos();
return;
}
}
}
2012-05-26 14:26:10 +02:00
qreal x = centerX();
2014-05-26 15:31:36 +02:00
qreal distance0 = score()->styleS(StyleIdx::propertyDistance).val() * _spatium;
qreal distance1 = score()->styleS(StyleIdx::propertyDistanceHead).val() * _spatium;
qreal distance2 = score()->styleS(StyleIdx::propertyDistanceStem).val() * _spatium;
2012-05-26 14:26:10 +02:00
qreal chordTopY = upPos(); // note position of highest note
qreal chordBotY = downPos(); // note position of lowest note
qreal staffTopY = -distance2;
qreal staffBotY = staff()->height() + distance2;
// avoid collisions of staff articulations with chord notes:
// gap between note and staff articulation is distance0 + 0.5 spatium
if (type() == Element::Type::CHORD) {
2012-05-26 14:26:10 +02:00
Chord* chord = static_cast<Chord*>(this);
Stem* stem = chord->stem();
if (stem) {
qreal y = stem->pos().y() + pos().y();
if (up() && stem->stemLen() < 0.0)
y += stem->stemLen();
else if (!up() && stem->stemLen() > 0.0)
y -= stem->stemLen();
2013-01-02 09:29:17 +01:00
2012-05-26 14:26:10 +02:00
if (beam()) {
2014-05-26 15:31:36 +02:00
qreal bw = score()->styleS(StyleIdx::beamWidth).val() * _spatium;
2012-05-26 14:26:10 +02:00
y += up() ? -bw : bw;
}
if (up())
staffTopY = qMin(staffTopY, qreal(y - 0.5 * _spatium));
else
staffBotY = qMax(staffBotY, qreal(y + 0.5 * _spatium));
}
}
staffTopY = qMin(staffTopY, qreal(chordTopY - distance0 - 0.5 * _spatium));
staffBotY = qMax(staffBotY, qreal(chordBotY + distance0 + 0.5 * _spatium));
qreal dy = 0.0;
2013-01-02 09:29:17 +01:00
int n = _articulations.size();
for (int i = 0; i < n; ++i) {
Articulation* a = _articulations.at(i);
2012-05-26 14:26:10 +02:00
//
// determine MScore::Direction
2012-05-26 14:26:10 +02:00
//
if (a->direction() != MScore::Direction::AUTO) {
a->setUp(a->direction() == MScore::Direction::UP);
2012-05-26 14:26:10 +02:00
}
else {
if (a->anchor() == ArticulationAnchor::CHORD)
2012-05-26 14:26:10 +02:00
a->setUp(!up());
else
a->setUp(a->anchor() == ArticulationAnchor::TOP_STAFF || a->anchor() == ArticulationAnchor::TOP_CHORD);
2012-05-26 14:26:10 +02:00
}
}
//
// pass 1
// place tenuto and staccato
//
2013-01-02 09:29:17 +01:00
for (int i = 0; i < n; ++i) {
Articulation* a = _articulations.at(i);
2012-05-26 14:26:10 +02:00
a->layout();
ArticulationAnchor aa = a->anchor();
if ((a->articulationType() != ArticulationType::Tenuto)
&& (a->articulationType() != ArticulationType::Staccato))
2012-05-26 14:26:10 +02:00
continue;
if (aa != ArticulationAnchor::CHORD && aa != ArticulationAnchor::TOP_CHORD && aa != ArticulationAnchor::BOTTOM_CHORD)
2012-05-26 14:26:10 +02:00
continue;
bool bottom;
if ((aa == ArticulationAnchor::CHORD) && measure()->hasVoices(a->staffIdx()))
2012-05-26 14:26:10 +02:00
bottom = !up();
else
bottom = (aa == ArticulationAnchor::BOTTOM_CHORD) || (aa == ArticulationAnchor::CHORD && up());
2012-05-26 14:26:10 +02:00
bool headSide = bottom == up();
dy += distance1;
qreal y;
Chord* chord = static_cast<Chord*>(this);
if (bottom) {
int line = downLine();
y = chordBotY + dy;
if (!headSide && type() == Element::Type::CHORD && chord->stem()) {
2012-05-26 14:26:10 +02:00
Stem* stem = chord->stem();
y = chordTopY + stem->stemLen();
if (chord->beam())
2014-05-26 15:31:36 +02:00
y += score()->styleS(StyleIdx::beamWidth).val() * _spatium * .5;
2012-05-26 14:26:10 +02:00
x = stem->pos().x();
int line = lrint((y+0.5*_spatium) / _spatium);
if (line <= 4) // align between staff lines
y = line * _spatium + _spatium * .5;
else
y += _spatium;
}
else {
int lines = (staff()->lines() - 1) * 2;
if (line < lines)
y = (line & ~1) + 3;
else
y = line + 2;
y *= _spatium * .5;
}
}
else {
int line = upLine();
y = chordTopY - dy;
if (!headSide && type() == Element::Type::CHORD && chord->stem()) {
2012-05-26 14:26:10 +02:00
Stem* stem = chord->stem();
y = chordBotY + stem->stemLen();
if (chord->beam())
2014-05-26 15:31:36 +02:00
y -= score()->styleS(StyleIdx::beamWidth).val() * _spatium * .5;
2012-05-26 14:26:10 +02:00
x = stem->pos().x();
int line = lrint((y-0.5*_spatium) / _spatium);
if (line >= 0) // align between staff lines
y = line * _spatium - _spatium * .5;
else
y -= _spatium;
}
else {
if (line > 0)
y = ((line+1) & ~1) - 3;
else
y = line - 2;
y *= _spatium * .5;
}
}
dy += _spatium * .5;
a->setPos(x, y);
}
// reserve space for slur
bool botGap = false;
bool topGap = false;
2013-01-02 09:29:17 +01:00
2013-07-05 11:23:52 +02:00
#if 0 // TODO-S: optimize
for (Spanner* sp = _spannerFor; sp; sp = sp->next()) {
2012-05-26 14:26:10 +02:00
if (sp->type() != SLUR)
continue;
Slur* s = static_cast<Slur*>(sp);
if (s->up())
topGap = true;
else
botGap = true;
}
for (Spanner* sp = _spannerBack; sp; sp = sp->next()) {
2012-05-26 14:26:10 +02:00
if (sp->type() != SLUR)
continue;
Slur* s = static_cast<Slur*>(sp);
if (s->up())
topGap = true;
else
botGap = true;
}
2013-06-10 11:03:34 +02:00
#endif
2012-05-26 14:26:10 +02:00
if (botGap)
chordBotY += _spatium;
if (topGap)
chordTopY -= _spatium;
//
// pass 2
// place all articulations with anchor at chord/rest
//
2013-01-02 09:29:17 +01:00
n = _articulations.size();
for (int i = 0; i < n; ++i) {
Articulation* a = _articulations.at(i);
2012-05-26 14:26:10 +02:00
a->layout();
ArticulationAnchor aa = a->anchor();
if ((a->articulationType() == ArticulationType::Tenuto)
|| (a->articulationType() == ArticulationType::Staccato))
2012-05-26 14:26:10 +02:00
continue;
if (aa != ArticulationAnchor::CHORD && aa != ArticulationAnchor::TOP_CHORD && aa != ArticulationAnchor::BOTTOM_CHORD)
2012-05-26 14:26:10 +02:00
continue;
// for tenuto and staccate check for staff line collision
bool staffLineCT = a->articulationType() == ArticulationType::Tenuto
|| a->articulationType() == ArticulationType::Staccato;
2012-05-26 14:26:10 +02:00
// qreal sh = a->bbox().height() * mag();
bool bottom = (aa == ArticulationAnchor::BOTTOM_CHORD) || (aa == ArticulationAnchor::CHORD && up());
2012-05-26 14:26:10 +02:00
dy += distance1;
if (bottom) {
qreal y = chordBotY + dy;
if (staffLineCT && (y <= staffBotY -.1 - dy)) {
qreal l = y / _spatium;
qreal delta = fabs(l - round(l));
if (delta < 0.4) {
y += _spatium * .5;
dy += _spatium * .5;
}
}
a->setPos(x, y); // - a->bbox().y() + a->bbox().height() * .5);
}
else {
qreal y = chordTopY - dy;
if (staffLineCT && (y >= (staffTopY +.1 + dy))) {
qreal l = y / _spatium;
qreal delta = fabs(l - round(l));
if (delta < 0.4) {
y -= _spatium * .5;
dy += _spatium * .5;
}
}
a->setPos(x, y); // + a->bbox().y() - a->bbox().height() * .5);
}
}
//
// pass 3
// now place all articulations with staff top or bottom anchor
//
qreal dyTop = staffTopY;
qreal dyBot = staffBotY;
/* if ((upPos() - _spatium) < dyTop)
dyTop = upPos() - _spatium;
if ((downPos() + _spatium) > dyBot)
dyBot = downPos() + _spatium;
*/
2013-01-02 09:29:17 +01:00
for (int i = 0; i < n; ++i) {
Articulation* a = _articulations.at(i);
2012-05-26 14:26:10 +02:00
ArticulationAnchor aa = a->anchor();
if (aa == ArticulationAnchor::TOP_STAFF || aa == ArticulationAnchor::BOTTOM_STAFF) {
2012-05-26 14:26:10 +02:00
if (a->up()) {
a->setPos(x, dyTop);
dyTop -= distance0;
}
else {
a->setPos(x, dyBot);
dyBot += distance0;
}
}
a->adjustReadPos();
}
}
//---------------------------------------------------------
// drop
//---------------------------------------------------------
Element* ChordRest::drop(const DropData& data)
{
Element* e = data.element;
Measure* m = measure();
switch (e->type()) {
case Element::Type::BREATH:
2012-05-26 14:26:10 +02:00
{
Breath* b = static_cast<Breath*>(e);
b->setTrack(staffIdx() * VOICES);
// TODO: insert automatically in all staves?
Segment* seg = m->undoGetSegment(Segment::Type::Breath, tick());
2012-05-26 14:26:10 +02:00
b->setParent(seg);
score()->undoAddElement(b);
}
return e;
case Element::Type::BAR_LINE:
2012-05-26 14:26:10 +02:00
{
BarLine* bl = static_cast<BarLine*>(e);
bl->setTrack(staffIdx() * VOICES);
if (tick() == m->tick())
return m->drop(data);
Segment* seg = m->undoGetSegment(Segment::Type::BarLine, tick());
2012-05-26 14:26:10 +02:00
bl->setParent(seg);
score()->undoAddElement(bl);
}
return e;
case Element::Type::CLEF:
2014-07-25 17:13:27 +02:00
score()->cmdInsertClef(static_cast<Clef*>(e), this);
2012-05-26 14:26:10 +02:00
break;
case Element::Type::TEMPO_TEXT:
2012-05-26 14:26:10 +02:00
{
TempoText* tt = static_cast<TempoText*>(e);
tt->setParent(segment());
TextStyleType st = tt->textStyleType();
2014-03-14 11:30:19 +01:00
tt->setTextStyleType(st);
2012-05-26 14:26:10 +02:00
score()->undoAddElement(tt);
}
return e;
case Element::Type::DYNAMIC:
{
Dynamic* d = static_cast<Dynamic*>(e);
d->setTrack(track());
TextStyleType st = d->textStyleType();
d->setTextStyleType(st);
d->setParent(segment());
score()->undoAddElement(d);
}
return e;
case Element::Type::FRET_DIAGRAM:
2014-08-26 15:07:24 +02:00
case Element::Type::TREMOLOBAR:
case Element::Type::SYMBOL:
2012-05-26 14:26:10 +02:00
e->setTrack(track());
e->setParent(segment());
score()->undoAddElement(e);
return e;
case Element::Type::NOTE:
2012-05-26 14:26:10 +02:00
{
Note* note = static_cast<Note*>(e);
NoteVal nval;
nval.pitch = note->pitch();
nval.headGroup = note->headGroup();
nval.fret = note->fret();
nval.string = note->string();
score()->setNoteRest(segment(), track(), nval, data.duration, MScore::Direction::AUTO);
2012-05-26 14:26:10 +02:00
delete e;
}
break;
case Element::Type::HARMONY:
2012-05-26 14:26:10 +02:00
static_cast<Harmony*>(e)->render();
// fall through
case Element::Type::TEXT:
case Element::Type::STAFF_TEXT:
case Element::Type::STAFF_STATE:
case Element::Type::INSTRUMENT_CHANGE:
case Element::Type::REHEARSAL_MARK:
2012-05-26 14:26:10 +02:00
e->setParent(segment());
e->setTrack((track() / VOICES) * VOICES);
2012-10-25 16:21:07 +02:00
{
Text* f = static_cast<Text*>(e);
TextStyleType st = f->textStyleType();
if (st >= TextStyleType::DEFAULT)
f->setTextStyleType(st);
2012-10-25 16:21:07 +02:00
}
2012-05-26 14:26:10 +02:00
score()->undoAddElement(e);
return e;
case Element::Type::FIGURED_BASS:
2012-05-26 14:26:10 +02:00
{
bool bNew;
FiguredBass * fb = static_cast<FiguredBass *>(e);
fb->setParent( segment() );
fb->setTrack( (track() / VOICES) * VOICES );
fb->setTicks( duration().ticks() );
fb->setOnNote(true);
FiguredBass::addFiguredBassToSegment(segment(),
2012-05-26 14:26:10 +02:00
fb->track(), fb->ticks(), &bNew);
if (bNew)
score()->undoAddElement(e);
return e;
}
case Element::Type::IMAGE:
2012-05-26 14:26:10 +02:00
e->setParent(segment());
score()->undoAddElement(e);
return e;
case Element::Type::ICON:
2012-05-26 14:26:10 +02:00
{
switch(static_cast<Icon*>(e)->iconType()) {
case IconType::SBEAM:
score()->undoChangeProperty(this, P_ID::BEAM_MODE, int(Beam::Mode::BEGIN));
2012-05-26 14:26:10 +02:00
break;
case IconType::MBEAM:
score()->undoChangeProperty(this, P_ID::BEAM_MODE, int(Beam::Mode::MID));
2012-05-26 14:26:10 +02:00
break;
case IconType::NBEAM:
score()->undoChangeProperty(this, P_ID::BEAM_MODE, int(Beam::Mode::NONE));
2012-05-26 14:26:10 +02:00
break;
case IconType::BEAM32:
score()->undoChangeProperty(this, P_ID::BEAM_MODE, int(Beam::Mode::BEGIN32));
2012-05-26 14:26:10 +02:00
break;
case IconType::BEAM64:
score()->undoChangeProperty(this, P_ID::BEAM_MODE, int(Beam::Mode::BEGIN64));
2012-05-26 14:26:10 +02:00
break;
case IconType::AUTOBEAM:
score()->undoChangeProperty(this, P_ID::BEAM_MODE, int(Beam::Mode::AUTO));
2012-05-26 14:26:10 +02:00
break;
default:
break;
2012-05-26 14:26:10 +02:00
}
}
delete e;
break;
default:
qDebug("cannot drop %s", e->name());
delete e;
return 0;
}
return 0;
}
//---------------------------------------------------------
// setBeam
//---------------------------------------------------------
void ChordRest::setBeam(Beam* b)
{
_beam = b;
}
//---------------------------------------------------------
// setDurationType
//---------------------------------------------------------
void ChordRest::setDurationType(TDuration::DurationType t)
{
_durationType.setType(t);
_crossMeasure = CrossMeasure::UNKNOWN;
2012-05-26 14:26:10 +02:00
}
void ChordRest::setDurationType(const QString& s)
{
_durationType.setType(s);
_crossMeasure = CrossMeasure::UNKNOWN;
2012-05-26 14:26:10 +02:00
}
void ChordRest::setDurationType(int ticks)
{
_durationType.setVal(ticks);
_crossMeasure = CrossMeasure::UNKNOWN;
2012-05-26 14:26:10 +02:00
}
void ChordRest::setDurationType(const TDuration& v)
{
_durationType = v;
_crossMeasure = CrossMeasure::UNKNOWN;
2012-05-26 14:26:10 +02:00
}
QString ChordRest::durationUserName()
{
QString duration = tr("Duration");
QString tupletType = "";
if(tuplet()) {
switch (tuplet()->ratio().numerator()) {
case 2:
tupletType += " " + tr("Duplet");
break;
case 3:
tupletType += " " + tr("Triplet");
break;
case 4:
tupletType += " " + tr("Quadruplet");
break;
case 5:
tupletType += " " + tr("Quintuplet");
break;
case 6:
tupletType += " " + tr("Sextuplet");
break;
case 7:
tupletType += " " + tr("Septuplet");
break;
case 8:
tupletType += " " + tr("Octuplet");
break;
case 9:
tupletType += " " + tr("Nonuplet");
break;
default:
tupletType += " " + tr("Custom Tuplet");
}
}
QString dotString = "";
switch (dots()) {
case 1:
dotString += " " + tr("Dotted");
break;
case 2:
dotString += " " + tr("Double dotted");
break;
case 3:
dotString += " " + tr("Triple dotted");
break;
}
return QString("%1:%2%3 %4").arg(duration).arg(tupletType).arg(dotString).arg(durationType().durationTypeUserName());
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// setTrack
//---------------------------------------------------------
void ChordRest::setTrack(int val)
{
2012-11-20 20:51:18 +01:00
foreach(Articulation* a, _articulations)
2012-05-26 14:26:10 +02:00
a->setTrack(val);
Element::setTrack(val);
if (type() == Element::Type::CHORD) {
2012-05-26 14:26:10 +02:00
foreach(Note* n, static_cast<Chord*>(this)->notes())
n->setTrack(val);
}
if (_beam)
_beam->setTrack(val);
2012-09-24 10:54:08 +02:00
foreach(Lyrics* l, _lyricsList) {
if (l)
l->setTrack(val);
}
2012-05-26 14:26:10 +02:00
if (tuplet())
tuplet()->setTrack(val);
}
//---------------------------------------------------------
// tick
//---------------------------------------------------------
int ChordRest::tick() const
{
return segment() ? segment()->tick() : -1;
}
//---------------------------------------------------------
// rtick
//---------------------------------------------------------
int ChordRest::rtick() const
{
return segment() ? segment()->rtick() : -1;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// add
//---------------------------------------------------------
void ChordRest::add(Element* e)
{
e->setParent(this);
e->setTrack(track());
switch(e->type()) {
case Element::Type::ARTICULATION:
2012-05-26 14:26:10 +02:00
{
Articulation* a = static_cast<Articulation*>(e);
2012-11-20 20:51:18 +01:00
_articulations.push_back(a);
2013-06-03 14:39:59 +02:00
if (a->timeStretch() != 1.0)
score()->fixTicks(); // update tempo map
2012-05-26 14:26:10 +02:00
}
break;
case Element::Type::LYRICS:
2012-05-26 14:26:10 +02:00
{
Lyrics* l = static_cast<Lyrics*>(e);
int size = _lyricsList.size();
if (l->no() >= size) {
for (int i = size-1; i < l->no(); ++i)
_lyricsList.append(0);
}
_lyricsList[l->no()] = l;
}
break;
default:
qFatal("ChordRest::add: unknown element %s", e->name());
2012-05-26 14:26:10 +02:00
break;
}
}
//---------------------------------------------------------
// remove
//---------------------------------------------------------
void ChordRest::remove(Element* e)
{
switch (e->type()) {
case Element::Type::ARTICULATION:
2012-05-26 14:26:10 +02:00
{
Articulation* a = static_cast<Articulation*>(e);
2012-11-20 20:51:18 +01:00
if (!_articulations.removeOne(a))
2012-05-26 14:26:10 +02:00
qDebug("ChordRest::remove(): articulation not found");
2013-06-03 14:39:59 +02:00
if (a->timeStretch() != 1.0)
score()->fixTicks(); // update tempo map
2012-05-26 14:26:10 +02:00
}
break;
case Element::Type::LYRICS:
2012-05-26 14:26:10 +02:00
{
for (int i = 0; i < _lyricsList.size(); ++i) {
if (_lyricsList[i] != e)
continue;
_lyricsList[i] = 0;
while (!_lyricsList.isEmpty() && _lyricsList.back() == 0)
_lyricsList.takeLast();
return;
}
}
qDebug("ChordRest::remove: %s %p not found", e->name(), e);
break;
default:
qFatal("ChordRest::remove: unknown element <%s>", e->name());
2012-05-26 14:26:10 +02:00
}
}
//---------------------------------------------------------
// removeDeleteBeam
2013-07-11 12:25:25 +02:00
// beamed - the chordrest is beamed (will get a (new) beam)
// remove ChordRest from beam
// delete beam if empty
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
2013-07-11 12:25:25 +02:00
void ChordRest::removeDeleteBeam(bool beamed)
2012-05-26 14:26:10 +02:00
{
if (_beam) {
Beam* b = _beam;
2013-05-22 15:20:14 +02:00
_beam->remove(this);
2012-05-26 14:26:10 +02:00
if (b->isEmpty())
2013-05-22 15:20:14 +02:00
score()->undoRemoveElement(b);
2012-05-26 14:26:10 +02:00
}
if (!beamed && type() == Element::Type::CHORD)
2013-07-14 12:02:55 +02:00
static_cast<Chord*>(this)->layoutHook1();
2012-05-26 14:26:10 +02:00
}
2013-03-01 10:07:27 +01:00
//---------------------------------------------------------
// undoSetBeamMode
//---------------------------------------------------------
void ChordRest::undoSetBeamMode(Beam::Mode mode)
2013-03-01 10:07:27 +01:00
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::BEAM_MODE, int(mode));
2013-03-01 10:07:27 +01:00
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// getProperty
//---------------------------------------------------------
QVariant ChordRest::getProperty(P_ID propertyId) const
{
switch(propertyId) {
2014-08-20 09:50:42 +02:00
case P_ID::SMALL: return QVariant(small());
case P_ID::BEAM_MODE: return int(beamMode());
case P_ID::STAFF_MOVE: return staffMove();
default: return DurationElement::getProperty(propertyId);
2012-05-26 14:26:10 +02:00
}
}
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
bool ChordRest::setProperty(P_ID propertyId, const QVariant& v)
{
switch(propertyId) {
2014-08-20 09:50:42 +02:00
case P_ID::SMALL: setSmall(v.toBool()); break;
case P_ID::BEAM_MODE: setBeamMode(Beam::Mode(v.toInt())); break;
case P_ID::STAFF_MOVE: setStaffMove(v.toInt()); break;
default: return DurationElement::setProperty(propertyId, v);
2012-05-26 14:26:10 +02:00
}
score()->setLayoutAll(true);
return true;
}
2012-08-12 11:44:36 +02:00
//---------------------------------------------------------
// propertyDefault
//---------------------------------------------------------
QVariant ChordRest::propertyDefault(P_ID propertyId) const
{
switch(propertyId) {
2014-08-20 09:50:42 +02:00
case P_ID::SMALL: return false;
case P_ID::BEAM_MODE: return int(Beam::Mode::AUTO);
case P_ID::STAFF_MOVE: return 0;
2012-08-12 11:44:36 +02:00
default: return DurationElement::propertyDefault(propertyId);
}
score()->setLayoutAll(true);
}
2013-06-19 16:25:29 +02:00
//---------------------------------------------------------
// isGrace
//---------------------------------------------------------
2013-06-16 23:33:37 +02:00
bool ChordRest::isGrace() const
{
return type() == Element::Type::CHORD && ((Chord*)this)->noteType() != NoteType::NORMAL;
2013-06-16 23:33:37 +02:00
}
2013-06-19 16:25:29 +02:00
2014-04-23 18:07:38 +02:00
//---------------------------------------------------------
// isGraceBefore
//---------------------------------------------------------
bool ChordRest::isGraceBefore() const
{
return (type() == Element::Type::CHORD && (((Chord*)this)->noteType() == NoteType::ACCIACCATURA
2014-05-27 10:35:28 +02:00
|| ((Chord*)this)->noteType() == NoteType::APPOGGIATURA
|| ((Chord*)this)->noteType() == NoteType::GRACE4
|| ((Chord*)this)->noteType() == NoteType::GRACE16
|| ((Chord*)this)->noteType() == NoteType::GRACE32));
2014-04-23 18:07:38 +02:00
}
//---------------------------------------------------------
// isGraceAfter
//---------------------------------------------------------
bool ChordRest::isGraceAfter() const
{
return (type() == Element::Type::CHORD && (((Chord*)this)->noteType() == NoteType::GRACE8_AFTER
2014-05-27 10:35:28 +02:00
|| ((Chord*)this)->noteType() == NoteType::GRACE16_AFTER
|| ((Chord*)this)->noteType() == NoteType::GRACE32_AFTER));
2014-04-23 18:07:38 +02:00
}
2013-06-19 16:25:29 +02:00
//---------------------------------------------------------
// writeBeam
//---------------------------------------------------------
void ChordRest::writeBeam(Xml& xml)
{
Beam* b = beam();
#ifndef NDEBUG
if (b && b->elements().front() == this && (MScore::testMode || !b->generated())) {
b->setId(xml.beamId++);
b->write(xml);
}
#else
if (b && !b->generated() && b->elements().front() == this) {
b->setId(xml.beamId++);
b->write(xml);
}
#endif
}
//---------------------------------------------------------
// nextSegmentAfterCR
// returns first segment at tick CR->tick + CR->actualTicks
// of given types
//---------------------------------------------------------
2014-05-31 17:22:45 +02:00
Segment* ChordRest::nextSegmentAfterCR(Segment::Type types) const
{
for (Segment* s = segment()->next1MM(types); s; s = s->next1MM(types)) {
// chordrest ends at tick+actualTicks
// we return the segment at or after the end of the chordrest
if (s->tick() >= tick() + actualTicks())
return s;
}
return 0;
}
//---------------------------------------------------------
// nextElement
//---------------------------------------------------------
Element* ChordRest::nextElement()
{
return segment()->firstInNextSegments(staffIdx());
}
//---------------------------------------------------------
// prevElement
//---------------------------------------------------------
Element* ChordRest::prevElement()
{
return segment()->lastInPrevSegments(staffIdx());
}
QString ChordRest::accessibleExtraInfo()
{
QString rez = "";
foreach (Articulation* a, articulations())
rez = QString("%1 %2").arg(rez).arg(a->screenReaderInfo());
foreach (Element* l, lyricsList()) {
if (!l)
continue;
rez = QString("%1 %2").arg(rez).arg(l->screenReaderInfo());
}
2014-08-19 14:10:43 +02:00
if (segment()) {
foreach (Element* e, segment()->annotations()) {
if (e->staffIdx() == staffIdx() )
rez = QString("%1 %2").arg(rez).arg(e->screenReaderInfo());
}
2014-08-19 14:10:43 +02:00
SpannerMap& smap = score()->spannerMap();
auto spanners = smap.findOverlapping(tick(), tick());
for (auto i = spanners.begin(); i < spanners.end(); i++) {
const ::Interval<Spanner*> interval = *i;
Spanner* s = interval.value;
if (s->type() == Element::Type::VOLTA || //voltas are added for barlines
s->type() == Element::Type::TIE ) //ties are added in notes
continue;
Segment* seg = 0;
if (s->type() == Element::Type::SLUR) {
2014-08-19 14:10:43 +02:00
if (s->tick() == tick() && s->track() == track())
rez = tr("%1 Start of %2").arg(rez).arg(s->screenReaderInfo());
2014-08-19 14:10:43 +02:00
if (s->tick2() == tick() && s->track2() == track())
rez = tr("%1 End of %2").arg(rez).arg(s->screenReaderInfo());
}
else {
2014-08-19 14:10:43 +02:00
if (s->tick() == tick() && s->staffIdx() == staffIdx())
rez = tr("%1 Start of %2").arg(rez).arg(s->screenReaderInfo());
seg = segment()->next1MM(Segment::Type::ChordRest);
if (!seg)
continue;
2014-08-19 14:10:43 +02:00
if (s->tick2() == seg->tick() && s->staffIdx() == staffIdx())
rez = tr("%1 End of %2").arg(rez).arg(s->screenReaderInfo());
}
}
}
return rez;
}
2013-05-13 18:49:17 +02:00
}