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