MuseScore/libmscore/tuplet.cpp

1084 lines
38 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 "tuplet.h"
#include "score.h"
#include "chord.h"
#include "note.h"
#include "xml.h"
#include "staff.h"
2012-05-26 14:26:10 +02:00
#include "style.h"
#include "text.h"
#include "element.h"
#include "undo.h"
#include "stem.h"
2013-09-02 19:07:39 +02:00
#include "beam.h"
#include "measure.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
//---------------------------------------------------------
// Tuplet
//---------------------------------------------------------
Tuplet::Tuplet(Score* s)
: DurationElement(s)
{
setFlags(ElementFlag::MOVABLE | ElementFlag::SELECTABLE);
_direction = score()->style(StyleIdx::tupletDirection).value<Direction>();
_numberType = NumberType(score()->style(StyleIdx::tupletNumberType).toInt());
_bracketType = BracketType(score()->style(StyleIdx::tupletBracketType).toInt());
_ratio = Fraction(1, 1);
2012-05-26 14:26:10 +02:00
_number = 0;
_hasBracket = false;
_isUp = true;
}
Tuplet::Tuplet(const Tuplet& t)
: DurationElement(t)
{
_tick = t._tick;
_hasBracket = t._hasBracket;
_ratio = t._ratio;
_baseLen = t._baseLen;
_direction = t._direction;
_numberType = t._numberType;
_bracketType = t._bracketType;
2012-05-26 14:26:10 +02:00
directionStyle = t.directionStyle;
numberStyle = t.numberStyle;
bracketStyle = t.bracketStyle;
_isUp = t._isUp;
p1 = t.p1;
p2 = t.p2;
_p1 = t._p1;
_p2 = t._p2;
2012-05-26 14:26:10 +02:00
if (t._number)
_number = new Text(*t._number);
else
_number = 0;
}
//---------------------------------------------------------
// ~Tuplet
//---------------------------------------------------------
Tuplet::~Tuplet()
{
delete _number;
}
//---------------------------------------------------------
// setSelected
//---------------------------------------------------------
void Tuplet::setSelected(bool f)
{
Element::setSelected(f);
if (_number)
_number->setSelected(f);
}
//---------------------------------------------------------
// setVisible
//---------------------------------------------------------
void Tuplet::setVisible(bool f)
{
Element::setVisible(f);
if (_number)
_number->setVisible(f);
}
//---------------------------------------------------------
// layout
//---------------------------------------------------------
void Tuplet::layout()
{
if (_elements.empty()) {
qDebug("Tuplet::layout(): tuplet is empty");
return;
}
// is in a TAB without stems, skip any format: tuplets are not shown
if (staff() && staff()->isTabStaff() && staff()->staffType()->slashStyle())
return;
2012-05-26 14:26:10 +02:00
qreal _spatium = spatium();
if (_numberType != NumberType::NO_TEXT) {
2012-05-26 14:26:10 +02:00
if (_number == 0) {
_number = new Text(score());
_number->setTextStyleType(TextStyleType::TUPLET);
_number->setTrack(track());
2012-05-26 14:26:10 +02:00
_number->setParent(this);
_number->setVisible(visible());
}
if (_numberType == NumberType::SHOW_NUMBER)
_number->setXmlText(QString("%1").arg(_ratio.numerator()));
2012-05-26 14:26:10 +02:00
else
_number->setXmlText(QString("%1:%2").arg(_ratio.numerator()).arg(_ratio.denominator()));
2012-05-26 14:26:10 +02:00
}
else {
if (_number) {
if (_number->selected())
score()->deselect(_number);
delete _number;
_number = 0;
}
}
//
// find out main direction
//
2016-03-02 13:20:19 +01:00
if (_direction == Direction::AUTO) {
2012-05-26 14:26:10 +02:00
int up = 1;
2013-05-24 11:44:21 +02:00
foreach (const DurationElement* e, _elements) {
if (e->isChord()) {
const Chord* c = toChord(e);
2016-03-02 13:20:19 +01:00
if (c->stemDirection() != Direction::AUTO)
up += c->stemDirection() == Direction::UP ? 1000 : -1000;
2012-05-26 14:26:10 +02:00
else
up += c->up() ? 1 : -1;
}
else if (e->isTuplet()) {
2012-05-26 14:26:10 +02:00
// TODO
}
}
_isUp = up > 0;
}
else
2016-03-02 13:20:19 +01:00
_isUp = _direction == Direction::UP;
2012-05-26 14:26:10 +02:00
const DurationElement* cr1 = _elements.front();
while (cr1->isTuplet()) {
const Tuplet* t = toTuplet(cr1);
2012-05-26 14:26:10 +02:00
if (t->elements().empty())
break;
cr1 = t->elements().front();
}
const DurationElement* cr2 = _elements.back();
while (cr2->isTuplet()) {
const Tuplet* t = toTuplet(cr2);
2012-05-26 14:26:10 +02:00
if (t->elements().empty())
break;
cr2 = t->elements().back();
}
//
// shall we draw a bracket?
//
if (_bracketType == BracketType::AUTO_BRACKET) {
2013-05-24 11:44:21 +02:00
_hasBracket = false;
foreach (DurationElement* e, _elements) {
if (e->isTuplet() || e->isRest()) {
2013-05-24 11:44:21 +02:00
_hasBracket = true;
break;
}
else if (e->isChordRest()) {
ChordRest* cr = toChordRest(e);
2013-05-24 11:44:21 +02:00
//
// maybe we should check for more than one beam
//
if (cr->beam() == 0) {
2012-05-26 14:26:10 +02:00
_hasBracket = true;
break;
}
}
}
}
else
_hasBracket = _bracketType != BracketType::SHOW_NO_BRACKET;
2012-05-26 14:26:10 +02:00
//
// calculate bracket start and end point p1 p2
//
2014-05-26 15:31:36 +02:00
qreal maxSlope = score()->styleD(StyleIdx::tupletMaxSlope);
bool outOfStaff = score()->styleB(StyleIdx::tupletOufOfStaff);
2016-03-19 11:41:38 +01:00
qreal vHeadDistance = score()->styleP(StyleIdx::tupletVHeadDistance);
qreal vStemDistance = score()->styleP(StyleIdx::tupletVStemDistance);
qreal stemLeft = score()->styleP(StyleIdx::tupletStemLeftDistance);
qreal stemRight = score()->styleP(StyleIdx::tupletStemRightDistance);
qreal noteLeft = score()->styleP(StyleIdx::tupletNoteLeftDistance);
qreal noteRight = score()->styleP(StyleIdx::tupletNoteRightDistance);
2013-09-02 19:07:39 +02:00
int move = 0;
if (outOfStaff && cr1->isChordRest() && cr2->isChordRest()) {
// account for staff move when adjusting bracket to avoid staff
// but don't attempt adjustment unless both endpoints are in same staff
if (toChordRest(cr1)->staffMove() == toChordRest(cr2)->staffMove())
move = toChordRest(cr1)->staffMove();
else
outOfStaff = false;
}
2013-09-02 19:07:39 +02:00
qreal l1 = _spatium; // bracket tip height
qreal l2l = vHeadDistance; // left bracket vertical distance
qreal l2r = vHeadDistance; // right bracket vertical distance right
2013-05-24 11:44:21 +02:00
if (_isUp)
2013-09-02 19:07:39 +02:00
vHeadDistance = -vHeadDistance;
2013-05-24 11:44:21 +02:00
p1 = cr1->pagePos();
p2 = cr2->pagePos();
2013-09-02 19:07:39 +02:00
p1.rx() -= noteLeft;
p2.rx() += score()->noteHeadWidth() + noteRight;
p1.ry() += vHeadDistance;
p2.ry() += vHeadDistance;
2012-05-26 14:26:10 +02:00
2013-09-02 19:07:39 +02:00
qreal xx1 = p1.x(); // use to center the number on the beam
// follow beam angle if one beam extends over entire tuplet
bool followBeam = false;
qreal beamAdjust = 0.0;
if (cr1->beam() && cr1->beam() == cr2->beam()) {
followBeam = true;
2016-03-19 11:41:38 +01:00
beamAdjust = score()->styleP(StyleIdx::beamWidth) * 0.5 * mag();
}
2013-05-24 11:44:21 +02:00
if (_isUp) {
if (cr1->isChord()) {
const Chord* chord1 = toChord(cr1);
2012-05-26 14:26:10 +02:00
Stem* stem = chord1->stem();
2013-09-02 19:07:39 +02:00
if (stem)
xx1 = stem->abbox().x();
2013-09-02 19:07:39 +02:00
if (chord1->up()) {
if (stem) {
if (followBeam)
p1.ry() = stem->abbox().y() - beamAdjust;
else if (chord1->beam())
2013-09-02 19:07:39 +02:00
p1.ry() = chord1->beam()->abbox().y();
else
p1.ry() = stem->abbox().y();
l2l = vStemDistance;
}
else {
p1.ry() = chord1->upNote()->abbox().top(); // whole note
2012-05-26 14:26:10 +02:00
}
}
2013-09-02 19:07:39 +02:00
else if (!chord1->up()) {
p1.ry() = chord1->upNote()->abbox().top();
if (stem)
2013-09-02 19:07:39 +02:00
p1.rx() = cr1->pagePos().x() - stemLeft;
}
2012-05-26 14:26:10 +02:00
}
if (cr2->isChord()) {
const Chord* chord2 = toChord(cr2);
2012-05-26 14:26:10 +02:00
Stem* stem = chord2->stem();
2013-09-02 19:07:39 +02:00
if (stem && chord2->up()) {
if (followBeam)
p2.ry() = stem->abbox().top() - beamAdjust;
else if (chord2->beam())
2013-09-02 19:07:39 +02:00
p2.ry() = chord2->beam()->abbox().top();
else
p2.ry() = stem->abbox().top();
l2r = vStemDistance;
p2.rx() = chord2->pagePos().x() + chord2->maxHeadWidth() + stemRight;
}
else {
p2.ry() = chord2->upNote()->abbox().top();
2012-05-26 14:26:10 +02:00
}
}
//
// special case: one of the bracket endpoints is
// a rest
//
if (cr1->isChord() && cr2->isChord()) {
2012-05-26 14:26:10 +02:00
if (p2.y() < p1.y())
p1.setY(p2.y());
else
p2.setY(p1.y());
}
else if (cr1->isChord() && !cr2->isChord()) {
2012-05-26 14:26:10 +02:00
if (p1.y() < p2.y())
p2.setY(p1.y());
else
p1.setY(p2.y());
}
2013-09-02 19:07:39 +02:00
// outOfStaff
if (outOfStaff) {
qreal min = cr1->measure()->staffabbox(cr1->staffIdx() + move).y();
2013-09-02 19:07:39 +02:00
if (min < p1.y()) {
p1.ry() = min;
l2l = vStemDistance;
}
min = cr2->measure()->staffabbox(cr2->staffIdx() + move).y();
2013-09-02 19:07:39 +02:00
if (min < p2.y()) {
p2.ry() = min;
l2r = vStemDistance;
}
}
// check that slope is no more than max
2013-09-02 19:07:39 +02:00
qreal d = (p2.y() - p1.y())/(p2.x() - p1.x());
if (d < -maxSlope) {
// move p1 y up
p1.ry() = p2.y() + maxSlope * (p2.x() - p1.x());
}
else if (d > maxSlope) {
// move p2 y up
p2.ry() = p1.ry() + maxSlope * (p2.x() - p1.x());
}
2012-05-26 14:26:10 +02:00
2013-09-02 19:07:39 +02:00
// check for collisions
2012-05-26 14:26:10 +02:00
int n = _elements.size();
if (n >= 3) {
2013-09-02 19:07:39 +02:00
d = (p2.y() - p1.y())/(p2.x() - p1.x());
2012-05-26 14:26:10 +02:00
for (int i = 1; i < (n-1); ++i) {
Element* e = _elements[i];
if (e->isChord()) {
const Chord* chord = toChord(e);
2012-05-26 14:26:10 +02:00
const Stem* stem = chord->stem();
if (stem) {
QRectF r(chord->up() ? stem->abbox() : chord->upNote()->abbox());
2012-05-26 14:26:10 +02:00
qreal y3 = r.top();
qreal x3 = r.x() + r.width() * .5;
qreal y0 = p1.y() + (x3 - p1.x()) * d;
qreal c = y0 - y3;
if (c > 0) {
p1.ry() -= c;
p2.ry() -= c;
}
}
}
}
}
}
else {
if (cr1->isChord()) {
const Chord* chord1 = toChord(cr1);
2012-05-26 14:26:10 +02:00
Stem* stem = chord1->stem();
2013-09-02 19:07:39 +02:00
if (stem)
xx1 = stem->abbox().x();
2013-09-02 19:07:39 +02:00
if (!chord1->up()) {
if (stem) {
if (followBeam)
p1.ry() = stem->abbox().bottom() + beamAdjust;
else if (chord1->beam())
2013-09-02 19:07:39 +02:00
p1.ry() = chord1->beam()->abbox().bottom();
else
p1.ry() = stem->abbox().bottom();
l2l = vStemDistance;
p1.rx() = cr1->pagePos().x() - stemLeft;
}
else {
2013-09-02 19:07:39 +02:00
p1.ry() = chord1->downNote()->abbox().bottom(); // whole note
2012-05-26 14:26:10 +02:00
}
}
2013-09-02 19:07:39 +02:00
else if (chord1->up()) {
p1.ry() = chord1->downNote()->abbox().bottom();
}
2012-05-26 14:26:10 +02:00
}
if (cr2->isChord()) {
const Chord* chord2 = toChord(cr2);
2012-05-26 14:26:10 +02:00
Stem* stem = chord2->stem();
if (stem && !chord2->up()) {
2013-05-24 11:44:21 +02:00
// if (chord2->beam())
// p2.setX(stem->abbox().x());
if (followBeam)
p2.ry() = stem->abbox().bottom() + beamAdjust;
if (chord2->beam())
2013-09-02 19:07:39 +02:00
p2.ry() = chord2->beam()->abbox().bottom();
else
p2.ry() = stem->abbox().bottom();
l2r = vStemDistance;
2012-05-26 14:26:10 +02:00
}
2013-09-02 19:07:39 +02:00
else {
p2.ry() = chord2->downNote()->abbox().bottom();
if (stem)
p2.rx() = chord2->pagePos().x() + chord2->maxHeadWidth() + stemRight;
2012-05-26 14:26:10 +02:00
}
}
2013-09-02 19:07:39 +02:00
//
// special case: one of the bracket endpoints is
// a rest
//
if (!cr1->isChord() && cr2->isChord()) {
2012-05-26 14:26:10 +02:00
if (p2.y() > p1.y())
p1.setY(p2.y());
else
p2.setY(p1.y());
}
else if (cr1->isChord() && !cr2->isChord()) {
2012-05-26 14:26:10 +02:00
if (p1.y() > p2.y())
p2.setY(p1.y());
else
p1.setY(p2.y());
}
2013-09-02 19:07:39 +02:00
// outOfStaff
if (outOfStaff) {
qreal max = cr1->measure()->staffabbox(cr1->staffIdx() + move).bottom();
2013-09-02 19:07:39 +02:00
if (max > p1.y()) {
p1.ry() = max;
l2l = vStemDistance;
}
max = cr2->measure()->staffabbox(cr2->staffIdx() + move).bottom();
2013-09-02 19:07:39 +02:00
if (max > p2.y()) {
p2.ry() = max;
l2r = vStemDistance;
}
}
// check that slope is no more than max
2013-09-02 19:07:39 +02:00
qreal d = (p2.y() - p1.y())/(p2.x() - p1.x());
if (d < -maxSlope) {
// move p1 y up
p2.ry() = p1.y() - maxSlope * (p2.x() - p1.x());
}
else if (d > maxSlope) {
// move p2 y up
p1.ry() = p2.ry() - maxSlope * (p2.x() - p1.x());
}
2012-05-26 14:26:10 +02:00
// check for collisions
int n = _elements.size();
if (n >= 3) {
qreal d = (p2.y() - p1.y())/(p2.x() - p1.x());
for (int i = 1; i < (n-1); ++i) {
Element* e = _elements[i];
if (e->isChord()) {
const Chord* chord = toChord(e);
2012-05-26 14:26:10 +02:00
const Stem* stem = chord->stem();
if (stem) {
QRectF r(chord->up() ? chord->downNote()->abbox() : stem->abbox());
2012-05-26 14:26:10 +02:00
qreal y3 = r.bottom();
qreal x3 = r.x() + r.width() * .5;
qreal y0 = p1.y() + (x3 - p1.x()) * d;
qreal c = y0 - y3;
if (c < 0) {
p1.ry() -= c;
p2.ry() -= c;
}
}
}
}
}
}
setPos(0.0, 0.0);
QPointF mp(parent()->pagePos());
p1 -= mp;
p2 -= mp;
p1 += _p1;
p2 += _p2;
xx1 -= mp.x();
2013-09-02 19:07:39 +02:00
p1.ry() -= l2l * (_isUp ? 1.0 : -1.0);
p2.ry() -= l2r * (_isUp ? 1.0 : -1.0);
2012-05-26 14:26:10 +02:00
// center number
qreal x3 = 0.0;
qreal numberWidth = 0.0;
if (_number) {
_number->layout();
2013-09-02 19:07:39 +02:00
numberWidth = _number->bbox().width();
//
// for beamed tuplets, center number on beam
//
2013-09-02 19:07:39 +02:00
if (cr1->beam() && cr2->beam() && cr1->beam() == cr2->beam()) {
const ChordRest* crr = toChordRest(cr1);
2013-09-02 19:07:39 +02:00
if(_isUp == crr->up()) {
qreal deltax = cr2->pagePos().x() - cr1->pagePos().x();
x3 = xx1 + deltax * .5;
}
else {
qreal deltax = p2.x() - p1.x();
x3 = p1.x() + deltax * .5;
}
}
else {
qreal deltax = p2.x() - p1.x();
x3 = p1.x() + deltax * .5;
}
2012-05-26 14:26:10 +02:00
2013-09-02 19:07:39 +02:00
qreal y3 = p1.y() + (p2.y() - p1.y()) * .5 - l1 * (_isUp ? 1.0 : -1.0);
2012-05-26 14:26:10 +02:00
_number->setPos(QPointF(x3, y3) - ipos());
}
if (_hasBracket) {
qreal slope = (p2.y() - p1.y()) / (p2.x() - p1.x());
if (_isUp) {
if (_number) {
2013-09-02 19:07:39 +02:00
bracketL[0] = QPointF(p1.x(), p1.y());
bracketL[1] = QPointF(p1.x(), p1.y() - l1);
2012-05-26 14:26:10 +02:00
qreal x = x3 - numberWidth * .5 - _spatium * .5;
qreal y = p1.y() + (x - p1.x()) * slope;
2013-09-02 19:07:39 +02:00
bracketL[2] = QPointF(x, y - l1);
2012-05-26 14:26:10 +02:00
x = x3 + numberWidth * .5 + _spatium * .5;
y = p1.y() + (x - p1.x()) * slope;
2013-09-02 19:07:39 +02:00
bracketR[0] = QPointF(x, y - l1);
bracketR[1] = QPointF(p2.x(), p2.y() - l1);
bracketR[2] = QPointF(p2.x(), p2.y());
2012-05-26 14:26:10 +02:00
}
else {
2013-09-02 19:07:39 +02:00
bracketL[0] = QPointF(p1.x(), p1.y());
bracketL[1] = QPointF(p1.x(), p1.y() - l1);
bracketL[2] = QPointF(p2.x(), p2.y() - l1);
bracketL[3] = QPointF(p2.x(), p2.y());
2012-05-26 14:26:10 +02:00
}
}
else {
if (_number) {
2013-09-02 19:07:39 +02:00
bracketL[0] = QPointF(p1.x(), p1.y());
bracketL[1] = QPointF(p1.x(), p1.y() + l1);
2012-05-26 14:26:10 +02:00
qreal x = x3 - numberWidth * .5 - _spatium * .5;
qreal y = p1.y() + (x - p1.x()) * slope;
2013-09-02 19:07:39 +02:00
bracketL[2] = QPointF(x, y + l1);
2012-05-26 14:26:10 +02:00
x = x3 + numberWidth * .5 + _spatium * .5;
y = p1.y() + (x - p1.x()) * slope;
2013-09-02 19:07:39 +02:00
bracketR[0] = QPointF(x, y + l1);
bracketR[1] = QPointF(p2.x(), p2.y() + l1);
bracketR[2] = QPointF(p2.x(), p2.y());
2012-05-26 14:26:10 +02:00
}
else {
2013-09-02 19:07:39 +02:00
bracketL[0] = QPointF(p1.x(), p1.y());
bracketL[1] = QPointF(p1.x(), p1.y() + l1);
bracketL[2] = QPointF(p2.x(), p2.y() + l1);
bracketL[3] = QPointF(p2.x(), p2.y());
2012-05-26 14:26:10 +02:00
}
}
}
QRectF r;
if (_number) {
r |= _number->bbox().translated(_number->pos());
if (_hasBracket) {
QRectF b;
b.setCoords(bracketL[1].x(), bracketL[1].y(), bracketR[2].x(), bracketR[2].y());
r |= b;
}
}
else if (_hasBracket) {
QRectF b;
b.setCoords(bracketL[1].x(), bracketL[1].y(), bracketL[3].x(), bracketL[3].y());
r |= b;
}
setbbox(r);
}
//---------------------------------------------------------
// draw
//---------------------------------------------------------
void Tuplet::draw(QPainter* painter) const
{
// if in a TAB without stems, tuplets are not shown
if (staff() && staff()->isTabStaff() && staff()->staffType()->slashStyle())
return;
2012-05-26 14:26:10 +02:00
QColor color(curColor());
if (_number) {
painter->setPen(color);
QPointF pos(_number->pos());
painter->translate(pos);
_number->draw(painter);
painter->translate(-pos);
}
if (_hasBracket) {
2016-03-19 11:41:38 +01:00
painter->setPen(QPen(color, mag() * score()->styleP(StyleIdx::tupletBracketWidth)));
2012-05-26 14:26:10 +02:00
if (!_number)
painter->drawPolyline(bracketL, 4);
else {
painter->drawPolyline(bracketL, 3);
painter->drawPolyline(bracketR, 3);
}
}
}
//---------------------------------------------------------
// scanElements
//---------------------------------------------------------
void Tuplet::scanElements(void* data, void (*func)(void*, Element*), bool all)
{
func(data, this);
if (_number && all)
func(data, _number);
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// write
//---------------------------------------------------------
void Tuplet::write(Xml& xml) const
{
xml.stag(QString("Tuplet id=\"%1\"").arg(_id));
if (tuplet())
xml.tag("Tuplet", tuplet()->id());
Element::writeProperties(xml);
2014-05-26 18:18:01 +02:00
writeProperty(xml, P_ID::DIRECTION);
writeProperty(xml, P_ID::NUMBER_TYPE);
writeProperty(xml, P_ID::BRACKET_TYPE);
writeProperty(xml, P_ID::NORMAL_NOTES);
writeProperty(xml, P_ID::ACTUAL_NOTES);
writeProperty(xml, P_ID::P1);
writeProperty(xml, P_ID::P2);
2012-08-10 17:01:35 +02:00
2012-05-26 14:26:10 +02:00
xml.tag("baseNote", _baseLen.name());
if (_number) {
xml.stag("Number");
_number->writeProperties(xml);
xml.etag();
}
if (!userOff().isNull())
xml.tag("offset", userOff() / spatium());
xml.etag();
}
//---------------------------------------------------------
// read
//---------------------------------------------------------
2013-01-11 18:10:18 +01:00
void Tuplet::read(XmlReader& e)
2012-05-26 14:26:10 +02:00
{
int bl = -1;
2013-01-11 18:10:18 +01:00
_id = e.intAttribute("id", 0);
2012-05-26 14:26:10 +02:00
2013-01-11 18:10:18 +01:00
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
2012-05-26 14:26:10 +02:00
2012-08-10 17:01:35 +02:00
if (tag == "direction")
2016-03-02 13:20:19 +01:00
readProperty(e, P_ID::DIRECTION);
2012-08-10 17:01:35 +02:00
else if (tag == "numberType")
_numberType = NumberType(e.readInt());
2012-08-10 17:01:35 +02:00
else if (tag == "bracketType")
_bracketType = BracketType(e.readInt());
2012-08-10 17:01:35 +02:00
else if (tag == "normalNotes")
2013-01-11 18:10:18 +01:00
_ratio.setDenominator(e.readInt());
2012-08-10 17:01:35 +02:00
else if (tag == "actualNotes")
2013-01-11 18:10:18 +01:00
_ratio.setNumerator(e.readInt());
2012-08-10 17:01:35 +02:00
else if (tag == "p1")
2013-01-11 18:10:18 +01:00
_p1 = e.readPoint();
2012-08-10 17:01:35 +02:00
else if (tag == "p2")
2013-01-11 18:10:18 +01:00
_p2 = e.readPoint();
2012-05-26 14:26:10 +02:00
else if (tag == "baseNote")
2013-01-11 18:10:18 +01:00
_baseLen = TDuration(e.readElementText());
2012-05-26 14:26:10 +02:00
else if (tag == "Number") {
_number = new Text(score());
_number->setParent(this);
_number->read(e);
_number->setTextStyleType(TextStyleType::TUPLET);
2012-05-26 14:26:10 +02:00
_number->setVisible(visible()); //?? override saved property
_number->setTrack(track());
2012-05-26 14:26:10 +02:00
}
else if (tag == "subtype") // obsolete
2013-06-04 18:29:14 +02:00
e.skipCurrentElement();
2012-05-26 14:26:10 +02:00
else if (tag == "hasNumber") // obsolete
_numberType = e.readInt() ? NumberType::SHOW_NUMBER : NumberType::NO_TEXT;
2012-05-26 14:26:10 +02:00
else if (tag == "hasLine") { // obsolete
2013-01-11 18:10:18 +01:00
_hasBracket = e.readInt();
_bracketType = BracketType::AUTO_BRACKET;
2012-05-26 14:26:10 +02:00
}
else if (tag == "baseLen") // obsolete
2013-01-11 18:10:18 +01:00
bl = e.readInt();
else if (!DurationElement::readProperties(e))
2013-01-11 18:10:18 +01:00
e.unknown();
2012-05-26 14:26:10 +02:00
}
2016-11-02 08:42:32 +01:00
// Fraction f(_ratio.reduced().denominator(), _baseLen.fraction().denominator());
Fraction f(_ratio.denominator(), _baseLen.fraction().denominator());
setDuration(f.reduced());
2012-05-26 14:26:10 +02:00
if (bl != -1) { // obsolete
TDuration d;
d.setVal(bl);
_baseLen = d;
// qDebug("Tuplet base len %d/%d", d.fraction().numerator(), d.fraction().denominator());
// qDebug(" %s dots %d, %d/%d", qPrintable(d.name()), d.dots(), _ratio.numerator(), _ratio.denominator());
d.setVal(bl * _ratio.denominator());
setDuration(d.fraction());
}
}
//---------------------------------------------------------
// add
//---------------------------------------------------------
void Tuplet::add(Element* e)
{
#ifndef NDEBUG
for(DurationElement* el : _elements) {
2012-05-26 14:26:10 +02:00
if (el == e) {
qDebug("%p Tuplet::add: %p %s already there", this, e, e->name());
return;
2012-05-26 14:26:10 +02:00
}
}
#endif
switch (e->type()) {
case Element::Type::TEXT:
2012-05-26 14:26:10 +02:00
_number = static_cast<Text*>(e);
break;
case Element::Type::CHORD:
case Element::Type::REST:
case Element::Type::TUPLET: {
2013-01-24 09:31:41 +01:00
bool found = false;
2012-05-26 14:26:10 +02:00
DurationElement* de = static_cast<DurationElement*>(e);
int tick = de->tick();
if (tick != -1) {
for (unsigned int i = 0; i < _elements.size(); ++i) {
2012-05-26 14:26:10 +02:00
if (_elements[i]->tick() > tick) {
_elements.insert(_elements.begin() + i, de);
2013-01-24 09:31:41 +01:00
found = true;
break;
2012-05-26 14:26:10 +02:00
}
}
}
2013-01-24 09:31:41 +01:00
if (!found)
_elements.push_back(de);
2012-05-26 14:26:10 +02:00
de->setTuplet(this);
2013-01-24 09:31:41 +01:00
}
2012-05-26 14:26:10 +02:00
break;
default:
qDebug("Tuplet::add() unknown element");
break;
}
}
//---------------------------------------------------------
// remove
//---------------------------------------------------------
void Tuplet::remove(Element* e)
{
switch (e->type()) {
case Element::Type::TEXT:
2012-05-26 14:26:10 +02:00
if (e == _number)
_number = 0;
break;
case Element::Type::CHORD:
case Element::Type::REST:
case Element::Type::TUPLET: {
auto i = std::find(_elements.begin(), _elements.end(), static_cast<DurationElement*>(e));
if (i == _elements.end()) {
2014-04-15 17:01:41 +02:00
qDebug("Tuplet::remove: cannot find element <%s>", e->name());
2016-07-10 12:00:57 +02:00
qDebug(" elements %zu", _elements.size());
2012-05-26 14:26:10 +02:00
}
else
_elements.erase(i);
}
2012-05-26 14:26:10 +02:00
break;
default:
qDebug("Tuplet::remove: unknown element");
break;
}
}
//---------------------------------------------------------
// isEditable
//---------------------------------------------------------
bool Tuplet::isEditable() const
{
return _hasBracket;
}
//---------------------------------------------------------
// editDrag
//---------------------------------------------------------
void Tuplet::editDrag(const EditData& ed)
{
if (ed.curGrip == Grip::START)
2012-05-26 14:26:10 +02:00
_p1 += ed.delta;
else
_p2 += ed.delta;
setGenerated(false);
layout();
2016-03-02 13:20:19 +01:00
score()->setUpdateAll();
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// updateGrips
//---------------------------------------------------------
void Tuplet::updateGrips(Grip* defaultGrip, QVector<QRectF>& grip) const
2012-05-26 14:26:10 +02:00
{
*defaultGrip = Grip::END;
2012-05-26 14:26:10 +02:00
grip[0].translate(pagePos() + p1);
grip[1].translate(pagePos() + p2);
}
//---------------------------------------------------------
2012-11-19 10:08:15 +01:00
// reset
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
2012-11-19 10:08:15 +01:00
void Tuplet::reset()
2012-05-26 14:26:10 +02:00
{
if (directionStyle == PropertyStyle::UNSTYLED)
undoChangeProperty(P_ID::DIRECTION, propertyDefault(P_ID::DIRECTION), PropertyStyle::STYLED);
if (numberStyle == PropertyStyle::UNSTYLED)
undoChangeProperty(P_ID::NUMBER_TYPE, propertyDefault(P_ID::NUMBER_TYPE), PropertyStyle::STYLED);
if (bracketStyle == PropertyStyle::UNSTYLED)
undoChangeProperty(P_ID::BRACKET_TYPE, propertyDefault(P_ID::BRACKET_TYPE), PropertyStyle::STYLED);
2012-05-26 14:26:10 +02:00
score()->addRefresh(canvasBoundingRect());
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::P1, QPointF());
undoChangeProperty(P_ID::P2, QPointF());
undoChangeProperty(P_ID::DIRECTION, propertyDefault(P_ID::DIRECTION));
2012-05-26 14:26:10 +02:00
2012-11-19 10:08:15 +01:00
Element::reset();
2012-05-26 14:26:10 +02:00
layout();
score()->addRefresh(canvasBoundingRect());
}
//---------------------------------------------------------
// dump
//---------------------------------------------------------
void Tuplet::dump() const
{
Element::dump();
qDebug("ratio %s", qPrintable(_ratio.print()));
}
//---------------------------------------------------------
// setTrack
//---------------------------------------------------------
void Tuplet::setTrack(int val)
{
if (_number)
_number->setTrack(val);
2012-05-26 14:26:10 +02:00
Element::setTrack(val);
}
//---------------------------------------------------------
// tickGreater
//---------------------------------------------------------
static bool tickGreater(const DurationElement* a, const DurationElement* b)
{
return a->tick() < b->tick();
}
//---------------------------------------------------------
// sortElements
//---------------------------------------------------------
void Tuplet::sortElements()
{
qSort(_elements.begin(), _elements.end(), tickGreater);
}
//---------------------------------------------------------
// elementsDuration
/// Get the sum of the element fraction in the tuplet,
/// even if the tuplet is not complete yet
//---------------------------------------------------------
Fraction Tuplet::elementsDuration()
{
Fraction f;
for (DurationElement* el : _elements)
2013-08-02 10:45:46 +02:00
f += el->duration();
return f;
}
2012-08-10 17:01:35 +02:00
//---------------------------------------------------------
// getProperty
//---------------------------------------------------------
QVariant Tuplet::getProperty(P_ID propertyId) const
2012-05-26 14:26:10 +02:00
{
2016-03-08 17:58:04 +01:00
switch (propertyId) {
2014-05-26 18:18:01 +02:00
case P_ID::DIRECTION:
2016-03-02 13:20:19 +01:00
return _direction;
2014-05-26 18:18:01 +02:00
case P_ID::NUMBER_TYPE:
return int(_numberType);
2014-05-26 18:18:01 +02:00
case P_ID::BRACKET_TYPE:
return int(_bracketType);
2014-05-26 18:18:01 +02:00
case P_ID::NORMAL_NOTES:
2012-08-10 17:01:35 +02:00
return _ratio.denominator();
2014-05-26 18:18:01 +02:00
case P_ID::ACTUAL_NOTES:
2012-08-10 17:01:35 +02:00
return _ratio.numerator();
2014-05-26 18:18:01 +02:00
case P_ID::P1:
2012-08-10 17:01:35 +02:00
return _p1;
2014-05-26 18:18:01 +02:00
case P_ID::P2:
2012-08-10 17:01:35 +02:00
return _p2;
default:
2012-05-26 14:26:10 +02:00
break;
}
2012-09-25 13:33:50 +02:00
return DurationElement::getProperty(propertyId);
2012-05-26 14:26:10 +02:00
}
2012-08-10 17:01:35 +02:00
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
2012-05-26 14:26:10 +02:00
bool Tuplet::setProperty(P_ID propertyId, const QVariant& v)
{
2016-03-08 17:58:04 +01:00
switch (propertyId) {
2014-05-26 18:18:01 +02:00
case P_ID::DIRECTION:
directionStyle = PropertyStyle::UNSTYLED;
2016-03-02 13:20:19 +01:00
setDirection(v.value<Direction>());
2012-08-10 17:01:35 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::NUMBER_TYPE:
numberStyle = PropertyStyle::UNSTYLED;
setNumberType(NumberType(v.toInt()));
2012-08-10 17:01:35 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::BRACKET_TYPE:
bracketStyle = PropertyStyle::UNSTYLED;
setBracketType(BracketType(v.toInt()));
2012-08-10 17:01:35 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::NORMAL_NOTES:
2012-08-10 17:01:35 +02:00
_ratio.setDenominator(v.toInt());
break;
2014-05-26 18:18:01 +02:00
case P_ID::ACTUAL_NOTES:
2012-08-10 17:01:35 +02:00
_ratio.setNumerator(v.toInt());
break;
2014-05-26 18:18:01 +02:00
case P_ID::P1:
2012-08-10 17:01:35 +02:00
_p1 = v.toPointF();
break;
2014-05-26 18:18:01 +02:00
case P_ID::P2:
2012-08-10 17:01:35 +02:00
_p2 = v.toPointF();
break;
default:
return DurationElement::setProperty(propertyId, v);
2012-05-26 14:26:10 +02:00
}
if (!_elements.empty()) {
_elements.front()->triggerLayout();
_elements.back()->triggerLayout();
}
2012-08-10 17:01:35 +02:00
return true;
2012-05-26 14:26:10 +02:00
}
2012-08-10 17:01:35 +02:00
//---------------------------------------------------------
// propertyDefault
//---------------------------------------------------------
QVariant Tuplet::propertyDefault(P_ID id) const
2012-05-26 14:26:10 +02:00
{
2012-08-10 17:01:35 +02:00
switch(id) {
2014-05-26 18:18:01 +02:00
case P_ID::DIRECTION:
return score()->style(StyleIdx::tupletDirection);
2014-05-26 18:18:01 +02:00
case P_ID::NUMBER_TYPE:
return score()->style(StyleIdx::tupletNumberType);
2014-05-26 18:18:01 +02:00
case P_ID::BRACKET_TYPE:
return score()->style(StyleIdx::tupletBracketType);
2014-05-26 18:18:01 +02:00
case P_ID::NORMAL_NOTES:
case P_ID::ACTUAL_NOTES:
return 0;
2014-05-26 18:18:01 +02:00
case P_ID::P1:
case P_ID::P2:
2012-08-10 17:01:35 +02:00
return QPointF();
default:
return DurationElement::propertyDefault(id);
2012-05-26 14:26:10 +02:00
}
}
//---------------------------------------------------------
// propertyStyle
//---------------------------------------------------------
PropertyStyle Tuplet::propertyStyle(P_ID id) const
{
switch (id) {
case P_ID::DIRECTION:
return directionStyle;
case P_ID::NUMBER_TYPE:
return numberStyle;
case P_ID::BRACKET_TYPE:
return bracketStyle;
case P_ID::NORMAL_NOTES:
case P_ID::ACTUAL_NOTES:
case P_ID::P1:
case P_ID::P2:
return PropertyStyle::NOSTYLE;
default:
return DurationElement::propertyStyle(id);
}
}
//---------------------------------------------------------
// getPropertyStyle
//---------------------------------------------------------
StyleIdx Tuplet::getPropertyStyle(P_ID id) const
{
switch (id) {
case P_ID::DIRECTION:
return StyleIdx::tupletDirection;
case P_ID::NUMBER_TYPE:
return StyleIdx::tupletNumberType;
case P_ID::BRACKET_TYPE:
return StyleIdx::tupletBracketType;
default:
break;
}
return StyleIdx::NOSTYLE;
}
//---------------------------------------------------------
// resetProperty
//---------------------------------------------------------
void Tuplet::resetProperty(P_ID id)
{
switch (id) {
case P_ID::DIRECTION:
setProperty(id, propertyDefault(id));
directionStyle = PropertyStyle::STYLED;
break;
case P_ID::NUMBER_TYPE:
setProperty(id, propertyDefault(id));
numberStyle = PropertyStyle::STYLED;
break;
case P_ID::BRACKET_TYPE:
setProperty(id, propertyDefault(id));
bracketStyle = PropertyStyle::STYLED;
break;
case P_ID::NORMAL_NOTES:
case P_ID::ACTUAL_NOTES:
break;
case P_ID::P1:
case P_ID::P2:
setProperty(id, propertyDefault(id));
break;
default:
return DurationElement::resetProperty(id);
}
}
//---------------------------------------------------------
// styleChanged
// reset all styled values to actual style
//---------------------------------------------------------
void Tuplet::styleChanged()
{
if (directionStyle == PropertyStyle::STYLED)
setDirection(score()->style(StyleIdx::tupletDirection).value<Direction>());
if (numberStyle == PropertyStyle::STYLED)
setNumberType(NumberType(score()->styleI(StyleIdx::tupletNumberType)));
if (bracketStyle == PropertyStyle::STYLED)
setBracketType(BracketType(score()->styleI(StyleIdx::tupletBracketType)));
if (!_elements.empty()) {
_elements.front()->triggerLayout();
_elements.back()->triggerLayout();
}
}
} // namespace Ms
2013-05-13 18:49:17 +02:00