MuseScore/libmscore/note.cpp

2053 lines
72 KiB
C++
Raw Normal View History

2012-05-26 14:26:10 +02:00
//=============================================================================
// MuseScore
// Music Composition & Notation
//
2012-11-19 09:29:46 +01: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
//=============================================================================
/**
\file
Implementation of classes Note and ShadowNote.
*/
#include <assert.h>
#include "note.h"
#include "score.h"
#include "key.h"
#include "chord.h"
#include "sym.h"
#include "xml.h"
#include "slur.h"
2013-08-22 12:18:14 +02:00
#include "tie.h"
2012-05-26 14:26:10 +02:00
#include "text.h"
#include "clef.h"
#include "staff.h"
#include "pitchspelling.h"
#include "arpeggio.h"
#include "tremolo.h"
#include "utils.h"
#include "image.h"
#include "system.h"
#include "tuplet.h"
#include "articulation.h"
#include "drumset.h"
#include "segment.h"
#include "measure.h"
#include "undo.h"
#include "part.h"
#include "stafftype.h"
#include "tablature.h"
#include "fret.h"
#include "harmony.h"
#include "fingering.h"
#include "bend.h"
#include "mscore.h"
#include "accidental.h"
#include "page.h"
#include "icon.h"
#include "notedot.h"
2012-09-12 16:19:03 +02:00
#include "spanner.h"
#include "glissando.h"
2013-08-01 22:24:36 +02:00
#include "bagpembell.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
//---------------------------------------------------------
// noteHeads
// note head groups
//---------------------------------------------------------
const SymId noteHeads[2][Note::HEAD_GROUPS][HEAD_TYPES] = {
2012-05-26 14:26:10 +02:00
{ // down stem
{ wholeheadSym, halfheadSym, quartheadSym, brevisheadSym },
{ wholecrossedheadSym, halfcrossedheadSym, crossedheadSym, wholecrossedheadSym },
{ wholediamondheadSym, halfdiamondheadSym, diamondheadSym, wholediamondheadSym },
{ s0triangleHeadSym, d1triangleHeadSym, d2triangleHeadSym, s0triangleHeadSym },
{ s0miHeadSym, s1miHeadSym, s2miHeadSym, noSym },
2012-05-26 14:26:10 +02:00
{ wholeslashheadSym, halfslashheadSym, quartslashheadSym, wholeslashheadSym },
{ xcircledheadSym, xcircledheadSym, xcircledheadSym, xcircledheadSym },
{ s0doHeadSym, d1doHeadSym, d2doHeadSym, noSym },
{ s0reHeadSym, d1reHeadSym, d2reHeadSym, noSym },
{ d0faHeadSym, d1faHeadSym, d2faHeadSym, noSym },
{ s0laHeadSym, s1laHeadSym, s2laHeadSym, noSym },
{ s0tiHeadSym, d1tiHeadSym, d2tiHeadSym, noSym },
{ s0solHeadSym, s1solHeadSym, s2solHeadSym, noSym },
2012-05-26 14:26:10 +02:00
{ wholeheadSym, halfheadSym, quartheadSym, brevisheadaltSym },
},
{ // up stem
{ wholeheadSym, halfheadSym, quartheadSym, brevisheadSym },
{ wholecrossedheadSym, halfcrossedheadSym, crossedheadSym, wholecrossedheadSym },
{ wholediamondheadSym, halfdiamondheadSym, diamondheadSym, wholediamondheadSym },
{ s0triangleHeadSym, u1triangleHeadSym, u2triangleHeadSym, s0triangleHeadSym },
{ s0miHeadSym, s1miHeadSym, s2miHeadSym, noSym },
2012-05-26 14:26:10 +02:00
{ wholeslashheadSym, halfslashheadSym, quartslashheadSym, wholeslashheadSym },
{ xcircledheadSym, xcircledheadSym, xcircledheadSym, xcircledheadSym },
{ s0doHeadSym, u1doHeadSym, u2doHeadSym, noSym },
{ s0reHeadSym, u1reHeadSym, u2reHeadSym, noSym },
{ u0faHeadSym, u1faHeadSym, u2faHeadSym, noSym },
{ s0laHeadSym, s1laHeadSym, s2laHeadSym, noSym },
{ s0tiHeadSym, u1tiHeadSym, u2tiHeadSym, noSym },
{ s0solHeadSym, s1solHeadSym, s2solHeadSym, noSym },
2012-05-26 14:26:10 +02:00
{ wholeheadSym, halfheadSym, quartheadSym, brevisheadaltSym },
}
};
2012-08-08 20:46:29 +02:00
//---------------------------------------------------------
// NoteVal
//---------------------------------------------------------
NoteVal::NoteVal()
{
pitch = -1;
tpc = INVALID_TPC,
fret = FRET_NONE;
string = STRING_NONE;
2012-08-08 20:46:29 +02:00
headGroup = 0;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// noteHeadSym
//---------------------------------------------------------
Sym* noteHeadSym(bool up, int group, int type)
{
return &symbols[0][noteHeads[up][group][type]];
}
//---------------------------------------------------------
// write
//---------------------------------------------------------
void NoteHead::write(Xml& xml) const
{
xml.stag("NoteHead");
xml.tag("name", Sym::id2name(_sym));
2012-05-26 14:26:10 +02:00
Element::writeProperties(xml);
xml.etag();
}
//---------------------------------------------------------
// Note
//---------------------------------------------------------
Note::Note(Score* s)
: Element(s)
{
setFlags(ELEMENT_MOVABLE | ELEMENT_SELECTABLE);
dragMode = false;
_pitch = 0;
_play = true;
2012-05-26 14:26:10 +02:00
_tuning = 0.0;
_accidental = 0;
_mirror = false;
2012-08-04 15:46:43 +02:00
_userMirror = MScore::DH_AUTO;
2012-05-26 14:26:10 +02:00
_small = false;
2012-08-04 15:46:43 +02:00
_dotPosition = MScore::AUTO;
2012-05-26 14:26:10 +02:00
_line = 0;
_fret = -1;
_string = -1;
_fretConflict = false;
_ghost = false;
_lineOffset = 0;
_tieFor = 0;
_tieBack = 0;
_tpc = INVALID_TPC;
_headGroup = HEAD_NORMAL;
_headType = HEAD_AUTO;
_hidden = false;
_subchannel = 0;
2012-08-04 15:46:43 +02:00
_veloType = MScore::OFFSET_VAL;
2012-05-26 14:26:10 +02:00
_veloOffset = 0;
_dots[0] = 0;
_dots[1] = 0;
_dots[2] = 0;
2012-11-19 09:29:46 +01:00
_playEvents.append(NoteEvent()); // add default play event
2013-02-20 17:53:15 +01:00
_mark = 0;
2012-05-26 14:26:10 +02:00
}
Note::~Note()
{
delete _accidental;
2012-11-19 09:29:46 +01:00
qDeleteAll(_el);
2012-05-26 14:26:10 +02:00
delete _tieFor;
delete _dots[0];
delete _dots[1];
delete _dots[2];
}
Note::Note(const Note& n)
: Element(n)
{
_subchannel = n._subchannel;
_line = n._line;
_fret = n._fret;
_string = n._string;
_fretConflict = n._fretConflict;
_ghost = n._ghost;
dragMode = n.dragMode;
_pitch = n._pitch;
_tpc = n._tpc;
_hidden = n._hidden;
_play = n._play;
2012-05-26 14:26:10 +02:00
_tuning = n._tuning;
_veloType = n._veloType;
_veloOffset = n._veloOffset;
_headGroup = n._headGroup;
_headType = n._headType;
_mirror = n._mirror;
_userMirror = n._userMirror;
_small = n._small;
_dotPosition = n._dotPosition;
_accidental = 0;
2012-05-26 14:26:10 +02:00
if (n._accidental)
add(new Accidental(*(n._accidental)));
2013-03-25 16:27:20 +01:00
for (const Element* e : n._el)
add(e->clone());
2012-11-19 09:29:46 +01:00
_playEvents = n._playEvents;
2012-05-26 14:26:10 +02:00
if (n._tieFor) {
_tieFor = new Tie(*n._tieFor);
_tieFor->setStartNote(this);
_tieFor->setEndNote(0);
}
else
_tieFor = 0;
_tieBack = 0;
for (int i = 0; i < 3; ++i) {
_dots[i] = 0;
if (n._dots[i])
add(new NoteDot(*n._dots[i]));
}
_lineOffset = n._lineOffset;
2013-02-20 17:53:15 +01:00
_mark = n._mark;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// setPitch
//---------------------------------------------------------
void Note::setPitch(int val)
{
_pitch = restrict(val, 0, 127);
int pitchOffset = 0;
if (score()) {
2012-11-04 13:24:44 +01:00
Part* part = staff() ? staff()->part() : 0;
2012-05-26 14:26:10 +02:00
if (part)
pitchOffset = score()->styleB(ST_concertPitch) ? 0 : part->instr()->transpose().chromatic;
}
2012-11-19 09:29:46 +01:00
for (int i = 0; i < _playEvents.size(); ++i)
_playEvents[i].setPitch(pitchOffset);
2012-05-26 14:26:10 +02:00
if (chord())
2013-07-15 14:36:51 +02:00
chord()->lineChanged();
2012-05-26 14:26:10 +02:00
}
void Note::setPitch(int a, int b)
{
setPitch(a);
_tpc = b;
}
//---------------------------------------------------------
// undoSetPitch
//---------------------------------------------------------
void Note::undoSetPitch(int p)
{
score()->undoChangeProperty(this, P_PITCH, p);
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// setTpcFromPitch
//---------------------------------------------------------
void Note::setTpcFromPitch()
{
KeySigEvent key = (staff() && chord()) ? staff()->key(chord()->tick()) : KeySigEvent();
_tpc = pitch2tpc(_pitch, key.accidentalType(), PREFER_NEAREST);
2012-05-26 14:26:10 +02:00
// qDebug("setTpcFromPitch pitch %d tick %d key %d tpc %d\n", pitch(), chord()->tick(), key.accidentalType(), _tpc);
}
//---------------------------------------------------------
// setTpc
//---------------------------------------------------------
void Note::setTpc(int v)
{
if (!tpcIsValid(v)) {
qDebug("Note::setTpc: bad tpc %d\n", v);
abort();
}
_tpc = v;
}
//---------------------------------------------------------
// undoSetTpc
//---------------------------------------------------------
void Note::undoSetTpc(int tpc)
{
score()->undoChangeProperty(this, P_TPC, tpc);
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// noteHead
//---------------------------------------------------------
int Note::noteHead() const
{
2013-01-02 20:13:58 +01:00
int hg, ht;
2012-05-26 14:26:10 +02:00
if (chord()) {
hg = chord()->up();
ht = chord()->durationType().headType();
}
2013-01-02 20:13:58 +01:00
else {
hg = 1;
2013-03-07 13:31:14 +01:00
ht = HEAD_QUARTER;
2012-05-26 14:26:10 +02:00
}
2013-01-28 21:45:36 +01:00
if (_headType != HEAD_AUTO)
ht = _headType;
2013-01-02 20:13:58 +01:00
2013-01-28 21:45:36 +01:00
int t = noteHeads[hg][int(_headGroup)][ht];
if (t == -1) {
qDebug("invalid note head %d/%d", _headGroup, _headType);
t = noteHeads[hg][0][ht];
}
return t;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// headWidth
//
// returns the width of the note head symbol
// or the width of the string representation of the fret mark
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
qreal Note::headWidth() const
{
2013-03-07 13:31:14 +01:00
int head = noteHead();
2013-01-28 21:45:36 +01:00
qreal val = symbols[score()->symIdx()][head].width(magS());
return val;
2012-05-26 14:26:10 +02:00
}
qreal Note::tabHeadWidth(StaffTypeTablature* tab) const
{
qreal val;
if (tab && _fret != FRET_NONE && _string != STRING_NONE) {
qreal mags = magS();
QFont f = tab->fretFont();
int size = lrint(tab->fretFontSize() * MScore::DPI / PPI);
f.setPixelSize(size);
QFontMetricsF fm(f);
QString s = tab->fretString(_fret, _ghost);
val = fm.width(s) * mags;
2013-05-23 16:58:22 +02:00
}
else
val = headWidth();
return val;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// headHeight
//
// returns the height of the note head symbol
// or the height of the string representation of the fret mark
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
qreal Note::headHeight() const
{
return symbols[score()->symIdx()][noteHead()].height(magS());
}
qreal Note::tabHeadHeight(StaffTypeTablature *tab) const
{
if(tab && _fret != FRET_NONE && _string != STRING_NONE)
return tab->fretBoxH() * magS();
return headHeight();
}
2013-01-02 09:29:17 +01:00
//---------------------------------------------------------
// attach
//---------------------------------------------------------
QPointF Note::attach() const
{
return symbols[score()->symIdx()][noteHead()].attach(magS());
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// playTicks
2013-05-12 12:43:22 +02:00
/// Return total tick len of tied notes
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
int Note::playTicks() const
{
const Note* note = this;
while (note->tieBack())
note = note->tieBack()->startNote();
2013-05-12 12:43:22 +02:00
int stick = note->chord()->tick();
while (note->tieFor() && note->tieFor()->endNote())
2012-05-26 14:26:10 +02:00
note = note->tieFor()->endNote();
2013-05-12 12:43:22 +02:00
return note->chord()->tick() + note->chord()->actualTicks() - stick;
2012-05-26 14:26:10 +02:00
}
2012-09-12 16:19:03 +02:00
//---------------------------------------------------------
// addSpanner
//---------------------------------------------------------
void Note::addSpanner(Spanner* l)
{
Element* e = l->endElement();
if (e)
static_cast<Note*>(e)->addSpannerBack(l);
addSpannerFor(l);
2012-09-12 16:19:03 +02:00
}
//---------------------------------------------------------
// removeSpanner
//---------------------------------------------------------
void Note::removeSpanner(Spanner* l)
{
if (!static_cast<Note*>(l->endElement())->removeSpannerBack(l)) {
qDebug("Note::removeSpanner(%p): cannot remove spannerBack %s %p", this, l->name(), l);
2012-09-12 16:19:03 +02:00
// abort();
}
if (!removeSpannerFor(l)) {
qDebug("Note(%p): cannot remove spannerFor %s %p", this, l->name(), l);
2012-09-12 16:19:03 +02:00
// abort();
}
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// add
//---------------------------------------------------------
void Note::add(Element* e)
{
e->setParent(this);
e->setTrack(track());
2012-05-26 14:26:10 +02:00
switch(e->type()) {
case NOTEDOT:
{
NoteDot* dot = static_cast<NoteDot*>(e);
_dots[dot->idx()] = dot;
}
break;
case SYMBOL:
case IMAGE:
case FINGERING:
case TEXT:
case BEND:
2013-03-25 16:27:20 +01:00
_el.push_back(e);
2012-05-26 14:26:10 +02:00
break;
case TIE:
{
Tie* tie = static_cast<Tie*>(e);
tie->setStartNote(this);
tie->setTrack(track());
setTieFor(tie);
if (tie->endNote())
tie->endNote()->setTieBack(tie);
2013-01-30 17:50:01 +01:00
int n = tie->spannerSegments().size();
for (int i = 0; i < n; ++i) {
SpannerSegment* ss = tie->spannerSegments().at(i);
2012-05-26 14:26:10 +02:00
if (ss->system())
ss->system()->add(ss);
}
}
break;
case ACCIDENTAL:
_accidental = static_cast<Accidental*>(e);
break;
2012-09-12 16:19:03 +02:00
case TEXTLINE:
addSpanner(static_cast<Spanner*>(e));
break;
2012-05-26 14:26:10 +02:00
default:
qDebug("Note::add() not impl. %s\n", e->name());
break;
}
}
//---------------------------------------------------------
// remove
//---------------------------------------------------------
void Note::remove(Element* e)
{
switch(e->type()) {
case NOTEDOT:
for (int i = 0; i < 3; ++i) {
if (_dots[i] == e) {
_dots[i] = 0;
break;
}
}
break;
case TEXT:
case SYMBOL:
case IMAGE:
case FINGERING:
case BEND:
if (!_el.remove(e))
qDebug("Note::remove(): cannot find %s\n", e->name());
break;
case TIE:
{
Tie* tie = static_cast<Tie*>(e);
setTieFor(0);
if (tie->endNote())
tie->endNote()->setTieBack(0);
2013-01-30 17:50:01 +01:00
int n = tie->spannerSegments().size();
for (int i = 0; i < n; ++i) {
SpannerSegment* ss = tie->spannerSegments().at(i);
2012-05-26 14:26:10 +02:00
Q_ASSERT(ss->spanner() == tie);
if (ss->system())
ss->system()->remove(ss);
}
}
break;
case ACCIDENTAL:
_accidental = 0;
break;
2012-09-12 16:19:03 +02:00
case TEXTLINE:
removeSpanner(static_cast<Spanner*>(e));
break;
2012-05-26 14:26:10 +02:00
default:
qDebug("Note::remove() not impl. %s\n", e->name());
break;
}
}
//---------------------------------------------------------
// draw
//---------------------------------------------------------
void Note::draw(QPainter* painter) const
{
if (_hidden)
return;
2013-02-20 17:53:15 +01:00
2013-03-07 21:00:22 +01:00
QColor c(curColor());
2013-02-20 17:53:15 +01:00
painter->setPen(c);
bool tablature = staff() && staff()->isTabStaff();
// tablature
if (tablature) {
StaffTypeTablature* tab = (StaffTypeTablature*)staff()->staffType();
if (tieBack() && tab->slashStyle())
return;
QString s = tab->fretString(_fret, _ghost);
// draw background, if required
if (!tab->linesThrough() || fretConflict()) {
qreal d = spatium() * .1;
QRectF bb = QRectF(bbox().x()-d, tab->fretMaskY(), bbox().width() + 2*d, tab->fretMaskH());
// we do not know which viewer did this draw() call
// so update all:
foreach(MuseScoreView* view, score()->getViewer())
view->drawBackground(painter, bb);
if (fretConflict()) { //on fret conflict, draw on red background
painter->save();
painter->setPen(Qt::red);
painter->setBrush(QBrush(QColor(Qt::red)));
painter->drawRect(bb);
painter->restore();
2012-05-26 14:26:10 +02:00
}
}
qreal mag = magS();
qreal imag = 1.0 / mag;
painter->scale(mag, mag);
painter->setFont(tab->fretFont());
2013-03-07 21:00:22 +01:00
painter->setPen(c);
painter->drawText(QPointF(bbox().x(), tab->fretFontYOffset()), s);
painter->scale(imag, imag);
}
// NOT tablature
else {
2013-05-12 12:51:42 +02:00
// skip drawing, if second note of a cross-measure value
2013-07-15 08:15:20 +02:00
if (chord()->crossMeasure() == CROSSMEASURE_SECOND)
2013-05-12 12:51:42 +02:00
return;
//
// warn if pitch extends usable range of instrument
// by coloring the note head
//
if (chord() && chord()->segment() && staff() && !selected()
&& !score()->printing() && MScore::warnPitchRange) {
const Instrument* in = staff()->part()->instr();
int i = ppitch();
if (i < in->minPitchP() || i > in->maxPitchP())
painter->setPen(Qt::red);
else if (i < in->minPitchA() || i > in->maxPitchA())
painter->setPen(Qt::darkYellow);
2012-05-26 14:26:10 +02:00
}
2013-01-02 09:29:17 +01:00
symbols[score()->symIdx()][noteHead()].draw(painter, magS());
2012-05-26 14:26:10 +02:00
}
}
//--------------------------------------------------
// Note::write
//---------------------------------------------------------
void Note::write(Xml& xml) const
{
xml.stag("Note");
Element::writeProperties(xml);
//
// get real pitch for clipboard (copy/paste)
//
int rpitch = pitch();
int rtpc = tpc();
if (staff()) {
const Interval& interval = staff()->part()->instr()->transpose();
if (xml.clipboardmode && !score()->styleB(ST_concertPitch) && interval.chromatic)
transposeInterval(rpitch, rtpc, &_pitch, &_tpc, interval, true);
}
if (_accidental)
_accidental->write(xml);
_el.write(xml);
2012-09-14 10:09:06 +02:00
int dots = chord() ? chord()->dots() : 0;
2012-05-26 14:26:10 +02:00
if (dots) {
bool hasUserModifiedDots = false;
for (int i = 0; i < dots; ++i) {
if (_dots[i] && (!_dots[i]->userOff().isNull() || !_dots[i]->visible()
|| _dots[i]->color() != Qt::black)) {
hasUserModifiedDots = true;
break;
}
}
if (hasUserModifiedDots) {
for (int i = 0; i < dots; ++i)
_dots[i]->write(xml);
}
}
if (_tieFor) {
_tieFor->setId(++xml.spannerId);
2012-05-26 14:26:10 +02:00
_tieFor->write(xml);
}
if (_tieBack)
xml.tagE(QString("endSpanner id=\"%1\"").arg(_tieBack->id()));
2012-11-22 10:47:00 +01:00
if (chord() == 0 || chord()->userPlayEvents()) {
2012-11-19 09:29:46 +01:00
if (!_playEvents.isEmpty()) {
xml.stag("Events");
foreach(const NoteEvent& e, _playEvents)
e.write(xml);
xml.etag();
}
2012-05-26 14:26:10 +02:00
}
2012-08-10 17:01:35 +02:00
writeProperty(xml, P_PITCH);
writeProperty(xml, P_TPC);
writeProperty(xml, P_SMALL);
writeProperty(xml, P_MIRROR_HEAD);
writeProperty(xml, P_DOT_POSITION);
writeProperty(xml, P_HEAD_GROUP);
writeProperty(xml, P_VELO_OFFSET);
writeProperty(xml, P_PLAY);
2012-08-10 17:01:35 +02:00
writeProperty(xml, P_TUNING);
writeProperty(xml, P_FRET);
writeProperty(xml, P_STRING);
writeProperty(xml, P_GHOST);
writeProperty(xml, P_HEAD_TYPE);
writeProperty(xml, P_VELO_TYPE);
2013-06-10 11:03:34 +02:00
foreach (Spanner* e, _spannerFor) {
2012-09-12 16:19:03 +02:00
e->setId(++xml.spannerId);
e->write(xml);
}
2013-06-10 11:03:34 +02:00
foreach (Spanner* e, _spannerBack)
2012-09-12 16:19:03 +02:00
xml.tagE(QString("endSpanner id=\"%1\"").arg(e->id()));
2012-05-26 14:26:10 +02:00
_pitch = rpitch;
_tpc = rtpc;
xml.etag();
}
//---------------------------------------------------------
// Note::read
//---------------------------------------------------------
2013-01-11 18:10:18 +01:00
void Note::read(XmlReader& e)
2012-05-26 14:26:10 +02:00
{
bool hasAccidental = false; // used for userAccidental backward compatibility
_tpc = INVALID_TPC;
2013-01-11 18:10:18 +01:00
if (e.hasAttribute("pitch")) // obsolete
_pitch = e.intAttribute("pitch");
if (e.hasAttribute("tpc")) // obsolete
_tpc = e.intAttribute("tpc");
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-08-10 17:01:35 +02:00
if (tag == "pitch")
2013-01-11 18:10:18 +01:00
_pitch = e.readInt();
2012-08-10 17:01:35 +02:00
else if (tag == "tpc")
2013-01-11 18:10:18 +01:00
_tpc = e.readInt();
2012-08-10 17:01:35 +02:00
else if (tag == "small")
2013-01-11 18:10:18 +01:00
setSmall(e.readInt());
2012-08-10 17:01:35 +02:00
else if (tag == "mirror")
2013-05-13 18:49:17 +02:00
setProperty(P_MIRROR_HEAD, Ms::getProperty(P_MIRROR_HEAD, e));
2012-08-10 17:01:35 +02:00
else if (tag == "dotPosition")
2013-05-13 18:49:17 +02:00
setProperty(P_DOT_POSITION, Ms::getProperty(P_DOT_POSITION, e));
2012-08-10 17:01:35 +02:00
else if (tag == "onTimeOffset")
2013-01-22 21:53:45 +01:00
e.skipCurrentElement(); // TODO setOnTimeUserOffset(val.toInt());
2012-08-10 17:01:35 +02:00
else if (tag == "offTimeOffset")
2013-01-22 21:53:45 +01:00
e.skipCurrentElement(); // TODO setOffTimeUserOffset(val.toInt());
2012-08-10 17:01:35 +02:00
else if (tag == "head")
2013-05-13 18:49:17 +02:00
setProperty(P_HEAD_GROUP, Ms::getProperty(P_HEAD_GROUP, e));
2012-08-10 17:01:35 +02:00
else if (tag == "velocity")
2013-01-11 18:10:18 +01:00
setVeloOffset(e.readInt());
else if (tag == "play")
setPlay(e.readInt());
2012-08-10 17:01:35 +02:00
else if (tag == "tuning")
2013-01-11 18:10:18 +01:00
setTuning(e.readDouble());
2012-08-10 17:01:35 +02:00
else if (tag == "fret")
2013-01-11 18:10:18 +01:00
setFret(e.readInt());
2012-08-10 17:01:35 +02:00
else if (tag == "string")
2013-01-11 18:10:18 +01:00
setString(e.readInt());
2012-08-10 17:01:35 +02:00
else if (tag == "ghost")
2013-01-11 18:10:18 +01:00
setGhost(e.readInt());
2012-08-10 17:01:35 +02:00
else if (tag == "headType")
if (score()->mscVersion() <= 114)
setProperty(P_HEAD_TYPE, Ms::getProperty(P_HEAD_TYPE, e).toInt() - 1);
else
setProperty(P_HEAD_TYPE, Ms::getProperty(P_HEAD_TYPE, e).toInt());
2012-08-10 17:01:35 +02:00
else if (tag == "veloType")
2013-05-13 18:49:17 +02:00
setProperty(P_VELO_TYPE, Ms::getProperty(P_VELO_TYPE, e));
2012-05-26 14:26:10 +02:00
else if (tag == "line")
2013-01-11 18:10:18 +01:00
_line = e.readInt();
2012-05-26 14:26:10 +02:00
else if (tag == "Tie") {
_tieFor = new Tie(score());
_tieFor->setTrack(track());
_tieFor->read(e);
_tieFor->setStartNote(this);
score()->addSpanner(_tieFor);
2012-05-26 14:26:10 +02:00
}
else if (tag == "Fingering" || tag == "Text") { // Text is obsolete
Fingering* f = new Fingering(score());
f->setTextStyleType(TEXT_STYLE_FINGERING);
2012-05-26 14:26:10 +02:00
f->read(e);
add(f);
}
else if (tag == "Symbol") {
Symbol* s = new Symbol(score());
s->setTrack(track());
s->read(e);
add(s);
}
else if (tag == "Image") {
2013-01-11 18:10:18 +01:00
Image* image = new Image(score());
image->setTrack(track());
image->read(e);
add(image);
2012-05-26 14:26:10 +02:00
}
else if (tag == "userAccidental") {
2013-01-11 18:10:18 +01:00
QString val(e.readElementText());
2012-05-26 14:26:10 +02:00
bool ok;
int k = val.toInt(&ok);
if (ok) {
// on older scores, a note could have both a <userAccidental> tag and an <Accidental> tag
// if a userAccidental has some other property set (like for instance offset)
// only construct a new accidental, if the other tag has not been read yet
2012-05-26 14:26:10 +02:00
// (<userAccidental> tag is only used in older scores: no need to check the score mscVersion)
if (!hasAccidental) {
_accidental = new Accidental(score());
_accidental->setParent(this);
}
// TODO: for backward compatibility
bool bracket = k & 0x8000;
k &= 0xfff;
Accidental::AccidentalType at = Accidental::ACC_NONE;
2012-05-26 14:26:10 +02:00
switch(k) {
case 0: at = Accidental::ACC_NONE; break;
case 1: at = Accidental::ACC_SHARP; break;
case 2: at = Accidental::ACC_FLAT; break;
case 3: at = Accidental::ACC_SHARP2; break;
case 4: at = Accidental::ACC_FLAT2; break;
case 5: at = Accidental::ACC_NATURAL; break;
case 6: at = Accidental::ACC_FLAT_SLASH; break;
case 7: at = Accidental::ACC_FLAT_SLASH2; break;
case 8: at = Accidental::ACC_MIRRORED_FLAT2; break;
case 9: at = Accidental::ACC_MIRRORED_FLAT; break;
case 10: at = Accidental::ACC_MIRRIRED_FLAT_SLASH; break;
case 11: at = Accidental::ACC_FLAT_FLAT_SLASH; break;
case 12: at = Accidental::ACC_SHARP_SLASH; break;
case 13: at = Accidental::ACC_SHARP_SLASH2; break;
case 14: at = Accidental::ACC_SHARP_SLASH3; break;
case 15: at = Accidental::ACC_SHARP_SLASH4; break;
case 16: at = Accidental::ACC_SHARP_ARROW_UP; break;
case 17: at = Accidental::ACC_SHARP_ARROW_DOWN; break;
case 18: at = Accidental::ACC_SHARP_ARROW_BOTH; break;
case 19: at = Accidental::ACC_FLAT_ARROW_UP; break;
case 20: at = Accidental::ACC_FLAT_ARROW_DOWN; break;
case 21: at = Accidental::ACC_FLAT_ARROW_BOTH; break;
case 22: at = Accidental::ACC_NATURAL_ARROW_UP; break;
case 23: at = Accidental::ACC_NATURAL_ARROW_DOWN; break;
case 24: at = Accidental::ACC_NATURAL_ARROW_BOTH; break;
case 25: at = Accidental::ACC_SORI; break;
case 26: at = Accidental::ACC_KORON; break;
2012-05-26 14:26:10 +02:00
}
_accidental->setAccidentalType(at);
2012-05-26 14:26:10 +02:00
_accidental->setHasBracket(bracket);
_accidental->setRole(Accidental::ACC_USER);
2012-05-26 14:26:10 +02:00
hasAccidental = true; // we now have an accidental
}
}
else if (tag == "Accidental") {
// on older scores, a note could have both a <userAccidental> tag and an <Accidental> tag
// if a userAccidental has some other property set (like for instance offset)
Accidental* a;
if (hasAccidental) // if the other tag has already been read,
a = _accidental; // re-use the accidental it constructed
else
a = new Accidental(score());
// the accidental needs to know the properties of the
// track it belongs to (??)
a->setTrack(track());
a->read(e);
if (!hasAccidental) // only the new accidental, if it has been added previously
add(a);
if (score()->mscVersion() < 117)
hasAccidental = true; // we now have an accidental
}
else if (tag == "move") // obsolete
2013-01-11 18:10:18 +01:00
chord()->setStaffMove(e.readInt());
2012-05-26 14:26:10 +02:00
else if (tag == "Bend") {
Bend* b = new Bend(score());
b->setTrack(track());
b->read(e);
add(b);
}
else if (tag == "NoteDot") {
NoteDot* dot = new NoteDot(score());
dot->read(e);
for (int i = 0; i < 3; ++i) {
if (_dots[i] == 0) {
dot->setIdx(i);
add(dot);
dot = 0;
break;
}
}
if (dot) {
qDebug("Note: too many dots\n");
delete dot;
}
}
else if (tag == "Events") {
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 == "Event") {
2012-11-19 09:29:46 +01:00
NoteEvent ne;
2013-01-11 18:10:18 +01:00
ne.read(e);
2012-05-26 14:26:10 +02:00
_playEvents.append(ne);
}
else
2013-01-11 18:10:18 +01:00
e.unknown();
2012-05-26 14:26:10 +02:00
}
2012-11-22 10:47:00 +01:00
if (chord())
chord()->setUserPlayEvents(true);
2012-05-26 14:26:10 +02:00
}
2012-09-12 16:19:03 +02:00
else if (tag == "endSpanner") {
2013-01-11 18:10:18 +01:00
int id = e.intAttribute("id");
2013-07-04 13:40:25 +02:00
Spanner* sp = score()->findSpanner(id);
if (sp) {
sp->setEndElement(this);
if (sp->type() == TIE)
_tieBack = static_cast<Tie*>(sp);
else
addSpannerBack(sp);
score()->removeSpanner(sp);
2012-09-12 16:19:03 +02:00
}
else
qDebug("Note::read(): cannot find spanner %d", id);
2013-07-04 13:40:25 +02:00
e.readNext();
2012-09-12 16:19:03 +02:00
}
else if (tag == "TextLine") {
2013-01-18 10:55:52 +01:00
Spanner* sp = static_cast<Spanner*>(Element::name2Element(tag, score()));
2012-09-12 16:19:03 +02:00
sp->setTrack(track());
sp->read(e);
sp->setAnchor(Spanner::ANCHOR_NOTE);
sp->setStartElement(this);
2013-07-04 13:40:25 +02:00
sp->setTick(e.tick());
addSpannerFor(sp);
2012-09-12 16:19:03 +02:00
sp->setParent(this);
2013-07-04 13:40:25 +02:00
score()->addSpanner(sp);
2012-09-12 16:19:03 +02:00
}
2012-05-26 14:26:10 +02:00
else if (tag == "onTimeType") // obsolete
2013-01-22 21:53:45 +01:00
e.skipCurrentElement(); // _onTimeType = readValueType(e);
2012-05-26 14:26:10 +02:00
else if (tag == "offTimeType") // obsolete
2013-01-22 21:53:45 +01:00
e.skipCurrentElement(); // _offTimeType = readValueType(e);
2012-05-26 14:26:10 +02:00
else if (tag == "tick") // bad input file
2013-01-22 21:53:45 +01:00
e.skipCurrentElement();
2012-05-26 14:26:10 +02:00
else if (Element::readProperties(e))
;
else
2013-01-11 18:10:18 +01:00
e.unknown();
2012-05-26 14:26:10 +02:00
}
// ensure sane values:
_pitch = restrict(_pitch, 0, 127);
if (!tpcIsValid(_tpc))
setTpcFromPitch();
}
//---------------------------------------------------------
// drag
//---------------------------------------------------------
2013-10-22 12:05:31 +02:00
QRectF Note::drag(EditData* data)
2012-05-26 14:26:10 +02:00
{
2013-03-22 18:30:05 +01:00
if (staff()->isDrumStaff())
return QRect();
2012-05-26 14:26:10 +02:00
dragMode = true;
QRectF bb(chord()->bbox());
qreal _spatium = spatium();
bool tab = staff()->isTabStaff();
2012-05-26 14:26:10 +02:00
qreal step = _spatium * (tab ? staff()->staffType()->lineDistance().val() : 0.5);
2013-10-22 12:05:31 +02:00
_lineOffset = lrint(data->pos.y() / step);
2013-06-05 15:47:34 +02:00
score()->setLayoutAll(true);
2012-05-26 14:26:10 +02:00
return bb.translated(chord()->pagePos());
}
//---------------------------------------------------------
// endDrag
//---------------------------------------------------------
void Note::endDrag()
{
dragMode = false;
if (_lineOffset == 0)
return;
int nLine;
int staffIdx = chord()->staffIdx() + chord()->staffMove();
Staff* staff = score()->staff(staffIdx);
int nPitch;
int tpc;
int nString;
int nFret;
if (staff->isTabStaff()) {
2012-05-26 14:26:10 +02:00
// on TABLATURE staves, dragging a note keeps same pitch on a different string (if possible)
// determine new string of dragged note (if tablature is upside down, invert _lineOffset)
nString = _string + (static_cast<StaffTypeTablature*>(staff->staffType())->upsideDown() ?
-_lineOffset : _lineOffset);
_lineOffset = 0;
// get a fret number for same pitch on new string
nFret = staff->part()->instr()->stringData()->fret(_pitch, nString);
2012-08-06 09:29:11 +02:00
if (nFret < 0) // no fret?
2012-05-26 14:26:10 +02:00
return; // no party!
2012-08-06 09:29:11 +02:00
score()->undoChangeProperty(this, P_FRET, nFret);
score()->undoChangeProperty(this, P_STRING, nString);
2012-05-26 14:26:10 +02:00
}
else {
// on PITCHED / PERCUSSION staves, dragging a note changes the note pitch
nLine = _line + _lineOffset;
_lineOffset = 0;
// get note context
int tick = chord()->tick();
2013-09-05 16:37:49 +02:00
ClefType clef = staff->clef(tick);
2012-05-26 14:26:10 +02:00
int key = staff->key(tick).accidentalType();
// determine new pitch of dragged note
nPitch = line2pitch(nLine, clef, key);
tpc = pitch2tpc(nPitch, key, PREFER_NEAREST);
2012-05-26 14:26:10 +02:00
// undefined for non-tablature staves
nString = -1;
nFret = -1;
// }
Note* n = this;
while (n->tieBack())
n = n->tieBack()->startNote();
for (; n; n = n->tieFor() ? n->tieFor()->endNote() : 0)
score()->undoChangePitch(n, nPitch, tpc, nLine/*, nFret, nString*/);
}
score()->select(this, SELECT_SINGLE, 0);
}
//---------------------------------------------------------
// acceptDrop
//---------------------------------------------------------
bool Note::acceptDrop(MuseScoreView*, const QPointF&, Element* e) const
{
int type = e->type();
return (type == ARTICULATION
|| type == CHORDLINE
|| type == TEXT
|| type == REHEARSAL_MARK
|| type == FINGERING
|| type == ACCIDENTAL
|| type == BREATH
|| type == ARPEGGIO
|| type == NOTEHEAD
|| type == NOTE
|| type == TREMOLO
|| type == STAFF_STATE
|| type == INSTRUMENT_CHANGE
|| type == IMAGE
|| type == CHORD
|| type == HARMONY
|| type == DYNAMIC
|| (noteType() == NOTE_NORMAL && type == ICON && static_cast<Icon*>(e)->iconType() == ICON_ACCIACCATURA)
|| (noteType() == NOTE_NORMAL && type == ICON && static_cast<Icon*>(e)->iconType() == ICON_APPOGGIATURA)
|| (noteType() == NOTE_NORMAL && type == ICON && static_cast<Icon*>(e)->iconType() == ICON_GRACE4)
|| (noteType() == NOTE_NORMAL && type == ICON && static_cast<Icon*>(e)->iconType() == ICON_GRACE8B)
|| (noteType() == NOTE_NORMAL && type == ICON && static_cast<Icon*>(e)->iconType() == ICON_GRACE16)
|| (noteType() == NOTE_NORMAL && type == ICON && static_cast<Icon*>(e)->iconType() == ICON_GRACE32)
2013-08-01 22:24:36 +02:00
|| (noteType() == NOTE_NORMAL && type == BAGPIPE_EMBELLISHMENT)
|| (type == ICON && static_cast<Icon*>(e)->iconType() == ICON_SBEAM)
|| (type == ICON && static_cast<Icon*>(e)->iconType() == ICON_MBEAM)
|| (type == ICON && static_cast<Icon*>(e)->iconType() == ICON_NBEAM)
|| (type == ICON && static_cast<Icon*>(e)->iconType() == ICON_BEAM32)
|| (type == ICON && static_cast<Icon*>(e)->iconType() == ICON_BEAM64)
|| (type == ICON && static_cast<Icon*>(e)->iconType() == ICON_AUTOBEAM)
|| (type == ICON && static_cast<Icon*>(e)->iconType() == ICON_BRACKETS)
2012-05-26 14:26:10 +02:00
|| (type == SYMBOL)
|| (type == CLEF)
|| (type == BAR_LINE)
|| (type == GLISSANDO)
|| (type == SLUR)
|| (type == STAFF_TEXT)
|| (type == TEMPO_TEXT)
|| (type == BEND && (staff()->isTabStaff()))
|| (type == FRET_DIAGRAM));
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// drop
//---------------------------------------------------------
Element* Note::drop(const DropData& data)
{
Element* e = data.element;
Chord* ch = chord();
switch(e->type()) {
case REHEARSAL_MARK:
return ch->drop(data);
case SYMBOL:
case IMAGE:
2012-10-25 16:21:07 +02:00
e->setParent(this);
score()->undoAddElement(e);
return e;
2012-05-26 14:26:10 +02:00
case FINGERING:
e->setParent(this);
score()->undoAddElement(e);
2012-10-25 16:21:07 +02:00
{
// set style
Fingering* f = static_cast<Fingering*>(e);
int st = f->textStyleType();
if (st != TEXT_STYLE_UNKNOWN)
f->setTextStyleType(st);
2012-10-25 16:21:07 +02:00
}
2012-05-26 14:26:10 +02:00
return e;
case SLUR:
delete e;
data.view->cmdAddSlur(this, 0);
return 0;
case LYRICS:
e->setParent(ch);
e->setTrack(track());
2012-05-26 14:26:10 +02:00
score()->undoAddElement(e);
return e;
case ACCIDENTAL:
score()->changeAccidental(this, static_cast<Accidental*>(e)->accidentalType());
2012-05-26 14:26:10 +02:00
break;
case BEND:
{
Bend* b = static_cast<Bend*>(e);
b->setParent(this);
b->setTrack(track());
score()->undoAddElement(b);
}
return e;
case NOTEHEAD:
{
Symbol* s = (Symbol*)e;
NoteHeadGroup group = HEAD_INVALID;
for (int i = 0; i < HEAD_GROUPS; ++i) {
if (noteHeads[0][i][1] == s->sym() || noteHeads[0][i][3] == s->sym()) {
group = (NoteHeadGroup)i;
break;
}
}
if (group == HEAD_INVALID) {
qDebug("unknown note head\n");
group = HEAD_NORMAL;
}
delete s;
if (group != _headGroup) {
if (links()) {
foreach(Element* e, *links()) {
e->score()->undoChangeProperty(e, P_HEAD_GROUP, group);
Note* note = static_cast<Note*>(e);
if (note->staff() && note->staff()->isTabStaff()
2012-05-26 14:26:10 +02:00
&& group == HEAD_CROSS) {
e->score()->undoChangeProperty(e, P_GHOST, true);
}
}
}
else
score()->undoChangeProperty(this, P_HEAD_GROUP, group);
score()->select(this);
}
}
break;
case ICON:
{
switch(static_cast<Icon*>(e)->iconType()) {
2012-05-26 14:26:10 +02:00
case ICON_ACCIACCATURA:
score()->setGraceNote(ch, pitch(), NOTE_ACCIACCATURA, false, MScore::division/2);
break;
case ICON_APPOGGIATURA:
score()->setGraceNote(ch, pitch(), NOTE_APPOGGIATURA, false, MScore::division/2);
break;
case ICON_GRACE4:
score()->setGraceNote(ch, pitch(), NOTE_GRACE4, false, MScore::division);
break;
case ICON_GRACE8B:
score()->setGraceNote(ch, pitch(), NOTE_APPOGGIATURA, true, MScore::division/2);
break;
case ICON_GRACE16:
score()->setGraceNote(ch, pitch(), NOTE_GRACE16, false, MScore::division/4);
break;
case ICON_GRACE32:
score()->setGraceNote(ch, pitch(), NOTE_GRACE32, false, MScore::division/8);
break;
case ICON_SBEAM:
case ICON_MBEAM:
case ICON_NBEAM:
case ICON_BEAM32:
case ICON_BEAM64:
case ICON_AUTOBEAM:
return ch->drop(data);
break;
case ICON_BRACKETS:
{
Symbol* s = new Symbol(score(), leftparenSym);
s->setParent(this);
score()->undoAddElement(s);
s = new Symbol(score(), rightparenSym);
s->setParent(this);
score()->undoAddElement(s);
}
break;
2012-05-26 14:26:10 +02:00
}
}
delete e;
break;
2013-08-01 22:24:36 +02:00
case BAGPIPE_EMBELLISHMENT:
{
2013-08-04 10:35:46 +02:00
BagpipeEmbellishment* b = static_cast<BagpipeEmbellishment*>(e);
noteList nl = b->getNoteList();
// add grace notes in reverse order, as setGraceNote adds a grace note
// before the current note
for (int i = nl.size() - 1; i >= 0; --i) {
int p = BagpipeEmbellishment::BagpipeNoteInfoList[nl.at(i)].pitch;
score()->setGraceNote(ch, p, NOTE_GRACE32, false, MScore::division/8);
2013-08-01 22:24:36 +02:00
}
}
delete e;
break;
2012-05-26 14:26:10 +02:00
case NOTE:
{
Chord* ch = chord();
if (ch->noteType() != NOTE_NORMAL) {
delete e;
return 0;
}
e->setParent(ch);
score()->undoRemoveElement(this);
score()->undoAddElement(e);
}
break;
case GLISSANDO:
{
Segment* s = ch->segment();
s = s->next1();
while (s) {
2013-06-12 14:23:57 +02:00
if ((s->segmentType() == Segment::SegChordRest) && s->element(track()))
2012-05-26 14:26:10 +02:00
break;
s = s->next1();
}
if (s == 0) {
qDebug("no segment for second note of glissando found\n");
delete e;
return 0;
}
ChordRest* cr1 = static_cast<ChordRest*>(s->element(track()));
if (cr1 == 0 || cr1->type() != CHORD) {
qDebug("no second note for glissando found, track %d\n", track());
delete e;
return 0;
}
e->setTrack(track());
e->setParent(cr1);
// in TAB, use straight line with no text
if (staff()->isTabStaff()) {
(static_cast<Glissando*>(e))->setGlissandoType(GlissandoType::STRAIGHT);
(static_cast<Glissando*>(e))->setShowText(false);
2013-02-28 15:06:54 +01:00
}
2012-05-26 14:26:10 +02:00
score()->undoAddElement(e);
}
break;
case CHORD:
{
Chord* c = static_cast<Chord*>(e);
Note* n = c->upNote();
2012-08-04 15:46:43 +02:00
MScore::Direction dir = c->stemDirection();
2013-01-02 20:13:58 +01:00
int t = (staff2track(staffIdx()) + n->voice());
2012-05-26 14:26:10 +02:00
score()->select(0, SELECT_SINGLE, 0);
NoteVal nval;
nval.pitch = n->pitch();
nval.headGroup = n->headGroup();
Segment* seg = score()->setNoteRest(chord()->segment(), t, nval,
score()->inputState().duration().fraction(), dir);
ChordRest* cr = static_cast<ChordRest*>(seg->element(t));
if (cr)
score()->nextInputPos(cr, true);
delete e;
}
break;
default:
return ch->drop(data);
}
return 0;
}
//---------------------------------------------------------
// layout
//---------------------------------------------------------
void Note::layout()
{
bool useTablature = staff() && staff()->isTabStaff();
2012-05-26 14:26:10 +02:00
if (useTablature) {
StaffTypeTablature* tab = (StaffTypeTablature*)staff()->staffType();
qreal mags = magS();
qreal w = tabHeadWidth(tab);
bbox().setRect(0.0, tab->fretBoxY() * mags, w, tab->fretBoxH() * mags);
2012-05-26 14:26:10 +02:00
}
2012-08-01 18:00:27 +02:00
else {
2012-05-26 14:26:10 +02:00
setbbox(symbols[score()->symIdx()][noteHead()].bbox(magS()));
2012-08-01 18:00:27 +02:00
if (parent() == 0)
return;
}
// if not TAB or TAB with stems through
if (!useTablature || ((StaffTypeTablature*)staff()->staffType())->stemThrough()) {
2012-05-26 14:26:10 +02:00
int dots = chord()->dots();
for (int i = 0; i < 3; ++i) {
if (i < dots) {
if (_dots[i] == 0) {
NoteDot* dot = new NoteDot(score());
dot->setIdx(i);
dot->setParent(this);
dot->setTrack(track()); // needed to know the staff it belongs to (and detect tablature)
score()->undoAddElement(dot); // move dot to _dots[i]
}
_dots[i]->layout();
}
else if (_dots[i])
score()->undoRemoveElement(_dots[i]);
}
}
}
//---------------------------------------------------------
// layout2
// called after final position of note is set
//---------------------------------------------------------
void Note::layout2()
{
adjustReadPos();
int dots = chord()->dots();
if (dots) {
qreal d = point(score()->styleS(ST_dotNoteDistance));
qreal dd = point(score()->styleS(ST_dotDotDistance));
qreal y = 0.0;
qreal x = chord()->dotPosX() - pos().x() - chord()->pos().x();
bool onLine = false;
2012-05-26 14:26:10 +02:00
// do not draw dots on staff line:
// if TAB and stems through staff
if (staff()->isTabStaff()) {
// with TAB's, dotPosX is not set:
// get dot X from width of fret text and use TAB default spacing
StaffTypeTablature* tab = static_cast<StaffTypeTablature*>( staff()->staffType());
x = width();
dd = STAFFTYPE_TAB_DEFAULTDOTDIST_X * spatium();
d = dd * 0.5;
if(tab->stemThrough() ) {
// if fret mark on lines, use standard processing
if (tab->onLines())
onLine = true;
else
// if fret marks above lines, raise the dots by half line distance
y = -staff()->lineDistance() * 0.5;
}
// if stems beside staff, do nothing
else
return;
}
// if not TAB, look at note line
2013-05-29 10:31:26 +02:00
else
onLine = (_line & 1) == 0;
// if on line, displace dots by half spatium up or down according to voice
if (onLine) {
2012-08-04 15:46:43 +02:00
if (_dotPosition == MScore::AUTO)
y = (voice() & 1) == 0 ? -0.5 : 0.5;
2012-08-04 15:46:43 +02:00
else if (_dotPosition == MScore::UP)
y = -0.5;
2012-05-26 14:26:10 +02:00
else
y = 0.5;
2012-05-26 14:26:10 +02:00
}
y *= spatium();
// apply to dots
2012-05-26 14:26:10 +02:00
for (int i = 0; i < dots; ++i) {
NoteDot* dot = _dots[i];
if (dot) {
dot->setPos(x + d + dd * i, y);
_dots[i]->adjustReadPos();
}
}
}
// layout elements attached to note
2013-03-25 16:27:20 +01:00
for (Element* e : _el) {
2012-05-26 14:26:10 +02:00
if (!score()->tagIsValid(e->tag()))
continue;
e->setMag(mag());
e->layout();
if (e->type() == SYMBOL && static_cast<Symbol*>(e)->sym() == rightparenSym) {
qreal w = headWidth();
if (staff()->isTabStaff()) {
StaffTypeTablature* tab = (StaffTypeTablature*)staff()->staffType();
w = tabHeadWidth(tab);
}
e->setPos(w, 0.0);
}
2012-05-26 14:26:10 +02:00
}
}
//---------------------------------------------------------
// dotIsUp
//---------------------------------------------------------
bool Note::dotIsUp() const
{
if (_dots[0] == 0)
return true;
return _dots[0]->y() < spatium() * .1;
}
//---------------------------------------------------------
// layout10
// compute actual accidental and line
//---------------------------------------------------------
void Note::layout10(AccidentalState* as)
{
if (staff()->isTabStaff()) {
2012-05-26 14:26:10 +02:00
if (_accidental) {
delete _accidental;
_accidental = 0;
}
if (_fret < 0) {
int string, fret;
StringData* stringData = staff()->part()->instr()->stringData();
if (stringData->convertPitch(_pitch, &string, &fret)) {
2012-05-26 14:26:10 +02:00
_fret = fret;
_string = string;
}
}
}
else {
2012-08-07 16:05:37 +02:00
_line = absStep(_tpc, _pitch);
2012-05-26 14:26:10 +02:00
// calculate accidental
Accidental::AccidentalType acci = Accidental::ACC_NONE;
if (_accidental && _accidental->role() == Accidental::ACC_USER) {
acci = _accidental->accidentalType();
if (acci == Accidental::ACC_SHARP || acci == Accidental::ACC_FLAT) {
// TODO - what about double flat and double sharp?
KeySigEvent key = (staff() && chord()) ? staff()->key(chord()->tick()) : KeySigEvent();
int ntpc = pitch2tpc(_pitch, key.accidentalType(), acci == Accidental::ACC_SHARP ? PREFER_SHARPS : PREFER_FLATS);
2012-05-26 14:26:10 +02:00
if (ntpc != _tpc) {
qDebug("note has wrong tpc: %d, expected %d", _tpc, ntpc);
2012-09-11 21:10:44 +02:00
// setColor(QColor(255, 0, 0));
2012-05-26 14:26:10 +02:00
_tpc = ntpc;
2012-08-07 16:05:37 +02:00
_line = absStep(_tpc, _pitch);
2012-05-26 14:26:10 +02:00
}
}
}
else {
2012-08-08 20:46:29 +02:00
AccidentalVal accVal = tpc2alter(_tpc);
2012-05-26 14:26:10 +02:00
if ((accVal != as->accidentalVal(int(_line))) || hidden() || as->tieContext(int(_line))) {
as->setAccidentalVal(int(_line), accVal, _tieBack != 0);
if (!_tieBack) {
acci = Accidental::value2subtype(accVal);
if (acci == Accidental::ACC_NONE)
acci = Accidental::ACC_NATURAL;
2012-05-26 14:26:10 +02:00
}
}
}
if (acci != Accidental::ACC_NONE && !_tieBack && !_hidden) {
2012-05-26 14:26:10 +02:00
if (_accidental == 0) {
_accidental = new Accidental(score());
_accidental->setGenerated(true);
add(_accidental);
2012-05-26 14:26:10 +02:00
}
_accidental->setAccidentalType(acci);
2012-05-26 14:26:10 +02:00
}
else {
if (_accidental) {
if (_accidental->selected()) {
score()->deselect(_accidental);
}
delete _accidental;
_accidental = 0;
}
}
//
// calculate the real note line depending on clef
//
Staff* s = score()->staff(staffIdx() + chord()->staffMove());
int tick = chord()->tick();
_line = relStep(_line, s->clef(tick));
2012-05-26 14:26:10 +02:00
}
}
//---------------------------------------------------------
// noteType
//---------------------------------------------------------
NoteType Note::noteType() const
{
return chord()->noteType();
}
//---------------------------------------------------------
// pagePos
//---------------------------------------------------------
QPointF Note::pagePos() const
{
if (parent() == 0)
return pos();
return parent()->pagePos() + pos();
}
//---------------------------------------------------------
// canvasPos
//---------------------------------------------------------
QPointF Note::canvasPos() const
{
if (parent() == 0)
return pos();
return parent()->canvasPos() + pos();
}
//---------------------------------------------------------
// scanElements
//---------------------------------------------------------
void Note::scanElements(void* data, void (*func)(void*, Element*), bool all)
{
func(data, this);
// tie segments are collected from System
// if (_tieFor && !staff()->isTabStaff()) // no ties in tablature
2012-05-26 14:26:10 +02:00
// _tieFor->scanElements(data, func, all);
2013-03-25 16:27:20 +01:00
for (Element* e : _el) {
2012-05-26 14:26:10 +02:00
if (score()->tagIsValid(e->tag()))
e->scanElements(data, func, all);
}
2013-07-04 13:40:25 +02:00
for (Spanner* sp : _spannerFor)
sp->scanElements(data, func, all);
2012-05-26 14:26:10 +02:00
if (!dragMode && _accidental)
func(data, _accidental);
if (chord()) {
for (int i = 0; i < chord()->dots(); ++i) {
if (_dots[i])
func(data, _dots[i]);
}
}
}
//---------------------------------------------------------
// setTrack
//---------------------------------------------------------
void Note::setTrack(int val)
{
Element::setTrack(val);
if (_tieFor) {
_tieFor->setTrack(val);
foreach(SpannerSegment* seg, _tieFor->spannerSegments())
seg->setTrack(val);
}
2013-06-16 23:33:37 +02:00
foreach (Element* e, _el)
2012-05-26 14:26:10 +02:00
e->setTrack(val);
if (_accidental)
_accidental->setTrack(val);
if (!chord()) // if note is dragged with shift+ctrl
return;
for (int i = 0; i < chord()->dots(); ++i) {
if (_dots[i])
_dots[i]->setTrack(val);
}
}
//---------------------------------------------------------
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 Note::reset()
2012-05-26 14:26:10 +02:00
{
2012-08-23 17:40:04 +02:00
score()->undoChangeProperty(this, P_USER_OFF, QPointF());
score()->undoChangeProperty(chord(), P_USER_OFF, QPointF());
2012-08-04 15:46:43 +02:00
score()->undoChangeProperty(chord(), P_STEM_DIRECTION, MScore::AUTO);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
2013-05-28 15:42:02 +02:00
// mag
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
2013-05-28 15:42:02 +02:00
qreal Note::mag() const
2012-05-26 14:26:10 +02:00
{
2013-05-28 15:42:02 +02:00
qreal m = chord()->mag();
if (_small)
m *= score()->styleD(ST_smallNoteMag);
return m;
2012-05-26 14:26:10 +02:00
}
2013-01-02 09:29:17 +01:00
//---------------------------------------------------------
// setSmall
//---------------------------------------------------------
void Note::setSmall(bool val)
{
_small = val;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// setLine
//---------------------------------------------------------
void Note::setLine(int n)
{
_line = n;
rypos() = _line * spatium() * .5;
2013-07-15 14:36:51 +02:00
if (chord())
chord()->lineChanged();
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// setString
//---------------------------------------------------------
void Note::setString(int val)
{
_string = val;
rypos() = _string * spatium() * 1.5;
}
//---------------------------------------------------------
// setHeadGroup
//---------------------------------------------------------
void Note::setHeadGroup(NoteHeadGroup val)
{
Q_ASSERT(val >= 0 && val < HEAD_GROUPS);
_headGroup = val;
}
//---------------------------------------------------------
// ppitch
// playback pitch
// honours ottava and transposing instruments
//---------------------------------------------------------
int Note::ppitch() const
{
int tick = chord()->segment()->tick();
int pitchOffset = score()->styleB(ST_concertPitch) ? 0 : staff()->part()->instr()->transpose().chromatic;
2013-07-16 09:03:47 +02:00
return _pitch + staff()->pitchOffset(tick) + pitchOffset;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// customizeVelocity
// Input is the global velocity determined by dynamic
// signs and crescende/decrescendo etc.
// Returns the actual play velocity for this note
// modified by veloOffset
//---------------------------------------------------------
int Note::customizeVelocity(int velo) const
{
2012-08-04 15:46:43 +02:00
if (veloType() == MScore::OFFSET_VAL)
2012-05-26 14:26:10 +02:00
velo = velo + (velo * veloOffset()) / 100;
2012-08-04 15:46:43 +02:00
else if (veloType() == MScore::USER_VAL)
2012-05-26 14:26:10 +02:00
velo = veloOffset();
return restrict(velo, 1, 127);
}
//---------------------------------------------------------
// endEdit
//---------------------------------------------------------
void Note::endEdit()
{
Chord* ch = chord();
if (ch->notes().size() == 1) {
2012-08-23 17:40:04 +02:00
score()->undoChangeProperty(ch, P_USER_OFF, ch->userOff() + userOff());
2012-05-26 14:26:10 +02:00
setUserOff(QPointF());
score()->setLayoutAll(true);
2012-05-26 14:26:10 +02:00
}
}
//---------------------------------------------------------
// updateAccidental
//---------------------------------------------------------
void Note::updateAccidental(AccidentalState* as)
{
2012-08-07 16:05:37 +02:00
_line = absStep(_tpc, _pitch);
2012-05-26 14:26:10 +02:00
// don't touch accidentals that don't concern tpc such as
// quarter tones
2013-03-25 16:27:20 +01:00
if (_accidental &&
_accidental->accidentalType() > Accidental::ACC_NATURAL) {
// calculate the real note line depending on clef
Staff* s = score()->staff(staffIdx() + chord()->staffMove());
int tick = chord()->tick();
ClefType clef = s->clef(tick);
_line = relStep(_line, clef);
return;
}
2012-05-26 14:26:10 +02:00
// calculate accidental
Accidental::AccidentalType acci = Accidental::ACC_NONE;
AccidentalVal accVal = tpc2alter(_tpc);
if ((accVal != as->accidentalVal(int(_line))) || hidden() || as->tieContext(int(_line))) {
as->setAccidentalVal(int(_line), accVal, _tieBack != 0);
if (_tieBack)
acci = Accidental::ACC_NONE;
else {
acci = Accidental::value2subtype(accVal);
if (acci == Accidental::ACC_NONE)
acci = Accidental::ACC_NATURAL;
2012-05-26 14:26:10 +02:00
}
}
if (acci != Accidental::ACC_NONE && !_tieBack && !_hidden) {
2012-05-26 14:26:10 +02:00
if (_accidental == 0) {
Accidental* a = new Accidental(score());
a->setParent(this);
a->setAccidentalType(acci);
2012-05-26 14:26:10 +02:00
score()->undoAddElement(a);
}
else if (_accidental->accidentalType() != acci) {
Accidental* a = _accidental->clone();
2012-05-26 14:26:10 +02:00
a->setParent(this);
a->setAccidentalType(acci);
2012-05-26 14:26:10 +02:00
score()->undoChangeElement(_accidental, a);
}
}
else {
if (_accidental) {
// remove this if it was ACC_AUTO:
if (_accidental->role() == Accidental::ACC_AUTO)
score()->undoRemoveElement(_accidental);
else {
// keep it, but update type if needed
acci = Accidental::value2subtype(accVal);
if (acci == Accidental::ACC_NONE)
acci = Accidental::ACC_NATURAL;
if (_accidental->accidentalType() != acci) {
Accidental* a = _accidental->clone();
a->setParent(this);
a->setAccidentalType(acci);
score()->undoChangeElement(_accidental, a);
}
}
}
2012-05-26 14:26:10 +02:00
}
2012-05-26 14:26:10 +02:00
//
// calculate the real note line depending on clef
//
Staff* s = score()->staff(staffIdx() + chord()->staffMove());
int tick = chord()->tick();
2012-08-07 12:44:19 +02:00
ClefType clef = s->clef(tick);
2012-08-07 16:05:37 +02:00
_line = relStep(_line, clef);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// updateLine
//---------------------------------------------------------
void Note::updateLine()
{
2012-08-07 12:44:19 +02:00
Staff* s = score()->staff(staffIdx() + chord()->staffMove());
ClefType clef = s->clef(chord()->tick());
2012-08-07 16:05:37 +02:00
_line = relStep(_pitch, _tpc, clef);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// setNval
//---------------------------------------------------------
void Note::setNval(NoteVal nval)
{
setPitch(nval.pitch);
_fret = nval.fret;
_string = nval.string;
2012-08-08 20:46:29 +02:00
if (nval.tpc != INVALID_TPC)
_tpc = nval.tpc;
2012-08-04 15:46:43 +02:00
_headGroup = NoteHeadGroup(nval.headGroup);
2012-05-26 14:26:10 +02:00
}
2012-08-10 17:01:35 +02:00
//---------------------------------------------------------
// getProperty
//---------------------------------------------------------
QVariant Note::getProperty(P_ID propertyId) const
{
switch(propertyId) {
case P_PITCH:
return pitch();
case P_TPC:
return tpc();
case P_SMALL:
return small();
case P_MIRROR_HEAD:
return userMirror();
case P_DOT_POSITION:
return dotPosition();
case P_HEAD_GROUP:
return headGroup();
case P_VELO_OFFSET:
return veloOffset();
case P_TUNING:
return tuning();
case P_FRET:
return fret();
case P_STRING:
return string();
case P_GHOST:
return ghost();
case P_HEAD_TYPE:
return headType();
case P_VELO_TYPE:
return veloType();
case P_PLAY:
return play();
2012-08-10 17:01:35 +02:00
default:
break;
}
return Element::getProperty(propertyId);
}
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
bool Note::setProperty(P_ID propertyId, const QVariant& v)
{
switch(propertyId) {
case P_PITCH:
setPitch(v.toInt());
break;
case P_TPC:
setTpc(v.toInt());
break;
case P_SMALL:
setSmall(v.toBool());
break;
case P_MIRROR_HEAD:
setUserMirror(MScore::DirectionH(v.toInt()));
break;
case P_DOT_POSITION:
setDotPosition(MScore::Direction(v.toInt()));
break;
case P_HEAD_GROUP:
setHeadGroup(NoteHeadGroup(v.toInt()));
break;
case P_VELO_OFFSET:
setVeloOffset(v.toInt());
break;
case P_TUNING:
setTuning(v.toDouble());
break;
case P_FRET:
setFret(v.toInt());
break;
case P_STRING:
setString(v.toInt());
break;
case P_GHOST:
setGhost(v.toBool());
break;
case P_HEAD_TYPE:
setHeadType(NoteHeadType(v.toInt()));
break;
case P_VELO_TYPE:
setVeloType(MScore::ValueType(v.toInt()));
break;
2013-01-02 09:29:17 +01:00
case P_VISIBLE: { // P_VISIBLE requires reflecting property on dots
setVisible(v.toBool());
int dots = chord()->dots();
for (int i = 0; i < dots; ++i) {
if (_dots[i])
_dots[i]->setVisible(visible());
}
break;
}
case P_PLAY:
setPlay(v.toBool());
break;
2012-08-10 17:01:35 +02:00
default:
if (!Element::setProperty(propertyId, v))
return false;
break;
}
score()->setLayoutAll(true);
return true;
}
2012-08-12 11:44:36 +02:00
//---------------------------------------------------------
// undoSetFret
//---------------------------------------------------------
void Note::undoSetFret(int val)
{
undoChangeProperty(P_FRET, val);
}
//---------------------------------------------------------
// undoSetString
//---------------------------------------------------------
void Note::undoSetString(int val)
{
undoChangeProperty(P_STRING, val);
}
//---------------------------------------------------------
// undoSetGhost
//---------------------------------------------------------
void Note::undoSetGhost(bool val)
{
undoChangeProperty(P_GHOST, val);
}
//---------------------------------------------------------
// undoSetSmall
//---------------------------------------------------------
void Note::undoSetSmall(bool val)
{
undoChangeProperty(P_SMALL, val);
}
//---------------------------------------------------------
// undoSetPlay
//---------------------------------------------------------
void Note::undoSetPlay(bool val)
{
undoChangeProperty(P_PLAY, val);
}
2012-08-12 11:44:36 +02:00
//---------------------------------------------------------
// undoSetTuning
//---------------------------------------------------------
void Note::undoSetTuning(qreal val)
{
undoChangeProperty(P_TUNING, val);
}
//---------------------------------------------------------
// undoSetVeloType
//---------------------------------------------------------
void Note::undoSetVeloType(MScore::ValueType val)
{
undoChangeProperty(P_VELO_TYPE, val);
}
//---------------------------------------------------------
// undoSetVeloOffset
//---------------------------------------------------------
void Note::undoSetVeloOffset(int val)
{
undoChangeProperty(P_VELO_OFFSET, val);
}
//---------------------------------------------------------
// undoSetUserMirror
//---------------------------------------------------------
void Note::undoSetUserMirror(MScore::DirectionH val)
{
undoChangeProperty(P_MIRROR_HEAD, val);
}
//---------------------------------------------------------
// undoSetDotPosition
//---------------------------------------------------------
void Note::undoSetDotPosition(MScore::Direction val)
{
undoChangeProperty(P_DOT_POSITION, val);
}
//---------------------------------------------------------
// undoSetHeadGroup
//---------------------------------------------------------
void Note::undoSetHeadGroup(NoteHeadGroup val)
{
undoChangeProperty(P_HEAD_GROUP, val);
}
2013-01-28 21:45:36 +01:00
//---------------------------------------------------------
// setHeadType
//---------------------------------------------------------
void Note::setHeadType(NoteHeadType t)
{
_headType = t;
}
2012-08-12 11:44:36 +02:00
//---------------------------------------------------------
// undoSetHeadType
//---------------------------------------------------------
void Note::undoSetHeadType(NoteHeadType val)
{
undoChangeProperty(P_HEAD_TYPE, val);
}
2012-08-10 17:01:35 +02:00
//---------------------------------------------------------
// propertyDefault
//---------------------------------------------------------
QVariant Note::propertyDefault(P_ID propertyId) const
{
switch(propertyId) {
case P_GHOST:
2012-08-10 17:01:35 +02:00
case P_SMALL:
return false;
case P_MIRROR_HEAD:
return MScore::DH_AUTO;
case P_DOT_POSITION:
return MScore::AUTO;
case P_HEAD_GROUP:
case P_VELO_OFFSET:
return 0;
case P_TUNING:
return 0.0;
case P_FRET:
case P_STRING:
return -1;
case P_HEAD_TYPE:
return Note::HEAD_AUTO;
case P_VELO_TYPE:
return MScore::OFFSET_VAL;
case P_PLAY:
return true;
2012-08-10 17:01:35 +02:00
default:
break;
}
return Element::propertyDefault(propertyId);
2012-08-10 17:01:35 +02:00
}
2012-11-19 09:29:46 +01:00
//---------------------------------------------------------
// setOnTimeOffset
//---------------------------------------------------------
void Note::setOnTimeOffset(int)
{
// TODO
}
//---------------------------------------------------------
// setOffTimeOffset
//---------------------------------------------------------
void Note::setOffTimeOffset(int)
{
// TODO
}
2013-01-02 20:13:58 +01:00
//---------------------------------------------------------
2013-06-10 11:03:34 +02:00
// setScore
//---------------------------------------------------------
2013-06-10 11:03:34 +02:00
void Note::setScore(Score* s)
{
2013-06-10 11:03:34 +02:00
Element::setScore(s);
if (_tieFor)
_tieFor->setScore(s);
}
2013-05-13 18:49:17 +02:00
}