MuseScore/libmscore/rest.cpp

629 lines
22 KiB
C++
Raw Normal View History

2012-05-26 14:26:10 +02:00
//=============================================================================
// MuseScore
// Music Composition & Notation
//
2012-08-08 20:46:29 +02:00
// Copyright (C) 2002-2012 Werner Schweer
2012-05-26 14:26:10 +02:00
//
// 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 "rest.h"
#include "score.h"
#include "xml.h"
#include "style.h"
#include "utils.h"
#include "tuplet.h"
#include "sym.h"
#include "stafftext.h"
#include "articulation.h"
#include "chord.h"
#include "note.h"
#include "measure.h"
#include "undo.h"
#include "staff.h"
#include "harmony.h"
#include "lyrics.h"
#include "segment.h"
#include "stafftype.h"
#include "icon.h"
2013-05-13 18:49:17 +02:00
namespace Ms {
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// Rest
//--------------------------------------------------------
Rest::Rest(Score* s)
: ChordRest(s)
{
setFlags(ElementFlag::MOVABLE | ElementFlag::SELECTABLE | ElementFlag::ON_STAFF);
_beamMode = Beam::Mode::NONE;
2012-05-26 14:26:10 +02:00
dotline = -1;
2013-11-06 15:58:05 +01:00
_sym = SymId::restQuarter;
2012-05-26 14:26:10 +02:00
}
Rest::Rest(Score* s, const TDuration& d)
: ChordRest(s)
{
setFlags(ElementFlag::MOVABLE | ElementFlag::SELECTABLE | ElementFlag::ON_STAFF);
_beamMode = Beam::Mode::NONE;
2012-05-26 14:26:10 +02:00
dotline = -1;
2013-11-06 15:58:05 +01:00
_sym = SymId::restQuarter;
2012-05-26 14:26:10 +02:00
setDurationType(d);
if (d.fraction().isValid())
setDuration(d.fraction());
}
2014-06-27 13:41:49 +02:00
Rest::Rest(const Rest& r, bool link)
: ChordRest(r, link)
{
if (link)
linkTo((Rest*)&r); // HACK!
_sym = r._sym;
dotline = r.dotline;
_mmWidth = r._mmWidth;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// Rest::draw
//---------------------------------------------------------
void Rest::draw(QPainter* painter) const
{
2014-08-02 16:50:10 +02:00
if (
(staff() && staff()->isTabStaff()
// in tab staff, do not draw rests is rests are off OR if dur. symbols are on
&& (!staff()->staffType()->showRests() || staff()->staffType()->genDurations())
&& !measure()->isMMRest()) // show multi measure rest always
|| generated()
)
2012-05-26 14:26:10 +02:00
return;
qreal _spatium = spatium();
painter->setPen(curColor());
if (parent() && measure() && measure()->isMMRest()) {
//only on voice 1
2013-10-28 15:52:32 +01:00
if ((track() % VOICES) != 0)
return;
2012-05-26 14:26:10 +02:00
Measure* m = measure();
int n = m->mmRestCount();
2012-05-26 14:26:10 +02:00
qreal pw = _spatium * .7;
QPen pen(painter->pen());
pen.setWidthF(pw);
painter->setPen(pen);
qreal w = _mmWidth;
qreal y = 0.0;
2012-05-26 14:26:10 +02:00
qreal x1 = 0.0;
qreal x2 = w;
pw *= .5;
painter->drawLine(QLineF(x1 + pw, y, x2 - pw, y));
// draw vertical lines:
pen.setWidthF(_spatium * .2);
painter->setPen(pen);
painter->drawLine(QLineF(x1, y-_spatium, x1, y+_spatium));
painter->drawLine(QLineF(x2, y-_spatium, x2, y+_spatium));
2013-11-07 16:05:00 +01:00
painter->setFont(score()->scoreFont()->font());
QFontMetricsF fm(score()->scoreFont()->font());
2013-11-19 19:23:15 +01:00
QString s = toTimeSigString(QString("%1").arg(n));
y = -_spatium * 1.5 - staff()->height() *.5;
qreal x = center(x1, x2);
x -= symBbox(s).width() * .5;
drawSymbols(s, painter, QPointF(x, y));
2012-05-26 14:26:10 +02:00
}
else {
2013-11-06 15:58:05 +01:00
drawSymbol(_sym, painter);
2012-05-26 14:26:10 +02:00
int dots = durationType().dots();
if (dots) {
qreal y = dotline * _spatium * .5;
qreal dnd = point(score()->styleS(StyleIdx::dotNoteDistance)) * mag();
qreal ddd = point(score()->styleS(StyleIdx::dotDotDistance)) * mag();
2012-05-26 14:26:10 +02:00
for (int i = 1; i <= dots; ++i) {
qreal x = symWidth(_sym) + dnd + ddd * (i - 1);
2013-11-06 15:58:05 +01:00
drawSymbol(SymId::augmentationDot, painter, QPointF(x, y));
2012-05-26 14:26:10 +02:00
}
}
}
}
//---------------------------------------------------------
// setUserOff, overriden from Element
2012-05-26 14:26:10 +02:00
// - raster vertical position in spatium units
// - half rests and whole rests outside the staff are
// replaced by special symbols with ledger lines
//---------------------------------------------------------
void Rest::setUserOff(const QPointF& o)
2012-05-26 14:26:10 +02:00
{
qreal _spatium = spatium();
int line = lrint(o.y()/_spatium);
if (_sym == SymId::restWhole && (line <= -2 || line >= 3))
_sym = SymId::restWholeLegerLine;
else if (_sym == SymId::restWholeLegerLine && (line > -2 && line < 4))
_sym = SymId::restWhole;
else if (_sym == SymId::restHalf && (line <= -3 || line >= 3))
_sym = SymId::restHalfLegerLine;
else if (_sym == SymId::restHalfLegerLine && (line > -3 && line < 3))
_sym = SymId::restHalf;
Element::setUserOff(QPointF(o.x(), qreal(line) * _spatium));
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// drag
//---------------------------------------------------------
2013-10-22 12:05:31 +02:00
QRectF Rest::drag(EditData* data)
2012-05-26 14:26:10 +02:00
{
2013-10-24 12:09:00 +02:00
QPointF s(data->delta);
2012-05-26 14:26:10 +02:00
QRectF r(abbox());
// Limit horizontal drag range
2013-07-31 11:05:48 +02:00
static const qreal xDragRange = spatium() * 5;
if (fabs(s.x()) > xDragRange)
s.rx() = xDragRange * (s.x() < 0 ? -1.0 : 1.0);
setUserOff(QPointF(s.x(), s.y()));
2012-05-26 14:26:10 +02:00
layout();
2013-07-31 11:05:48 +02:00
score()->rebuildBspTree();
2012-05-26 14:26:10 +02:00
return abbox() | r;
}
//---------------------------------------------------------
// acceptDrop
//---------------------------------------------------------
2014-08-13 21:01:21 +02:00
bool Rest::acceptDrop(const DropData& data) const
2012-05-26 14:26:10 +02:00
{
2014-08-13 21:01:21 +02:00
Element* e = data.element;
Element::Type type = e->type();
2012-05-26 14:26:10 +02:00
if (
(type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::SBEAM)
|| (type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::MBEAM)
|| (type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::NBEAM)
|| (type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::BEAM32)
|| (type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::BEAM64)
|| (type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::AUTOBEAM)
|| (type == Element::Type::ARTICULATION && static_cast<Articulation*>(e)->isFermata())
|| (type == Element::Type::CLEF)
|| (type == Element::Type::STAFF_TEXT)
|| (type == Element::Type::BAR_LINE)
|| (type == Element::Type::BREATH)
|| (type == Element::Type::CHORD)
|| (type == Element::Type::STAFF_STATE)
|| (type == Element::Type::INSTRUMENT_CHANGE)
|| (type == Element::Type::DYNAMIC)
|| (type == Element::Type::HARMONY)
|| (type == Element::Type::TEMPO_TEXT)
|| (type == Element::Type::STAFF_TEXT)
|| (type == Element::Type::REHEARSAL_MARK)
|| (type == Element::Type::FRET_DIAGRAM)
|| (type == Element::Type::SYMBOL)
2012-05-26 14:26:10 +02:00
) {
return true;
}
if(type == Element::Type::REPEAT_MEASURE && durationType().type() == TDuration::DurationType::V_MEASURE)
return true;
2012-05-26 14:26:10 +02:00
return false;
}
//---------------------------------------------------------
// drop
//---------------------------------------------------------
Element* Rest::drop(const DropData& data)
{
Element* e = data.element;
switch (e->type()) {
case Element::Type::ARTICULATION:
{
Articulation* a = static_cast<Articulation*>(e);
if (!a->isFermata()
|| !score()->addArticulation(this, a)) {
2012-05-26 14:26:10 +02:00
delete e;
e = 0;
}
}
2012-05-26 14:26:10 +02:00
return e;
case Element::Type::CHORD:
2012-05-26 14:26:10 +02:00
{
Chord* c = static_cast<Chord*>(e);
Note* n = c->upNote();
MScore::Direction dir = c->stemDirection();
// score()->select(0, SelectType::SINGLE, 0);
2012-05-26 14:26:10 +02:00
NoteVal nval;
nval.pitch = n->pitch();
nval.headGroup = n->headGroup();
Fraction d = score()->inputState().duration().fraction();
if (!d.isZero()) {
Segment* seg = score()->setNoteRest(segment(), track(), nval, d, dir);
if (seg) {
ChordRest* cr = static_cast<ChordRest*>(seg->element(track()));
if (cr)
score()->nextInputPos(cr, true);
}
}
delete e;
}
break;
case Element::Type::REPEAT_MEASURE:
delete e;
if (durationType().type() == TDuration::DurationType::V_MEASURE) {
measure()->cmdInsertRepeatMeasure(staffIdx());
}
break;
2012-05-26 14:26:10 +02:00
default:
return ChordRest::drop(data);
}
return 0;
}
//---------------------------------------------------------
// getSymbol
//---------------------------------------------------------
2013-11-06 15:58:05 +01:00
SymId Rest::getSymbol(TDuration::DurationType type, int line, int lines, int* yoffset)
2012-05-26 14:26:10 +02:00
{
*yoffset = 2;
2012-05-26 14:26:10 +02:00
switch(type) {
case TDuration::DurationType::V_LONG:
2013-11-06 15:58:05 +01:00
return SymId::restLonga;
case TDuration::DurationType::V_BREVE:
2013-11-06 15:58:05 +01:00
return SymId::restDoubleWhole;
case TDuration::DurationType::V_MEASURE:
2012-05-26 14:26:10 +02:00
if (duration() >= Fraction(2, 1))
2013-11-06 15:58:05 +01:00
return SymId::restDoubleWhole;
2012-05-26 14:26:10 +02:00
// fall trough
case TDuration::DurationType::V_WHOLE:
2012-05-26 14:26:10 +02:00
*yoffset = 1;
return (line <= -2 || line >= (lines - 1)) ? SymId::restWholeLegerLine : SymId::restWhole;
case TDuration::DurationType::V_HALF:
return (line <= -3 || line >= (lines - 2)) ? SymId::restHalfLegerLine : SymId::restHalf;
case TDuration::DurationType::V_QUARTER:
2013-11-06 15:58:05 +01:00
return SymId::restQuarter;
case TDuration::DurationType::V_EIGHTH:
2013-11-06 15:58:05 +01:00
return SymId::rest8th;
case TDuration::DurationType::V_16TH:
2013-11-06 15:58:05 +01:00
return SymId::rest16th;
case TDuration::DurationType::V_32ND:
2013-11-06 15:58:05 +01:00
return SymId::rest32nd;
case TDuration::DurationType::V_64TH:
2013-11-06 15:58:05 +01:00
return SymId::rest64th;
case TDuration::DurationType::V_128TH:
2013-11-06 15:58:05 +01:00
return SymId::rest128th;
case TDuration::DurationType::V_256TH:
2013-11-06 15:58:05 +01:00
return SymId::rest256th;
case TDuration::DurationType::V_512TH:
2013-11-06 15:58:05 +01:00
return SymId::rest512th;
case TDuration::DurationType::V_1024TH:
2013-11-06 15:58:05 +01:00
return SymId::rest1024th;
2012-05-26 14:26:10 +02:00
default:
qDebug("unknown rest type %hhd", type);
2013-11-06 15:58:05 +01:00
return SymId::restQuarter;
2012-05-26 14:26:10 +02:00
}
}
//---------------------------------------------------------
// layout
//---------------------------------------------------------
void Rest::layout()
{
_space.setLw(0.0);
if (parent() && measure() && measure()->isMMRest()) {
2014-05-26 15:31:36 +02:00
_space.setRw(point(score()->styleS(StyleIdx::minMMRestWidth)));
2014-04-25 13:13:08 +02:00
static const qreal verticalLineWidth = .2;
qreal _spatium = spatium();
qreal h = _spatium * (2 + verticalLineWidth);
qreal w = _mmWidth + _spatium * verticalLineWidth*.5;
bbox().setRect(-_spatium * verticalLineWidth*.5, -h * .5, w, h);
// text
qreal y = -_spatium * 2.5 - staff()->height() *.5;
addbbox(QRectF(0, y, w, _spatium * 2)); // approximation
return;
}
2012-09-24 15:15:37 +02:00
rxpos() = 0.0;
if (staff() && staff()->isTabStaff()) {
2014-04-28 18:38:50 +02:00
StaffType* tab = staff()->staffType();
// if rests are shown and note values are shown as duration symbols
2014-04-28 18:38:50 +02:00
if (tab->showRests() && tab->genDurations()) {
TDuration::DurationType type = durationType().type();
int dots = durationType().dots();
// if rest is whole measure, convert into actual type and dot values
if (type == TDuration::DurationType::V_MEASURE) {
int ticks = measure()->ticks();
TDuration dur = TDuration(Fraction::fromTicks(ticks)).type();
type = dur.type();
dots = dur.dots();
}
// symbol needed; if not exist, create, if exists, update duration
if (!_tabDur)
_tabDur = new TabDurationSymbol(score(), tab, type, dots);
else
_tabDur->setDuration(type, dots, tab);
_tabDur->setParent(this);
// needed? _tabDur->setTrack(track());
_tabDur->layout();
setbbox(_tabDur->bbox());
setPos(0.0, 0.0); // no rest is drawn: reset any position might be set for it
_space.setLw(0.0);
_space.setRw(width());
return;
}
// if no rests or no duration symbols, delete any dur. symbol and chain into standard staff mngmt
// this is to ensure horiz space is reserved for rest, even if they are not diplayed
// Rest::draw() will skip their drawing, if not needed
if(_tabDur) {
delete _tabDur;
_tabDur = 0;
}
}
2012-05-26 14:26:10 +02:00
switch(durationType().type()) {
case TDuration::DurationType::V_64TH:
case TDuration::DurationType::V_32ND:
2012-05-26 14:26:10 +02:00
dotline = -3;
break;
case TDuration::DurationType::V_1024TH:
case TDuration::DurationType::V_512TH:
case TDuration::DurationType::V_256TH:
case TDuration::DurationType::V_128TH:
2012-05-26 14:26:10 +02:00
dotline = -5;
break;
default:
dotline = -1;
break;
}
qreal _spatium = spatium();
2012-09-24 15:15:37 +02:00
int stepOffset = 0;
2012-08-08 20:46:29 +02:00
if (staff())
2012-05-26 14:26:10 +02:00
stepOffset = staff()->staffType()->stepOffset();
2012-09-24 15:15:37 +02:00
int line = lrint(userOff().y() / _spatium); // + ((staff()->lines()-1) * 2);
qreal lineDist = staff() ? staff()->staffType()->lineDistance().val() : 1.0;
2012-05-26 14:26:10 +02:00
int lines = staff() ? staff()->lines() : 5;
int lineOffset = computeLineOffset();
int yo;
_sym = getSymbol(durationType().type(), line + lineOffset/2, lines, &yo);
layoutArticulations();
rypos() = (qreal(yo) + qreal(lineOffset + stepOffset) * .5) * lineDist * _spatium;
Spatium rs;
if (dots()) {
2014-05-26 15:31:36 +02:00
rs = Spatium(score()->styleS(StyleIdx::dotNoteDistance)
+ dots() * score()->styleS(StyleIdx::dotDotDistance));
}
if (dots()) {
2014-05-26 15:31:36 +02:00
rs = Spatium(score()->styleS(StyleIdx::dotNoteDistance)
+ dots() * score()->styleS(StyleIdx::dotDotDistance));
}
2013-11-11 15:11:28 +01:00
setbbox(symBbox(_sym));
qreal symOffset = bbox().x();
if (symOffset < 0.0)
_space.setLw(-symOffset);
_space.setRw(width() + point(rs) + symOffset);
}
//---------------------------------------------------------
// centerX
//---------------------------------------------------------
int Rest::computeLineOffset()
{
int lineOffset = 0;
int lines = staff() ? staff()->lines() : 5;
2012-05-26 14:26:10 +02:00
if (segment() && measure() && measure()->mstaff(staffIdx())->hasVoices) {
// move rests in a multi voice context
bool up = (voice() == 0) || (voice() == 2); // TODO: use style values
switch(durationType().type()) {
case TDuration::DurationType::V_LONG:
2012-05-26 14:26:10 +02:00
lineOffset = up ? -3 : 5;
break;
case TDuration::DurationType::V_BREVE:
2012-05-26 14:26:10 +02:00
lineOffset = up ? -3 : 5;
break;
case TDuration::DurationType::V_MEASURE:
2012-05-26 14:26:10 +02:00
if (duration() >= Fraction(2, 1)) // breve symbol
lineOffset = up ? -3 : 5;
// fall through
case TDuration::DurationType::V_WHOLE:
2012-05-26 14:26:10 +02:00
lineOffset = up ? -4 : 6;
break;
case TDuration::DurationType::V_HALF:
2012-05-26 14:26:10 +02:00
lineOffset = up ? -4 : 4;
break;
case TDuration::DurationType::V_QUARTER:
2012-05-26 14:26:10 +02:00
lineOffset = up ? -4 : 4;
break;
case TDuration::DurationType::V_EIGHTH:
2012-05-26 14:26:10 +02:00
lineOffset = up ? -4 : 4;
break;
case TDuration::DurationType::V_16TH:
2012-05-26 14:26:10 +02:00
lineOffset = up ? -6 : 4;
break;
case TDuration::DurationType::V_32ND:
2012-05-26 14:26:10 +02:00
lineOffset = up ? -6 : 6;
break;
case TDuration::DurationType::V_64TH:
2012-05-26 14:26:10 +02:00
lineOffset = up ? -8 : 6;
break;
case TDuration::DurationType::V_128TH:
2012-05-26 14:26:10 +02:00
lineOffset = up ? -8 : 8;
break;
case TDuration::DurationType::V_1024TH:
case TDuration::DurationType::V_512TH:
case TDuration::DurationType::V_256TH:
2012-05-26 14:26:10 +02:00
lineOffset = up ? -10 : 6;
break;
default:
break;
}
}
else {
switch(durationType().type()) {
case TDuration::DurationType::V_LONG:
case TDuration::DurationType::V_BREVE:
case TDuration::DurationType::V_MEASURE:
case TDuration::DurationType::V_WHOLE:
2012-05-26 14:26:10 +02:00
if (lines == 1)
lineOffset = -2;
break;
case TDuration::DurationType::V_HALF:
case TDuration::DurationType::V_QUARTER:
case TDuration::DurationType::V_EIGHTH:
case TDuration::DurationType::V_16TH:
case TDuration::DurationType::V_32ND:
case TDuration::DurationType::V_64TH:
case TDuration::DurationType::V_128TH:
case TDuration::DurationType::V_256TH:
case TDuration::DurationType::V_512TH:
case TDuration::DurationType::V_1024TH:
2012-05-26 14:26:10 +02:00
if (lines == 1)
lineOffset = -4;
break;
default:
break;
}
}
return lineOffset;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// centerX
//---------------------------------------------------------
qreal Rest::centerX() const
{
2013-11-11 15:11:28 +01:00
return symWidth(_sym) * .5;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// upPos
//---------------------------------------------------------
qreal Rest::upPos() const
{
2013-11-11 15:11:28 +01:00
return symBbox(_sym).y();
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// downPos
//---------------------------------------------------------
qreal Rest::downPos() const
{
2013-11-11 15:11:28 +01:00
return symBbox(_sym).y() + symHeight(_sym);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// scanElements
//---------------------------------------------------------
void Rest::scanElements(void* data, void (*func)(void*, Element*), bool all)
{
func(data, this);
ChordRest::scanElements(data, func, all);
}
//---------------------------------------------------------
// setMMWidth
//---------------------------------------------------------
void Rest::setMMWidth(qreal val)
{
_mmWidth = val;
2014-04-25 13:13:08 +02:00
layout();
2012-05-26 14:26:10 +02:00
}
2012-08-23 17:40:04 +02:00
//---------------------------------------------------------
2012-11-19 10:08:15 +01:00
// reset
2012-08-23 17:40:04 +02:00
//---------------------------------------------------------
2012-11-19 10:08:15 +01:00
void Rest::reset()
2012-08-23 17:40:04 +02:00
{
score()->undoChangeProperty(this, P_ID::BEAM_MODE, int(Beam::Mode::NONE));
2012-11-19 10:08:15 +01:00
ChordRest::reset();
2012-08-23 17:40:04 +02:00
}
2013-05-28 15:42:02 +02:00
//---------------------------------------------------------
// mag
//---------------------------------------------------------
qreal Rest::mag() const
{
qreal m = staff()->mag();
if (small())
2014-05-26 15:31:36 +02:00
m *= score()->styleD(StyleIdx::smallNoteMag);
2013-05-28 15:42:02 +02:00
return m;
}
2014-05-16 13:52:22 +02:00
//---------------------------------------------------------
// upLine
//---------------------------------------------------------
int Rest::upLine() const
{
2014-05-17 15:48:18 +02:00
return lrint((pos().y() + bbox().top() + spatium()) * 2 / spatium());
2014-05-16 13:52:22 +02:00
}
//---------------------------------------------------------
// downLine
//---------------------------------------------------------
int Rest::downLine() const
{
2014-05-17 15:48:18 +02:00
return lrint((pos().y() + bbox().top() + spatium()) * 2 / spatium());
2014-05-16 13:52:22 +02:00
}
//---------------------------------------------------------
// stemPos
// point to connect stem
//---------------------------------------------------------
QPointF Rest::stemPos() const
{
return pagePos();
}
2014-05-17 15:48:18 +02:00
//---------------------------------------------------------
// stemPosBeam
// return stem position of note on beam side
// return canvas coordinates
//---------------------------------------------------------
QPointF Rest::stemPosBeam() const
{
QPointF p(pagePos());
if (_up)
p.ry() += bbox().top() + spatium() * 2;
else
p.ry() += bbox().bottom() - spatium() * 2;
return p;
}
2014-05-16 13:52:22 +02:00
//---------------------------------------------------------
// stemPosX
//---------------------------------------------------------
qreal Rest::stemPosX() const
{
if (_up)
return bbox().right();
else
return bbox().left();
}
2013-05-13 18:49:17 +02:00
}