MuseScore/libmscore/note.cpp

2713 lines
102 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
//=============================================================================
2015-02-19 10:28:25 +01:00
2012-05-26 14:26:10 +02:00
/**
\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 "stringdata.h"
2012-05-26 14:26:10 +02:00
#include "fret.h"
#include "harmony.h"
#include "fingering.h"
#include "bend.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"
#include "hairpin.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
//---------------------------------------------------------
static const SymId noteHeads[2][int(NoteHead::Group::HEAD_GROUPS)][int(NoteHead::Type::HEAD_TYPES)] = {
// previous non-SMUFL data kept in comments for future reference
{ // down stem
{ SymId::noteheadWhole, SymId::noteheadHalf, SymId::noteheadBlack, SymId::noteheadDoubleWhole },
{ SymId::noteheadXWhole, SymId::noteheadXHalf, SymId::noteheadXBlack, SymId::noteheadXWhole },
{ SymId::noteheadDiamondWhole,SymId::noteheadDiamondHalf, SymId::noteheadDiamondBlack, SymId::noteheadDiamondWhole },
// { SymId(s0triangleHeadSym), SymId(d1triangleHeadSym), SymId(d2triangleHeadSym), SymId(s0triangleHeadSym) },
{ SymId::noteheadTriangleDownWhole, SymId::noteheadTriangleDownHalf, SymId::noteheadTriangleDownBlack, SymId::noteheadTriangleDownDoubleWhole },
// { SymId(s0miHeadSym), SymId(s1miHeadSym), SymId(s2miHeadSym), SymId::noSym },
2013-11-15 14:41:00 +01:00
{ SymId::noteShapeDiamondWhite, SymId::noteShapeDiamondWhite, SymId::noteShapeDiamondBlack, SymId::noSym },
// { SymId(wholeslashheadSym), SymId(halfslashheadSym), SymId(quartslashheadSym), SymId(wholeslashheadSym)},
2014-04-18 18:55:33 +02:00
{ SymId::noteheadSlashWhiteWhole, SymId::noteheadSlashWhiteHalf, SymId::noteheadSlashHorizontalEnds, SymId::noteheadSlashWhiteWhole},
// { SymId(xcircledheadSym), SymId(xcircledheadSym), SymId(xcircledheadSym), SymId(xcircledheadSym) },
{ SymId::noteheadCircleXWhole,SymId::noteheadCircleXHalf, SymId::noteheadCircleX, SymId::noteheadCircleXDoubleWhole},
// { SymId(s0doHeadSym), SymId(d1doHeadSym), SymId(d2doHeadSym), SymId::noSym },
2013-11-15 14:41:00 +01:00
{ SymId::noteShapeTriangleUpWhite, SymId::noteShapeTriangleUpWhite, SymId::noteShapeTriangleUpBlack, SymId::noSym },
// { SymId(s0reHeadSym), SymId(d1reHeadSym), SymId(d2reHeadSym), SymId::noSym },
2013-11-15 14:41:00 +01:00
{ SymId::noteShapeMoonWhite, SymId::noteShapeMoonWhite, SymId::noteShapeMoonBlack, SymId::noSym },
// { SymId(d0faHeadSym), SymId(d1faHeadSym), SymId(d2faHeadSym), SymId::noSym },
2013-11-15 14:41:00 +01:00
{ SymId::noteShapeTriangleRightWhite, SymId::noteShapeTriangleRightWhite, SymId::noteShapeTriangleRightBlack, SymId::noSym },
// { SymId(s0laHeadSym), SymId(s1laHeadSym), SymId(s2laHeadSym), SymId::noSym },
2013-11-15 14:41:00 +01:00
{ SymId::noteShapeSquareWhite, SymId::noteShapeSquareWhite, SymId::noteShapeSquareBlack, SymId::noSym },
// { SymId(s0tiHeadSym), SymId(d1tiHeadSym), SymId(d2tiHeadSym), SymId::noSym },
2013-11-15 14:41:00 +01:00
{ SymId::noteShapeTriangleRoundWhite, SymId::noteShapeTriangleRoundWhite, SymId::noteShapeTriangleRoundBlack, SymId::noSym },
// { SymId(s0solHeadSym), SymId(s1solHeadSym), SymId(s2solHeadSym), SymId::noSym },
2013-11-15 14:41:00 +01:00
{ SymId::noteShapeRoundWhite, SymId::noteShapeRoundWhite, SymId::noteShapeRoundBlack, SymId::noSym },
{ SymId::noteheadWhole, SymId::noteheadHalf, SymId::noteheadBlack, SymId::noteheadDoubleWholeSquare },
},
{ // up stem
{ SymId::noteheadWhole, SymId::noteheadHalf, SymId::noteheadBlack, SymId::noteheadDoubleWhole },
{ SymId::noteheadXWhole, SymId::noteheadXHalf, SymId::noteheadXBlack, SymId::noteheadXWhole },
{ SymId::noteheadDiamondWhole,SymId::noteheadDiamondHalf, SymId::noteheadDiamondBlack, SymId::noteheadDiamondWhole },
// { SymId(s0triangleHeadSym), SymId(d1triangleHeadSym), SymId(d2triangleHeadSym), SymId(s0triangleHeadSym) },
2013-11-15 14:41:00 +01:00
{ SymId::noteheadTriangleDownWhole, SymId::noteheadTriangleDownHalf, SymId::noteheadTriangleDownBlack, SymId::noteheadTriangleDownDoubleWhole },
// { SymId(s0miHeadSym), SymId(s1miHeadSym), SymId(s2miHeadSym), SymId::noSym },
2013-11-15 14:41:00 +01:00
{ SymId::noteShapeDiamondWhite, SymId::noteShapeDiamondWhite, SymId::noteShapeDiamondBlack, SymId::noSym },
// { SymId(wholeslashheadSym), SymId(halfslashheadSym), SymId(quartslashheadSym), SymId(wholeslashheadSym)},
2014-04-18 18:55:33 +02:00
{ SymId::noteheadSlashWhiteWhole, SymId::noteheadSlashWhiteHalf, SymId::noteheadSlashHorizontalEnds, SymId::noteheadSlashWhiteWhole},
// { SymId(xcircledheadSym), SymId(xcircledheadSym), SymId(xcircledheadSym), SymId(xcircledheadSym) },
2013-11-15 14:41:00 +01:00
{ SymId::noteheadCircleXWhole, SymId::noteheadCircleXHalf, SymId::noteheadCircleX, SymId::noteheadCircleXDoubleWhole},
// { SymId(s0doHeadSym), SymId(d1doHeadSym), SymId(d2doHeadSym), SymId::noSym },
2013-11-15 14:41:00 +01:00
{ SymId::noteShapeTriangleUpWhite, SymId::noteShapeTriangleUpWhite, SymId::noteShapeTriangleUpBlack, SymId::noSym },
// { SymId(s0reHeadSym), SymId(d1reHeadSym), SymId(d2reHeadSym), SymId::noSym },
2013-11-15 14:41:00 +01:00
{ SymId::noteShapeMoonWhite, SymId::noteShapeMoonWhite, SymId::noteShapeMoonBlack, SymId::noSym },
// { SymId(d0faHeadSym), SymId(d1faHeadSym), SymId(d2faHeadSym), SymId::noSym },
2014-10-01 10:45:36 +02:00
{ SymId::noteShapeTriangleLeftWhite, SymId::noteShapeTriangleLeftWhite, SymId::noteShapeTriangleLeftBlack, SymId::noSym },
// { SymId(s0laHeadSym), SymId(s1laHeadSym), SymId(s2laHeadSym), SymId::noSym },
2013-11-15 14:41:00 +01:00
{ SymId::noteShapeSquareWhite, SymId::noteShapeSquareWhite, SymId::noteShapeSquareBlack, SymId::noSym },
// { SymId(s0tiHeadSym), SymId(d1tiHeadSym), SymId(d2tiHeadSym), SymId::noSym },
2013-11-15 14:41:00 +01:00
{ SymId::noteShapeTriangleRoundWhite, SymId::noteShapeTriangleRoundWhite, SymId::noteShapeTriangleRoundBlack, SymId::noSym },
// { SymId(s0solHeadSym), SymId(s1solHeadSym), SymId(s2solHeadSym), SymId::noSym },
2013-11-15 14:41:00 +01:00
{ SymId::noteShapeRoundWhite, SymId::noteShapeRoundWhite, SymId::noteShapeRoundBlack, SymId::noSym },
{ SymId::noteheadWhole, SymId::noteheadHalf, SymId::noteheadBlack, SymId::noteheadDoubleWholeSquare },
}
};
2012-05-26 14:26:10 +02:00
// same order as NoteHead::Group
static const char* noteHeadNames[] = {
QT_TRANSLATE_NOOP("noteheadnames", "Normal"),
QT_TRANSLATE_NOOP("noteheadnames", "Cross"),
QT_TRANSLATE_NOOP("noteheadnames", "Diamond"),
QT_TRANSLATE_NOOP("noteheadnames", "Triangle"),
QT_TRANSLATE_NOOP("noteheadnames", "Mi"),
QT_TRANSLATE_NOOP("noteheadnames", "Slash"),
QT_TRANSLATE_NOOP("noteheadnames", "XCircle"),
QT_TRANSLATE_NOOP("noteheadnames", "Do"),
QT_TRANSLATE_NOOP("noteheadnames", "Re"),
QT_TRANSLATE_NOOP("noteheadnames", "Fa"),
QT_TRANSLATE_NOOP("noteheadnames", "La"),
QT_TRANSLATE_NOOP("noteheadnames", "Ti"),
QT_TRANSLATE_NOOP("noteheadnames", "Sol"),
QT_TRANSLATE_NOOP("noteheadnames", "Alt. Brevis")
};
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
2013-11-25 15:42:40 +01:00
// noteHead
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
SymId Note::noteHead(int direction, NoteHead::Group g, NoteHead::Type t)
2013-11-25 15:42:40 +01:00
{
return noteHeads[direction][int(g)][int(t)];
};
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// 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();
}
//---------------------------------------------------------
// headGroup
//---------------------------------------------------------
NoteHead::Group NoteHead::headGroup() const
{
Group group = Group::HEAD_INVALID;
for (int i = 0; i < int(Group::HEAD_GROUPS); ++i) {
if (noteHeads[0][i][1] == _sym || noteHeads[0][i][3] == _sym) {
group = (Group)i;
break;
}
}
return group;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// Note
//---------------------------------------------------------
Note::Note(Score* s)
: Element(s)
{
setFlags(ElementFlag::MOVABLE | ElementFlag::SELECTABLE);
2012-11-19 09:29:46 +01:00
_playEvents.append(NoteEvent()); // add default play event
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;
2015-02-19 10:28:25 +01:00
qDeleteAll(_dots);
2012-05-26 14:26:10 +02:00
}
2014-07-10 18:59:44 +02:00
Note::Note(const Note& n, bool link)
2012-05-26 14:26:10 +02:00
: Element(n)
{
2014-07-10 18:59:44 +02:00
if (link)
score()->undo(new Link(this, const_cast<Note*>(&n)));
2012-05-26 14:26:10 +02:00
_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[0] = n._tpc[0];
_tpc[1] = n._tpc[1];
2014-08-13 16:36:09 +02:00
_dotsHidden = n._dotsHidden;
2012-05-26 14:26:10 +02:00
_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;
2014-04-02 20:31:37 +02:00
_userDotPosition = n._userDotPosition;
_fixed = n._fixed;
_fixedLine = n._fixedLine;
2012-05-26 14:26:10 +02:00
_accidental = 0;
2012-05-26 14:26:10 +02:00
if (n._accidental)
add(new Accidental(*(n._accidental)));
2014-07-10 18:59:44 +02:00
// types in _el: SYMBOL, IMAGE, FINGERING, TEXT, BEND
for (Element* e : n._el) {
Element* ce = e->clone();
add(ce);
if (link)
score()->undo(new Link(ce, const_cast<Element*>(e)));
2014-07-10 18:59:44 +02:00
}
2013-03-25 16:27:20 +01:00
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;
2015-02-19 10:28:25 +01:00
for (int i = 0; i < MAX_DOTS; ++i) {
2012-05-26 14:26:10 +02:00
if (n._dots[i])
add(new NoteDot(*n._dots[i]));
2015-02-19 10:28:25 +01:00
else
_dots[i] = 0;
2012-05-26 14:26:10 +02:00
}
_lineOffset = n._lineOffset;
2013-02-20 17:53:15 +01:00
_mark = n._mark;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// concertPitchIdx
//---------------------------------------------------------
inline int Note::concertPitchIdx() const
{
return concertPitch() ? 0 : 1;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// setPitch
//---------------------------------------------------------
void Note::setPitch(int val)
{
2014-04-09 16:09:21 +02:00
Q_ASSERT(val >= 0 && val <= 127);
if (_pitch != val) {
_pitch = val;
2015-01-30 17:03:51 +01:00
score()->setPlaylistDirty();
2012-05-26 14:26:10 +02:00
}
}
void Note::setPitch(int pitch, int tpc1, int tpc2)
2012-05-26 14:26:10 +02:00
{
2014-04-09 11:11:02 +02:00
Q_ASSERT(tpcIsValid(tpc1));
Q_ASSERT(tpcIsValid(tpc2));
_tpc[0] = tpc1;
_tpc[1] = tpc2;
2014-04-09 11:11:02 +02:00
setPitch(pitch);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// undoSetPitch
//---------------------------------------------------------
void Note::undoSetPitch(int p)
{
2014-05-26 18:18:01 +02:00
score()->undoChangeProperty(this, P_ID::PITCH, p);
}
2014-04-10 13:13:37 +02:00
//---------------------------------------------------------
// tpc1default
//---------------------------------------------------------
int Note::tpc1default(int p) const
{
2014-06-20 17:07:22 +02:00
Key key = Key::C;
2014-04-10 13:13:37 +02:00
if (staff() && chord()) {
2014-06-03 15:28:10 +02:00
key = staff()->key(chord()->tick());
2014-04-10 13:13:37 +02:00
if (!concertPitch()) {
Interval interval = part()->instrument()->transpose();
2014-04-10 13:13:37 +02:00
if (!interval.isZero()) {
interval.flip();
key = transposeKey(key, interval);
}
}
}
return pitch2tpc(p, key, Prefer::NEAREST);
2014-04-10 13:13:37 +02:00
}
//---------------------------------------------------------
// tpc2default
//---------------------------------------------------------
int Note::tpc2default(int p) const
{
2014-06-20 17:07:22 +02:00
Key key = Key::C;
2014-04-10 13:13:37 +02:00
if (staff() && chord()) {
2014-06-03 15:28:10 +02:00
key = staff()->key(chord()->tick());
2014-04-10 13:13:37 +02:00
if (concertPitch()) {
Interval interval = part()->instrument()->transpose();
2014-04-10 13:13:37 +02:00
if (!interval.isZero())
key = transposeKey(key, interval);
}
}
return pitch2tpc(p - transposition(), key, Prefer::NEAREST);
2014-04-10 13:13:37 +02:00
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// setTpcFromPitch
//---------------------------------------------------------
void Note::setTpcFromPitch()
{
2015-01-17 23:32:14 +01:00
// works best if note is already added to score, otherwise we can't determine transposition or key
Interval v = staff() ? part()->instrument()->transpose() : Interval();
2014-06-20 17:07:22 +02:00
Key key = (staff() && chord()) ? staff()->key(chord()->tick()) : Key::C;
// convert key to concert pitch
if (!concertPitch() && !v.isZero())
key = transposeKey(key, v);
// set concert pitch tpc
_tpc[0] = pitch2tpc(_pitch, key, Prefer::NEAREST);
// set transposed tpc
if (v.isZero())
_tpc[1] = _tpc[0];
else {
v.flip();
_tpc[1] = Ms::transposeTpc(_tpc[0], v, true);
}
2014-04-09 11:11:02 +02:00
Q_ASSERT(tpcIsValid(_tpc[0]));
Q_ASSERT(tpcIsValid(_tpc[1]));
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// setTpc
//---------------------------------------------------------
void Note::setTpc(int v)
{
2014-04-02 18:11:56 +02:00
if (!tpcIsValid(v))
qFatal("Note::setTpc: bad tpc %d", v);
_tpc[concertPitchIdx()] = v;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// undoSetTpc
// change the current tpc
//---------------------------------------------------------
void Note::undoSetTpc(int v)
{
if (concertPitch()) {
if (v != tpc1())
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::TPC1, v);
}
else {
if (v != tpc2())
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::TPC2, v);
}
}
//---------------------------------------------------------
// tpc
//---------------------------------------------------------
int Note::tpc() const
{
return _tpc[concertPitchIdx()];
}
2015-02-19 10:28:25 +01:00
//---------------------------------------------------------
// tpcUserName
//---------------------------------------------------------
QString Note::tpcUserName(bool explicitAccidental)
{
QString pitchName = tpc2name(tpc(), NoteSpellingType::STANDARD, NoteCaseType::AUTO, explicitAccidental);
2014-10-13 23:32:53 +02:00
QString octaveName = QString::number((pitch() / 12) - 1);
return pitchName + (explicitAccidental ? " " : "") + octaveName;
}
2014-04-23 11:08:51 +02:00
//---------------------------------------------------------
// transposeTpc
// return transposed tpc
// If in concertPitch mode return tpc for transposed view
// else return tpc for concert pitch view.
//---------------------------------------------------------
int Note::transposeTpc(int tpc)
{
Interval v = part()->instrument()->transpose();
2014-04-23 11:08:51 +02:00
if (v.isZero())
return tpc;
if (concertPitch()) {
v.flip();
return Ms::transposeTpc(tpc, v, true);
2014-04-23 11:08:51 +02:00
}
else
return Ms::transposeTpc(tpc, v, true);
2014-04-23 11:08:51 +02:00
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// noteHead
//---------------------------------------------------------
2013-11-06 15:58:05 +01:00
SymId Note::noteHead() const
2012-05-26 14:26:10 +02:00
{
int up;
NoteHead::Type ht;
2012-05-26 14:26:10 +02:00
if (chord()) {
up = chord()->up();
2012-05-26 14:26:10 +02:00
ht = chord()->durationType().headType();
}
2013-01-02 20:13:58 +01:00
else {
up = 1;
ht = NoteHead::Type::HEAD_QUARTER;
2012-05-26 14:26:10 +02:00
}
if (_headType != NoteHead::Type::HEAD_AUTO)
2013-01-28 21:45:36 +01:00
ht = _headType;
2013-01-02 20:13:58 +01:00
2013-11-25 15:42:40 +01:00
SymId t = noteHead(up, _headGroup, ht);
2013-11-06 15:58:05 +01:00
if (t == SymId::noSym) {
qDebug("invalid note head %hhd/%hhd", _headGroup, ht);
t = noteHead(up, NoteHead::Group::HEAD_NORMAL, ht);
2013-01-28 21:45:36 +01:00
}
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-11-11 15:11:28 +01:00
return symWidth(noteHead());
2012-05-26 14:26:10 +02:00
}
2015-02-19 10:28:25 +01:00
//---------------------------------------------------------
// tabHeadWidth
//---------------------------------------------------------
2014-04-28 18:38:50 +02:00
qreal Note::tabHeadWidth(StaffType* 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;
if (fixed())
s = "/";
else
TAB: Support for input and display of bass string notations Supports 'standard' configuration for bass string notations in historic tablatures (lutes and other plucked instruments, as well as viols), both of the French and of the Italian style. This should fill the last 'big hole' in historic TAB support. Bass strings (or bourdons) are extra strings in addition to the 6 'standard' strings, which are not represented by tab lines and were indicated by other typograhic devices in historic sources. Among the innumerable variations shown in sources, this implementation supports the following styles, chosen to be general enough to suit the majority of cases, without requiring new parameters in the TAB style dialogue box: - French: the first 4 bass courses are indicated by a fret mark in the 'seventh' TAB position (below bottom string) with 0, 1, 2 or 3 slashes prefixed; other bass courses are indicated, also in the 'seventh' TAB position, by the string number (from 4 on) and cannot contain a fret mark (as they didn't in historic sources). - Italian: the first 2 bass courses are indicated by a fret mark in the 'seventh' TAB position (abover top string) with 1 or 2 'ledger lines' underneath; other bass courses are indicated, also in the 'seventh' TAB position, by the string number (from 9 on) and cannot contain a fret mark (as they didn't in historic sources). Rhythm marks above these indication are raised to leave room for them. Both styles do not blindly assume that French style is top-to-bottom and Italian is 'upside-down' -- as historic sources are -- but adapt to the actual string order of the TAB. The choice between the two styles depends on the TAB using numbers or letters for the fret marks. The implementation does not try to detect if the TAB is really of a historic style and applies either bass string notation whenever more strings are used than there are TAB lines. If this proves unsuitable to modern usage, some better heuristics can probably be found. For a discussion and some screen shots, see: https://musescore.org/en/node/67261 **Note entry** During TAB note entry, if the instruments has more strings than the TAB has lines, the string cursor can be moved outside of the TAB body, one position below for 'top-to-bottom' TAB's and one position above for 'upside-down' TAB's. Further up or down movements add, to the 'blue cursor rectangle', markers indicating which is the actual target string (the cursor does not actually move), equal to the marks a note in that string will receive (slashes, ledger lines or string ordinal, according to the style and the string); during input the user will then receive the same info as when reading entered notes. Other Notes: - the `InputStatus::_string` variable, holding the current target TAB string in TAB note entry, changed meaning from the __visual__ string index to the __physical__ string index: this allows a better containment of the peculiarities of the individual TAB styles within the `StaffStyle` class, leaving other classes somehow freer of concern about TAB visual order and other peculiarities. As this variable is only used with TAB's, this change should not affect other functions. - Some calculation for rhythm symbols have been moved from `TabDurationSymbol::draw()` to `TabDurationSymbol::layout()`, hopefully speeding up the drawing process. - In fonts for historic styles, '10' has been replaced by 'X' both in fret numbers and in string ordinals, as this is more common in historic sources. Currently, this is not configurable; an additional style parameter could be added in future, if there will be enough request for it.
2015-07-06 17:40:02 +02:00
s = tab->fretString(_fret, _string, _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
{
2013-11-11 15:11:28 +01:00
return symHeight(noteHead());
2012-05-26 14:26:10 +02:00
}
2014-04-28 18:38:50 +02:00
//---------------------------------------------------------
// tabHeadHeight
//---------------------------------------------------------
qreal Note::tabHeadHeight(StaffType* tab) const
{
2015-02-19 10:28:25 +01:00
if (tab && _fret != FRET_NONE && _string != STRING_NONE)
return tab->fretBoxH() * magS();
return headHeight();
}
2013-01-02 09:29:17 +01:00
//---------------------------------------------------------
// stemDownNW
2013-01-02 09:29:17 +01:00
//---------------------------------------------------------
QPointF Note::stemDownNW() const
2013-01-02 09:29:17 +01:00
{
return symStemDownNW(noteHead());
}
//---------------------------------------------------------
// stemUpSE
//---------------------------------------------------------
QPointF Note::stemUpSE() const
{
return symStemUpSE(noteHead());
2013-01-02 09:29:17 +01:00
}
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
{
int stick = firstTiedNote()->chord()->tick();
const Note* note = lastTiedNote();
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)
{
Note* e = static_cast<Note*>(l->endElement());
if (e && e->type() == Element::Type::NOTE) {
e->addSpannerBack(l);
if (l->type() == Element::Type::GLISSANDO)
e->chord()->setEndsGlissando(true);
}
addSpannerFor(l);
2012-09-12 16:19:03 +02:00
}
//---------------------------------------------------------
// removeSpanner
//---------------------------------------------------------
void Note::removeSpanner(Spanner* l)
{
Note* e = static_cast<Note*>(l->endElement());
if (e && e->type() == Element::Type::NOTE) {
if (!e->removeSpannerBack(l)) {
qDebug("Note::removeSpanner(%p): cannot remove spannerBack %s %p", this, l->name(), l);
// abort();
}
if (l->type() == Element::Type::GLISSANDO)
e->chord()->setEndsGlissando(false);
2012-09-12 16:19:03 +02:00
}
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 Element::Type::NOTEDOT:
2012-05-26 14:26:10 +02:00
{
NoteDot* dot = static_cast<NoteDot*>(e);
_dots[dot->idx()] = dot;
}
break;
case Element::Type::SYMBOL:
case Element::Type::IMAGE:
case Element::Type::FINGERING:
case Element::Type::TEXT:
case Element::Type::BEND:
2013-03-25 16:27:20 +01:00
_el.push_back(e);
2012-05-26 14:26:10 +02:00
break;
case Element::Type::TIE:
2012-05-26 14:26:10 +02:00
{
Tie* tie = static_cast<Tie*>(e);
2014-05-30 13:35:44 +02:00
tie->setStartNote(this);
2012-05-26 14:26:10 +02:00
tie->setTrack(track());
2014-05-30 13:35:44 +02:00
setTieFor(tie);
2012-05-26 14:26:10 +02:00
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 Element::Type::ACCIDENTAL:
2012-05-26 14:26:10 +02:00
_accidental = static_cast<Accidental*>(e);
break;
case Element::Type::TEXTLINE:
case Element::Type::GLISSANDO:
2012-09-12 16:19:03 +02:00
addSpanner(static_cast<Spanner*>(e));
break;
2012-05-26 14:26:10 +02:00
default:
qDebug("Note::add() not impl. %s", e->name());
2012-05-26 14:26:10 +02:00
break;
}
}
//---------------------------------------------------------
// remove
//---------------------------------------------------------
void Note::remove(Element* e)
{
switch(e->type()) {
case Element::Type::NOTEDOT:
2015-02-19 10:28:25 +01:00
for (int i = 0; i < MAX_DOTS; ++i) {
2012-05-26 14:26:10 +02:00
if (_dots[i] == e) {
_dots[i] = 0;
break;
}
}
break;
case Element::Type::TEXT:
case Element::Type::SYMBOL:
case Element::Type::IMAGE:
case Element::Type::FINGERING:
case Element::Type::BEND:
2012-05-26 14:26:10 +02:00
if (!_el.remove(e))
qDebug("Note::remove(): cannot find %s", e->name());
2012-05-26 14:26:10 +02:00
break;
case Element::Type::TIE:
2012-05-26 14:26:10 +02:00
{
Tie* tie = static_cast<Tie*>(e);
setTieFor(0);
if (tie->endNote())
2012-05-26 14:26:10 +02:00
tie->endNote()->setTieBack(0);
for (SpannerSegment* ss : tie->spannerSegments()) {
2012-05-26 14:26:10 +02:00
Q_ASSERT(ss->spanner() == tie);
if (ss->system())
ss->system()->remove(ss);
}
}
break;
case Element::Type::ACCIDENTAL:
2012-05-26 14:26:10 +02:00
_accidental = 0;
break;
case Element::Type::TEXTLINE:
case Element::Type::GLISSANDO:
2012-09-12 16:19:03 +02:00
removeSpanner(static_cast<Spanner*>(e));
break;
2012-05-26 14:26:10 +02:00
default:
qDebug("Note::remove() not impl. %s", e->name());
2012-05-26 14:26:10 +02:00
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) {
2014-04-28 18:38:50 +02:00
StaffType* tab = staff()->staffType();
if (tieBack() && !tab->showBackTied()) // skip back-tied notes if not shown
return;
QString s;
if (fixed())
s = "/";
else
TAB: Support for input and display of bass string notations Supports 'standard' configuration for bass string notations in historic tablatures (lutes and other plucked instruments, as well as viols), both of the French and of the Italian style. This should fill the last 'big hole' in historic TAB support. Bass strings (or bourdons) are extra strings in addition to the 6 'standard' strings, which are not represented by tab lines and were indicated by other typograhic devices in historic sources. Among the innumerable variations shown in sources, this implementation supports the following styles, chosen to be general enough to suit the majority of cases, without requiring new parameters in the TAB style dialogue box: - French: the first 4 bass courses are indicated by a fret mark in the 'seventh' TAB position (below bottom string) with 0, 1, 2 or 3 slashes prefixed; other bass courses are indicated, also in the 'seventh' TAB position, by the string number (from 4 on) and cannot contain a fret mark (as they didn't in historic sources). - Italian: the first 2 bass courses are indicated by a fret mark in the 'seventh' TAB position (abover top string) with 1 or 2 'ledger lines' underneath; other bass courses are indicated, also in the 'seventh' TAB position, by the string number (from 9 on) and cannot contain a fret mark (as they didn't in historic sources). Rhythm marks above these indication are raised to leave room for them. Both styles do not blindly assume that French style is top-to-bottom and Italian is 'upside-down' -- as historic sources are -- but adapt to the actual string order of the TAB. The choice between the two styles depends on the TAB using numbers or letters for the fret marks. The implementation does not try to detect if the TAB is really of a historic style and applies either bass string notation whenever more strings are used than there are TAB lines. If this proves unsuitable to modern usage, some better heuristics can probably be found. For a discussion and some screen shots, see: https://musescore.org/en/node/67261 **Note entry** During TAB note entry, if the instruments has more strings than the TAB has lines, the string cursor can be moved outside of the TAB body, one position below for 'top-to-bottom' TAB's and one position above for 'upside-down' TAB's. Further up or down movements add, to the 'blue cursor rectangle', markers indicating which is the actual target string (the cursor does not actually move), equal to the marks a note in that string will receive (slashes, ledger lines or string ordinal, according to the style and the string); during input the user will then receive the same info as when reading entered notes. Other Notes: - the `InputStatus::_string` variable, holding the current target TAB string in TAB note entry, changed meaning from the __visual__ string index to the __physical__ string index: this allows a better containment of the peculiarities of the individual TAB styles within the `StaffStyle` class, leaving other classes somehow freer of concern about TAB visual order and other peculiarities. As this variable is only used with TAB's, this change should not affect other functions. - Some calculation for rhythm symbols have been moved from `TabDurationSymbol::draw()` to `TabDurationSymbol::layout()`, hopefully speeding up the drawing process. - In fonts for historic styles, '10' has been replaced by 'X' both in fret numbers and in string ordinals, as this is more common in historic sources. Currently, this is not configurable; an additional style parameter could be added in future, if there will be enough request for it.
2015-07-06 17:40:02 +02:00
s = tab->fretString(_fret, _string, _ghost);
TAB: Support for input and display of bass string notations Supports 'standard' configuration for bass string notations in historic tablatures (lutes and other plucked instruments, as well as viols), both of the French and of the Italian style. This should fill the last 'big hole' in historic TAB support. Bass strings (or bourdons) are extra strings in addition to the 6 'standard' strings, which are not represented by tab lines and were indicated by other typograhic devices in historic sources. Among the innumerable variations shown in sources, this implementation supports the following styles, chosen to be general enough to suit the majority of cases, without requiring new parameters in the TAB style dialogue box: - French: the first 4 bass courses are indicated by a fret mark in the 'seventh' TAB position (below bottom string) with 0, 1, 2 or 3 slashes prefixed; other bass courses are indicated, also in the 'seventh' TAB position, by the string number (from 4 on) and cannot contain a fret mark (as they didn't in historic sources). - Italian: the first 2 bass courses are indicated by a fret mark in the 'seventh' TAB position (abover top string) with 1 or 2 'ledger lines' underneath; other bass courses are indicated, also in the 'seventh' TAB position, by the string number (from 9 on) and cannot contain a fret mark (as they didn't in historic sources). Rhythm marks above these indication are raised to leave room for them. Both styles do not blindly assume that French style is top-to-bottom and Italian is 'upside-down' -- as historic sources are -- but adapt to the actual string order of the TAB. The choice between the two styles depends on the TAB using numbers or letters for the fret marks. The implementation does not try to detect if the TAB is really of a historic style and applies either bass string notation whenever more strings are used than there are TAB lines. If this proves unsuitable to modern usage, some better heuristics can probably be found. For a discussion and some screen shots, see: https://musescore.org/en/node/67261 **Note entry** During TAB note entry, if the instruments has more strings than the TAB has lines, the string cursor can be moved outside of the TAB body, one position below for 'top-to-bottom' TAB's and one position above for 'upside-down' TAB's. Further up or down movements add, to the 'blue cursor rectangle', markers indicating which is the actual target string (the cursor does not actually move), equal to the marks a note in that string will receive (slashes, ledger lines or string ordinal, according to the style and the string); during input the user will then receive the same info as when reading entered notes. Other Notes: - the `InputStatus::_string` variable, holding the current target TAB string in TAB note entry, changed meaning from the __visual__ string index to the __physical__ string index: this allows a better containment of the peculiarities of the individual TAB styles within the `StaffStyle` class, leaving other classes somehow freer of concern about TAB visual order and other peculiarities. As this variable is only used with TAB's, this change should not affect other functions. - Some calculation for rhythm symbols have been moved from `TabDurationSymbol::draw()` to `TabDurationSymbol::layout()`, hopefully speeding up the drawing process. - In fonts for historic styles, '10' has been replaced by 'X' both in fret numbers and in string ordinals, as this is more common in historic sources. Currently, this is not configurable; an additional style parameter could be added in future, if there will be enough request for it.
2015-07-06 17:40:02 +02:00
// draw background, if required (to hide a segment of string line or to show a fretting conflict)
if (!tab->linesThrough() || fretConflict()) {
qreal d = spatium() * .1;
QRectF bb = QRectF(bbox().x()-d, tab->fretMaskY()*magS(), bbox().width() + 2*d, tab->fretMaskH()*magS());
// we do not know which viewer did this draw() call
// so update all:
foreach(MuseScoreView* view, score()->getViewer())
view->drawBackground(painter, bb);
if (fretConflict() && !score()->printing()) { //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
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 = part()->instrument();
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-11-06 15:58:05 +01:00
drawSymbol(noteHead(), painter);
2012-05-26 14:26:10 +02:00
}
}
//--------------------------------------------------
// Note::write
//---------------------------------------------------------
void Note::write(Xml& xml) const
{
xml.stag("Note");
Element::writeProperties(xml);
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) {
for (int i = 0; i < dots; ++i) {
if (_dots[i] && (!_dots[i]->userOff().isNull() || !_dots[i]->visible()
2014-05-22 18:57:48 +02:00
|| _dots[i]->color() != Qt::black || _dots[i]->visible() != visible())) {
2015-02-19 10:28:25 +01:00
for (int i = 0; i < dots; ++i)
_dots[i]->write(xml);
2012-05-26 14:26:10 +02:00
break;
}
}
}
2014-07-21 13:24:21 +02:00
if (_tieFor)
2012-05-26 14:26:10 +02:00
_tieFor->write(xml);
2014-07-21 13:24:21 +02:00
if (_tieBack) {
int id = xml.spannerId(_tieBack);
xml.tagE(QString("endSpanner id=\"%1\"").arg(id));
}
if ((chord() == 0 || chord()->playEventType() != PlayEventType::Auto) && !_playEvents.isEmpty()) {
xml.stag("Events");
foreach(const NoteEvent& e, _playEvents)
e.write(xml);
xml.etag();
2012-05-26 14:26:10 +02:00
}
2014-05-26 18:18:01 +02:00
writeProperty(xml, P_ID::PITCH);
2014-05-05 12:41:50 +02:00
// write tpc1 before tpc2 !
2014-05-26 18:18:01 +02:00
writeProperty(xml, P_ID::TPC1);
if (_tpc[1] != _tpc[0])
2014-05-26 18:18:01 +02:00
writeProperty(xml, P_ID::TPC2);
writeProperty(xml, P_ID::SMALL);
writeProperty(xml, P_ID::MIRROR_HEAD);
writeProperty(xml, P_ID::DOT_POSITION);
writeProperty(xml, P_ID::HEAD_GROUP);
writeProperty(xml, P_ID::VELO_OFFSET);
writeProperty(xml, P_ID::PLAY);
writeProperty(xml, P_ID::TUNING);
writeProperty(xml, P_ID::FRET);
writeProperty(xml, P_ID::STRING);
writeProperty(xml, P_ID::GHOST);
writeProperty(xml, P_ID::HEAD_TYPE);
writeProperty(xml, P_ID::VELO_TYPE);
writeProperty(xml, P_ID::FIXED);
writeProperty(xml, P_ID::FIXED_LINE);
2012-08-10 17:01:35 +02:00
2014-07-21 13:24:21 +02:00
foreach (Spanner* e, _spannerFor)
2012-09-12 16:19:03 +02:00
e->write(xml);
2013-06-10 11:03:34 +02:00
foreach (Spanner* e, _spannerBack)
2014-07-21 13:24:21 +02:00
xml.tagE(QString("endSpanner id=\"%1\"").arg(xml.spannerId(e)));
2012-05-26 14:26:10 +02:00
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[0] = Tpc::TPC_INVALID;
_tpc[1] = Tpc::TPC_INVALID;
2012-05-26 14:26:10 +02:00
2013-01-11 18:10:18 +01:00
if (e.hasAttribute("pitch")) // obsolete
_pitch = e.intAttribute("pitch");
if (e.hasAttribute("tpc")) // obsolete
_tpc[0] = 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();
2014-05-05 12:41:50 +02:00
else if (tag == "tpc") {
_tpc[0] = e.readInt();
2014-05-05 12:41:50 +02:00
_tpc[1] = _tpc[0];
}
else if (tag == "tpc2")
_tpc[1] = 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());
else if (tag == "mirror")
2014-05-26 18:18:01 +02:00
setProperty(P_ID::MIRROR_HEAD, Ms::getProperty(P_ID::MIRROR_HEAD, e));
2012-08-10 17:01:35 +02:00
else if (tag == "dotPosition")
2014-05-26 18:18:01 +02:00
setProperty(P_ID::DOT_POSITION, Ms::getProperty(P_ID::DOT_POSITION, e));
else if (tag == "fixed")
setFixed(e.readBool());
else if (tag == "fixedLine")
setFixedLine(e.readInt());
else if (tag == "onTimeType") { //obsolete
if (e.readElementText() == "offset")
_onTimeType = 2;
else
_onTimeType = 1;
}
else if (tag == "offTimeType") { //obsolete
if (e.readElementText() == "offset")
_offTimeType = 2;
else
_offTimeType = 1;
}
else if (tag == "onTimeOffset") {// obsolete
if (_onTimeType == 1)
setOnTimeOffset(e.readInt() * 1000 / chord()->actualTicks());
else
setOnTimeOffset(e.readInt() * 10);
}
else if (tag == "offTimeOffset") {// obsolete
if (_offTimeType == 1)
setOffTimeOffset(1000 + (e.readInt() * 1000 / chord()->actualTicks()));
else
setOffTimeOffset(1000 + (e.readInt() * 10));
}
2012-08-10 17:01:35 +02:00
else if (tag == "head")
2014-05-26 18:18:01 +02:00
setProperty(P_ID::HEAD_GROUP, Ms::getProperty(P_ID::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)
2014-05-26 18:18:01 +02:00
setProperty(P_ID::HEAD_TYPE, Ms::getProperty(P_ID::HEAD_TYPE, e).toInt() - 1);
else
2014-05-26 18:18:01 +02:00
setProperty(P_ID::HEAD_TYPE, Ms::getProperty(P_ID::HEAD_TYPE, e).toInt());
2012-08-10 17:01:35 +02:00
else if (tag == "veloType")
2014-05-26 18:18:01 +02:00
setProperty(P_ID::VELO_TYPE, Ms::getProperty(P_ID::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") {
Tie* tie = new Tie(score());
tie->setParent(this);
tie->setTrack(track());
tie->read(e);
tie->setStartNote(this);
_tieFor = tie;
2012-05-26 14:26:10 +02:00
}
else if (tag == "Fingering" || tag == "Text") { // Text is obsolete
Fingering* f = new Fingering(score());
f->setTextStyleType(TextStyleType::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") {
if (MScore::noImages)
e.skipCurrentElement();
else {
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* a = new Accidental(score());
add(a);
2012-05-26 14:26:10 +02:00
}
// TODO: for backward compatibility
bool bracket = k & 0x8000;
k &= 0xfff;
2015-04-02 10:33:53 +02:00
AccidentalType at = AccidentalType::NONE;
2012-05-26 14:26:10 +02:00
switch(k) {
2015-04-02 10:33:53 +02:00
case 0: at = AccidentalType::NONE; break;
case 1: at = AccidentalType::SHARP; break;
case 2: at = AccidentalType::FLAT; break;
case 3: at = AccidentalType::SHARP2; break;
case 4: at = AccidentalType::FLAT2; break;
case 5: at = AccidentalType::NATURAL; break;
case 6: at = AccidentalType::FLAT_SLASH; break;
case 7: at = AccidentalType::FLAT_SLASH2; break;
case 8: at = AccidentalType::MIRRORED_FLAT2; break;
case 9: at = AccidentalType::MIRRORED_FLAT; break;
case 10: at = AccidentalType::MIRRORED_FLAT_SLASH; break;
case 11: at = AccidentalType::FLAT_FLAT_SLASH; break;
case 12: at = AccidentalType::SHARP_SLASH; break;
case 13: at = AccidentalType::SHARP_SLASH2; break;
case 14: at = AccidentalType::SHARP_SLASH3; break;
case 15: at = AccidentalType::SHARP_SLASH4; break;
case 16: at = AccidentalType::SHARP_ARROW_UP; break;
case 17: at = AccidentalType::SHARP_ARROW_DOWN; break;
case 18: at = AccidentalType::SHARP_ARROW_BOTH; break;
case 19: at = AccidentalType::FLAT_ARROW_UP; break;
case 20: at = AccidentalType::FLAT_ARROW_DOWN; break;
case 21: at = AccidentalType::FLAT_ARROW_BOTH; break;
case 22: at = AccidentalType::NATURAL_ARROW_UP; break;
case 23: at = AccidentalType::NATURAL_ARROW_DOWN; break;
case 24: at = AccidentalType::NATURAL_ARROW_BOTH; break;
case 25: at = AccidentalType::SORI; break;
case 26: at = AccidentalType::KORON; break;
2012-05-26 14:26:10 +02:00
}
_accidental->setAccidentalType(at);
2012-05-26 14:26:10 +02:00
_accidental->setHasBracket(bracket);
2015-04-02 10:33:53 +02:00
_accidental->setRole(AccidentalRole::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);
2015-02-19 10:28:25 +01:00
for (int i = 0; i < MAX_DOTS; ++i) {
2012-05-26 14:26:10 +02:00
if (_dots[i] == 0) {
dot->setIdx(i);
add(dot);
dot = 0;
break;
}
}
if (dot) {
qDebug("Note: too many dots");
2012-05-26 14:26:10 +02:00
delete dot;
}
}
else if (tag == "Events") {
_playEvents.clear(); // remove default event
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()->setPlayEventType(PlayEventType::User);
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");
2014-06-24 14:45:50 +02:00
Spanner* sp = e.findSpanner(id);
2013-07-04 13:40:25 +02:00
if (sp) {
sp->setEndElement(this);
if (sp->type() == Element::Type::TIE)
_tieBack = static_cast<Tie*>(sp);
else {
if (sp->type() == Element::Type::GLISSANDO
&& parent() && parent()->type() == Element::Type::CHORD)
static_cast<Chord*>(parent())->setEndsGlissando(true);
addSpannerBack(sp);
}
2014-06-24 14:45:50 +02:00
e.removeSpanner(sp);
2012-09-12 16:19:03 +02:00
}
else {
// End of a spanner whose start element will appear later;
// may happen for cross-staff spanner from a lower to a higher staff
// (for instance a glissando from bass to treble staff of piano).
// Create a place-holder spanner with end data
// (a TextLine is used only because both Spanner or SLine are abstract,
// the actual class does not matter, as long as it is derived from Spanner)
int id = e.intAttribute("id", -1);
if (id != -1 &&
// DISABLE if pasting into a staff with linked staves
// because the glissando is not properly cloned into the linked staves
(!e.pasteMode() || !staff()->linkedStaves() || staff()->linkedStaves()->isEmpty())) {
Spanner* placeholder = new TextLine(score());
placeholder->setAnchor(Spanner::Anchor::NOTE);
placeholder->setEndElement(this);
placeholder->setTrack2(track());
placeholder->setTick(0);
placeholder->setTick2(e.tick());
e.addSpanner(id, placeholder);
}
}
2013-07-04 13:40:25 +02:00
e.readNext();
2012-09-12 16:19:03 +02:00
}
else if (tag == "TextLine"
|| tag == "Glissando") {
2013-01-18 10:55:52 +01:00
Spanner* sp = static_cast<Spanner*>(Element::name2Element(tag, score()));
// check this is not a lower-to-higher cross-staff spanner we already got
int id = e.intAttribute("id");
Spanner* placeholder = e.findSpanner(id);
if (placeholder) {
// if it is, fill end data from place-holder
sp->setAnchor(Spanner::Anchor::NOTE); // make sure we can set a Note as end element
sp->setEndElement(placeholder->endElement());
sp->setTrack2(placeholder->track2());
sp->setTick(e.tick()); // make sure tick2 will be correct
sp->setTick2(placeholder->tick2());
2015-03-01 12:19:56 +01:00
static_cast<Note*>(placeholder->endElement())->addSpannerBack(sp);
// remove no longer needed place-holder before reading the new spanner,
// as reading it also adds it to XML reader list of spanners,
// which would overwrite the place-holder
e.removeSpanner(placeholder);
delete placeholder;
}
2012-09-12 16:19:03 +02:00
sp->setTrack(track());
sp->read(e);
// DISABLE pasting of glissandi into staves with other lionked staves
// because the glissando is not properly cloned into the linked staves
if (e.pasteMode() && staff()->linkedStaves() && !staff()->linkedStaves()->isEmpty()) {
e.removeSpanner(sp); // read() added the element to the XMLReader: remove it
delete sp;
}
else {
sp->setAnchor(Spanner::Anchor::NOTE);
sp->setStartElement(this);
sp->setTick(e.tick());
addSpannerFor(sp);
sp->setParent(this);
}
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();
else if (tag == "offset") {
if (score()->mscVersion() > 114) // || voice() >= 2)
Element::readProperties(e);
else
e.skipCurrentElement(); // ignore manual layout in older scores
}
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 = limit(_pitch, 0, 127);
2014-07-18 18:38:14 +02:00
if (score()->mscVersion() < 117) {
if (concertPitch()) {
_tpc[1] = Tpc::TPC_INVALID;
}
else {
_pitch += transposition();
_tpc[1] = _tpc[0];
_tpc[0] = Tpc::TPC_INVALID;
}
2014-04-14 13:16:46 +02:00
}
if (!tpcIsValid(_tpc[0]) && !tpcIsValid(_tpc[1])) {
2014-06-20 17:07:22 +02:00
Key key = (staff() && chord()) ? staff()->key(chord()->tick()) : Key::C;
2014-06-05 11:37:21 +02:00
int tpc = pitch2tpc(_pitch, key, Prefer::NEAREST);
2014-04-14 13:16:46 +02:00
if (concertPitch())
_tpc[0] = tpc;
else
_tpc[1] = tpc;
}
if (!(tpcIsValid(_tpc[0]) && tpcIsValid(_tpc[1]))) {
Interval v = staff() ? part()->instrument()->transpose() : Interval();
2014-04-14 13:16:46 +02:00
if (tpcIsValid(_tpc[0])) {
v.flip();
if (v.isZero())
_tpc[1] = _tpc[0];
else
_tpc[1] = Ms::transposeTpc(_tpc[0], v, true);
2014-04-14 13:16:46 +02:00
}
else {
if (v.isZero())
_tpc[0] = _tpc[1];
else
_tpc[0] = Ms::transposeTpc(_tpc[1], v, true);
}
}
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// 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-24 12:09:00 +02:00
_lineOffset = lrint(data->delta.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());
}
//---------------------------------------------------------
// transposition
//---------------------------------------------------------
int Note::transposition() const
{
return staff() ? part()->instrument()->transpose().chromatic : 0;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// endDrag
//---------------------------------------------------------
void Note::endDrag()
{
dragMode = false;
if (_lineOffset == 0)
return;
int staffIdx = chord()->staffIdx() + chord()->staffMove();
Staff* staff = score()->staff(staffIdx);
int tick = chord()->tick();
if (staff->isTabStaff()) {
// 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)
// and fret for the same pitch on the new string
const StringData* strData = staff->part()->instrument()->stringData();
int nString = _string + (staff->staffType()->upsideDown() ? -_lineOffset : _lineOffset);
int nFret = strData->fret(_pitch, nString, staff, tick);
if (nFret < 0) // no fret?
return; // no party!
// move the note together with all notes tied to it
for (Note* nn : tiedNotes()) {
bool refret = false;
if (nn->fret() != nFret) {
nn->undoChangeProperty(P_ID::FRET, nFret);
refret = true;
}
if (nn->string() != nString) {
nn->undoChangeProperty(P_ID::STRING, nString);
refret = true;
}
if (refret)
strData->fretChords(nn->chord());
}
}
else {
// on PITCHED / PERCUSSION staves, dragging a note changes the note pitch
int nLine = _line + _lineOffset;
// get note context
ClefType clef = staff->clef(tick);
Key key = staff->key(tick);
// determine new pitch of dragged note
int nPitch = line2pitch(nLine, clef, key);
if (!concertPitch()) {
Interval interval = staff->part()->instrument()->transpose();
nPitch += interval.chromatic;
2014-07-15 11:01:24 +02:00
}
int tpc1 = pitch2tpc(nPitch, key, Prefer::NEAREST);
int tpc2 = pitch2tpc(nPitch - transposition(), key, Prefer::NEAREST);
// undefined for non-tablature staves
for (Note* nn : tiedNotes()) {
if (nn->pitch() != nPitch)
nn->undoChangeProperty(P_ID::PITCH, nPitch);
if (nn->_tpc[0] != tpc1)
nn->undoChangeProperty(P_ID::TPC1, tpc1);
if (nn->_tpc[1] != tpc2)
nn->undoChangeProperty(P_ID::TPC2, tpc2);
}
2012-05-26 14:26:10 +02:00
}
_lineOffset = 0;
score()->select(this, SelectType::SINGLE, 0);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// acceptDrop
//---------------------------------------------------------
2014-08-13 21:01:21 +02:00
bool Note::acceptDrop(const DropData& data) const
2012-05-26 14:26:10 +02:00
{
2014-08-13 21:01:21 +02:00
Element* e = data.element;
Element::Type type = e->type();
2015-02-25 11:43:55 +01:00
if (type == Element::Type::GLISSANDO) {
for (auto e : _spannerFor)
if (e->type() == Element::Type::GLISSANDO) {
return false;
}
return true;
}
return (type == Element::Type::ARTICULATION
|| type == Element::Type::CHORDLINE
|| type == Element::Type::TEXT
|| type == Element::Type::REHEARSAL_MARK
|| type == Element::Type::FINGERING
|| type == Element::Type::ACCIDENTAL
|| type == Element::Type::BREATH
|| type == Element::Type::ARPEGGIO
|| type == Element::Type::NOTEHEAD
|| type == Element::Type::NOTE
|| type == Element::Type::TREMOLO
|| type == Element::Type::STAFF_STATE
|| type == Element::Type::INSTRUMENT_CHANGE
|| type == Element::Type::IMAGE
|| type == Element::Type::CHORD
|| type == Element::Type::HARMONY
|| type == Element::Type::DYNAMIC
|| (type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::ACCIACCATURA)
|| (type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::APPOGGIATURA)
|| (type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::GRACE4)
|| (type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::GRACE16)
|| (type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::GRACE32)
|| (type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::GRACE8_AFTER)
|| (type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::GRACE16_AFTER)
|| (type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::GRACE32_AFTER)
|| (noteType() == NoteType::NORMAL && type == Element::Type::BAGPIPE_EMBELLISHMENT)
|| (type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::SBEAM)
|| (type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::MBEAM)
|| (type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::NBEAM)
|| (type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::BEAM32)
|| (type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::BEAM64)
|| (type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::AUTOBEAM)
|| (type == Element::Type::ICON && static_cast<Icon*>(e)->iconType() == IconType::BRACKETS)
|| (type == Element::Type::SYMBOL)
|| (type == Element::Type::CLEF)
|| (type == Element::Type::KEYSIG)
|| (type == Element::Type::TIMESIG)
|| (type == Element::Type::BAR_LINE)
|| (type == Element::Type::SLUR)
|| (type == Element::Type::HAIRPIN)
|| (type == Element::Type::STAFF_TEXT)
|| (type == Element::Type::TEMPO_TEXT)
|| (type == Element::Type::BEND)
2014-08-26 15:07:24 +02:00
|| (type == Element::Type::TREMOLOBAR)
|| (type == Element::Type::FRET_DIAGRAM));
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// drop
//---------------------------------------------------------
Element* Note::drop(const DropData& data)
{
Element* e = data.element;
bool fromPalette = (e->track() == -1);
2012-05-26 14:26:10 +02:00
Chord* ch = chord();
switch(e->type()) {
case Element::Type::REHEARSAL_MARK:
2012-05-26 14:26:10 +02:00
return ch->drop(data);
case Element::Type::SYMBOL:
case Element::Type::IMAGE:
2012-10-25 16:21:07 +02:00
e->setParent(this);
score()->undoAddElement(e);
return e;
case Element::Type::FINGERING:
2012-05-26 14:26:10 +02:00
e->setParent(this);
score()->undoAddElement(e);
2012-10-25 16:21:07 +02:00
{
// set style
Fingering* f = static_cast<Fingering*>(e);
TextStyleType st = f->textStyleType();
//f->setTextStyleType(st);
if (st >= TextStyleType::DEFAULT && fromPalette)
f->textStyle().restyle(MScore::baseStyle()->textStyle(st), score()->textStyle(st));
2012-10-25 16:21:07 +02:00
}
2012-05-26 14:26:10 +02:00
return e;
case Element::Type::SLUR:
2012-05-26 14:26:10 +02:00
delete e;
data.view->cmdAddSlur(this, 0);
return 0;
case Element::Type::HAIRPIN:
{
Hairpin* hairpin = static_cast<Hairpin*>(e);
bool decresc = (hairpin->hairpinType() == Hairpin::Type::DECRESCENDO);
delete e;
data.view->cmdAddHairpin(decresc);
}
return 0;
case Element::Type::LYRICS:
e->setParent(ch);
e->setTrack(track());
2012-05-26 14:26:10 +02:00
score()->undoAddElement(e);
return e;
case Element::Type::ACCIDENTAL:
score()->changeAccidental(this, static_cast<Accidental*>(e)->accidentalType());
2012-05-26 14:26:10 +02:00
break;
case Element::Type::BEND:
2012-05-26 14:26:10 +02:00
{
Bend* b = static_cast<Bend*>(e);
b->setParent(this);
b->setTrack(track());
score()->undoAddElement(b);
}
return e;
case Element::Type::NOTEHEAD:
2012-05-26 14:26:10 +02:00
{
NoteHead* s = static_cast<NoteHead*>(e);
NoteHead::Group group = s->headGroup();
if (group == NoteHead::Group::HEAD_INVALID) {
qDebug("unknown note head");
group = NoteHead::Group::HEAD_NORMAL;
2012-05-26 14:26:10 +02:00
}
delete s;
if (group != _headGroup) {
if (links()) {
foreach(ScoreElement* e, *links()) {
2014-05-26 18:18:01 +02:00
e->score()->undoChangeProperty(e, P_ID::HEAD_GROUP, int(group));
2012-05-26 14:26:10 +02:00
Note* note = static_cast<Note*>(e);
if (note->staff() && note->staff()->isTabStaff()
&& group == NoteHead::Group::HEAD_CROSS) {
2014-05-26 18:18:01 +02:00
e->score()->undoChangeProperty(e, P_ID::GHOST, true);
2012-05-26 14:26:10 +02:00
}
}
}
else
2014-05-26 18:18:01 +02:00
score()->undoChangeProperty(this, P_ID::HEAD_GROUP, int(group));
2012-05-26 14:26:10 +02:00
score()->select(this);
}
}
break;
case Element::Type::ICON:
2012-05-26 14:26:10 +02:00
{
switch(static_cast<Icon*>(e)->iconType()) {
case IconType::ACCIACCATURA:
2014-05-27 10:35:28 +02:00
score()->setGraceNote(ch, pitch(), NoteType::ACCIACCATURA, MScore::division/2);
2012-05-26 14:26:10 +02:00
break;
case IconType::APPOGGIATURA:
2014-05-27 10:35:28 +02:00
score()->setGraceNote(ch, pitch(), NoteType::APPOGGIATURA, MScore::division/2);
2012-05-26 14:26:10 +02:00
break;
case IconType::GRACE4:
2014-05-27 10:35:28 +02:00
score()->setGraceNote(ch, pitch(), NoteType::GRACE4, MScore::division);
2012-05-26 14:26:10 +02:00
break;
case IconType::GRACE16:
2014-05-27 10:35:28 +02:00
score()->setGraceNote(ch, pitch(), NoteType::GRACE16, MScore::division/4);
2012-05-26 14:26:10 +02:00
break;
case IconType::GRACE32:
2014-05-27 10:35:28 +02:00
score()->setGraceNote(ch, pitch(), NoteType::GRACE32, MScore::division/8);
2014-04-23 18:07:38 +02:00
break;
case IconType::GRACE8_AFTER:
2014-05-27 10:35:28 +02:00
score()->setGraceNote(ch, pitch(), NoteType::GRACE8_AFTER, MScore::division/2);
2014-04-23 18:07:38 +02:00
break;
case IconType::GRACE16_AFTER:
2014-05-27 10:35:28 +02:00
score()->setGraceNote(ch, pitch(), NoteType::GRACE16_AFTER, MScore::division/4);
2014-04-23 18:07:38 +02:00
break;
case IconType::GRACE32_AFTER:
2014-05-27 10:35:28 +02:00
score()->setGraceNote(ch, pitch(), NoteType::GRACE32_AFTER, MScore::division/8);
2012-05-26 14:26:10 +02:00
break;
case IconType::SBEAM:
case IconType::MBEAM:
case IconType::NBEAM:
case IconType::BEAM32:
case IconType::BEAM64:
case IconType::AUTOBEAM:
2012-05-26 14:26:10 +02:00
return ch->drop(data);
break;
case IconType::BRACKETS:
{
addBracket();
}
break;
default:
break;
2012-05-26 14:26:10 +02:00
}
}
delete e;
break;
case Element::Type::BAGPIPE_EMBELLISHMENT:
2013-08-01 22:24:36 +02:00
{
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;
2014-05-27 10:35:28 +02:00
score()->setGraceNote(ch, p, NoteType::GRACE32, MScore::division/8);
2013-08-01 22:24:36 +02:00
}
}
delete e;
break;
2012-05-26 14:26:10 +02:00
case Element::Type::NOTE:
2012-05-26 14:26:10 +02:00
{
Chord* ch = chord();
2014-05-27 10:35:28 +02:00
if (ch->noteType() != NoteType::NORMAL) {
2012-05-26 14:26:10 +02:00
delete e;
return 0;
}
e->setParent(ch);
score()->undoRemoveElement(this);
score()->undoAddElement(e);
}
break;
case Element::Type::GLISSANDO:
2012-05-26 14:26:10 +02:00
{
2015-02-25 11:43:55 +01:00
for (auto e : _spannerFor) {
if (e->type() == Element::Type::GLISSANDO) {
2015-02-25 19:53:10 +01:00
qDebug("there is already a glissando");
2015-02-25 11:43:55 +01:00
delete e;
return 0;
}
}
// this is the glissando initial note, look for a suitable final note
Note* finalNote = Glissando::guessFinalNote(chord());
if (finalNote != nullptr) {
// init glissando data
Glissando* gliss = static_cast<Glissando*>(e);
gliss->setAnchor(Spanner::Anchor::NOTE);
gliss->setStartElement(this);
gliss->setEndElement(finalNote);
gliss->setTick(ch->tick());
gliss->setTick2(finalNote->chord()->tick());
gliss->setTrack(track());
gliss->setTrack2(finalNote->track());
// in TAB, use straight line with no text
if (staff()->isTabStaff()) {
gliss->setGlissandoType(Glissando::Type::STRAIGHT);
gliss->setShowText(false);
}
gliss->setParent(this);
score()->undoAddElement(e);
2012-05-26 14:26:10 +02:00
}
else {
qDebug("no segment for second note of glissando found");
2012-05-26 14:26:10 +02:00
delete e;
return 0;
}
}
break;
case Element::Type::CHORD:
2012-05-26 14:26:10 +02:00
{
Chord* c = static_cast<Chord*>(e);
Note* n = c->upNote();
MScore::Direction dir = c->stemDirection();
2013-01-02 20:13:58 +01:00
int t = (staff2track(staffIdx()) + n->voice());
score()->select(0, SelectType::SINGLE, 0);
2012-05-26 14:26:10 +02:00
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;
}
//---------------------------------------------------------
// addBracket
//---------------------------------------------------------
2014-07-18 18:38:14 +02:00
void Note::addBracket()
{
Symbol* s = new Symbol(score());
s->setSym(SymId::noteheadParenthesisLeft);
s->setParent(this);
score()->undoAddElement(s);
s = new Symbol(score());
s->setSym(SymId::noteheadParenthesisRight);
s->setParent(this);
score()->undoAddElement(s);
}
2014-07-18 18:38:14 +02:00
2014-04-02 20:31:37 +02:00
//---------------------------------------------------------
// setDotY
2014-04-02 20:31:37 +02:00
//---------------------------------------------------------
void Note::setDotY(MScore::Direction pos)
2014-04-02 20:31:37 +02:00
{
bool onLine = false;
qreal y = 0;
if (staff()->isTabStaff()) {
// with TAB's, dotPosX is not set:
// get dot X from width of fret text and use TAB default spacing
2014-04-28 18:38:50 +02:00
StaffType* tab = staff()->staffType();
2014-04-02 20:31:37 +02:00
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 = -0.5;
}
// if stems beside staff, do nothing
else
return;
}
else
onLine = !(line() & 1);
bool oddVoice = voice() & 1;
if (onLine) {
// displace dots by half spatium up or down according to voice
if (pos == MScore::Direction::AUTO)
2014-04-02 20:31:37 +02:00
y = oddVoice ? 0.5 : -0.5;
else if (pos == MScore::Direction::UP)
2014-04-02 20:31:37 +02:00
y = -0.5;
else
y = 0.5;
}
else {
if (pos == MScore::Direction::UP && !oddVoice)
2014-04-02 20:31:37 +02:00
y -= 1.0;
else if (pos == MScore::Direction::DOWN && oddVoice)
2014-04-02 20:31:37 +02:00
y += 1.0;
}
y *= spatium() * staff()->lineDistance();
// apply to dots
int dots = chord()->dots();
2015-02-19 10:28:25 +01:00
for (int i = 0; i < MAX_DOTS; ++i) {
2014-04-02 20:31:37 +02:00
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)
2014-05-22 18:57:48 +02:00
dot->setVisible(visible());
2014-04-02 20:31:37 +02:00
score()->undoAddElement(dot); // move dot to _dots[i]
}
_dots[i]->layout();
_dots[i]->rypos() = y;
}
else if (_dots[i])
score()->undoRemoveElement(_dots[i]);
}
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// layout
//---------------------------------------------------------
void Note::layout()
{
bool useTablature = staff() && staff()->isTabStaff();
2012-05-26 14:26:10 +02:00
if (useTablature) {
2014-04-28 18:38:50 +02:00
StaffType* tab = staff()->staffType();
2012-05-26 14:26:10 +02:00
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 {
2013-11-11 15:11:28 +01:00
setbbox(symBbox(noteHead()));
2012-08-01 18:00:27 +02:00
if (parent() == 0)
return;
}
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// layout2
// called after final position of note is set
//---------------------------------------------------------
void Note::layout2()
{
// for standard staves this is done in Score::layoutChords3()
// so that the results are available there
if (staff()->isTabStaff())
adjustReadPos();
2012-05-26 14:26:10 +02:00
int dots = chord()->dots();
if (dots) {
2014-08-11 06:57:11 +02:00
qreal d = score()->point(score()->styleS(StyleIdx::dotNoteDistance)) * mag();
qreal dd = score()->point(score()->styleS(StyleIdx::dotDotDistance)) * mag();
2012-05-26 14:26:10 +02:00
qreal x = chord()->dotPosX() - pos().x() - chord()->pos().x();
bool layoutDots = true;
// if TAB and stems through staff
if (staff()->isTabStaff()) {
StaffType* tab = staff()->staffType();
if (tab->stemThrough()) {
// with TAB's, dot Y is not calculated during layoutChords3(),
// as layoutChords3() is not even called for TAB's;
// setDotY() actually also manages creation/deletion of NoteDot's
setDotY(MScore::Direction::AUTO);
// use TAB default note-to-dot spacing
dd = STAFFTYPE_TAB_DEFAULTDOTDIST_X * spatium();
d = dd * 0.5;
}
else {
layoutDots = false; // if !stemThrough, there are no dots at all
}
}
if (layoutDots) {
// apply to dots
for (int i = 0; i < dots; ++i) {
NoteDot* dot = _dots[i];
if (dot) {
dot->rxpos() = x + d + dd * i;
_dots[i]->adjustReadPos();
}
2012-05-26 14:26:10 +02:00
}
}
}
// 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());
if (e->type() == Element::Type::SYMBOL) {
qreal w = headWidth();
Symbol* sym = static_cast<Symbol*>(e);
2014-07-15 15:05:36 +02:00
QPointF rp = e->readPos();
e->layout();
if (sym->sym() == SymId::noteheadParenthesisRight) {
if (staff()->isTabStaff()) {
StaffType* tab = staff()->staffType();
w = tabHeadWidth(tab);
}
e->rxpos() += w;
}
else if (sym->sym() == SymId::noteheadParenthesisLeft) {
e->rxpos() -= symWidth(SymId::noteheadParenthesisLeft);
}
if (sym->sym() == SymId::noteheadParenthesisLeft || sym->sym() == SymId::noteheadParenthesisRight) {
// adjustReadPos() was called too early in layout(), adjust:
if (!rp.isNull()) {
e->setUserOff(QPointF());
e->setReadPos(rp);
e->adjustReadPos();
}
2014-07-15 15:05:36 +02:00
}
}
2014-07-15 15:05:36 +02:00
else
e->layout();
2012-05-26 14:26:10 +02:00
}
}
//---------------------------------------------------------
// dotIsUp
//---------------------------------------------------------
bool Note::dotIsUp() const
{
if (_dots[0] == 0)
return true;
if (_userDotPosition == MScore::Direction::AUTO)
2014-04-02 22:37:13 +02:00
return _dots[0]->y() < spatium() * .1;
else
return (_userDotPosition == MScore::Direction::UP);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// updateAccidental
// set _accidental and _line depending on tpc
//---------------------------------------------------------
void Note::updateAccidental(AccidentalState* as)
{
int relLine = absStep(tpc(), epitch());
// don't touch accidentals that don't concern tpc such as
// quarter tones
2015-04-02 10:33:53 +02:00
if (!(_accidental && _accidental->accidentalType() > AccidentalType::NATURAL)) {
// calculate accidental
2015-04-02 10:33:53 +02:00
AccidentalType acci = AccidentalType::NONE;
AccidentalVal accVal = tpc2alter(tpc());
if ((accVal != as->accidentalVal(relLine)) || hidden() || as->tieContext(relLine)) {
as->setAccidentalVal(relLine, accVal, _tieBack != 0);
if (_tieBack)
2015-04-02 10:33:53 +02:00
acci = AccidentalType::NONE;
else {
acci = Accidental::value2subtype(accVal);
2015-04-02 10:33:53 +02:00
if (acci == AccidentalType::NONE)
acci = AccidentalType::NATURAL;
}
}
2015-04-02 10:33:53 +02:00
if (acci != AccidentalType::NONE && !_tieBack && !_hidden) {
if (_accidental == 0) {
Accidental* a = new Accidental(score());
a->setParent(this);
a->setAccidentalType(acci);
score()->undoAddElement(a);
}
else if (_accidental->accidentalType() != acci) {
Accidental* a = _accidental->clone();
a->setParent(this);
a->setAccidentalType(acci);
score()->undoChangeElement(_accidental, a);
}
}
else {
if (_accidental) {
// remove this if it was AUTO:
2015-04-02 10:33:53 +02:00
if (_accidental->role() == AccidentalRole::AUTO)
score()->undoRemoveElement(_accidental);
else {
// keep it, but update type if needed
acci = Accidental::value2subtype(accVal);
2015-04-02 10:33:53 +02:00
if (acci == AccidentalType::NONE)
acci = AccidentalType::NATURAL;
if (_accidental->accidentalType() != acci) {
Accidental* a = _accidental->clone();
a->setParent(this);
a->setAccidentalType(acci);
score()->undoChangeElement(_accidental, a);
}
}
}
}
}
else {
// microtonal accidentals playback as naturals
// in 1.X, they had no effect on accidental state of measure
// ultimetely, they should probably get their own state
// for now, at least change state to natural, so subsequent notes playback as might be expected
// this is an incompatible change, but better to break it for 2.0 than wait until later
as->setAccidentalVal(relLine, AccidentalVal::NATURAL, _tieBack != 0);
}
updateRelLine(relLine, true);
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// noteType
//---------------------------------------------------------
NoteType Note::noteType() const
{
return chord()->noteType();
}
//---------------------------------------------------------
// noteTypeUserName
//---------------------------------------------------------
QString Note::noteTypeUserName()
{
switch (noteType()) {
case NoteType::ACCIACCATURA:
return tr("Acciaccatura");
case NoteType::APPOGGIATURA:
return tr("Appoggiatura");
case NoteType::GRACE8_AFTER:
case NoteType::GRACE16_AFTER:
case NoteType::GRACE32_AFTER:
return tr("Grace note after");
case NoteType::GRACE4:
case NoteType::GRACE16:
2014-08-20 09:50:42 +02:00
case NoteType::GRACE32:
return tr("Grace note before");
case NoteType::INVALID:
return tr("Invalid note");
default:
return tr("Note");
}
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// 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);
}
for (Spanner* s : _spannerFor) {
s->setTrack(val);
}
for (Spanner* s : _spannerBack) {
s->setTrack2(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
{
2014-05-26 18:18:01 +02:00
score()->undoChangeProperty(this, P_ID::USER_OFF, QPointF());
score()->undoChangeProperty(chord(), P_ID::USER_OFF, QPointF());
score()->undoChangeProperty(chord(), P_ID::STEM_DIRECTION, int(MScore::Direction::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)
2014-05-26 15:31:36 +02:00
m *= score()->styleD(StyleIdx::smallNoteMag);
2013-05-28 15:42:02 +02:00
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;
}
2015-02-19 10:28:25 +01:00
//---------------------------------------------------------
// line
//---------------------------------------------------------
int Note::line() const
{
if (_fixed)
return _fixedLine;
else
return _line + _lineOffset;
}
//---------------------------------------------------------
// setLine
//---------------------------------------------------------
2012-05-26 14:26:10 +02:00
void Note::setLine(int n)
{
_line = n;
2014-08-23 17:39:59 +02:00
int off = 0;
if (staff())
off = staff()->staffType()->stepOffset();
rypos() = (_line + off) * spatium() * .5;
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(NoteHead::Group val)
2012-05-26 14:26:10 +02:00
{
Q_ASSERT(int(val) >= 0 && int(val) < int(NoteHead::Group::HEAD_GROUPS));
2012-05-26 14:26:10 +02:00
_headGroup = val;
}
//---------------------------------------------------------
// ppitch
// playback pitch
//---------------------------------------------------------
int Note::ppitch() const
{
return _pitch + staff()->pitchOffset(chord()->segment()->tick());
2014-04-02 18:11:56 +02:00
}
//---------------------------------------------------------
// epitch
// effective pitch
// honours transposing instruments
//---------------------------------------------------------
int Note::epitch() const
{
return _pitch - (concertPitch() ? 0 : transposition());
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
{
2014-05-07 18:09:01 +02:00
if (veloType() == ValueType::OFFSET_VAL)
2012-05-26 14:26:10 +02:00
velo = velo + (velo * veloOffset()) / 100;
2014-05-07 18:09:01 +02:00
else if (veloType() == ValueType::USER_VAL)
2012-05-26 14:26:10 +02:00
velo = veloOffset();
return limit(velo, 1, 127);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// endEdit
//---------------------------------------------------------
void Note::endEdit()
{
Chord* ch = chord();
if (ch->notes().size() == 1) {
2014-05-26 18:18:01 +02:00
score()->undoChangeProperty(ch, P_ID::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
}
}
2014-04-09 16:09:21 +02:00
//---------------------------------------------------------
// updateRelLine
// calculate the real note line depending on clef,
// _line is the absolute line
//---------------------------------------------------------
2014-04-22 17:02:03 +02:00
void Note::updateRelLine(int relLine, bool undoable)
2014-04-09 16:09:21 +02:00
{
if (staff() && chord()->staffMove()) {
// check that destination staff makes sense (might have been deleted)
int idx = staffIdx() + chord()->staffMove();
int minStaff = part()->startTrack() / VOICES;
int maxStaff = part()->endTrack() / VOICES;
if (idx < minStaff || idx >= maxStaff || score()->staff(idx)->staffGroup() != staff()->staffGroup())
chord()->undoChangeProperty(P_ID::STAFF_MOVE, 0);
}
2014-08-20 09:50:42 +02:00
2012-05-26 14:26:10 +02:00
Staff* s = score()->staff(staffIdx() + chord()->staffMove());
2014-04-09 16:09:21 +02:00
ClefType clef = s->clef(chord()->tick());
2014-04-22 17:02:03 +02:00
int line = relStep(relLine, clef);
2014-04-09 16:09:21 +02:00
if (line != _line) {
2014-04-22 17:02:03 +02:00
if (undoable)
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::LINE, line);
2014-04-22 17:02:03 +02:00
else
setLine(line);
2014-04-09 16:09:21 +02:00
}
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// updateLine
//---------------------------------------------------------
void Note::updateLine()
{
2014-04-22 17:02:03 +02:00
int relLine = absStep(tpc(), epitch());
updateRelLine(relLine, false);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// setNval
2014-09-22 14:31:28 +02:00
// set note properties from NoteVal
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
void Note::setNval(const NoteVal& nval, int tick)
2012-05-26 14:26:10 +02:00
{
setPitch(nval.pitch);
2014-06-26 11:41:18 +02:00
_fret = nval.fret;
_string = nval.string;
2014-09-22 14:31:28 +02:00
_tpc[0] = nval.tpc1;
_tpc[1] = nval.tpc2;
Interval v = part()->instrument()->transpose();
2014-09-22 14:31:28 +02:00
if (nval.tpc1 == Tpc::TPC_INVALID) {
if (tick == -1)
tick = chord()->tick();
Key key = staff()->key(tick);
if (!concertPitch() && !v.isZero())
key = transposeKey(key, v);
_tpc[0] = pitch2tpc(nval.pitch, key, Prefer::NEAREST);
2014-09-22 14:31:28 +02:00
}
if (nval.tpc2 == Tpc::TPC_INVALID) {
2014-04-17 12:32:10 +02:00
if (v.isZero())
_tpc[1] = _tpc[0];
else {
v.flip();
_tpc[1] = Ms::transposeTpc(_tpc[0], v, true);
2014-04-17 12:32:10 +02:00
}
2014-04-11 14:50:53 +02:00
}
_headGroup = NoteHead::Group(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) {
2014-05-26 18:18:01 +02:00
case P_ID::PITCH:
2012-08-10 17:01:35 +02:00
return pitch();
2014-05-26 18:18:01 +02:00
case P_ID::TPC1:
return _tpc[0];
2014-05-26 18:18:01 +02:00
case P_ID::TPC2:
return _tpc[1];
2014-05-26 18:18:01 +02:00
case P_ID::SMALL:
2012-08-10 17:01:35 +02:00
return small();
2014-05-26 18:18:01 +02:00
case P_ID::MIRROR_HEAD:
2014-05-07 18:09:01 +02:00
return int(userMirror());
2014-05-26 18:18:01 +02:00
case P_ID::DOT_POSITION:
2014-05-07 18:09:01 +02:00
return int(userDotPosition());
2014-05-26 18:18:01 +02:00
case P_ID::HEAD_GROUP:
return int(headGroup());
2014-05-26 18:18:01 +02:00
case P_ID::VELO_OFFSET:
2012-08-10 17:01:35 +02:00
return veloOffset();
2014-05-26 18:18:01 +02:00
case P_ID::TUNING:
2012-08-10 17:01:35 +02:00
return tuning();
2014-05-26 18:18:01 +02:00
case P_ID::FRET:
2012-08-10 17:01:35 +02:00
return fret();
2014-05-26 18:18:01 +02:00
case P_ID::STRING:
2012-08-10 17:01:35 +02:00
return string();
2014-05-26 18:18:01 +02:00
case P_ID::GHOST:
2012-08-10 17:01:35 +02:00
return ghost();
2014-05-26 18:18:01 +02:00
case P_ID::HEAD_TYPE:
return int(headType());
2014-05-26 18:18:01 +02:00
case P_ID::VELO_TYPE:
2014-05-07 18:09:01 +02:00
return int(veloType());
2014-05-26 18:18:01 +02:00
case P_ID::PLAY:
return play();
2014-05-26 18:18:01 +02:00
case P_ID::LINE:
2014-04-22 17:02:03 +02:00
return _line;
case P_ID::FIXED:
return fixed();
case P_ID::FIXED_LINE:
return fixedLine();
2012-08-10 17:01:35 +02:00
default:
break;
}
return Element::getProperty(propertyId);
}
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
bool Note::setProperty(P_ID propertyId, const QVariant& v)
{
2015-01-05 15:15:48 +01:00
Measure* m = chord() ? chord()->measure() : nullptr;
2012-08-10 17:01:35 +02:00
switch(propertyId) {
2014-05-26 18:18:01 +02:00
case P_ID::PITCH:
2012-08-10 17:01:35 +02:00
setPitch(v.toInt());
2015-01-30 17:03:51 +01:00
score()->setPlaylistDirty();
2012-08-10 17:01:35 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::TPC1:
_tpc[0] = v.toInt();
break;
2014-05-26 18:18:01 +02:00
case P_ID::TPC2:
_tpc[1] = v.toInt();
2014-04-22 17:02:03 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::LINE:
2014-04-22 17:02:03 +02:00
_line = v.toInt();
2012-08-10 17:01:35 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::SMALL:
2012-08-10 17:01:35 +02:00
setSmall(v.toBool());
break;
2014-05-26 18:18:01 +02:00
case P_ID::MIRROR_HEAD:
setUserMirror(MScore::DirectionH(v.toInt()));
2012-08-10 17:01:35 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::DOT_POSITION:
setUserDotPosition(MScore::Direction(v.toInt()));
2012-08-10 17:01:35 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::HEAD_GROUP:
setHeadGroup(NoteHead::Group(v.toInt()));
2012-08-10 17:01:35 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::VELO_OFFSET:
2012-08-10 17:01:35 +02:00
setVeloOffset(v.toInt());
2015-01-30 17:03:51 +01:00
score()->setPlaylistDirty();
2012-08-10 17:01:35 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::TUNING:
2012-08-10 17:01:35 +02:00
setTuning(v.toDouble());
2015-01-30 17:03:51 +01:00
score()->setPlaylistDirty();
2012-08-10 17:01:35 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::FRET:
2012-08-10 17:01:35 +02:00
setFret(v.toInt());
break;
2014-05-26 18:18:01 +02:00
case P_ID::STRING:
2012-08-10 17:01:35 +02:00
setString(v.toInt());
break;
2014-05-26 18:18:01 +02:00
case P_ID::GHOST:
2012-08-10 17:01:35 +02:00
setGhost(v.toBool());
break;
2014-05-26 18:18:01 +02:00
case P_ID::HEAD_TYPE:
setHeadType(NoteHead::Type(v.toInt()));
2012-08-10 17:01:35 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::VELO_TYPE:
2014-05-07 18:09:01 +02:00
setVeloType(ValueType(v.toInt()));
2015-01-30 17:03:51 +01:00
score()->setPlaylistDirty();
2012-08-10 17:01:35 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::VISIBLE: { // P_ID::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());
}
if (m)
m->checkMultiVoices(chord()->staffIdx());
break;
}
2014-05-26 18:18:01 +02:00
case P_ID::PLAY:
setPlay(v.toBool());
2015-01-30 17:03:51 +01:00
score()->setPlaylistDirty();
break;
case P_ID::FIXED:
setFixed(v.toBool());
break;
case P_ID::FIXED_LINE:
setFixedLine(v.toInt());
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)
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::FRET, val);
2012-08-12 11:44:36 +02:00
}
//---------------------------------------------------------
// undoSetString
//---------------------------------------------------------
void Note::undoSetString(int val)
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::STRING, val);
2012-08-12 11:44:36 +02:00
}
//---------------------------------------------------------
// undoSetGhost
//---------------------------------------------------------
void Note::undoSetGhost(bool val)
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::GHOST, val);
2012-08-12 11:44:36 +02:00
}
//---------------------------------------------------------
// undoSetSmall
//---------------------------------------------------------
void Note::undoSetSmall(bool val)
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::SMALL, val);
2012-08-12 11:44:36 +02:00
}
//---------------------------------------------------------
// undoSetPlay
//---------------------------------------------------------
void Note::undoSetPlay(bool val)
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::PLAY, val);
}
2012-08-12 11:44:36 +02:00
//---------------------------------------------------------
// undoSetTuning
//---------------------------------------------------------
void Note::undoSetTuning(qreal val)
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::TUNING, val);
2012-08-12 11:44:36 +02:00
}
//---------------------------------------------------------
// undoSetVeloType
//---------------------------------------------------------
2014-05-07 18:09:01 +02:00
void Note::undoSetVeloType(ValueType val)
2012-08-12 11:44:36 +02:00
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::VELO_TYPE, int(val));
2012-08-12 11:44:36 +02:00
}
//---------------------------------------------------------
// undoSetVeloOffset
//---------------------------------------------------------
void Note::undoSetVeloOffset(int val)
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::VELO_OFFSET, val);
2012-08-12 11:44:36 +02:00
}
//---------------------------------------------------------
// undoSetUserMirror
//---------------------------------------------------------
void Note::undoSetUserMirror(MScore::DirectionH val)
2012-08-12 11:44:36 +02:00
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::MIRROR_HEAD, int(val));
2012-08-12 11:44:36 +02:00
}
//---------------------------------------------------------
2014-04-02 20:31:37 +02:00
// undoSetUserDotPosition
2012-08-12 11:44:36 +02:00
//---------------------------------------------------------
void Note::undoSetUserDotPosition(MScore::Direction val)
2012-08-12 11:44:36 +02:00
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::DOT_POSITION, int(val));
2012-08-12 11:44:36 +02:00
}
//---------------------------------------------------------
// undoSetHeadGroup
//---------------------------------------------------------
void Note::undoSetHeadGroup(NoteHead::Group val)
2012-08-12 11:44:36 +02:00
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::HEAD_GROUP, int(val));
2012-08-12 11:44:36 +02:00
}
2013-01-28 21:45:36 +01:00
//---------------------------------------------------------
// setHeadType
//---------------------------------------------------------
void Note::setHeadType(NoteHead::Type t)
2013-01-28 21:45:36 +01:00
{
_headType = t;
}
2012-08-12 11:44:36 +02:00
//---------------------------------------------------------
// undoSetHeadType
//---------------------------------------------------------
void Note::undoSetHeadType(NoteHead::Type val)
2012-08-12 11:44:36 +02:00
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::HEAD_TYPE, int(val));
2012-08-12 11:44:36 +02:00
}
2012-08-10 17:01:35 +02:00
//---------------------------------------------------------
// propertyDefault
//---------------------------------------------------------
QVariant Note::propertyDefault(P_ID propertyId) const
{
switch(propertyId) {
2014-05-26 18:18:01 +02:00
case P_ID::GHOST:
case P_ID::SMALL:
2012-08-10 17:01:35 +02:00
return false;
2014-05-26 18:18:01 +02:00
case P_ID::MIRROR_HEAD:
return int(MScore::DirectionH::AUTO);
2014-05-26 18:18:01 +02:00
case P_ID::DOT_POSITION:
return int(MScore::Direction::AUTO);
2014-05-26 18:18:01 +02:00
case P_ID::HEAD_GROUP:
return int(NoteHead::Group::HEAD_NORMAL);
2014-05-26 18:18:01 +02:00
case P_ID::VELO_OFFSET:
2012-08-10 17:01:35 +02:00
return 0;
2014-05-26 18:18:01 +02:00
case P_ID::TUNING:
2012-08-10 17:01:35 +02:00
return 0.0;
2014-05-26 18:18:01 +02:00
case P_ID::FRET:
case P_ID::STRING:
2012-08-10 17:01:35 +02:00
return -1;
2014-05-26 18:18:01 +02:00
case P_ID::HEAD_TYPE:
return int(NoteHead::Type::HEAD_AUTO);
2014-05-26 18:18:01 +02:00
case P_ID::VELO_TYPE:
2014-05-07 18:09:01 +02:00
return int (ValueType::OFFSET_VAL);
2014-05-26 18:18:01 +02:00
case P_ID::PLAY:
return true;
case P_ID::FIXED:
return false;
case P_ID::FIXED_LINE:
return 0;
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 val)
2012-11-19 09:29:46 +01:00
{
_playEvents[0].setOntime(val);
chord()->setPlayEventType(PlayEventType::User);
2012-11-19 09:29:46 +01:00
}
//---------------------------------------------------------
// setOffTimeOffset
//---------------------------------------------------------
void Note::setOffTimeOffset(int val)
2012-11-19 09:29:46 +01:00
{
_playEvents[0].setLen(val - _playEvents[0].ontime());
chord()->setPlayEventType(PlayEventType::User);
2012-11-19 09:29:46 +01:00
}
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);
}
2014-06-26 11:41:18 +02:00
//---------------------------------------------------------
// accessibleInfo
//---------------------------------------------------------
QString Note::accessibleInfo()
{
QString duration = chord()->durationUserName();
QString voice = tr("Voice: %1").arg(QString::number(track() % VOICES + 1));
QString pitchName;
2015-04-12 10:17:00 +02:00
const Drumset* drumset = part()->instrument()->drumset();
if (fixed() && headGroup() == NoteHead::Group::HEAD_SLASH)
pitchName = chord()->noStem() ? tr("Beat Slash") : tr("Rhythm Slash");
2015-04-12 10:17:00 +02:00
else if (staff()->isDrumStaff() && drumset)
pitchName = qApp->translate("drumset", drumset->name(pitch()).toUtf8().constData());
else
pitchName = tpcUserName(false);
return tr("%1; Pitch: %2; Duration: %3%4").arg(noteTypeUserName()).arg(pitchName).arg(duration).arg((chord()->isGrace() ? "" : QString("; %1").arg(voice)));
}
//---------------------------------------------------------
// screenReaderInfo
//---------------------------------------------------------
QString Note::screenReaderInfo()
{
QString duration = chord()->durationUserName();
QString voice = tr("Voice: %1").arg(QString::number(track() % VOICES + 1));
QString pitchName;
2015-04-12 10:17:00 +02:00
const Drumset* drumset = part()->instrument()->drumset();
if (fixed() && headGroup() == NoteHead::Group::HEAD_SLASH)
pitchName = chord()->noStem() ? tr("Beat Slash") : tr("Rhythm Slash");
2015-04-12 10:17:00 +02:00
else if (staff()->isDrumStaff() && drumset)
pitchName = qApp->translate("drumset", drumset->name(pitch()).toUtf8().constData());
else
pitchName = tpcUserName(true);
return QString("%1 %2 %3%4").arg(noteTypeUserName()).arg(pitchName).arg(duration).arg((chord()->isGrace() ? "" : QString("; %1").arg(voice)));
}
//---------------------------------------------------------
// accessibleExtraInfo
//---------------------------------------------------------
QString Note::accessibleExtraInfo()
{
QString rez = "";
if (accidental()) {
rez = QString("%1 %2").arg(rez).arg(accidental()->screenReaderInfo());
}
if (!el().isEmpty()) {
foreach (Element* e, el()) {
if (!score()->selectionFilter().canSelect(e)) continue;
rez = QString("%1 %2").arg(rez).arg(e->screenReaderInfo());
}
}
if (tieFor())
rez = tr("%1 Start of %2").arg(rez).arg(tieFor()->screenReaderInfo());
if (tieBack())
rez = tr("%1 End of %2").arg(rez).arg(tieBack()->screenReaderInfo());
if (!spannerFor().isEmpty()) {
foreach (Spanner* s, spannerFor()) {
if (!score()->selectionFilter().canSelect(s)) continue;
rez = tr("%1 Start of %2").arg(rez).arg(s->screenReaderInfo());
}
}
if (!spannerBack().isEmpty()) {
foreach (Spanner* s, spannerBack()) {
if (!score()->selectionFilter().canSelect(s)) continue;
rez = tr("%1 End of %2").arg(rez).arg(s->screenReaderInfo());
}
}
rez = QString("%1 %2").arg(rez).arg(chord()->accessibleExtraInfo());
return rez;
}
2014-06-26 11:41:18 +02:00
//---------------------------------------------------------
// noteVal
//---------------------------------------------------------
NoteVal Note::noteVal() const
{
NoteVal nval;
nval.pitch = pitch();
2014-09-22 14:31:28 +02:00
nval.tpc1 = tpc1();
nval.tpc2 = tpc2();
2014-06-26 11:41:18 +02:00
nval.fret = fret();
nval.string = string();
nval.headGroup = headGroup();
return nval;
}
//---------------------------------------------------------
2015-03-19 10:39:18 +01:00
// qmlDotsCount
// returns number of dots for plugins
//---------------------------------------------------------
2015-03-19 10:39:18 +01:00
int Note::qmlDotsCount()
{
2015-02-19 10:28:25 +01:00
int i = 0;
for (NoteDot* dot : _dots)
2015-02-19 10:28:25 +01:00
if (dot)
++i;
return i;
}
2015-02-19 10:28:25 +01:00
//---------------------------------------------------------
// groupToGroupName
//---------------------------------------------------------
const char* NoteHead::groupToGroupName(NoteHead::Group group)
{
return noteHeadNames[int(group)];
}
2015-02-19 10:28:25 +01:00
//---------------------------------------------------------
// subtypeName
//---------------------------------------------------------
QString Note::subtypeName() const
{
return qApp->translate("noteheadnames", NoteHead::groupToGroupName(_headGroup));
}
//---------------------------------------------------------
// nextElement
//---------------------------------------------------------
Element* Note::nextElement()
{
if (chord()->isGrace())
return Element::nextElement();
QList<Note*> notes = chord()->notes();
int idx = notes.indexOf(this);
if (idx == 0)
return chord()->nextElement();
return notes.at(idx - 1);
}
//---------------------------------------------------------
// prevElement
//---------------------------------------------------------
Element* Note::prevElement()
{
if (chord()->isGrace())
return Element::prevElement();
QList<Note*> notes = chord()->notes();
int idx = notes.indexOf(this);
if (idx == notes.size() - 1)
return chord()->prevElement();
return notes.at(idx + 1);
}
//---------------------------------------------------------
// lastTiedNote
//---------------------------------------------------------
Note* Note::lastTiedNote() const
{
QList<Note*> notes;
Note* note = const_cast<Note*>(this);
notes.append(note);
while (note->tieFor()) {
if (notes.contains(note->tieFor()->endNote()))
break;
note = note->tieFor()->endNote();
notes.append(note);
}
return note;
}
//---------------------------------------------------------
// firstTiedNote
// if note has ties, return last note in chain
// - handle recursion in connected notes
//---------------------------------------------------------
Note* Note::firstTiedNote() const
{
QList<Note*> notes;
Note* note = const_cast<Note*>(this);
notes.append(note);
while (note->tieBack()) {
if (notes.contains(note->tieBack()->startNote()))
break;
note = note->tieBack()->startNote();
notes.append(note);
}
return note;
}
//---------------------------------------------------------
// tiedNotes
//---------------------------------------------------------
QList<Note*> Note::tiedNotes() const
{
QList<Note*> notes;
Note* note = firstTiedNote();
notes.append(note);
while (note->tieFor()) {
if (notes.contains(note->tieFor()->endNote()))
break;
note = note->tieFor()->endNote();
notes.append(note);
}
return notes;
}
2015-04-03 18:23:51 +02:00
//---------------------------------------------------------
// accidentalType
//---------------------------------------------------------
AccidentalType Note::accidentalType() const
{
return _accidental ? _accidental->accidentalType() : AccidentalType::NONE;
}
//---------------------------------------------------------
// setAccidentalType
//---------------------------------------------------------
void Note::setAccidentalType(AccidentalType type)
{
if (_score)
_score->changeAccidental(this, type);
}
}