MuseScore/libmscore/rest.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1136 lines
34 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 "segment.h"
#include "stafftype.h"
#include "icon.h"
2014-10-16 18:12:58 +02:00
#include "image.h"
2012-05-26 14:26:10 +02:00
2013-05-13 18:49:17 +02:00
namespace Ms {
//---------------------------------------------------------
// restStyle
//---------------------------------------------------------
static const ElementStyle restStyle {
{ Sid::mmRestNumberPos, Pid::MMREST_NUMBER_POS },
};
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// Rest
//--------------------------------------------------------
2020-05-29 18:47:27 +02:00
Rest::Rest(Score* s)
: ChordRest(s)
2012-05-26 14:26:10 +02:00
{
_mmWidth = 0;
_beamMode = Beam::Mode::NONE;
2013-11-06 15:58:05 +01:00
_sym = SymId::restQuarter;
if (score()) {
initElementStyle(&restStyle);
2020-05-26 15:54:26 +02:00
}
2012-05-26 14:26:10 +02:00
}
2020-05-26 15:54:26 +02:00
2020-05-29 18:47:27 +02:00
Rest::Rest(Score* s, const TDuration& d)
: ChordRest(s)
2012-05-26 14:26:10 +02:00
{
_mmWidth = 0;
_beamMode = Beam::Mode::NONE;
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()) {
setTicks(d.fraction());
2020-05-26 15:54:26 +02:00
}
if (score()) {
initElementStyle(&restStyle);
2012-05-26 14:26:10 +02:00
}
2020-05-26 15:54:26 +02:00
}
2020-05-29 18:47:27 +02:00
Rest::Rest(const Rest& r, bool link)
: ChordRest(r, link)
2014-06-27 13:41:49 +02:00
{
if (link) {
2018-04-27 13:29:20 +02:00
score()->undo(new Link(this, const_cast<Rest*>(&r)));
setAutoplace(true);
}
_gap = r._gap;
2014-06-27 13:41:49 +02:00
_sym = r._sym;
dotline = r.dotline;
_mmWidth = r._mmWidth;
for (NoteDot* dot : r._dots) {
add(new NoteDot(*dot));
2020-05-26 15:54:26 +02:00
}
2014-06-27 13:41:49 +02:00
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// Rest::draw
//---------------------------------------------------------
void Rest::draw(QPainter* painter) const
{
const StaffType* stt = staff() ? staff()->staffTypeForElement(this) : nullptr;
2014-08-02 16:50:10 +02:00
if (
(stt && stt->isTabStaff()
2014-08-02 16:50:10 +02:00
// in tab staff, do not draw rests is rests are off OR if dur. symbols are on
&& (!stt->showRests() || stt->genDurations())
&& (!measure() || !measure()->isMMRest())) // show multi measure rest always
2014-08-02 16:50:10 +02:00
|| generated()
) {
2012-05-26 14:26:10 +02:00
return;
2020-05-26 15:54:26 +02:00
}
2012-05-26 14:26:10 +02:00
qreal _spatium = spatium();
painter->setPen(curColor());
2014-11-22 11:46:47 +01:00
if (measure() && measure()->isMMRest()) {
//only on voice 1
if (track() % VOICES) {
2020-05-26 15:54:26 +02:00
return;
}
// draw number
int n = measure()->mmRestCount();
std::vector<SymId>&& s = toTimeSigString(QString("%1").arg(n));
qreal y = _mmRestNumberPos * spatium() - staff()->height() * .5;
qreal x = (_mmWidth - symBbox(s).width()) * .5;
drawSymbols(s, painter, QPointF(x, y));
2020-05-26 15:54:26 +02:00
// draw horizontal line
qreal pw = _spatium * .7;
2012-05-26 14:26:10 +02:00
QPen pen(painter->pen());
pen.setWidthF(pw);
painter->setPen(pen);
qreal x1 = pw * .5;
qreal x2 = _mmWidth - x1;
if (_mmRestNumberPos > 0.7 && _mmRestNumberPos < 3.3) { // hack for when number encounters horizontal line
qreal gapDistance = symBbox(s).width() * .5 + _spatium;
qreal midpoint = (x1 + x2) * .5;
painter->drawLine(QLineF(x1, 0.0, midpoint - gapDistance, 0.0));
painter->drawLine(QLineF(midpoint + gapDistance, 0.0, x2, 0.0));
} else {
painter->drawLine(QLineF(x1, 0.0, x2, 0.0));
}
2020-05-26 15:54:26 +02:00
// draw vertical lines
2012-05-26 14:26:10 +02:00
pen.setWidthF(_spatium * .2);
painter->setPen(pen);
2017-10-17 14:28:03 +02:00
painter->drawLine(QLineF(0.0, -_spatium, 0.0, _spatium));
painter->drawLine(QLineF(_mmWidth, -_spatium, _mmWidth, _spatium));
} else {
2013-11-06 15:58:05 +01:00
drawSymbol(_sym, painter);
2020-05-26 15:54:26 +02:00
}
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// setOffset, overridden from Element
// (- raster vertical position in spatium units) -> no
2012-05-26 14:26:10 +02:00
// - half rests and whole rests outside the staff are
// replaced by special symbols with ledger lines
//---------------------------------------------------------
void Rest::setOffset(const QPointF& o)
2012-05-26 14:26:10 +02:00
{
qreal _spatium = spatium();
int line = lrint(o.y() / _spatium);
2020-05-26 15:54:26 +02:00
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;
2020-05-26 15:54:26 +02:00
}
Element::setOffset(o);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// drag
//---------------------------------------------------------
2017-03-31 13:03:15 +02:00
QRectF Rest::drag(EditData& ed)
2012-05-26 14:26:10 +02:00
{
// don't allow drag for Measure Rests, because they can't be easily laid out in correct position while dragging
if (measure() && durationType().type() == TDuration::DurationType::V_MEASURE) {
return QRectF();
2020-05-26 15:54:26 +02:00
}
2017-03-31 13:03:15 +02:00
QPointF s(ed.delta);
2012-05-26 14:26:10 +02:00
QRectF r(abbox());
2020-05-26 15:54:26 +02:00
2012-05-26 14:26:10 +02:00
// 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);
2020-05-26 15:54:26 +02:00
}
setOffset(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
//---------------------------------------------------------
2017-03-31 13:03:15 +02:00
bool Rest::acceptDrop(EditData& data) const
2012-05-26 14:26:10 +02:00
{
Element* e = data.dropElement;
2017-01-18 14:16:33 +01:00
ElementType type = e->type();
2012-05-26 14:26:10 +02:00
if (
2017-12-20 16:49:30 +01:00
(type == ElementType::ICON && toIcon(e)->iconType() == IconType::SBEAM)
|| (type == ElementType::ICON && toIcon(e)->iconType() == IconType::MBEAM)
|| (type == ElementType::ICON && toIcon(e)->iconType() == IconType::NBEAM)
|| (type == ElementType::ICON && toIcon(e)->iconType() == IconType::BEAM32)
|| (type == ElementType::ICON && toIcon(e)->iconType() == IconType::BEAM64)
|| (type == ElementType::ICON && toIcon(e)->iconType() == IconType::AUTOBEAM)
2018-01-16 13:38:17 +01:00
|| (type == ElementType::FERMATA)
2017-01-18 14:16:33 +01:00
|| (type == ElementType::CLEF)
|| (type == ElementType::KEYSIG)
|| (type == ElementType::TIMESIG)
2017-01-23 10:54:57 +01:00
|| (type == ElementType::SYSTEM_TEXT)
2017-01-18 14:16:33 +01:00
|| (type == ElementType::STAFF_TEXT)
|| (type == ElementType::BAR_LINE)
|| (type == ElementType::BREATH)
|| (type == ElementType::CHORD)
|| (type == ElementType::NOTE)
|| (type == ElementType::STAFF_STATE)
|| (type == ElementType::INSTRUMENT_CHANGE)
|| (type == ElementType::DYNAMIC)
|| (type == ElementType::HAIRPIN)
2017-01-18 14:16:33 +01:00
|| (type == ElementType::HARMONY)
|| (type == ElementType::TEMPO_TEXT)
|| (type == ElementType::REHEARSAL_MARK)
|| (type == ElementType::FRET_DIAGRAM)
|| (type == ElementType::TREMOLOBAR)
|| (type == ElementType::IMAGE)
|| (type == ElementType::SYMBOL)
|| (type == ElementType::REPEAT_MEASURE && durationType().type() == TDuration::DurationType::V_MEASURE)
2012-05-26 14:26:10 +02:00
) {
return true;
}
return false;
}
//---------------------------------------------------------
// drop
//---------------------------------------------------------
2017-03-31 13:03:15 +02:00
Element* Rest::drop(EditData& data)
2012-05-26 14:26:10 +02:00
{
Element* e = data.dropElement;
2012-05-26 14:26:10 +02:00
switch (e->type()) {
2017-01-18 14:16:33 +01:00
case ElementType::ARTICULATION:
{
2016-10-06 12:21:28 +02:00
Articulation* a = toArticulation(e);
if (!a->isFermata() || !score()->addArticulation(this, a)) {
2012-05-26 14:26:10 +02:00
delete e;
e = 0;
}
2020-05-26 15:54:26 +02:00
}
2012-05-26 14:26:10 +02:00
return e;
2020-05-26 15:54:26 +02:00
2017-12-20 16:49:30 +01:00
case ElementType::CHORD: {
Chord* c = toChord(e);
Note* n = c->upNote();
Direction dir = c->stemDirection();
2012-05-26 14:26:10 +02:00
// score()->select(0, SelectType::SINGLE, 0);
NoteVal nval;
2017-12-20 16:49:30 +01:00
nval.pitch = n->pitch();
2012-05-26 14:26:10 +02:00
nval.headGroup = n->headGroup();
Fraction d = score()->inputState().ticks();
if (!d.isZero()) {
Segment* seg = score()->setNoteRest(segment(), track(), nval, d, dir);
2017-01-18 14:16:33 +01:00
if (seg) {
2014-10-16 18:12:58 +02:00
ChordRest* cr = toChordRest(seg->element(track()));
2020-05-26 15:54:26 +02:00
if (cr) {
2014-10-16 18:12:58 +02:00
score()->nextInputPos(cr, true);
2020-05-26 15:54:26 +02:00
}
2012-05-26 14:26:10 +02:00
}
2020-05-26 15:54:26 +02:00
}
delete e;
2020-05-26 15:54:26 +02:00
}
break;
2017-01-18 14:16:33 +01:00
case ElementType::REPEAT_MEASURE:
delete e;
if (durationType().type() == TDuration::DurationType::V_MEASURE) {
measure()->cmdInsertRepeatMeasure(staffIdx());
2020-05-26 15:54:26 +02:00
}
break;
2017-01-18 14:16:33 +01:00
case ElementType::SYMBOL:
case ElementType::IMAGE:
2014-10-16 18:12:58 +02:00
e->setParent(this);
score()->undoAddElement(e);
2014-10-16 18:12:58 +02:00
return e;
2020-05-26 15:54:26 +02:00
default:
2012-05-26 14:26:10 +02:00
return ChordRest::drop(data);
2020-05-26 15:54:26 +02:00
}
2012-05-26 14:26:10 +02:00
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:
if (ticks() >= Fraction(2, 1)) {
2013-11-06 15:58:05 +01:00
return SymId::restDoubleWhole;
2020-05-26 15:54:26 +02:00
}
// fall through
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:
2015-10-27 11:30:09 +01:00
qDebug("unknown rest type %d", int(type));
2013-11-06 15:58:05 +01:00
return SymId::restQuarter;
2012-05-26 14:26:10 +02:00
}
}
2017-10-17 14:28:03 +02:00
//---------------------------------------------------------
// layoutMMRest
//---------------------------------------------------------
void Rest::layoutMMRest(qreal val)
{
// static const qreal verticalLineWidth = .2;
qreal _spatium = spatium();
_mmWidth = val;
// qreal h = _spatium * (2 + verticalLineWidth);
// qreal w = _mmWidth + _spatium * verticalLineWidth * .5;
// bbox().setRect(-_spatium * verticalLineWidth * .5, -h * .5, w, h);
bbox().setRect(0.0, -_spatium, _mmWidth, _spatium * 2);
// text
// qreal y = -_spatium * 2.5 - staff()->height() *.5;
// addbbox(QRectF(0, y, w, _spatium * 2)); // approximation
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// layout
//---------------------------------------------------------
void Rest::layout()
{
2016-06-03 11:56:11 +02:00
if (_gap) {
return;
2020-05-26 15:54:26 +02:00
}
2016-12-06 09:35:52 +01:00
for (Element* e : el()) {
2014-10-16 18:12:58 +02:00
e->layout();
2020-05-26 15:54:26 +02:00
}
2017-10-18 13:09:35 +02:00
qreal _spatium = spatium();
2014-11-22 11:46:47 +01:00
if (measure() && measure()->isMMRest()) {
2018-03-27 15:36:00 +02:00
_mmWidth = score()->styleP(Sid::minMMRestWidth) * mag();
2017-10-18 13:09:35 +02:00
// setbbox(QRectF(0.0, -_spatium, _mmWidth, 2.0 * _spatium));
return;
2020-05-26 15:54:26 +02:00
}
rxpos() = 0.0;
const StaffType* stt = staffType();
if (stt && stt->isTabStaff()) {
const StaffType* tab = stt;
// 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) {
Fraction ticks = measure()->ticks();
TDuration dur = TDuration(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
2020-05-26 15:54:26 +02:00
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 displayed
// Rest::draw() will skip their drawing, if not needed
if (_tabDur) {
delete _tabDur;
_tabDur = 0;
2020-05-26 15:54:26 +02:00
}
}
dotline = Rest::getDotline(durationType().type());
2020-05-26 15:54:26 +02:00
qreal yOff = offset().y();
const Staff* stf = staff();
const StaffType* st = stf->staffTypeForElement(this);
2016-12-18 14:31:13 +01:00
qreal lineDist = st ? st->lineDistance().val() : 1.0;
int userLine = yOff == 0.0 ? 0 : lrint(yOff / (lineDist * _spatium));
2016-12-18 14:31:13 +01:00
int lines = st ? st->lines() : 5;
int lineOffset = computeLineOffset(lines);
2020-05-26 15:54:26 +02:00
int yo;
_sym = getSymbol(durationType().type(), lineOffset / 2 + userLine, lines, &yo);
2016-12-18 14:31:13 +01:00
rypos() = (qreal(yo) + qreal(lineOffset) * .5) * lineDist * _spatium;
2013-11-11 15:11:28 +01:00
setbbox(symBbox(_sym));
layoutDots();
}
//---------------------------------------------------------
// layout
//---------------------------------------------------------
void Rest::layoutDots()
{
checkDots();
qreal x = symWidth(_sym) + score()->styleP(Sid::dotNoteDistance) * mag();
qreal dx = score()->styleP(Sid::dotDotDistance) * mag();
qreal y = dotline * spatium() * .5;
for (NoteDot* dot : _dots) {
dot->layout();
dot->setPos(x, y);
x += dx;
}
}
//---------------------------------------------------------
// checkDots
//---------------------------------------------------------
void Rest::checkDots()
{
int n = dots() - int(_dots.size());
for (int i = 0; i < n; ++i) {
NoteDot* dot = new NoteDot(score());
dot->setParent(this);
dot->setVisible(visible());
score()->undoAddElement(dot);
}
if (n < 0) {
for (int i = 0; i < -n; ++i) {
score()->undoRemoveElement(_dots.back());
2020-05-26 15:54:26 +02:00
}
}
}
//---------------------------------------------------------
// dot
//---------------------------------------------------------
NoteDot* Rest::dot(int n)
{
checkDots();
return _dots[n];
}
//---------------------------------------------------------
// getDotline
//---------------------------------------------------------
int Rest::getDotline(TDuration::DurationType durationType)
{
int dl = -1;
switch (durationType) {
case TDuration::DurationType::V_64TH:
case TDuration::DurationType::V_32ND:
dl = -3;
break;
case TDuration::DurationType::V_1024TH:
case TDuration::DurationType::V_512TH:
case TDuration::DurationType::V_256TH:
case TDuration::DurationType::V_128TH:
dl = -5;
break;
default:
dl = -1;
break;
}
return dl;
}
//---------------------------------------------------------
2016-12-18 14:31:13 +01:00
// computeLineOffset
//---------------------------------------------------------
2016-12-18 14:31:13 +01:00
int Rest::computeLineOffset(int lines)
{
Segment* s = segment();
bool offsetVoices = s && measure() && measure()->hasVoices(staffIdx(), tick(), actualTicks());
if (offsetVoices && voice() == 0) {
// do not offset voice 1 rest if there exists a matching invisible rest in voice 2;
Element* e = s->element(track() + 1);
if (e && e->isRest() && !e->visible() && !toRest(e)->isGap()) {
2017-12-20 16:49:30 +01:00
Rest* r = toRest(e);
if (r->globalTicks() == globalTicks()) {
offsetVoices = false;
}
2020-05-26 15:54:26 +02:00
}
}
#if 0
if (offsetVoices && staff()->mergeMatchingRests()) {
// automatically merge matching rests in voices 1 & 2 if nothing in any other voice
// this is not always the right thing to do do, but is useful in choral music
// and perhaps could be made enabled via a staff property
// so choral staves can be treated differently than others
bool matchFound = false;
bool nothingElse = true;
int baseTrack = staffIdx() * VOICES;
for (int v = 0; v < VOICES; ++v) {
if (v == voice()) {
continue;
2020-05-26 15:54:26 +02:00
}
Element* e = s->element(baseTrack + v);
if (v <= 1) {
// try to find match in other voice (1 or 2)
2017-01-18 14:16:33 +01:00
if (e && e->type() == ElementType::REST) {
2017-12-20 16:49:30 +01:00
Rest* r = toRest(e);
if (r->globalDuration() == globalDuration()) {
matchFound = true;
continue;
2020-05-26 15:54:26 +02:00
}
}
// no match found; no sense looking for anything else
break;
} else {
// if anything in another voice, do not merge
if (e) {
nothingElse = false;
break;
}
}
2020-05-26 15:54:26 +02:00
}
if (matchFound && nothingElse) {
offsetVoices = false;
2020-05-26 15:54:26 +02:00
}
}
#endif
2016-12-18 14:31:13 +01:00
int lineOffset = 0;
int assumedCenter = 4;
2016-12-18 14:31:13 +01:00
int actualCenter = (lines - 1);
int centerDiff = actualCenter - assumedCenter;
2020-05-26 15:54:26 +02:00
2012-05-26 14:26:10 +02:00
if (offsetVoices) {
// 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:
if (ticks() >= Fraction(2, 1)) { // breve symbol
2012-05-26 14:26:10 +02:00
lineOffset = up ? -3 : 5;
} else {
lineOffset = up ? -4 : 6; // whole symbol
2012-05-26 14:26:10 +02:00
}
break;
case TDuration::DurationType::V_WHOLE:
2012-05-26 14:26:10 +02:00
lineOffset = up ? -4 : 6;
2020-05-26 15:54:26 +02:00
break;
case TDuration::DurationType::V_HALF:
2012-05-26 14:26:10 +02:00
lineOffset = up ? -4 : 4;
2020-05-26 15:54:26 +02:00
break;
case TDuration::DurationType::V_QUARTER:
2012-05-26 14:26:10 +02:00
lineOffset = up ? -4 : 4;
2020-05-26 15:54:26 +02:00
break;
case TDuration::DurationType::V_EIGHTH:
2012-05-26 14:26:10 +02:00
lineOffset = up ? -4 : 4;
2020-05-26 15:54:26 +02:00
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;
2020-05-26 15:54:26 +02:00
break;
case TDuration::DurationType::V_128TH:
lineOffset = up ? -8 : 8;
2012-05-26 14:26:10 +02:00
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;
2020-05-26 15:54:26 +02:00
break;
default:
break;
}
// adjust offsets for staves with other than five lines
if (lines != 5) {
lineOffset += centerDiff;
if (centerDiff & 1) {
// round to line
if (lines == 2 && staff() && staff()->lineDistance(tick()) < 2.0) {
// leave alone
2015-08-06 23:29:55 +02:00
} else if (lines <= 6) {
lineOffset += lineOffset > 0 ? -1 : 1; // round inward
2020-05-26 15:54:26 +02:00
} else {
2012-05-26 14:26:10 +02:00
lineOffset += lineOffset > 0 ? 1 : -1; // round outward
}
}
2020-05-26 15:54:26 +02:00
}
} else {
// Gould says to center rests on middle line or space
// but subjectively, many rests look strange centered on a space
// so we do it for 2-line staves only
if (centerDiff & 1 && lines != 2) {
centerDiff += 1; // round down
2020-05-26 15:54:26 +02:00
}
lineOffset = centerDiff;
2012-05-26 14:26:10 +02:00
switch (durationType().type()) {
case TDuration::DurationType::V_LONG:
case TDuration::DurationType::V_BREVE:
case TDuration::DurationType::V_MEASURE:
case TDuration::DurationType::V_WHOLE:
if (lineOffset & 1) {
lineOffset += 1; // always round to nearest line
2015-08-06 23:29:55 +02:00
} else if (lines <= 3) {
lineOffset += 2; // special case - move down for 1-line or 3-line staff
2020-05-26 15:54:26 +02:00
}
break;
case TDuration::DurationType::V_HALF:
if (lineOffset & 1) {
lineOffset += 1; // always round to nearest line
2020-05-26 15:54:26 +02:00
}
break;
default:
break;
}
}
// DEBUG: subtract this off only to be added back in layout()?
// that would throw off calculation of when ledger lines are needed
//if (staff())
// lineOffset -= staff()->staffType()->stepOffset();
return lineOffset;
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)
{
ChordRest::scanElements(data, func, all);
2016-12-06 09:35:52 +01:00
for (Element* e : el()) {
2014-10-16 18:12:58 +02:00
e->scanElements(data, func, all);
2020-05-26 15:54:26 +02:00
}
for (NoteDot* dot : _dots) {
dot->scanElements(data, func, all);
2020-05-26 15:54:26 +02:00
}
if (!isGap()) {
func(data, this);
2020-05-26 15:54:26 +02:00
}
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// setTrack
//---------------------------------------------------------
void Rest::setTrack(int val)
{
ChordRest::setTrack(val);
for (NoteDot* dot : _dots) {
dot->setTrack(val);
2020-05-26 15:54:26 +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
{
2018-03-27 15:36:00 +02:00
undoChangeProperty(Pid::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(this);
2013-05-28 15:42:02 +02:00
if (small()) {
2018-03-27 15:36:00 +02:00
m *= score()->styleD(Sid::smallNoteMag);
2020-05-26 15:54:26 +02:00
}
2013-05-28 15:42:02 +02:00
return m;
}
2014-05-16 13:52:22 +02:00
//---------------------------------------------------------
// upLine
//---------------------------------------------------------
int Rest::upLine() const
{
2016-02-15 12:23:28 +01:00
qreal _spatium = spatium();
return lrint((pos().y() + bbox().top() + _spatium) * 2 / _spatium);
2014-05-16 13:52:22 +02:00
}
//---------------------------------------------------------
// downLine
//---------------------------------------------------------
int Rest::downLine() const
{
2016-02-15 12:23:28 +01:00
qreal _spatium = spatium();
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() * 1.5;
2014-05-17 15:48:18 +02:00
} else {
p.ry() += bbox().bottom() - spatium() * 1.5;
2020-05-26 15:54:26 +02:00
}
2014-05-17 15:48:18 +02:00
return p;
}
2014-05-16 13:52:22 +02:00
//---------------------------------------------------------
// stemPosX
//---------------------------------------------------------
qreal Rest::stemPosX() const
{
if (_up) {
return bbox().right();
} else {
return bbox().left();
2020-05-26 15:54:26 +02:00
}
2014-05-16 13:52:22 +02:00
}
//---------------------------------------------------------
// accent
//---------------------------------------------------------
bool Rest::accent()
{
return voice() >= 2 && small();
}
//---------------------------------------------------------
// setAccent
//---------------------------------------------------------
void Rest::setAccent(bool flag)
{
2018-03-27 15:36:00 +02:00
undoChangeProperty(Pid::SMALL, flag);
if (voice() % 2 == 0) {
if (flag) {
qreal yOffset = -(bbox().bottom());
if (durationType() >= TDuration::DurationType::V_HALF) {
2016-12-23 12:05:18 +01:00
yOffset -= staff()->spatium(tick()) * 0.5;
}
// undoChangeProperty(Pid::OFFSET, QPointF(0.0, yOffset));
rypos() += yOffset;
} else {
// undoChangeProperty(Pid::OFFSET, QPointF()); TODO::check
2020-05-26 15:54:26 +02:00
}
}
}
//---------------------------------------------------------
// accessibleInfo
//---------------------------------------------------------
2016-02-04 17:06:32 +01:00
QString Rest::accessibleInfo() const
{
QString voice = QObject::tr("Voice: %1").arg(QString::number(track() % VOICES + 1));
return QObject::tr("%1; Duration: %2; %3").arg(Element::accessibleInfo()).arg(durationUserName()).arg(voice);
}
//---------------------------------------------------------
// accessibleInfo
//---------------------------------------------------------
2016-02-04 17:06:32 +01:00
QString Rest::screenReaderInfo() const
{
Measure* m = measure();
bool voices = m ? m->hasVoices(staffIdx()) : false;
QString voice = voices ? QObject::tr("Voice: %1").arg(QString::number(track() % VOICES + 1)) : "";
return QString("%1 %2 %3").arg(Element::accessibleInfo()).arg(durationUserName()).arg(voice);
}
2014-10-16 18:12:58 +02:00
//---------------------------------------------------------
// add
//---------------------------------------------------------
void Rest::add(Element* e)
{
e->setParent(this);
e->setTrack(track());
2020-05-26 15:54:26 +02:00
2014-10-16 18:12:58 +02:00
switch (e->type()) {
case ElementType::NOTEDOT:
_dots.push_back(toNoteDot(e));
break;
2017-01-18 14:16:33 +01:00
case ElementType::SYMBOL:
case ElementType::IMAGE:
2016-12-06 09:35:52 +01:00
el().push_back(e);
2014-10-16 18:12:58 +02:00
break;
default:
2014-10-16 18:51:14 +02:00
ChordRest::add(e);
2014-10-16 18:12:58 +02:00
break;
}
}
//---------------------------------------------------------
// remove
//---------------------------------------------------------
void Rest::remove(Element* e)
{
switch (e->type()) {
case ElementType::NOTEDOT:
_dots.pop_back();
break;
2017-01-18 14:16:33 +01:00
case ElementType::SYMBOL:
case ElementType::IMAGE:
2016-12-06 09:35:52 +01:00
if (!el().remove(e)) {
2014-10-16 18:12:58 +02:00
qDebug("Rest::remove(): cannot find %s", e->name());
2020-05-26 15:54:26 +02:00
}
2014-10-16 18:12:58 +02:00
break;
default:
2014-10-16 18:51:14 +02:00
ChordRest::remove(e);
2014-10-16 18:12:58 +02:00
break;
}
}
//--------------------------------------------------
// Rest::write
//---------------------------------------------------------
2016-11-19 11:51:21 +01:00
void Rest::write(XmlWriter& xml) const
2014-10-16 18:12:58 +02:00
{
if (_gap) {
return;
2020-05-26 15:54:26 +02:00
}
2018-06-09 04:45:54 +02:00
writeBeam(xml);
xml.stag(this);
2014-10-16 18:12:58 +02:00
ChordRest::writeProperties(xml);
2016-12-06 09:35:52 +01:00
el().write(xml);
bool write_dots = false;
for (NoteDot* dot : _dots) {
if (!dot->offset().isNull() || !dot->visible() || dot->color() != Qt::black || dot->visible() != visible()) {
write_dots = true;
break;
}
2020-05-26 15:54:26 +02:00
}
if (write_dots) {
for (NoteDot* dot: _dots) {
dot->write(xml);
2020-05-26 15:54:26 +02:00
}
}
2014-10-16 18:12:58 +02:00
xml.etag();
}
//---------------------------------------------------------
// Rest::read
//---------------------------------------------------------
void Rest::read(XmlReader& e)
{
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "Symbol") {
Symbol* s = new Symbol(score());
s->setTrack(track());
s->read(e);
add(s);
} else if (tag == "Image") {
if (MScore::noImages) {
e.skipCurrentElement();
} else {
Image* image = new Image(score());
image->setTrack(track());
image->read(e);
add(image);
2014-10-16 18:12:58 +02:00
}
} else if (tag == "NoteDot") {
NoteDot* dot = new NoteDot(score());
dot->read(e);
2020-05-26 15:54:26 +02:00
add(dot);
2014-10-16 18:12:58 +02:00
} else if (ChordRest::readProperties(e)) {
2020-05-26 15:54:26 +02:00
} else {
2014-10-16 18:12:58 +02:00
e.unknown();
2020-05-26 15:54:26 +02:00
}
}
}
2014-10-16 18:12:58 +02:00
//---------------------------------------------------------
// localSpatiumChanged
//---------------------------------------------------------
void Rest::localSpatiumChanged(qreal oldValue, qreal newValue)
{
ChordRest::localSpatiumChanged(oldValue, newValue);
for (Element* e : _dots) {
e->localSpatiumChanged(oldValue, newValue);
2020-05-26 15:54:26 +02:00
}
for (Element* e : el()) {
e->localSpatiumChanged(oldValue, newValue);
2020-05-26 15:54:26 +02:00
}
}
2016-06-03 11:56:11 +02:00
//---------------------------------------------------------
// propertyDefault
2016-06-03 11:56:11 +02:00
//---------------------------------------------------------
QVariant Rest::propertyDefault(Pid propertyId) const
2016-06-03 11:56:11 +02:00
{
switch (propertyId) {
2018-03-27 15:36:00 +02:00
case Pid::GAP:
return false;
case Pid::MMREST_NUMBER_POS:
return score()->styleV(Sid::mmRestNumberPos);
2016-06-03 11:56:11 +02:00
default:
return ChordRest::propertyDefault(propertyId);
2016-06-03 11:56:11 +02:00
}
}
//————————————————————————————
// resetProperty
//————————————————————————————
void Rest::resetProperty(Pid id)
{
setProperty(id, propertyDefault(id));
return;
}
//————————————————————————————
// getPropertyStyle
//————————————————————————————
Sid Rest::getPropertyStyle(Pid pid) const
{
if (pid == Pid::MMREST_NUMBER_POS) {
return Sid::mmRestNumberPos;
2020-05-26 15:54:26 +02:00
}
return ChordRest::getPropertyStyle(pid);
}
2016-06-03 11:56:11 +02:00
//---------------------------------------------------------
// getProperty
2016-06-03 11:56:11 +02:00
//---------------------------------------------------------
QVariant Rest::getProperty(Pid propertyId) const
2016-06-03 11:56:11 +02:00
{
switch (propertyId) {
2018-03-27 15:36:00 +02:00
case Pid::GAP:
return _gap;
case Pid::MMREST_NUMBER_POS:
return _mmRestNumberPos;
2016-06-03 11:56:11 +02:00
default:
return ChordRest::getProperty(propertyId);
2016-06-03 11:56:11 +02:00
}
}
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
2018-03-27 15:36:00 +02:00
bool Rest::setProperty(Pid propertyId, const QVariant& v)
{
switch (propertyId) {
2018-03-27 15:36:00 +02:00
case Pid::GAP:
2016-06-03 11:56:11 +02:00
_gap = v.toBool();
2019-10-24 15:49:23 +02:00
triggerLayout();
2016-06-03 11:56:11 +02:00
break;
case Pid::VISIBLE:
setVisible(v.toBool());
2019-10-24 15:49:23 +02:00
triggerLayout();
break;
case Pid::OFFSET:
score()->addRefresh(canvasBoundingRect());
setOffset(v.toPointF());
layout();
score()->addRefresh(canvasBoundingRect());
if (measure() && durationType().type() == TDuration::DurationType::V_MEASURE) {
measure()->triggerLayout();
2020-05-26 15:54:26 +02:00
}
2019-10-24 15:49:23 +02:00
triggerLayout();
break;
case Pid::MMREST_NUMBER_POS:
_mmRestNumberPos = v.toDouble();
triggerLayout();
break;
default:
return ChordRest::setProperty(propertyId, v);
}
return true;
}
//---------------------------------------------------------
// undoChangeDotsVisible
//---------------------------------------------------------
void Rest::undoChangeDotsVisible(bool v)
{
for (NoteDot* dot : _dots) {
dot->undoChangeProperty(Pid::VISIBLE, QVariant(v));
2020-05-26 15:54:26 +02:00
}
}
//---------------------------------------------------------
// nextElement
//---------------------------------------------------------
Element* Rest::nextElement()
{
return ChordRest::nextElement();
}
//---------------------------------------------------------
// prevElement
//---------------------------------------------------------
Element* Rest::prevElement()
{
return ChordRest::prevElement();
}
2016-01-04 14:48:58 +01:00
//---------------------------------------------------------
// shape
//---------------------------------------------------------
Shape Rest::shape() const
{
Shape shape;
2016-06-03 11:59:31 +02:00
if (!_gap) {
shape.add(ChordRest::shape());
2017-10-17 14:28:03 +02:00
if (measure() && measure()->isMMRest()) {
2017-09-14 11:12:03 +02:00
qreal _spatium = spatium();
shape.add(QRectF(0.0, -_spatium, _mmWidth, 2.0 * _spatium));
2020-05-26 15:54:26 +02:00
int n = measure()->mmRestCount();
2017-10-18 13:09:35 +02:00
std::vector<SymId>&& s = toTimeSigString(QString("%1").arg(n));
2020-05-26 15:54:26 +02:00
2017-10-18 13:09:35 +02:00
QRectF r = symBbox(s);
qreal y = _mmRestNumberPos * spatium() - staff()->height() * .5;
qreal x = .5 * (_mmWidth - r.width());
2020-05-26 15:54:26 +02:00
2017-10-18 13:09:35 +02:00
r.translate(QPointF(x, y));
shape.add(r);
2016-06-03 11:59:31 +02:00
} else
#ifndef NDEBUG
2020-05-26 15:54:26 +02:00
{
shape.add(bbox(), name());
2020-05-26 15:54:26 +02:00
}
#else
2020-05-26 15:54:26 +02:00
{
shape.add(bbox());
2020-05-26 15:54:26 +02:00
}
#endif
for (NoteDot* dot : _dots) {
shape.add(symBbox(SymId::augmentationDot).translated(dot->pos()));
2016-06-03 11:59:31 +02:00
}
2020-05-26 15:54:26 +02:00
}
for (Element* e : el()) {
if (e->addToSkyline()) {
shape.add(e->shape().translated(e->pos()));
}
2020-05-26 15:54:26 +02:00
}
2016-01-04 14:48:58 +01:00
return shape;
}
}