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"
|
2013-12-05 21:37:28 +01:00
|
|
|
#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"
|
2012-12-11 20:37:35 +01:00
|
|
|
#include "glissando.h"
|
2013-08-01 22:24:36 +02:00
|
|
|
#include "bagpembell.h"
|
2014-12-16 18:07:51 +01:00
|
|
|
#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
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-25 22:01:26 +02:00
|
|
|
static const SymId noteHeads[2][int(NoteHead::Group::HEAD_GROUPS)][int(NoteHead::Type::HEAD_TYPES)] = {
|
2013-11-15 11:18:16 +01:00
|
|
|
// 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 },
|
2013-11-15 11:18:16 +01:00
|
|
|
// { SymId(wholeslashheadSym), SymId(halfslashheadSym), SymId(quartslashheadSym), SymId(wholeslashheadSym)},
|
2014-04-18 18:55:33 +02:00
|
|
|
{ SymId::noteheadSlashWhiteWhole, SymId::noteheadSlashWhiteHalf, SymId::noteheadSlashHorizontalEnds, SymId::noteheadSlashWhiteWhole},
|
2013-11-15 11:18:16 +01:00
|
|
|
// { 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 },
|
2013-11-15 11:18:16 +01:00
|
|
|
// { SymId(s0reHeadSym), SymId(d1reHeadSym), SymId(d2reHeadSym), SymId::noSym },
|
2013-11-15 14:41:00 +01:00
|
|
|
{ SymId::noteShapeMoonWhite, SymId::noteShapeMoonWhite, SymId::noteShapeMoonBlack, SymId::noSym },
|
2013-11-15 11:18:16 +01:00
|
|
|
// { SymId(d0faHeadSym), SymId(d1faHeadSym), SymId(d2faHeadSym), SymId::noSym },
|
2013-11-15 14:41:00 +01:00
|
|
|
{ SymId::noteShapeTriangleRightWhite, SymId::noteShapeTriangleRightWhite, SymId::noteShapeTriangleRightBlack, SymId::noSym },
|
2013-11-15 11:18:16 +01:00
|
|
|
// { SymId(s0laHeadSym), SymId(s1laHeadSym), SymId(s2laHeadSym), SymId::noSym },
|
2013-11-15 14:41:00 +01:00
|
|
|
{ SymId::noteShapeSquareWhite, SymId::noteShapeSquareWhite, SymId::noteShapeSquareBlack, SymId::noSym },
|
2013-11-15 11:18:16 +01:00
|
|
|
// { SymId(s0tiHeadSym), SymId(d1tiHeadSym), SymId(d2tiHeadSym), SymId::noSym },
|
2013-11-15 14:41:00 +01:00
|
|
|
{ SymId::noteShapeTriangleRoundWhite, SymId::noteShapeTriangleRoundWhite, SymId::noteShapeTriangleRoundBlack, SymId::noSym },
|
2013-11-15 11:18:16 +01:00
|
|
|
// { SymId(s0solHeadSym), SymId(s1solHeadSym), SymId(s2solHeadSym), SymId::noSym },
|
2013-11-15 14:41:00 +01:00
|
|
|
{ SymId::noteShapeRoundWhite, SymId::noteShapeRoundWhite, SymId::noteShapeRoundBlack, SymId::noSym },
|
2013-11-29 00:37:48 +01:00
|
|
|
{ SymId::noteheadWhole, SymId::noteheadHalf, SymId::noteheadBlack, SymId::noteheadDoubleWholeSquare },
|
2013-11-15 11:18:16 +01:00
|
|
|
},
|
|
|
|
{ // 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 },
|
2013-11-15 11:18:16 +01:00
|
|
|
// { SymId(s0miHeadSym), SymId(s1miHeadSym), SymId(s2miHeadSym), SymId::noSym },
|
2013-11-15 14:41:00 +01:00
|
|
|
{ SymId::noteShapeDiamondWhite, SymId::noteShapeDiamondWhite, SymId::noteShapeDiamondBlack, SymId::noSym },
|
2013-11-15 11:18:16 +01:00
|
|
|
// { SymId(wholeslashheadSym), SymId(halfslashheadSym), SymId(quartslashheadSym), SymId(wholeslashheadSym)},
|
2014-04-18 18:55:33 +02:00
|
|
|
{ SymId::noteheadSlashWhiteWhole, SymId::noteheadSlashWhiteHalf, SymId::noteheadSlashHorizontalEnds, SymId::noteheadSlashWhiteWhole},
|
2013-11-15 11:18:16 +01:00
|
|
|
// { SymId(xcircledheadSym), SymId(xcircledheadSym), SymId(xcircledheadSym), SymId(xcircledheadSym) },
|
2013-11-15 14:41:00 +01:00
|
|
|
{ SymId::noteheadCircleXWhole, SymId::noteheadCircleXHalf, SymId::noteheadCircleX, SymId::noteheadCircleXDoubleWhole},
|
2013-11-15 11:18:16 +01:00
|
|
|
// { SymId(s0doHeadSym), SymId(d1doHeadSym), SymId(d2doHeadSym), SymId::noSym },
|
2013-11-15 14:41:00 +01:00
|
|
|
{ SymId::noteShapeTriangleUpWhite, SymId::noteShapeTriangleUpWhite, SymId::noteShapeTriangleUpBlack, SymId::noSym },
|
2013-11-15 11:18:16 +01:00
|
|
|
// { SymId(s0reHeadSym), SymId(d1reHeadSym), SymId(d2reHeadSym), SymId::noSym },
|
2013-11-15 14:41:00 +01:00
|
|
|
{ SymId::noteShapeMoonWhite, SymId::noteShapeMoonWhite, SymId::noteShapeMoonBlack, SymId::noSym },
|
2013-11-15 11:18:16 +01:00
|
|
|
// { SymId(d0faHeadSym), SymId(d1faHeadSym), SymId(d2faHeadSym), SymId::noSym },
|
2014-10-01 10:45:36 +02:00
|
|
|
{ SymId::noteShapeTriangleLeftWhite, SymId::noteShapeTriangleLeftWhite, SymId::noteShapeTriangleLeftBlack, SymId::noSym },
|
2013-11-15 11:18:16 +01:00
|
|
|
// { SymId(s0laHeadSym), SymId(s1laHeadSym), SymId(s2laHeadSym), SymId::noSym },
|
2013-11-15 14:41:00 +01:00
|
|
|
{ SymId::noteShapeSquareWhite, SymId::noteShapeSquareWhite, SymId::noteShapeSquareBlack, SymId::noSym },
|
2013-11-15 11:18:16 +01:00
|
|
|
// { SymId(s0tiHeadSym), SymId(d1tiHeadSym), SymId(d2tiHeadSym), SymId::noSym },
|
2013-11-15 14:41:00 +01:00
|
|
|
{ SymId::noteShapeTriangleRoundWhite, SymId::noteShapeTriangleRoundWhite, SymId::noteShapeTriangleRoundBlack, SymId::noSym },
|
2013-11-15 11:18:16 +01:00
|
|
|
// { SymId(s0solHeadSym), SymId(s1solHeadSym), SymId(s2solHeadSym), SymId::noSym },
|
2013-11-15 14:41:00 +01:00
|
|
|
{ SymId::noteShapeRoundWhite, SymId::noteShapeRoundWhite, SymId::noteShapeRoundBlack, SymId::noSym },
|
2013-11-29 00:37:48 +01:00
|
|
|
{ SymId::noteheadWhole, SymId::noteheadHalf, SymId::noteheadBlack, SymId::noteheadDoubleWholeSquare },
|
2013-11-15 11:18:16 +01:00
|
|
|
}
|
|
|
|
};
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-08-13 11:22:21 +02:00
|
|
|
// same order as NoteHead::Group
|
2014-07-22 14:26:24 +02:00
|
|
|
static const char* noteHeadNames[] = {
|
2014-10-15 19:57:54 +02:00
|
|
|
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")
|
2014-07-22 14:26:24 +02:00
|
|
|
};
|
|
|
|
|
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
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-25 22:01:26 +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");
|
2012-09-08 11:33:46 +02:00
|
|
|
xml.tag("name", Sym::id2name(_sym));
|
2012-05-26 14:26:10 +02:00
|
|
|
Element::writeProperties(xml);
|
|
|
|
xml.etag();
|
|
|
|
}
|
|
|
|
|
2013-11-25 15:02:23 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// headGroup
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-25 21:18:26 +02:00
|
|
|
NoteHead::Group NoteHead::headGroup() const
|
2013-11-25 15:02:23 +01:00
|
|
|
{
|
2014-06-25 21:18:26 +02:00
|
|
|
Group group = Group::HEAD_INVALID;
|
2013-11-25 15:02:23 +01:00
|
|
|
|
2014-06-25 21:18:26 +02:00
|
|
|
for (int i = 0; i < int(Group::HEAD_GROUPS); ++i) {
|
2013-11-25 15:02:23 +01:00
|
|
|
if (noteHeads[0][i][1] == _sym || noteHeads[0][i][3] == _sym) {
|
2014-06-25 21:18:26 +02:00
|
|
|
group = (Group)i;
|
2013-11-25 15:02:23 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return group;
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// Note
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Note::Note(Score* s)
|
|
|
|
: Element(s)
|
|
|
|
{
|
2014-05-22 10:10:58 +02:00
|
|
|
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)
|
2015-03-10 16:21:12 +01:00
|
|
|
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;
|
2014-04-09 09:40:25 +02:00
|
|
|
_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;
|
2013-08-23 13:55:54 +02:00
|
|
|
_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;
|
2014-11-15 21:28:16 +01:00
|
|
|
_fixed = n._fixed;
|
|
|
|
_fixedLine = n._fixedLine;
|
2012-05-26 14:26:10 +02:00
|
|
|
_accidental = 0;
|
2013-01-03 16:56:56 +01:00
|
|
|
|
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)
|
2015-03-10 16:21:12 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2014-04-09 09:40:25 +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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-09 09:40:25 +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));
|
2014-04-09 09:40:25 +02:00
|
|
|
_tpc[0] = tpc1;
|
|
|
|
_tpc[1] = tpc2;
|
2014-04-09 11:11:02 +02:00
|
|
|
setPitch(pitch);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2012-07-16 20:21:31 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// undoSetPitch
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Note::undoSetPitch(int p)
|
|
|
|
{
|
2014-05-26 18:18:01 +02:00
|
|
|
score()->undoChangeProperty(this, P_ID::PITCH, p);
|
2012-07-16 20:21:31 +02:00
|
|
|
}
|
|
|
|
|
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()) {
|
2015-03-13 11:16:43 +01:00
|
|
|
Interval interval = part()->instrument()->transpose();
|
2014-04-10 13:13:37 +02:00
|
|
|
if (!interval.isZero()) {
|
|
|
|
interval.flip();
|
|
|
|
key = transposeKey(key, interval);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-05-23 21:14:22 +02:00
|
|
|
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()) {
|
2015-03-13 11:16:43 +01:00
|
|
|
Interval interval = part()->instrument()->transpose();
|
2014-04-10 13:13:37 +02:00
|
|
|
if (!interval.isZero())
|
|
|
|
key = transposeKey(key, interval);
|
|
|
|
}
|
|
|
|
}
|
2014-05-23 21:14:22 +02:00
|
|
|
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
|
2015-03-13 11:16:43 +01:00
|
|
|
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;
|
2015-01-17 17:38:51 +01:00
|
|
|
// convert key to concert pitch
|
|
|
|
if (!concertPitch() && !v.isZero())
|
|
|
|
key = transposeKey(key, v);
|
|
|
|
// set concert pitch tpc
|
2014-05-23 21:14:22 +02:00
|
|
|
_tpc[0] = pitch2tpc(_pitch, key, Prefer::NEAREST);
|
2015-01-17 17:38:51 +01:00
|
|
|
// 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))
|
2014-06-05 20:58:05 +02:00
|
|
|
qFatal("Note::setTpc: bad tpc %d", v);
|
2014-04-09 09:40:25 +02:00
|
|
|
_tpc[concertPitchIdx()] = v;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2012-07-16 20:21:31 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// undoSetTpc
|
2014-04-09 09:40:25 +02:00
|
|
|
// change the current tpc
|
2012-07-16 20:21:31 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-04-09 09:40:25 +02:00
|
|
|
void Note::undoSetTpc(int v)
|
2012-07-16 20:21:31 +02:00
|
|
|
{
|
2014-04-09 09:40:25 +02:00
|
|
|
if (concertPitch()) {
|
|
|
|
if (v != tpc1())
|
2014-05-26 18:18:01 +02:00
|
|
|
undoChangeProperty(P_ID::TPC1, v);
|
2014-04-09 09:40:25 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (v != tpc2())
|
2014-05-26 18:18:01 +02:00
|
|
|
undoChangeProperty(P_ID::TPC2, v);
|
2014-04-09 09:40:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// tpc
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Note::tpc() const
|
|
|
|
{
|
|
|
|
return _tpc[concertPitchIdx()];
|
2012-07-16 20:21:31 +02:00
|
|
|
}
|
|
|
|
|
2015-02-19 10:28:25 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// tpcUserName
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-07-10 14:13:37 +02:00
|
|
|
QString Note::tpcUserName(bool explicitAccidental)
|
|
|
|
{
|
2014-12-17 18:15:26 +01:00
|
|
|
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-07-10 14:13:37 +02:00
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
{
|
2015-03-13 11:16:43 +01:00
|
|
|
Interval v = part()->instrument()->transpose();
|
2014-04-23 11:08:51 +02:00
|
|
|
if (v.isZero())
|
|
|
|
return tpc;
|
|
|
|
if (concertPitch()) {
|
|
|
|
v.flip();
|
2014-12-20 00:27:06 +01:00
|
|
|
return Ms::transposeTpc(tpc, v, true);
|
2014-04-23 11:08:51 +02:00
|
|
|
}
|
|
|
|
else
|
2014-12-20 00:27:06 +01:00
|
|
|
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
|
|
|
{
|
2013-11-25 15:02:23 +01:00
|
|
|
int up;
|
2014-06-25 22:01:26 +02:00
|
|
|
NoteHead::Type ht;
|
2012-05-26 14:26:10 +02:00
|
|
|
if (chord()) {
|
2013-11-15 11:18:16 +01:00
|
|
|
up = chord()->up();
|
2012-05-26 14:26:10 +02:00
|
|
|
ht = chord()->durationType().headType();
|
|
|
|
}
|
2013-01-02 20:13:58 +01:00
|
|
|
else {
|
2013-11-15 11:18:16 +01:00
|
|
|
up = 1;
|
2014-06-25 22:01:26 +02:00
|
|
|
ht = NoteHead::Type::HEAD_QUARTER;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2014-06-25 22:01:26 +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) {
|
2014-06-17 14:20:24 +02:00
|
|
|
qDebug("invalid note head %hhd/%hhd", _headGroup, ht);
|
2014-06-25 21:18:26 +02:00
|
|
|
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
|
2012-12-26 21:03:00 +01:00
|
|
|
//
|
|
|
|
// 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
|
2012-12-26 21:03:00 +01:00
|
|
|
{
|
|
|
|
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);
|
2014-11-15 21:28:16 +01:00
|
|
|
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);
|
2012-12-26 21:03:00 +01:00
|
|
|
val = fm.width(s) * mags;
|
2013-05-23 16:58:22 +02:00
|
|
|
}
|
2012-12-26 21:03:00 +01:00
|
|
|
else
|
2012-12-28 00:30:46 +01:00
|
|
|
val = headWidth();
|
2012-12-26 21:03:00 +01:00
|
|
|
return val;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// headHeight
|
2012-12-26 21:03:00 +01:00
|
|
|
//
|
|
|
|
// 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
|
2012-12-26 21:03:00 +01:00
|
|
|
{
|
2015-02-19 10:28:25 +01:00
|
|
|
if (tab && _fret != FRET_NONE && _string != STRING_NONE)
|
2012-12-26 21:03:00 +01:00
|
|
|
return tab->fretBoxH() * magS();
|
|
|
|
return headHeight();
|
|
|
|
}
|
|
|
|
|
2013-01-02 09:29:17 +01:00
|
|
|
//---------------------------------------------------------
|
2015-06-03 10:23:01 +02:00
|
|
|
// stemDownNW
|
2013-01-02 09:29:17 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2015-06-03 10:23:01 +02:00
|
|
|
QPointF Note::stemDownNW() const
|
2013-01-02 09:29:17 +01:00
|
|
|
{
|
2015-06-03 10:23:01 +02: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
|
|
|
|
{
|
2015-01-28 15:20:33 +01:00
|
|
|
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)
|
|
|
|
{
|
2014-12-13 17:40:39 +01:00
|
|
|
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);
|
|
|
|
}
|
2013-01-03 16:56:56 +01:00
|
|
|
addSpannerFor(l);
|
2012-09-12 16:19:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// removeSpanner
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Note::removeSpanner(Spanner* l)
|
|
|
|
{
|
2014-12-13 17:40:39 +01:00
|
|
|
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
|
|
|
}
|
2013-01-03 16:56:56 +01: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)
|
|
|
|
{
|
2014-02-22 11:19:44 +01:00
|
|
|
e->setParent(this);
|
2012-08-16 11:09:36 +02:00
|
|
|
e->setTrack(track());
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
switch(e->type()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::NOTEDOT:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
NoteDot* dot = static_cast<NoteDot*>(e);
|
|
|
|
_dots[dot->idx()] = dot;
|
|
|
|
}
|
|
|
|
break;
|
2014-06-24 18:36:02 +02:00
|
|
|
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;
|
2014-06-24 18:36:02 +02:00
|
|
|
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;
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::ACCIDENTAL:
|
2012-05-26 14:26:10 +02:00
|
|
|
_accidental = static_cast<Accidental*>(e);
|
|
|
|
break;
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::TEXTLINE:
|
2014-12-13 17:40:39 +01:00
|
|
|
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:
|
2014-03-25 13:33:47 +01:00
|
|
|
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()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
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;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
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))
|
2014-03-25 13:33:47 +01:00
|
|
|
qDebug("Note::remove(): cannot find %s", e->name());
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::TIE:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
Tie* tie = static_cast<Tie*>(e);
|
|
|
|
setTieFor(0);
|
2015-02-17 20:22:24 +01:00
|
|
|
if (tie->endNote())
|
2012-05-26 14:26:10 +02:00
|
|
|
tie->endNote()->setTieBack(0);
|
2015-02-26 11:02:11 +01:00
|
|
|
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;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::ACCIDENTAL:
|
2012-05-26 14:26:10 +02:00
|
|
|
_accidental = 0;
|
|
|
|
break;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::TEXTLINE:
|
2014-12-13 17:40:39 +01:00
|
|
|
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:
|
2014-03-25 13:33:47 +01:00
|
|
|
qDebug("Note::remove() not impl. %s", e->name());
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// draw
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Note::draw(QPainter* painter) const
|
|
|
|
{
|
2012-06-09 10:50:51 +02:00
|
|
|
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);
|
2012-08-16 11:09:36 +02:00
|
|
|
bool tablature = staff() && staff()->isTabStaff();
|
2012-08-17 00:33:38 +02:00
|
|
|
|
|
|
|
// tablature
|
|
|
|
|
2012-06-09 10:50:51 +02:00
|
|
|
if (tablature) {
|
2014-04-28 18:38:50 +02:00
|
|
|
StaffType* tab = staff()->staffType();
|
2015-08-25 08:11:01 +02:00
|
|
|
if (tieBack() && !tab->showBackTied()) // skip back-tied notes if not shown
|
2012-09-21 18:31:31 +02:00
|
|
|
return;
|
2014-11-15 21:28:16 +01:00
|
|
|
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);
|
2012-06-09 10:50:51 +02:00
|
|
|
|
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)
|
2012-06-09 10:50:51 +02:00
|
|
|
if (!tab->linesThrough() || fretConflict()) {
|
2012-09-21 18:31:31 +02:00
|
|
|
qreal d = spatium() * .1;
|
2014-08-07 17:39:31 +02:00
|
|
|
QRectF bb = QRectF(bbox().x()-d, tab->fretMaskY()*magS(), bbox().width() + 2*d, tab->fretMaskH()*magS());
|
2012-06-09 10:50:51 +02:00
|
|
|
// we do not know which viewer did this draw() call
|
|
|
|
// so update all:
|
|
|
|
foreach(MuseScoreView* view, score()->getViewer())
|
|
|
|
view->drawBackground(painter, bb);
|
|
|
|
|
2015-05-15 00:06:18 +02:00
|
|
|
if (fretConflict() && !score()->printing()) { //on fret conflict, draw on red background
|
2012-06-09 10:50:51 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
2013-03-11 13:30:43 +01: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);
|
2012-06-09 10:50:51 +02:00
|
|
|
painter->drawText(QPointF(bbox().x(), tab->fretFontYOffset()), s);
|
|
|
|
painter->scale(imag, imag);
|
|
|
|
}
|
2012-08-17 00:33:38 +02:00
|
|
|
|
|
|
|
// NOT tablature
|
|
|
|
|
|
|
|
else {
|
2013-05-12 12:51:42 +02:00
|
|
|
// skip drawing, if second note of a cross-measure value
|
2014-05-21 15:41:23 +02:00
|
|
|
if (chord()->crossMeasure() == CrossMeasure::SECOND)
|
2013-05-12 12:51:42 +02:00
|
|
|
return;
|
2012-06-09 10:50:51 +02:00
|
|
|
//
|
|
|
|
// warn if pitch extends usable range of instrument
|
|
|
|
// by coloring the note head
|
|
|
|
//
|
|
|
|
if (chord() && chord()->segment() && staff() && !selected()
|
|
|
|
&& !score()->printing() && MScore::warnPitchRange) {
|
2015-03-13 11:16:43 +01:00
|
|
|
const Instrument* in = part()->instrument();
|
2012-06-09 10:50:51 +02:00
|
|
|
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));
|
2013-08-21 11:59:41 +02:00
|
|
|
}
|
2014-03-28 16:29:55 +01:00
|
|
|
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);
|
2015-03-27 02:10:24 +01:00
|
|
|
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);
|
2014-11-15 21:28:16 +01:00
|
|
|
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)));
|
2013-01-03 16:56:56 +01:00
|
|
|
|
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
|
|
|
|
|
2014-05-30 10:18:20 +02:00
|
|
|
_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
|
2014-04-09 09:40:25 +02:00
|
|
|
_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") {
|
2014-04-09 09:40:25 +02:00
|
|
|
_tpc[0] = e.readInt();
|
2014-05-05 12:41:50 +02:00
|
|
|
_tpc[1] = _tpc[0];
|
|
|
|
}
|
2014-04-09 09:40:25 +02:00
|
|
|
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());
|
2014-05-07 05:56:46 +02:00
|
|
|
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));
|
2014-11-15 21:28:16 +01:00
|
|
|
else if (tag == "fixed")
|
|
|
|
setFixed(e.readBool());
|
|
|
|
else if (tag == "fixedLine")
|
|
|
|
setFixedLine(e.readInt());
|
2014-04-23 15:07:37 +02:00
|
|
|
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)
|
2014-07-17 16:22:16 +02:00
|
|
|
setOffTimeOffset(1000 + (e.readInt() * 1000 / chord()->actualTicks()));
|
2014-04-23 15:07:37 +02:00
|
|
|
else
|
2014-07-17 16:22:16 +02:00
|
|
|
setOffTimeOffset(1000 + (e.readInt() * 10));
|
2014-04-23 15:07:37 +02:00
|
|
|
}
|
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());
|
2013-08-23 13:55:54 +02:00
|
|
|
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")
|
2013-10-08 00:07:26 +02:00
|
|
|
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);
|
2013-10-08 00:07:26 +02:00
|
|
|
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") {
|
2015-02-26 11:02:11 +01:00
|
|
|
Tie* tie = new Tie(score());
|
|
|
|
tie->setParent(this);
|
|
|
|
tie->setTrack(track());
|
|
|
|
tie->read(e);
|
|
|
|
tie->setStartNote(this);
|
2015-02-26 19:53:59 +01:00
|
|
|
_tieFor = tie;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else if (tag == "Fingering" || tag == "Text") { // Text is obsolete
|
|
|
|
Fingering* f = new Fingering(score());
|
2014-05-30 10:13:29 +02:00
|
|
|
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") {
|
2014-03-04 17:40:23 +01:00
|
|
|
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)
|
2013-07-02 09:41:04 +02:00
|
|
|
// 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) {
|
2014-06-13 04:13:41 +02:00
|
|
|
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
|
|
|
}
|
2013-03-05 20:23:59 +01: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) {
|
2014-03-25 13:33:47 +01:00
|
|
|
qDebug("Note: too many dots");
|
2012-05-26 14:26:10 +02:00
|
|
|
delete dot;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tag == "Events") {
|
2014-03-28 16:29:55 +01:00
|
|
|
_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())
|
2014-03-28 16:29:55 +01:00
|
|
|
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);
|
2014-06-24 18:36:02 +02:00
|
|
|
if (sp->type() == Element::Type::TIE)
|
2013-08-21 11:59:41 +02:00
|
|
|
_tieBack = static_cast<Tie*>(sp);
|
2014-12-13 17:40:39 +01:00
|
|
|
else {
|
|
|
|
if (sp->type() == Element::Type::GLISSANDO
|
|
|
|
&& parent() && parent()->type() == Element::Type::CHORD)
|
|
|
|
static_cast<Chord*>(parent())->setEndsGlissando(true);
|
2013-08-21 11:59:41 +02:00
|
|
|
addSpannerBack(sp);
|
2014-12-13 17:40:39 +01:00
|
|
|
}
|
2014-06-24 14:45:50 +02:00
|
|
|
e.removeSpanner(sp);
|
2012-09-12 16:19:03 +02:00
|
|
|
}
|
2015-02-28 19:53:31 +01: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);
|
2015-03-16 12:04:28 +01:00
|
|
|
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())) {
|
2015-02-28 19:53:31 +01:00
|
|
|
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
|
|
|
}
|
2014-12-13 17:40:39 +01:00
|
|
|
else if (tag == "TextLine"
|
|
|
|
|| tag == "Glissando") {
|
2013-01-18 10:55:52 +01:00
|
|
|
Spanner* sp = static_cast<Spanner*>(Element::name2Element(tag, score()));
|
2015-02-28 19:53:31 +01:00
|
|
|
// 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);
|
2015-02-28 19:53:31 +01:00
|
|
|
// 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);
|
2015-03-16 12:04:28 +01:00
|
|
|
// 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();
|
2014-05-06 04:28:15 +02:00
|
|
|
else if (tag == "offset") {
|
2014-05-06 17:05:27 +02:00
|
|
|
if (score()->mscVersion() > 114) // || voice() >= 2)
|
2014-05-06 04:28:15 +02:00
|
|
|
Element::readProperties(e);
|
|
|
|
else
|
2014-05-06 17:05:27 +02:00
|
|
|
e.skipCurrentElement(); // ignore manual layout in older scores
|
2014-05-06 04:28:15 +02:00
|
|
|
}
|
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:
|
2014-05-12 12:52:13 +02:00
|
|
|
_pitch = limit(_pitch, 0, 127);
|
2014-04-09 09:40:25 +02:00
|
|
|
|
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]))) {
|
2015-03-13 11:16:43 +01:00
|
|
|
Interval v = staff() ? part()->instrument()->transpose() : Interval();
|
2014-04-14 13:16:46 +02:00
|
|
|
if (tpcIsValid(_tpc[0])) {
|
|
|
|
v.flip();
|
|
|
|
if (v.isZero())
|
2014-04-09 09:40:25 +02:00
|
|
|
_tpc[1] = _tpc[0];
|
|
|
|
else
|
2014-12-20 00:27:06 +01:00
|
|
|
_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
|
2014-12-20 00:27:06 +01:00
|
|
|
_tpc[0] = Ms::transposeTpc(_tpc[1], v, true);
|
2014-04-09 09:40:25 +02:00
|
|
|
}
|
|
|
|
}
|
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();
|
2012-08-16 11:09:36 +02:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2014-04-09 09:40:25 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// transposition
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Note::transposition() const
|
|
|
|
{
|
2015-03-13 11:16:43 +01:00
|
|
|
return staff() ? part()->instrument()->transpose().chromatic : 0;
|
2014-04-09 09:40:25 +02:00
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// endDrag
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Note::endDrag()
|
|
|
|
{
|
|
|
|
dragMode = false;
|
|
|
|
if (_lineOffset == 0)
|
|
|
|
return;
|
2015-02-12 11:41:39 +01:00
|
|
|
|
|
|
|
int staffIdx = chord()->staffIdx() + chord()->staffMove();
|
|
|
|
Staff* staff = score()->staff(staffIdx);
|
2015-02-13 01:09:01 +01:00
|
|
|
int tick = chord()->tick();
|
2015-02-12 11:41:39 +01:00
|
|
|
|
|
|
|
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)
|
2015-02-25 13:30:22 +01:00
|
|
|
// and fret for the same pitch on the new string
|
2015-03-13 11:16:43 +01:00
|
|
|
const StringData* strData = staff->part()->instrument()->stringData();
|
2015-02-25 13:30:22 +01:00
|
|
|
int nString = _string + (staff->staffType()->upsideDown() ? -_lineOffset : _lineOffset);
|
|
|
|
int nFret = strData->fret(_pitch, nString, staff, tick);
|
2015-02-12 11:41:39 +01:00
|
|
|
if (nFret < 0) // no fret?
|
|
|
|
return; // no party!
|
2015-02-25 13:30:22 +01:00
|
|
|
// 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());
|
|
|
|
}
|
2015-02-12 11:41:39 +01:00
|
|
|
}
|
|
|
|
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()) {
|
2015-03-13 11:16:43 +01:00
|
|
|
Interval interval = staff->part()->instrument()->transpose();
|
2015-02-12 11:41:39 +01:00
|
|
|
nPitch += interval.chromatic;
|
2014-07-15 11:01:24 +02:00
|
|
|
}
|
2015-02-12 11:41:39 +01: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);
|
2014-04-09 09:40:25 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2015-02-09 16:28:57 +01:00
|
|
|
_lineOffset = 0;
|
2014-05-27 10:34:08 +02:00
|
|
|
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;
|
2014-06-24 18:36:02 +02:00
|
|
|
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;
|
|
|
|
}
|
2014-06-24 18:36:02 +02:00
|
|
|
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
|
2015-01-17 22:40:28 +01:00
|
|
|
|| (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)
|
2014-06-24 18:36:02 +02:00
|
|
|
|| (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)
|
2015-07-03 22:18:40 +02:00
|
|
|
|| (type == Element::Type::KEYSIG)
|
|
|
|
|| (type == Element::Type::TIMESIG)
|
2014-06-24 18:36:02 +02:00
|
|
|
|| (type == Element::Type::BAR_LINE)
|
|
|
|
|| (type == Element::Type::SLUR)
|
2014-12-16 18:07:51 +01:00
|
|
|
|| (type == Element::Type::HAIRPIN)
|
2014-06-24 18:36:02 +02:00
|
|
|
|| (type == Element::Type::STAFF_TEXT)
|
|
|
|
|| (type == Element::Type::TEMPO_TEXT)
|
2015-04-18 18:35:58 +02:00
|
|
|
|| (type == Element::Type::BEND)
|
2014-08-26 15:07:24 +02:00
|
|
|
|| (type == Element::Type::TREMOLOBAR)
|
2014-06-24 18:36:02 +02:00
|
|
|
|| (type == Element::Type::FRET_DIAGRAM));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// drop
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Element* Note::drop(const DropData& data)
|
|
|
|
{
|
|
|
|
Element* e = data.element;
|
2015-01-02 02:06:32 +01:00
|
|
|
bool fromPalette = (e->track() == -1);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
Chord* ch = chord();
|
|
|
|
switch(e->type()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::REHEARSAL_MARK:
|
2012-05-26 14:26:10 +02:00
|
|
|
return ch->drop(data);
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::SYMBOL:
|
|
|
|
case Element::Type::IMAGE:
|
2012-10-25 16:21:07 +02:00
|
|
|
e->setParent(this);
|
|
|
|
score()->undoAddElement(e);
|
|
|
|
return e;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
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);
|
2014-07-03 11:22:42 +02:00
|
|
|
TextStyleType st = f->textStyleType();
|
2015-01-02 02:06:32 +01:00
|
|
|
//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;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::SLUR:
|
2012-05-26 14:26:10 +02:00
|
|
|
delete e;
|
|
|
|
data.view->cmdAddSlur(this, 0);
|
|
|
|
return 0;
|
|
|
|
|
2014-12-16 18:07:51 +01:00
|
|
|
case Element::Type::HAIRPIN:
|
|
|
|
{
|
|
|
|
Hairpin* hairpin = static_cast<Hairpin*>(e);
|
2014-12-17 11:02:37 +01:00
|
|
|
bool decresc = (hairpin->hairpinType() == Hairpin::Type::DECRESCENDO);
|
2014-12-16 18:07:51 +01:00
|
|
|
delete e;
|
2014-12-17 11:02:37 +01:00
|
|
|
data.view->cmdAddHairpin(decresc);
|
2014-12-16 18:07:51 +01:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::LYRICS:
|
2013-10-05 17:04:41 +02:00
|
|
|
e->setParent(ch);
|
|
|
|
e->setTrack(track());
|
2012-05-26 14:26:10 +02:00
|
|
|
score()->undoAddElement(e);
|
|
|
|
return e;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::ACCIDENTAL:
|
2013-03-05 20:23:59 +01:00
|
|
|
score()->changeAccidental(this, static_cast<Accidental*>(e)->accidentalType());
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
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;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::NOTEHEAD:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2013-11-25 15:02:23 +01:00
|
|
|
NoteHead* s = static_cast<NoteHead*>(e);
|
2014-06-25 21:18:26 +02:00
|
|
|
NoteHead::Group group = s->headGroup();
|
|
|
|
if (group == NoteHead::Group::HEAD_INVALID) {
|
2014-03-25 13:33:47 +01:00
|
|
|
qDebug("unknown note head");
|
2014-06-25 21:18:26 +02:00
|
|
|
group = NoteHead::Group::HEAD_NORMAL;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
delete s;
|
|
|
|
|
|
|
|
if (group != _headGroup) {
|
|
|
|
if (links()) {
|
2015-02-12 11:41:39 +01:00
|
|
|
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);
|
2012-08-16 11:09:36 +02:00
|
|
|
if (note->staff() && note->staff()->isTabStaff()
|
2014-06-25 21:18:26 +02:00
|
|
|
&& 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;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::ICON:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2013-03-05 20:23:59 +01:00
|
|
|
switch(static_cast<Icon*>(e)->iconType()) {
|
2014-05-30 10:14:57 +02:00
|
|
|
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;
|
2014-05-30 10:14:57 +02:00
|
|
|
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;
|
2014-05-30 10:14:57 +02:00
|
|
|
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;
|
2014-05-30 10:14:57 +02:00
|
|
|
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;
|
2014-05-30 10:14:57 +02:00
|
|
|
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;
|
2014-05-30 10:14:57 +02:00
|
|
|
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;
|
2014-05-30 10:14:57 +02:00
|
|
|
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;
|
2014-05-30 10:14:57 +02:00
|
|
|
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;
|
2014-05-30 10:14:57 +02:00
|
|
|
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;
|
2014-05-30 10:14:57 +02:00
|
|
|
case IconType::BRACKETS:
|
2013-05-27 11:28:20 +02:00
|
|
|
{
|
2014-07-18 00:11:00 +02:00
|
|
|
addBracket();
|
2013-05-27 11:28:20 +02:00
|
|
|
}
|
|
|
|
break;
|
2014-05-30 10:14:57 +02:00
|
|
|
default:
|
|
|
|
break;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
delete e;
|
|
|
|
break;
|
2013-08-21 11:59:41 +02:00
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
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
|
|
|
|
2014-06-24 18:36:02 +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;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-13 17:40:39 +01:00
|
|
|
// 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
|
|
|
}
|
2014-12-13 17:40:39 +01:00
|
|
|
else {
|
2014-03-25 13:33:47 +01:00
|
|
|
qDebug("no segment for second note of glissando found");
|
2012-05-26 14:26:10 +02:00
|
|
|
delete e;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::CHORD:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
Chord* c = static_cast<Chord*>(e);
|
|
|
|
Note* n = c->upNote();
|
2014-06-26 10:53:57 +02:00
|
|
|
MScore::Direction dir = c->stemDirection();
|
2013-01-02 20:13:58 +01:00
|
|
|
int t = (staff2track(staffIdx()) + n->voice());
|
2014-05-27 10:34:08 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-07-18 00:11:00 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// addBracket
|
|
|
|
//---------------------------------------------------------
|
2014-07-18 18:38:14 +02:00
|
|
|
|
2014-07-18 00:11:00 +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
|
|
|
//---------------------------------------------------------
|
2014-08-10 19:39:52 +02:00
|
|
|
// setDotY
|
2014-04-02 20:31:37 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-26 10:53:57 +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
|
2014-06-26 10:53:57 +02:00
|
|
|
if (pos == MScore::Direction::AUTO)
|
2014-04-02 20:31:37 +02:00
|
|
|
y = oddVoice ? 0.5 : -0.5;
|
2014-06-26 10:53:57 +02:00
|
|
|
else if (pos == MScore::Direction::UP)
|
2014-04-02 20:31:37 +02:00
|
|
|
y = -0.5;
|
|
|
|
else
|
|
|
|
y = 0.5;
|
|
|
|
}
|
|
|
|
else {
|
2014-06-26 10:53:57 +02:00
|
|
|
if (pos == MScore::Direction::UP && !oddVoice)
|
2014-04-02 20:31:37 +02:00
|
|
|
y -= 1.0;
|
2014-06-26 10:53:57 +02:00
|
|
|
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()
|
|
|
|
{
|
2012-08-16 11:09:36 +02:00
|
|
|
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();
|
2012-12-28 00:30:46 +01:00
|
|
|
qreal w = tabHeadWidth(tab);
|
2013-02-04 23:28:24 +01:00
|
|
|
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;
|
2013-03-09 09:58:43 +01:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// layout2
|
|
|
|
// called after final position of note is set
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Note::layout2()
|
|
|
|
{
|
2015-06-22 20:42:22 +02:00
|
|
|
// for standard staves this is done in Score::layoutChords3()
|
2014-05-21 00:43:28 +02:00
|
|
|
// so that the results are available there
|
2015-06-22 20:42:22 +02:00
|
|
|
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();
|
2015-06-11 21:49:55 +02:00
|
|
|
bool layoutDots = true;
|
2013-03-23 11:36:04 +01:00
|
|
|
// if TAB and stems through staff
|
|
|
|
if (staff()->isTabStaff()) {
|
2014-08-12 20:36:23 +02:00
|
|
|
StaffType* tab = staff()->staffType();
|
2015-06-11 21:49:55 +02:00
|
|
|
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
|
|
|
|
}
|
2013-03-23 11:36:04 +01:00
|
|
|
}
|
2015-06-11 21:49:55 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-23 11:36:04 +01: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());
|
2014-11-25 18:01:41 +01:00
|
|
|
if (e->type() == Element::Type::SYMBOL) {
|
2013-06-03 14:53:50 +02:00
|
|
|
qreal w = headWidth();
|
2014-11-25 18:01:41 +01:00
|
|
|
Symbol* sym = static_cast<Symbol*>(e);
|
2014-07-15 15:05:36 +02:00
|
|
|
QPointF rp = e->readPos();
|
|
|
|
e->layout();
|
2014-11-25 18:01:41 +01:00
|
|
|
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
|
|
|
}
|
2013-06-03 14:53:50 +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;
|
2014-06-26 10:53:57 +02:00
|
|
|
if (_userDotPosition == MScore::Direction::AUTO)
|
2014-04-02 22:37:13 +02:00
|
|
|
return _dots[0]->y() < spatium() * .1;
|
|
|
|
else
|
2014-06-26 10:53:57 +02:00
|
|
|
return (_userDotPosition == MScore::Direction::UP);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2015-02-17 22:17:22 +01: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)) {
|
2015-02-17 22:17:22 +01:00
|
|
|
// calculate accidental
|
2015-04-02 10:33:53 +02:00
|
|
|
AccidentalType acci = AccidentalType::NONE;
|
2015-02-17 22:17:22 +01:00
|
|
|
|
|
|
|
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;
|
2015-02-17 22:17:22 +01:00
|
|
|
else {
|
|
|
|
acci = Accidental::value2subtype(accVal);
|
2015-04-02 10:33:53 +02:00
|
|
|
if (acci == AccidentalType::NONE)
|
|
|
|
acci = AccidentalType::NATURAL;
|
2015-02-17 22:17:22 +01:00
|
|
|
}
|
|
|
|
}
|
2015-04-02 10:33:53 +02:00
|
|
|
if (acci != AccidentalType::NONE && !_tieBack && !_hidden) {
|
2015-02-17 22:17:22 +01:00
|
|
|
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)
|
2015-02-17 22:17:22 +01:00
|
|
|
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;
|
2015-02-17 22:17:22 +01:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2014-07-10 14:13:37 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// noteTypeUserName
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QString Note::noteTypeUserName()
|
|
|
|
{
|
|
|
|
switch (noteType()) {
|
|
|
|
case NoteType::ACCIACCATURA:
|
2014-08-19 16:22:30 +02:00
|
|
|
return tr("Acciaccatura");
|
2014-07-10 14:13:37 +02:00
|
|
|
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:
|
2014-07-10 14:13:37 +02:00
|
|
|
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
|
2012-08-16 11:09:36 +02:00
|
|
|
// 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-09-17 10:43:59 +02:00
|
|
|
|
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);
|
|
|
|
}
|
2015-03-05 18:57:48 +01:00
|
|
|
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());
|
2014-06-26 10:53:57 +02:00
|
|
|
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
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-11-15 21:28:16 +01:00
|
|
|
int Note::line() const
|
|
|
|
{
|
|
|
|
if (_fixed)
|
|
|
|
return _fixedLine;
|
|
|
|
else
|
|
|
|
return _line + _lineOffset;
|
|
|
|
}
|
|
|
|
|
2014-08-23 18:13:27 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-25 21:18:26 +02:00
|
|
|
void Note::setHeadGroup(NoteHead::Group val)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2014-06-25 21:18:26 +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
|
|
|
|
{
|
2014-04-09 09:40:25 +02:00
|
|
|
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
|
|
|
|
{
|
2014-04-09 09:40:25 +02:00
|
|
|
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();
|
2014-05-12 12:52:13 +02:00
|
|
|
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());
|
2012-06-09 10:50:51 +02:00
|
|
|
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
|
|
|
|
//---------------------------------------------------------
|
2013-03-10 14:10:09 +01:00
|
|
|
|
2014-04-22 17:02:03 +02:00
|
|
|
void Note::updateRelLine(int relLine, bool undoable)
|
2014-04-09 16:09:21 +02:00
|
|
|
{
|
2014-11-27 01:47:03 +01:00
|
|
|
if (staff() && chord()->staffMove()) {
|
|
|
|
// check that destination staff makes sense (might have been deleted)
|
|
|
|
int idx = staffIdx() + chord()->staffMove();
|
2015-03-13 11:16:43 +01:00
|
|
|
int minStaff = part()->startTrack() / VOICES;
|
|
|
|
int maxStaff = part()->endTrack() / VOICES;
|
2014-11-27 01:47:03 +01:00
|
|
|
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
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-12-15 23:08:33 +01: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;
|
|
|
|
|
2015-03-13 11:16:43 +01:00
|
|
|
Interval v = part()->instrument()->transpose();
|
2014-09-22 14:31:28 +02:00
|
|
|
if (nval.tpc1 == Tpc::TPC_INVALID) {
|
2014-12-15 23:08:33 +01:00
|
|
|
if (tick == -1)
|
|
|
|
tick = chord()->tick();
|
|
|
|
Key key = staff()->key(tick);
|
2015-01-12 23:40:22 +01:00
|
|
|
if (!concertPitch() && !v.isZero())
|
|
|
|
key = transposeKey(key, v);
|
2014-05-23 21:14:22 +02:00
|
|
|
_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();
|
2014-12-20 00:27:06 +01:00
|
|
|
_tpc[1] = Ms::transposeTpc(_tpc[0], v, true);
|
2014-04-17 12:32:10 +02:00
|
|
|
}
|
2014-04-11 14:50:53 +02:00
|
|
|
}
|
|
|
|
|
2014-06-25 21:18:26 +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:
|
2014-04-09 09:40:25 +02:00
|
|
|
return _tpc[0];
|
2014-05-26 18:18:01 +02:00
|
|
|
case P_ID::TPC2:
|
2014-04-09 09:40:25 +02:00
|
|
|
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:
|
2013-11-25 15:02:23 +01:00
|
|
|
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:
|
2013-11-25 15:02:23 +01:00
|
|
|
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:
|
2013-08-23 13:55:54 +02:00
|
|
|
return play();
|
2014-05-26 18:18:01 +02:00
|
|
|
case P_ID::LINE:
|
2014-04-22 17:02:03 +02:00
|
|
|
return _line;
|
2014-11-15 21:28:16 +01:00
|
|
|
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:
|
2014-04-09 09:40:25 +02:00
|
|
|
_tpc[0] = v.toInt();
|
|
|
|
break;
|
2014-05-26 18:18:01 +02:00
|
|
|
case P_ID::TPC2:
|
2014-04-09 09:40:25 +02:00
|
|
|
_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:
|
2014-06-26 10:53:57 +02:00
|
|
|
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:
|
2014-06-26 10:53:57 +02:00
|
|
|
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:
|
2014-06-25 21:18:26 +02:00
|
|
|
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:
|
2014-06-25 22:01:26 +02:00
|
|
|
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
|
2012-12-05 13:44:30 +01:00
|
|
|
setVisible(v.toBool());
|
|
|
|
int dots = chord()->dots();
|
|
|
|
for (int i = 0; i < dots; ++i) {
|
|
|
|
if (_dots[i])
|
|
|
|
_dots[i]->setVisible(visible());
|
|
|
|
}
|
2015-01-26 22:58:46 +01:00
|
|
|
if (m)
|
|
|
|
m->checkMultiVoices(chord()->staffIdx());
|
2012-12-05 13:44:30 +01:00
|
|
|
break;
|
|
|
|
}
|
2014-05-26 18:18:01 +02:00
|
|
|
case P_ID::PLAY:
|
2013-08-23 13:55:54 +02:00
|
|
|
setPlay(v.toBool());
|
2015-01-30 17:03:51 +01:00
|
|
|
score()->setPlaylistDirty();
|
2013-08-23 13:55:54 +02:00
|
|
|
break;
|
2014-11-15 21:28:16 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2013-08-23 13:55:54 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// undoSetPlay
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Note::undoSetPlay(bool val)
|
|
|
|
{
|
2014-05-26 18:18:01 +02:00
|
|
|
undoChangeProperty(P_ID::PLAY, val);
|
2013-08-23 13:55:54 +02:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-26 10:53:57 +02:00
|
|
|
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
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-26 10:53:57 +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
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-25 21:18:26 +02:00
|
|
|
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
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-25 22:01:26 +02:00
|
|
|
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
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-25 22:01:26 +02:00
|
|
|
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:
|
2014-06-26 10:53:57 +02:00
|
|
|
return int(MScore::DirectionH::AUTO);
|
2014-05-26 18:18:01 +02:00
|
|
|
case P_ID::DOT_POSITION:
|
2014-06-26 10:53:57 +02:00
|
|
|
return int(MScore::Direction::AUTO);
|
2014-05-26 18:18:01 +02:00
|
|
|
case P_ID::HEAD_GROUP:
|
2014-06-25 21:18:26 +02:00
|
|
|
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:
|
2014-06-25 22:01:26 +02:00
|
|
|
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:
|
2013-08-23 13:55:54 +02:00
|
|
|
return true;
|
2014-11-15 21:28:16 +01:00
|
|
|
case P_ID::FIXED:
|
|
|
|
return false;
|
|
|
|
case P_ID::FIXED_LINE:
|
|
|
|
return 0;
|
2012-08-10 17:01:35 +02:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2012-10-31 14:21:13 +01:00
|
|
|
return Element::propertyDefault(propertyId);
|
2012-08-10 17:01:35 +02:00
|
|
|
}
|
|
|
|
|
2012-11-19 09:29:46 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// setOnTimeOffset
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-04-09 09:40:25 +02:00
|
|
|
void Note::setOnTimeOffset(int val)
|
2012-11-19 09:29:46 +01:00
|
|
|
{
|
2014-04-09 09:40:25 +02:00
|
|
|
_playEvents[0].setOntime(val);
|
2014-04-23 15:07:37 +02:00
|
|
|
chord()->setPlayEventType(PlayEventType::User);
|
2012-11-19 09:29:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setOffTimeOffset
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-04-09 09:40:25 +02:00
|
|
|
void Note::setOffTimeOffset(int val)
|
2012-11-19 09:29:46 +01:00
|
|
|
{
|
2014-04-09 09:40:25 +02:00
|
|
|
_playEvents[0].setLen(val - _playEvents[0].ontime());
|
2014-04-23 15:07:37 +02:00
|
|
|
chord()->setPlayEventType(PlayEventType::User);
|
2012-11-19 09:29:46 +01:00
|
|
|
}
|
2013-01-02 20:13:58 +01:00
|
|
|
|
2013-01-03 16:56:56 +01:00
|
|
|
//---------------------------------------------------------
|
2013-06-10 11:03:34 +02:00
|
|
|
// setScore
|
2013-01-03 16:56:56 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-06-10 11:03:34 +02:00
|
|
|
void Note::setScore(Score* s)
|
2013-01-03 16:56:56 +01:00
|
|
|
{
|
2013-06-10 11:03:34 +02:00
|
|
|
Element::setScore(s);
|
|
|
|
if (_tieFor)
|
|
|
|
_tieFor->setScore(s);
|
2013-01-03 16:56:56 +01:00
|
|
|
}
|
2014-06-26 11:41:18 +02:00
|
|
|
|
2014-07-10 14:13:37 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// accessibleInfo
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QString Note::accessibleInfo()
|
|
|
|
{
|
|
|
|
QString duration = chord()->durationUserName();
|
|
|
|
QString voice = tr("Voice: %1").arg(QString::number(track() % VOICES + 1));
|
2014-11-21 01:15:36 +01:00
|
|
|
QString pitchName;
|
2015-04-12 10:17:00 +02:00
|
|
|
const Drumset* drumset = part()->instrument()->drumset();
|
2014-11-21 01:15:36 +01:00
|
|
|
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());
|
2014-11-21 01:15:36 +01:00
|
|
|
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)));
|
2014-07-10 14:13:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// screenReaderInfo
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QString Note::screenReaderInfo()
|
|
|
|
{
|
|
|
|
QString duration = chord()->durationUserName();
|
|
|
|
QString voice = tr("Voice: %1").arg(QString::number(track() % VOICES + 1));
|
2014-11-21 01:15:36 +01:00
|
|
|
QString pitchName;
|
2015-04-12 10:17:00 +02:00
|
|
|
const Drumset* drumset = part()->instrument()->drumset();
|
2014-11-21 01:15:36 +01:00
|
|
|
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());
|
2014-11-21 01:15:36 +01:00
|
|
|
else
|
|
|
|
pitchName = tpcUserName(true);
|
|
|
|
return QString("%1 %2 %3%4").arg(noteTypeUserName()).arg(pitchName).arg(duration).arg((chord()->isGrace() ? "" : QString("; %1").arg(voice)));
|
2014-07-10 14:13:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// accessibleExtraInfo
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QString Note::accessibleExtraInfo()
|
|
|
|
{
|
|
|
|
QString rez = "";
|
|
|
|
if (accidental()) {
|
2014-08-21 18:32:52 +02:00
|
|
|
rez = QString("%1 %2").arg(rez).arg(accidental()->screenReaderInfo());
|
2014-07-10 14:13:37 +02:00
|
|
|
}
|
|
|
|
if (!el().isEmpty()) {
|
|
|
|
foreach (Element* e, el()) {
|
2015-02-03 01:55:37 +01:00
|
|
|
if (!score()->selectionFilter().canSelect(e)) continue;
|
2014-08-21 18:32:52 +02:00
|
|
|
rez = QString("%1 %2").arg(rez).arg(e->screenReaderInfo());
|
2014-07-10 14:13:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (tieFor())
|
2014-08-21 18:32:52 +02:00
|
|
|
rez = tr("%1 Start of %2").arg(rez).arg(tieFor()->screenReaderInfo());
|
2014-07-10 14:13:37 +02:00
|
|
|
|
|
|
|
if (tieBack())
|
2014-08-21 18:32:52 +02:00
|
|
|
rez = tr("%1 End of %2").arg(rez).arg(tieBack()->screenReaderInfo());
|
2014-07-10 14:13:37 +02:00
|
|
|
|
|
|
|
if (!spannerFor().isEmpty()) {
|
|
|
|
foreach (Spanner* s, spannerFor()) {
|
2015-02-03 01:55:37 +01:00
|
|
|
if (!score()->selectionFilter().canSelect(s)) continue;
|
2014-08-21 18:32:52 +02:00
|
|
|
rez = tr("%1 Start of %2").arg(rez).arg(s->screenReaderInfo());
|
2014-07-10 14:13:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!spannerBack().isEmpty()) {
|
|
|
|
foreach (Spanner* s, spannerBack()) {
|
2015-02-03 01:55:37 +01:00
|
|
|
if (!score()->selectionFilter().canSelect(s)) continue;
|
2014-08-21 18:32:52 +02:00
|
|
|
rez = tr("%1 End of %2").arg(rez).arg(s->screenReaderInfo());
|
2014-07-10 14:13:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-21 18:32:52 +02:00
|
|
|
rez = QString("%1 %2").arg(rez).arg(chord()->accessibleExtraInfo());
|
2014-07-10 14:13:37 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-07-04 11:38:41 +02:00
|
|
|
//---------------------------------------------------------
|
2015-03-19 10:39:18 +01:00
|
|
|
// qmlDotsCount
|
2014-12-18 18:02:44 +01:00
|
|
|
// returns number of dots for plugins
|
2014-07-04 11:38:41 +02:00
|
|
|
//---------------------------------------------------------
|
2015-03-19 10:39:18 +01:00
|
|
|
int Note::qmlDotsCount()
|
2014-07-04 11:38:41 +02:00
|
|
|
{
|
2015-02-19 10:28:25 +01:00
|
|
|
int i = 0;
|
2014-07-04 11:38:41 +02:00
|
|
|
for (NoteDot* dot : _dots)
|
2015-02-19 10:28:25 +01:00
|
|
|
if (dot)
|
|
|
|
++i;
|
|
|
|
return i;
|
2014-07-04 11:38:41 +02:00
|
|
|
}
|
|
|
|
|
2015-02-19 10:28:25 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// groupToGroupName
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-07-22 14:26:24 +02:00
|
|
|
const char* NoteHead::groupToGroupName(NoteHead::Group group)
|
|
|
|
{
|
|
|
|
return noteHeadNames[int(group)];
|
|
|
|
}
|
|
|
|
|
2015-02-19 10:28:25 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// subtypeName
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-07-17 15:07:12 +02:00
|
|
|
QString Note::subtypeName() const
|
|
|
|
{
|
2014-08-22 16:25:50 +02:00
|
|
|
return qApp->translate("noteheadnames", NoteHead::groupToGroupName(_headGroup));
|
2014-07-17 15:07:12 +02:00
|
|
|
}
|
|
|
|
|
2014-06-20 22:48:34 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2015-01-28 15:20:33 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// prevElement
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-20 22:48:34 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-01-28 15:20:33 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-04 11:38:41 +02:00
|
|
|
}
|