MuseScore/libmscore/arpeggio.cpp

519 lines
16 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 "arpeggio.h"
#include "sym.h"
#include "chord.h"
#include "note.h"
#include "score.h"
#include "staff.h"
#include "part.h"
#include "segment.h"
#include "property.h"
2014-04-09 16:09:21 +02:00
#include "xml.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
//---------------------------------------------------------
// Arpeggio
//---------------------------------------------------------
Arpeggio::Arpeggio(Score* s)
: Element(s, ElementFlag::MOVABLE)
2012-05-26 14:26:10 +02:00
{
_arpeggioType = ArpeggioType::NORMAL;
2012-05-26 14:26:10 +02:00
setHeight(spatium() * 4); // for use in palettes
2013-11-19 12:12:07 +01:00
_span = 1;
2013-03-04 09:45:05 +01:00
_userLen1 = 0.0;
_userLen2 = 0.0;
2015-07-06 18:16:52 +02:00
_playArpeggio = true;
_stretch = 1.0;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// setHeight
//---------------------------------------------------------
void Arpeggio::setHeight(qreal h)
{
_height = h;
}
//---------------------------------------------------------
// write
//---------------------------------------------------------
2016-11-19 11:51:21 +01:00
void Arpeggio::write(XmlWriter& xml) const
2012-05-26 14:26:10 +02:00
{
2014-08-15 17:20:20 +02:00
if (!xml.canWrite(this))
return;
xml.stag(this);
2012-05-26 14:26:10 +02:00
Element::writeProperties(xml);
2019-05-31 11:47:18 +02:00
writeProperty(xml, Pid::ARPEGGIO_TYPE);
if (_userLen1 != 0.0)
xml.tag("userLen1", _userLen1 / spatium());
if (_userLen2 != 0.0)
xml.tag("userLen2", _userLen2 / spatium());
2012-05-26 14:26:10 +02:00
if (_span != 1)
xml.tag("span", _span);
2018-03-27 15:36:00 +02:00
writeProperty(xml, Pid::PLAY);
writeProperty(xml, Pid::TIME_STRETCH);
2012-05-26 14:26:10 +02:00
xml.etag();
}
//---------------------------------------------------------
// read
//---------------------------------------------------------
2013-01-11 18:10:18 +01:00
void Arpeggio::read(XmlReader& e)
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
if (tag == "subtype")
_arpeggioType = ArpeggioType(e.readInt());
2012-05-26 14:26:10 +02:00
else if (tag == "userLen1")
_userLen1 = e.readDouble() * spatium();
2012-05-26 14:26:10 +02:00
else if (tag == "userLen2")
_userLen2 = e.readDouble() * spatium();
2012-05-26 14:26:10 +02:00
else if (tag == "span")
2013-01-11 18:10:18 +01:00
_span = e.readInt();
2015-07-06 18:16:52 +02:00
else if (tag == "play")
_playArpeggio = e.readBool();
else if (tag == "timeStretch")
_stretch = e.readDouble();
2012-05-26 14:26:10 +02:00
else if (!Element::readProperties(e))
2013-01-11 18:10:18 +01:00
e.unknown();
2012-05-26 14:26:10 +02:00
}
}
2013-11-19 12:12:07 +01:00
//---------------------------------------------------------
// symbolLine
// construct a string of symbols aproximating width w
//---------------------------------------------------------
2013-12-02 14:10:47 +01:00
void Arpeggio::symbolLine(SymId end, SymId fill)
2013-11-19 12:12:07 +01:00
{
qreal y1 = -_userLen1;
qreal y2 = _height + _userLen2;
qreal w = y2 - y1;
qreal mag = magS();
ScoreFont* f = score()->scoreFont();
symbols.clear();
2015-05-08 08:57:24 +02:00
qreal w1 = f->advance(end, mag);
qreal w2 = f->advance(fill, mag);
2013-11-19 12:12:07 +01:00
int n = lrint((w - w1) / w2);
for (int i = 0; i < n; ++i)
2016-01-04 14:48:58 +01:00
symbols.push_back(fill);
2016-02-04 11:27:47 +01:00
symbols.push_back(end);
2013-11-19 12:12:07 +01:00
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// layout
//---------------------------------------------------------
void Arpeggio::layout()
{
2013-11-19 12:12:07 +01:00
qreal y1 = -_userLen1;
qreal y2 = _height + _userLen2;
_hidden = false;
2018-03-27 15:36:00 +02:00
if (score()->styleB(Sid::ArpeggioHiddenInStdIfTab)) {
if (staff() && staff()->isPitchedStaff(tick())) {
for (Staff* s : staff()->staffList()) {
if (s->score() == score() && s->isTabStaff(tick())) {
_hidden = true;
setbbox(QRect());
return;
}
}
}
}
if (staff())
2016-12-23 12:05:18 +01:00
setMag(staff()->mag(tick()));
switch (arpeggioType()) {
2013-12-02 14:10:47 +01:00
case ArpeggioType::NORMAL: {
2013-11-19 12:12:07 +01:00
symbolLine(SymId::wiggleArpeggiatoUp, SymId::wiggleArpeggiatoUp);
2013-12-02 14:10:47 +01:00
// string is rotated -90 degrees
QRectF r(symBbox(symbols));
setbbox(QRectF(0.0, -r.x() + y1, r.height(), r.width()));
}
2013-11-19 12:12:07 +01:00
break;
2013-12-02 14:10:47 +01:00
case ArpeggioType::UP: {
symbolLine(SymId::wiggleArpeggiatoUpArrow, SymId::wiggleArpeggiatoUp);
// string is rotated -90 degrees
QRectF r(symBbox(symbols));
setbbox(QRectF(0.0, -r.x() + y1, r.height(), r.width()));
}
2013-11-19 12:12:07 +01:00
break;
2013-12-02 14:10:47 +01:00
case ArpeggioType::DOWN: {
symbolLine(SymId::wiggleArpeggiatoDownArrow, SymId::wiggleArpeggiatoDown);
// string is rotated +90 degrees
QRectF r(symBbox(symbols));
setbbox(QRectF(0.0, r.x() + y1, r.height(), r.width()));
}
2013-11-19 12:12:07 +01:00
break;
2013-12-02 14:10:47 +01:00
case ArpeggioType::UP_STRAIGHT: {
2013-11-19 12:12:07 +01:00
qreal _spatium = spatium();
qreal x1 = _spatium * .5;
qreal w = symBbox(SymId::arrowheadBlackUp).width();
2018-03-27 15:36:00 +02:00
qreal lw = score()->styleS(Sid::ArpeggioLineWidth).val() * _spatium;
2013-11-19 12:12:07 +01:00
setbbox(QRectF(x1 - w * .5, y1, w, y2 - y1 + lw * .5));
}
break;
2013-12-02 14:10:47 +01:00
case ArpeggioType::DOWN_STRAIGHT: {
qreal _spatium = spatium();
2013-11-19 12:12:07 +01:00
qreal x1 = _spatium * .5;
qreal w = symBbox(SymId::arrowheadBlackDown).width();
2018-03-27 15:36:00 +02:00
qreal lw = score()->styleS(Sid::ArpeggioLineWidth).val() * _spatium;
2013-11-19 12:12:07 +01:00
setbbox(QRectF(x1 - w * .5, y1 - lw * .5, w, y2 - y1 + lw * .5));
}
break;
2013-12-02 14:10:47 +01:00
case ArpeggioType::BRACKET: {
2013-11-19 12:12:07 +01:00
qreal _spatium = spatium();
2018-03-27 15:36:00 +02:00
qreal lw = score()->styleS(Sid::ArpeggioLineWidth).val() * _spatium * .5;
qreal w = score()->styleS(Sid::ArpeggioHookLen).val() * _spatium;
2013-11-19 12:12:07 +01:00
setbbox(QRectF(0.0, y1, w, y2-y1).adjusted(-lw, -lw, lw, lw));
break;
2012-05-26 14:26:10 +02:00
}
}
}
//---------------------------------------------------------
// draw
//---------------------------------------------------------
void Arpeggio::draw(QPainter* p) const
{
if (_hidden)
return;
2012-05-26 14:26:10 +02:00
qreal _spatium = spatium();
2013-11-19 12:12:07 +01:00
qreal y1 = -_userLen1;
qreal y2 = _height + _userLen2;
2013-02-27 11:23:30 +01:00
2013-11-19 12:12:07 +01:00
p->setPen(QPen(curColor(),
2018-03-27 15:36:00 +02:00
score()->styleS(Sid::ArpeggioLineWidth).val() * _spatium,
2013-11-19 12:12:07 +01:00
Qt::SolidLine, Qt::RoundCap));
2015-05-08 08:57:24 +02:00
p->save();
2013-11-19 12:12:07 +01:00
switch (arpeggioType()) {
case ArpeggioType::NORMAL:
2013-12-02 14:10:47 +01:00
case ArpeggioType::UP:
2013-11-19 12:12:07 +01:00
{
2013-12-02 14:10:47 +01:00
QRectF r(symBbox(symbols));
2015-05-08 08:57:24 +02:00
qreal scale = p->worldTransform().m11();
2013-11-19 12:12:07 +01:00
p->rotate(-90.0);
2015-05-08 08:57:24 +02:00
score()->scoreFont()->draw(symbols, p, magS(), QPointF(-r.right() - y1, -r.bottom() + r.height()), scale);
2013-12-02 14:10:47 +01:00
}
break;
case ArpeggioType::DOWN:
{
QRectF r(symBbox(symbols));
2015-05-08 08:57:24 +02:00
qreal scale = p->worldTransform().m11();
2013-11-19 12:12:07 +01:00
p->rotate(90.0);
2015-05-08 08:57:24 +02:00
score()->scoreFont()->draw(symbols, p, magS(), QPointF(-r.left() + y1, -r.top() - r.height()), scale);
2013-11-19 12:12:07 +01:00
}
2012-05-26 14:26:10 +02:00
break;
2013-02-27 11:23:30 +01:00
case ArpeggioType::UP_STRAIGHT:
2013-11-19 12:12:07 +01:00
{
QRectF r(symBbox(SymId::arrowheadBlackUp));
qreal x1 = _spatium * .5;
drawSymbol(SymId::arrowheadBlackUp, p, QPointF(x1 - r.width() * .5, y1 - r.top()));
y1 -= r.top() * .5;
p->drawLine(QLineF(x1, y1, x1, y2));
2013-11-19 12:12:07 +01:00
}
break;
2013-02-27 11:23:30 +01:00
case ArpeggioType::DOWN_STRAIGHT:
2013-11-19 12:12:07 +01:00
{
QRectF r(symBbox(SymId::arrowheadBlackDown));
qreal x1 = _spatium * .5;
drawSymbol(SymId::arrowheadBlackDown, p, QPointF(x1 - r.width() * .5, y2 - r.bottom()));
y2 += r.top() * .5;
p->drawLine(QLineF(x1, y1, x1, y2));
2013-11-19 12:12:07 +01:00
}
break;
2013-02-27 11:23:30 +01:00
case ArpeggioType::BRACKET:
2012-05-26 14:26:10 +02:00
{
2018-03-27 15:36:00 +02:00
qreal w = score()->styleS(Sid::ArpeggioHookLen).val() * _spatium;
2012-05-26 14:26:10 +02:00
p->drawLine(QLineF(0.0, y1, 0.0, y2));
p->drawLine(QLineF(0.0, y1, w, y1));
p->drawLine(QLineF(0.0, y2, w, y2));
}
break;
}
2015-05-08 08:57:24 +02:00
p->restore();
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// gripsPositions
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
std::vector<QPointF> Arpeggio::gripsPositions(const EditData&) const
2012-05-26 14:26:10 +02:00
{
const QPointF pp(pagePos());
QPointF p1(0.0, -_userLen1);
QPointF p2(0.0, _height + _userLen2);
return { p1 + pp, p2 + pp };
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// editDrag
//---------------------------------------------------------
2017-03-31 13:03:15 +02:00
void Arpeggio::editDrag(EditData& ed)
2012-05-26 14:26:10 +02:00
{
qreal d = ed.delta.y();
if (ed.curGrip == Grip::START)
2012-05-26 14:26:10 +02:00
_userLen1 -= d;
else if (ed.curGrip == Grip::END)
2012-05-26 14:26:10 +02:00
_userLen2 += d;
2013-11-19 12:12:07 +01:00
layout();
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// dragAnchorLines
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
QVector<QLineF> Arpeggio::dragAnchorLines() const
2012-05-26 14:26:10 +02:00
{
QVector<QLineF> result;
2012-05-26 14:26:10 +02:00
Chord* c = chord();
if (c)
result << QLineF(pagePos(), c->upNote()->pagePos());
return QVector<QLineF>();
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// gripAnchorLines
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
QVector<QLineF> Arpeggio::gripAnchorLines(Grip grip) const
2012-05-26 14:26:10 +02:00
{
QVector<QLineF> result;
Chord* _chord = chord();
if (!_chord)
return result;
QPointF gripPosition = gripsPositions()[static_cast<int>(grip)];
if (grip == Grip::START)
result << QLineF(_chord->upNote()->pagePos(), gripPosition);
else if (grip == Grip::END) {
Note* downNote = _chord->downNote();
2012-05-26 14:26:10 +02:00
int btrack = track() + (_span - 1) * VOICES;
Element* e = _chord->segment()->element(btrack);
2017-12-20 16:49:30 +01:00
if (e && e->isChord())
downNote = toChord(e)->downNote();
result << QLineF(downNote->pagePos(), gripPosition);
2012-05-26 14:26:10 +02:00
}
return result;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// startEdit
//---------------------------------------------------------
2017-03-31 13:03:15 +02:00
void Arpeggio::startEdit(EditData& ed)
{
Element::startEdit(ed);
ElementEditData* eed = ed.getData(this);
eed->pushProperty(Pid::ARP_USER_LEN1);
eed->pushProperty(Pid::ARP_USER_LEN2);
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// edit
//---------------------------------------------------------
2017-03-31 13:03:15 +02:00
bool Arpeggio::edit(EditData& ed)
2012-05-26 14:26:10 +02:00
{
2017-03-31 13:03:15 +02:00
if (ed.curGrip != Grip::END || !(ed.modifiers & Qt::ShiftModifier))
2012-05-26 14:26:10 +02:00
return false;
2017-03-31 13:03:15 +02:00
if (ed.key == Qt::Key_Down) {
2012-05-26 14:26:10 +02:00
Staff* s = staff();
Part* part = s->part();
int n = part->nstaves();
int ridx = part->staves()->indexOf(s);
if (ridx >= 0) {
if (_span + ridx < n)
++_span;
}
}
2017-03-31 13:03:15 +02:00
else if (ed.key == Qt::Key_Up) {
2012-05-26 14:26:10 +02:00
if (_span > 1)
--_span;
}
else
return false;
layout();
Chord* c = chord();
rxpos() = -(width() + spatium() * .5);
c->layoutArpeggio2();
return true;
}
//---------------------------------------------------------
// spatiumChanged
//---------------------------------------------------------
void Arpeggio::spatiumChanged(qreal oldValue, qreal newValue)
{
_userLen1 *= (newValue / oldValue);
_userLen2 *= (newValue / oldValue);
}
//---------------------------------------------------------
// acceptDrop
//---------------------------------------------------------
2017-03-31 13:03:15 +02:00
bool Arpeggio::acceptDrop(EditData& data) const
{
return data.dropElement->type() == ElementType::ARPEGGIO;
}
//---------------------------------------------------------
// drop
//---------------------------------------------------------
2017-03-31 13:03:15 +02:00
Element* Arpeggio::drop(EditData& data)
{
Element* e = data.dropElement;
switch(e->type()) {
2017-01-18 14:16:33 +01:00
case ElementType::ARPEGGIO:
{
2017-12-20 16:49:30 +01:00
Arpeggio* a = toArpeggio(e);
if (parent())
score()->undoRemoveElement(this);
a->setTrack(track());
a->setParent(parent());
score()->undoAddElement(a);
}
return e;
default:
delete e;
break;
}
return 0;
}
2019-05-04 08:24:02 +02:00
//---------------------------------------------------------
// reset
//---------------------------------------------------------
void Arpeggio::reset()
{
undoChangeProperty(Pid::ARP_USER_LEN1, 0.0);
undoChangeProperty(Pid::ARP_USER_LEN2, 0.0);
Element::reset();
}
//---------------------------------------------------------
// getProperty
//---------------------------------------------------------
2018-03-27 15:36:00 +02:00
QVariant Arpeggio::getProperty(Pid propertyId) const
{
switch(propertyId) {
2019-05-31 11:47:18 +02:00
case Pid::ARPEGGIO_TYPE:
return int(_arpeggioType);
case Pid::TIME_STRETCH:
return Stretch();
2018-03-27 15:36:00 +02:00
case Pid::ARP_USER_LEN1:
return userLen1();
2018-03-27 15:36:00 +02:00
case Pid::ARP_USER_LEN2:
return userLen2();
2018-03-27 15:36:00 +02:00
case Pid::PLAY:
2015-07-06 18:16:52 +02:00
return _playArpeggio;
default:
break;
}
return Element::getProperty(propertyId);
}
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
2018-03-27 15:36:00 +02:00
bool Arpeggio::setProperty(Pid propertyId, const QVariant& val)
{
switch(propertyId) {
2019-05-31 11:47:18 +02:00
case Pid::ARPEGGIO_TYPE:
setArpeggioType(ArpeggioType(val.toInt()));
break;
case Pid::TIME_STRETCH:
setStretch(val.toDouble());
2018-10-25 15:23:13 +02:00
break;
2018-03-27 15:36:00 +02:00
case Pid::ARP_USER_LEN1:
setUserLen1(val.toDouble());
break;
2018-03-27 15:36:00 +02:00
case Pid::ARP_USER_LEN2:
setUserLen2(val.toDouble());
break;
2018-03-27 15:36:00 +02:00
case Pid::PLAY:
2015-07-06 18:16:52 +02:00
setPlayArpeggio(val.toBool());
break;
default:
if (!Element::setProperty(propertyId, val))
return false;
break;
}
triggerLayout();
return true;
}
2015-07-06 18:16:52 +02:00
//---------------------------------------------------------
// propertyDefault
//---------------------------------------------------------
2018-03-27 15:36:00 +02:00
QVariant Arpeggio::propertyDefault(Pid propertyId) const
2015-07-06 18:16:52 +02:00
{
switch(propertyId) {
2018-03-27 15:36:00 +02:00
case Pid::ARP_USER_LEN1:
2015-07-06 18:16:52 +02:00
return 0.0;
2018-03-27 15:36:00 +02:00
case Pid::ARP_USER_LEN2:
2015-07-06 18:16:52 +02:00
return 0.0;
case Pid::TIME_STRETCH:
return 1.0;
2018-03-27 15:36:00 +02:00
case Pid::PLAY:
2015-07-06 18:16:52 +02:00
return true;
default:
break;
}
return Element::propertyDefault(propertyId);
}
2019-05-31 11:47:18 +02:00
//---------------------------------------------------------
// propertyId
//---------------------------------------------------------
Pid Arpeggio::propertyId(const QStringRef& name) const
{
if (name == "subtype")
return Pid::ARPEGGIO_TYPE;
return Element::propertyId(name);
}
2013-05-13 18:49:17 +02:00
}