diff --git a/src/engraving/api/tests/score_tests.cpp b/src/engraving/api/tests/score_tests.cpp
index c34cb04e3f83f..e947f68ab5a62 100644
--- a/src/engraving/api/tests/score_tests.cpp
+++ b/src/engraving/api/tests/score_tests.cpp
@@ -78,7 +78,7 @@ TEST_F(Engraving_ApiScoreTests, replaceInstrumentAtDomLevel)
newInstrument.setTrackName(u"Replaced Instrument");
score->startCmd(TranslatableString::untranslatable("Replace instrument test"));
- score->undo(new ChangePart(part, new Instrument(newInstrument), u"Replaced Part"));
+ score->undo(new ChangePart(part, new Instrument(newInstrument)));
score->endCmd();
// [THEN] The part's instrument should be changed
@@ -116,7 +116,7 @@ TEST_F(Engraving_ApiScoreTests, replaceInstrumentUndo)
newInstrument.setTrackName(u"New Instrument");
score->startCmd(TranslatableString::untranslatable("Replace instrument test"));
- score->undo(new ChangePart(part, new Instrument(newInstrument), u"New Part"));
+ score->undo(new ChangePart(part, new Instrument(newInstrument)));
score->endCmd();
// Verify it changed
@@ -155,7 +155,7 @@ TEST_F(Engraving_ApiScoreTests, replaceInstrumentRedo)
newInstrument.setId(u"test.replaced");
score->startCmd(TranslatableString::untranslatable("Replace instrument test"));
- score->undo(new ChangePart(part, new Instrument(newInstrument), u"Replaced"));
+ score->undo(new ChangePart(part, new Instrument(newInstrument)));
score->endCmd();
EXPECT_EQ(part->instrumentId(), u"test.replaced");
diff --git a/src/engraving/dom/dom.cmake b/src/engraving/dom/dom.cmake
index 05c04af4b2659..e9e002a0716ba 100644
--- a/src/engraving/dom/dom.cmake
+++ b/src/engraving/dom/dom.cmake
@@ -286,7 +286,7 @@ set(DOM_SRC
${CMAKE_CURRENT_LIST_DIR}/staff.h
${CMAKE_CURRENT_LIST_DIR}/stafflines.cpp
${CMAKE_CURRENT_LIST_DIR}/stafflines.h
- ${CMAKE_CURRENT_LIST_DIR}/staffname.h
+ ${CMAKE_CURRENT_LIST_DIR}/stafflabel.h
${CMAKE_CURRENT_LIST_DIR}/staffstate.cpp
${CMAKE_CURRENT_LIST_DIR}/staffstate.h
${CMAKE_CURRENT_LIST_DIR}/stafftext.cpp
diff --git a/src/engraving/dom/excerpt.cpp b/src/engraving/dom/excerpt.cpp
index 221782bac1a74..748f61e1abf91 100644
--- a/src/engraving/dom/excerpt.cpp
+++ b/src/engraving/dom/excerpt.cpp
@@ -361,7 +361,6 @@ void Excerpt::createExcerpt(Excerpt* excerpt)
Part* p = new Part(score);
p->setId(part->id());
p->setInstrument(*part->instrument());
- p->setPartName(part->partName());
p->setPreferSharpFlat(part->preferSharpFlat());
for (Staff* staff : part->staves()) {
diff --git a/src/engraving/dom/instrtemplate.h b/src/engraving/dom/instrtemplate.h
index fa5f57627e79f..ec1a2d8820728 100644
--- a/src/engraving/dom/instrtemplate.h
+++ b/src/engraving/dom/instrtemplate.h
@@ -84,7 +84,7 @@ class InstrumentTemplate
String id;
String soundId;
String trackName;
- StaffName instrumentName;
+ InstrumentLabel instrumentName;
String musicXmlId; ///< used in MusicXML 3.0
String description; ///< a longer description of the instrument
diff --git a/src/engraving/dom/instrument.cpp b/src/engraving/dom/instrument.cpp
index 04175035b4b12..36df940fd7d5c 100644
--- a/src/engraving/dom/instrument.cpp
+++ b/src/engraving/dom/instrument.cpp
@@ -74,7 +74,7 @@ Instrument::Instrument(const Instrument& i)
{
m_id = i.m_id;
m_soundId = i.m_soundId;
- m_instrumentName = i.m_instrumentName;
+ m_instrumentLabel = i.m_instrumentLabel;
m_trackName = i.m_trackName;
m_minPitchA = i.m_minPitchA;
m_maxPitchA = i.m_maxPitchA;
@@ -106,7 +106,7 @@ void Instrument::operator=(const Instrument& i)
m_id = i.m_id;
m_soundId = i.m_soundId;
- m_instrumentName = i.m_instrumentName;
+ m_instrumentLabel = i.m_instrumentLabel;
m_trackName = i.m_trackName;
m_minPitchA = i.m_minPitchA;
m_maxPitchA = i.m_maxPitchA;
@@ -152,8 +152,8 @@ String Instrument::recognizeMusicXmlId() const
nameList.reserve(3);
nameList.push_back(m_trackName);
- nameList.push_back(m_instrumentName.longName());
- nameList.push_back(m_instrumentName.shortName());
+ nameList.push_back(m_instrumentLabel.longName());
+ nameList.push_back(m_instrumentLabel.shortName());
const InstrumentTemplate* tmplByName = mu::engraving::searchTemplateForInstrNameList(nameList, m_useDrumset);
@@ -805,7 +805,7 @@ void Instrument::setGlissandoStyle(GlissandoStyle style)
bool Instrument::operator==(const Instrument& i) const
{
- bool equal = i.m_instrumentName == m_instrumentName;
+ bool equal = i.m_instrumentLabel == m_instrumentLabel;
equal &= i.m_trackName == m_trackName;
equal &= i.m_id == m_id;
equal &= i.m_soundId == m_soundId;
@@ -864,14 +864,36 @@ bool Instrument::isDifferentInstrument(const Instrument& i) const
String Instrument::family() const
{
- auto search = searchTemplateIndexForId(m_id);
+ static const String NO_FAMILY = u"-";
- if (!search.instrTemplate) {
- static String empty;
- return empty;
+ if (m_familyCache.empty()) {
+ auto search = searchTemplateIndexForId(m_id);
+
+ if (search.instrTemplate) {
+ m_familyCache = search.instrTemplate->familyId();
+ } else {
+ m_familyCache = NO_FAMILY;
+ }
+ }
+
+ return m_familyCache == NO_FAMILY ? String() : m_familyCache;
+}
+
+String Instrument::group() const
+{
+ static const String NO_GROUP = u"-";
+
+ if (m_groupCache.empty()) {
+ auto search = searchTemplateIndexForId(m_id);
+
+ if (search.instrTemplate) {
+ m_groupCache = search.instrTemplate->groupId;
+ } else {
+ m_groupCache = NO_GROUP;
+ }
}
- return search.instrTemplate->familyId();
+ return m_groupCache == NO_GROUP ? String() : m_groupCache;
}
//---------------------------------------------------------
@@ -1051,22 +1073,22 @@ void Instrument::setTrackName(const String& s)
String Instrument::nameAsXmlText() const
{
- return m_instrumentName.longName();
+ return m_instrumentLabel.longName();
}
String Instrument::nameAsPlainText() const
{
- return TextBase::unEscape(m_instrumentName.longName());
+ return TextBase::unEscape(m_instrumentLabel.longName());
}
String Instrument::abbreviatureAsXmlText() const
{
- return m_instrumentName.shortName();
+ return m_instrumentLabel.shortName();
}
String Instrument::abbreviatureAsPlainText() const
{
- return TextBase::unEscape(m_instrumentName.shortName());
+ return TextBase::unEscape(m_instrumentLabel.shortName());
}
Instrument Instrument::fromTemplate(const InstrumentTemplate* templ)
diff --git a/src/engraving/dom/instrument.h b/src/engraving/dom/instrument.h
index 2c71994a08f84..95a01285bdcbf 100644
--- a/src/engraving/dom/instrument.h
+++ b/src/engraving/dom/instrument.h
@@ -29,7 +29,7 @@
#include "clef.h"
#include "interval.h"
#include "notifier.h"
-#include "staffname.h"
+#include "stafflabel.h"
#include "stringdata.h"
#include "../compat/midi/midicoreevent.h"
@@ -285,6 +285,7 @@ class Instrument
const String& soundId() const { return m_soundId; }
void setSoundId(const String& id) { m_soundId = id; }
String family() const;
+ String group() const;
void setId(const String& id) { m_id = id; }
void setMinPitchP(int v) { m_minPitchP = v; }
void setMaxPitchP(int v) { m_maxPitchP = v; }
@@ -326,12 +327,19 @@ class Instrument
void setStringData(const StringData& d) { m_stringData.set(d); }
bool hasStrings() const { return m_stringData.strings() > 0; }
- void setLongName(const String& f) { m_instrumentName.setLongName(f); }
- void setShortName(const String& f) { m_instrumentName.setShortName(f); }
- void setInstrumentName(const StaffName& n) { m_instrumentName = n; }
- const String& longName() const { return m_instrumentName.longName(); }
- const String& shortName() const { return m_instrumentName.shortName(); }
- const StaffName& instrumentName() const { return m_instrumentName; }
+ void setLongName(const String& f) { m_instrumentLabel.setLongName(f); }
+ void setShortName(const String& f) { m_instrumentLabel.setShortName(f); }
+ void setInstrumentName(const InstrumentLabel& n) { m_instrumentLabel = n; }
+ const String& longName() const { return m_instrumentLabel.longName(); }
+ const String& shortName() const { return m_instrumentLabel.shortName(); }
+ const InstrumentLabel& instrumentLabel() const { return m_instrumentLabel; }
+ InstrumentLabel& instrumentLabel() { return m_instrumentLabel; }
+
+ int number() const { return m_instrumentLabel.number(); }
+ void setNumber(int v) { return m_instrumentLabel.setNumber(v); }
+
+ const String& transposition() const { return m_instrumentLabel.transposition(); }
+ void setTransposition(const String& s) { m_instrumentLabel.setTransposition(s); }
int minPitchP() const;
int maxPitchP() const;
@@ -369,7 +377,7 @@ class Instrument
private:
- StaffName m_instrumentName;
+ InstrumentLabel m_instrumentLabel;
String m_trackName;
String m_id;
@@ -396,6 +404,9 @@ class Instrument
Trait m_trait;
bool m_isPrimary = false;
+ mutable String m_familyCache;
+ mutable String m_groupCache;
+
GlissandoStyle m_glissandoStyle = GlissandoStyle::CHROMATIC;
};
diff --git a/src/engraving/dom/instrumentname.cpp b/src/engraving/dom/instrumentname.cpp
index 825826dbca0f3..877d8d949a696 100644
--- a/src/engraving/dom/instrumentname.cpp
+++ b/src/engraving/dom/instrumentname.cpp
@@ -116,8 +116,12 @@ mu::engraving::staff_idx_t mu::engraving::InstrumentName::effectiveStaffIdx() co
{
if (m_sysStaff->show() || m_instrumentNameRole == InstrumentNameRole::STAFF) {
return staffIdx();
+ } else if (m_instrumentNameRole == InstrumentNameRole::PART) {
+ return system()->firstVisibleSysStaffOfPart(score()->staff(staffIdx())->part());
+ } else {
+ staff_idx_t curIdx = staffIdx();
+ Instrument* instr = score()->staff(curIdx)->part()->instrument(system()->first()->tick());
+ return system()->firstVisibleSysStaffWithInstrument(instr->id(), curIdx);
}
-
- return system()->firstVisibleSysStaffOfPart(score()->staff(staffIdx())->part());
}
}
diff --git a/src/engraving/dom/instrumentname.h b/src/engraving/dom/instrumentname.h
index 96072a5d58f1d..7cf6ebb311edb 100644
--- a/src/engraving/dom/instrumentname.h
+++ b/src/engraving/dom/instrumentname.h
@@ -30,7 +30,7 @@ enum class InstrumentNameType : char {
LONG, SHORT
};
enum class InstrumentNameRole : char {
- STAFF, PART
+ STAFF, PART, GROUP
};
class System;
@@ -71,6 +71,19 @@ class InstrumentName final : public TextBase
staff_idx_t effectiveStaffIdx() const override;
+ struct LayoutData : public TextBase::LayoutData {
+ public:
+ int column() const { return m_column; }
+ void setColumn(int v) { m_column = v; }
+ staff_idx_t endIdxOfGroup() const { return m_endIdxOfGroup; }
+ void setEndIdxOfGroup(staff_idx_t v) { m_endIdxOfGroup = v; }
+
+ private:
+ int m_column = 0;
+ staff_idx_t m_endIdxOfGroup = muse::nidx; // one-after last spanned staff (for GROUP types)
+ };
+ DECLARE_LAYOUTDATA_METHODS(InstrumentName)
+
private:
InstrumentNameType m_instrumentNameType = InstrumentNameType::LONG;
diff --git a/src/engraving/dom/part.cpp b/src/engraving/dom/part.cpp
index 8bfb37a16e760..758f9c096aede 100644
--- a/src/engraving/dom/part.cpp
+++ b/src/engraving/dom/part.cpp
@@ -67,7 +67,6 @@ Part::Part(Score* s)
void Part::initFromInstrTemplate(const InstrumentTemplate* t)
{
- m_partName = t->instrumentName.longName();
setInstrument(Instrument::fromTemplate(t));
}
@@ -557,6 +556,26 @@ void Part::setShortNameAll(const String& s)
}
}
+int Part::number(const Fraction& tick) const
+{
+ return instrument(tick)->number();
+}
+
+void Part::setNumber(int v, const Fraction& tick)
+{
+ instrument(tick)->setNumber(v);
+}
+
+String Part::transposition(const Fraction& tick) const
+{
+ return instrument(tick)->transposition();
+}
+
+void Part::setTransposition(const String& s, const Fraction& tick)
+{
+ instrument(tick)->setTransposition(s);
+}
+
//---------------------------------------------------------
// setPlainLongName
//---------------------------------------------------------
@@ -832,6 +851,25 @@ Fraction Part::currentHarpDiagramTick(const Fraction& tick) const
return Fraction::fromTicks(i->first);
}
+String Part::partName() const
+{
+ const Instrument* i = instrument();
+ String fullName = i->longName();
+
+ const String& transp = i->transposition();
+ if (!transp.empty()) {
+ //: For instrument transposition, e.g. Horn in F
+ fullName += u" " + muse::mtrc("notation", "in") + u" " + transp;
+ }
+
+ int n = number();
+ if (n != 0) {
+ fullName += u" " + String::number(n);
+ }
+
+ return fullName;
+}
+
bool Part::isVisible() const
{
return m_show;
diff --git a/src/engraving/dom/part.h b/src/engraving/dom/part.h
index c40de9303933a..c87d7de45b020 100644
--- a/src/engraving/dom/part.h
+++ b/src/engraving/dom/part.h
@@ -103,6 +103,12 @@ class Part final : public EngravingObject
void setLongNameAll(const String& s); // For all instruments in _instruments
void setShortNameAll(const String& s); // For all instruments in _instruments
+ int number(const Fraction& tick = { -1, 1 }) const;
+ void setNumber(int v, const Fraction& tick = { -1, 1 });
+
+ String transposition(const Fraction& tick = { -1, 1 }) const;
+ void setTransposition(const String& s, const Fraction& tick = { -1, 1 });
+
void setPlainLongName(const String& s);
void setPlainShortName(const String& s);
void setPlainLongNameAll(const String& s);
@@ -153,8 +159,8 @@ class Part final : public EngravingObject
HarpPedalDiagram* prevHarpDiagram(const Fraction&) const;
Fraction currentHarpDiagramTick(const Fraction&) const;
- String partName() const { return m_partName; }
- void setPartName(const String& s) { m_partName = s; }
+ String partName() const;
+
int color() const { return m_color; }
void setColor(int value) { m_color = value; }
@@ -196,7 +202,6 @@ class Part final : public EngravingObject
private:
friend class read206::Read206;
- String m_partName; ///< used in tracklist (mixer)
InstrumentList m_instruments;
std::vector m_staves;
muse::ID m_id = INVALID_ID; ///< used for MusicXML import
diff --git a/src/engraving/dom/stafflabel.h b/src/engraving/dom/stafflabel.h
new file mode 100644
index 0000000000000..4f054ff88c0b3
--- /dev/null
+++ b/src/engraving/dom/stafflabel.h
@@ -0,0 +1,154 @@
+/*
+ * SPDX-License-Identifier: GPL-3.0-only
+ * MuseScore-Studio-CLA-applies
+ *
+ * MuseScore Studio
+ * Music Composition & Notation
+ *
+ * Copyright (C) 2021 MuseScore Limited
+ *
+ * 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 .
+ */
+#pragma once
+
+#include "global/types/string.h"
+
+namespace mu::engraving {
+class StaffLabel
+{
+public:
+ StaffLabel() = default;
+ StaffLabel(const muse::String& longName, const muse::String& shortName)
+ : m_longName(longName), m_shortName(shortName) {}
+
+ bool operator==(const StaffLabel& i) const { return m_longName == i.m_longName && m_shortName == i.m_shortName; }
+
+ const muse::String& longName() const { return m_longName; }
+ const muse::String& shortName() const { return m_shortName; }
+ void setLongName(const muse::String& s) { m_longName = s; }
+ void setShortName(const muse::String& s) { m_shortName = s; }
+
+ bool empty() const { return m_longName.empty() && m_shortName.empty(); }
+
+private:
+ muse::String m_longName;
+ muse::String m_shortName;
+};
+
+class InstrumentLabel : public StaffLabel
+{
+public:
+ bool operator==(const InstrumentLabel& i) const;
+
+ int number() const { return m_number; }
+ void setNumber(int v) { m_number = v; }
+
+ const muse::String& transposition() const { return m_transposition; }
+ void setTransposition(const muse::String& s) { m_transposition = s; }
+
+ bool showNumberLong() const { return m_showNumberLong; }
+ void setShowNumberLong(bool v) { m_showNumberLong = v; }
+
+ bool showNumberShort() const { return m_showNumberShort; }
+ void setShowNumberShort(bool v) { m_showNumberShort = v; }
+
+ bool showTranspositionLong() const { return m_showTranspositionLong; }
+ void setShowTranspositionLong(bool v) { m_showTranspositionLong = v; }
+
+ bool showTranspositionShort() const { return m_showTranspositionShort; }
+ void setShowTranspositionShort(bool v) { m_showTranspositionShort = v; }
+
+ bool useCustomName() const { return m_useCustomName; }
+ void setUseCustomName(bool v) { m_useCustomName = v; }
+
+ muse::String customNameLong() const { return m_customNameLong; }
+ void setCustomNameLong(const muse::String& v) { m_customNameLong = v; }
+
+ muse::String customNameShort() const { return m_customNameShort; }
+ void setCustomNameShort(const muse::String& v) { m_customNameShort = v; }
+
+ bool allowGroupName() const { return m_allowGroupName; }
+ void setAllowGroupName(bool v) { m_allowGroupName = v; }
+
+ muse::String customNameLongGroup() const { return m_customNameLongGroup; }
+ void setCustomNameLongGroup(const muse::String& v) { m_customNameLongGroup = v; }
+
+ muse::String customNameShortGroup() const { return m_customNameShortGroup; }
+ void setCustomNameShortGroup(const muse::String& v) { m_customNameShortGroup = v; }
+
+ bool useCustomGroupName() const { return m_useCustomGroupName; }
+ void setUseCustomGroupName(bool v) { m_useCustomGroupName = v; }
+
+ muse::String customNameLongIndividual() const { return m_customNameLongIndividual; }
+ void setCustomNameLongIndividual(const muse::String& v) { m_customNameLongIndividual = v; }
+
+ muse::String customNameShortIndividual() const { return m_customNameShortIndividual; }
+ void setCustomNameShortIndividual(const muse::String& v) { m_customNameShortIndividual = v; }
+
+ bool useCustomIndividualName() const { return m_useCustomIndividualName; }
+ void setUseCustomIndividualName(bool v) { m_useCustomIndividualName = v; }
+
+ bool empty() const;
+
+private:
+ int m_number = 0;
+ bool m_showNumberLong = true;
+ bool m_showNumberShort = true;
+
+ muse::String m_transposition;
+ bool m_showTranspositionLong = true;
+ bool m_showTranspositionShort = true;
+
+ bool m_useCustomName = false;
+ muse::String m_customNameLong;
+ muse::String m_customNameShort;
+
+ bool m_allowGroupName = true;
+
+ muse::String m_customNameLongGroup;
+ muse::String m_customNameShortGroup;
+ bool m_useCustomGroupName = false;
+
+ muse::String m_customNameLongIndividual;
+ muse::String m_customNameShortIndividual;
+ bool m_useCustomIndividualName = false;
+};
+
+inline bool InstrumentLabel::operator==(const InstrumentLabel& i) const
+{
+ return StaffLabel::operator==(i)
+ && m_number == i.m_number
+ && m_transposition == i.m_transposition
+ && m_showNumberLong == i.m_showNumberLong
+ && m_showNumberShort == i.m_showNumberShort
+ && m_useCustomName == i.m_useCustomName
+ && m_customNameLong == i.m_customNameLong
+ && m_customNameShort == i.m_customNameShort
+ && m_showTranspositionLong == i.m_showTranspositionLong
+ && m_showTranspositionShort == i.m_showTranspositionShort
+ && m_allowGroupName == i.m_allowGroupName
+ && m_customNameLongGroup == i.m_customNameLongGroup
+ && m_customNameShortGroup == i.m_customNameShortGroup
+ && m_useCustomGroupName == i.m_useCustomGroupName
+ && m_customNameLongIndividual == i.m_customNameLongIndividual
+ && m_customNameShortIndividual == i.m_customNameShortIndividual
+ && m_useCustomIndividualName == i.m_useCustomIndividualName;
+}
+
+inline bool InstrumentLabel::empty() const
+{
+ static const InstrumentLabel EMPTY_LABEL = InstrumentLabel();
+
+ return *this == EMPTY_LABEL;
+}
+}
diff --git a/src/engraving/dom/staffname.h b/src/engraving/dom/staffname.h
deleted file mode 100644
index 93c8c7c8e38c5..0000000000000
--- a/src/engraving/dom/staffname.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-3.0-only
- * MuseScore-Studio-CLA-applies
- *
- * MuseScore Studio
- * Music Composition & Notation
- *
- * Copyright (C) 2021 MuseScore Limited
- *
- * 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 .
- */
-#pragma once
-
-#include "global/types/string.h"
-
-namespace mu::engraving {
-class StaffName
-{
-public:
- StaffName() = default;
- StaffName(const muse::String& longName, const muse::String& shortName)
- : m_longName(longName), m_shortName(shortName) {}
-
- bool operator==(const StaffName& i) const { return m_longName == i.m_longName && m_shortName == i.m_shortName; }
-
- const muse::String& longName() const { return m_longName; }
- const muse::String& shortName() const { return m_shortName; }
- void setLongName(const muse::String& s) { m_longName = s; }
- void setShortName(const muse::String& s) { m_shortName = s; }
-
- bool empty() const { return m_longName.empty() && m_shortName.empty(); }
-
-private:
- muse::String m_longName;
- muse::String m_shortName;
-};
-}
diff --git a/src/engraving/dom/stafftype.cpp b/src/engraving/dom/stafftype.cpp
index c2167e861d798..72d81444e02d5 100644
--- a/src/engraving/dom/stafftype.cpp
+++ b/src/engraving/dom/stafftype.cpp
@@ -177,7 +177,7 @@ bool StaffType::operator==(const StaffType& st) const
equal &= (m_group == st.m_group);
equal &= (m_xmlName == st.m_xmlName);
equal &= (m_staffTypeName == st.m_staffTypeName);
- equal &= (m_staffName == st.m_staffName);
+ equal &= (m_staffLabel == st.m_staffLabel);
equal &= (m_userMag == st.m_userMag);
equal &= (m_yoffset == st.m_yoffset);
equal &= (m_small == st.m_small);
diff --git a/src/engraving/dom/stafftype.h b/src/engraving/dom/stafftype.h
index 6bf6c86632ae7..36e09f5946da7 100644
--- a/src/engraving/dom/stafftype.h
+++ b/src/engraving/dom/stafftype.h
@@ -27,7 +27,7 @@
#include "draw/types/font.h"
#include "engravingitem.h"
-#include "staffname.h"
+#include "stafflabel.h"
#include "../types/types.h"
@@ -180,11 +180,12 @@ class StaffType
void setXmlName(const String& val) { m_xmlName = val; }
String translatedGroupName() const;
- const StaffName& staffName() const { return m_staffName; }
- const String& longName() const { return m_staffName.longName(); }
- const String& shortName() const { return m_staffName.shortName(); }
- void setLongName(const String& s) { m_staffName.setLongName(s); }
- void setShortName(const String& s) { m_staffName.setShortName(s); }
+ const StaffLabel& staffLabel() const { return m_staffLabel; }
+ StaffLabel& staffLabel() { return m_staffLabel; }
+ const String& longName() const { return m_staffLabel.longName(); }
+ const String& shortName() const { return m_staffLabel.shortName(); }
+ void setLongName(const String& s) { m_staffLabel.setLongName(s); }
+ void setShortName(const String& s) { m_staffLabel.setShortName(s); }
void setLines(int val) { m_lines = val; }
int lines() const { return m_lines; }
@@ -335,7 +336,7 @@ class StaffType
String m_xmlName; // the name used to reference this preset in instruments.xml
String m_staffTypeName; // user visible name
- StaffName m_staffName;
+ StaffLabel m_staffLabel;
double m_userMag = 1.0; // allowed 0.1 - 10.0
Spatium m_yoffset;
diff --git a/src/engraving/dom/system.cpp b/src/engraving/dom/system.cpp
index 41139edfd04a1..53a99fb1a34cf 100644
--- a/src/engraving/dom/system.cpp
+++ b/src/engraving/dom/system.cpp
@@ -70,6 +70,7 @@ namespace mu::engraving {
SysStaff::~SysStaff()
{
+ delete groupName;
delete instrumentName;
delete individualStaffName;
}
@@ -476,15 +477,19 @@ void System::add(EngravingItem* el)
switch (el->type()) {
case ElementType::INSTRUMENT_NAME:
-// LOGD(" staffIdx %d, staves %d", el->staffIdx(), _staves.size());
- if (toInstrumentName(el)->instrumentNameRole() == InstrumentNameRole::PART) {
- m_staves[el->staffIdx()]->instrumentName = toInstrumentName(el);
+ {
+ InstrumentName* n = toInstrumentName(el);
+ SysStaff* sysStaff = m_staves[n->staffIdx()];
+ if (n->instrumentNameRole() == InstrumentNameRole::GROUP) {
+ sysStaff->groupName = n;
+ } else if (n->instrumentNameRole() == InstrumentNameRole::PART) {
+ sysStaff->instrumentName = n;
} else {
- m_staves[el->staffIdx()]->individualStaffName = toInstrumentName(el);
+ sysStaff->individualStaffName = n;
}
- toInstrumentName(el)->setSysStaff(m_staves[el->staffIdx()]);
+ n->setSysStaff(sysStaff);
break;
-
+ }
case ElementType::BEAM:
score()->addElement(el);
break;
@@ -566,14 +571,20 @@ void System::remove(EngravingItem* el)
{
switch (el->type()) {
case ElementType::INSTRUMENT_NAME:
+ {
// TODO: I'm pretty sure that this gets leadked. Needs fixing.
- if (toInstrumentName(el)->instrumentNameRole() == InstrumentNameRole::PART) {
- m_staves[el->staffIdx()]->instrumentName = nullptr;
+ InstrumentName* n = toInstrumentName(el);
+ SysStaff* sysStaff = m_staves[n->staffIdx()];
+ if (n->instrumentNameRole() == InstrumentNameRole::GROUP) {
+ sysStaff->groupName = nullptr;
+ } else if (n->instrumentNameRole() == InstrumentNameRole::PART) {
+ sysStaff->instrumentName = nullptr;
} else {
- m_staves[el->staffIdx()]->individualStaffName = nullptr;
+ sysStaff->individualStaffName = nullptr;
}
- toInstrumentName(el)->setSysStaff(0);
+ n->setSysStaff(nullptr);
break;
+ }
case ElementType::BEAM:
score()->removeElement(el);
break;
@@ -736,14 +747,22 @@ void System::scanElements(std::function func)
for (const SysStaff* st : m_staves) {
if (st->show()) {
+ if (InstrumentName* n = st->groupName) {
+ func(n);
+ }
if (InstrumentName* t = st->instrumentName) {
func(t);
}
if (InstrumentName* n = st->individualStaffName) {
func(n);
}
- } else if (InstrumentName* n = st->instrumentName; n && n->effectiveStaffIdx() != muse::nidx) {
- func(n);
+ } else {
+ if (InstrumentName* n = st->groupName; n && n->effectiveStaffIdx() != muse::nidx) {
+ func(n);
+ }
+ if (InstrumentName* n = st->instrumentName; n && n->effectiveStaffIdx() != muse::nidx) {
+ func(n);
+ }
}
}
@@ -1169,6 +1188,22 @@ staff_idx_t System::firstVisibleSysStaffOfPart(const Part* part) const
return muse::nidx; // No visible staves on this part.
}
+staff_idx_t System::firstVisibleSysStaffWithInstrument(const String& instrumentId, staff_idx_t startFrom)
+{
+ Fraction tick = first()->tick();
+ for (staff_idx_t idx = startFrom; idx < m_staves.size(); ++idx) {
+ Part* part = score()->staff(idx)->part();
+ if (part->instrument(tick)->id() == instrumentId) {
+ staff_idx_t firstVisOfPart = firstVisibleSysStaffOfPart(part);
+ if (firstVisOfPart != muse::nidx) {
+ return firstVisOfPart;
+ }
+ }
+ }
+
+ return muse::nidx;
+}
+
//---------------------------------------------------------
// lastSysStaffOfPart
//---------------------------------------------------------
@@ -1215,4 +1250,19 @@ std::vector System::visibleStavesOfPart(const Part* part) const
return result;
}
+
+std::vector System::visiblePartsOfGroup(staff_idx_t start, staff_idx_t end) const
+{
+ std::vector result;
+
+ for (staff_idx_t idx = start; idx < end;) {
+ Part* part = score()->staff(idx)->part();
+ if (visibleStavesOfPart(part).size() > 0) {
+ result.push_back(part);
+ }
+ idx += part->nstaves();
+ }
+
+ return result;
+}
}
diff --git a/src/engraving/dom/system.h b/src/engraving/dom/system.h
index e1adaf34bd351..5f4007c6f136b 100644
--- a/src/engraving/dom/system.h
+++ b/src/engraving/dom/system.h
@@ -28,6 +28,7 @@
*/
#include "engravingitem.h"
+#include "stafflabel.h"
namespace mu::engraving {
class Box;
@@ -50,6 +51,7 @@ class SysStaff
SysStaff() {}
~SysStaff();
+ InstrumentName* groupName = nullptr;
InstrumentName* instrumentName = nullptr;
InstrumentName* individualStaffName = nullptr;
@@ -191,9 +193,11 @@ class System final : public EngravingItem
staff_idx_t firstSysStaffOfPart(const Part* part) const;
staff_idx_t firstVisibleSysStaffOfPart(const Part* part) const;
+ staff_idx_t firstVisibleSysStaffWithInstrument(const String& instrumentId, staff_idx_t startFrom);
staff_idx_t lastSysStaffOfPart(const Part* part) const;
staff_idx_t lastVisibleSysStaffOfPart(const Part* part) const;
std::vector visibleStavesOfPart(const Part* part) const;
+ std::vector visiblePartsOfGroup(staff_idx_t start, staff_idx_t end) const;
#ifndef ENGRAVING_NO_ACCESSIBILITY
AccessibleItemPtr createAccessible() override;
@@ -220,19 +224,27 @@ class System final : public EngravingItem
void setUseLongNames(bool v) { m_useLongNames = v; }
double instrumentNameOffset() const { return m_instrumentNameOffset; }
void setInstrumentNameOffset(double v) { m_instrumentNameOffset = v; }
- double staffNamesWidth() const { return m_staffNamesWidth; }
- void setStaffNamesWidth(double v) { m_staffNamesWidth = v; }
- double instrumentNamesWidth() const { return m_instrumentNamesWidth; }
- void setInstrumentNamesWidth(double v) { m_instrumentNamesWidth = v; }
+
+ double firstColumnWidth() const { return m_firstColumnWidth; }
+ void setFirstColumnWidth(double v) { m_firstColumnWidth = v; }
+ double secondColumnWidth() const { return m_secondColumnWidth; }
+ void setSecondColumnWidth(double v) { m_secondColumnWidth = v; }
double totalNamesWidth() const { return m_totalNamesWidth; }
void setTotalNamesWidth(double v) { m_totalNamesWidth = v; }
+ const std::unordered_map& partsWithGroupName() const { return m_partsWithGroupName; }
+ void addPartWithGroupNames(Part* p, InstrumentName* n) { m_partsWithGroupName.emplace(p, n); }
+ void clearPartsWithGroupNames() { m_partsWithGroupName.clear(); }
+
private:
bool m_useLongNames = false;
double m_instrumentNameOffset = 0.0;
- double m_staffNamesWidth = 0.0;
- double m_instrumentNamesWidth = 0.0;
+
+ double m_firstColumnWidth = 0.0;
+ double m_secondColumnWidth = 0.0;
double m_totalNamesWidth = 0.0;
+
+ std::unordered_map m_partsWithGroupName;
};
DECLARE_LAYOUTDATA_METHODS(System)
diff --git a/src/engraving/editing/editpart.cpp b/src/engraving/editing/editpart.cpp
index 8dc41985abf53..c76caeefffa75 100644
--- a/src/engraving/editing/editpart.cpp
+++ b/src/engraving/editing/editpart.cpp
@@ -122,19 +122,16 @@ void SetSoloist::redo(EditData*)
// ChangePart
//---------------------------------------------------------
-ChangePart::ChangePart(Part* _part, Instrument* i, const String& s)
+ChangePart::ChangePart(Part* _part, Instrument* i)
{
instrument = i;
part = _part;
- partName = s;
}
void ChangePart::flip(EditData*)
{
Instrument* oi = part->instrument(); //tick?
- String s = part->partName();
part->setInstrument(instrument);
- part->setPartName(partName);
part->updateHarmonyChannels(false);
@@ -148,7 +145,6 @@ void ChangePart::flip(EditData*)
score->setLayoutAll();
- partName = s;
instrument = oi;
}
@@ -192,6 +188,48 @@ void ChangeInstrumentShort::flip(EditData*)
part->score()->setLayoutAll();
}
+//---------------------------------------------------------
+// ChangeInstrumentLong
+//---------------------------------------------------------
+
+ChangeInstrumentGroupOptions::ChangeInstrumentGroupOptions(const Fraction& _tick, Part* p, bool useCustom, const String& longName,
+ const String& shortName)
+ : part(p), tick(_tick), useCustom(useCustom), longName(longName), shortName(shortName)
+{
+}
+
+void ChangeInstrumentGroupOptions::flip(EditData*)
+{
+ InstrumentLabel& label = part->instrument(tick)->instrumentLabel();
+
+ bool curUseCustom = label.useCustomGroupName();
+ const String& curLong = label.customNameLongGroup();
+ const String& curShort = label.customNameShortGroup();
+
+ label.setUseCustomGroupName(useCustom);
+ label.setCustomNameLongGroup(longName);
+ label.setCustomNameShortGroup(shortName);
+
+ useCustom = curUseCustom;
+ longName = curLong;
+ shortName = curShort;
+
+ part->score()->setLayoutAll();
+}
+
+ChangeInstrumentNumber::ChangeInstrumentNumber(const Fraction& _tick, Part* p, int v)
+ : part(p), tick(_tick), number(v)
+{
+}
+
+void ChangeInstrumentNumber::flip(EditData*)
+{
+ int v = part->number(tick);
+ part->setNumber(number, tick);
+ number = v;
+ part->score()->setLayoutAll();
+}
+
//---------------------------------------------------------
// ChangeDrumset
//---------------------------------------------------------
@@ -300,15 +338,13 @@ static InstrumentChange* findInstrumentChange(Score* score, const Part* part, co
}
void EditPart::replacePartInstrument(Score* score, Part* part, const Instrument& newInstrument,
- const StaffType* newStaffType, const String& partName)
+ const StaffType* newStaffType)
{
if (!score || !part) {
return;
}
- // Change the part's instrument and name
- String newPartName = partName.isEmpty() ? newInstrument.trackName() : partName;
- score->undo(new ChangePart(part, new Instrument(newInstrument), newPartName));
+ score->undo(new ChangePart(part, new Instrument(newInstrument)));
// Update clefs and staff type for all staves in the part
for (staff_idx_t staffIdx = 0; staffIdx < part->nstaves(); ++staffIdx) {
@@ -407,6 +443,16 @@ void EditPart::setInstrumentAbbreviature(Score* score, Part* part, const Fractio
score->undo(new ChangeInstrumentShort(tick, part, abbreviature));
}
+void EditPart::setInstrumentGroupNameOptions(Score* score, Part* part, const Fraction& tick, bool useCustom, const String& longName,
+ const String& shortName)
+{
+ if (!score || !part) {
+ return;
+ }
+
+ score->undo(new ChangeInstrumentGroupOptions(tick, part, useCustom, longName, shortName));
+}
+
void EditPart::setStaffType(Score* score, Staff* staff, StaffTypes typeId)
{
if (!score || !staff) {
diff --git a/src/engraving/editing/editpart.h b/src/engraving/editing/editpart.h
index 98b94ab0c9cd9..5dea1274bfd9b 100644
--- a/src/engraving/editing/editpart.h
+++ b/src/engraving/editing/editpart.h
@@ -90,12 +90,11 @@ class ChangePart : public UndoCommand
Part* part = nullptr;
Instrument* instrument = nullptr;
- String partName;
void flip(EditData*) override;
public:
- ChangePart(Part*, Instrument*, const String& name);
+ ChangePart(Part*, Instrument*);
void cleanup(bool) override;
UNDO_TYPE(CommandType::ChangePart)
@@ -139,6 +138,44 @@ class ChangeInstrumentShort : public UndoCommand
UNDO_CHANGED_OBJECTS({ part })
};
+class ChangeInstrumentGroupOptions : public UndoCommand
+{
+ OBJECT_ALLOCATOR(engraving, ChangeInstrumentGroupOptions)
+
+ Part* part = nullptr;
+ Fraction tick;
+ bool useCustom;
+ String longName;
+ String shortName;
+
+ void flip(EditData*) override;
+
+public:
+ ChangeInstrumentGroupOptions(const Fraction&, Part*, bool, const String&, const String&);
+
+ UNDO_TYPE(CommandType::ChangeInstrumentGroupOptions)
+ UNDO_NAME("ChangeInstrumentGroupOptions")
+ UNDO_CHANGED_OBJECTS({ part })
+};
+
+class ChangeInstrumentNumber : public UndoCommand
+{
+ OBJECT_ALLOCATOR(engraving, ChangeInstrumentNumber)
+
+ Part* part = nullptr;
+ Fraction tick;
+ int number;
+
+ void flip(EditData*) override;
+
+public:
+ ChangeInstrumentNumber(const Fraction&, Part*, int v);
+
+ UNDO_TYPE(CommandType::ChangeInstrumentNumber)
+ UNDO_NAME("ChangeInstrumentNumber")
+ UNDO_CHANGED_OBJECTS({ part })
+};
+
class ChangeDrumset : public UndoCommand
{
OBJECT_ALLOCATOR(engraving, ChangeDrumset)
@@ -213,8 +250,7 @@ enum class StaffTypes : signed char;
class EditPart
{
public:
- static void replacePartInstrument(Score* score, Part* part, const Instrument& newInstrument, const StaffType* newStaffType = nullptr,
- const String& partName = String());
+ static void replacePartInstrument(Score* score, Part* part, const Instrument& newInstrument, const StaffType* newStaffType = nullptr);
static bool replaceInstrumentAtTick(Score* score, Part* part, const Fraction& tick, const Instrument& newInstrument);
@@ -223,6 +259,9 @@ class EditPart
static void setPartSharpFlat(Score* score, Part* part, PreferSharpFlat sharpFlat);
static void setInstrumentName(Score* score, Part* part, const Fraction& tick, const String& name);
static void setInstrumentAbbreviature(Score* score, Part* part, const Fraction& tick, const String& abbreviature);
+ static void setInstrumentGroupNameOptions(Score* score, Part* part, const Fraction& tick, bool useCustom, const String& longName,
+ const String& shortName);
+ static void setInstrumentCustomGroupAbbreviature(Score* score, Part* part, const Fraction& tick, const String& abbreviature);
static void setStaffType(Score* score, Staff* staff, StaffTypes typeId);
static void removeParts(Score* score, const std::vector& parts);
diff --git a/src/engraving/editing/undo.h b/src/engraving/editing/undo.h
index 3d9543a16484d..bd4cc0fa52390 100644
--- a/src/engraving/editing/undo.h
+++ b/src/engraving/editing/undo.h
@@ -65,6 +65,8 @@ enum class CommandType : signed char {
// Instruments
ChangeInstrumentShort,
ChangeInstrumentLong,
+ ChangeInstrumentGroupOptions,
+ ChangeInstrumentNumber,
ChangeInstrument,
ChangeDrumset,
diff --git a/src/engraving/rendering/score/systemheaderlayout.cpp b/src/engraving/rendering/score/systemheaderlayout.cpp
index b4bb720298b28..43d4a4e4b4c72 100644
--- a/src/engraving/rendering/score/systemheaderlayout.cpp
+++ b/src/engraving/rendering/score/systemheaderlayout.cpp
@@ -309,84 +309,138 @@ void SystemHeaderLayout::computeInstrumentNameOffset(System* system, LayoutConte
void SystemHeaderLayout::computeInstrumentNamesWidth(System* system, LayoutContext& ctx)
{
System::LayoutData* ldata = system->mutldata();
- ldata->setStaffNamesWidth(0.0);
- ldata->setInstrumentNamesWidth(0.0);
+ ldata->setFirstColumnWidth(0.0);
+ ldata->setSecondColumnWidth(0.0);
ldata->setTotalNamesWidth(0.0);
- std::set partsWithIndividualStaffNames;
+ std::unordered_set partsWithIndividualStaffNames;
+ std::unordered_set partsWithGroupNames;
- for (staff_idx_t staffIdx = 0; staffIdx < ctx.dom().nstaves(); ++staffIdx) {
- if (!ctx.dom().staff(staffIdx)->show()) {
- // We know that the staff is hidden in the entire score so safe to skip
- continue;
+ std::vector groupNames;
+ std::vector instrumentNames;
+
+ for (staff_idx_t staffIdx = 0; staffIdx < system->staves().size(); ++staffIdx) {
+ const SysStaff* sysStaff = system->staff(staffIdx);
+ const Staff* staff = ctx.dom().staff(staffIdx);
+
+ if (InstrumentName* groupName = sysStaff->groupName) {
+ if (ldata->useLongNames() && groupName->effectiveStaffIdx() == muse::nidx) {
+ // NOTE: We can only do this for long-name systems because they don't need to have
+ // the left barline aligned with the other systems across the page.
+ groupName->mutldata()->setIsSkipDraw(true);
+ continue;
+ }
+ groupName->mutldata()->setIsSkipDraw(false);
+
+ groupNames.push_back(groupName);
+ groupName->mutldata()->setColumn(1);
+ for (staff_idx_t groupIdx = groupName->staffIdx(); groupIdx < groupName->ldata()->endIdxOfGroup(); ++groupIdx) {
+ partsWithGroupNames.insert(ctx.dom().staff(groupIdx)->part());
+ }
+ ldata->setSecondColumnWidth(std::max(ldata->secondColumnWidth(), groupName->width()));
+ ldata->setTotalNamesWidth(std::max(ldata->totalNamesWidth(), groupName->width()));
}
- const SysStaff* staff = system->staff(staffIdx);
- if (ldata->useLongNames() && !staff->show()) {
- // NOTE: We can only do this for long-name systems because they don't need to have
- // the left barline aligned with the other systems across the page.
+ if (!staff->show()) {
+ // We know that the staff is hidden in the entire score so safe to skip
continue;
}
- if (InstrumentName* name = staff->individualStaffName) {
- TLayout::layoutInstrumentName(name, name->mutldata());
- ldata->setStaffNamesWidth(std::max(ldata->staffNamesWidth(), name->width()));
- ldata->setTotalNamesWidth(std::max(ldata->totalNamesWidth(), name->width()));
+ if (InstrumentName* name = sysStaff->individualStaffName) {
+ if (ldata->useLongNames() && !sysStaff->show()) {
+ // NOTE: We can only do this for long-name systems because they don't need to have
+ // the left barline aligned with the other systems across the page.
+ name->mutldata()->setIsSkipDraw(true);
+ continue;
+ }
+ name->mutldata()->setIsSkipDraw(false);
+
+ name->mutldata()->setColumn(0);
partsWithIndividualStaffNames.insert(ctx.dom().staff(staffIdx)->part());
+ ldata->setFirstColumnWidth(std::max(ldata->firstColumnWidth(), name->width()));
+ ldata->setTotalNamesWidth(std::max(ldata->totalNamesWidth(), name->width()));
}
}
- bool stackVertically = stackLabelsVertically(system);
+ for (staff_idx_t staffIdx = 0; staffIdx < system->staves().size(); ++staffIdx) {
+ const SysStaff* sysStaff = system->staff(staffIdx);
+ const Staff* staff = ctx.dom().staff(staffIdx);
+ Part* part = staff->part();
- for (staff_idx_t staffIdx = 0; staffIdx < ctx.dom().nstaves(); ++staffIdx) {
- Part* part = ctx.dom().staff(staffIdx)->part();
if (!part->show() || part->visibleStavesCount() == 0) {
// We know that the part is hidden in the entire score so safe to skip
continue;
}
- const SysStaff* staff = system->staff(staffIdx);
-
- InstrumentName* name = staff->instrumentName;
- if (!name) {
+ InstrumentName* instrName = sysStaff->instrumentName;
+ if (!instrName) {
continue;
}
- if (ldata->useLongNames() && name->effectiveStaffIdx() == muse::nidx) {
+ if (ldata->useLongNames() && instrName->effectiveStaffIdx() == muse::nidx) {
+ // NOTE: We can only do this for long-name systems because they don't need to have
+ // the left barline aligned with the other systems across the page.
+ instrName->mutldata()->setIsSkipDraw(true);
continue;
}
+ instrName->mutldata()->setIsSkipDraw(false);
- TLayout::layoutInstrumentName(name, name->mutldata());
- ldata->setInstrumentNamesWidth(std::max(ldata->instrumentNamesWidth(), name->width()));
- ldata->setTotalNamesWidth(std::max(ldata->totalNamesWidth(), name->width()));
+ instrumentNames.push_back(instrName);
+ ldata->setTotalNamesWidth(std::max(ldata->totalNamesWidth(), instrName->width()));
- if (stackVertically) {
- continue;
+ if (partsWithGroupNames.count(part)) {
+ instrName->mutldata()->setColumn(0);
+ ldata->setFirstColumnWidth(std::max(ldata->firstColumnWidth(), instrName->width()));
+ } else {
+ instrName->mutldata()->setColumn(1);
+ ldata->setSecondColumnWidth(std::max(ldata->secondColumnWidth(), instrName->width()));
}
+ }
- size_t visibleStaveCount = system->visibleStavesOfPart(part).size();
- if (ldata->useLongNames() && visibleStaveCount % 2 == 0) {
+ if (stackLabelsVertically(system)) {
+ return;
+ }
+
+ auto sumWidth = [&](InstrumentName* outerName, InstrumentName* innerName) {
+ AlignH staffNameAlign = innerName->position();
+ if (staffNameAlign == AlignH::LEFT || staffNameAlign == AlignH::JUSTIFY) {
+ return ldata->firstColumnWidth() + outerName->width() + ldata->instrumentNameOffset();
+ } else if (staffNameAlign == AlignH::HCENTER) {
+ double sumWidth = innerName->width() + outerName->width() + ldata->instrumentNameOffset();
+ double move = 0.5 * (ldata->firstColumnWidth() - innerName->width());
+ return sumWidth + move;
+ } else {
+ return innerName->width() + outerName->width() + ldata->instrumentNameOffset();
+ }
+ };
+
+ for (InstrumentName* groupName : groupNames) {
+ staff_idx_t startStaff = groupName->staffIdx();
+ staff_idx_t endStaff = groupName->ldata()->endIdxOfGroup();
+ if (ldata->useLongNames() && system->visiblePartsOfGroup(startStaff, endStaff).size() % 2 == 0) {
continue;
}
+ for (staff_idx_t idx = startStaff; idx < endStaff; ++idx) {
+ if (InstrumentName* n = system->staff(idx)->individualStaffName; n && !n->ldata()->isSkipDraw()) {
+ ldata->setTotalNamesWidth(std::max(ldata->totalNamesWidth(), sumWidth(groupName, n)));
+ }
+ if (InstrumentName* n = system->staff(idx)->instrumentName; n && !n->ldata()->isSkipDraw()) {
+ ldata->setTotalNamesWidth(std::max(ldata->totalNamesWidth(), sumWidth(groupName, n)));
+ }
+ }
+ }
- if (muse::contains(partsWithIndividualStaffNames, part)) {
- staff_idx_t startStaff = system->firstSysStaffOfPart(part);
- staff_idx_t endStaff = startStaff + part->nstaves();
- for (staff_idx_t idx = startStaff; idx < endStaff; ++idx) {
- if (InstrumentName* staffName = system->staff(idx)->individualStaffName) {
- double sumWidth = 0.0;
- AlignH staffNameAlign = staffName->position();
- if (staffNameAlign == AlignH::LEFT || staffNameAlign == AlignH::JUSTIFY) {
- sumWidth = ldata->staffNamesWidth() + name->width() + ldata->instrumentNameOffset();
- } else if (staffNameAlign == AlignH::HCENTER) {
- sumWidth = staffName->width() + name->width() + ldata->instrumentNameOffset();
- double move = 0.5 * (ldata->staffNamesWidth() - staffName->width());
- sumWidth += move;
- } else {
- sumWidth = staffName->width() + name->width() + ldata->instrumentNameOffset();
- }
- ldata->setTotalNamesWidth(std::max(ldata->totalNamesWidth(), sumWidth));
- }
+ for (InstrumentName* instrName : instrumentNames) {
+ if (instrName->ldata()->column() == 0) {
+ continue;
+ }
+ Part* part = ctx.dom().staff(instrName->staffIdx())->part();
+ if (ldata->useLongNames() && system->visibleStavesOfPart(part).size() % 2 == 0) {
+ continue;
+ }
+ for (staff_idx_t idx : part->staveIdxList()) {
+ if (InstrumentName* n = system->staff(idx)->individualStaffName; n && !n->ldata()->isSkipDraw()) {
+ ldata->setTotalNamesWidth(std::max(ldata->totalNamesWidth(), sumWidth(instrName, n)));
}
}
}
@@ -413,6 +467,7 @@ void SystemHeaderLayout::setInstrumentNamesVerticalPos(System* system, LayoutCon
size_t nstaves = p->nstaves();
SysStaff* s = system->staff(staffIdx);
+
InstrumentName* t = s->instrumentName;
if (!t || t->effectiveStaffIdx() == muse::nidx) {
staffIdx += nstaves;
@@ -435,37 +490,138 @@ void SystemHeaderLayout::setInstrumentNamesVerticalPos(System* system, LayoutCon
y1 = topSt->bbox().top();
y2 = bottomSt->bbox().bottom();
t->mutldata()->setPosY(0.5 * (y1 + y2) - yCenter);
+
+ staffIdx += nstaves;
+ continue;
+ }
+
+ if (visibleStavesCount % 2 == 0) {
+ SysStaff* staffAboveMid = system->staff(visibleStavesOfPart[visibleStavesCount / 2 - 1]);
+ SysStaff* staffBelowMid = system->staff(visibleStavesOfPart[visibleStavesCount / 2]);
+ y1 = staffAboveMid->bbox().top();
+ y2 = staffBelowMid->bbox().bottom();
+ t->mutldata()->setPosY(0.5 * (y1 + y2) - yCenter);
+
+ staffIdx += nstaves;
+ continue;
+ }
+
+ SysStaff* midStaff = system->staff(visibleStavesOfPart[visibleStavesCount / 2]);
+ y1 = midStaff->bbox().top();
+ y2 = midStaff->bbox().bottom();
+ if (InstrumentName* staffName = midStaff->individualStaffName) {
+ if (stackVertically || t->ldata()->column() == 0) {
+ double lineSpacing = t->lineSpacing();
+ double instrNameBottom = t->ldata()->blocks.back().y();
+ double centerY = 0.5 * (midStaff->bbox().top() + midStaff->bbox().bottom());
+ t->mutldata()->setPosY(centerY - instrNameBottom - 0.25 * lineSpacing);
+ double staffNameTop = staffName->ldata()->blocks.front().y();
+ staffName->mutldata()->setPosY(centerY - staffNameTop + 0.75 * lineSpacing);
+ } else if (staffName->ldata()->rows() == 1 && t->ldata()->rows() == 1) {
+ t->mutldata()->setPosY(staffName->y());
+ } else {
+ t->mutldata()->setPosY(0.5 * (y1 + y2) - yCenter);
+ }
+ }
+
+ staffIdx += nstaves;
+ }
+
+ for (staff_idx_t staffIdx = 0; staffIdx < system->staves().size(); ++staffIdx) {
+ InstrumentName* groupName = system->staff(staffIdx)->groupName;
+ if (!groupName || groupName->effectiveStaffIdx() == muse::nidx) {
+ continue;
+ }
+
+ const RectF& bbox = groupName->ldata()->bbox();
+ double yCenter = 0.5 * (bbox.bottom() + bbox.top());
+
+ std::vector visibleParts = system->visiblePartsOfGroup(staffIdx, groupName->ldata()->endIdxOfGroup());
+ size_t visiblePartsCount = visibleParts.size();
+ DO_ASSERT(visiblePartsCount > 0);
+
+ double y1 = 0;
+ double y2 = 0;
+
+ if (visiblePartsCount % 2 == 0) {
+ Part* partAboveMid = visibleParts[visiblePartsCount / 2 - 1];
+ staff_idx_t staffIdxAboveMid = system->lastVisibleSysStaffOfPart(partAboveMid);
+ DO_ASSERT(staffIdxAboveMid != muse::nidx);
+ SysStaff* staffAboveMid = system->staff(staffIdxAboveMid);
+
+ Part* partBelowMid = visibleParts[visiblePartsCount / 2];
+ staff_idx_t staffIdxBelowMid = system->firstVisibleSysStaffOfPart(partBelowMid);
+ DO_ASSERT(staffIdxBelowMid != muse::nidx);
+ SysStaff* staffBelowMid = system->staff(staffIdxBelowMid);
+
+ y1 = staffAboveMid->bbox().top();
+ y2 = staffBelowMid->bbox().bottom();
+ groupName->mutldata()->setPosY(0.5 * (y1 + y2) - yCenter);
+
+ continue;
+ }
+
+ Part* midPart = visibleParts[visiblePartsCount / 2];
+ InstrumentName* instrName = system->staff(*midPart->staveIdxList().begin())->instrumentName;
+ std::vector visibleStaves = system->visibleStavesOfPart(midPart);
+ size_t visibleStavesCount = visibleStaves.size();
+
+ if (visibleStavesCount % 2) {
+ SysStaff* midStaff = system->staff(visibleStaves[visibleStavesCount / 2]);
+ y1 = midStaff->bbox().top();
+ y2 = midStaff->bbox().bottom();
} else {
- if (visibleStavesCount % 2) {
- SysStaff* midStaff = system->staff(visibleStavesOfPart[visibleStavesCount / 2]);
- y1 = midStaff->bbox().top();
- y2 = midStaff->bbox().bottom();
- if (InstrumentName* staffName = midStaff->individualStaffName) {
- if (stackVertically) {
- double lineSpacing = t->fontMetrics().lineSpacing();
- double instrNameBottom = t->ldata()->blocks.back().y();
- double centerY = 0.5 * (midStaff->bbox().top() + midStaff->bbox().bottom());
- t->mutldata()->setPosY(centerY - instrNameBottom - 0.25 * lineSpacing);
- double staffNameTop = staffName->ldata()->blocks.front().y();
- staffName->mutldata()->setPosY(centerY - staffNameTop + 0.75 * lineSpacing);
- } else if (staffName->ldata()->rows() == 1 && t->ldata()->rows() == 1) {
- t->mutldata()->setPosY(staffName->y());
- } else {
- t->mutldata()->setPosY(0.5 * (y1 + y2) - yCenter);
- }
+ SysStaff* staffAboveMid = system->staff(visibleStaves[visibleStavesCount / 2 - 1]);
+ SysStaff* staffBelowMid = system->staff(visibleStaves[visibleStavesCount / 2]);
+ y1 = staffAboveMid->bbox().top();
+ y2 = staffBelowMid->bbox().bottom();
+ }
+
+ if (instrName) {
+ if (stackVertically) {
+ double lineSpacing = groupName->lineSpacing();
+ double groupNameBottom = groupName->ldata()->blocks.back().y();
+ if (visibleStavesCount % 2 && system->staff(visibleStaves[visibleStavesCount / 2])->individualStaffName) {
+ groupName->mutldata()->setPosY(instrName->y() - groupNameBottom - lineSpacing);
} else {
- t->mutldata()->setPosY(0.5 * (y1 + y2) - yCenter);
+ double centerY = 0.5 * (y1 + y2);
+ groupName->mutldata()->setPosY(centerY - groupNameBottom - 0.25 * lineSpacing);
+ double instrNameTop = instrName->ldata()->blocks.front().y();
+ instrName->mutldata()->setPosY(centerY - instrNameTop + 0.75 * lineSpacing);
}
+ } else if (instrName->ldata()->rows() == 1 && groupName->ldata()->rows() == 1) {
+ groupName->mutldata()->setPosY(instrName->y());
} else {
- SysStaff* staffAboveMid = system->staff(visibleStavesOfPart[visibleStavesCount / 2 - 1]);
- SysStaff* staffBelowMid = system->staff(visibleStavesOfPart[visibleStavesCount / 2]);
- y1 = staffAboveMid->bbox().top();
- y2 = staffBelowMid->bbox().bottom();
- t->mutldata()->setPosY(0.5 * (y1 + y2) - yCenter);
+ groupName->mutldata()->setPosY(0.5 * (y1 + y2) - yCenter);
}
+
+ continue;
}
- staffIdx += nstaves;
+ if (visibleStavesCount % 2 == 0) {
+ groupName->mutldata()->setPosY(0.5 * (y1 + y2) - yCenter);
+ continue;
+ }
+
+ SysStaff* midStaff = system->staff(visibleStaves[visibleStavesCount / 2]);
+ InstrumentName* staffName = midStaff->individualStaffName;
+ if (!staffName) {
+ groupName->mutldata()->setPosY(0.5 * (y1 + y2) - yCenter);
+ continue;
+ }
+
+ if (stackVertically) {
+ double lineSpacing = groupName->lineSpacing();
+ double groupNameBottom = groupName->ldata()->blocks.back().y();
+ double centerY = 0.5 * (y1 + y2);
+ groupName->mutldata()->setPosY(centerY - groupNameBottom - 0.25 * lineSpacing);
+ double staffNameTop = staffName->ldata()->blocks.front().y();
+ staffName->mutldata()->setPosY(centerY - staffNameTop + 0.75 * lineSpacing);
+ } else if (staffName->ldata()->rows() == 1 && groupName->ldata()->rows() == 1) {
+ groupName->mutldata()->setPosY(staffName->y());
+ } else {
+ groupName->mutldata()->setPosY(0.5 * (y1 + y2) - yCenter);
+ }
}
}
@@ -479,78 +635,141 @@ void SystemHeaderLayout::setInstrumentNamesHorizontalPos(System* system)
System::LayoutData* ldata = system->mutldata();
double totalNamesWidth = ldata->totalNamesWidth();
- double staffNamesWidth = ldata->staffNamesWidth();
+ double firstColumnWidth = ldata->firstColumnWidth();
+ InstrumentNamesAlign align = system->style().styleV(
+ ldata->useLongNames() ? Sid::instrumentNamesAlignLong : Sid::instrumentNamesAlignShort).value();
+
+ auto placeFirstColumnName = [&](InstrumentName* name) {
+ const RectF& bbox = name->ldata()->bbox();
+ if (align == InstrumentNamesAlign::CENTER_CENTER) {
+ name->mutldata()->setPosX(totalNamesWidth * .5 - (bbox.right() + bbox.left()) * .5);
+ } else {
+ switch (name->position()) {
+ case AlignH::JUSTIFY:
+ case AlignH::LEFT:
+ name->mutldata()->setPosX(totalNamesWidth - firstColumnWidth - bbox.left());
+ break;
+ case AlignH::HCENTER:
+ name->mutldata()->setPosX(totalNamesWidth - 0.5 * firstColumnWidth - 0.5 * (bbox.right() + bbox.left()));
+ break;
+ case AlignH::RIGHT:
+ name->mutldata()->setPosX(totalNamesWidth - bbox.right());
+ }
+ }
+ };
for (const SysStaff* s : system->staves()) {
- if (InstrumentName* t = s->individualStaffName) {
- bool longName = t->instrumentNameType() == InstrumentNameType::LONG;
- InstrumentNamesAlign align
- = t->style().styleV(longName ? Sid::instrumentNamesAlignLong : Sid::instrumentNamesAlignShort).value();
- const RectF& bbox = t->ldata()->bbox();
- if (align == InstrumentNamesAlign::CENTER_CENTER) {
- t->mutldata()->setPosX(totalNamesWidth * .5 - (bbox.right() + bbox.left()) * .5);
- } else {
- switch (t->position()) {
- case AlignH::JUSTIFY:
- case AlignH::LEFT:
- t->mutldata()->setPosX(totalNamesWidth - staffNamesWidth - bbox.left());
- break;
- case AlignH::HCENTER:
- t->mutldata()->setPosX(totalNamesWidth - 0.5 * staffNamesWidth - 0.5 * (bbox.right() + bbox.left()));
- break;
- case AlignH::RIGHT:
- t->mutldata()->setPosX(totalNamesWidth - bbox.right());
- }
- }
+ if (InstrumentName* n = s->individualStaffName) {
+ placeFirstColumnName(n);
+ }
+ if (InstrumentName* n = s->instrumentName; n && n->ldata()->column() == 0 && n->effectiveStaffIdx() != muse::nidx) {
+ placeFirstColumnName(n);
}
}
bool stackVertically = stackLabelsVertically(system);
- for (staff_idx_t staffIdx = 0; staffIdx < system->staves().size(); ++staffIdx) {
- const SysStaff* s = system->staff(staffIdx);
- const Part* p = system->score()->staff(staffIdx)->part();
- if (InstrumentName* t = s->instrumentName; t && t->effectiveStaffIdx() != muse::nidx) {
- const RectF& bbox = t->ldata()->bbox();
- bool longName = t->instrumentNameType() == InstrumentNameType::LONG;
- InstrumentNamesAlign align
- = t->style().styleV(longName ? Sid::instrumentNamesAlignLong : Sid::instrumentNamesAlignShort).value();
-
- if (align == InstrumentNamesAlign::LEFT_RIGHT) {
- t->mutldata()->setPosX(0 - bbox.left());
- } else if (align == InstrumentNamesAlign::CENTER_CENTER) {
- t->mutldata()->setPosX(0.5 * totalNamesWidth - 0.5 * (bbox.right() + bbox.left()));
- } else if (align == InstrumentNamesAlign::CENTER_RIGHT) {
- t->mutldata()->moveX(0.5 * ldata->instrumentNamesWidth() - 0.5 * (bbox.right() + bbox.left()));
+ auto placeSecondColumnName = [&](InstrumentName* name, staff_idx_t staffIdx) {
+ const RectF& bbox = name->ldata()->bbox();
+
+ if (align == InstrumentNamesAlign::LEFT_RIGHT) {
+ name->mutldata()->setPosX(0 - bbox.left());
+ return;
+ }
+
+ if (align == InstrumentNamesAlign::CENTER_CENTER) {
+ name->mutldata()->setPosX(0.5 * totalNamesWidth - 0.5 * (bbox.right() + bbox.left()));
+ return;
+ }
+
+ if (align == InstrumentNamesAlign::CENTER_RIGHT) {
+ name->mutldata()->setPosX(0.5 * ldata->secondColumnWidth() - 0.5 * (bbox.right() + bbox.left()));
+ return;
+ }
+
+ if (stackVertically) {
+ name->mutldata()->setPosX(totalNamesWidth - bbox.right());
+ return;
+ }
+
+ if (name->instrumentNameRole() == InstrumentNameRole::PART) {
+ const Part* p = system->score()->staff(staffIdx)->part();
+ std::vector visibleStavesForPart = system->visibleStavesOfPart(p);
+ size_t visibleStaveCount = visibleStavesForPart.size();
+ if (visibleStaveCount % 2 == 0) {
+ name->mutldata()->setPosX(totalNamesWidth - bbox.right());
+ return;
+ }
+
+ staff_idx_t centerStaff = visibleStavesForPart[visibleStaveCount / 2];
+ if (InstrumentName* staffName = system->staff(centerStaff)->individualStaffName) {
+ name->mutldata()->setPosX(
+ staffName->x() + staffName->ldata()->bbox().left() - bbox.right() - ldata->instrumentNameOffset());
} else {
- std::vector visibleStavesForPart = system->visibleStavesOfPart(p);
- size_t visibleStaveCount = visibleStavesForPart.size();
- if (visibleStaveCount % 2 && !stackVertically) {
- staff_idx_t centerStaff = visibleStavesForPart[visibleStaveCount / 2];
- if (InstrumentName* staffName = system->staff(centerStaff)->individualStaffName) {
- t->mutldata()->setPosX(
- staffName->x() + staffName->ldata()->bbox().left() - bbox.right() - ldata->instrumentNameOffset());
- } else {
- t->mutldata()->setPosX(totalNamesWidth - bbox.right());
- }
- } else {
- t->mutldata()->setPosX(totalNamesWidth - bbox.right());
- }
+ name->mutldata()->setPosX(totalNamesWidth - bbox.right());
}
+
+ return;
+ }
+
+ std::vector visiblePartsOfGroup = system->visiblePartsOfGroup(staffIdx, name->ldata()->endIdxOfGroup());
+ size_t visiblePartsCount = visiblePartsOfGroup.size();
+ if (visiblePartsCount % 2 == 0) {
+ name->mutldata()->setPosX(totalNamesWidth - bbox.right());
+ return;
+ }
+
+ Part* centerPart = visiblePartsOfGroup[visiblePartsCount / 2];
+ if (InstrumentName* instrName = system->staff(*centerPart->staveIdxList().begin())->instrumentName) {
+ name->mutldata()->setPosX(
+ instrName->x() + instrName->ldata()->bbox().left() - bbox.right() - ldata->instrumentNameOffset());
+ return;
+ }
+
+ std::vector visibleStavesOfPart = system->visibleStavesOfPart(centerPart);
+ size_t visibleStavesCount = visibleStavesOfPart.size();
+ if (visibleStavesCount % 2 == 0) {
+ name->mutldata()->setPosX(totalNamesWidth - bbox.right());
+ return;
+ }
+
+ staff_idx_t centerStaff = visibleStavesOfPart[visibleStavesCount / 2];
+ if (InstrumentName* staffName = system->staff(centerStaff)->individualStaffName) {
+ name->mutldata()->setPosX(
+ staffName->x() + staffName->ldata()->bbox().left() - bbox.right() - ldata->instrumentNameOffset());
+ } else {
+ name->mutldata()->setPosX(totalNamesWidth - bbox.right());
+ }
+ };
+
+ for (staff_idx_t staffIdx = 0; staffIdx < system->staves().size(); ++staffIdx) {
+ const SysStaff* s = system->staff(staffIdx);
+ InstrumentName* instrName = s->instrumentName;
+ if (instrName && instrName->effectiveStaffIdx() != muse::nidx && instrName->ldata()->column() > 0) {
+ placeSecondColumnName(instrName, staffIdx);
+ }
+ }
+
+ for (staff_idx_t staffIdx = 0; staffIdx < system->staves().size(); ++staffIdx) {
+ const SysStaff* s = system->staff(staffIdx);
+ InstrumentName* groupName = s->groupName;
+ if (groupName && groupName->effectiveStaffIdx() != muse::nidx) {
+ placeSecondColumnName(groupName, staffIdx);
}
}
}
-void SystemHeaderLayout::updateName(System* system, staff_idx_t staffIdx, LayoutContext& ctx, const String& name,
- InstrumentNameType type, InstrumentNameRole role)
+InstrumentName* SystemHeaderLayout::updateName(System* system, staff_idx_t staffIdx, LayoutContext& ctx, const String& name,
+ InstrumentNameType type, InstrumentNameRole role)
{
SysStaff* sysStaff = system->staff(staffIdx);
- InstrumentName* iname = role == InstrumentNameRole::PART ? sysStaff->instrumentName : sysStaff->individualStaffName;
+ InstrumentName* iname = role == InstrumentNameRole::GROUP ? sysStaff->groupName
+ : role == InstrumentNameRole::PART ? sysStaff->instrumentName : sysStaff->individualStaffName;
if (name.empty()) {
if (iname) {
ctx.mutDom().removeElement(iname);
}
- return;
+ return nullptr;
}
if (!iname) {
@@ -566,6 +785,159 @@ void SystemHeaderLayout::updateName(System* system, staff_idx_t staffIdx, Layout
iname->setAlign(Align(iname->align().horizontal, AlignV::BASELINE));
iname->setXmlText(name);
+
+ iname->mutldata()->setColumn(0); // Reset here, will be computed later
+ TLayout::layoutInstrumentName(iname, iname->mutldata());
+
+ return iname;
+}
+
+String SystemHeaderLayout::formattedInstrumentName(System* system, Part* part, const Fraction& tick)
+{
+ bool longNames = system->ldata()->useLongNames();
+ Instrument* instr = part->instrument(tick);
+ const InstrumentLabel& label = instr->instrumentLabel();
+
+ if (muse::contains(system->ldata()->partsWithGroupName(), part)) {
+ if (label.useCustomIndividualName()) {
+ return longNames ? label.customNameLongIndividual() : label.customNameShortIndividual();
+ }
+
+ int number = part->number(tick);
+ return number > 0 ? String::number(number) : String();
+ }
+
+ if (label.useCustomName()) {
+ return longNames ? label.customNameLong() : label.customNameShort();
+ }
+
+ const MStyle& style = system->style();
+
+ String instrName = longNames ? instr->longName() : instr->shortName();
+
+ bool showNumber = longNames ? label.showNumberLong() : label.showNumberShort();
+ String number = instr->number() > 0 && showNumber ? String::number(instr->number()) : String();
+
+ bool showTranspo = longNames ? label.showTranspositionLong() : label.showTranspositionShort();
+ showTranspo &= style.styleB(longNames ? Sid::instrumentNamesShowTranspositionLong : Sid::instrumentNamesShowTranspositionShort);
+ String transposition = showTranspo ? instr->transposition() : String();
+
+ if (transposition.empty()) {
+ String result = instrName;
+ if (!number.empty()) {
+ result += u" " + number;
+ }
+ return result;
+ }
+
+ InstrumentNamesFormat nameFormat
+ = style.styleV(longNames ? Sid::instrumentNamesFormatLong : Sid::instrumentNamesFormatShort).value();
+
+ //: For instrument transposition, e.g. Horn in F
+ String in = TranslatableString("notation", "in").translated();
+
+ switch (nameFormat) {
+ case InstrumentNamesFormat::NAME_IN_TRANSP_NUM:
+ return instrName + u" " + in + u" " + transposition + (number.empty() ? String() : u" " + number);
+ case InstrumentNamesFormat::NAME_NUM_IN_TRANSP:
+ return instrName + (number.empty() ? String() : u" " + number) + u" " + in + u" " + transposition;
+ case InstrumentNamesFormat::TRANSP_NAME_NUM:
+ return transposition + u" " + instrName + (number.empty() ? String() : u" " + number);
+ default:
+ {
+ String result = style.styleSt(longNames ? Sid::instrumentNamesCustomFormatLong : Sid::instrumentNamesCustomFormatShort);
+ return resolveTokens(result, instrName, transposition, number);
+ }
+ }
+}
+
+String SystemHeaderLayout::formattedGroupName(System* system, Part* part, const Fraction& tick)
+{
+ const MStyle& style = system->style();
+
+ bool longNames = system->ldata()->useLongNames();
+ Instrument* instr = part->instrument(tick);
+ const InstrumentLabel& label = instr->instrumentLabel();
+
+ if (label.useCustomGroupName()) {
+ return longNames ? label.customNameLongGroup() : label.customNameShortGroup();
+ }
+
+ String instrName = longNames ? instr->longName() : instr->shortName();
+
+ bool showTranspo = longNames ? label.showTranspositionLong() : label.showTranspositionShort();
+ showTranspo &= style.styleB(longNames ? Sid::instrumentNamesShowTranspositionLong : Sid::instrumentNamesShowTranspositionShort);
+
+ String transposition = showTranspo ? instr->transposition() : String();
+
+ if (transposition.empty()) {
+ return instrName;
+ }
+
+ InstrumentNamesFormat nameFormat
+ = style.styleV(longNames ? Sid::instrumentNamesFormatLong : Sid::instrumentNamesFormatShort).value();
+
+ //: For instrument transposition, e.g. Horn in F
+ String in = TranslatableString("notation", "in").translated();
+
+ switch (nameFormat) {
+ case InstrumentNamesFormat::NAME_IN_TRANSP_NUM:
+ return instrName + u" " + in + u" " + transposition;
+ case InstrumentNamesFormat::NAME_NUM_IN_TRANSP:
+ return instrName + u" " + in + u" " + transposition;
+ case InstrumentNamesFormat::TRANSP_NAME_NUM:
+ return transposition + u" " + instrName;
+ default:
+ {
+ String result = style.styleSt(longNames ? Sid::instrumentNamesCustomFormatLong : Sid::instrumentNamesCustomFormatShort);
+ return resolveTokens(result, instrName, transposition, /*number*/ String());
+ }
+ }
+}
+
+String& SystemHeaderLayout::resolveTokens(String& str, const String& name, const String& transposition, const String& number)
+{
+ static const String NAME = u"$name";
+ static const String TRANSP = u"$transposition";
+ static const String NUMBER = u"$number";
+
+ str.replace(NAME, name);
+ str.replace(TRANSP, transposition);
+
+ if (!number.empty()) {
+ str.replace(NUMBER, number);
+ } else if (str.contains(u" $number")) {
+ str.remove(u" $number");
+ } else if (str.contains(u"$number ")) {
+ str.remove(u"$number ");
+ } else if (str.contains(u"$number")) {
+ str.remove(u"$number");
+ }
+
+ return str;
+}
+
+bool SystemHeaderLayout::showNames(LayoutContext& ctx)
+{
+ if (!ctx.conf().isShowInstrumentNames()) {
+ return false;
+ }
+
+ if (ctx.conf().styleB(Sid::hideInstrumentNameIfOneInstrument) && ctx.dom().visiblePartCount() <= 1) {
+ return false;
+ }
+
+ if (ctx.state().firstSystem()
+ && ctx.conf().styleV(Sid::firstSystemInstNameVisibility).value() == InstrumentLabelVisibility::HIDE) {
+ return false;
+ }
+
+ if (!ctx.state().firstSystem()
+ && ctx.conf().styleV(Sid::subsSystemInstNameVisibility).value() == InstrumentLabelVisibility::HIDE) {
+ return false;
+ }
+
+ return true;
}
void SystemHeaderLayout::setInstrumentNames(System* system, LayoutContext& ctx, bool longName, Fraction tick)
@@ -574,63 +946,127 @@ void SystemHeaderLayout::setInstrumentNames(System* system, LayoutContext& ctx,
return;
}
- system->mutldata()->setUseLongNames(longName);
+ System::LayoutData* ldata = system->mutldata();
+ ldata->setUseLongNames(longName);
- if (!ctx.conf().isShowInstrumentNames()
- || (ctx.conf().styleB(Sid::hideInstrumentNameIfOneInstrument) && ctx.dom().visiblePartCount() <= 1)
- || (ctx.state().firstSystem()
- && ctx.conf().styleV(Sid::firstSystemInstNameVisibility).value() == InstrumentLabelVisibility::HIDE)
- || (!ctx.state().firstSystem()
- && ctx.conf().styleV(Sid::subsSystemInstNameVisibility).value()
- == InstrumentLabelVisibility::HIDE)) {
- for (SysStaff* staff : system->staves()) {
- if (InstrumentName* iName = staff->instrumentName) {
- ctx.mutDom().removeElement(iName);
- }
- if (InstrumentName* sName = staff->individualStaffName) {
- ctx.mutDom().removeElement(sName);
- }
+ InstrumentNameType type = longName ? InstrumentNameType::LONG : InstrumentNameType::SHORT;
+
+ if (!showNames(ctx)) {
+ for (staff_idx_t idx = 0; idx < system->staves().size(); ++idx) {
+ updateName(system, idx, ctx, String(), type, InstrumentNameRole::STAFF);
+ updateName(system, idx, ctx, String(), type, InstrumentNameRole::PART);
+ updateName(system, idx, ctx, String(), type, InstrumentNameRole::GROUP);
}
return;
}
- InstrumentNameType type = longName ? InstrumentNameType::LONG : InstrumentNameType::SHORT;
+ updateGroupNames(system, ctx, tick);
for (size_t staffIdx = 0; staffIdx < system->staves().size(); /*empty*/) {
Part* part = ctx.dom().staff(staffIdx)->part();
+ size_t partNstaves = part->nstaves();
+ size_t visibleStavesCount = part->visibleStavesCount();
- if (!part->show() || part->visibleStavesCount() == 0) {
- for (size_t i = 0; i < part->nstaves(); ++i) {
- SysStaff* sysStaff = system->staff(staffIdx + i);
- if (InstrumentName* iName = sysStaff->instrumentName) {
- ctx.mutDom().removeElement(iName);
- }
- if (InstrumentName* sName = sysStaff->individualStaffName) {
- ctx.mutDom().removeElement(sName);
- }
+ for (size_t idxInPart = 0; idxInPart < partNstaves; ++idxInPart) {
+ staff_idx_t globalIdx = staffIdx + idxInPart;
+
+ String instrumentName;
+ if (idxInPart == 0 && part->show() && visibleStavesCount > 0) {
+ instrumentName = formattedInstrumentName(system, part, tick);
+ }
+ updateName(system, globalIdx, ctx, instrumentName, type, InstrumentNameRole::PART);
+
+ const Staff* staff = ctx.dom().staff(globalIdx);
+ String staffName;
+ if (staff->show()) {
+ staffName = longName ? staff->individualStaffNameLong(tick) : staff->individualStaffNameShort(tick);
}
- staffIdx += part->nstaves();
+ updateName(system, globalIdx, ctx, staffName, type, InstrumentNameRole::STAFF);
+ }
+
+ staffIdx += partNstaves;
+ }
+}
+
+void SystemHeaderLayout::updateGroupNames(System* system, LayoutContext& ctx, const Fraction& tick)
+{
+ const MStyle& style = ctx.conf().style();
+ InstrumentNameType type = system->ldata()->useLongNames() ? InstrumentNameType::LONG : InstrumentNameType::SHORT;
+
+ System::LayoutData* ldata = system->mutldata();
+ ldata->clearPartsWithGroupNames();
+
+ auto useGroupNames = [&](String instrumentGroup) {
+ if (instrumentGroup == "woodwinds" || instrumentGroup == "brass") {
+ return style.styleB(Sid::windsNameByGroup);
+ }
+ if (instrumentGroup == "vocals") {
+ return style.styleB(Sid::vocalsNameByGroup);
+ }
+ if (instrumentGroup == "strings") {
+ return style.styleB(Sid::stringsNameByGroup);
+ }
+ return style.styleB(Sid::othersNameByGroup);
+ };
+
+ for (staff_idx_t startOfGroup = 0; startOfGroup < system->staves().size();) {
+ Part* curPart = ctx.dom().staff(startOfGroup)->part();
+ const Instrument* curInstrument = curPart->instrument(tick);
+ const InstrumentLabel& curLabel = curInstrument->instrumentLabel();
+ if (!curInstrument->instrumentLabel().allowGroupName()) {
+ ++startOfGroup;
continue;
}
- for (size_t i = 0; i < part->nstaves(); ++i) {
- staff_idx_t idx = staffIdx + i;
- SysStaff* sysStaff = system->staff(idx);
- if (i == 0) {
- const String& instrName = longName ? part->longName(tick) : part->shortName(tick);
- updateName(system, idx, ctx, instrName, type, InstrumentNameRole::PART);
- } else {
- if (sysStaff->instrumentName) {
- ctx.mutDom().removeElement(sysStaff->instrumentName);
+ std::vector partsInThisGroup;
+
+ staff_idx_t endOfGroup = startOfGroup;
+ while (endOfGroup < system->staves().size()) {
+ Part* nextPart = ctx.dom().staff(endOfGroup)->part();
+ Instrument* nextInstrument = nextPart->instrument(tick);
+ InstrumentLabel& nextLabel = nextInstrument->instrumentLabel();
+ if (nextPart != curPart && (nextInstrument->id() != curInstrument->id() || !nextLabel.allowGroupName())) {
+ break;
+ }
+
+ if (type == InstrumentNameType::LONG && system->visibleStavesOfPart(nextPart).size() == 0) {
+ // NOTE: We can only do this for long-name systems because they don't need to have
+ // the left barline aligned with the other systems across the page.
+ endOfGroup += nextPart->nstaves();
+ continue;
+ }
+
+ if (nextPart->show() && nextPart->visibleStavesCount() > 0) {
+ partsInThisGroup.push_back(nextPart);
+ nextLabel.setUseCustomGroupName(curLabel.useCustomGroupName());
+ nextLabel.setCustomNameLongGroup(curLabel.customNameLongGroup());
+ nextLabel.setCustomNameShortGroup(curLabel.customNameShortGroup());
+ }
+
+ endOfGroup += nextPart->nstaves();
+ }
+
+ if (partsInThisGroup.size() > 1 && useGroupNames(curInstrument->group())) {
+ String name = formattedGroupName(system, curPart, tick);
+ InstrumentName* groupName = updateName(system, startOfGroup, ctx, name, type, InstrumentNameRole::GROUP);
+
+ if (groupName) {
+ groupName->mutldata()->setEndIdxOfGroup(endOfGroup);
+
+ for (Part* p : partsInThisGroup) {
+ ldata->addPartWithGroupNames(p, groupName);
}
}
- const Staff* staff = ctx.dom().staff(idx);
- if (staff->show()) {
- const String& staffName = longName ? staff->individualStaffNameLong(tick) : staff->individualStaffNameShort(tick);
- updateName(system, idx, ctx, staffName, type, InstrumentNameRole::STAFF);
+
+ for (staff_idx_t idx = startOfGroup + 1; idx < endOfGroup; ++idx) {
+ updateName(system, idx, ctx, String(), type, InstrumentNameRole::GROUP);
+ }
+ } else {
+ for (staff_idx_t idx = startOfGroup; idx < endOfGroup; ++idx) {
+ updateName(system, idx, ctx, String(), type, InstrumentNameRole::GROUP);
}
}
- staffIdx += part->nstaves();
+ startOfGroup = endOfGroup;
}
}
diff --git a/src/engraving/rendering/score/systemheaderlayout.h b/src/engraving/rendering/score/systemheaderlayout.h
index 951baa67694bb..6934a259efae7 100644
--- a/src/engraving/rendering/score/systemheaderlayout.h
+++ b/src/engraving/rendering/score/systemheaderlayout.h
@@ -51,8 +51,14 @@ class SystemHeaderLayout
static Bracket* createBracket(System* system, LayoutContext& ctx, BracketItem* bi, size_t column, staff_idx_t staffIdx,
std::vector& bl, Measure* measure);
- static void updateName(System* system, staff_idx_t staffIdx, LayoutContext& ctx, const String& name, InstrumentNameType type,
- InstrumentNameRole role);
+ static void updateGroupNames(System* system, LayoutContext& ctx, const Fraction& tick);
+ static InstrumentName* updateName(System* system, staff_idx_t staffIdx, LayoutContext& ctx, const String& name, InstrumentNameType type,
+ InstrumentNameRole role);
+ static String formattedInstrumentName(System* system, Part* part, const Fraction& tick);
+ static String formattedGroupName(System* system, Part* part, const Fraction& tick);
+ static String& resolveTokens(String& str, const String& name, const String& transposition, const String& number);
+ static bool showNames(LayoutContext& ctx);
+
static bool stackLabelsVertically(System* system);
};
}
diff --git a/src/engraving/rw/read114/read114.cpp b/src/engraving/rw/read114/read114.cpp
index 976445e8491a6..bddb01582d0e2 100644
--- a/src/engraving/rw/read114/read114.cpp
+++ b/src/engraving/rw/read114/read114.cpp
@@ -2584,17 +2584,12 @@ static void readPart(Part* part, XmlReader& e, ReadContext& ctx)
readText114(e, ctx, t, t);
part->instrument()->setShortName(t->xmlText());
delete t;
- } else if (tag == "trackName") {
- part->setPartName(e.readText());
} else if (tag == "show") {
part->setShow(e.readInt());
} else {
e.unknown();
}
}
- if (part->partName().isEmpty()) {
- part->setPartName(part->instrument()->trackName());
- }
if (part->instrument()->useDrumset()) {
for (Staff* staff : part->staves()) {
diff --git a/src/engraving/rw/read206/read206.cpp b/src/engraving/rw/read206/read206.cpp
index 5cbcbcca8a188..39807afcf1283 100644
--- a/src/engraving/rw/read206/read206.cpp
+++ b/src/engraving/rw/read206/read206.cpp
@@ -922,18 +922,12 @@ void Read206::readPart206(Part* part, XmlReader& e, ReadContext& ctx)
part->instrument()->setLongName(e.readText());
} else if (tag == "shortName") {
part->instrument()->setShortName(e.readText());
- } else if (tag == "trackName") {
- part->setPartName(e.readText());
} else if (tag == "show") {
part->setShow(e.readInt());
} else {
e.unknown();
}
}
-
- if (part->partName().isEmpty()) {
- part->setPartName(part->instrument()->trackName());
- }
}
//---------------------------------------------------------
diff --git a/src/engraving/rw/read400/tread.cpp b/src/engraving/rw/read400/tread.cpp
index 21bbba67fafc6..84d0e0966f8ce 100644
--- a/src/engraving/rw/read400/tread.cpp
+++ b/src/engraving/rw/read400/tread.cpp
@@ -803,9 +803,9 @@ bool TRead::readProperties(Instrument* item, XmlReader& e, ReadContext& ctx, Par
const AsciiStringView tag(e.name());
if (tag == "longName") {
- item->setLongName(read460::TRead::readStaffName(e));
+ item->setLongName(read460::TRead::readLegacyStaffName(e));
} else if (tag == "shortName") {
- item->setShortName(read460::TRead::readStaffName(e));
+ item->setShortName(read460::TRead::readLegacyStaffName(e));
} else if (tag == "trackName") {
item->setTrackName(e.readText());
} else if (tag == "minPitch") { // obsolete
@@ -3296,10 +3296,6 @@ void TRead::read(Part* p, XmlReader& e, ReadContext& ctx)
}
}
- if (p->partName().isEmpty()) {
- p->setPartName(p->instrument()->trackName());
- }
-
read(p, staffHideModes, ctx.style().styleB(Sid::hideEmptyStaves));
}
@@ -3396,8 +3392,6 @@ bool TRead::readProperties(Part* p, XmlReader& e, ReadContext& ctx, StaffHideMod
p->setColor(e.readInt());
} else if (tag == "shortName") {
p->instrument()->setShortName(e.readText());
- } else if (tag == "trackName") {
- p->setPartName(e.readText());
} else if (tag == "show") {
p->setShow(e.readInt());
} else if (tag == "soloist") {
diff --git a/src/engraving/rw/read410/tread.cpp b/src/engraving/rw/read410/tread.cpp
index 0425d8e093ccf..a4fd7c4a968d5 100644
--- a/src/engraving/rw/read410/tread.cpp
+++ b/src/engraving/rw/read410/tread.cpp
@@ -998,9 +998,9 @@ bool TRead::readProperties(Instrument* item, XmlReader& e, ReadContext& ctx, Par
if (tag == "soundId") {
item->setSoundId(e.readText());
} else if (tag == "longName") {
- item->setLongName(read460::TRead::readStaffName(e));
+ item->setLongName(read460::TRead::readLegacyStaffName(e));
} else if (tag == "shortName") {
- item->setShortName(read460::TRead::readStaffName(e));
+ item->setShortName(read460::TRead::readLegacyStaffName(e));
} else if (tag == "trackName") {
item->setTrackName(e.readText());
} else if (tag == "minPitchA") {
@@ -3494,10 +3494,6 @@ void TRead::read(Part* p, XmlReader& e, ReadContext& ctx)
}
}
- if (p->partName().isEmpty()) {
- p->setPartName(p->instrument()->trackName());
- }
-
read400::TRead::read(p, staffHideModes, ctx.style().styleB(Sid::hideEmptyStaves));
}
@@ -3545,8 +3541,6 @@ bool TRead::readProperties(Part* p, XmlReader& e, ReadContext& ctx, StaffHideMod
p->setColor(e.readInt());
} else if (tag == "shortName") {
p->instrument()->setShortName(e.readText());
- } else if (tag == "trackName") {
- p->setPartName(e.readText());
} else if (tag == "show") {
p->setShow(e.readInt());
} else if (tag == "soloist") {
diff --git a/src/engraving/rw/read460/tread.cpp b/src/engraving/rw/read460/tread.cpp
index addd77a273c31..bdfb75efbcf8e 100644
--- a/src/engraving/rw/read460/tread.cpp
+++ b/src/engraving/rw/read460/tread.cpp
@@ -975,9 +975,11 @@ bool TRead::readProperties(Instrument* item, XmlReader& e, ReadContext& ctx, Par
if (tag == "soundId") {
item->setSoundId(e.readText());
} else if (tag == "longName") {
- item->setLongName(readStaffName(e));
+ item->setLongName(readLegacyStaffName(e)); // Old implementation
} else if (tag == "shortName") {
- item->setShortName(readStaffName(e));
+ item->setShortName(readLegacyStaffName(e)); // Old implementation
+ } else if (tag == "InstrumentLabel") {
+ readInstrumentLabel(item->instrumentLabel(), e); // New implementation
} else if (tag == "trackName") {
item->setTrackName(e.readText());
} else if (tag == "minPitchA") {
@@ -3543,10 +3545,6 @@ void TRead::read(Part* p, XmlReader& e, ReadContext& ctx)
e.unknown();
}
}
-
- if (p->partName().isEmpty()) {
- p->setPartName(p->instrument()->trackName());
- }
}
void TRead::read(PartialLyricsLine* p, XmlReader& xml, ReadContext& ctx)
@@ -3593,8 +3591,6 @@ bool TRead::readProperties(Part* p, XmlReader& e, ReadContext& ctx)
p->setColor(e.readInt());
} else if (tag == "shortName") {
p->instrument()->setShortName(e.readText());
- } else if (tag == "trackName") {
- p->setPartName(e.readText());
} else if (tag == "show") {
p->setShow(e.readInt());
} else if (tag == "soloist") {
@@ -3819,10 +3815,11 @@ void TRead::readHopoText(HammerOnPullOffSegment* hopoSeg, XmlReader& xml, ReadCo
hopoSeg->addHopoText(hopoText);
}
-void TRead::lineBreakFromTag(String& str)
+String TRead::lineBreakFromTag(const String& str)
{
// Raw newlines appearing next to tags (", u"\n");
+ String s = str;
+ return s.replace(u"
", u"\n");
}
void TRead::readNoteParenGroup(Chord* ch, XmlReader& e, ReadContext& ctx)
@@ -3937,10 +3934,9 @@ void TRead::read(StaffType* t, XmlReader& e, ReadContext& ctx)
const AsciiStringView tag(e.name());
if (tag == "name") {
t->setXmlName(e.readText());
- } else if (tag == "longName") {
- t->setLongName(readStaffName(e));
- } else if (tag == "shortName") {
- t->setShortName(readStaffName(e));
+ } else if (tag == "StaffLabel") {
+ StaffLabel& staffLabel = t->staffLabel();
+ readStaffLabel(staffLabel, e);
} else if (tag == "lines") {
t->setLines(e.readInt());
} else if (tag == "lineDistance") {
@@ -4133,10 +4129,9 @@ bool TRead::readProperties(Staff* s, XmlReader& e, ReadContext& ctx)
return true;
}
-String TRead::readStaffName(XmlReader& xml)
+String TRead::readLegacyStaffName(XmlReader& xml)
{
- String name = xml.readXml();
- lineBreakFromTag(name);
+ String name = lineBreakFromTag(xml.readXml());
if (name.startsWith(u"")) {
// compatibility to old html implementation:
name = HtmlParser::parse(name);
@@ -4144,6 +4139,83 @@ String TRead::readStaffName(XmlReader& xml)
return name;
}
+void TRead::readStaffLabel(StaffLabel& item, XmlReader& xml)
+{
+ while (xml.readNextStartElement()) {
+ if (!readProperties(item, xml)) {
+ xml.unknown();
+ }
+ }
+}
+
+bool TRead::readProperties(StaffLabel& item, XmlReader& xml)
+{
+ AsciiStringView tag = xml.name();
+
+ if (tag == "longName") {
+ item.setLongName(lineBreakFromTag(xml.readXml()));
+ } else if (tag == "shortName") {
+ item.setShortName(lineBreakFromTag(xml.readXml()));
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void TRead::readInstrumentLabel(InstrumentLabel& item, XmlReader& xml)
+{
+ while (xml.readNextStartElement()) {
+ if (!readProperties(item, xml)) {
+ xml.unknown();
+ }
+ }
+}
+
+bool TRead::readProperties(InstrumentLabel& item, XmlReader& xml)
+{
+ AsciiStringView tag = xml.name();
+
+ if (readProperties(static_cast(item), xml)) {
+ } else if (tag == "transposition") {
+ item.setTransposition(lineBreakFromTag(xml.readXml()));
+ } else if (tag == "showTranspositionLong") {
+ item.setShowTranspositionLong(xml.readBool());
+ } else if (tag == "showTranspositionShort") {
+ item.setShowTranspositionShort(xml.readBool());
+ } else if (tag == "number") {
+ item.setNumber(xml.readInt());
+ } else if (tag == "showNumberLong") {
+ item.setShowNumberLong(xml.readBool());
+ } else if (tag == "showNumberShort") {
+ item.setShowNumberLong(xml.readBool());
+ } else if (tag == "useCustomName") {
+ item.setUseCustomName(xml.readBool());
+ } else if (tag == "customNameLong") {
+ item.setCustomNameLong(lineBreakFromTag(xml.readXml()));
+ } else if (tag == "customNameShort") {
+ item.setCustomNameShort(lineBreakFromTag(xml.readXml()));
+ } else if (tag == "allowGroupName") {
+ item.setAllowGroupName(xml.readBool());
+ } else if (tag == "customNameLongGroup") {
+ item.setCustomNameLongGroup(lineBreakFromTag(xml.readXml()));
+ } else if (tag == "customNameShortGroup") {
+ item.setCustomNameShortGroup(lineBreakFromTag(xml.readXml()));
+ } else if (tag == "useCustomGroupName") {
+ item.setUseCustomGroupName(xml.readBool());
+ } else if (tag == "customNameLongIndividual") {
+ item.setCustomNameLongIndividual(lineBreakFromTag(xml.readXml()));
+ } else if (tag == "customNameShortIndividual") {
+ item.setCustomNameShortIndividual(lineBreakFromTag(xml.readXml()));
+ } else if (tag == "useCustomIndividualName") {
+ item.setUseCustomIndividualName(xml.readBool());
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
void TRead::read(Stem* s, XmlReader& e, ReadContext& ctx)
{
while (e.readNextStartElement()) {
@@ -4477,8 +4549,7 @@ bool TRead::readProperties(TextBase* t, XmlReader& e, ReadContext& ctx)
}
if (tag == "text") {
- String str = e.readXml();
- lineBreakFromTag(str);
+ String str = lineBreakFromTag(e.readXml());
t->setXmlText(str);
t->checkCustomFormatting(str);
} else if (tag == "bold") {
diff --git a/src/engraving/rw/read460/tread.h b/src/engraving/rw/read460/tread.h
index 5db0f5097fc59..16b7812490b63 100644
--- a/src/engraving/rw/read460/tread.h
+++ b/src/engraving/rw/read460/tread.h
@@ -91,6 +91,7 @@ class Hook;
class Instrument;
class InstrChannel;
class InstrumentChange;
+class InstrumentLabel;
class Jump;
@@ -145,7 +146,7 @@ class SlurTieSegment;
class Spanner;
class Spacer;
class Staff;
-class StaffName;
+class StaffLabel;
class StaffState;
class StaffText;
class StaffTextBase;
@@ -393,7 +394,10 @@ class TRead
static void readItemEID(EngravingObject* item, XmlReader& xml);
static void readItemLink(EngravingItem* item, XmlReader& xml, ReadContext& ctx);
- static String readStaffName(XmlReader& xml);
+ static String readLegacyStaffName(XmlReader& xml);
+
+ static void readStaffLabel(StaffLabel& item, XmlReader& xml);
+ static void readInstrumentLabel(InstrumentLabel& item, XmlReader& xml);
private:
static bool readProperties(Box* b, XmlReader& xml, ReadContext& ctx);
@@ -406,8 +410,11 @@ class TRead
static void readHopoText(HammerOnPullOffSegment* hopoSeg, XmlReader& xml, ReadContext& ctx, int idx);
- static void lineBreakFromTag(String& str);
+ static String lineBreakFromTag(const String& str);
static void readNoteParenGroup(Chord* ch, XmlReader& e, ReadContext& ctx);
+
+ static bool readProperties(StaffLabel& item, XmlReader& xml);
+ static bool readProperties(InstrumentLabel& item, XmlReader& xml);
};
}
diff --git a/src/engraving/rw/write/twrite.cpp b/src/engraving/rw/write/twrite.cpp
index 0f94f266d02fc..b40bdd419c255 100644
--- a/src/engraving/rw/write/twrite.cpp
+++ b/src/engraving/rw/write/twrite.cpp
@@ -534,10 +534,11 @@ void TWrite::writeSystemLock(const SystemLock* systemLock, XmlWriter& xml)
xml.endElement();
}
-void TWrite::lineBreakToTag(String& str)
+String TWrite::lineBreakToTag(const String& str)
{
// Raw newlines appearing next to tags (");
+ String s = str;
+ return s.replace(u"\n", u"
");
}
void TWrite::writeStyledProperties(const EngravingItem* item, XmlWriter& xml)
@@ -1342,9 +1343,7 @@ void TWrite::writeProperties(const TextBase* item, XmlWriter& xml, WriteContext&
writeProperty(item, xml, spp.pid);
}
if (writeText) {
- String xmlStr = item->xmlText();
- lineBreakToTag(xmlStr);
- xml.writeXml(u"text", xmlStr);
+ xml.writeXml(u"text", lineBreakToTag(item->xmlText()));
}
writeProperty(item, xml, Pid::TEXT_LINKED_TO_MASTER);
@@ -1915,7 +1914,9 @@ void TWrite::write(const Instrument* item, XmlWriter& xml, WriteContext&, const
xml.tag("soundId", item->soundId());
}
- write(item->instrumentName(), xml);
+ if (!item->instrumentLabel().empty()) {
+ write(item->instrumentLabel(), xml);
+ }
// if (!_trackName.empty())
xml.tag("trackName", item->trackName());
@@ -2093,18 +2094,100 @@ void TWrite::write(const MidiArticulation* item, XmlWriter& xml)
xml.endElement();
}
-void TWrite::write(const StaffName& item, XmlWriter& xml)
+void TWrite::write(const StaffLabel& item, XmlWriter& xml)
+{
+ xml.startElement("StaffLabel");
+ writeProperties(item, xml);
+ xml.endElement();
+}
+
+void TWrite::writeProperties(const StaffLabel& item, XmlWriter& xml)
{
String longName = item.longName();
if (!longName.empty()) {
- lineBreakToTag(longName);
- xml.writeXml(u"longName", longName);
+ xml.writeXml(u"longName", lineBreakToTag(longName));
}
String shortName = item.shortName();
if (!shortName.empty()) {
- lineBreakToTag(shortName);
- xml.writeXml(u"shortName", shortName);
+ xml.writeXml(u"shortName", lineBreakToTag(shortName));
+ }
+}
+
+void TWrite::write(const InstrumentLabel& item, XmlWriter& xml)
+{
+ xml.startElement("InstrumentLabel");
+ writeProperties(item, xml);
+ xml.endElement();
+}
+
+void TWrite::writeProperties(const InstrumentLabel& item, XmlWriter& xml)
+{
+ writeProperties(static_cast(item), xml);
+
+ String transposition = item.transposition();
+ if (!transposition.empty()) {
+ xml.writeXml(u"transposition", lineBreakToTag(transposition));
+ }
+
+ if (!item.showTranspositionLong()) {
+ xml.tag("showTranspositionLong", item.showTranspositionLong());
+ }
+
+ if (!item.showTranspositionShort()) {
+ xml.tag("showTranspositionShort", item.showTranspositionShort());
+ }
+
+ if (item.number() != 0) {
+ xml.tag("number", item.number());
+ }
+
+ if (!item.showNumberLong()) {
+ xml.tag("showNumberLong", item.showNumberLong());
+ }
+
+ if (!item.showNumberShort()) {
+ xml.tag("showNumberShort", item.showNumberShort());
+ }
+
+ if (item.useCustomName()) {
+ xml.tag("useCustomName", item.useCustomName());
+ }
+
+ if (!item.customNameLong().empty()) {
+ xml.writeXml(u"customNameLong", item.customNameLong());
+ }
+
+ if (!item.customNameShort().empty()) {
+ xml.writeXml(u"customNameShort", item.customNameShort());
+ }
+
+ if (!item.allowGroupName()) {
+ xml.tag("allowGroupName", item.allowGroupName());
+ }
+
+ if (!item.customNameLongGroup().empty()) {
+ xml.writeXml(u"customNameLongGroup", item.customNameLongGroup());
+ }
+
+ if (!item.customNameShortGroup().empty()) {
+ xml.writeXml(u"customNameShortGroup", item.customNameShortGroup());
+ }
+
+ if (item.useCustomGroupName()) {
+ xml.tag("useCustomGroupName", item.useCustomGroupName());
+ }
+
+ if (!item.customNameLongIndividual().empty()) {
+ xml.writeXml(u"customNameLongIndividual", item.customNameLongIndividual());
+ }
+
+ if (!item.customNameShortIndividual().empty()) {
+ xml.writeXml(u"customNameShortIndividual", item.customNameShortIndividual());
+ }
+
+ if (item.useCustomIndividualName()) {
+ xml.tag("useCustomIndividualName", item.useCustomIndividualName());
}
}
@@ -2504,8 +2587,6 @@ void TWrite::write(const Part* item, XmlWriter& xml, WriteContext& ctx)
xml.tag("soloist", item->soloist());
}
- xml.tag("trackName", item->partName());
-
if (item->color() != Part::DEFAULT_COLOR) {
xml.tag("color", item->color());
}
@@ -2901,8 +2982,8 @@ void TWrite::write(const StaffType* item, XmlWriter& xml, WriteContext& ctx)
if (!item->xmlName().isEmpty()) {
xml.tag("name", item->xmlName());
}
- if (!item->staffName().empty()) {
- write(item->staffName(), xml);
+ if (!item->staffLabel().empty()) {
+ write(item->staffLabel(), xml);
}
if (item->lines() != 5) {
xml.tag("lines", item->lines());
diff --git a/src/engraving/rw/write/twrite.h b/src/engraving/rw/write/twrite.h
index 05a2eb528be14..7086b97b17520 100644
--- a/src/engraving/rw/write/twrite.h
+++ b/src/engraving/rw/write/twrite.h
@@ -83,6 +83,7 @@ class Image;
class Instrument;
class InstrChannel;
class InstrumentChange;
+class InstrumentLabel;
class Jump;
@@ -129,7 +130,7 @@ class SLine;
class Spanner;
class Spacer;
class Staff;
-class StaffName;
+class StaffLabel;
class StaffState;
class StaffText;
class StaffTextBase;
@@ -275,7 +276,6 @@ class TWrite
static void write(const Slur* item, XmlWriter& xml, WriteContext& ctx);
static void write(const Spacer* item, XmlWriter& xml, WriteContext& ctx);
static void write(const Staff* item, XmlWriter& xml, WriteContext& ctx);
- static void write(const StaffName& item, XmlWriter& xml);
static void write(const StaffState* item, XmlWriter& xml, WriteContext& ctx);
static void write(const StaffText* item, XmlWriter& xml, WriteContext& ctx);
static void write(const StaffType* item, XmlWriter& xml, WriteContext& ctx);
@@ -320,6 +320,9 @@ class TWrite
static void writeItemEid(const EngravingObject* item, XmlWriter& xml, WriteContext& ctx);
static void writeItemLink(const EngravingObject* item, XmlWriter& xml, WriteContext& ctx);
+ static void write(const StaffLabel& item, XmlWriter& xml);
+ static void write(const InstrumentLabel& item, XmlWriter& xml);
+
private:
static void writeStyledProperties(const EngravingItem* item, XmlWriter& xml);
@@ -362,6 +365,8 @@ class TWrite
static void writeSystemLock(const SystemLock* systemLock, XmlWriter& xml);
- static void lineBreakToTag(String& str);
+ static muse::String lineBreakToTag(const String& str);
+ static void writeProperties(const StaffLabel& item, XmlWriter& xml);
+ static void writeProperties(const InstrumentLabel& item, XmlWriter& xml);
};
}
diff --git a/src/engraving/style/style.cpp b/src/engraving/style/style.cpp
index 159f56e1e9204..b7d61574cd671 100644
--- a/src/engraving/style/style.cpp
+++ b/src/engraving/style/style.cpp
@@ -238,6 +238,9 @@ bool MStyle::readProperties(XmlReader& e)
case P_TYPE::INSTRUMENT_NAMES_ALIGN:
set(idx, TConv::fromXml(e.readAsciiText(), InstrumentNamesAlign::RIGHT_RIGHT));
break;
+ case P_TYPE::INSTRUMENT_NAMES_FORMAT:
+ set(idx, TConv::fromXml(e.readAsciiText(), InstrumentNamesFormat::NAME_IN_TRANSP_NUM));
+ break;
default:
ASSERT_X(u"unhandled type " + String::number(int(type)));
}
@@ -636,6 +639,11 @@ void MStyle::read(XmlReader& e, compat::ReadChordListHook* readChordListHook, in
}
}
+ if (mscVersion < 500) {
+ set(Sid::windsNameByGroup, false);
+ set(Sid::vocalsNameByGroup, false);
+ }
+
if (mscVersion < 470) {
set(Sid::dividerLeftAlignToSystemBarline, false);
set(Sid::dividerRightAlignToSystemBarline, false);
@@ -772,6 +780,8 @@ void MStyle::save(XmlWriter& xml, bool optimize)
xml.tag(st.xmlName, TConv::toXml(value(idx).value()));
} else if (P_TYPE::INSTRUMENT_NAMES_ALIGN == type) {
xml.tag(st.xmlName, TConv::toXml(value(idx).value()));
+ } else if (P_TYPE::INSTRUMENT_NAMES_FORMAT == type) {
+ xml.tag(st.xmlName, TConv::toXml(value(idx).value()));
} else {
PropertyValue val = value(idx);
//! NOTE for compatibility
diff --git a/src/engraving/style/styledef.cpp b/src/engraving/style/styledef.cpp
index 65f69f73018e2..ec3773b1bdc9e 100644
--- a/src/engraving/style/styledef.cpp
+++ b/src/engraving/style/styledef.cpp
@@ -58,9 +58,21 @@ const std::array StyleDef::styleValue
styleDef(minSystemDistance, 8.5_sp),
styleDef(maxSystemDistance, 15.0_sp),
styleDef(alignSystemToMargin, true),
+
+ styleDef(instrumentNamesShowTranspositionLong, true),
+ styleDef(instrumentNamesShowTranspositionShort, true),
+ styleDef(instrumentNamesFormatLong, InstrumentNamesFormat::NAME_IN_TRANSP_NUM),
+ styleDef(instrumentNamesCustomFormatLong, String(u"$name in $transposition $number")),
+ styleDef(instrumentNamesFormatShort, InstrumentNamesFormat::NAME_IN_TRANSP_NUM),
+ styleDef(instrumentNamesCustomFormatShort, String(u"$name in $transposition $number")),
+
styleDef(instrumentNamesAlignLong, InstrumentNamesAlign::RIGHT_RIGHT),
styleDef(instrumentNamesAlignShort, InstrumentNamesAlign::RIGHT_RIGHT),
styleDef(instrumentNamesStackVertically, false),
+ styleDef(windsNameByGroup, true),
+ styleDef(vocalsNameByGroup, true),
+ styleDef(stringsNameByGroup, false),
+ styleDef(othersNameByGroup, false),
styleDef(enableVerticalSpread, true),
styleDef(spreadSystem, 2.5),
diff --git a/src/engraving/style/styledef.h b/src/engraving/style/styledef.h
index 0fa61681626d2..f1b4fd31ff77d 100644
--- a/src/engraving/style/styledef.h
+++ b/src/engraving/style/styledef.h
@@ -69,9 +69,20 @@ enum class Sid : short {
minSystemDistance,
maxSystemDistance,
alignSystemToMargin,
+
+ instrumentNamesShowTranspositionLong,
+ instrumentNamesShowTranspositionShort,
+ instrumentNamesFormatLong,
+ instrumentNamesCustomFormatLong,
+ instrumentNamesFormatShort,
+ instrumentNamesCustomFormatShort,
instrumentNamesAlignLong,
instrumentNamesAlignShort,
instrumentNamesStackVertically,
+ windsNameByGroup,
+ vocalsNameByGroup,
+ stringsNameByGroup,
+ othersNameByGroup,
enableVerticalSpread,
spreadSystem,
diff --git a/src/engraving/tests/barline_data/barlinedelete-ref.mscx b/src/engraving/tests/barline_data/barlinedelete-ref.mscx
index dff833179fdbb..b2bbcc17d5078 100644
--- a/src/engraving/tests/barline_data/barlinedelete-ref.mscx
+++ b/src/engraving/tests/barline_data/barlinedelete-ref.mscx
@@ -29,10 +29,11 @@
stdNormal
- Piano
- Piano
- Pno.
+
+ Piano
+ Pno.
+
Piano
21
108
diff --git a/src/engraving/tests/beam_data/Beam-A.mscx b/src/engraving/tests/beam_data/Beam-A.mscx
index 804d47707834e..b02a34a6e1c4d 100644
--- a/src/engraving/tests/beam_data/Beam-A.mscx
+++ b/src/engraving/tests/beam_data/Beam-A.mscx
@@ -4,6 +4,8 @@
B_B
480
Style Score
-
- u_u
-
- Flute
-
- v_v
+ u_u
G
G
1
- w_w
+ v_v
H_H
- x_x
+ w_w
I_I
4
4
- y_y
+ x_x
J_J
whole
- z_z
+ y_y
K_K
72
14
@@ -420,14 +412,14 @@
- 0_0
+ z_z
- 1_1
+ 0_0
M_M
whole
- 2_2
+ 1_1
N_N
72
14
@@ -436,14 +428,14 @@
- 3_3
+ 2_2
- 4_4
+ 3_3
P_P
whole
- 5_5
+ 4_4
Q_Q
72
14
@@ -452,14 +444,14 @@
- 6_6
+ 5_5
- 7_7
+ 6_6
S_S
whole
- 8_8
+ 7_7
T_T
72
14
@@ -468,14 +460,14 @@
- 9_9
+ 8_8
- +_+
+ 9_9
V_V
whole
- /_/
+ +_+
W_W
72
14
@@ -484,14 +476,14 @@
- AB_AB
+ /_/
- BB_BB
+ AB_AB
Y_Y
whole
- CB_CB
+ BB_BB
Z_Z
72
14
@@ -500,14 +492,14 @@
- DB_DB
+ CB_CB
- EB_EB
+ DB_DB
b_b
whole
- FB_FB
+ EB_EB
c_c
72
14
@@ -516,14 +508,14 @@
- GB_GB
+ FB_FB
- HB_HB
+ GB_GB
e_e
whole
- IB_IB
+ HB_HB
f_f
72
14
@@ -534,8 +526,7 @@
- JB_JB
- Oboe
+ IB_IB
480
Style Score
-
- NB_NB
-
- Oboe
-
- OB_OB
+ MB_MB
G
G
1
- PB_PB
+ NB_NB
g_g
- QB_QB
+ OB_OB
h_h
4
4
- RB_RB
+ PB_PB
i_i
measure
4/4
@@ -626,10 +612,10 @@
- SB_SB
+ QB_QB
- TB_TB
+ RB_RB
j_j
measure
4/4
@@ -637,10 +623,10 @@
- UB_UB
+ SB_SB
- VB_VB
+ TB_TB
k_k
measure
4/4
@@ -648,10 +634,10 @@
- WB_WB
+ UB_UB
- XB_XB
+ VB_VB
l_l
measure
4/4
@@ -659,10 +645,10 @@
- YB_YB
+ WB_WB
- ZB_ZB
+ XB_XB
m_m
measure
4/4
@@ -670,10 +656,10 @@
- aB_aB
+ YB_YB
- bB_bB
+ ZB_ZB
n_n
measure
4/4
@@ -681,10 +667,10 @@
- cB_cB
+ aB_aB
- dB_dB
+ bB_bB
o_o
measure
4/4
@@ -692,10 +678,10 @@
- eB_eB
+ cB_cB
- fB_fB
+ dB_dB
p_p
measure
4/4
diff --git a/src/engraving/tests/parts_data/partStyle-score-reload-ref.mscx b/src/engraving/tests/parts_data/partStyle-score-reload-ref.mscx
index 2194672674402..5d368bff2482b 100644
--- a/src/engraving/tests/parts_data/partStyle-score-reload-ref.mscx
+++ b/src/engraving/tests/parts_data/partStyle-score-reload-ref.mscx
@@ -35,7 +35,6 @@
stdNormal
- Flute
Flute
59
@@ -71,9 +70,10 @@
stdNormal
- Oboe
- Ob.
+
+ Ob.
+
Oboe
58
93
@@ -352,7 +352,6 @@
stdNormal
- Flute
Flute
59
@@ -565,9 +564,10 @@
stdNormal
- Oboe
- Ob.
+
+ Ob.
+
Oboe
58
93
diff --git a/src/engraving/tests/parts_data/voices-ref.mscx b/src/engraving/tests/parts_data/voices-ref.mscx
index 9d5304de1e4c1..08d9cf555d48c 100644
--- a/src/engraving/tests/parts_data/voices-ref.mscx
+++ b/src/engraving/tests/parts_data/voices-ref.mscx
@@ -38,10 +38,11 @@
F
- Piano
- Piano
- Pno.
+
+ Piano
+ Pno.
+
Piano
21
108
@@ -97,10 +98,11 @@
F
- Trombone
- Trombone
- Tbn.
+
+ Trombone
+ Tbn.
+
Trombone
35
74
@@ -1116,10 +1118,11 @@
F
- Piano
- Piano
- Pno.
+
+ Piano
+ Pno.
+
Piano
21
108
@@ -1777,10 +1780,11 @@
F
- Trombone
- Trombone
- Tbn.
+
+ Trombone
+ Tbn.
+
Trombone
35
74
@@ -2190,10 +2194,11 @@
F
- Trombone
- Trombone
- Tbn.
+
+ Trombone
+ Tbn.
+
Trombone
35
74
diff --git a/src/engraving/tests/readwriteundoreset_data/barlines.mscx b/src/engraving/tests/readwriteundoreset_data/barlines.mscx
index 63c973cbc0b44..f0228bdd28ccb 100644
--- a/src/engraving/tests/readwriteundoreset_data/barlines.mscx
+++ b/src/engraving/tests/readwriteundoreset_data/barlines.mscx
@@ -4,6 +4,8 @@
B_B
480