diff --git a/src/engraving/libmscore/property.cpp b/src/engraving/libmscore/property.cpp
index c968f4c085540..01f3ba78231bd 100644
--- a/src/engraving/libmscore/property.cpp
+++ b/src/engraving/libmscore/property.cpp
@@ -153,6 +153,8 @@ static constexpr PropertyMetaData propertyList[] = {
{ Pid::SPACE, false, "space", P_TYPE::SP_REAL, DUMMY_QT_TR_NOOP("propertyName", "space") },
{ Pid::TEMPO, true, "tempo", P_TYPE::TEMPO, DUMMY_QT_TR_NOOP("propertyName", "tempo") },
{ Pid::TEMPO_FOLLOW_TEXT, true, "followText", P_TYPE::BOOL, DUMMY_QT_TR_NOOP("propertyName", "following text") },
+ { Pid::TEMPO_EQUATION, true, "tempoEquation", P_TYPE::STRING, DUMMY_QT_TR_NOOP("propertyName", "tempo equation") },
+ { Pid::TEMPO_EQUATION_VISIBLE, true, "tempoEquationVisible", P_TYPE::BOOL, DUMMY_QT_TR_NOOP("propertyName", "tempo is equation visible") },
{ Pid::ACCIDENTAL_BRACKET, false, "bracket", P_TYPE::INT, DUMMY_QT_TR_NOOP("propertyName", "bracket") },
{ Pid::ACCIDENTAL_TYPE, true, "subtype", P_TYPE::INT, DUMMY_QT_TR_NOOP("propertyName", "type") },
{ Pid::NUMERATOR_STRING, false, "textN", P_TYPE::STRING, DUMMY_QT_TR_NOOP("propertyName", "numerator string") },
diff --git a/src/engraving/libmscore/property.h b/src/engraving/libmscore/property.h
index 6b514082b6b84..67be893743b8c 100644
--- a/src/engraving/libmscore/property.h
+++ b/src/engraving/libmscore/property.h
@@ -156,6 +156,8 @@ enum class Pid {
SPACE, // used for spacer
TEMPO,
TEMPO_FOLLOW_TEXT,
+ TEMPO_EQUATION,
+ TEMPO_EQUATION_VISIBLE,
ACCIDENTAL_BRACKET,
ACCIDENTAL_TYPE,
NUMERATOR_STRING,
diff --git a/src/engraving/libmscore/tempotext.cpp b/src/engraving/libmscore/tempotext.cpp
index 92a51b33153e9..e79783677a685 100644
--- a/src/engraving/libmscore/tempotext.cpp
+++ b/src/engraving/libmscore/tempotext.cpp
@@ -33,6 +33,7 @@
#include "tempotext.h"
#include "undo.h"
#include "xml.h"
+#include "textedit.h"
using namespace mu;
@@ -60,10 +61,13 @@ TempoText::TempoText(Score* s)
: TextBase(s, Tid::TEMPO, ElementFlags(ElementFlag::SYSTEM))
{
initElementStyle(&tempoStyle);
- _tempo = 2.0; // propertyDefault(P_TEMPO).toDouble();
- _followText = false;
- _relative = 1.0;
+ _playbackTempo = 2.0; // propertyDefault(P_TEMPO).toDouble();
+ _notatedTempo = 120;
+ _followText = true;
+ _relative = 1.0;
_isRelative = false;
+ _equation = "q = 120";
+ _isEquationVisible = true;
}
//---------------------------------------------------------
@@ -73,7 +77,10 @@ TempoText::TempoText(Score* s)
void TempoText::write(XmlWriter& xml) const
{
xml.stag(this);
- xml.tag("tempo", _tempo);
+ xml.tag("playbackTempo", _playbackTempo);
+ xml.tag("notatedTempo", _notatedTempo);
+ xml.tag("equation", _equation);
+ xml.tag("equationVisible", _isEquationVisible);
if (_followText) {
xml.tag("followText", _followText);
}
@@ -89,77 +96,85 @@ void TempoText::read(XmlReader& e)
{
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
- if (tag == "tempo") {
+ if (tag == "playbackTempo") {
setTempo(e.readDouble());
} else if (tag == "followText") {
_followText = e.readInt();
+ } else if (tag == "equation") {
+ _equation = e.readElementText();
+ } else if (tag == "notatedTempo") {
+ _notatedTempo = e.readInt();
+ } else if (tag == "equationVisible") {
+ _isEquationVisible = e.readBool();
} else if (!TextBase::readProperties(e)) {
e.unknown();
}
}
// check sanity
if (xmlText().isEmpty()) {
- setXmlText(QString("metNoteQuarterUp = %1").arg(lrint(60 * _tempo)));
+ _equation = QString("q = %1").arg(lrint(60 * _playbackTempo));
setVisible(false);
}
}
qreal TempoText::tempoBpm() const
{
- //! NOTE: find tempo in format " = 180"
- QRegularExpression regex("\\s*=\\s*(\\d+[.]{0,1}\\d*)");
- QStringList matches = regex.match(xmlText()).capturedTexts();
-
- if (matches.empty() || matches.size() < 1) {
- return 0;
- }
-
- qreal tempo = matches[1].toDouble();
- return tempo;
+ return _notatedTempo;
}
//---------------------------------------------------------
// TempoPattern
//---------------------------------------------------------
-struct TempoPattern {
- const char* pattern;
- qreal f;
- TDuration d;
- TempoPattern(const char* s, qreal v, TDuration::DurationType val, int dots = 0)
- : pattern(s), f(v), d(val)
+struct EquationDescrption
+{
+ QString letter;
+
+ QString unicode;
+ QString symbol;
+
+ float relative;
+ TDuration::DurationType duration;
+
+ EquationDescrption(QString l, float r, QString u, QString s, TDuration::DurationType val)
+ : letter(l), unicode(u), symbol(s), relative(r), duration(val)
{
- d.setDots(dots);
}
};
-// note: findTempoDuration requires the longer patterns to be before the shorter patterns in tp
-
-static const TempoPattern tp[] = {
- TempoPattern("\uECA5\\s*\uECB7\\s*\uECB7", 1.75 / 60.0, TDuration::DurationType::V_QUARTER, 2), // double dotted 1/4
- TempoPattern("\uECA5\\s*\uECB7", 1.5 / 60.0, TDuration::DurationType::V_QUARTER, 1), // dotted 1/4
- TempoPattern("\uECA5", 1.0 / 60.0, TDuration::DurationType::V_QUARTER), // 1/4
- TempoPattern("\uECA3\\s*\uECB7\\s*\uECB7", 1.75 / 30.0, TDuration::DurationType::V_HALF, 2), // double dotted 1/2
- TempoPattern("\uECA3\\s*\uECB7", 1.5 / 30.0, TDuration::DurationType::V_HALF, 1), // dotted 1/2
- TempoPattern("\uECA3", 1.0 / 30.0, TDuration::DurationType::V_HALF), // 1/2
- TempoPattern("\uECA7\\s*\uECB7\\s*\uECB7", 1.75 / 120.0, TDuration::DurationType::V_EIGHTH, 2), // double dotted 1/8
- TempoPattern("\uECA7\\s*\uECB7", 1.5 / 120.0, TDuration::DurationType::V_EIGHTH, 1), // dotted 1/8
- TempoPattern("\uECA7", 1.0 / 120.0, TDuration::DurationType::V_EIGHTH), // 1/8
- TempoPattern("\uECA2\\s*\uECB7", 1.5 / 15.0, TDuration::DurationType::V_WHOLE, 1), // dotted whole
- TempoPattern("\uECA2", 1.0 / 15.0, TDuration::DurationType::V_WHOLE), // whole
- TempoPattern("\uECA9\\s*\uECB7", 1.5 / 240.0, TDuration::DurationType::V_16TH, 1), // dotted 1/16
- TempoPattern("\uECA9", 1.0 / 240.0, TDuration::DurationType::V_16TH), // 1/16
- TempoPattern("\uECAB\\s*\uECB7", 1.5 / 480.0, TDuration::DurationType::V_32ND, 1), // dotted 1/32
- TempoPattern("\uECAB", 1.0 / 480.0, TDuration::DurationType::V_32ND), // 1/32
- TempoPattern("\uECA1", 1.0 / 7.5, TDuration::DurationType::V_BREVE), // longa
- TempoPattern("\uECA0", 1.0 / 7.5, TDuration::DurationType::V_BREVE), // double whole
- TempoPattern("\uECAD", 1.0 / 960.0, TDuration::DurationType::V_64TH), // 1/64
- TempoPattern("\uECAF", 1.0 / 1920.0, TDuration::DurationType::V_128TH), // 1/128
- TempoPattern("\uECB1", 1.0 / 3840.0, TDuration::DurationType::V_256TH), // 1/256
- TempoPattern("\uECB3", 1.0 / 7680.0, TDuration::DurationType::V_512TH), // 1/512
- TempoPattern("\uECB5", 1.0 / 15360.0, TDuration::DurationType::V_1024TH), // 1/1024
+static const EquationDescrption durationMap[] = {
+ { "d", 8.0f, "\uECA0", "metNoteDoubleWhole", TDuration::DurationType::V_BREVE },
+ { "w", 4.0f, "\uECA2", "metNoteWhole", TDuration::DurationType::V_WHOLE },
+ { "h", 2.0f, "\uECA3", "metNoteHalfUp", TDuration::DurationType::V_HALF },
+ { "q", 1.0f, "\uECA5", "metNoteQuarterUp", TDuration::DurationType::V_QUARTER },
+ { "e", 0.5f, "\uECA7", "metNote8thUp", TDuration::DurationType::V_EIGHTH },
+ { "s", 0.25f, "\uECA9", "metNote16thUp", TDuration::DurationType::V_16TH },
+ { "t", 0.125f, "\uECAB", "metNote32ndUp", TDuration::DurationType::V_32ND },
+ { ".", 0, "\uECB7", "metAugmentationDot", TDuration::DurationType::V_32ND },
};
+QString TempoText::regexGroup(bool symbol)
+{
+ static QString unicodeString;
+ static QString symbolString;
+
+ if (symbol && !symbolString.isEmpty()) {
+ return symbolString;
+ } else if (!symbol && !unicodeString.isEmpty()) {
+ return unicodeString;
+ }
+
+ for (auto pattern : durationMap) {
+ unicodeString += pattern.unicode;
+ symbolString += QString(pattern.symbol).replace("/", "\\/") + "|";
+ }
+
+ unicodeString = "[" + unicodeString + "]";
+ symbolString = "(" + symbolString.left(symbolString.size() - 1) + ")";
+
+ return symbol ? symbolString : unicodeString;
+}
+
//---------------------------------------------------------
// findTempoDuration
// find the duration part (note + dot) of a tempo text in string s
@@ -169,59 +184,41 @@ static const TempoPattern tp[] = {
int TempoText::findTempoDuration(const QString& s, int& len, TDuration& dur)
{
- len = 0;
- dur = TDuration();
- for (const auto& i : tp) {
- QRegularExpression regex(i.pattern);
- QRegularExpressionMatch match = regex.match(s);
- if (match.hasMatch()) {
- len = match.capturedLength();
- dur = i.d;
- return match.capturedStart();
- }
+ static const QRegularExpression tempoExpression(QString("(?%1+)").arg(TempoText::regexGroup(false)));
+ QRegularExpressionMatch match = tempoExpression.match(s);
+ if (match.hasMatch()) {
+ len = match.capturedLength();
+ dur = TempoText::findTempoDuration(match.captured("pattern"));
+ return match.capturedStart();
}
+
return -1;
}
+TDuration TempoText::findTempoDuration(const QString& s)
+{
+ static const QRegularExpression tempoExpression(QString("(?%1)(?\uECB7*)").arg(TempoText::regexGroup(false)));
+ QRegularExpressionMatch match = tempoExpression.match(s);
+
+ if (!match.hasMatch()) {
+ return TDuration(TDuration::DurationType::V_INVALID);
+ }
+
+ for (auto pattern : durationMap) {
+ if (pattern.unicode == match.captured("equation")) {
+ TDuration duration(pattern.duration);
+ duration.setDots(match.captured("dots").size());
+ return duration;
+ }
+ }
+
+ return TDuration(TDuration::DurationType::V_INVALID);
+}
+
TDuration TempoText::duration() const
{
- int dummy = 0;
- TDuration result;
-
- findTempoDuration(xmlText(), dummy, result);
-
- return result;
-}
-
-static const TempoPattern tpSym[] = {
- TempoPattern("metNoteQuarterUp\\s*metAugmentationDot\\s*metAugmentationDot",
- 1.75 / 60.0, TDuration::DurationType::V_QUARTER, 2), // double dotted 1/4
- TempoPattern("metNoteQuarterUp\\s*metAugmentationDot", 1.5 / 60.0, TDuration::DurationType::V_QUARTER,
- 1), // dotted 1/4
- TempoPattern("metNoteQuarterUp", 1.0 / 60.0, TDuration::DurationType::V_QUARTER), // 1/4
- TempoPattern("metNoteHalfUp\\s*metAugmentationDot\\s*metAugmentationDot",
- 1.75 / 30.0, TDuration::DurationType::V_HALF, 2), // double dotted 1/2
- TempoPattern("metNoteHalfUp\\s*metAugmentationDot", 1.5 / 30.0, TDuration::DurationType::V_HALF, 1), // dotted 1/2
- TempoPattern("metNoteHalfUp", 1.0 / 30.0, TDuration::DurationType::V_HALF), // 1/2
- TempoPattern("metNote8thUp\\s*metAugmentationDot\\s*metAugmentationDot", 1.75 / 120.0,
- TDuration::DurationType::V_EIGHTH, 2), // double dotted 1/8
- TempoPattern("metNote8thUp\\s*metAugmentationDot", 1.5 / 120.0, TDuration::DurationType::V_EIGHTH,
- 1), // dotted 1/8
- TempoPattern("metNote8thUp", 1.0 / 120.0, TDuration::DurationType::V_EIGHTH), // 1/8
- TempoPattern("metNoteWhole\\s*metAugmentationDot", 1.5 / 15.0, TDuration::DurationType::V_WHOLE, 1), // dotted whole
- TempoPattern("metNoteWhole", 1.0 / 15.0, TDuration::DurationType::V_WHOLE), // whole
- TempoPattern("metNote16thUp\\s*metAugmentationDot", 1.5 / 240.0, TDuration::DurationType::V_16TH, 1), // dotted 1/16
- TempoPattern("metNote16thUp", 1.0 / 240.0, TDuration::DurationType::V_16TH), // 1/16
- TempoPattern("metNote32ndUp\\s*metAugmentationDot", 1.5 / 480.0, TDuration::DurationType::V_32ND, 1), // dotted 1/32
- TempoPattern("metNote32ndUp", 1.0 / 480.0, TDuration::DurationType::V_32ND), // 1/32
- TempoPattern("metNoteDoubleWholeSquare", 1.0 / 7.5, TDuration::DurationType::V_BREVE), // longa
- TempoPattern("metNoteDoubleWhole", 1.0 / 7.5, TDuration::DurationType::V_BREVE), // double whole
- TempoPattern("metNote64thUp", 1.0 / 960.0, TDuration::DurationType::V_64TH), // 1/64
- TempoPattern("metNote128thUp", 1.0 / 1920.0, TDuration::DurationType::V_128TH), // 1/128
- TempoPattern("metNote256thUp", 1.0 / 3840.0, TDuration::DurationType::V_256TH), // 1/256
- TempoPattern("metNote512thUp", 1.0 / 7680.0, TDuration::DurationType::V_512TH), // 1/512
- TempoPattern("metNote1024thUp", 1.0 / 15360.0, TDuration::DurationType::V_1024TH), // 1/1024
-};
+ return findTempoDuration(TempoText::mapEquationToText(_equation, false).split("=")[0]);
+}
//---------------------------------------------------------
// duration2tempoTextString
@@ -230,14 +227,20 @@ static const TempoPattern tpSym[] = {
QString TempoText::duration2tempoTextString(const TDuration dur)
{
- for (const TempoPattern& pa : tpSym) {
- if (pa.d == dur) {
- QString res = pa.pattern;
- res.replace("\\s*", " ");
- return res;
+ QString tempoString = "q";
+
+ for (auto pattern : durationMap) {
+ if (pattern.duration == dur.type()) {
+ tempoString = pattern.letter;
+ break;
}
}
- return "";
+
+ for (int i = 0; i < dur.dots(); i++) {
+ tempoString += ".";
+ }
+
+ return tempoString;
}
//---------------------------------------------------------
@@ -247,7 +250,7 @@ QString TempoText::duration2tempoTextString(const TDuration dur)
void TempoText::updateScore()
{
if (segment()) {
- score()->setTempo(segment(), _tempo);
+ score()->setTempo(segment(), _playbackTempo);
}
score()->fixTicks();
score()->setPlaylistDirty();
@@ -263,6 +266,21 @@ void TempoText::updateRelative()
setTempo(tempoBefore * _relative);
}
+void TempoText::startEdit(EditData& ed)
+{
+ TextBase::startEdit(ed);
+
+ TextEditData* ted = static_cast(ed.getData(this));
+ TextCursor* cursor = ted->cursor();
+
+ int cursorIndex = textIndexFromCursor(cursor->row(), cursor->column());
+ std::pair eqnIndices = equationIndices();
+
+ if (cursorIndex >= eqnIndices.first && cursorIndex <= eqnIndices.second) {
+ cursor->movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, cursorIndex - eqnIndices.first);
+ }
+}
+
//---------------------------------------------------------
// endEdit
// text may have changed
@@ -286,6 +304,106 @@ void TempoText::endEdit(EditData& ed)
}
}
+int TempoText::textIndexFromCursor(int row, int column) const
+{
+ int index = 0;
+ for (int i = 0; i < row; i++) {
+ index = xmlText().indexOf("\n", index);
+ }
+
+ index += column;
+
+ return index;
+}
+
+std::pair TempoText::cursorIndexFromTextIndex(int index) const
+{
+ int row = 0;
+ int lineIndex = plainText().indexOf("\n");
+
+ while (lineIndex < index && row < xmlText().count("\n")) {
+ lineIndex = plainText().indexOf("\n", lineIndex);
+ row++;
+ }
+
+ int column = index - lineIndex - 1;
+
+ return std::make_pair(row, column);
+}
+
+std::pair TempoText::equationIndices() const
+{
+ static const QRegularExpression equationExpression(QString("(?[\\[\\(]?%1+ *= *(%1+|[0-9]+)[\\]\\)]?)").arg(TempoText::
+ regexGroup(false)));
+ QRegularExpressionMatch match = equationExpression.match(plainText());
+
+ if (!match.hasMatch()) {
+ return std::make_pair(0, 0);
+ }
+
+ return std::make_pair(match.capturedStart("equation"), match.capturedEnd("equation"));
+}
+
+bool TempoText::moveCursor(TextCursor* cursor, int key, bool ctrlPressed, QTextCursor::MoveMode moveMode) const
+{
+ int cursorIndex = textIndexFromCursor(cursor->row(), cursor->column());
+
+ std::pair eqnIndices = equationIndices();
+
+ if (eqnIndices.first == 0 && eqnIndices.second == 0) {
+ return TextBase::moveCursor(cursor, key, ctrlPressed, moveMode);
+ }
+
+ int length = eqnIndices.second - eqnIndices.first;
+
+ if (eqnIndices.first == cursorIndex && key == Qt::Key_Right) {
+ return cursor->movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, length);
+ } else if (eqnIndices.second == cursorIndex && key == Qt::Key_Left) {
+ return cursor->movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, length);
+ } else {
+ if (key == Qt::Key_Left) {
+ return cursor->movePosition(ctrlPressed ? QTextCursor::WordLeft : QTextCursor::Left, moveMode);
+ } else if (key == Qt::Key_Right) {
+ return cursor->movePosition(ctrlPressed ? QTextCursor::WordRight : QTextCursor::Right, moveMode);
+ }
+ }
+
+ return false;
+}
+
+void TempoText::dragTo(EditData& ed)
+{
+ TextEditData* ted = static_cast(ed.getData(this));
+ TextCursor* cursor = ted->cursor();
+
+ cursor->set(ed.pos, QTextCursor::KeepAnchor);
+
+ int cursorEndIndex = textIndexFromCursor(cursor->row(), cursor->column());
+ int cursorStartIndex = textIndexFromCursor(cursor->selectLine(), cursor->selectColumn());
+
+ std::pair eqnIndices = equationIndices();
+
+ if (!(eqnIndices.first == 0 && eqnIndices.second == 0)) {
+ int equationStart = eqnIndices.first;
+ int equationEnd = eqnIndices.second;
+
+ if (cursorStartIndex <= equationStart && cursorEndIndex > equationStart) {
+ std::pair indices = cursorIndexFromTextIndex(equationStart);
+
+ cursor->setRow(indices.first);
+ cursor->setColumn(indices.second);
+ } else if (cursorStartIndex >= equationEnd && cursorEndIndex < equationEnd) {
+ std::pair indices = cursorIndexFromTextIndex(equationEnd);
+
+ cursor->setRow(indices.first);
+ cursor->setColumn(indices.second);
+ }
+ }
+
+ score()->setUpdateAll();
+ score()->update();
+}
+
//---------------------------------------------------------
// undoChangeProperty
//---------------------------------------------------------
@@ -308,51 +426,41 @@ void TempoText::undoChangeProperty(Pid id, const QVariant& v, PropertyFlags ps)
void TempoText::updateTempo()
{
- // cache regexp, they are costly to create
- static QHash regexps;
- static QHash regexps2;
- QString s = plainText();
- s.replace(",", ".");
- s.replace("space", " ");
- for (const TempoPattern& pa : tp) {
- QRegularExpression re;
- if (!regexps.contains(pa.pattern)) {
- re = QRegularExpression(QString("%1\\s*=\\s*(\\d+[.]{0,1}\\d*)\\s*").arg(pa.pattern));
- regexps[pa.pattern] = re;
- }
- re = regexps.value(pa.pattern);
- QRegularExpressionMatch match = re.match(s);
- if (match.hasMatch()) {
- QStringList sl = match.capturedTexts();
- if (sl.size() == 2) {
- qreal nt = qreal(sl[1].toDouble()) * pa.f;
- if (nt != _tempo) {
- undoChangeProperty(Pid::TEMPO, QVariant(qreal(sl[1].toDouble()) * pa.f), propertyFlags(Pid::TEMPO));
- _relative = 1.0;
- _isRelative = false;
- updateScore();
- }
- break;
- }
- } else {
- for (const TempoPattern& pa2 : tp) {
- QString key = QString("%1_%2").arg(pa.pattern, pa2.pattern);
- QRegularExpression re2;
- if (!regexps2.contains(key)) {
- re2 = QRegularExpression(QString("%1\\s*=\\s*%2\\s*").arg(pa.pattern, pa2.pattern));
- regexps2[key] = re2;
- }
- re2 = regexps2.value(key);
- QRegularExpressionMatch match2 = re2.match(s);
- if (match2.hasMatch()) {
- _relative = pa2.f / pa.f;
- _isRelative = true;
- updateRelative();
- updateScore();
- return;
- }
+ static const float quarterNotePlayback = 1.f / 60.f;
+
+ static const QRegularExpression bpmExpression("^[\\[,\\(]?[ ]*(?[dwhqest].*) *= *(?\\d+)[\\],\\)]?$");
+ static const QRegularExpression relativeExpression("^[\\[,\\(]?[ ]*(?[dwhqest].*) *= *(?[dwhqest].*)[\\],\\)]?$");
+
+ QRegularExpressionMatch bpmMatch = bpmExpression.match(_equation);
+ QRegularExpressionMatch relativeMatch = relativeExpression.match(_equation);
+
+ if (bpmMatch.hasMatch()) {
+ const float relativeDuration = TempoText::getRelativeDuration(bpmMatch.captured("note"));
+ const float bpm = bpmMatch.captured("bpm").toFloat();
+
+ _notatedTempo = bpm;
+
+ qreal playbackTempo(relativeDuration * bpm * quarterNotePlayback);
+
+ if (playbackTempo != _playbackTempo) {
+ if (segment()) {
+ undoChangeProperty(Pid::TEMPO, QVariant(playbackTempo), propertyFlags(Pid::TEMPO));
}
+
+ _playbackTempo = playbackTempo;
+
+ _relative = 1.0;
+ _isRelative = false;
+
+ updateScore();
}
+ } else if (relativeMatch.hasMatch()) {
+ _relative = TempoText::getRelativeDuration(relativeMatch.captured("note2"))
+ / TempoText::getRelativeDuration(relativeMatch.captured("note1"));
+ _isRelative = true;
+
+ updateRelative();
+ updateScore();
}
}
@@ -367,7 +475,13 @@ void TempoText::setTempo(qreal v)
} else if (v > MAX_TEMPO) {
v = MAX_TEMPO;
}
- _tempo = v;
+ _playbackTempo = v;
+}
+
+void TempoText::setEquationFromTempo(int tempo)
+{
+ _equation = QString("q = %1").arg(tempo);
+ parseEquation();
}
//---------------------------------------------------------
@@ -396,9 +510,13 @@ QVariant TempoText::getProperty(Pid propertyId) const
{
switch (propertyId) {
case Pid::TEMPO:
- return _tempo;
+ return _playbackTempo;
case Pid::TEMPO_FOLLOW_TEXT:
return _followText;
+ case Pid::TEMPO_EQUATION:
+ return _equation;
+ case Pid::TEMPO_EQUATION_VISIBLE:
+ return _isEquationVisible;
default:
return TextBase::getProperty(propertyId);
}
@@ -413,12 +531,22 @@ bool TempoText::setProperty(Pid propertyId, const QVariant& v)
switch (propertyId) {
case Pid::TEMPO:
setTempo(v.toDouble());
- score()->setTempo(segment(), _tempo);
+ score()->setTempo(segment(), _playbackTempo);
score()->fixTicks();
break;
case Pid::TEMPO_FOLLOW_TEXT:
_followText = v.toBool();
break;
+ case Pid::TEMPO_EQUATION:
+ if (isEquationValid(v.toString())) {
+ _equation = v.toString();
+ parseEquation();
+ }
+ break;
+ case Pid::TEMPO_EQUATION_VISIBLE:
+ _isEquationVisible = v.toBool();
+ parseEquation();
+ break;
default:
if (!TextBase::setProperty(propertyId, v)) {
return false;
@@ -442,6 +570,10 @@ QVariant TempoText::propertyDefault(Pid id) const
return 2.0;
case Pid::TEMPO_FOLLOW_TEXT:
return false;
+ case Pid::TEMPO_EQUATION:
+ return "q = 120";
+ case Pid::TEMPO_EQUATION_VISIBLE:
+ return true;
default:
return TextBase::propertyDefault(id);
}
@@ -476,6 +608,94 @@ void TempoText::layout()
autoplaceSegmentElement();
}
+//---------------------------------------------------------
+// parseEquation
+//---------------------------------------------------------
+
+void TempoText::parseEquation()
+{
+ static const QString equationBase = "[\\[\\(]?%1+ *= *(%1+|\\d+)[\\]\\)]?";
+ static const QRegularExpression equationExpression(equationBase.arg(TempoText::regexGroup()));
+ static const QRegularExpression unicodeEquationExpression(equationBase.arg(TempoText::regexGroup(false)));
+
+ if (_isEquationVisible) {
+ QString equation = TempoText::mapEquationToText(_equation);
+
+ if (equationExpression.match(xmlText()).hasMatch()) {
+ setXmlText(xmlText().replace(equationExpression, equation));
+ } else if (unicodeEquationExpression.match(plainText()).hasMatch()) {
+ auto split = plainText().split(unicodeEquationExpression);
+ setXmlText(split[0] + equation + split[1]);
+ } else {
+ setXmlText(xmlText() + equation);
+ }
+ } else {
+ setXmlText(xmlText().replace(equationExpression, ""));
+ }
+
+ if (_followText) {
+ updateTempo();
+ }
+}
+
+//---------------------------------------------------------
+// isEquationValid
+//---------------------------------------------------------
+
+bool TempoText::isEquationValid(const QString equation) const
+{
+ const QRegularExpression equationExpression("^[\\[,\\(]?[ ]*([dwhqest].*) *= *(\\d+|[dwhqest].*)[\\],\\)]?$");
+ QRegularExpressionMatch match = equationExpression.match(equation);
+
+ return match.hasMatch();
+}
+
+//---------------------------------------------------------
+// mapEquationToText
+//---------------------------------------------------------
+QString TempoText::mapEquationToText(const QString equation, bool symbol)
+{
+ QString mapped;
+
+ for (auto c : equation) {
+ bool added = false;
+
+ for (auto pattern : durationMap) {
+ if (pattern.letter == c) {
+ mapped += (symbol ? pattern.symbol : pattern.unicode);
+ added = true;
+ break;
+ }
+ }
+
+ if (!added) {
+ mapped += c;
+ }
+ }
+
+ return mapped;
+}
+
+//---------------------------------------------------------
+// getRelativeDuration
+//---------------------------------------------------------
+
+float TempoText::getRelativeDuration(const QString marking)
+{
+ float baseDuration = 1.0f;
+
+ for (auto duration : durationMap) {
+ if (marking[0] == duration.letter) {
+ baseDuration = duration.relative;
+ break;
+ }
+ }
+
+ const int dots = marking.count(".");
+
+ return baseDuration * std::pow(1.5, float(dots));
+}
+
//---------------------------------------------------------
// duration2userName
//---------------------------------------------------------
diff --git a/src/engraving/libmscore/tempotext.h b/src/engraving/libmscore/tempotext.h
index 6c29ef2e08460..48c04e94b06b3 100644
--- a/src/engraving/libmscore/tempotext.h
+++ b/src/engraving/libmscore/tempotext.h
@@ -37,16 +37,28 @@ namespace Ms {
class TempoText final : public TextBase
{
- qreal _tempo; // beats per second
+ qreal _playbackTempo; // beats per second
+ qreal _notatedTempo; // tempo user enters
bool _followText; // parse text to determine tempo
qreal _relative;
bool _isRelative;
+ QString _equation;
+ bool _isEquationVisible;
void updateScore();
void updateTempo();
+
+ void startEdit(EditData&) override;
void endEdit(EditData&) override;
+
void undoChangeProperty(Pid id, const QVariant&, PropertyFlags ps) override;
+ bool isEquationValid(const QString equation) const;
+
+ int textIndexFromCursor(int row, int column) const;
+ std::pair cursorIndexFromTextIndex(int index) const;
+ std::pair equationIndices() const;
+
public:
TempoText(Score*);
@@ -59,7 +71,7 @@ class TempoText final : public TextBase
Segment* segment() const { return toSegment(parent()); }
Measure* measure() const { return toMeasure(parent()->parent()); }
- qreal tempo() const { return _tempo; }
+ qreal tempo() const { return _playbackTempo; }
qreal tempoBpm() const;
void setTempo(qreal v);
void undoSetTempo(qreal v);
@@ -68,16 +80,32 @@ class TempoText final : public TextBase
bool followText() const { return _followText; }
void setFollowText(bool v) { _followText = v; }
+ QString equation() const { return _equation; }
+ void setEquation(QString equation) { _equation = equation; }
+ void setEquationVisible(bool equationVisible) { _isEquationVisible = equationVisible; }
void undoSetFollowText(bool v);
void updateRelative();
+ void setEquationFromTempo(int tempo);
+
+ void parseEquation();
+
+ bool moveCursor(TextCursor* cursor, int key, bool ctrlPressed, QTextCursor::MoveMode moveMode) const override;
+
+ void dragTo(EditData& ed) override;
+
void layout() override;
TDuration duration() const;
+ static QString regexGroup(bool symbol = true);
+
static int findTempoDuration(const QString& s, int& len, TDuration& dur);
+ static TDuration findTempoDuration(const QString& s);
static QString duration2tempoTextString(const TDuration dur);
static QString duration2userName(const TDuration t);
+ static float getRelativeDuration(const QString marking);
+ static QString mapEquationToText(const QString equation, bool symbol = true);
QVariant getProperty(Pid propertyId) const override;
bool setProperty(Pid propertyId, const QVariant&) override;
diff --git a/src/engraving/libmscore/textbase.cpp b/src/engraving/libmscore/textbase.cpp
index c0e4ecc2ab2b7..12904e7cbb3b3 100644
--- a/src/engraving/libmscore/textbase.cpp
+++ b/src/engraving/libmscore/textbase.cpp
@@ -3084,6 +3084,17 @@ Sid TextBase::offsetSid() const
return Sid::NOSTYLE;
}
+bool TextBase::moveCursor(TextCursor* cursor, int key, bool ctrlPressed, QTextCursor::MoveMode moveMode) const
+{
+ if (key == Qt::Key_Left) {
+ return cursor->movePosition(ctrlPressed ? QTextCursor::WordLeft : QTextCursor::Left, moveMode);
+ } else if (key == Qt::Key_Right) {
+ return cursor->movePosition(ctrlPressed ? QTextCursor::WordRight : QTextCursor::Right, moveMode);
+ } else {
+ return false;
+ }
+}
+
//---------------------------------------------------------
// getHtmlStartTag - helper function for extractText with withFormat = true
//---------------------------------------------------------
diff --git a/src/engraving/libmscore/textbase.h b/src/engraving/libmscore/textbase.h
index ae930c646ba61..9d7a3ad8e7858 100644
--- a/src/engraving/libmscore/textbase.h
+++ b/src/engraving/libmscore/textbase.h
@@ -319,6 +319,8 @@ class TextBase : public Element
void insertText(EditData&, const QString&);
+ virtual bool moveCursor(TextCursor* cursor, int key, bool ctrlPressed, QTextCursor::MoveMode moveMode) const;
+
virtual void layout() override;
virtual void layout1();
qreal lineSpacing() const;
@@ -364,7 +366,7 @@ class TextBase : public Element
mu::RectF pageRectangle() const;
- void dragTo(EditData&);
+ virtual void dragTo(EditData&);
QVector dragAnchorLines() const override;
diff --git a/src/engraving/libmscore/textedit.cpp b/src/engraving/libmscore/textedit.cpp
index bda4c75eef8f4..87cc8de5840d4 100644
--- a/src/engraving/libmscore/textedit.cpp
+++ b/src/engraving/libmscore/textedit.cpp
@@ -360,21 +360,14 @@ bool TextBase::edit(EditData& ed)
return true;
case Qt::Key_Left:
- if (!_cursor->movePosition(ctrlPressed ? QTextCursor::WordLeft : QTextCursor::Left,
- mm) && type() == ElementType::LYRICS) {
- return false;
- }
- s.clear();
- break;
-
case Qt::Key_Right:
- if (!_cursor->movePosition(ctrlPressed ? QTextCursor::NextWord : QTextCursor::Right,
- mm) && type() == ElementType::LYRICS) {
+ if (!toTextBase(ed.element)->moveCursor(_cursor, ed.key, ctrlPressed, mm) && type() == ElementType::LYRICS) {
return false;
}
+
s.clear();
- break;
+ break;
case Qt::Key_Up:
#if defined(Q_OS_MAC)
if (!cursor->movePosition(QTextCursor::Up, mm)) {
diff --git a/src/engraving/tests/barline_data/barlinedelete-ref.mscx b/src/engraving/tests/barline_data/barlinedelete-ref.mscx
index a8cd45430cf4e..468dda5ea913c 100644
--- a/src/engraving/tests/barline_data/barlinedelete-ref.mscx
+++ b/src/engraving/tests/barline_data/barlinedelete-ref.mscx
@@ -81,7 +81,10 @@
4
- 2
+ 2
+ 120
+ q = 120
+ 1
1
Tempo text
diff --git a/src/engraving/tests/barline_data/barlinedelete.mscx b/src/engraving/tests/barline_data/barlinedelete.mscx
index b4643493fa5ba..3d0e8505fdd9b 100644
--- a/src/engraving/tests/barline_data/barlinedelete.mscx
+++ b/src/engraving/tests/barline_data/barlinedelete.mscx
@@ -80,7 +80,10 @@
4
- 2
+ 2
+ 120
+ q = 120
+ 1
1
Tempo text
diff --git a/src/importexport/bww/internal/bww/importbww.cpp b/src/importexport/bww/internal/bww/importbww.cpp
index 33ab4a61f5b46..d4f826edd29ac 100644
--- a/src/importexport/bww/internal/bww/importbww.cpp
+++ b/src/importexport/bww/internal/bww/importbww.cpp
@@ -119,11 +119,9 @@ static void xmlSetPitch(Ms::Note* n, char step, int alter, int octave)
static void setTempo(Ms::Score* score, int tempo)
{
Ms::TempoText* tt = new Ms::TempoText(score);
- tt->setTempo(double(tempo) / 60.0);
+ tt->setEquationFromTempo(tempo);
tt->setTrack(0);
- QString tempoText = Ms::TempoText::duration2tempoTextString(Ms::TDuration::DurationType::V_QUARTER);
- tempoText += QString(" = %1").arg(tempo);
- tt->setPlainText(tempoText);
+
Ms::Measure* measure = score->firstMeasure();
Ms::Segment* segment = measure->getSegment(Ms::SegmentType::ChordRest, Ms::Fraction(0, 1));
segment->add(tt);
diff --git a/src/importexport/guitarpro/internal/importgtp.cpp b/src/importexport/guitarpro/internal/importgtp.cpp
index f691bc3e11414..d4e35780ebd6e 100644
--- a/src/importexport/guitarpro/internal/importgtp.cpp
+++ b/src/importexport/guitarpro/internal/importgtp.cpp
@@ -1354,8 +1354,7 @@ void GuitarPro::setTempo(int temp, Measure* measure)
}
TempoText* tt = new TempoText(score);
- tt->setTempo(double(temp) / 60.0);
- tt->setXmlText(QString("metNoteQuarterUp = %1").arg(temp));
+ tt->setEquationFromTempo(temp);
tt->setTrack(0);
segment->add(tt);
diff --git a/src/importexport/guitarpro/internal/importptb.cpp b/src/importexport/guitarpro/internal/importptb.cpp
index 01916c2573caa..60c180e2be9d9 100644
--- a/src/importexport/guitarpro/internal/importptb.cpp
+++ b/src/importexport/guitarpro/internal/importptb.cpp
@@ -835,8 +835,7 @@ void PowerTab::addToScore(ptSection& sec)
}
if (sec.tempo) {
TempoText* tt = new TempoText(score);
- tt->setTempo(double(sec.tempo) / 60.0f);
- tt->setXmlText(QString("metNoteQuarterUp = %1").arg(sec.tempo));
+ tt->setEquationFromTempo(sec.tempo);
tt->setTrack(0);
Segment* segment = measure->getSegment(SegmentType::ChordRest, measure->tick());
segment->add(tt);
diff --git a/src/importexport/midi/internal/midiimport/importmidi_tempo.cpp b/src/importexport/midi/internal/midiimport/importmidi_tempo.cpp
index 51b3e94704ba7..4bc053b0ffc4a 100644
--- a/src/importexport/midi/internal/midiimport/importmidi_tempo.cpp
+++ b/src/importexport/midi/internal/midiimport/importmidi_tempo.cpp
@@ -76,8 +76,7 @@ void setTempoToScore(Score* score, int tick, double beatsPerSecond)
const int tempoInBpm = qRound(beatsPerSecond * 60.0);
TempoText* tempoText = new TempoText(score);
- tempoText->setTempo(beatsPerSecond);
- tempoText->setXmlText(QString("metNoteQuarterUp = %1").arg(tempoInBpm));
+ tempoText->setEquationFromTempo(tempoInBpm);
tempoText->setTrack(0);
Measure* measure = score->tick2measure(Fraction::fromTicks(tick));
diff --git a/src/importexport/musicxml/internal/musicxml/exportxml.cpp b/src/importexport/musicxml/internal/musicxml/exportxml.cpp
index 1aad91839366f..f2ef79590b46a 100644
--- a/src/importexport/musicxml/internal/musicxml/exportxml.cpp
+++ b/src/importexport/musicxml/internal/musicxml/exportxml.cpp
@@ -4124,6 +4124,12 @@ static bool findMetronome(const QList& list,
int rparen = s6.indexOf(")");
hasParen = (lparen == s1.length() - 1 && rparen == 0);
+ if (!hasParen) {
+ lparen = s1.indexOf("[");
+ rparen = s6.indexOf("]");
+ hasParen = (lparen == s1.length() - 1 && rparen == 0);
+ }
+
metroLeft = s2;
metroRight = s5;
diff --git a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp
index 330bcbd8de0b5..0c8f45ece23aa 100644
--- a/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp
+++ b/src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp
@@ -2136,10 +2136,8 @@ void MusicXMLParserPass2::measure(const QString& partId, const Fraction time)
} else {
double tpo = tempo.toDouble() / 60;
TempoText* t = new TempoText(_score);
- t->setXmlText(QString("%1 = %2").arg(TempoText::duration2tempoTextString(TDuration(TDuration::DurationType::V_QUARTER)),
- tempo));
+ t->setEquationFromTempo(tempo.toDouble());
t->setVisible(false);
- t->setTempo(tpo);
t->setFollowText(true);
_score->setTempo(tick, tpo);
@@ -2631,17 +2629,10 @@ void MusicXMLParserDirection::direction(const QString& partId,
if (hasTempoTextAtTick(_score->tempomap(), tick.ticks())) {
_logger->logError(QString("duplicate tempo at tick %1").arg(tick.ticks()), &_e);
} else {
- double tpo = _tpoSound / 60;
TempoText* t = new TempoText(_score);
- t->setXmlText(QString("%1 = %2").arg(TempoText::duration2tempoTextString(TDuration(TDuration::DurationType::V_QUARTER))).arg(
- _tpoSound));
- t->setVisible(false);
- t->setTempo(tpo);
+ t->setEquationFromTempo(_tpoSound);
t->setFollowText(true);
- // TBD may want ro use tick + _offset if sound is affected
- _score->setTempo(tick, tpo);
-
addElemOffset(t, track, placement, measure, tick + _offset);
}
}
@@ -3366,7 +3357,7 @@ QString MusicXMLParserDirection::metronome(double& r)
tempoText += ")";
}
- return tempoText;
+ return TempoText::mapEquationToText(tempoText);
}
//---------------------------------------------------------
diff --git a/src/importexport/musicxml/tests/data/testExcludeInvisibleElements.mscx b/src/importexport/musicxml/tests/data/testExcludeInvisibleElements.mscx
index e3cb8954e46a9..61521e44e689f 100644
--- a/src/importexport/musicxml/tests/data/testExcludeInvisibleElements.mscx
+++ b/src/importexport/musicxml/tests/data/testExcludeInvisibleElements.mscx
@@ -238,7 +238,10 @@
- 1.3333299999999999
+ 1.3333299999999999
+ 80
+ q = 80
+ 1
1
metNoteQuarterUp = 80
@@ -257,7 +260,10 @@
- 1.5
+ 1.5
+ 90
+ q = 90
+ 1
1
0
= 90
diff --git a/src/importexport/ove/internal/importove.cpp b/src/importexport/ove/internal/importove.cpp
index eb9e8d8b5e186..f271543d393d7 100644
--- a/src/importexport/ove/internal/importove.cpp
+++ b/src/importexport/ove/internal/importove.cpp
@@ -1038,6 +1038,55 @@ TDuration OveNoteType_To_Duration(ovebase::NoteType noteType)
d.setType(TDuration::DurationType::V_256TH);
break;
}
+ // case ovebase::NoteType::Note_512: {
+ // d.setType(TDuration::DurationType::V_512TH);
+ // break;
+ // }
+ // case ovebase::NoteType::Note_1024: {
+ // d.setType(TDuration::DurationType::V_1024TH);
+ // break;
+ // }
+ default:
+ d.setType(TDuration::DurationType::V_QUARTER);
+ break;
+ }
+
+ return d;
+}
+
+QString OveNoteType_To_EquationString(ovebase::NoteType noteType)
+{
+ switch (noteType) {
+ case ovebase::NoteType::Note_DoubleWhole: {
+ return "d";
+ }
+ case ovebase::NoteType::Note_Whole: {
+ return "w";
+ }
+ case ovebase::NoteType::Note_Half: {
+ return "h";
+ }
+ case ovebase::NoteType::Note_Quarter: {
+ return "q";
+ }
+ case ovebase::NoteType::Note_Eight: {
+ return "e";
+ }
+ case ovebase::NoteType::Note_Sixteen: {
+ return "s";
+ }
+ case ovebase::NoteType::Note_32: {
+ return "t";
+ }
+ //case ovebase::NoteType::Note_64: {
+ // return "w";
+ //}
+ //case ovebase::NoteType::Note_128: {
+ // return "w";
+ //}
+ //case ovebase::NoteType::Note_256: {
+ // return "w";
+ //}
// case ovebase::NoteType::Note_512: {
// d.setType(TDuration::DurationType::V_512TH);
// break;
@@ -1047,11 +1096,8 @@ TDuration OveNoteType_To_Duration(ovebase::NoteType noteType)
// break;
// }
default:
- d.setType(TDuration::DurationType::V_QUARTER);
- break;
+ return "q";
}
-
- return d;
}
int accidentalToAlter(ovebase::AccidentalType type)
@@ -1362,62 +1408,65 @@ void OveToMScore::convertMeasureMisc(Measure* measure, int part, int staff, int
m_score->setTempo(Fraction::fromTicks(absTick), tpo);
- t->setTempo(tpo);
- QString durationTempoL;
- QString durationTempoR;
- if (static_cast(tempoPtr->getLeftNoteType())) {
- durationTempoL = TempoText::duration2tempoTextString(OveNoteType_To_Duration(tempoPtr->getLeftNoteType()));
- }
- if (static_cast(tempoPtr->getRightNoteType())) {
- durationTempoR = TempoText::duration2tempoTextString(OveNoteType_To_Duration(tempoPtr->getRightNoteType()));
- }
- QString textTempo;
+ QString text;
+ QString equation;
+
if (tempoPtr->getShowBeforeText()) {
- textTempo += (tempoPtr->getLeftText()).toHtmlEscaped();
+ text += (tempoPtr->getLeftText()).toHtmlEscaped();
}
if (tempoPtr->getShowMark()) {
- if (!textTempo.isEmpty()) {
- textTempo += " ";
+ if (!text.isEmpty()) {
+ text += " ";
}
if (tempoPtr->getShowParenthesis()) {
- textTempo += "(";
+ equation += "(";
}
- textTempo += durationTempoL;
+
+ equation += OveNoteType_To_EquationString(tempoPtr->getLeftNoteType());
if (tempoPtr->getLeftNoteDot()) {
- textTempo += "spacemetAugmentationDot";
+ equation += ".";
}
- textTempo += " = ";
+
+ equation += " = ";
+
switch (tempoPtr->getRightSideType()) {
case 1:
- textTempo += durationTempoR;
+ equation += OveNoteType_To_EquationString(tempoPtr->getLeftNoteType());
if (tempoPtr->getRightNoteDot()) {
- textTempo += "spacemetAugmentationDot";
+ equation += ".";
}
break;
case 2:
- textTempo += (tempoPtr->getRightText()).toHtmlEscaped();
break;
case 3:
- textTempo += QString::number(qFloor(tempoPtr->getTypeTempo()));
+ equation += QString::number(qFloor(tempoPtr->getTypeTempo()));
break;
case 0:
default:
- textTempo += QString::number(tempoPtr->getTypeTempo());
+ equation += QString::number(tempoPtr->getTypeTempo());
break;
}
if (tempoPtr->getShowParenthesis()) {
- textTempo += ")";
+ equation += ")";
}
}
- if (textTempo.isEmpty()) {
- textTempo = durationTempoL;
+
+ if (equation.isEmpty()) {
+ equation = OveNoteType_To_EquationString(tempoPtr->getLeftNoteType());
if (tempoPtr->getLeftNoteDot()) {
- textTempo += "spacemetAugmentationDot";
+ equation += ".";
}
- textTempo += " = " + QString::number(tempoPtr->getTypeTempo());
+
+ equation += " = " + QString::number(tempoPtr->getTypeTempo());
+
t->setVisible(false);
}
- t->setXmlText(textTempo);
+
+ t->setXmlText(text);
+ t->setEquation(equation);
+ t->parseEquation();
+ t->setXmlText(t->xmlText() + tempoPtr->getRightText());
+
// TODO:ws t->setAbove(true);
t->setTrack(track);
diff --git a/src/inspector/models/inspectorlistmodel.cpp b/src/inspector/models/inspectorlistmodel.cpp
index 6e0f8e73415c9..f60af5e0392ff 100644
--- a/src/inspector/models/inspectorlistmodel.cpp
+++ b/src/inspector/models/inspectorlistmodel.cpp
@@ -64,7 +64,6 @@ void InspectorListModel::buildModelsForEmptySelection(const QSet persistentSectionList {
AbstractInspectorModel::InspectorSectionType::SECTION_SCORE_DISPLAY,
AbstractInspectorModel::InspectorSectionType::SECTION_SCORE_APPEARANCE,
- AbstractInspectorModel::InspectorSectionType::SECTION_NOTATION,
};
removeUnusedModels(selectedElementSet, persistentSectionList);
@@ -119,8 +118,10 @@ int InspectorListModel::columnCount(const QModelIndex&) const
return 1;
}
-QVariant InspectorListModel::inspectorModelBySection(const int type) const
+QVariant InspectorListModel::inspectorModelBySection(const int type)
{
+ createModelsBySectionType({ static_cast(type) });
+
for (AbstractInspectorModel* model : m_modelList) {
if (static_cast(model->sectionType()) == type) {
QObject* result = qobject_cast(model);
diff --git a/src/inspector/models/inspectorlistmodel.h b/src/inspector/models/inspectorlistmodel.h
index 758b6e2d69a0f..01d8923cf2fb3 100644
--- a/src/inspector/models/inspectorlistmodel.h
+++ b/src/inspector/models/inspectorlistmodel.h
@@ -45,7 +45,7 @@ class InspectorListModel : public QAbstractListModel, public mu::async::Asyncabl
QHash roleNames() const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
- Q_INVOKABLE QVariant inspectorModelBySection(const int type) const;
+ Q_INVOKABLE QVariant inspectorModelBySection(const int type);
signals:
void elementsModified();
diff --git a/src/inspector/models/notation/tempos/temposettingsmodel.cpp b/src/inspector/models/notation/tempos/temposettingsmodel.cpp
index 732177df8a059..46a0a46d1c3cf 100644
--- a/src/inspector/models/notation/tempos/temposettingsmodel.cpp
+++ b/src/inspector/models/notation/tempos/temposettingsmodel.cpp
@@ -44,6 +44,8 @@ void TempoSettingsModel::createProperties()
});
m_tempo = buildPropertyItem(Ms::Pid::TEMPO);
+ m_equation = buildPropertyItem(Ms::Pid::TEMPO_EQUATION);
+ m_isEquationVisible = buildPropertyItem(Ms::Pid::TEMPO_EQUATION_VISIBLE);
}
void TempoSettingsModel::requestElements()
@@ -57,12 +59,16 @@ void TempoSettingsModel::loadProperties()
loadPropertyItem(m_tempo, [](const QVariant& elementPropertyValue) -> QVariant {
return DataFormatter::formatDouble(elementPropertyValue.toDouble());
});
+ loadPropertyItem(m_equation);
+ loadPropertyItem(m_isEquationVisible);
}
void TempoSettingsModel::resetProperties()
{
m_isDefaultTempoForced->resetToDefault();
m_tempo->resetToDefault();
+ m_equation->resetToDefault();
+ m_isEquationVisible->resetToDefault();
}
PropertyItem* TempoSettingsModel::isDefaultTempoForced() const
@@ -74,3 +80,13 @@ PropertyItem* TempoSettingsModel::tempo() const
{
return m_tempo;
}
+
+PropertyItem* TempoSettingsModel::equation() const
+{
+ return m_equation;
+}
+
+PropertyItem* TempoSettingsModel::isEquationVisible() const
+{
+ return m_isEquationVisible;
+}
diff --git a/src/inspector/models/notation/tempos/temposettingsmodel.h b/src/inspector/models/notation/tempos/temposettingsmodel.h
index 684b6a65656a7..b9416de2568f4 100644
--- a/src/inspector/models/notation/tempos/temposettingsmodel.h
+++ b/src/inspector/models/notation/tempos/temposettingsmodel.h
@@ -31,6 +31,8 @@ class TempoSettingsModel : public AbstractInspectorModel
Q_PROPERTY(PropertyItem * isDefaultTempoForced READ isDefaultTempoForced CONSTANT)
Q_PROPERTY(PropertyItem * tempo READ tempo CONSTANT)
+ Q_PROPERTY(PropertyItem * equation READ equation CONSTANT)
+ Q_PROPERTY(PropertyItem * isEquationVisible READ isEquationVisible CONSTANT)
public:
explicit TempoSettingsModel(QObject* parent, IElementRepositoryService* repository);
@@ -42,10 +44,14 @@ class TempoSettingsModel : public AbstractInspectorModel
PropertyItem* isDefaultTempoForced() const;
PropertyItem* tempo() const;
+ PropertyItem* equation() const;
+ PropertyItem* isEquationVisible() const;
private:
PropertyItem* m_isDefaultTempoForced = nullptr;
PropertyItem* m_tempo = nullptr;
+ PropertyItem* m_equation = nullptr;
+ PropertyItem* m_isEquationVisible = nullptr;
};
}
diff --git a/src/notation/notationscene.qrc b/src/notation/notationscene.qrc
index fbdf3f09bc202..fe162cfa0d782 100644
--- a/src/notation/notationscene.qrc
+++ b/src/notation/notationscene.qrc
@@ -24,5 +24,6 @@
qml/MuseScore/NotationScene/UndoRedoToolBar.qml
qml/MuseScore/NotationScene/internal/ElementPopup.qml
qml/MuseScore/NotationScene/internal/TempoPopup.qml
+ qml/MuseScore/NotationScene/internal/DurationPicker.qml
diff --git a/src/notation/qml/MuseScore/NotationScene/NotationView.qml b/src/notation/qml/MuseScore/NotationScene/NotationView.qml
index 4e6334654c375..36734fd4f0142 100644
--- a/src/notation/qml/MuseScore/NotationScene/NotationView.qml
+++ b/src/notation/qml/MuseScore/NotationScene/NotationView.qml
@@ -49,11 +49,6 @@ FocusScope {
readonly property int scrollbarMargin: 4
}
- Component.onCompleted: {
- notationView.load()
- notationNavigator.load()
- }
-
InspectorListModel {
id: inspectorListModel
}
@@ -126,8 +121,6 @@ FocusScope {
ElementPopup {
id: elementPopup
-
- model: inspectorListModel.inspectorModelBySection(Inspector.SECTION_NOTATION)
}
StyledScrollBar {
@@ -267,11 +260,15 @@ FocusScope {
function showNotationPopup(type, pos, size) {
elementPopup.close();
+ elementPopup.model = inspectorListModel.inspectorModelBySection(Inspector.SECTION_NOTATION);
+
elementPopup.type = type;
elementPopup.x = pos.x + size.x / 2 - elementPopup.width / 2;
elementPopup.y = pos.y + size.y / 2;
+ elementPopup.preOpening();
+
elementPopup.open();
}
diff --git a/src/notation/qml/MuseScore/NotationScene/internal/DurationPicker.qml b/src/notation/qml/MuseScore/NotationScene/internal/DurationPicker.qml
new file mode 100644
index 0000000000000..394ee4e5554db
--- /dev/null
+++ b/src/notation/qml/MuseScore/NotationScene/internal/DurationPicker.qml
@@ -0,0 +1,119 @@
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+import QtQuick.Controls 2.15
+import QtQuick.Controls 1.3
+
+import MuseScore.NotationScene 1.0
+import MuseScore.UiComponents 1.0
+import MuseScore.Ui 1.0
+
+Row {
+ id: root
+
+ spacing: 4
+
+ property int buttonWidthBig: 35;
+ property int buttonWidthSmall: 20;
+
+ property string currentValue: "q";
+
+ signal valueChanged(var changedValue)
+
+ function setValue(s) {
+ noteButton.setOptionFromNote(s[0]);
+ dotList.currentValue = s.substr(1);
+
+ root.currentValue = s;
+ }
+
+ FlatButton {
+ id: noteButton
+
+ icon: IconCode.NOTE_QUARTER
+
+ width: buttonWidthBig
+ height: root.height
+
+ accentButton: true
+
+ property string currentValue: "q"
+
+ function setOptionFromNote(note) {
+ for(let optionItem in privateProperties.items) {
+ let option = privateProperties.items[optionItem];
+ if(option.value === note) {
+ noteButton.icon = option.icon;
+ noteButton.currentValue = option.value;
+ return;
+ }
+ }
+ }
+
+ QtObject {
+ id: privateProperties
+
+ readonly property var items: [
+ {code: "0", value: "d", icon: IconCode.NOTE_WHOLE_DOUBLE, title: qsTrc("global", "Double Whole")},
+ {code: "1", value: "w", icon: IconCode.NOTE_WHOLE, title: qsTrc("global", "Whole")},
+ {code: "2", value: "h", icon: IconCode.NOTE_HALF, title: qsTrc("global", "Half")},
+ {code: "3", value: "q", icon: IconCode.NOTE_QUARTER, title: qsTrc("global", "Quarter")},
+ {code: "4", value: "e", icon: IconCode.NOTE_8TH, title: qsTrc("global", "Eighth")},
+ {code: "5", value: "s", icon: IconCode.NOTE_16TH, title: qsTrc("global", "Sixteenth")},
+ {code: "6", value: "t", icon: IconCode.NOTE_32TH, title: qsTrc("global", "Thirty-Second")},
+ ]
+ }
+
+ onClicked: {
+ menuLoader.toggleOpened(privateProperties.items)
+ }
+
+ StyledMenuLoader {
+ id: menuLoader
+ onHandleAction: {
+ noteButton.icon = privateProperties.items[parseInt(actionCode)]["icon"];
+
+ noteButton.currentValue = privateProperties.items[parseInt(actionCode)]["value"];
+
+ root.currentValue = noteButton.currentValue + dotList.currentValue;
+ valueChanged(root.currentValue);
+ }
+ }
+
+ }
+
+ RadioButtonGroup {
+ id: dotList
+
+ property string currentValue: ""
+
+ width: buttonWidthSmall * 2
+
+ model: [
+ { textRole: "."},
+ { textRole: ".."},
+ ]
+
+ delegate: FlatRadioButton {
+ ButtonGroup.group: dotList.radioButtonGroup
+
+ height: root.height
+
+ checked: dotList.currentValue === modelData["textRole"]
+
+ onClicked: {
+ if (dotList.currentValue !== modelData["textRole"])
+ dotList.currentValue = modelData["textRole"]
+ else
+ dotList.currentValue = ""
+
+ root.currentValue = noteButton.currentValue + dotList.currentValue;
+ valueChanged(root.currentValue);
+ }
+
+ StyledTextLabel {
+ text: modelData["textRole"]
+ }
+ }
+ }
+
+}
diff --git a/src/notation/qml/MuseScore/NotationScene/internal/ElementPopup.qml b/src/notation/qml/MuseScore/NotationScene/internal/ElementPopup.qml
index 4bbe3f4c8abcf..d12f13c60e946 100644
--- a/src/notation/qml/MuseScore/NotationScene/internal/ElementPopup.qml
+++ b/src/notation/qml/MuseScore/NotationScene/internal/ElementPopup.qml
@@ -16,17 +16,32 @@ StyledPopup {
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
- width: 200
-
- leftPadding: 0
+ leftPadding: 10
+ rightPadding: 10
function isVisible(type) {
return type === root.type;
}
+ function popupWidth() {
+ return root.width - root.leftPadding - root.rightPadding;
+ }
+
+ function preOpening() {
+ switch(root.type) {
+ case "Tempo":
+ tempoPopup.model = root.model.modelByType(Inspector.TYPE_TEMPO);
+ tempoPopup.parseEquation();
+ tempoPopup.setActiveTab();
+ break;
+ default:
+ break;
+ }
+ }
+
TempoPopup {
- model: root.model.modelByType(Inspector.TYPE_TEMPO)
- width: root.width
+ id: tempoPopup
+ model:null
visible: isVisible("Tempo")
}
}
diff --git a/src/notation/qml/MuseScore/NotationScene/internal/TempoPopup.qml b/src/notation/qml/MuseScore/NotationScene/internal/TempoPopup.qml
index 89095b45c6db5..89f17370e8763 100644
--- a/src/notation/qml/MuseScore/NotationScene/internal/TempoPopup.qml
+++ b/src/notation/qml/MuseScore/NotationScene/internal/TempoPopup.qml
@@ -9,31 +9,317 @@ import MuseScore.Ui 1.0
import MuseScore.Inspector 1.0
-TabPanel {
- id: tabPanel
+Column {
+ id: root
+
+ property QtObject model;
+
+ property string currentEquation;
+ property int currentParantheses;
+
+ property int startupTab: -1;
- height: 50
width: 200
- property QtObject model;
+ spacing: 10
+
+ function setEquation(left, right) {
+ currentEquation = right ? left + " = " + right : left;
+ addParantheses();
+ }
+
+ function addParantheses() {
+ switch(paranthesesList.currentValue) {
+ case 0:
+ root.model.equation.value = root.currentEquation;
+ break;
+ case 1:
+ root.model.equation.value = "(" + root.currentEquation + ")";
+ break;
+ case 2:
+ root.model.equation.value = "[" + root.currentEquation + "]";
+ break;
+ }
+ }
+
+ function parseEquation() {
+ let eq = root.model.equation.value;
+ let l = eq.length;
+
+ if(eq[0] === "(" && eq[l - 1] === ")") {
+ root.currentEquation = eq.substr(1, l - 2);
+ root.currentParantheses = 1;
+ }
+ else if(eq[0] === "[" && eq[l - 1] === "]") {
+ root.currentEquation = eq.substr(1, l - 2);
+ root.currentParantheses = 2;
+ } else {
+ root.currentEquation = eq;
+ root.currentParantheses = 0;
+ }
+ }
+
+ function setActiveTab() {
+ root.startupTab = -1;
+
+ if(/[dwhqest].{0,2} = [0-9]+/.test(root.currentEquation)) {
+ root.startupTab = 0;
+ } else if(/[dwhqest].{0,2} = [dwhqest].{0,2}/.test(root.currentEquation)) {
+ root.startupTab = 1;
+ } else {
+ root.startupTab = 2;
+ }
+
+ if(root.startupTab != -1)
+ tabPanel.currentIndex = root.startupTab;
+
+ tempoTab.startup = true;
+ equationTab.startup = true;
+
+ }
+
+ TabPanel {
+ id: tabPanel
+
+ height: 90
+
+ Tab {
+ id: tempoTab
+
+ title: "Tempo"
+
+ property bool startup: true;
+
+ Row {
+ id: tempoRow
+
+ function init() {
+ if(root.startupTab === 0 && startup) {
+ tempoDuration.setValue(root.currentEquation.split("=")[0].trim());
+ tempoBPM.currentValue = root.currentEquation.split("=")[1].trim();
+ } else {
+ tempoBPM.currentValue = Math.floor(root.model.tempo.value).toString();
+ root.setEquation(tempoDuration.currentValue, tempoBPM.currentValue.toString());
+ }
+
+ tempoTab.startup = false;
+ }
+
+ onVisibleChanged: {
+ if(tabPanel.currentIndex === 0) {
+ tempoRow.init();
+ }
+ }
+
+ Component.onCompleted: {
+ tempoRow.init();
+ }
+
+ DurationPicker {
+ id: tempoDuration
+
+ height: 35
+
+ anchors.verticalCenter: parent.verticalCenter
+
+ onValueChanged: {
+ root.setEquation(tempoDuration.currentValue, tempoBPM.currentValue.toString());
+ }
+
+ }
+
+ StyledTextLabel {
+ text: "="
+
+ anchors.verticalCenter: parent.verticalCenter
+
+ width: root.width - tempoDuration.width - tempoBPM.width
+
+ horizontalAlignment: Qt.AlignHCenter
+ }
+
+ IncrementalPropertyControl {
+ id: tempoBPM
+
+ iconMode: iconModeEnum.hidden
+
+ anchors.verticalCenter: parent.verticalCenter
+
+ width: tempoDuration.width
+
+ maxValue: 999
+ minValue: 0
+ step:1
+
+ decimals: 0
+
+ onValueEdited: {
+ currentValue = newValue;
+ root.setEquation(tempoDuration.currentValue, tempoBPM.currentValue.toString());
+ }
+ }
+ }
- Tab {
- id: firstTab
+ }
- title: "Tempo"
+ Tab {
+ id: equationTab
+
+ title: "Equation"
+
+ property bool startup: true;
+
+ Row {
+ id: equationRow
+
+ function init() {
+ if(root.startupTab === 1 && startup) {
+ equationLeft.setValue(root.currentEquation.split("=")[0].trim());
+ equationRight.setValue(root.currentEquation.split("=")[1].trim());
+ } else {
+ root.setEquation(equationLeft.currentValue, equationRight.currentValue);
+ }
+
+ equationTab.startup = false;
+ }
+
+ onVisibleChanged: {
+ if(tabPanel.currentIndex === 1) {
+ equationRow.init();
+ }
+ }
+
+ Component.onCompleted: {
+ equationRow.init();
+ }
+
+ DurationPicker {
+ id: equationLeft
+
+ height: 35
+
+ anchors.verticalCenter: parent.verticalCenter
+
+ onValueChanged: {
+ root.setEquation(equationLeft.currentValue, equationRight.currentValue);
+ }
+ }
+
+ StyledTextLabel {
+ text: "="
+
+ anchors.verticalCenter: parent.verticalCenter
+
+ width: root.width - equationLeft.width - equationRight.width
+
+ horizontalAlignment: Qt.AlignHCenter
+ }
+
+ DurationPicker {
+ id: equationRight
+
+ height: 35
+
+ anchors.verticalCenter: parent.verticalCenter
+
+ onValueChanged: {
+ root.setEquation(equationLeft.currentValue, equationRight.currentValue);
+ }
+ }
+
+ }
+
+ }
+
+ Tab {
+ id: codeTab
+
+ title: "Code"
+
+ Column {
+ spacing: 8
+
+ onVisibleChanged: {
+ if(tabPanel.currentIndex === 2) {
+ root.setEquation(codeInput.currentText);
+ }
+ }
+
+ Component.onCompleted: {
+ root.setEquation(codeInput.currentText);
+ }
+
+ StyledTextLabel {
+ text: qsTrc("inspector", "Enter Code") + " (eg: e = 100)"
+ }
+
+ TextInputField {
+ id: codeInput
+
+ height: 30
+ width: root.width
+
+ onCurrentTextEdited: {
+ root.setEquation(newTextValue);
+ }
+ }
+ }
+
+ }
+ }
+
+ SeparatorLine {
}
- Tab {
- id: secondTab
+ StyledTextLabel {
+ text: qsTrc("Inspector", "Parentheses")
+ }
+
+ RadioButtonGroup {
+ id: paranthesesList
+
+ property int currentValue: root.currentParantheses
+
+ height: 30
+ width: root.width
+
+ model: [
+ { textRole: "None", valueRole: 0 },
+ { textRole: "( )", valueRole: 1 },
+ { textRole: "[ ]", valueRole: 2 }
+ ]
+
+ delegate: FlatRadioButton {
+ ButtonGroup.group: paranthesesList.radioButtonGroup
- title: "Equation"
+ checked: paranthesesList.currentValue === modelData["valueRole"]
+ onToggled: {
+ paranthesesList.currentValue = modelData["valueRole"]
+ root.addParantheses();
+ }
+
+ StyledTextLabel {
+ text: modelData["textRole"]
+ }
+ }
+ }
+
+ SeparatorLine {
}
- Tab {
- id: thirdTab
+ CheckBox {
+ id: visibilityToggle
- title: "Code"
+ width: root.width
+ checked: root.model ? root.model.isEquationVisible.value : false;
+
+ text: qsTrc("inspector", "Visibility")
+ onClicked: {
+ checked = !checked
+ root.model.isEquationVisible.value = checked;
+ }
}
+
}
diff --git a/src/palette/internal/palette/palettecreator.cpp b/src/palette/internal/palette/palettecreator.cpp
index 2a4fa768207e2..da0c3ff99a1ba 100644
--- a/src/palette/internal/palette/palettecreator.cpp
+++ b/src/palette/internal/palette/palettecreator.cpp
@@ -88,6 +88,8 @@
#include "palette/palette.h"
#include "translation.h"
+#include "log.h"
+
using namespace mu::actions;
namespace Ms {
@@ -296,18 +298,16 @@ Palette* PaletteCreator::newLinesPalette()
//---------------------------------------------------------
struct TempoPattern {
- QString pattern;
- const char* name;
- double f;
- bool relative;
- bool italian;
- bool followText;
- bool basic;
- bool masterOnly;
-
- TempoPattern(const QString& s, const char* n, double v, bool r, bool i, bool f, bool b, bool m)
- : pattern(s),
- name(n), f(v), relative(r), italian(i), followText(f), basic(b), masterOnly(m) {}
+ QString text;
+ QString equation;
+ QString name;
+
+ TempoPattern(const QString& t, const QString& e, const QString& n = "")
+ : text(t)
+ , equation(e)
+ , name(n)
+ {
+ }
};
//---------------------------------------------------------
@@ -1525,70 +1525,36 @@ PalettePanel* PaletteCreator::newTempoPalettePanel(bool defaultPalettePanel)
}
sp->setDrawGrid(true);
- static const TempoPattern tps[] = {
- TempoPattern("metNoteHalfUp = 80", QT_TRANSLATE_NOOP("palette",
- "Half note = 80 BPM"), 80.0 / 30.0, false, false, true, true,
- false), // 1/2
- TempoPattern("metNoteQuarterUp = 80", QT_TRANSLATE_NOOP("palette",
- "Quarter note = 80 BPM"), 80.0 / 60.0, false, false, true, true,
- false), // 1/4
- TempoPattern("metNote8thUp = 80", QT_TRANSLATE_NOOP("palette",
- "Eighth note = 80 BPM"), 80.0 / 120.0, false, false, true, true,
- false), // 1/8
- TempoPattern("metNoteHalfUpspacemetAugmentationDot = 80",
- QT_TRANSLATE_NOOP("palette",
- "Dotted half note = 80 BPM"), 120 / 30.0, false, false, true, false, false), // dotted 1/2
- TempoPattern("metNoteQuarterUpspacemetAugmentationDot = 80",
- QT_TRANSLATE_NOOP("palette",
- "Dotted quarter note = 80 BPM"), 120 / 60.0, false, false, true, true, false), // dotted 1/4
- TempoPattern("metNote8thUpspacemetAugmentationDot = 80",
- QT_TRANSLATE_NOOP("palette",
- "Dotted eighth note = 80 BPM"), 120 / 120.0, false, false, true, false,
- false), // dotted 1/8
-
- TempoPattern("Grave", "Grave", 35.0 / 60.0, false, true, false, false, false),
- TempoPattern("Largo", "Largo", 50.0 / 60.0, false, true, false, false, false),
- TempoPattern("Lento", "Lento", 52.5 / 60.0, false, true, false, false, false),
- TempoPattern("Larghetto", "Larghetto", 63.0 / 60.0, false, true, false, false, true),
- TempoPattern("Adagio", "Adagio", 71.0 / 60.0, false, true, false, false, false),
- TempoPattern("Andante", "Andante", 92.0 / 60.0, false, true, false, false, false),
- TempoPattern("Andantino", "Andantino", 94.0 / 60.0, false, true, false, false, true),
- TempoPattern("Moderato", "Moderato", 114.0 / 60.0, false, true, false, false, false),
- TempoPattern("Allegretto", "Allegretto", 116.0 / 60.0, false, true, false, false, false),
- TempoPattern("Allegro moderato", "Allegro moderato", 118.0 / 60.0, false, true, false, false, true),
- TempoPattern("Allegro", "Allegro", 144.0 / 60.0, false, true, false, false, false),
- TempoPattern("Vivace", "Vivace", 172.0 / 60.0, false, true, false, false, false),
- TempoPattern("Presto", "Presto", 187.0 / 60.0, false, true, false, false, false),
- TempoPattern("Prestissimo", "Prestissimo", 200.0 / 60.0, false, true, false, false, true),
-
- TempoPattern(
- "metNoteQuarterUp = metNoteQuarterUpspacemetAugmentationDot", QT_TRANSLATE_NOOP(
- "palette",
- "Quarter note = dotted quarter note metric modulation"), 3.0 / 2.0, true, false, true, false, false),
- TempoPattern(
- "metNoteQuarterUpspacemetAugmentationDot = metNoteQuarterUp", QT_TRANSLATE_NOOP(
- "palette",
- "Dotted quarter note = quarter note metric modulation"), 2.0 / 3.0, true, false, true, false, false),
- TempoPattern("metNoteHalfUp = metNoteQuarterUp",
- QT_TRANSLATE_NOOP("palette",
- "Half note = quarter note metric modulation"), 1.0 / 2.0, true, false, true, false,
- false),
- TempoPattern("metNoteQuarterUp = metNoteHalfUp",
- QT_TRANSLATE_NOOP("palette",
- "Quarter note = half note metric modulation"), 2.0 / 1.0, true, false, true, false,
- false),
- TempoPattern("metNote8thUp = metNote8thUp",
- QT_TRANSLATE_NOOP("palette",
- "Eighth note = eighth note metric modulation"), 1.0 / 1.0, true, false, true, false,
- false),
- TempoPattern("metNoteQuarterUp = metNoteQuarterUp",
- QT_TRANSLATE_NOOP("palette",
- "Quarter note = quarter note metric modulation"), 1.0 / 1.0, true, false, true, false,
- false),
- TempoPattern(
- "metNote8thUpspacemetAugmentationDot = metNoteQuarterUp", QT_TRANSLATE_NOOP(
- "palette",
- "Dotted eighth note = quarter note metric modulation"), 2.0 / 3.0, true, false, true, false, false),
+ static const TempoPattern tempoPatterns[] = {
+ TempoPattern("", "h = 80", "Half notes = 80 BPM"),
+ TempoPattern("", "q = 80", "Quarter notes = 80 BPM"),
+ TempoPattern("", "e = 80", "Eighth notes = 80 BPM"),
+
+ TempoPattern("", "h. = 80", "Dotted half note = 80 BPM"),
+ TempoPattern("", "q. = 80", "Dotted quarter note = 80 BPM"),
+ TempoPattern("", "e. = 80", "Dotted eighth note = 80 BPM"),
+
+ TempoPattern("Grave", "q = 35"),
+ TempoPattern("Largo", "q = 50"),
+ TempoPattern("Lento", "q = 52"),
+ TempoPattern("Larghetto", "q = 63"),
+ TempoPattern("Adagio", "q = 71"),
+ TempoPattern("Andante", "q = 92"),
+ TempoPattern("Andantino", "q = 94"),
+ TempoPattern("Moderato", "q = 114"),
+ TempoPattern("Allegretto", "q = 116"),
+ TempoPattern("Allegro moderato", "q = 118"),
+ TempoPattern("Allegro", "q = 144"),
+ TempoPattern("Vivace", "q = 172"),
+ TempoPattern("Presto", "q = 187"),
+ TempoPattern("Prestissimo", "q = 200"),
+
+ TempoPattern("", "q = q.", "Quarter note = dotted quarter note metric modulation"),
+ TempoPattern("", "q. = q", "Dotted quarter note = quarter note metric modulation"),
+ TempoPattern("", "h = q", "Half note = quarter note metric modulation"),
+ TempoPattern("", "q = h", "Quarter note = half note metric modulation"),
+ TempoPattern("", "e = e", "Eighth note = eighth note metric modulation"),
+ TempoPattern("", "q = q", "Quarter note = quarter note metric modulation")
};
auto stxt = makeElement(gscore);
@@ -1597,20 +1563,18 @@ PalettePanel* PaletteCreator::newTempoPalettePanel(bool defaultPalettePanel)
stxt->setSwing(true);
sp->append(stxt, QT_TRANSLATE_NOOP("palette", "Swing"))->setElementTranslated(true);
- for (TempoPattern tp : tps) {
- auto tt = makeElement(gscore);
- tt->setFollowText(tp.followText);
- tt->setXmlText(tp.pattern);
- if (tp.relative) {
- tt->setRelative(tp.f);
- sp->append(tt, mu::qtrc("palette", tp.name), 1.5);
- } else if (tp.italian) {
- tt->setTempo(tp.f);
- sp->append(tt, tp.name, 1.3);
- } else {
- tt->setTempo(tp.f);
- sp->append(tt, mu::qtrc("palette", tp.name), 1.5);
- }
+ for (TempoPattern tempoPattern : tempoPatterns) {
+ auto tempoText = makeElement(gscore);
+
+ tempoText->setFollowText(true);
+ tempoText->setXmlText(tempoPattern.text + " ");
+ tempoText->setEquation(tempoPattern.equation);
+ tempoText->setEquationVisible(tempoPattern.text.isEmpty());
+ tempoText->parseEquation();
+
+ sp->append(tempoText, tempoPattern.name.isEmpty() ? tempoPattern.text : mu::qtrc("palette",
+ tempoPattern.name.toStdString().c_str()),
+ tempoPattern.name.isEmpty() ? 1.5 : 1.3);
}
sp->setMoreElements(false);