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"
|
2015-06-19 16:34:38 +02:00
|
|
|
#include "utils.h"
|
2015-07-19 17:18:38 +02:00
|
|
|
#include "keysig.h"
|
2015-09-05 15:16:03 +02:00
|
|
|
#include "page.h"
|
2016-01-04 14:48:58 +01:00
|
|
|
#include "hook.h"
|
2012-05-26 14:26:10 +02:00
|
|
|
|
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)
|
|
|
|
{
|
2013-03-05 20:23:59 +01:00
|
|
|
ArticulationType idx = aa->articulationType();
|
2012-11-20 20:51:18 +01:00
|
|
|
foreach(Articulation* a, _articulations) {
|
2013-03-05 20:23:59 +01:00
|
|
|
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;
|
2013-01-03 16:56:56 +01:00
|
|
|
_beam = 0;
|
2013-07-11 14:44:35 +02:00
|
|
|
_tabDur = 0;
|
2013-01-03 16:56:56 +01:00
|
|
|
_up = true;
|
2016-05-19 13:15:34 +02:00
|
|
|
_beamMode = Beam::Mode::AUTO;
|
2013-07-11 14:44:35 +02:00
|
|
|
_small = false;
|
2014-05-21 15:41:23 +02:00
|
|
|
_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;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-08-17 12:52:35 +02:00
|
|
|
for (Lyrics* l : cr._lyrics) { // make deep copy
|
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());
|
2016-08-24 14:49:34 +02:00
|
|
|
_lyrics.push_back(nl);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-07 10:55:36 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// undoUnlink
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void ChordRest::undoUnlink()
|
|
|
|
{
|
|
|
|
DurationElement::undoUnlink();
|
|
|
|
for (Articulation* a : _articulations)
|
|
|
|
a->undoUnlink();
|
2016-08-24 14:49:34 +02:00
|
|
|
for (Lyrics* l : _lyrics)
|
|
|
|
l->undoUnlink();
|
2014-08-07 10:55:36 +02:00
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// ChordRest
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
ChordRest::~ChordRest()
|
|
|
|
{
|
2016-08-17 12:52:35 +02:00
|
|
|
qDeleteAll(_articulations);
|
|
|
|
qDeleteAll(_lyrics);
|
|
|
|
delete _tabDur;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// scanElements
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void ChordRest::scanElements(void* data, void (*func)(void*, Element*), bool all)
|
|
|
|
{
|
2013-06-22 10:55:22 +02:00
|
|
|
if (_beam && (_beam->elements().front() == this)
|
|
|
|
&& !measure()->slashStyle(staffIdx()))
|
2012-05-26 14:26:10 +02:00
|
|
|
_beam->scanElements(data, func, all);
|
2016-08-17 12:52:35 +02:00
|
|
|
for (Articulation* a : _articulations)
|
2012-05-26 14:26:10 +02:00
|
|
|
func(data, a);
|
2016-08-24 14:49:34 +02:00
|
|
|
for (Lyrics* l : _lyrics)
|
|
|
|
l->scanElements(data, func, all);
|
2012-05-26 14:26:10 +02:00
|
|
|
DurationElement* de = this;
|
|
|
|
while (de->tuplet() && de->tuplet()->elements().front() == de) {
|
2015-06-05 11:55:37 +02:00
|
|
|
de->tuplet()->scanElements(data, func, all);
|
2012-05-26 14:26:10 +02:00
|
|
|
de = de->tuplet();
|
|
|
|
}
|
|
|
|
if (_tabDur)
|
|
|
|
func(data, _tabDur);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// writeProperties
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void ChordRest::writeProperties(Xml& xml) const
|
|
|
|
{
|
|
|
|
DurationElement::writeProperties(xml);
|
|
|
|
|
|
|
|
//
|
2014-06-26 07:45:09 +02:00
|
|
|
// Beam::Mode default:
|
|
|
|
// REST - Beam::Mode::NONE
|
|
|
|
// CHORD - Beam::Mode::AUTO
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
2014-06-26 07:45:09 +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) {
|
2014-06-26 07:45:09 +02:00
|
|
|
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()
|
2016-07-07 16:20:16 +02:00
|
|
|
|| (actualDurationType().fraction() != duration()))) {
|
|
|
|
xml.tag("duration", duration());
|
|
|
|
//xml.tagE("duration z=\"%d\" n=\"%d\"", duration().numerator(), duration().denominator());
|
|
|
|
}
|
2014-10-10 14:24:37 +02:00
|
|
|
|
2016-03-05 19:50:55 +01:00
|
|
|
for (const Articulation* a : _articulations)
|
2012-05-26 14:26:10 +02:00
|
|
|
a->write(xml);
|
2016-03-29 14:19:39 +02:00
|
|
|
|
2016-03-05 19:50:55 +01:00
|
|
|
#ifndef NDEBUG
|
2013-03-28 11:09:31 +01:00
|
|
|
if (_beam && (MScore::testMode || !_beam->generated()))
|
2012-05-26 14:26:10 +02:00
|
|
|
xml.tag("Beam", _beam->id());
|
2016-03-05 19:50:55 +01:00
|
|
|
#else
|
|
|
|
if (_beam && !_beam->generated())
|
|
|
|
xml.tag("Beam", _beam->id());
|
|
|
|
#endif
|
2016-08-24 14:49:34 +02:00
|
|
|
for (Lyrics* lyrics : _lyrics)
|
|
|
|
lyrics->write(xml);
|
2013-06-19 16:25:29 +02:00
|
|
|
if (!isGrace()) {
|
|
|
|
Fraction t(globalDuration());
|
|
|
|
if (staff())
|
2015-04-09 17:01:34 +02:00
|
|
|
t /= staff()->timeStretch(xml.curTick);
|
2013-06-19 16:25:29 +02:00
|
|
|
xml.curTick += t.ticks();
|
|
|
|
}
|
2014-11-01 13:05:18 +01:00
|
|
|
for (auto i : score()->spanner()) { // TODO: dont search whole list
|
|
|
|
Spanner* s = i.second;
|
|
|
|
if (s->generated() || s->type() != Element::Type::SLUR || !xml.canWrite(s))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (s->startElement() == this) {
|
|
|
|
int id = xml.spannerId(s);
|
|
|
|
xml.tagE(QString("Slur type=\"start\" id=\"%1\"").arg(id));
|
|
|
|
}
|
|
|
|
else if (s->endElement() == this) {
|
|
|
|
int id = xml.spannerId(s);
|
|
|
|
xml.tagE(QString("Slur type=\"stop\" id=\"%1\"").arg(id));
|
|
|
|
}
|
|
|
|
}
|
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());
|
2014-05-21 15:43:19 +02:00
|
|
|
if (actualDurationType().type() != TDuration::DurationType::V_MEASURE) {
|
2015-05-21 21:37:51 +02:00
|
|
|
if (score()->mscVersion() < 112 && (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
|
2014-05-21 15:43:19 +02:00
|
|
|
(actualDurationType()==TDuration::DurationType::V_WHOLE && duration() <= Fraction(4, 4)) ) {
|
2013-05-29 10:31:26 +02:00
|
|
|
// old pre 2.0 scores: convert
|
2014-05-21 15:43:19 +02:00
|
|
|
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 {
|
2016-05-13 06:55:03 +02:00
|
|
|
if (score()->mscVersion() <= 114) {
|
2013-05-29 10:31:26 +02:00
|
|
|
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());
|
2014-06-26 07:45:09 +02:00
|
|
|
Beam::Mode bm = Beam::Mode::AUTO;
|
2012-05-26 14:26:10 +02:00
|
|
|
if (val == "auto")
|
2014-06-26 07:45:09 +02:00
|
|
|
bm = Beam::Mode::AUTO;
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (val == "begin")
|
2014-06-26 07:45:09 +02:00
|
|
|
bm = Beam::Mode::BEGIN;
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (val == "mid")
|
2014-06-26 07:45:09 +02:00
|
|
|
bm = Beam::Mode::MID;
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (val == "end")
|
2014-06-26 07:45:09 +02:00
|
|
|
bm = Beam::Mode::END;
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (val == "no")
|
2014-06-26 07:45:09 +02:00
|
|
|
bm = Beam::Mode::NONE;
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (val == "begin32")
|
2014-06-26 07:45:09 +02:00
|
|
|
bm = Beam::Mode::BEGIN32;
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (val == "begin64")
|
2014-06-26 07:45:09 +02:00
|
|
|
bm = Beam::Mode::BEGIN64;
|
2012-05-26 14:26:10 +02:00
|
|
|
else
|
2014-06-26 07:45:09 +02:00
|
|
|
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());
|
2014-10-06 08:40:25 +02:00
|
|
|
atr->setTrack(track());
|
2012-05-26 14:26:10 +02:00
|
|
|
atr->read(e);
|
|
|
|
add(atr);
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
else if (tag == "leadingSpace" || tag == "trailingSpace") {
|
|
|
|
qDebug("ChordRest: %s obsolete", tag.toLocal8Bit().data());
|
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;
|
2014-06-24 18:36:02 +02:00
|
|
|
if ((type() == Element::Type::REST) && (mticks == i)) {
|
2014-05-21 15:43:19 +02:00
|
|
|
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);
|
2015-03-03 15:45:25 +01:00
|
|
|
QString atype(e.attribute("type"));
|
|
|
|
|
|
|
|
if (!spanner) {
|
|
|
|
if (atype == "stop") {
|
|
|
|
SpannerValues sv;
|
|
|
|
sv.spannerId = id;
|
2015-08-14 19:37:22 +02:00
|
|
|
sv.track2 = track();
|
2015-03-03 15:45:25 +01:00
|
|
|
sv.tick2 = e.tick();
|
|
|
|
e.addSpannerValues(sv);
|
|
|
|
}
|
|
|
|
else if (atype == "start")
|
|
|
|
qDebug("spanner: start without spanner");
|
|
|
|
}
|
2014-07-18 18:14:46 +02:00
|
|
|
else {
|
|
|
|
if (atype == "start") {
|
2015-03-03 18:24:54 +01:00
|
|
|
if (spanner->ticks() > 0 && spanner->tick() == -1) // stop has been read first
|
|
|
|
spanner->setTicks(spanner->ticks() - e.tick() - 1);
|
2014-08-07 10:18:50 +02:00
|
|
|
spanner->setTick(e.tick());
|
|
|
|
spanner->setTrack(track());
|
|
|
|
if (spanner->type() == Element::Type::SLUR)
|
|
|
|
spanner->setStartElement(this);
|
2014-07-30 12:34:56 +02:00
|
|
|
if (e.pasteMode()) {
|
2015-02-12 11:41:39 +01:00
|
|
|
for (ScoreElement* e : spanner->linkList()) {
|
2014-08-07 10:18:50 +02:00
|
|
|
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());
|
2015-02-12 11:41:39 +01:00
|
|
|
for (ScoreElement* ee : linkList()) {
|
2014-07-30 12:34:56 +02:00
|
|
|
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()) {
|
2015-02-12 11:41:39 +01:00
|
|
|
for (ScoreElement* e : spanner->linkList()) {
|
2014-08-07 10:18:50 +02:00
|
|
|
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());
|
2015-02-12 11:41:39 +01:00
|
|
|
for (ScoreElement* ee : linkList()) {
|
2014-07-30 12:34:56 +02:00
|
|
|
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);
|
|
|
|
}
|
2013-08-13 19:31:50 +02:00
|
|
|
else if (tag == "pos") {
|
|
|
|
QPointF pt = e.readPoint();
|
2016-06-05 10:23:37 +02:00
|
|
|
setUserOff(pt * spatium());
|
2014-05-06 15:47:57 +02:00
|
|
|
}
|
2016-06-06 11:21:24 +02:00
|
|
|
else if (tag == "offset")
|
|
|
|
DurationElement::readProperties(e);
|
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;
|
2016-06-06 11:21:24 +02:00
|
|
|
return true;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setSmall
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void ChordRest::setSmall(bool val)
|
|
|
|
{
|
2013-05-28 15:42:02 +02:00
|
|
|
_small = val;
|
2012-06-19 14:44:27 +02:00
|
|
|
}
|
|
|
|
|
2014-05-09 11:04:15 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// undoSetSmall
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void ChordRest::undoSetSmall(bool val)
|
|
|
|
{
|
2014-05-26 18:18:01 +02:00
|
|
|
undoChangeProperty(P_ID::SMALL, val);
|
2014-05-09 11:04:15 +02:00
|
|
|
}
|
|
|
|
|
2016-05-19 13:15:34 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// layout0
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void ChordRest::layout0(AccidentalState* as)
|
|
|
|
{
|
|
|
|
qreal m = staff()->mag();
|
|
|
|
if (small())
|
|
|
|
m *= score()->styleD(StyleIdx::smallNoteMag);
|
|
|
|
|
|
|
|
if (isChord()) {
|
|
|
|
Chord* chord = toChord(this);
|
|
|
|
for (Chord* c : chord->graceNotes())
|
|
|
|
c->layout0(as);
|
|
|
|
if (chord->isGrace())
|
|
|
|
m *= score()->styleD(StyleIdx::graceNoteMag);
|
|
|
|
else
|
|
|
|
chord->cmdUpdateNotes(as);
|
|
|
|
|
|
|
|
const Drumset* drumset = 0;
|
|
|
|
if (part()->instrument()->useDrumset())
|
|
|
|
drumset = part()->instrument()->drumset();
|
|
|
|
if (drumset) {
|
|
|
|
for (Note* note : chord->notes()) {
|
|
|
|
int pitch = note->pitch();
|
|
|
|
if (!drumset->isValid(pitch)) {
|
|
|
|
// qDebug("unmapped drum note %d", pitch);
|
|
|
|
}
|
|
|
|
else if (!note->fixed()) {
|
|
|
|
note->undoChangeProperty(P_ID::HEAD_GROUP, int(drumset->noteHead(pitch)));
|
|
|
|
// note->setHeadGroup(drumset->noteHead(pitch));
|
|
|
|
note->setLine(drumset->line(pitch));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-05-20 10:02:44 +02:00
|
|
|
chord->computeUp();
|
|
|
|
chord->layoutStem1(); // create stems needed to calculate spacing
|
|
|
|
// stem direction can change later during beam processing
|
2016-05-19 13:15:34 +02:00
|
|
|
}
|
|
|
|
setMag(m);
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// layoutArticulations
|
|
|
|
// called from chord()->layout()
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void ChordRest::layoutArticulations()
|
|
|
|
{
|
2016-02-06 22:03:43 +01:00
|
|
|
if (parent() == 0 || _articulations.empty())
|
2012-05-26 14:26:10 +02:00
|
|
|
return;
|
2015-09-01 20:18:28 +02:00
|
|
|
qreal _spatium = spatium();
|
|
|
|
bool scale = staff()->scaleNotesToLines();
|
|
|
|
qreal pld = staff()->lineDistance();
|
|
|
|
qreal lld = staff()->logicalLineDistance();
|
|
|
|
qreal _spStaff = _spatium * pld; // scaled to staff line distance for vert. pos. within a staff
|
|
|
|
qreal _spDist = _spatium; // scaling for distance between articulations
|
2013-08-06 19:28:04 +02:00
|
|
|
|
2016-04-06 15:28:10 +02:00
|
|
|
if (isChord()) {
|
2012-11-20 20:51:18 +01:00
|
|
|
if (_articulations.size() == 1) {
|
2016-04-06 15:28:10 +02:00
|
|
|
toChord(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];
|
2013-03-05 20:23:59 +01:00
|
|
|
ArticulationType st1 = a1->articulationType();
|
|
|
|
ArticulationType st2 = a2->articulationType();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-05-26 21:55:26 +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);
|
|
|
|
}
|
2014-05-26 21:55:26 +02:00
|
|
|
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);
|
2015-08-10 06:54:24 +02:00
|
|
|
//if (pt.y() < 0 || pt.y() > staff()->height())
|
|
|
|
// _spDist = _spatium;
|
|
|
|
pt.ry() += a1->up() ? -_spDist * .5 : _spDist * .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
|
|
|
|
//
|
2014-05-26 21:55:26 +02:00
|
|
|
if ((st2 == ArticulationType::Tenuto || st2 == ArticulationType::Staccato)
|
|
|
|
&& (st1 == ArticulationType::Sforzatoaccent)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
qSwap(a1, a2);
|
|
|
|
qSwap(st1, st2);
|
|
|
|
}
|
2014-05-26 21:55:26 +02:00
|
|
|
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);
|
2015-08-10 06:54:24 +02:00
|
|
|
//if (pt.y() < 0 || pt.y() > staff()->height())
|
|
|
|
// _spDist = _spatium;
|
|
|
|
pt.ry() += a1->up() ? -_spDist * .7 : _spDist * .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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-08-06 19:28:04 +02:00
|
|
|
|
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
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
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
|
|
|
//
|
2016-03-02 13:20:19 +01:00
|
|
|
// determine Direction
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
2016-03-02 13:20:19 +01:00
|
|
|
if (a->direction() != Direction::AUTO) {
|
|
|
|
a->setUp(a->direction() == Direction::UP);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else {
|
2014-05-21 15:37:28 +02:00
|
|
|
if (a->anchor() == ArticulationAnchor::CHORD)
|
2012-05-26 14:26:10 +02:00
|
|
|
a->setUp(!up());
|
|
|
|
else
|
2014-05-21 15:37:28 +02:00
|
|
|
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
|
|
|
|
//
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
for (Articulation* a : _articulations) {
|
2012-05-26 14:26:10 +02:00
|
|
|
a->layout();
|
|
|
|
ArticulationAnchor aa = a->anchor();
|
|
|
|
|
2014-05-26 21:55:26 +02:00
|
|
|
if ((a->articulationType() != ArticulationType::Tenuto)
|
|
|
|
&& (a->articulationType() != ArticulationType::Staccato))
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
|
2014-05-21 15:37:28 +02:00
|
|
|
if (aa != ArticulationAnchor::CHORD && aa != ArticulationAnchor::TOP_CHORD && aa != ArticulationAnchor::BOTTOM_CHORD)
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
bool bottom;
|
2014-05-21 15:37:28 +02:00
|
|
|
if ((aa == ArticulationAnchor::CHORD) && measure()->hasVoices(a->staffIdx()))
|
2012-05-26 14:26:10 +02:00
|
|
|
bottom = !up();
|
|
|
|
else
|
2014-05-21 15:37:28 +02:00
|
|
|
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();
|
2015-09-01 20:18:28 +02:00
|
|
|
if (!scale)
|
|
|
|
line = ceil((line * lld) / pld);
|
2012-05-26 14:26:10 +02:00
|
|
|
y = chordBotY + dy;
|
2014-06-24 18:36:02 +02:00
|
|
|
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;
|
2014-12-28 23:41:09 +01:00
|
|
|
// aligning horizontally to stem makes sense only for staccato
|
|
|
|
// and only if no other articulations on this side
|
|
|
|
//x = stem->pos().x();
|
2015-08-10 06:54:24 +02:00
|
|
|
int line = lrint((y+0.5*_spStaff) / _spStaff);
|
|
|
|
if (line < staff()->lines()) // align between staff lines
|
|
|
|
y = line * _spStaff + _spatium * .5;
|
2012-05-26 14:26:10 +02:00
|
|
|
else
|
|
|
|
y += _spatium;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
int lines = (staff()->lines() - 1) * 2;
|
|
|
|
if (line < lines)
|
2015-08-10 06:54:24 +02:00
|
|
|
y = ((line & ~1) + 3) * _spStaff;
|
2012-05-26 14:26:10 +02:00
|
|
|
else
|
2015-08-10 06:54:24 +02:00
|
|
|
y = line * _spStaff + 2 * _spatium;
|
|
|
|
y *= .5;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
int line = upLine();
|
2015-09-01 20:18:28 +02:00
|
|
|
if (!scale)
|
|
|
|
line = floor((line * lld) / pld);
|
2012-05-26 14:26:10 +02:00
|
|
|
y = chordTopY - dy;
|
2014-06-24 18:36:02 +02:00
|
|
|
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;
|
2014-12-28 23:41:09 +01:00
|
|
|
// aligning horizontally to stem makes sense only for staccato
|
|
|
|
// and only if no other articulations on this side
|
|
|
|
//x = stem->pos().x();
|
2015-08-10 06:54:24 +02:00
|
|
|
int line = lrint((y-0.5*_spStaff) / _spStaff);
|
2012-05-26 14:26:10 +02:00
|
|
|
if (line >= 0) // align between staff lines
|
2015-08-10 06:54:24 +02:00
|
|
|
y = line * _spStaff - _spatium * .5;
|
2012-05-26 14:26:10 +02:00
|
|
|
else
|
|
|
|
y -= _spatium;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (line > 0)
|
2015-08-10 06:54:24 +02:00
|
|
|
y = (((line+1) & ~1) - 3) * _spStaff;
|
2012-05-26 14:26:10 +02:00
|
|
|
else
|
2015-08-10 06:54:24 +02:00
|
|
|
y = line * _spStaff - 2 * _spatium;
|
|
|
|
y *= .5;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
|
|
|
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();
|
2014-05-26 21:55:26 +02:00
|
|
|
if ((a->articulationType() == ArticulationType::Tenuto)
|
|
|
|
|| (a->articulationType() == ArticulationType::Staccato))
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
|
2014-05-21 15:37:28 +02:00
|
|
|
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
|
2014-05-26 21:55:26 +02:00
|
|
|
bool staffLineCT = a->articulationType() == ArticulationType::Tenuto
|
|
|
|
|| a->articulationType() == ArticulationType::Staccato;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-05-21 15:37:28 +02:00
|
|
|
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)) {
|
2015-08-10 06:54:24 +02:00
|
|
|
qreal l = y / _spStaff;
|
2012-05-26 14:26:10 +02:00
|
|
|
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))) {
|
2015-08-10 06:54:24 +02:00
|
|
|
qreal l = y / _spStaff;
|
2012-05-26 14:26:10 +02:00
|
|
|
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;
|
|
|
|
|
2016-01-04 14:48:58 +01:00
|
|
|
for (Articulation* a : _articulations) {
|
2012-05-26 14:26:10 +02:00
|
|
|
ArticulationAnchor aa = a->anchor();
|
2014-05-21 15:37:28 +02:00
|
|
|
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)
|
|
|
|
{
|
2015-01-02 02:06:32 +01:00
|
|
|
Element* e = data.element;
|
|
|
|
Measure* m = measure();
|
|
|
|
bool fromPalette = (e->track() == -1);
|
2012-05-26 14:26:10 +02:00
|
|
|
switch (e->type()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::BREATH:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2016-05-02 13:41:41 +02:00
|
|
|
Breath* b = toBreath(e);
|
2015-02-06 17:01:41 +01:00
|
|
|
int track = staffIdx() * VOICES;
|
|
|
|
b->setTrack(track);
|
|
|
|
|
|
|
|
// find start tick of next note in staff
|
|
|
|
#if 0
|
|
|
|
int bt = tick() + actualTicks(); // this could make sense if we allowed breath marks in voice > 1
|
|
|
|
#else
|
|
|
|
Segment* next = segment()->nextCR(track);
|
|
|
|
int bt = next ? next->tick() : score()->lastSegment()->tick();
|
|
|
|
#endif
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
// TODO: insert automatically in all staves?
|
|
|
|
|
2015-02-06 17:01:41 +01:00
|
|
|
Segment* seg = m->undoGetSegment(Segment::Type::Breath, bt);
|
2012-05-26 14:26:10 +02:00
|
|
|
b->setParent(seg);
|
|
|
|
score()->undoAddElement(b);
|
|
|
|
}
|
|
|
|
return e;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::BAR_LINE:
|
2016-08-09 17:17:27 +02:00
|
|
|
if (data.control())
|
|
|
|
score()->splitMeasure(this);
|
|
|
|
else {
|
2016-08-06 10:33:32 +02:00
|
|
|
BarLine* bl = toBarLine(e);
|
2016-09-03 18:28:58 +02:00
|
|
|
bl->setPos(QPointF());
|
2016-08-06 10:33:32 +02:00
|
|
|
bl->setTrack(staffIdx() * VOICES);
|
|
|
|
bl->setGenerated(false);
|
|
|
|
|
|
|
|
if (tick() == m->tick())
|
|
|
|
return m->drop(data);
|
|
|
|
|
|
|
|
BarLine* obl = 0;
|
|
|
|
for (Staff* st : staff()->staffList()) {
|
|
|
|
Score* score = st->score();
|
|
|
|
Measure* measure = score->tick2measure(m->tick());
|
|
|
|
Segment* seg = measure->undoGetSegment(Segment::Type::BarLine, tick());
|
|
|
|
BarLine* l;
|
|
|
|
if (obl == 0)
|
|
|
|
obl = l = bl->clone();
|
|
|
|
else
|
2016-09-03 18:28:58 +02:00
|
|
|
l = toBarLine(obl->linkedClone());
|
2016-08-06 10:33:32 +02:00
|
|
|
l->setTrack(st->idx() * VOICES);
|
|
|
|
l->setScore(score);
|
|
|
|
l->setParent(seg);
|
|
|
|
score->undoAddElement(l);
|
|
|
|
}
|
2014-12-22 13:28:09 +01:00
|
|
|
}
|
|
|
|
delete e;
|
|
|
|
return 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::CLEF:
|
2016-09-03 18:28:58 +02:00
|
|
|
score()->cmdInsertClef(toClef(e), this);
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
|
2015-07-03 22:18:40 +02:00
|
|
|
case Element::Type::TIMESIG:
|
2015-09-05 15:16:03 +02:00
|
|
|
if (measure()->system()) {
|
|
|
|
DropData ndd = data;
|
2015-09-28 16:08:59 +02:00
|
|
|
// adding from palette sets pos, but normal paste does not
|
|
|
|
if (!fromPalette)
|
|
|
|
ndd.pos = pagePos();
|
|
|
|
// convert page-relative pos to score-relative
|
2015-09-05 15:16:03 +02:00
|
|
|
ndd.pos += measure()->system()->page()->pos();
|
|
|
|
return measure()->drop(ndd);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
delete e;
|
|
|
|
return 0;
|
|
|
|
}
|
2015-07-03 22:18:40 +02:00
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::TEMPO_TEXT:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
TempoText* tt = static_cast<TempoText*>(e);
|
2015-01-02 02:06:32 +01:00
|
|
|
tt->setTrack(0);
|
2012-05-26 14:26:10 +02:00
|
|
|
tt->setParent(segment());
|
2014-07-03 11:22:42 +02:00
|
|
|
TextStyleType st = tt->textStyleType();
|
2015-01-02 02:06:32 +01:00
|
|
|
//tt->setTextStyleType(st);
|
|
|
|
if (st >= TextStyleType::DEFAULT && fromPalette)
|
|
|
|
tt->textStyle().restyle(MScore::baseStyle()->textStyle(st), score()->textStyle(st));
|
2012-05-26 14:26:10 +02:00
|
|
|
score()->undoAddElement(tt);
|
|
|
|
}
|
|
|
|
return e;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::DYNAMIC:
|
2014-07-26 18:01:53 +02:00
|
|
|
{
|
|
|
|
Dynamic* d = static_cast<Dynamic*>(e);
|
|
|
|
d->setTrack(track());
|
|
|
|
TextStyleType st = d->textStyleType();
|
2015-01-02 02:06:32 +01:00
|
|
|
//d->setTextStyleType(st);
|
|
|
|
if (st >= TextStyleType::DEFAULT && fromPalette)
|
|
|
|
d->textStyle().restyle(MScore::baseStyle()->textStyle(st), score()->textStyle(st));
|
2014-07-26 18:01:53 +02:00
|
|
|
d->setParent(segment());
|
|
|
|
score()->undoAddElement(d);
|
|
|
|
}
|
|
|
|
return e;
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::FRET_DIAGRAM:
|
2014-08-26 15:07:24 +02:00
|
|
|
case Element::Type::TREMOLOBAR:
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::SYMBOL:
|
2012-05-26 14:26:10 +02:00
|
|
|
e->setTrack(track());
|
|
|
|
e->setParent(segment());
|
|
|
|
score()->undoAddElement(e);
|
|
|
|
return e;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
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();
|
2014-08-19 22:17:58 +02:00
|
|
|
nval.fret = note->fret();
|
|
|
|
nval.string = note->string();
|
2016-03-02 13:20:19 +01:00
|
|
|
score()->setNoteRest(segment(), track(), nval, data.duration, Direction::AUTO);
|
2012-05-26 14:26:10 +02:00
|
|
|
delete e;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::HARMONY:
|
2015-06-19 16:34:38 +02:00
|
|
|
{
|
|
|
|
// transpose
|
|
|
|
Harmony* harmony = static_cast<Harmony*>(e);
|
2015-09-21 02:13:22 +02:00
|
|
|
Interval interval = staff()->part()->instrument(tick())->transpose();
|
2015-06-19 16:34:38 +02:00
|
|
|
if (!score()->styleB(StyleIdx::concertPitch) && !interval.isZero()) {
|
|
|
|
interval.flip();
|
|
|
|
int rootTpc = transposeTpc(harmony->rootTpc(), interval, true);
|
|
|
|
int baseTpc = transposeTpc(harmony->baseTpc(), interval, true);
|
|
|
|
score()->undoTransposeHarmony(harmony, rootTpc, baseTpc);
|
|
|
|
}
|
|
|
|
// render
|
|
|
|
harmony->render();
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
// fall through
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::TEXT:
|
|
|
|
case Element::Type::STAFF_TEXT:
|
|
|
|
case Element::Type::STAFF_STATE:
|
|
|
|
case Element::Type::INSTRUMENT_CHANGE:
|
2015-03-05 16:46:37 +01:00
|
|
|
if (e->type() == Element::Type::INSTRUMENT_CHANGE
|
2015-03-13 12:54:24 +01:00
|
|
|
&& part()->instruments()->find(tick()) != part()->instruments()->end()) {
|
2015-02-08 20:55:18 +01:00
|
|
|
qDebug()<<"InstrumentChange already exists at tick = "<<tick();
|
|
|
|
delete e;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
// fall through
|
2014-06-24 18:36:02 +02:00
|
|
|
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
|
|
|
{
|
2015-01-02 02:06:32 +01:00
|
|
|
Text* t = static_cast<Text*>(e);
|
|
|
|
TextStyleType st = t->textStyleType();
|
|
|
|
// for palette items, we want to use current score text style settings
|
|
|
|
// except where the source element had explicitly overridden these via text properties
|
|
|
|
// palette text style will be relative to baseStyle, so rebase this to score
|
|
|
|
//f->setTextStyleType(st);
|
|
|
|
if (st >= TextStyleType::DEFAULT && fromPalette)
|
|
|
|
t->textStyle().restyle(MScore::baseStyle()->textStyle(st), score()->textStyle(st));
|
2015-06-08 17:58:07 +02:00
|
|
|
if (e->type() == Element::Type::REHEARSAL_MARK && fromPalette)
|
2015-04-27 12:59:30 +02:00
|
|
|
t->setXmlText(score()->createRehearsalMarkText(static_cast<RehearsalMark*>(e)));
|
2012-10-25 16:21:07 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
score()->undoAddElement(e);
|
|
|
|
return e;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
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);
|
2013-05-18 15:02:47 +02:00
|
|
|
FiguredBass::addFiguredBassToSegment(segment(),
|
2012-05-26 14:26:10 +02:00
|
|
|
fb->track(), fb->ticks(), &bNew);
|
|
|
|
if (bNew)
|
|
|
|
score()->undoAddElement(e);
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::IMAGE:
|
2012-05-26 14:26:10 +02:00
|
|
|
e->setParent(segment());
|
|
|
|
score()->undoAddElement(e);
|
|
|
|
return e;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::ICON:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2013-03-05 20:23:59 +01:00
|
|
|
switch(static_cast<Icon*>(e)->iconType()) {
|
2014-05-30 10:14:57 +02:00
|
|
|
case IconType::SBEAM:
|
2016-06-09 09:26:13 +02:00
|
|
|
undoChangeProperty(P_ID::BEAM_MODE, int(Beam::Mode::BEGIN));
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-30 10:14:57 +02:00
|
|
|
case IconType::MBEAM:
|
2016-06-09 09:26:13 +02:00
|
|
|
undoChangeProperty(P_ID::BEAM_MODE, int(Beam::Mode::MID));
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-30 10:14:57 +02:00
|
|
|
case IconType::NBEAM:
|
2016-06-09 09:26:13 +02:00
|
|
|
undoChangeProperty(P_ID::BEAM_MODE, int(Beam::Mode::NONE));
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-30 10:14:57 +02:00
|
|
|
case IconType::BEAM32:
|
2016-06-09 09:26:13 +02:00
|
|
|
undoChangeProperty(P_ID::BEAM_MODE, int(Beam::Mode::BEGIN32));
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-30 10:14:57 +02:00
|
|
|
case IconType::BEAM64:
|
2016-06-09 09:26:13 +02:00
|
|
|
undoChangeProperty(P_ID::BEAM_MODE, int(Beam::Mode::BEGIN64));
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-30 10:14:57 +02:00
|
|
|
case IconType::AUTOBEAM:
|
2016-06-09 09:26:13 +02:00
|
|
|
undoChangeProperty(P_ID::BEAM_MODE, int(Beam::Mode::AUTO));
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-05-30 10:14:57 +02:00
|
|
|
default:
|
|
|
|
break;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
delete e;
|
|
|
|
break;
|
2015-07-19 17:18:38 +02:00
|
|
|
|
|
|
|
case Element::Type::KEYSIG:
|
|
|
|
{
|
|
|
|
KeySig* ks = static_cast<KeySig*>(e);
|
|
|
|
KeySigEvent k = ks->keySigEvent();
|
|
|
|
delete ks;
|
|
|
|
|
|
|
|
// apply only to this stave
|
|
|
|
score()->undoChangeKeySig(staff(), tick(), k);
|
|
|
|
}
|
|
|
|
break;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
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);
|
2014-05-21 15:41:23 +02:00
|
|
|
_crossMeasure = CrossMeasure::UNKNOWN;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ChordRest::setDurationType(const QString& s)
|
|
|
|
{
|
|
|
|
_durationType.setType(s);
|
2014-05-21 15:41:23 +02:00
|
|
|
_crossMeasure = CrossMeasure::UNKNOWN;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ChordRest::setDurationType(int ticks)
|
|
|
|
{
|
|
|
|
_durationType.setVal(ticks);
|
2014-05-21 15:41:23 +02:00
|
|
|
_crossMeasure = CrossMeasure::UNKNOWN;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2015-02-16 09:44:21 +01:00
|
|
|
void ChordRest::setDurationType(TDuration v)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
_durationType = v;
|
2014-05-21 15:41:23 +02:00
|
|
|
_crossMeasure = CrossMeasure::UNKNOWN;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2015-02-16 09:44:21 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// durationUserName
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-02-04 17:06:32 +01:00
|
|
|
QString ChordRest::durationUserName() const
|
2014-07-10 14:13:37 +02:00
|
|
|
{
|
|
|
|
QString tupletType = "";
|
2015-02-16 09:44:21 +01:00
|
|
|
if (tuplet()) {
|
2014-07-10 14:13:37 +02:00
|
|
|
switch (tuplet()->ratio().numerator()) {
|
|
|
|
case 2:
|
2014-11-05 15:10:09 +01:00
|
|
|
tupletType = tr("Duplet");
|
2014-07-10 14:13:37 +02:00
|
|
|
break;
|
|
|
|
case 3:
|
2014-11-05 15:10:09 +01:00
|
|
|
tupletType = tr("Triplet");
|
2014-07-10 14:13:37 +02:00
|
|
|
break;
|
|
|
|
case 4:
|
2014-11-05 15:10:09 +01:00
|
|
|
tupletType = tr("Quadruplet");
|
2014-07-10 14:13:37 +02:00
|
|
|
break;
|
|
|
|
case 5:
|
2014-11-05 15:10:09 +01:00
|
|
|
tupletType = tr("Quintuplet");
|
2014-07-10 14:13:37 +02:00
|
|
|
break;
|
|
|
|
case 6:
|
2014-11-05 15:10:09 +01:00
|
|
|
tupletType = tr("Sextuplet");
|
2014-07-10 14:13:37 +02:00
|
|
|
break;
|
|
|
|
case 7:
|
2014-11-05 15:10:09 +01:00
|
|
|
tupletType = tr("Septuplet");
|
2014-07-10 14:13:37 +02:00
|
|
|
break;
|
|
|
|
case 8:
|
2014-11-05 15:10:09 +01:00
|
|
|
tupletType = tr("Octuplet");
|
2014-07-10 14:13:37 +02:00
|
|
|
break;
|
|
|
|
case 9:
|
2014-11-05 15:10:09 +01:00
|
|
|
tupletType = tr("Nonuplet");
|
2014-07-10 14:13:37 +02:00
|
|
|
break;
|
|
|
|
default:
|
2014-11-05 15:10:09 +01:00
|
|
|
tupletType = tr("Custom Tuplet");
|
2014-07-10 14:13:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
QString dotString = "";
|
2015-01-22 17:16:04 +01:00
|
|
|
if(!tupletType.isEmpty())
|
|
|
|
dotString += " ";
|
2014-07-10 14:13:37 +02:00
|
|
|
|
|
|
|
switch (dots()) {
|
|
|
|
case 1:
|
2015-01-22 17:16:04 +01:00
|
|
|
dotString += tr("Dotted %1").arg(durationType().durationTypeUserName()).trimmed();
|
2014-07-10 14:13:37 +02:00
|
|
|
break;
|
|
|
|
case 2:
|
2015-01-22 17:16:04 +01:00
|
|
|
dotString += tr("Double dotted %1").arg(durationType().durationTypeUserName()).trimmed();
|
2014-07-10 14:13:37 +02:00
|
|
|
break;
|
|
|
|
case 3:
|
2015-01-22 17:16:04 +01:00
|
|
|
dotString += tr("Triple dotted %1").arg(durationType().durationTypeUserName()).trimmed();
|
2014-07-10 14:13:37 +02:00
|
|
|
break;
|
2016-06-01 12:35:20 +02:00
|
|
|
case 4:
|
|
|
|
dotString += tr("Quadruple dotted %1").arg(durationType().durationTypeUserName()).trimmed();
|
|
|
|
break;
|
2015-01-22 17:16:04 +01:00
|
|
|
default:
|
|
|
|
dotString += durationType().durationTypeUserName();
|
2014-07-10 14:13:37 +02:00
|
|
|
}
|
2015-01-22 17:16:04 +01:00
|
|
|
return QString("%1%2").arg(tupletType).arg(dotString);
|
2014-07-10 14:13:37 +02:00
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// add
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void ChordRest::add(Element* e)
|
|
|
|
{
|
|
|
|
e->setParent(this);
|
|
|
|
e->setTrack(track());
|
|
|
|
switch(e->type()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::ARTICULATION:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2016-08-17 12:52:35 +02:00
|
|
|
Articulation* a = toArticulation(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;
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::LYRICS:
|
2016-08-24 14:49:34 +02:00
|
|
|
_lyrics.push_back(toLyrics(e));
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
default:
|
2014-03-04 13:06:23 +01:00
|
|
|
qFatal("ChordRest::add: unknown element %s", e->name());
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// remove
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void ChordRest::remove(Element* e)
|
|
|
|
{
|
2013-02-21 14:28:16 +01:00
|
|
|
switch (e->type()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::ARTICULATION:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2016-08-17 12:52:35 +02:00
|
|
|
Articulation* a = toArticulation(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;
|
2016-08-24 14:49:34 +02:00
|
|
|
case Element::Type::LYRICS: {
|
|
|
|
toLyrics(e)->removeFromScore();
|
|
|
|
auto i = std::find(_lyrics.begin(), _lyrics.end(), toLyrics(e));
|
|
|
|
if (i != _lyrics.end())
|
|
|
|
_lyrics.erase(i);
|
|
|
|
else
|
|
|
|
qDebug("ChordRest::remove: %s %p not found", e->name(), e);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2014-03-04 13:06:23 +01:00
|
|
|
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);
|
2016-02-06 22:03:43 +01:00
|
|
|
if (b->empty())
|
2013-05-22 15:20:14 +02:00
|
|
|
score()->undoRemoveElement(b);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-05-19 13:15:34 +02:00
|
|
|
if (!beamed && isChord())
|
|
|
|
toChord(this)->layoutStem();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2013-03-01 10:07:27 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// undoSetBeamMode
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-26 07:45:09 +02:00
|
|
|
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
|
|
|
|
{
|
2016-05-25 19:41:36 +02:00
|
|
|
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();
|
2015-02-16 09:44:21 +01:00
|
|
|
case P_ID::DURATION_TYPE: return QVariant::fromValue(actualDurationType());
|
2014-08-20 09:50:42 +02:00
|
|
|
default: return DurationElement::getProperty(propertyId);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setProperty
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool ChordRest::setProperty(P_ID propertyId, const QVariant& v)
|
|
|
|
{
|
2016-05-25 19:41:36 +02:00
|
|
|
switch (propertyId) {
|
2016-06-14 10:32:34 +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;
|
2015-01-26 22:58:46 +01:00
|
|
|
case P_ID::VISIBLE:
|
|
|
|
setVisible(v.toBool());
|
|
|
|
measure()->checkMultiVoices(staffIdx());
|
|
|
|
break;
|
2015-02-16 09:44:21 +01:00
|
|
|
case P_ID::DURATION_TYPE:
|
|
|
|
setDurationType(v.value<TDuration>());
|
|
|
|
break;
|
2015-01-26 22:58:46 +01:00
|
|
|
default:
|
|
|
|
return DurationElement::setProperty(propertyId, v);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-06-14 10:32:34 +02:00
|
|
|
triggerLayout();
|
2012-05-26 14:26:10 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-08-12 11:44:36 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// propertyDefault
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QVariant ChordRest::propertyDefault(P_ID propertyId) const
|
|
|
|
{
|
2016-05-25 19:41:36 +02:00
|
|
|
switch (propertyId) {
|
2016-06-14 10:32:34 +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;
|
|
|
|
default:
|
|
|
|
return DurationElement::propertyDefault(propertyId);
|
2012-08-12 11:44:36 +02:00
|
|
|
}
|
2016-06-14 10:32:34 +02:00
|
|
|
triggerLayout();
|
2012-08-12 11:44:36 +02:00
|
|
|
}
|
|
|
|
|
2013-06-19 16:25:29 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// isGrace
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-06-16 23:33:37 +02:00
|
|
|
bool ChordRest::isGrace() const
|
|
|
|
{
|
2016-06-14 10:32:34 +02:00
|
|
|
return isChord() && toChord(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
|
|
|
|
{
|
2016-06-14 10:32:34 +02:00
|
|
|
return isChord()
|
|
|
|
&& (toChord(this)->noteType() & (
|
|
|
|
NoteType::ACCIACCATURA | NoteType::APPOGGIATURA | NoteType::GRACE4 | NoteType::GRACE16 | NoteType::GRACE32
|
|
|
|
));
|
2014-04-23 18:07:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// isGraceAfter
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool ChordRest::isGraceAfter() const
|
|
|
|
{
|
2016-06-14 10:32:34 +02:00
|
|
|
return isChord()
|
|
|
|
&& (toChord(this)->noteType() & (NoteType::GRACE8_AFTER | NoteType::GRACE16_AFTER | 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();
|
|
|
|
if (b && b->elements().front() == this && (MScore::testMode || !b->generated())) {
|
|
|
|
b->setId(xml.beamId++);
|
|
|
|
b->write(xml);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-30 11:26:58 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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
|
2014-06-30 11:26:58 +02:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
2014-06-20 22:48:34 +02:00
|
|
|
|
2016-02-19 10:23:23 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// setTrack
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void ChordRest::setTrack(int val)
|
|
|
|
{
|
|
|
|
Element::setTrack(val);
|
|
|
|
processSiblings([val] (Element* e) { e->setTrack(val); } );
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setScore
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void ChordRest::setScore(Score* s)
|
|
|
|
{
|
|
|
|
Element::setScore(s);
|
|
|
|
processSiblings([s] (Element* e) { e->setScore(s); } );
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// processSiblings
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void ChordRest::processSiblings(std::function<void(Element*)> func)
|
|
|
|
{
|
|
|
|
if (_beam)
|
|
|
|
func(_beam);
|
|
|
|
for (Articulation* a : _articulations)
|
|
|
|
func(a);
|
|
|
|
if (_tabDur)
|
|
|
|
func(_tabDur);
|
2016-08-17 12:52:35 +02:00
|
|
|
for (Lyrics* l : _lyrics)
|
2016-08-24 14:49:34 +02:00
|
|
|
func(l);
|
2016-02-19 10:23:23 +01:00
|
|
|
if (tuplet())
|
|
|
|
func(tuplet());
|
|
|
|
}
|
|
|
|
|
2014-06-20 22:48:34 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// nextElement
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Element* ChordRest::nextElement()
|
|
|
|
{
|
|
|
|
return segment()->firstInNextSegments(staffIdx());
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// prevElement
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Element* ChordRest::prevElement()
|
|
|
|
{
|
|
|
|
return segment()->lastInPrevSegments(staffIdx());
|
|
|
|
}
|
|
|
|
|
2016-02-04 17:06:32 +01:00
|
|
|
QString ChordRest::accessibleExtraInfo() const
|
2014-07-10 14:13:37 +02:00
|
|
|
{
|
|
|
|
QString rez = "";
|
2016-08-17 12:52:35 +02:00
|
|
|
for (Articulation* a : articulations()) {
|
|
|
|
if (!score()->selectionFilter().canSelect(a))
|
|
|
|
continue;
|
2014-08-21 18:32:52 +02:00
|
|
|
rez = QString("%1 %2").arg(rez).arg(a->screenReaderInfo());
|
2015-02-03 01:55:37 +01:00
|
|
|
}
|
2014-07-10 14:13:37 +02:00
|
|
|
|
2016-08-17 12:52:35 +02:00
|
|
|
for (Element* l : lyrics()) {
|
|
|
|
if (!score()->selectionFilter().canSelect(l))
|
|
|
|
continue;
|
2014-08-21 18:32:52 +02:00
|
|
|
rez = QString("%1 %2").arg(rez).arg(l->screenReaderInfo());
|
2014-08-30 01:36:11 +02:00
|
|
|
}
|
2014-07-10 14:13:37 +02:00
|
|
|
|
2014-08-19 14:10:43 +02:00
|
|
|
if (segment()) {
|
2016-08-17 12:52:35 +02:00
|
|
|
for (Element* e : segment()->annotations()) {
|
|
|
|
if (!score()->selectionFilter().canSelect(e))
|
|
|
|
continue;
|
2014-07-10 14:13:37 +02:00
|
|
|
if (e->staffIdx() == staffIdx() )
|
2014-08-21 18:32:52 +02:00
|
|
|
rez = QString("%1 %2").arg(rez).arg(e->screenReaderInfo());
|
2014-07-10 14:13:37 +02:00
|
|
|
}
|
|
|
|
|
2014-08-19 14:10:43 +02:00
|
|
|
SpannerMap& smap = score()->spannerMap();
|
|
|
|
auto spanners = smap.findOverlapping(tick(), tick());
|
2016-01-13 20:13:08 +01:00
|
|
|
for (auto interval : spanners) {
|
2014-07-10 14:13:37 +02:00
|
|
|
Spanner* s = interval.value;
|
2016-08-17 12:52:35 +02:00
|
|
|
if (!score()->selectionFilter().canSelect(s))
|
|
|
|
continue;
|
2014-07-10 14:13:37 +02:00
|
|
|
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())
|
2014-08-21 18:32:52 +02:00
|
|
|
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())
|
2014-08-21 18:32:52 +02:00
|
|
|
rez = tr("%1 End of %2").arg(rez).arg(s->screenReaderInfo());
|
2014-07-10 14:13:37 +02:00
|
|
|
}
|
|
|
|
else {
|
2014-08-19 14:10:43 +02:00
|
|
|
if (s->tick() == tick() && s->staffIdx() == staffIdx())
|
2014-08-21 18:32:52 +02:00
|
|
|
rez = tr("%1 Start of %2").arg(rez).arg(s->screenReaderInfo());
|
2014-07-10 14:13:37 +02:00
|
|
|
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())
|
2014-08-21 18:32:52 +02:00
|
|
|
rez = tr("%1 End of %2").arg(rez).arg(s->screenReaderInfo());
|
2014-07-10 14:13:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rez;
|
|
|
|
}
|
2016-01-04 14:48:58 +01:00
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// shape
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Shape ChordRest::shape() const
|
|
|
|
{
|
|
|
|
Shape shape;
|
|
|
|
for (Articulation* a : _articulations)
|
|
|
|
shape.add(a->bbox().translated(a->pos()));
|
|
|
|
qreal margin = spatium() * .5;
|
2016-08-18 09:15:08 +02:00
|
|
|
qreal x1 = 1000000.0;
|
|
|
|
qreal x2 = -1000000.0;
|
2016-08-17 12:52:35 +02:00
|
|
|
for (Lyrics* l : _lyrics) {
|
2016-08-17 16:40:01 +02:00
|
|
|
if (l->autoplace())
|
|
|
|
l->rUserYoffset() = 0.0;
|
2016-08-18 09:15:08 +02:00
|
|
|
// for horizontal spacing we only need the lyrics width:
|
|
|
|
x1 = qMin(x1, l->bbox().x() - margin + l->pos().x());
|
|
|
|
x2 = qMax(x2, x1 + l->bbox().width() + margin);
|
2016-01-04 14:48:58 +01:00
|
|
|
}
|
2016-08-18 09:15:08 +02:00
|
|
|
if (x2 > x1)
|
2016-08-29 14:49:59 +02:00
|
|
|
shape.add(QRectF(x1, -2.0, x2-x1, 0.0));
|
2016-01-04 14:48:58 +01:00
|
|
|
return shape;
|
|
|
|
}
|
2016-08-24 14:49:34 +02:00
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// lyrics
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Lyrics* ChordRest::lyrics(int no, Placement p) const
|
|
|
|
{
|
|
|
|
for (Lyrics* l : _lyrics) {
|
|
|
|
if (l->placement() == p && l->no() == no)
|
|
|
|
return l;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// lastVerse
|
|
|
|
// return last verse number (starting from 0)
|
|
|
|
// return -1 if there are no lyrics;
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int ChordRest::lastVerse(Placement p) const
|
|
|
|
{
|
|
|
|
int lastVerse = -1;
|
|
|
|
|
|
|
|
for (Lyrics* l : _lyrics) {
|
|
|
|
if (l->placement() == p && l->no() > lastVerse)
|
|
|
|
lastVerse = l->no();
|
|
|
|
}
|
|
|
|
|
|
|
|
return lastVerse;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// flipLyrics
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void ChordRest::flipLyrics(Lyrics* l)
|
|
|
|
{
|
|
|
|
Element::Placement p = l->placement();
|
|
|
|
if (p == Element::Placement::ABOVE)
|
|
|
|
p = Element::Placement::BELOW;
|
|
|
|
else
|
|
|
|
p = Element::Placement::ABOVE;
|
|
|
|
int verses = lastVerse(p);
|
|
|
|
l->undoChangeProperty(P_ID::VERSE, verses + 1);
|
|
|
|
l->undoChangeProperty(P_ID::AUTOPLACE, true);
|
|
|
|
l->undoChangeProperty(P_ID::PLACEMENT, int(p));
|
|
|
|
}
|
|
|
|
|
2013-05-13 18:49:17 +02:00
|
|
|
}
|
|
|
|
|