e95d169d5c
needed to make them available to the plugin framework
705 lines
27 KiB
C++
705 lines
27 KiB
C++
//=============================================================================
|
|
// MuseScore
|
|
// Music Composition & Notation
|
|
//
|
|
// Copyright (C) 2002-2013 Werner Schweer and others
|
|
//
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License version 2
|
|
// as published by the Free Software Foundation and appearing in
|
|
// the file LICENCE.GPL
|
|
//=============================================================================
|
|
|
|
#include "ambitus.h"
|
|
#include "chord.h"
|
|
#include "measure.h"
|
|
#include "score.h"
|
|
#include "segment.h"
|
|
#include "staff.h"
|
|
#include "stafftype.h"
|
|
#include "sym.h"
|
|
#include "system.h"
|
|
#include "utils.h"
|
|
#include "xml.h"
|
|
|
|
namespace Ms {
|
|
|
|
static const NoteHead::Group NOTEHEADGROUP_DEFAULT = NoteHead::Group::HEAD_NORMAL;
|
|
static const NoteHead::Type NOTEHEADTYPE_DEFAULT = NoteHead::Type::HEAD_AUTO;
|
|
static const MScore::DirectionH DIR_DEFAULT = MScore::DirectionH::AUTO;
|
|
static const bool HASLINE_DEFAULT = true;
|
|
static const qreal LINEWIDTH_DEFAULT = 0.12;
|
|
#if 0 // yet(?) unused
|
|
static const qreal LEDGEROFFSET_DEFAULT = 0.25;
|
|
#endif
|
|
static const qreal LINEOFFSET_DEFAULT = 0.8; // the distance between note head and line
|
|
|
|
//---------------------------------------------------------
|
|
// Ambitus
|
|
//---------------------------------------------------------
|
|
|
|
Ambitus::Ambitus(Score* s)
|
|
: Element(s), _topAccid(s), _bottomAccid(s)
|
|
{
|
|
_noteHeadGroup = NOTEHEADGROUP_DEFAULT;
|
|
_noteHeadType = NOTEHEADTYPE_DEFAULT;
|
|
_dir = DIR_DEFAULT;
|
|
_hasLine = HASLINE_DEFAULT;
|
|
_lineWidth = LINEWIDTH_DEFAULT;
|
|
_topPitch = _bottomPitch = INVALID_PITCH;
|
|
_topTpc = _bottomTpc = Tpc::TPC_INVALID;
|
|
_topAccid.setParent(this);
|
|
_bottomAccid.setParent(this);
|
|
// _topAccid.setFlags(0);
|
|
// _bottomAccid.setFlags(0);
|
|
setFlags(ElementFlag::MOVABLE | ElementFlag::SELECTABLE);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// setTrack
|
|
//
|
|
// when the Ambitus element is assigned a track,
|
|
// initialize top and bottom 'notes' to top and bottom staff lines
|
|
//---------------------------------------------------------
|
|
|
|
void Ambitus::setTrack(int t)
|
|
{
|
|
Segment* segm = segment();
|
|
Staff* stf = score()->staff(track2staff(t));
|
|
|
|
Element::setTrack(t);
|
|
// if not initialized and there is a segment and a staff,
|
|
// initialize pitches and tpc's to first and last staff line
|
|
// (for use in palettes)
|
|
if (_topPitch == INVALID_PITCH || _topTpc == Tpc::TPC_INVALID
|
|
|| _bottomPitch == INVALID_PITCH ||_bottomTpc == Tpc::TPC_INVALID) {
|
|
if (segm && stf) {
|
|
updateRange();
|
|
_topAccid.setTrack(t);
|
|
_bottomAccid.setTrack(t);
|
|
}
|
|
// else {
|
|
// _topPitch = _bottomPitch = INVALID_PITCH;
|
|
// _topTpc = _bottomTpc = Tpc::TPC_INVALID;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// setTop/BottomPitch
|
|
//
|
|
// setting either pitch requires to adjust the corresponding tpc
|
|
//---------------------------------------------------------
|
|
|
|
void Ambitus::setTopPitch(int val)
|
|
{
|
|
int deltaPitch = val - topPitch();
|
|
// if deltaPitch is not an integer number of octaves, adjust tpc
|
|
// (to avoid 'wild' tpc changes with octave changes)
|
|
if (deltaPitch % PITCH_DELTA_OCTAVE != 0) {
|
|
int newTpc = topTpc() + deltaPitch * TPC_DELTA_SEMITONE;
|
|
// reduce newTpc into acceptable range via enharmonic
|
|
while (newTpc < Tpc::TPC_MIN)
|
|
newTpc += TPC_DELTA_ENHARMONIC;
|
|
while (newTpc > Tpc::TPC_MAX)
|
|
newTpc -= TPC_DELTA_ENHARMONIC;
|
|
_topTpc = newTpc;
|
|
}
|
|
_topPitch = val;
|
|
normalize();
|
|
}
|
|
|
|
void Ambitus::setBottomPitch(int val)
|
|
{
|
|
int deltaPitch = val - bottomPitch();
|
|
// if deltaPitch is not an integer number of octaves, adjust tpc
|
|
// (to avoid 'wild' tpc changes with octave changes)
|
|
if (deltaPitch % PITCH_DELTA_OCTAVE != 0) {
|
|
int newTpc = bottomTpc() + deltaPitch * TPC_DELTA_SEMITONE;
|
|
// reduce newTpc into acceptable range via enharmonic
|
|
while (newTpc < Tpc::TPC_MIN)
|
|
newTpc += TPC_DELTA_ENHARMONIC;
|
|
while (newTpc > Tpc::TPC_MAX)
|
|
newTpc -= TPC_DELTA_ENHARMONIC;
|
|
_bottomTpc = newTpc;
|
|
}
|
|
_bottomPitch= val;
|
|
normalize();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// setTop/BottomTpc
|
|
//
|
|
// setting either tpc requires to adjust the corresponding pitch
|
|
// (but remaining in the same octave)
|
|
//---------------------------------------------------------
|
|
|
|
void Ambitus::setTopTpc(int val)
|
|
{
|
|
int octave = topPitch() / PITCH_DELTA_OCTAVE;
|
|
int deltaTpc = val - topTpc();
|
|
// get new pitch according to tpc change
|
|
int newPitch = topPitch() + deltaTpc * TPC_DELTA_SEMITONE;
|
|
// reduce pitch to the same octave as original pitch
|
|
newPitch = (octave * PITCH_DELTA_OCTAVE) + (newPitch % PITCH_DELTA_OCTAVE);
|
|
_topPitch = newPitch;
|
|
_topTpc = val;
|
|
normalize();
|
|
}
|
|
|
|
void Ambitus::setBottomTpc(int val)
|
|
{
|
|
int octave = bottomPitch() / PITCH_DELTA_OCTAVE;
|
|
int deltaTpc = val - bottomTpc();
|
|
// get new pitch according to tpc change
|
|
int newPitch = bottomPitch() + deltaTpc * TPC_DELTA_SEMITONE;
|
|
// reduce pitch to the same octave as original pitch
|
|
newPitch = (octave * PITCH_DELTA_OCTAVE) + (newPitch % PITCH_DELTA_OCTAVE);
|
|
_bottomPitch= newPitch;
|
|
_bottomTpc = val;
|
|
normalize();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// write
|
|
//---------------------------------------------------------
|
|
|
|
void Ambitus::write(Xml& xml) const
|
|
{
|
|
xml.stag("Ambitus");
|
|
xml.tag(P_ID::HEAD_GROUP, int(_noteHeadGroup), int(NOTEHEADGROUP_DEFAULT));
|
|
xml.tag(P_ID::HEAD_TYPE, int(_noteHeadType), int(NOTEHEADTYPE_DEFAULT));
|
|
xml.tag(P_ID::MIRROR_HEAD,int(_dir), int(DIR_DEFAULT));
|
|
xml.tag("hasLine", _hasLine, true);
|
|
xml.tag(P_ID::LINE_WIDTH, _lineWidth, LINEWIDTH_DEFAULT);
|
|
xml.tag("topPitch", _topPitch);
|
|
xml.tag("topTpc", _topTpc);
|
|
xml.tag("bottomPitch",_bottomPitch);
|
|
xml.tag("bottomTpc", _bottomTpc);
|
|
if (_topAccid.accidentalType() != Accidental::Type::NONE) {
|
|
xml.stag("topAccidental");
|
|
_topAccid.write(xml);
|
|
xml.etag();
|
|
}
|
|
if (_bottomAccid.accidentalType() != Accidental::Type::NONE) {
|
|
xml.stag("bottomAccidental");
|
|
_bottomAccid.write(xml);
|
|
xml.etag();
|
|
}
|
|
Element::writeProperties(xml);
|
|
xml.etag();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// read
|
|
//---------------------------------------------------------
|
|
|
|
void Ambitus::read(XmlReader& e)
|
|
{
|
|
while (e.readNextStartElement()) {
|
|
const QStringRef& tag(e.name());
|
|
if (tag == "head")
|
|
setProperty(P_ID::HEAD_GROUP, Ms::getProperty(P_ID::HEAD_GROUP, e));
|
|
else if (tag == "headType")
|
|
setProperty(P_ID::HEAD_TYPE, Ms::getProperty(P_ID::HEAD_TYPE, e).toInt());
|
|
else if (tag == "mirror")
|
|
setProperty(P_ID::MIRROR_HEAD, Ms::getProperty(P_ID::MIRROR_HEAD, e).toInt());
|
|
else if (tag == "hasLine")
|
|
setHasLine(e.readInt());
|
|
else if (tag == "lineWidth")
|
|
setProperty(P_ID::LINE_WIDTH, Ms::getProperty(P_ID::LINE_WIDTH, e).toReal());
|
|
else if (tag == "topPitch")
|
|
_topPitch = e.readInt();
|
|
else if (tag == "bottomPitch")
|
|
_bottomPitch = e.readInt();
|
|
else if (tag == "topTpc")
|
|
_topTpc = e.readInt();
|
|
else if (tag == "bottomTpc")
|
|
_bottomTpc = e.readInt();
|
|
else if (tag == "topAccidental") {
|
|
while (e.readNextStartElement()) {
|
|
if (e.name() == "Accidental")
|
|
_topAccid.read(e);
|
|
else
|
|
e.skipCurrentElement();
|
|
}
|
|
}
|
|
else if (tag == "bottomAccidental") {
|
|
while (e.readNextStartElement()) {
|
|
if (e.name() == "Accidental")
|
|
_bottomAccid.read(e);
|
|
else
|
|
e.skipCurrentElement();
|
|
}
|
|
}
|
|
else if (!Element::readProperties(e))
|
|
e.unknown();
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// layout
|
|
//---------------------------------------------------------
|
|
|
|
void Ambitus::layout()
|
|
{
|
|
int bottomLine, topLine;
|
|
ClefType clf;
|
|
qreal headWdt = headWidth();
|
|
Key key;
|
|
qreal lineDist;
|
|
int numOfLines;
|
|
Segment* segm = segment();
|
|
qreal _spatium = spatium();
|
|
Staff* stf = nullptr;
|
|
if (segm && track() > -1) {
|
|
int tick = segm->tick();
|
|
stf = score()->staff(staffIdx());
|
|
lineDist = stf->lineDistance() * _spatium;
|
|
numOfLines = stf->lines();
|
|
clf = stf->clef(tick);
|
|
}
|
|
else { // for use in palettes
|
|
lineDist = _spatium;
|
|
numOfLines = 3;
|
|
clf = ClefType::G;
|
|
}
|
|
|
|
//
|
|
// NOTE HEADS Y POS
|
|
//
|
|
// if pitch == INVALID_PITCH oor tpc == INALID_TPC, set to some default:
|
|
// for use in palettes and when actual range cannot be calculated (new ambitus or no notes in staff)
|
|
//
|
|
qreal xAccidOffTop = 0;
|
|
qreal xAccidOffBottom = 0;
|
|
if (stf)
|
|
key = stf->key(segm->tick());
|
|
else
|
|
key = Key::C;
|
|
|
|
// top note head
|
|
if (_topPitch == INVALID_PITCH || _topTpc == Tpc::TPC_INVALID)
|
|
_topPos.setY(0); // if uninitialized, set to top staff line
|
|
else {
|
|
topLine = absStep(_topTpc, _topPitch);
|
|
topLine = relStep(topLine, clf);
|
|
_topPos.setY(topLine * lineDist * 0.5);
|
|
// compute accidental
|
|
Accidental::Type accidType;
|
|
// if (13 <= (tpc - key) <= 19) there is no accidental)
|
|
if (_topTpc - int(key) >= 13 && _topTpc - int(key) <= 19)
|
|
accidType = Accidental::Type::NONE;
|
|
else {
|
|
AccidentalVal accidVal = AccidentalVal( (_topTpc - Tpc::TPC_MIN) / TPC_DELTA_SEMITONE - 2 );
|
|
accidType = Accidental::value2subtype(accidVal);
|
|
if (accidType == Accidental::Type::NONE)
|
|
accidType = Accidental::Type::NATURAL;
|
|
}
|
|
_topAccid.setAccidentalType(accidType);
|
|
if (accidType != Accidental::Type::NONE)
|
|
_topAccid.layout();
|
|
else
|
|
_topAccid.setbbox(QRect());
|
|
_topAccid.rypos() = _topPos.y();
|
|
}
|
|
|
|
// bottom note head
|
|
if (_bottomPitch == INVALID_PITCH || _bottomTpc == Tpc::TPC_INVALID)
|
|
_bottomPos.setY( (numOfLines-1) * lineDist); // if uninitialized, set to last staff line
|
|
else {
|
|
bottomLine = absStep(_bottomTpc, _bottomPitch);
|
|
bottomLine = relStep(bottomLine, clf);
|
|
_bottomPos.setY(bottomLine * lineDist * 0.5);
|
|
// compute accidental
|
|
Accidental::Type accidType;
|
|
if (_bottomTpc - int(key) >= 13 && _bottomTpc - int(key) <= 19)
|
|
accidType = Accidental::Type::NONE;
|
|
else {
|
|
AccidentalVal accidVal = AccidentalVal( (_bottomTpc - Tpc::TPC_MIN) / TPC_DELTA_SEMITONE - 2 );
|
|
accidType = Accidental::value2subtype(accidVal);
|
|
if (accidType == Accidental::Type::NONE)
|
|
accidType = Accidental::Type::NATURAL;
|
|
}
|
|
_bottomAccid.setAccidentalType(accidType);
|
|
if (accidType != Accidental::Type::NONE)
|
|
_bottomAccid.layout();
|
|
else
|
|
_bottomAccid.setbbox(QRect());
|
|
_bottomAccid.rypos() = _bottomPos.y();
|
|
}
|
|
|
|
//
|
|
// NOTE HEAD X POS
|
|
//
|
|
// Note: manages colliding accidentals
|
|
//
|
|
qreal accNoteDist = point(score()->styleS(StyleIdx::accidentalNoteDistance));
|
|
xAccidOffTop = _topAccid.width() + accNoteDist;
|
|
xAccidOffBottom = _bottomAccid.width() + accNoteDist;
|
|
|
|
// if top accidental extends down more than bottom accidental extends up,
|
|
// AND ambitus is not leaning right, bottom accidental needs to be displaced
|
|
bool collision =
|
|
(_topAccid.ipos().y() + _topAccid.bbox().y() + _topAccid.height()
|
|
> _bottomAccid.ipos().y() + _bottomAccid.bbox().y() )
|
|
&& _dir != MScore::DirectionH::RIGHT;
|
|
if (collision) {
|
|
// displace bottom accidental (also attempting to 'undercut' flats)
|
|
xAccidOffBottom = xAccidOffTop +
|
|
((_bottomAccid.accidentalType() == Accidental::Type::FLAT
|
|
|| _bottomAccid.accidentalType() == Accidental::Type::FLAT2
|
|
|| _bottomAccid.accidentalType() == Accidental::Type::NATURAL)
|
|
? _bottomAccid.width() * 0.5 : _bottomAccid.width());
|
|
}
|
|
|
|
switch (_dir) {
|
|
case MScore::DirectionH::AUTO: // note heads one above the other
|
|
// left align note heads and right align accidentals 'hanging' on the left
|
|
_topPos.setX(0.0);
|
|
_bottomPos.setX(0.0);
|
|
_topAccid.rxpos() = - xAccidOffTop;
|
|
_bottomAccid.rxpos() = - xAccidOffBottom;
|
|
break;
|
|
case MScore::DirectionH::LEFT: // top note head at the left of bottom note head
|
|
// place top note head at left margin; bottom note head at right of top head;
|
|
// top accid. 'hanging' on left of top head and bottom accid. 'hanging' at left of bottom head
|
|
_topPos.setX(0.0);
|
|
_bottomPos.setX(headWdt);
|
|
_topAccid.rxpos() = - xAccidOffTop;
|
|
_bottomAccid.rxpos() = collision ? - xAccidOffBottom : headWdt - xAccidOffBottom;
|
|
break;
|
|
case MScore::DirectionH::RIGHT: // top note head at the right of bottom note head
|
|
// bottom note head at left margin; top note head at right of bottomnote head
|
|
// top accid. 'hanging' on left of top head and bottom accid. 'hanging' at left of bottom head
|
|
_bottomPos.setX(0.0);
|
|
_topPos.setX(headWdt);
|
|
_bottomAccid.rxpos() = - xAccidOffBottom;
|
|
_topAccid.rxpos() = headWdt - xAccidOffTop;
|
|
break;
|
|
}
|
|
|
|
// compute line from top note centre to bottom note centre
|
|
QLineF fullLine(_topPos.x() + headWdt*0.5, _topPos.y(),
|
|
_bottomPos.x() + headWdt*0.5, _bottomPos.y());
|
|
// shorten line on each side by offsets
|
|
qreal yDelta = _bottomPos.y() - _topPos.y();
|
|
if (yDelta != 0.0) {
|
|
qreal off = _spatium * LINEOFFSET_DEFAULT;
|
|
QPointF p1 = fullLine.pointAt(off / yDelta);
|
|
QPointF p2 = fullLine.pointAt(1 - (off / yDelta));
|
|
_line = QLineF(p1, p2);
|
|
}
|
|
else
|
|
_line = fullLine;
|
|
|
|
QRectF headRect = QRectF(0, -0.5*_spatium, headWdt, 1*_spatium);
|
|
setbbox(headRect.translated(_topPos).united(headRect.translated(_bottomPos))
|
|
.united(_topAccid.bbox().translated(_topAccid.ipos()))
|
|
.united(_bottomAccid.bbox().translated(_bottomAccid.ipos()))
|
|
);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// draw
|
|
//---------------------------------------------------------
|
|
|
|
void Ambitus::draw(QPainter* p) const
|
|
{
|
|
qreal _spatium = spatium();
|
|
qreal lw = lineWidth() * _spatium;
|
|
p->setPen(QPen(curColor(), lw, Qt::SolidLine, Qt::RoundCap));
|
|
drawSymbol(noteHead(), p, _topPos);
|
|
drawSymbol(noteHead(), p, _bottomPos);
|
|
if (_hasLine)
|
|
p->drawLine(_line);
|
|
|
|
// draw ledger lines (if not in a palette)
|
|
if (segment() && track() > -1) {
|
|
Staff* stf = score()->staff(staffIdx());
|
|
qreal lineDist = stf->lineDistance();
|
|
int numOfLines = stf->lines();
|
|
qreal step = lineDist * _spatium;
|
|
qreal stepTolerance = step * 0.1;
|
|
qreal ledgerOffset = score()->styleS(StyleIdx::ledgerLineLength).val() * 0.5 * _spatium;
|
|
p->setPen(QPen(curColor(), score()->styleS(StyleIdx::ledgerLineWidth).val() * _spatium,
|
|
Qt::SolidLine, Qt::RoundCap) );
|
|
if (_topPos.y()-stepTolerance <= -step) {
|
|
qreal xMin = _topPos.x() - ledgerOffset;
|
|
qreal xMax = _topPos.x() + headWidth() + ledgerOffset;
|
|
for (qreal y = -step; y >= _topPos.y()-stepTolerance; y -= step)
|
|
p->drawLine(QPointF(xMin, y), QPointF(xMax, y));
|
|
}
|
|
if (_bottomPos.y()+stepTolerance >= numOfLines * step) {
|
|
qreal xMin = _bottomPos.x() - ledgerOffset;
|
|
qreal xMax = _bottomPos.x() + headWidth() + ledgerOffset;
|
|
for (qreal y = numOfLines*step; y <= _bottomPos.y()+stepTolerance; y += step)
|
|
p->drawLine(QPointF(xMin, y), QPointF(xMax, y));
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// space
|
|
//---------------------------------------------------------
|
|
|
|
Space Ambitus::space() const
|
|
{
|
|
qreal _spatium = spatium();
|
|
// reduce left space if there accidentals
|
|
qreal leftSpace = _spatium *
|
|
((_topAccid.accidentalType() != Accidental::Type::NONE
|
|
|| _bottomAccid.accidentalType() != Accidental::Type::NONE)
|
|
? 0.5 : 0.75);
|
|
return Space(leftSpace - bbox().x(), width() + bbox().x() + _spatium * 0.5);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// scanElements
|
|
//---------------------------------------------------------
|
|
|
|
void Ambitus::scanElements(void* data, void (*func)(void*, Element*), bool /*all*/)
|
|
{
|
|
func(data, this);
|
|
if (_topAccid.accidentalType() != Accidental::Type::NONE)
|
|
func(data, &_topAccid);
|
|
if (_bottomAccid.accidentalType() != Accidental::Type::NONE)
|
|
func(data, &_bottomAccid);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// noteHead
|
|
//---------------------------------------------------------
|
|
|
|
SymId Ambitus::noteHead() const
|
|
{
|
|
int hg = 1;
|
|
NoteHead::Type ht = NoteHead::Type::HEAD_QUARTER;
|
|
|
|
if (_noteHeadType != NoteHead::Type::HEAD_AUTO)
|
|
ht = _noteHeadType;
|
|
|
|
SymId t = Note::noteHead(hg, _noteHeadGroup, ht);
|
|
if (t == SymId::noSym) {
|
|
qDebug("invalid note head %hhd/%hhd", _noteHeadGroup, _noteHeadType);
|
|
t = Note::noteHead(0, NoteHead::Group::HEAD_NORMAL, ht);
|
|
}
|
|
return t;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// headWidth
|
|
//
|
|
// returns the width of the note head symbol
|
|
//---------------------------------------------------------
|
|
|
|
qreal Ambitus::headWidth() const
|
|
{
|
|
// int head = noteHead();
|
|
// qreal val = symbols[score()->symIdx()][head].width(magS());
|
|
// return val;
|
|
return symWidth(noteHead());
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// pagePos
|
|
//---------------------------------------------------------
|
|
|
|
QPointF Ambitus::pagePos() const
|
|
{
|
|
if (parent() == 0)
|
|
return pos();
|
|
System* system = segment()->measure()->system();
|
|
qreal yp = y();
|
|
if (system)
|
|
yp += system->staff(staffIdx())->y() + system->y();
|
|
return QPointF(pageX(), yp);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// normalize
|
|
//
|
|
// makes sure _topPitch is not < _bottomPitch
|
|
//---------------------------------------------------------
|
|
|
|
void Ambitus::normalize()
|
|
{
|
|
if (_topPitch < _bottomPitch) {
|
|
int temp = _topPitch;
|
|
_topPitch = _bottomPitch;
|
|
_bottomPitch= temp;
|
|
temp = _topTpc;
|
|
_topTpc = _bottomTpc;
|
|
_bottomTpc = temp;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// updateRange
|
|
//
|
|
// scans the staff contents up to next section break to update the range pitches/tpc's
|
|
//---------------------------------------------------------
|
|
|
|
void Ambitus::updateRange()
|
|
{
|
|
if (!segment())
|
|
return;
|
|
Chord* chord;
|
|
int firstTrack = track();
|
|
int lastTrack = firstTrack + VOICES-1;
|
|
int pitchTop = -1000;
|
|
int pitchBottom = 1000;
|
|
int tpcTop, tpcBottom;
|
|
int trk;
|
|
Measure* meas = segment()->measure();
|
|
Segment* segm = meas->findSegment(Segment::Type::ChordRest, segment()->tick());
|
|
bool stop = meas->sectionBreak() != nullptr;
|
|
while (segm) {
|
|
// moved to another measure?
|
|
if (segm->measure() != meas) {
|
|
// if section break has been found, stop here
|
|
if (stop)
|
|
break;
|
|
// update meas and stop condition
|
|
meas = segm->measure();
|
|
stop = meas->sectionBreak() != nullptr;
|
|
}
|
|
// scan all relevant tracks of this segment for chords
|
|
for (trk=firstTrack; trk <= lastTrack; trk++)
|
|
if ( (chord=static_cast<Chord*>(segm->element(trk))) != nullptr
|
|
&& chord->type() == Element::Type::CHORD) {
|
|
// update pitch range (with associated tpc's)
|
|
foreach (Note* n, chord->notes()) {
|
|
int pitch = n->ppitch();
|
|
if (pitch > pitchTop) {
|
|
pitchTop = pitch;
|
|
tpcTop = n->tpc();
|
|
}
|
|
if (pitch < pitchBottom) {
|
|
pitchBottom = pitch;
|
|
tpcBottom = n->tpc();
|
|
}
|
|
}
|
|
}
|
|
segm = segm->nextCR();
|
|
}
|
|
|
|
if (pitchTop > -1000) { // if something has been found, update this
|
|
_topPitch = pitchTop;
|
|
_bottomPitch = pitchBottom;
|
|
_topTpc = tpcTop;
|
|
_bottomTpc = tpcBottom;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// getProperty
|
|
//---------------------------------------------------------
|
|
|
|
QVariant Ambitus::getProperty(P_ID propertyId) const
|
|
{
|
|
switch(propertyId) {
|
|
case P_ID::HEAD_GROUP:
|
|
return int(noteHeadGroup());
|
|
case P_ID::HEAD_TYPE:
|
|
return int(noteHeadType());
|
|
case P_ID::MIRROR_HEAD:
|
|
return int(direction());
|
|
case P_ID::GHOST: // recycled property = _hasLine
|
|
return hasLine();
|
|
case P_ID::LINE_WIDTH:
|
|
return lineWidth();
|
|
case P_ID::TPC1:
|
|
return topTpc();
|
|
case P_ID::FBPARENTHESIS1: // recycled property = _bottomTpc
|
|
return bottomTpc();
|
|
case P_ID::PITCH:
|
|
return topPitch();
|
|
case P_ID::FBPARENTHESIS2: // recycled property = _bottomPitch
|
|
return bottomPitch();
|
|
case P_ID::FBPARENTHESIS3: // recycled property = octave of _topPitch
|
|
return topOctave();
|
|
case P_ID::FBPARENTHESIS4: // recycled property = octave of _bottomPitch
|
|
return bottomOctave();
|
|
default:
|
|
return Element::getProperty(propertyId);
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// setProperty
|
|
//---------------------------------------------------------
|
|
|
|
bool Ambitus::setProperty(P_ID propertyId, const QVariant& v)
|
|
{
|
|
bool rv = true;
|
|
|
|
score()->addRefresh(canvasBoundingRect());
|
|
switch(propertyId) {
|
|
case P_ID::HEAD_GROUP:
|
|
setNoteHeadGroup( NoteHead::Group(v.toInt()) );
|
|
break;
|
|
case P_ID::HEAD_TYPE:
|
|
setNoteHeadType( NoteHead::Type(v.toInt()) );
|
|
break;
|
|
case P_ID::MIRROR_HEAD:
|
|
setDirection(MScore::DirectionH(v.toInt()) );
|
|
break;
|
|
case P_ID::GHOST: // recycled property = _hasLine
|
|
setHasLine(v.toBool());
|
|
break;
|
|
case P_ID::LINE_WIDTH:
|
|
setLineWidth(v.toReal());
|
|
break;
|
|
case P_ID::TPC1:
|
|
setTopTpc(v.toInt());
|
|
break;
|
|
case P_ID::FBPARENTHESIS1: // recycled property = _bottomTpc
|
|
setBottomTpc(v.toInt());
|
|
break;
|
|
case P_ID::PITCH:
|
|
setTopPitch(v.toInt());
|
|
break;
|
|
case P_ID::FBPARENTHESIS2: // recycled property = _bottomPitch
|
|
setBottomPitch(v.toInt());
|
|
break;
|
|
case P_ID::FBPARENTHESIS3: // recycled property = octave of _topPitch
|
|
setTopPitch(topPitch() % 12 + v.toInt() * 12);
|
|
break;
|
|
case P_ID::FBPARENTHESIS4: // recycled property = octave of _bottomPitch
|
|
setBottomPitch(bottomPitch() % 12 + v.toInt() * 12);
|
|
break;
|
|
default:
|
|
rv = Element::setProperty(propertyId, v);
|
|
break;
|
|
}
|
|
if (rv)
|
|
score()->setLayoutAll(true);
|
|
return rv;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// propertyDefault
|
|
//---------------------------------------------------------
|
|
|
|
QVariant Ambitus::propertyDefault(P_ID id) const
|
|
{
|
|
switch(id) {
|
|
case P_ID::HEAD_GROUP: return int(NOTEHEADGROUP_DEFAULT);
|
|
case P_ID::HEAD_TYPE: return int(NOTEHEADTYPE_DEFAULT);
|
|
case P_ID::MIRROR_HEAD: return int(DIR_DEFAULT);
|
|
case P_ID::GHOST: return HASLINE_DEFAULT;
|
|
case P_ID::LINE_WIDTH: return LINEWIDTH_DEFAULT;
|
|
case P_ID::TPC1: // no defaults for pitches, tpc's and octaves
|
|
case P_ID::FBPARENTHESIS1:
|
|
case P_ID::PITCH:
|
|
case P_ID::FBPARENTHESIS2:
|
|
case P_ID::FBPARENTHESIS3:
|
|
case P_ID::FBPARENTHESIS4:
|
|
break;
|
|
default: return Element::propertyDefault(id);
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
}
|
|
|