703 lines
23 KiB
C++
703 lines
23 KiB
C++
/*
|
|
* SPDX-License-Identifier: GPL-3.0-only
|
|
* MuseScore-CLA-applies
|
|
*
|
|
* MuseScore
|
|
* Music Composition & Notation
|
|
*
|
|
* Copyright (C) 2021 MuseScore BVBA 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 3 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "dynamic.h"
|
|
#include "style/style.h"
|
|
#include "io/xml.h"
|
|
|
|
#include "dynamichairpingroup.h"
|
|
#include "score.h"
|
|
#include "measure.h"
|
|
#include "system.h"
|
|
#include "symid.h"
|
|
#include "segment.h"
|
|
#include "utils.h"
|
|
#include "mscore.h"
|
|
#include "chord.h"
|
|
#include "undo.h"
|
|
#include "musescoreCore.h"
|
|
|
|
using namespace mu;
|
|
using namespace mu::engraving;
|
|
|
|
namespace Ms {
|
|
//-----------------------------------------------------------------------------
|
|
// Dyn
|
|
// see: http://en.wikipedia.org/wiki/File:Dynamic's_Note_Velocity.svg
|
|
//-----------------------------------------------------------------------------
|
|
|
|
struct Dyn {
|
|
int velocity; ///< associated midi velocity (0-127, -1 = none)
|
|
bool accent; ///< if true add velocity to current chord velocity
|
|
SymId symId;
|
|
const char* tag; // name of dynamics, eg. "fff"
|
|
const char* text; // utf8 text of dynamic
|
|
int changeInVelocity;
|
|
};
|
|
|
|
// variant with ligatures, works for both emmentaler and bravura:
|
|
|
|
static Dyn dynList[] = {
|
|
// dynamic:
|
|
{ -1, true, SymId::noSym, "other-dynamics", "", 0 },
|
|
{ 1, false, SymId::dynamicPPPPPP, "pppppp",
|
|
"<sym>dynamicPiano</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym>",
|
|
0 },
|
|
{ 5, false, SymId::dynamicPPPPP, "ppppp",
|
|
"<sym>dynamicPiano</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym>", 0 },
|
|
{ 10, false, SymId::dynamicPPPP, "pppp",
|
|
"<sym>dynamicPiano</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym>", 0 },
|
|
{ 16, false, SymId::dynamicPPP, "ppp", "<sym>dynamicPiano</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym>", 0 },
|
|
{ 33, false, SymId::dynamicPP, "pp", "<sym>dynamicPiano</sym><sym>dynamicPiano</sym>", 0 },
|
|
{ 49, false, SymId::dynamicPiano, "p", "<sym>dynamicPiano</sym>", 0 },
|
|
{ 64, false, SymId::dynamicMP, "mp", "<sym>dynamicMezzo</sym><sym>dynamicPiano</sym>", 0 },
|
|
{ 80, false, SymId::dynamicMF, "mf", "<sym>dynamicMezzo</sym><sym>dynamicForte</sym>", 0 },
|
|
{ 96, false, SymId::dynamicForte, "f", "<sym>dynamicForte</sym>", 0 },
|
|
{ 112, false, SymId::dynamicFF, "ff", "<sym>dynamicForte</sym><sym>dynamicForte</sym>", 0 },
|
|
{ 126, false, SymId::dynamicFFF, "fff", "<sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicForte</sym>", 0 },
|
|
{ 127, false, SymId::dynamicFFFF, "ffff",
|
|
"<sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicForte</sym>", 0 },
|
|
{ 127, false, SymId::dynamicFFFFF, "fffff",
|
|
"<sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicForte</sym>", 0 },
|
|
{ 127, false, SymId::dynamicFFFFFF, "ffffff",
|
|
"<sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicForte</sym>",
|
|
0 },
|
|
|
|
// accents:
|
|
{ 96, true, SymId::dynamicFortePiano, "fp", "<sym>dynamicForte</sym><sym>dynamicPiano</sym>", -47 },
|
|
{ 49, true, SymId::noSym, "pf", "<sym>dynamicPiano</sym><sym>dynamicForte</sym>", 47 },
|
|
{ 112, true, SymId::dynamicSforzando1, "sf", "<sym>dynamicSforzando</sym><sym>dynamicForte</sym>", -18 },
|
|
{ 112, true, SymId::dynamicSforzato, "sfz", "<sym>dynamicSforzando</sym><sym>dynamicForte</sym><sym>dynamicZ</sym>",
|
|
-18 },
|
|
{ 126, true, SymId::noSym, "sff", "<sym>dynamicSforzando</sym><sym>dynamicForte</sym><sym>dynamicForte</sym>",
|
|
-18 },
|
|
{ 126, true, SymId::dynamicSforzatoFF, "sffz",
|
|
"<sym>dynamicSforzando</sym><sym>dynamicForte</sym><sym>dynamicForte</sym><sym>dynamicZ</sym>", -18 },
|
|
{ 112, true, SymId::dynamicSforzandoPiano, "sfp", "<sym>dynamicSforzando</sym><sym>dynamicForte</sym><sym>dynamicPiano</sym>",
|
|
-47 },
|
|
{ 112, true, SymId::dynamicSforzandoPianissimo, "sfpp",
|
|
"<sym>dynamicSforzando</sym><sym>dynamicForte</sym><sym>dynamicPiano</sym><sym>dynamicPiano</sym>", -79 },
|
|
{ 112, true, SymId::dynamicRinforzando2, "rfz", "<sym>dynamicRinforzando</sym><sym>dynamicForte</sym><sym>dynamicZ</sym>",
|
|
-18 },
|
|
{ 112, true, SymId::dynamicRinforzando1, "rf", "<sym>dynamicRinforzando</sym><sym>dynamicForte</sym>", -18 },
|
|
{ 112, true, SymId::dynamicForzando, "fz", "<sym>dynamicForte</sym><sym>dynamicZ</sym>", -18 },
|
|
{ 96, true, SymId::dynamicMezzo, "m", "<sym>dynamicMezzo</sym>", -16 },
|
|
{ 112, true, SymId::dynamicRinforzando, "r", "<sym>dynamicRinforzando</sym>", -18 },
|
|
{ 112, true, SymId::dynamicSforzando, "s", "<sym>dynamicSforzando</sym>", -18 },
|
|
{ 80, true, SymId::dynamicZ, "z", "<sym>dynamicZ</sym>", 0 },
|
|
{ 49, true, SymId::dynamicNiente, "n", "<sym>dynamicNiente</sym>", -48 }
|
|
};
|
|
|
|
//---------------------------------------------------------
|
|
// dynamicsStyle
|
|
//---------------------------------------------------------
|
|
|
|
static const ElementStyle dynamicsStyle {
|
|
{ Sid::dynamicsPlacement, Pid::PLACEMENT },
|
|
{ Sid::dynamicsMinDistance, Pid::MIN_DISTANCE },
|
|
};
|
|
|
|
//---------------------------------------------------------
|
|
// changeSpeedTable
|
|
//---------------------------------------------------------
|
|
|
|
const std::vector<Dynamic::ChangeSpeedItem> Dynamic::changeSpeedTable {
|
|
{ Dynamic::Speed::NORMAL, "normal" },
|
|
{ Dynamic::Speed::SLOW, "slow" },
|
|
{ Dynamic::Speed::FAST, "fast" },
|
|
};
|
|
|
|
//---------------------------------------------------------
|
|
// findInString
|
|
//---------------------------------------------------------
|
|
|
|
// find the longest first match of dynList's dynamic text in s
|
|
// used by the MusicXML export to correctly export dynamics embedded
|
|
// in spanner begin- or endtexts
|
|
// return match's position and length and the dynamic type
|
|
|
|
int Dynamic::findInString(const QString& s, int& length, QString& type)
|
|
{
|
|
length = 0;
|
|
type = "";
|
|
int matchIndex { -1 };
|
|
const int n = sizeof(dynList) / sizeof(*dynList);
|
|
|
|
// for all dynamics, find their text in s
|
|
for (int i = 0; i < n; ++i) {
|
|
const QString dynamicText = dynList[i].text;
|
|
const int dynamicLength = dynamicText.length();
|
|
// note: skip entries with empty text
|
|
if (dynamicLength > 0) {
|
|
const auto index = s.indexOf(dynamicText);
|
|
if (index >= 0) {
|
|
// found a match, accept it if
|
|
// - it is the first one
|
|
// - or it starts a the same index but is longer ("pp" versus "p")
|
|
if (matchIndex == -1 || (index == matchIndex && dynamicLength > length)) {
|
|
matchIndex = index;
|
|
length = dynamicLength;
|
|
type = dynList[i].tag;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return matchIndex;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// Dynamic
|
|
//---------------------------------------------------------
|
|
|
|
Dynamic::Dynamic(Segment* parent)
|
|
: TextBase(ElementType::DYNAMIC, parent, Tid::DYNAMICS, ElementFlag::MOVABLE | ElementFlag::ON_STAFF)
|
|
{
|
|
_velocity = -1;
|
|
_dynRange = Range::PART;
|
|
_dynamicType = DynamicType::OTHER;
|
|
_changeInVelocity = 128;
|
|
_velChangeSpeed = Speed::NORMAL;
|
|
initElementStyle(&dynamicsStyle);
|
|
}
|
|
|
|
Dynamic::Dynamic(const Dynamic& d)
|
|
: TextBase(d)
|
|
{
|
|
_dynamicType = d._dynamicType;
|
|
_velocity = d._velocity;
|
|
_dynRange = d._dynRange;
|
|
_changeInVelocity = d._changeInVelocity;
|
|
_velChangeSpeed = d._velChangeSpeed;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// velocity
|
|
//---------------------------------------------------------
|
|
|
|
int Dynamic::velocity() const
|
|
{
|
|
return _velocity <= 0 ? dynList[int(dynamicType())].velocity : _velocity;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// changeInVelocity
|
|
//---------------------------------------------------------
|
|
|
|
int Dynamic::changeInVelocity() const
|
|
{
|
|
return _changeInVelocity >= 128 ? dynList[int(dynamicType())].changeInVelocity : _changeInVelocity;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// setChangeInVelocity
|
|
//---------------------------------------------------------
|
|
|
|
void Dynamic::setChangeInVelocity(int val)
|
|
{
|
|
if (dynList[int(dynamicType())].changeInVelocity == val) {
|
|
_changeInVelocity = 128;
|
|
} else {
|
|
_changeInVelocity = val;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// velocityChangeLength
|
|
// the time over which the velocity change occurs
|
|
//---------------------------------------------------------
|
|
|
|
Fraction Dynamic::velocityChangeLength() const
|
|
{
|
|
if (changeInVelocity() == 0) {
|
|
return Fraction::fromTicks(0);
|
|
}
|
|
|
|
double ratio = double(score()->tempomap()->tempo(segment()->tick().ticks())) / double(Score::defaultTempo());
|
|
double speedMult;
|
|
switch (velChangeSpeed()) {
|
|
case Dynamic::Speed::SLOW:
|
|
speedMult = 1.3;
|
|
break;
|
|
case Dynamic::Speed::FAST:
|
|
speedMult = 0.5;
|
|
break;
|
|
case Dynamic::Speed::NORMAL:
|
|
default:
|
|
speedMult = 0.8;
|
|
break;
|
|
}
|
|
|
|
return Fraction::fromTicks(int(ratio * (speedMult * double(MScore::division))));
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// isVelocityChangeAvailable
|
|
//---------------------------------------------------------
|
|
|
|
bool Dynamic::isVelocityChangeAvailable() const
|
|
{
|
|
switch (dynamicType()) {
|
|
case DynamicType::FP:
|
|
case DynamicType::SF:
|
|
case DynamicType::SFZ:
|
|
case DynamicType::SFF:
|
|
case DynamicType::SFFZ:
|
|
case DynamicType::SFP:
|
|
case DynamicType::SFPP:
|
|
case DynamicType::RFZ:
|
|
case DynamicType::RF:
|
|
case DynamicType::FZ:
|
|
case DynamicType::M:
|
|
case DynamicType::R:
|
|
case DynamicType::S:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// write
|
|
//---------------------------------------------------------
|
|
|
|
void Dynamic::write(XmlWriter& xml) const
|
|
{
|
|
if (!xml.canWrite(this)) {
|
|
return;
|
|
}
|
|
xml.startObject(this);
|
|
writeProperty(xml, Pid::DYNAMIC_TYPE);
|
|
writeProperty(xml, Pid::VELOCITY);
|
|
writeProperty(xml, Pid::DYNAMIC_RANGE);
|
|
writeProperty(xml, Pid::VELO_CHANGE);
|
|
writeProperty(xml, Pid::VELO_CHANGE_SPEED);
|
|
TextBase::writeProperties(xml, dynamicType() == DynamicType::OTHER);
|
|
xml.endObject();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// read
|
|
//---------------------------------------------------------
|
|
|
|
void Dynamic::read(XmlReader& e)
|
|
{
|
|
while (e.readNextStartElement()) {
|
|
const QStringRef& tag = e.name();
|
|
if (tag == "subtype") {
|
|
setDynamicType(e.readElementText());
|
|
} else if (tag == "velocity") {
|
|
_velocity = e.readInt();
|
|
} else if (tag == "dynType") {
|
|
_dynRange = Range(e.readInt());
|
|
} else if (tag == "veloChange") {
|
|
_changeInVelocity = e.readInt();
|
|
} else if (tag == "veloChangeSpeed") {
|
|
_velChangeSpeed = nameToSpeed(e.readElementText());
|
|
} else if (!TextBase::readProperties(e)) {
|
|
e.unknown();
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// layout
|
|
//---------------------------------------------------------
|
|
|
|
void Dynamic::layout()
|
|
{
|
|
TextBase::layout();
|
|
|
|
Segment* s = segment();
|
|
if (s) {
|
|
int t = track() & ~0x3;
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
|
EngravingItem* e = s->element(t + voice);
|
|
if (!e) {
|
|
continue;
|
|
}
|
|
if (e->isChord() && (align() & Align::HCENTER)) {
|
|
SymId symId = dynList[int(dynamicType())].symId;
|
|
|
|
// this value is different than chord()->mag() or mag()
|
|
// as it reflects the actual scaling of the text
|
|
// using chord()->mag(), mag() or fontSize will yield
|
|
// undesirable results with small staves or cue notes
|
|
qreal dynamicMag = spatium() / SPATIUM20;
|
|
|
|
qreal noteHeadWidth = score()->noteHeadWidth() * dynamicMag;
|
|
rxpos() += noteHeadWidth * .5;
|
|
|
|
qreal opticalCenter = symSmuflAnchor(symId, SmuflAnchorId::opticalCenter).x() * dynamicMag;
|
|
if (symId != SymId::noSym && opticalCenter) {
|
|
static const qreal DEFAULT_DYNAMIC_FONT_SIZE = 10.0;
|
|
qreal fontScaling = size() / DEFAULT_DYNAMIC_FONT_SIZE;
|
|
qreal left = symBbox(symId).bottomLeft().x() * dynamicMag; // this is negative per SMuFL spec
|
|
|
|
opticalCenter += fontScaling;
|
|
left += fontScaling;
|
|
|
|
qreal offset = opticalCenter - left - bbox().width() * 0.5;
|
|
rxpos() -= offset;
|
|
}
|
|
} else {
|
|
rxpos() += e->width() * .5;
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
setPos(PointF());
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// doAutoplace
|
|
//
|
|
// Move Dynamic up or down to avoid collisions with other elements.
|
|
//-------------------------------------------------------------------
|
|
|
|
void Dynamic::doAutoplace()
|
|
{
|
|
Segment* s = segment();
|
|
if (!(s && autoplace())) {
|
|
return;
|
|
}
|
|
|
|
qreal minDistance = score()->styleS(Sid::dynamicsMinDistance).val() * spatium();
|
|
RectF r = bbox().translated(pos() + s->pos() + s->measure()->pos());
|
|
qreal yOff = offset().y() - propertyDefault(Pid::OFFSET).value<PointF>().y();
|
|
r.translate(0.0, -yOff);
|
|
|
|
Skyline& sl = s->measure()->system()->staff(staffIdx())->skyline();
|
|
SkylineLine sk(!placeAbove());
|
|
sk.add(r);
|
|
|
|
if (placeAbove()) {
|
|
qreal d = sk.minDistance(sl.north());
|
|
if (d > -minDistance) {
|
|
rypos() += -(d + minDistance);
|
|
}
|
|
} else {
|
|
qreal d = sl.south().minDistance(sk);
|
|
if (d > -minDistance) {
|
|
rypos() += d + minDistance;
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// setDynamicType
|
|
//---------------------------------------------------------
|
|
|
|
void Dynamic::setDynamicType(const QString& tag)
|
|
{
|
|
int n = sizeof(dynList) / sizeof(*dynList);
|
|
for (int i = 0; i < n; ++i) {
|
|
if (dynList[i].tag == tag || dynList[i].text == tag) {
|
|
setDynamicType(DynamicType(i));
|
|
setXmlText(QString::fromUtf8(dynList[i].text));
|
|
return;
|
|
}
|
|
}
|
|
qDebug("setDynamicType: other <%s>", qPrintable(tag));
|
|
setDynamicType(DynamicType::OTHER);
|
|
setXmlText(tag);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// dynamicTypeName
|
|
//---------------------------------------------------------
|
|
|
|
QString Dynamic::dynamicTypeName(DynamicType type)
|
|
{
|
|
return dynList[int(type)].tag;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// startEdit
|
|
//---------------------------------------------------------
|
|
|
|
void Dynamic::startEdit(EditData& ed)
|
|
{
|
|
TextBase::startEdit(ed);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// endEdit
|
|
//---------------------------------------------------------
|
|
|
|
void Dynamic::endEdit(EditData& ed)
|
|
{
|
|
TextBase::endEdit(ed);
|
|
if (xmlText() != QString::fromUtf8(dynList[int(_dynamicType)].text)) {
|
|
_dynamicType = DynamicType::OTHER;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// reset
|
|
//---------------------------------------------------------
|
|
|
|
void Dynamic::reset()
|
|
{
|
|
TextBase::reset();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// getDragGroup
|
|
//---------------------------------------------------------
|
|
|
|
std::unique_ptr<ElementGroup> Dynamic::getDragGroup(std::function<bool(const EngravingItem*)> isDragged)
|
|
{
|
|
if (auto g = HairpinWithDynamicsDragGroup::detectFor(this, isDragged)) {
|
|
return g;
|
|
}
|
|
if (auto g = DynamicNearHairpinsDragGroup::detectFor(this, isDragged)) {
|
|
return g;
|
|
}
|
|
return TextBase::getDragGroup(isDragged);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// drag
|
|
//---------------------------------------------------------
|
|
|
|
mu::RectF Dynamic::drag(EditData& ed)
|
|
{
|
|
RectF f = EngravingItem::drag(ed);
|
|
|
|
//
|
|
// move anchor
|
|
//
|
|
Qt::KeyboardModifiers km = ed.modifiers;
|
|
if (km != (Qt::ShiftModifier | Qt::ControlModifier)) {
|
|
int si = staffIdx();
|
|
Segment* seg = segment();
|
|
score()->dragPosition(canvasPos(), &si, &seg);
|
|
if (seg != segment() || staffIdx() != si) {
|
|
const PointF oldOffset = offset();
|
|
PointF pos1(canvasPos());
|
|
score()->undo(new ChangeParent(this, seg, si));
|
|
setOffset(PointF());
|
|
layout();
|
|
PointF pos2(canvasPos());
|
|
const PointF newOffset = pos1 - pos2;
|
|
setOffset(newOffset);
|
|
ElementEditData* eed = ed.getData(this);
|
|
eed->initOffset += newOffset - oldOffset;
|
|
}
|
|
}
|
|
return f;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// undoSetDynRange
|
|
//---------------------------------------------------------
|
|
|
|
void Dynamic::undoSetDynRange(Range v)
|
|
{
|
|
undoChangeProperty(Pid::DYNAMIC_RANGE, int(v));
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// speedToName
|
|
//---------------------------------------------------------
|
|
|
|
QString Dynamic::speedToName(Speed speed)
|
|
{
|
|
for (auto i : Dynamic::changeSpeedTable) {
|
|
if (i.speed == speed) {
|
|
return i.name;
|
|
}
|
|
}
|
|
qFatal("Unrecognised change speed!");
|
|
return "none"; // silence a compiler warning
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// nameToSpeed
|
|
//---------------------------------------------------------
|
|
|
|
Dynamic::Speed Dynamic::nameToSpeed(QString name)
|
|
{
|
|
for (auto i : Dynamic::changeSpeedTable) {
|
|
if (i.name == name) {
|
|
return i.speed;
|
|
}
|
|
}
|
|
return Speed::NORMAL; // default
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// getProperty
|
|
//---------------------------------------------------------
|
|
|
|
PropertyValue Dynamic::getProperty(Pid propertyId) const
|
|
{
|
|
switch (propertyId) {
|
|
case Pid::DYNAMIC_TYPE:
|
|
return PropertyValue::fromValue(_dynamicType);
|
|
case Pid::DYNAMIC_RANGE:
|
|
return int(_dynRange);
|
|
case Pid::VELOCITY:
|
|
return velocity();
|
|
case Pid::SUBTYPE:
|
|
return int(_dynamicType);
|
|
case Pid::VELO_CHANGE:
|
|
if (isVelocityChangeAvailable()) {
|
|
return changeInVelocity();
|
|
} else {
|
|
return PropertyValue();
|
|
}
|
|
case Pid::VELO_CHANGE_SPEED:
|
|
return int(_velChangeSpeed);
|
|
default:
|
|
return TextBase::getProperty(propertyId);
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// setProperty
|
|
//---------------------------------------------------------
|
|
|
|
bool Dynamic::setProperty(Pid propertyId, const PropertyValue& v)
|
|
{
|
|
switch (propertyId) {
|
|
case Pid::DYNAMIC_TYPE:
|
|
_dynamicType = v.value<DynamicType>();
|
|
break;
|
|
case Pid::DYNAMIC_RANGE:
|
|
_dynRange = Range(v.toInt());
|
|
break;
|
|
case Pid::VELOCITY:
|
|
_velocity = v.toInt();
|
|
break;
|
|
case Pid::SUBTYPE:
|
|
_dynamicType = DynamicType(v.toInt());
|
|
break;
|
|
case Pid::VELO_CHANGE:
|
|
if (isVelocityChangeAvailable()) {
|
|
setChangeInVelocity(v.toInt());
|
|
}
|
|
break;
|
|
case Pid::VELO_CHANGE_SPEED:
|
|
_velChangeSpeed = Speed(v.toInt());
|
|
break;
|
|
default:
|
|
if (!TextBase::setProperty(propertyId, v)) {
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
triggerLayout();
|
|
return true;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// propertyDefault
|
|
//---------------------------------------------------------
|
|
|
|
PropertyValue Dynamic::propertyDefault(Pid id) const
|
|
{
|
|
switch (id) {
|
|
case Pid::SUB_STYLE:
|
|
return int(Tid::DYNAMICS);
|
|
case Pid::DYNAMIC_RANGE:
|
|
return int(Range::PART);
|
|
case Pid::VELOCITY:
|
|
return -1;
|
|
case Pid::VELO_CHANGE:
|
|
if (isVelocityChangeAvailable()) {
|
|
return dynList[int(dynamicType())].changeInVelocity;
|
|
} else {
|
|
return PropertyValue();
|
|
}
|
|
case Pid::VELO_CHANGE_SPEED:
|
|
return int(Speed::NORMAL);
|
|
default:
|
|
return TextBase::propertyDefault(id);
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// propertyId
|
|
//---------------------------------------------------------
|
|
|
|
Pid Dynamic::propertyId(const QStringRef& name) const
|
|
{
|
|
if (name == propertyName(Pid::DYNAMIC_TYPE)) {
|
|
return Pid::DYNAMIC_TYPE;
|
|
}
|
|
return TextBase::propertyId(name);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// propertyUserValue
|
|
//---------------------------------------------------------
|
|
|
|
QString Dynamic::propertyUserValue(Pid pid) const
|
|
{
|
|
switch (pid) {
|
|
case Pid::DYNAMIC_TYPE:
|
|
return dynamicTypeName();
|
|
default:
|
|
break;
|
|
}
|
|
return TextBase::propertyUserValue(pid);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// accessibleInfo
|
|
//---------------------------------------------------------
|
|
|
|
QString Dynamic::accessibleInfo() const
|
|
{
|
|
QString s;
|
|
|
|
if (dynamicType() == DynamicType::OTHER) {
|
|
s = plainText().simplified();
|
|
if (s.length() > 20) {
|
|
s.truncate(20);
|
|
s += "…";
|
|
}
|
|
} else {
|
|
s = dynamicTypeName();
|
|
}
|
|
return QString("%1: %2").arg(EngravingItem::accessibleInfo(), s);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// screenReaderInfo
|
|
//---------------------------------------------------------
|
|
|
|
QString Dynamic::screenReaderInfo() const
|
|
{
|
|
QString s;
|
|
|
|
if (dynamicType() == DynamicType::OTHER) {
|
|
s = plainText().simplified();
|
|
} else {
|
|
s = dynamicTypeName();
|
|
}
|
|
return QString("%1: %2").arg(EngravingItem::accessibleInfo(), s);
|
|
}
|
|
}
|