From 16b3d8f842656013ac5a9becf1475ea7b146a49a Mon Sep 17 00:00:00 2001 From: Igor Korsukov Date: Thu, 4 Feb 2021 15:13:07 +0200 Subject: [PATCH] ported #6935, #6987: Add a style option to show measure number range at mmrests --- src/libmscore/CMakeLists.txt | 4 + src/libmscore/ambitus.cpp | 2 + src/libmscore/element.cpp | 2 + src/libmscore/layout.cpp | 2 + src/libmscore/measure.cpp | 89 ++++++++++++- src/libmscore/measure.h | 9 ++ src/libmscore/measurenumber.cpp | 147 +-------------------- src/libmscore/measurenumber.h | 32 ++--- src/libmscore/measurenumberbase.cpp | 193 ++++++++++++++++++++++++++++ src/libmscore/measurenumberbase.h | 53 ++++++++ src/libmscore/mmrestrange.cpp | 118 +++++++++++++++++ src/libmscore/mmrestrange.h | 53 ++++++++ src/libmscore/property.cpp | 2 + src/libmscore/property.h | 3 +- src/libmscore/scoreElement.cpp | 2 + src/libmscore/scoreElement.h | 3 + src/libmscore/style.cpp | 39 ++++++ src/libmscore/style.h | 21 +++ src/libmscore/textbase.cpp | 4 + src/libmscore/textbase.h | 2 +- src/libmscore/types.h | 12 ++ 21 files changed, 627 insertions(+), 165 deletions(-) create mode 100644 src/libmscore/measurenumberbase.cpp create mode 100644 src/libmscore/measurenumberbase.h create mode 100644 src/libmscore/mmrestrange.cpp create mode 100644 src/libmscore/mmrestrange.h diff --git a/src/libmscore/CMakeLists.txt b/src/libmscore/CMakeLists.txt index adc7cfc9a63b7..e7a409ad25522 100644 --- a/src/libmscore/CMakeLists.txt +++ b/src/libmscore/CMakeLists.txt @@ -165,11 +165,15 @@ set(MODULE_SRC measure.h measurenumber.cpp measurenumber.h + measurenumberbase.cpp + measurenumberbase.h measurerepeat.cpp measurerepeat.h midimapping.cpp mmrest.cpp mmrest.h + mmrestrange.cpp + mmrestrange.h mscore.cpp mscore.h mscoreview.cpp diff --git a/src/libmscore/ambitus.cpp b/src/libmscore/ambitus.cpp index 78c221fb3a413..72aead68a77de 100644 --- a/src/libmscore/ambitus.cpp +++ b/src/libmscore/ambitus.cpp @@ -485,6 +485,7 @@ void Ambitus::draw(QPainter* p) const qreal ledgerLineLength = score()->styleS(Sid::ledgerLineLength).val() * _spatium; qreal ledgerLineWidth = score()->styleS(Sid::ledgerLineWidth).val() * _spatium; p->setPen(QPen(curColor(), ledgerLineWidth, Qt::SolidLine, Qt::FlatCap)); + if (_topPos.y() - stepTolerance <= -step) { qreal xMin = _topPos.x() - ledgerLineLength; qreal xMax = _topPos.x() + headWidth() + ledgerLineLength; @@ -492,6 +493,7 @@ void Ambitus::draw(QPainter* p) const p->drawLine(QPointF(xMin, y), QPointF(xMax, y)); } } + if (_bottomPos.y() + stepTolerance >= numOfLines * step) { qreal xMin = _bottomPos.x() - ledgerLineLength; qreal xMax = _bottomPos.x() + headWidth() + ledgerLineLength; diff --git a/src/libmscore/element.cpp b/src/libmscore/element.cpp index 7e9e911d49f17..f613d088e8398 100644 --- a/src/libmscore/element.cpp +++ b/src/libmscore/element.cpp @@ -83,6 +83,7 @@ #include "textframe.h" #include "text.h" #include "measurenumber.h" +#include "mmrestrange.h" #include "textline.h" #include "tie.h" #include "timesig.h" @@ -1097,6 +1098,7 @@ Element* Element::create(ElementType type, Score* score) case ElementType::DYNAMIC: return new Dynamic(score); case ElementType::TEXT: return new Text(score); case ElementType::MEASURE_NUMBER: return new MeasureNumber(score); + case ElementType::MMREST_RANGE: return new MMRestRange(score); case ElementType::INSTRUMENT_NAME: return new InstrumentName(score); case ElementType::STAFF_TEXT: return new StaffText(score); case ElementType::SYSTEM_TEXT: return new SystemText(score); diff --git a/src/libmscore/layout.cpp b/src/libmscore/layout.cpp index 18919c2395726..51620ae896b41 100644 --- a/src/libmscore/layout.cpp +++ b/src/libmscore/layout.cpp @@ -4306,6 +4306,8 @@ void Score::layoutSystemElements(System* system, LayoutContext& lc) } Measure* m = toMeasure(mb); m->layoutMeasureNumber(); + m->layoutMMRestRange(); + // in continuous view, entire score is one system // but we only need to process the range if (lineMode() && (m->tick() < lc.startTick || m->tick() > lc.endTick)) { diff --git a/src/libmscore/measure.cpp b/src/libmscore/measure.cpp index 9db5ba166cfb8..889f38fbfd427 100644 --- a/src/libmscore/measure.cpp +++ b/src/libmscore/measure.cpp @@ -69,6 +69,7 @@ #include "system.h" #include "tempotext.h" #include "measurenumber.h" +#include "mmrestrange.h" #include "tie.h" #include "tiemap.h" #include "timesig.h" @@ -94,6 +95,7 @@ namespace Ms { MStaff::~MStaff() { delete m_noText; + delete m_mmRangeText; delete m_lines; delete m_vspacerUp; delete m_vspacerDown; @@ -102,6 +104,7 @@ MStaff::~MStaff() MStaff::MStaff(const MStaff& m) { m_noText = 0; + m_mmRangeText = 0; m_lines = new StaffLines(*m.m_lines); m_hasVoices = m.m_hasVoices; m_vspacerUp = 0; @@ -132,6 +135,9 @@ void MStaff::setScore(Score* score) if (m_noText) { m_noText->setScore(score); } + if (m_mmRangeText) { + m_mmRangeText->setScore(score); + } } //--------------------------------------------------------- @@ -152,6 +158,9 @@ void MStaff::setTrack(int track) if (m_noText) { m_noText->setTrack(track); } + if (m_mmRangeText) { + m_mmRangeText->setTrack(track); + } } //--------------------------------------------------------- @@ -269,7 +278,6 @@ MStaff* Measure::mstaff(int staffIndex) const bool Measure::hasVoices(int staffIdx) const { MStaff* staff = mstaff(staffIdx); - return staff ? staff->hasVoices() : false; } @@ -321,6 +329,16 @@ void Measure::setStaffStemless(int staffIdx, bool stemless) } } +void Measure::setMMRangeText(int staffIdx, MMRestRange* t) +{ + m_mstaves[staffIdx]->setMMRangeText(t); +} + +MMRestRange* Measure::mmRangeText(int staffIdx) const +{ + return m_mstaves[staffIdx]->mmRangeText(); +} + //--------------------------------------------------------- // Measure //--------------------------------------------------------- @@ -464,7 +482,7 @@ AccidentalVal Measure::findAccidental(Segment* s, int staffIdx, int line, bool& //--------------------------------------------------------- // tick2pos -// return x position for tick relative to System +/// return x position for tick relative to System //--------------------------------------------------------- qreal Measure::tick2pos(Fraction tck) const @@ -617,6 +635,51 @@ void Measure::layoutMeasureNumber() } } +void Measure::layoutMMRestRange() +{ + if (!isMMRest() || !score()->styleB(Sid::mmRestShowMeasureNumberRange)) { + // Remove existing + for (unsigned staffIdx = 0; staffIdx < m_mstaves.size(); ++staffIdx) { + MStaff* ms = m_mstaves[staffIdx]; + MMRestRange* rr = ms->mmRangeText(); + if (rr) { + if (rr->generated()) { + score()->removeElement(rr); + } else { + score()->undo(new RemoveElement(rr)); + } + } + } + + return; + } + + QString s; + if (mmRestCount() > 1) { + // middle char is an en dash (not em) + s = QString("%1–%2").arg(no() + 1).arg(no() + mmRestCount()); + } else { + // If the minimum range to create a mmrest is set to 1, + // then simply show the measure number as there is no range + s = QString("%1").arg(no() + 1); + } + + for (unsigned staffIdx = 0; staffIdx < m_mstaves.size(); ++staffIdx) { + MStaff* ms = m_mstaves[staffIdx]; + MMRestRange* rr = ms->mmRangeText(); + if (!rr) { + rr = new MMRestRange(score()); + rr->setTrack(staffIdx * VOICES); + rr->setGenerated(true); + rr->setParent(this); + add(rr); + } + // setXmlText is reimplemented to take care of brackets + rr->setXmlText(s); + rr->layout(); + } +} + //--------------------------------------------------------- // layout2 // called after layout of page @@ -866,6 +929,15 @@ void Measure::add(Element* e) } break; + case ElementType::MMREST_RANGE: + if (e->staffIdx() < int(m_mstaves.size())) { + if (e->isStyled(Pid::OFFSET)) { + e->setOffset(e->propertyDefault(Pid::OFFSET).toPointF()); + } + m_mstaves[e->staffIdx()]->setMMRangeText(toMMRestRange(e)); + } + break; + case ElementType::SPACER: { Spacer* sp = toSpacer(e); @@ -964,6 +1036,10 @@ void Measure::remove(Element* e) m_mstaves[e->staffIdx()]->setNoText(nullptr); break; + case ElementType::MMREST_RANGE: + m_mstaves[e->staffIdx()]->setMMRangeText(nullptr); + break; + case ElementType::SPACER: switch (toSpacer(e)->spacerType()) { case SpacerType::DOWN: @@ -1932,6 +2008,10 @@ void Measure::write(XmlWriter& xml, int staff, bool writeSystemElements, bool fo mstaff->noText()->write(xml); } + if (mstaff->mmRangeText() && !mstaff->mmRangeText()->generated()) { + mstaff->mmRangeText()->write(xml); + } + if (mstaff->vspacerUp()) { xml.tag("vspacerUp", mstaff->vspacerUp()->gap() / _spatium); } @@ -2085,6 +2165,11 @@ void Measure::read(XmlReader& e, int staffIdx) noText->read(e); noText->setTrack(e.track()); add(noText); + } else if (tag == "MMRestRange") { + MMRestRange* range = new MMRestRange(score()); + range->read(e); + range->setTrack(e.track()); + add(range); } else if (MeasureBase::readProperties(e)) { } else { e.unknown(); diff --git a/src/libmscore/measure.h b/src/libmscore/measure.h index cede87728bfc5..9f84fde36722b 100644 --- a/src/libmscore/measure.h +++ b/src/libmscore/measure.h @@ -29,6 +29,7 @@ class Tuplet; class Staff; class Chord; class MeasureNumber; +class MMRestRange; class ChordRest; class Score; class MuseScoreView; @@ -69,6 +70,9 @@ class MStaff MeasureNumber* noText() const { return m_noText; } void setNoText(MeasureNumber* t) { m_noText = t; } + MMRestRange* mmRangeText() const { return m_mmRangeText; } + void setMMRangeText(MMRestRange* r) { m_mmRangeText = r; } + StaffLines* lines() const { return m_lines; } void setLines(StaffLines* l) { m_lines = l; } @@ -96,6 +100,7 @@ class MStaff private: MeasureNumber* m_noText { nullptr }; ///< Measure number text object + MMRestRange* m_mmRangeText { nullptr }; ///< Multi measure rest range text object StaffLines* m_lines { nullptr }; Spacer* m_vspacerUp { nullptr }; Spacer* m_vspacerDown { nullptr }; @@ -166,6 +171,9 @@ class Measure final : public MeasureBase MeasureNumber* noText(int staffIdx) const { return m_mstaves[staffIdx]->noText(); } void setNoText(int staffIdx, MeasureNumber* t) { m_mstaves[staffIdx]->setNoText(t); } + void setMMRangeText(int staffIdx, MMRestRange*); + MMRestRange* mmRangeText(int staffIdx) const; + void createStaves(int); MeasureNumberMode measureNumberMode() const { return m_noMode; } @@ -195,6 +203,7 @@ class Measure final : public MeasureBase bool showsMeasureNumber(); bool showsMeasureNumberInAutoMode(); void layoutMeasureNumber(); + void layoutMMRestRange(); Chord* findChord(Fraction tick, int track); ChordRest* findChordRest(Fraction tick, int track); diff --git a/src/libmscore/measurenumber.cpp b/src/libmscore/measurenumber.cpp index 22b4206765755..be0cf57dd0876 100644 --- a/src/libmscore/measurenumber.cpp +++ b/src/libmscore/measurenumber.cpp @@ -19,9 +19,7 @@ #include "score.h" #include "measurenumber.h" -// #include "xml.h" #include "measure.h" -#include "staff.h" namespace Ms { //--------------------------------------------------------- @@ -37,10 +35,9 @@ static const ElementStyle measureNumberStyle { // MeasureNumber //--------------------------------------------------------- -MeasureNumber::MeasureNumber(Score* s) - : TextBase(s, Tid::MEASURE_NUMBER) +MeasureNumber::MeasureNumber(Score* s, Tid tid) + : MeasureNumberBase(s, tid) { - setFlag(ElementFlag::ON_STAFF, true); initElementStyle(&measureNumberStyle); setHPlacement(score()->styleV(Sid::measureNumberHPlacement).value()); @@ -52,43 +49,9 @@ MeasureNumber::MeasureNumber(Score* s) //--------------------------------------------------------- MeasureNumber::MeasureNumber(const MeasureNumber& other) - : TextBase(other) + : MeasureNumberBase(other) { - setFlag(ElementFlag::ON_STAFF, true); initElementStyle(&measureNumberStyle); - - setHPlacement(other.hPlacement()); -} - -//--------------------------------------------------------- -// getProperty -//--------------------------------------------------------- - -QVariant MeasureNumber::getProperty(Pid id) const -{ - switch (id) { - case Pid::HPLACEMENT: - return int(hPlacement()); - default: - return TextBase::getProperty(id); - } -} - -//--------------------------------------------------------- -// setProperty -//--------------------------------------------------------- - -bool MeasureNumber::setProperty(Pid id, const QVariant& val) -{ - switch (id) { - case Pid::HPLACEMENT: - setHPlacement(HPlacement(val.toInt())); - setLayoutInvalid(); - triggerLayout(); - return true; - default: - return TextBase::setProperty(id, val); - } } //--------------------------------------------------------- @@ -105,109 +68,7 @@ QVariant MeasureNumber::propertyDefault(Pid id) const case Pid::HPLACEMENT: return score()->styleV(Sid::measureNumberHPlacement); default: - return TextBase::propertyDefault(id); - } -} - -//--------------------------------------------------------- -// readProperties -//--------------------------------------------------------- - -bool MeasureNumber::readProperties(XmlReader& xml) -{ - if (readProperty(xml.name(), xml, Pid::HPLACEMENT)) { - return true; - } else { - return TextBase::readProperties(xml); - } -} - -//--------------------------------------------------------- -// layout -//--------------------------------------------------------- - -void MeasureNumber::layout() -{ - setPos(QPointF()); - if (!parent()) { - setOffset(0.0, 0.0); - } - - // TextBase::layout1() needs to be called even if there's no measure attached to it. - // This happens for example in the palettes. - TextBase::layout1(); - // this could be if (!measure()) but it is the same as current and slower - // See implementation of MeasureNumber::measure(). - if (!parent()) { - return; - } - - static constexpr qreal MN_DEFAULT_Y_OFFSET = 2.0; - if (placeBelow()) { - qreal yoff = MN_DEFAULT_Y_OFFSET * spatium() + bbox().height(); - - // If there is only one line, the barline spans outside the staff lines, so the default position is not correct. - if (staff()->constStaffType(measure()->tick())->lines() == 1) { - yoff += 2 * spatium(); - } else { - yoff += staff()->height(); - } - - rypos() = yoff; - } else { - qreal yoff = -1 * MN_DEFAULT_Y_OFFSET * spatium(); - - // If there is only one line, the barline spans outside the staff lines, so the default position is not correct. - if (staff()->constStaffType(measure()->tick())->lines() == 1) { - yoff -= 2 * spatium(); - } - - rypos() = yoff; - } - - if (hPlacement() == HPlacement::CENTER) { - // measure numbers should be centered over where there can be notes. - // This means that header and trailing segments should be ignored, - // which includes all timesigs, clefs, keysigs, etc. - // This is how it should be centered: - // |bb 4/4 notes-chords #| other measure | - // | ------18------ | other measure | - - // x1 - left measure position of free space - // x2 - right measure position of free space - - const Measure* mea = measure(); - - // find first chordrest - Segment* chordRest = mea->first(SegmentType::ChordRest); - - Segment* s1 = chordRest->prevActive(); - // unfortunately, using !s1->header() does not work - while (s1 && (s1->isChordRestType() - || s1->isBreathType() - || s1->isClefType() - || s1->isBarLineType() - || !s1->element(staffIdx() * VOICES))) { - s1 = s1->prevActive(); - } - - Segment* s2 = chordRest->next(); - // unfortunately, using !s1->trailer() does not work - while (s2 && (s2->isChordRestType() - || s2->isBreathType() - || s2->isClefType() - || s2->isBarLineType() - || !s2->element(staffIdx() * VOICES))) { - s2 = s2->nextActive(); - } - - // if s1/s2 does not exist, it means there is no header/trailer segment. Align with start/end of measure. - qreal x1 = s1 ? s1->x() + s1->minRight() : 0; - qreal x2 = s2 ? s2->x() - s2->minLeft() : mea->width(); - - rxpos() = (x1 + x2) * 0.5; - } else if (hPlacement() == HPlacement::RIGHT) { - rxpos() = measure()->width(); + return MeasureNumberBase::propertyDefault(id); } } } // namespace MS diff --git a/src/libmscore/measurenumber.h b/src/libmscore/measurenumber.h index 8dd1b866122ce..4f173ab506b98 100644 --- a/src/libmscore/measurenumber.h +++ b/src/libmscore/measurenumber.h @@ -2,45 +2,41 @@ // MuseScore // Music Composition & Notation // -// Copyright (C) 2014 Werner Schweer +// Copyright (C) 2020 MuseScore BVBA and others // // This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 -// as published by the Free Software Foundation and appearing in -// the file LICENCE.GPL +// it under the terms of the GNU General Public License version 2. +// +// 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, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. //============================================================================= #ifndef __MEASURENUMBER_H__ #define __MEASURENUMBER_H__ -#include "textbase.h" +#include "measurenumberbase.h" namespace Ms { //--------------------------------------------------------- // MeasureNumber //--------------------------------------------------------- -class MeasureNumber final : public TextBase +class MeasureNumber : public MeasureNumberBase { - M_PROPERTY(HPlacement, hPlacement, setHPlacement) // Horizontal Placement - public: - MeasureNumber(Score* s = nullptr); + MeasureNumber(Score* = nullptr, Tid tid = Tid::MEASURE_NUMBER); MeasureNumber(const MeasureNumber& other); virtual ElementType type() const override { return ElementType::MEASURE_NUMBER; } virtual MeasureNumber* clone() const override { return new MeasureNumber(*this); } - virtual QVariant getProperty(Pid id) const override; - virtual bool setProperty(Pid id, const QVariant& val) override; virtual QVariant propertyDefault(Pid id) const override; - - virtual bool readProperties(XmlReader&) override; - - virtual void layout() override; - Measure* measure() const { return toMeasure(parent()); } - - virtual bool isEditable() const override { return false; } // The measure numbers' text should not be editable }; } // namespace Ms diff --git a/src/libmscore/measurenumberbase.cpp b/src/libmscore/measurenumberbase.cpp new file mode 100644 index 0000000000000..ca4f3b627f363 --- /dev/null +++ b/src/libmscore/measurenumberbase.cpp @@ -0,0 +1,193 @@ +//============================================================================= +// MuseScore +// Music Composition & Notation +// +// Copyright (C) 2020 MuseScore BVBA and others +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2. +// +// 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, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +//============================================================================= + +#include "score.h" +#include "measurenumberbase.h" +#include "measure.h" +#include "staff.h" + +namespace Ms { +//--------------------------------------------------------- +// MeasureNumberBase +//--------------------------------------------------------- + +MeasureNumberBase::MeasureNumberBase(Score* s, Tid tid) + : TextBase(s, tid) +{ + setFlag(ElementFlag::ON_STAFF, true); +} + +//--------------------------------------------------------- +// MeasureNumberBase +// Copy constructor +//--------------------------------------------------------- + +MeasureNumberBase::MeasureNumberBase(const MeasureNumberBase& other) + : TextBase(other) +{ + setFlag(ElementFlag::ON_STAFF, true); + setHPlacement(other.hPlacement()); +} + +//--------------------------------------------------------- +// getProperty +//--------------------------------------------------------- + +QVariant MeasureNumberBase::getProperty(Pid id) const +{ + switch (id) { + case Pid::HPLACEMENT: + return int(hPlacement()); + default: + return TextBase::getProperty(id); + } +} + +//--------------------------------------------------------- +// setProperty +//--------------------------------------------------------- + +bool MeasureNumberBase::setProperty(Pid id, const QVariant& val) +{ + switch (id) { + case Pid::HPLACEMENT: + setHPlacement(HPlacement(val.toInt())); + setLayoutInvalid(); + triggerLayout(); + return true; + default: + return TextBase::setProperty(id, val); + } +} + +//--------------------------------------------------------- +// propertyDefault +//--------------------------------------------------------- + +QVariant MeasureNumberBase::propertyDefault(Pid id) const +{ + switch (id) { + case Pid::SUB_STYLE: + return int(Tid::DEFAULT); + default: + return TextBase::propertyDefault(id); + } +} + +//--------------------------------------------------------- +// readProperties +//--------------------------------------------------------- + +bool MeasureNumberBase::readProperties(XmlReader& xml) +{ + if (readProperty(xml.name(), xml, Pid::HPLACEMENT)) { + return true; + } else { + return TextBase::readProperties(xml); + } +} + +//--------------------------------------------------------- +// layout +//--------------------------------------------------------- + +void MeasureNumberBase::layout() +{ + setPos(QPointF()); + if (!parent()) { + setOffset(0.0, 0.0); + } + + // TextBase::layout1() needs to be called even if there's no measure attached to it. + // This happens for example in the palettes. + TextBase::layout1(); + // this could be if (!measure()) but it is the same as current and slower + // See implementation of MeasureNumberBase::measure(). + if (!parent()) { + return; + } + + if (placeBelow()) { + qreal yoff = bbox().height(); + + // If there is only one line, the barline spans outside the staff lines, so the default position is not correct. + if (staff()->constStaffType(measure()->tick())->lines() == 1) { + yoff += 2.0 * spatium(); + } else { + yoff += staff()->height(); + } + + rypos() = yoff; + } else { + qreal yoff = 0.0; + + // If there is only one line, the barline spans outside the staff lines, so the default position is not correct. + if (staff()->constStaffType(measure()->tick())->lines() == 1) { + yoff -= 2.0 * spatium(); + } + + rypos() = yoff; + } + + if (hPlacement() == HPlacement::CENTER) { + // measure numbers should be centered over where there can be notes. + // This means that header and trailing segments should be ignored, + // which includes all timesigs, clefs, keysigs, etc. + // This is how it should be centered: + // |bb 4/4 notes-chords #| other measure | + // | ------18------ | other measure | + + // x1 - left measure position of free space + // x2 - right measure position of free space + + const Measure* mea = measure(); + + // find first chordrest + Segment* chordRest = mea->first(SegmentType::ChordRest); + + Segment* s1 = chordRest->prevActive(); + // unfortunately, using !s1->header() does not work + while (s1 && (s1->isChordRestType() + || s1->isBreathType() + || s1->isClefType() + || s1->isBarLineType() + || !s1->element(staffIdx() * VOICES))) { + s1 = s1->prevActive(); + } + + Segment* s2 = chordRest->next(); + // unfortunately, using !s1->trailer() does not work + while (s2 && (s2->isChordRestType() + || s2->isBreathType() + || s2->isClefType() + || s2->isBarLineType() + || !s2->element(staffIdx() * VOICES))) { + s2 = s2->nextActive(); + } + + // if s1/s2 does not exist, it means there is no header/trailer segment. Align with start/end of measure. + qreal x1 = s1 ? s1->x() + s1->minRight() : 0; + qreal x2 = s2 ? s2->x() - s2->minLeft() : mea->width(); + + rxpos() = (x1 + x2) * 0.5; + } else if (hPlacement() == HPlacement::RIGHT) { + rxpos() = measure()->width(); + } +} +} // namespace MS diff --git a/src/libmscore/measurenumberbase.h b/src/libmscore/measurenumberbase.h new file mode 100644 index 0000000000000..bee5ce9a898ad --- /dev/null +++ b/src/libmscore/measurenumberbase.h @@ -0,0 +1,53 @@ +//============================================================================= +// MuseScore +// Music Composition & Notation +// +// Copyright (C) 2020 MuseScore BVBA and others +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2. +// +// 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, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +//============================================================================= + +#ifndef __MEASURENUMBERBASE_H__ +#define __MEASURENUMBERBASE_H__ + +#include "textbase.h" + +namespace Ms { +//--------------------------------------------------------- +// MeasureNumberBase +/// The basic element making measure numbers. +/// Reimplemented by MMRestRange +//--------------------------------------------------------- + +class MeasureNumberBase : public TextBase +{ + M_PROPERTY(HPlacement, hPlacement, setHPlacement) // Horizontal Placement + +public: + MeasureNumberBase(Score* = nullptr, Tid = Tid::DEFAULT); + MeasureNumberBase(const MeasureNumberBase& other); + + virtual QVariant getProperty(Pid id) const override; + virtual bool setProperty(Pid id, const QVariant& val) override; + virtual QVariant propertyDefault(Pid id) const override; + + virtual bool readProperties(XmlReader&) override; + + virtual void layout() override; + Measure* measure() const { return toMeasure(parent()); } + + virtual bool isEditable() const override { return false; } // The measure numbers' text should not be editable +}; +} // namespace Ms + +#endif diff --git a/src/libmscore/mmrestrange.cpp b/src/libmscore/mmrestrange.cpp new file mode 100644 index 0000000000000..21c2f2ac4e6ac --- /dev/null +++ b/src/libmscore/mmrestrange.cpp @@ -0,0 +1,118 @@ +//============================================================================= +// MuseScore +// Music Composition & Notation +// +// Copyright (C) 2020 MuseScore BVBA and others +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2. +// +// 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, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +//============================================================================= + +#include "score.h" +#include "mmrestrange.h" +#include "measure.h" + +namespace Ms { +//--------------------------------------------------------- +// mmRestRangeStyle +//--------------------------------------------------------- + +static const ElementStyle mmRestRangeStyle { + { Sid::mmRestRangeBracketType, Pid::MMREST_RANGE_BRACKET_TYPE }, + { Sid::mmRestRangeVPlacement, Pid::PLACEMENT }, + { Sid::mmRestRangeHPlacement, Pid::HPLACEMENT } +}; + +MMRestRange::MMRestRange(Score* s) + : MeasureNumberBase(s, Tid::MMREST_RANGE) +{ + initElementStyle(&mmRestRangeStyle); +} + +//--------------------------------------------------------- +// MMRestRange +/// Copy constructor +//--------------------------------------------------------- + +MMRestRange::MMRestRange(const MMRestRange& other) + : MeasureNumberBase(other) +{ + initElementStyle(&mmRestRangeStyle); +} + +QVariant MMRestRange::getProperty(Pid id) const +{ + switch (id) { + case Pid::MMREST_RANGE_BRACKET_TYPE: + return int(bracketType()); + default: + return MeasureNumberBase::getProperty(id); + } +} + +bool MMRestRange::setProperty(Pid id, const QVariant& val) +{ + switch (id) { + case Pid::MMREST_RANGE_BRACKET_TYPE: + setBracketType(MMRestRangeBracketType(val.toInt())); + setLayoutInvalid(); + triggerLayout(); + return true; + default: + return MeasureNumberBase::setProperty(id, val); + } +} + +QVariant MMRestRange::propertyDefault(Pid id) const +{ + switch (id) { + case Pid::SUB_STYLE: + return int(Tid::MMREST_RANGE); + case Pid::PLACEMENT: + return score()->styleV(Sid::mmRestRangeVPlacement); + case Pid::HPLACEMENT: + return score()->styleV(Sid::mmRestRangeHPlacement); + default: + return MeasureNumberBase::propertyDefault(id); + } +} + +bool MMRestRange::readProperties(XmlReader& xml) +{ + if (readProperty(xml.name(), xml, Pid::MMREST_RANGE_BRACKET_TYPE)) { + return true; + } else { + return MeasureNumberBase::readProperties(xml); + } +} + +//--------------------------------------------------------- +// setXmlText +/// This is reimplemented from TextBase::setXmlText to take care of the brackets +//--------------------------------------------------------- + +void MMRestRange::setXmlText(const QString& s) +{ + switch (bracketType()) { + case MMRestRangeBracketType::BRACKETS: + TextBase::setXmlText("[" + s + "]"); + break; + case MMRestRangeBracketType::PARENTHESES: + TextBase::setXmlText("(" + s + ")"); + break; + case MMRestRangeBracketType::NONE: + default: + TextBase::setXmlText(s); + break; + } +} +} // namespace MS diff --git a/src/libmscore/mmrestrange.h b/src/libmscore/mmrestrange.h new file mode 100644 index 0000000000000..050be181231c9 --- /dev/null +++ b/src/libmscore/mmrestrange.h @@ -0,0 +1,53 @@ +//============================================================================= +// MuseScore +// Music Composition & Notation +// +// Copyright (C) 2020 MuseScore BVBA and others +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2. +// +// 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, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +//============================================================================= + +#ifndef __MMRESTRANGE_H__ +#define __MMRESTRANGE_H__ + +#include "measurenumberbase.h" +#include "property.h" + +namespace Ms { +//--------------------------------------------------------- +// MMRestRange +//--------------------------------------------------------- + +class MMRestRange : public MeasureNumberBase +{ + /// Bracketing: [18-24], (18-24) or 18-24 + M_PROPERTY(MMRestRangeBracketType, bracketType, setBracketType) + +public: + MMRestRange(Score* s = nullptr); + MMRestRange(const MMRestRange& other); + + virtual ElementType type() const override { return ElementType::MMREST_RANGE; } + virtual MMRestRange* clone() const override { return new MMRestRange(*this); } + + virtual QVariant getProperty(Pid id) const override; + virtual bool setProperty(Pid id, const QVariant& val) override; + virtual QVariant propertyDefault(Pid id) const override; + + virtual bool readProperties(XmlReader&) override; + + virtual void setXmlText(const QString&) override; +}; +} // namespace Ms + +#endif diff --git a/src/libmscore/property.cpp b/src/libmscore/property.cpp index af83e4b8da883..3acffebeba7c3 100644 --- a/src/libmscore/property.cpp +++ b/src/libmscore/property.cpp @@ -269,6 +269,8 @@ static constexpr PropertyMetaData propertyList[] = { DUMMY_QT_TRANSLATE_NOOP("propertyName", "placement") }, { Pid::HPLACEMENT, false, "hplacement", P_TYPE::HPLACEMENT, DUMMY_QT_TRANSLATE_NOOP("propertyName", "horizontal placement") }, + { Pid::MMREST_RANGE_BRACKET_TYPE, false, "mmrestRangeBracketType", P_TYPE::INT, + DUMMY_QT_TRANSLATE_NOOP("propertyName", "multimeasure rest range bracket type") }, { Pid::VELOCITY, false, "velocity", P_TYPE::INT, DUMMY_QT_TRANSLATE_NOOP("propertyName", "velocity") }, { Pid::JUMP_TO, true, "jumpTo", P_TYPE::STRING, diff --git a/src/libmscore/property.h b/src/libmscore/property.h index 2fe94ed25d6c8..69e7687c94e7a 100644 --- a/src/libmscore/property.h +++ b/src/libmscore/property.h @@ -180,6 +180,7 @@ enum class Pid { CHANGE_METHOD, PLACEMENT, // Goes with P_TYPE::PLACEMENT HPLACEMENT, // Goes with P_TYPE::HPLACEMENT + MMREST_RANGE_BRACKET_TYPE, // The brackets used arond the measure numbers indicating the range covered by the mmrest VELOCITY, JUMP_TO, PLAY_UNTIL, @@ -418,7 +419,7 @@ enum class P_TYPE : char { FONT, SUB_STYLE, ALIGN, - CHANGE_METHOD, // enum class VeloChangeMethod (for single notedynamics) + CHANGE_METHOD, // enum class VeloChangeMethod (for single note dynamics) CHANGE_SPEED, // enum class Dynamic::Speed CLEF_TYPE, // enum class ClefType DYNAMIC_TYPE, // enum class Dynamic::Type diff --git a/src/libmscore/scoreElement.cpp b/src/libmscore/scoreElement.cpp index fbaa7c40a502c..f3d4958084e0c 100644 --- a/src/libmscore/scoreElement.cpp +++ b/src/libmscore/scoreElement.cpp @@ -35,6 +35,7 @@ static const ElementName elementNames[] = { { ElementType::SYMBOL, "Symbol", QT_TRANSLATE_NOOP("elementName", "Symbol") }, { ElementType::TEXT, "Text", QT_TRANSLATE_NOOP("elementName", "Text") }, { ElementType::MEASURE_NUMBER, "MeasureNumber", QT_TRANSLATE_NOOP("elementName", "Measure Number") }, + { ElementType::MMREST_RANGE, "MMRestRange", QT_TRANSLATE_NOOP("elementName", "Multimeasure Rest Range") }, { ElementType::INSTRUMENT_NAME, "InstrumentName", QT_TRANSLATE_NOOP("elementName", "Instrument Name") }, { ElementType::SLUR_SEGMENT, "SlurSegment", QT_TRANSLATE_NOOP("elementName", "Slur Segment") }, { ElementType::TIE_SEGMENT, "TieSegment", QT_TRANSLATE_NOOP("elementName", "Tie Segment") }, @@ -933,6 +934,7 @@ bool ScoreElement::isTextBase() const || type() == ElementType::TEMPO_TEXT || type() == ElementType::INSTRUMENT_NAME || type() == ElementType::MEASURE_NUMBER + || type() == ElementType::MMREST_RANGE || type() == ElementType::STICKING ; } diff --git a/src/libmscore/scoreElement.h b/src/libmscore/scoreElement.h index c93b2cb4d2e9c..345536265733b 100644 --- a/src/libmscore/scoreElement.h +++ b/src/libmscore/scoreElement.h @@ -132,6 +132,7 @@ class VibratoSegment; class PalmMute; class PalmMuteSegment; class MeasureNumber; +class MMRestRange; class StaffTextBase; @@ -387,6 +388,7 @@ class ScoreElement CONVERT(Page, PAGE) CONVERT(Text, TEXT) CONVERT(MeasureNumber, MEASURE_NUMBER) + CONVERT(MMRestRange, MMREST_RANGE) CONVERT(StaffText, STAFF_TEXT) CONVERT(SystemText, SYSTEM_TEXT) CONVERT(BracketItem, BRACKET_ITEM) @@ -652,6 +654,7 @@ CONVERT(InstrumentChange) CONVERT(StaffTypeChange) CONVERT(Text) CONVERT(MeasureNumber) +CONVERT(MMRestRange) CONVERT(Hairpin) CONVERT(HairpinSegment) CONVERT(Bend) diff --git a/src/libmscore/style.cpp b/src/libmscore/style.cpp index d811475ff5294..47d836c095c76 100644 --- a/src/libmscore/style.cpp +++ b/src/libmscore/style.cpp @@ -882,6 +882,27 @@ static const StyleType styleTypes[] { { Sid::measureNumberFrameFgColor, "measureNumberFrameFgColor", QColor(0, 0, 0, 255) }, { Sid::measureNumberFrameBgColor, "measureNumberFrameBgColor", QColor(255, 255, 255, 0) }, + { Sid::mmRestShowMeasureNumberRange, "mmRestShowMeasureNumberRange", false }, + { Sid::mmRestRangeBracketType, "mmRestRangeBracketType", int(MMRestRangeBracketType::BRACKETS) }, + + { Sid::mmRestRangeFontFace, "mmRestRangeFontFace", "Edwin" }, + { Sid::mmRestRangeFontSize, "mmRestRangeFontSize", 8.0 }, + { Sid::mmRestRangeFontSpatiumDependent, "mmRestRangeFontSpatiumDependent", false }, + { Sid::mmRestRangeFontStyle, "mmRestRangeFontStyle", int(FontStyle::Italic) }, + { Sid::mmRestRangeColor, "mmRestRangeColor", QColor(0, 0, 0, 255) }, + { Sid::mmRestRangePosAbove, "measureNumberPosAbove", QPointF(0.0, -3.0) }, + { Sid::mmRestRangePosBelow, "measureNumberPosBelow", QPointF(0.0, 1.0) }, + { Sid::mmRestRangeOffsetType, "mmRestRangeOffsetType", int(OffsetType::SPATIUM) }, + { Sid::mmRestRangeVPlacement, "mmRestRangeVPlacement", int(Placement::BELOW) }, + { Sid::mmRestRangeHPlacement, "mmRestRangeHPlacement", int(HPlacement::CENTER) }, + { Sid::mmRestRangeAlign, "mmRestRangeAlign", QVariant::fromValue(Align::HCENTER | Align::BASELINE) }, + { Sid::mmRestRangeFrameType, "mmRestRangeFrameType", int(FrameType::NO_FRAME) }, + { Sid::mmRestRangeFramePadding, "mmRestRangeFramePadding", 0.2 }, + { Sid::mmRestRangeFrameWidth, "mmRestRangeFrameWidth", 0.1 }, + { Sid::mmRestRangeFrameRound, "mmRestRangeFrameRound", 0 }, + { Sid::mmRestRangeFrameFgColor, "mmRestRangeFrameFgColor", QColor(0, 0, 0, 255) }, + { Sid::mmRestRangeFrameBgColor, "mmRestRangeFrameBgColor", QColor(255, 255, 255, 0) }, + { Sid::translatorFontFace, "translatorFontFace", "FreeSerif" }, { Sid::translatorFontSize, "translatorFontSize", 11.0 }, { Sid::translatorFontSpatiumDependent, "translatorFontSpatiumDependent", false }, @@ -1693,6 +1714,22 @@ const TextStyle measureNumberTextStyle { { { Sid::measureNumberFrameBgColor, Pid::FRAME_BG_COLOR }, } }; +const TextStyle mmRestRangeTextStyle { { + { Sid::mmRestRangeFontFace, Pid::FONT_FACE }, + { Sid::mmRestRangeFontSize, Pid::FONT_SIZE }, + { Sid::mmRestRangeFontSpatiumDependent, Pid::SIZE_SPATIUM_DEPENDENT }, + { Sid::mmRestRangeFontStyle, Pid::FONT_STYLE }, + { Sid::mmRestRangeColor, Pid::COLOR }, + { Sid::mmRestRangeAlign, Pid::ALIGN }, + { Sid::mmRestRangePosAbove, Pid::OFFSET }, + { Sid::mmRestRangeFrameType, Pid::FRAME_TYPE }, + { Sid::mmRestRangeFramePadding, Pid::FRAME_PADDING }, + { Sid::mmRestRangeFrameWidth, Pid::FRAME_WIDTH }, + { Sid::mmRestRangeFrameRound, Pid::FRAME_ROUND }, + { Sid::mmRestRangeFrameFgColor, Pid::FRAME_FG_COLOR }, + { Sid::mmRestRangeFrameBgColor, Pid::FRAME_BG_COLOR }, +} }; + const TextStyle translatorTextStyle { { { Sid::translatorFontFace, Pid::FONT_FACE }, { Sid::translatorFontSize, Pid::FONT_SIZE }, @@ -2320,6 +2357,7 @@ static constexpr std::array textStyles { { { QT_TRANSLATE_NOOP("TextStyle", "Tempo"), &tempoTextStyle, Tid::TEMPO }, { QT_TRANSLATE_NOOP("TextStyle", "Metronome"), &metronomeTextStyle, Tid::METRONOME }, { QT_TRANSLATE_NOOP("TextStyle", "Measure Number"), &measureNumberTextStyle, Tid::MEASURE_NUMBER }, + { QT_TRANSLATE_NOOP("TextStyle", "Multi-Measure Rest Range"), &mmRestRangeTextStyle, Tid::MMREST_RANGE }, { QT_TRANSLATE_NOOP("TextStyle", "Translator"), &translatorTextStyle, Tid::TRANSLATOR }, { QT_TRANSLATE_NOOP("TextStyle", "Tuplet"), &tupletTextStyle, Tid::TUPLET }, @@ -2437,6 +2475,7 @@ static const std::vector _primaryTextStyles = { Tid::HEADER, Tid::FOOTER, Tid::MEASURE_NUMBER, + Tid::MMREST_RANGE, Tid::INSTRUMENT_EXCERPT, Tid::INSTRUMENT_CHANGE, Tid::STAFF, diff --git a/src/libmscore/style.h b/src/libmscore/style.h index a21c0ae2689a2..7af54079d590c 100644 --- a/src/libmscore/style.h +++ b/src/libmscore/style.h @@ -850,6 +850,27 @@ enum class Sid { measureNumberFrameFgColor, measureNumberFrameBgColor, + mmRestShowMeasureNumberRange, + mmRestRangeBracketType, + + mmRestRangeFontFace, + mmRestRangeFontSize, + mmRestRangeFontSpatiumDependent, + mmRestRangeFontStyle, + mmRestRangeColor, + mmRestRangePosAbove, + mmRestRangePosBelow, + mmRestRangeOffsetType, + mmRestRangeVPlacement, + mmRestRangeHPlacement, + mmRestRangeAlign, + mmRestRangeFrameType, + mmRestRangeFramePadding, + mmRestRangeFrameWidth, + mmRestRangeFrameRound, + mmRestRangeFrameFgColor, + mmRestRangeFrameBgColor, + translatorFontFace, translatorFontSize, translatorFontSpatiumDependent, diff --git a/src/libmscore/textbase.cpp b/src/libmscore/textbase.cpp index 98937f6df49e2..107bf9cbd6569 100644 --- a/src/libmscore/textbase.cpp +++ b/src/libmscore/textbase.cpp @@ -2813,6 +2813,7 @@ QString TextBase::accessibleInfo() const case Tid::POET: case Tid::TRANSLATOR: case Tid::MEASURE_NUMBER: + case Tid::MMREST_RANGE: rez = score() ? score()->getTextStyleUserName(tid()) : textStyleUserName(tid()); break; default: @@ -2842,6 +2843,7 @@ QString TextBase::screenReaderInfo() const case Tid::POET: case Tid::TRANSLATOR: case Tid::MEASURE_NUMBER: + case Tid::MMREST_RANGE: rez = score() ? score()->getTextStyleUserName(tid()) : textStyleUserName(tid()); break; default: @@ -3180,6 +3182,8 @@ Sid TextBase::offsetSid() const return above ? Sid::systemTextPosAbove : Sid::systemTextPosBelow; case Tid::TEMPO: return above ? Sid::tempoPosAbove : Sid::tempoPosBelow; + case Tid::MMREST_RANGE: + return above ? Sid::mmRestRangePosAbove : Sid::mmRestRangePosBelow; default: break; } diff --git a/src/libmscore/textbase.h b/src/libmscore/textbase.h index a763faae47d35..65717ce9da536 100644 --- a/src/libmscore/textbase.h +++ b/src/libmscore/textbase.h @@ -288,7 +288,7 @@ class TextBase : public Element static QString plainToXmlText(const QString& s) { return s.toHtmlEscaped(); } void setPlainText(const QString& t) { setXmlText(plainToXmlText(t)); } - void setXmlText(const QString&); + virtual void setXmlText(const QString&); QString xmlText() const; QString plainText() const; diff --git a/src/libmscore/types.h b/src/libmscore/types.h index 2a74480d9d5b7..ae227585cd92b 100644 --- a/src/libmscore/types.h +++ b/src/libmscore/types.h @@ -43,6 +43,7 @@ enum class ElementType { SYMBOL, TEXT, MEASURE_NUMBER, + MMREST_RANGE, INSTRUMENT_NAME, SLUR_SEGMENT, TIE_SEGMENT, @@ -416,6 +417,16 @@ enum class HPlacement { ///\} }; +//--------------------------------------------------------- +// MMRestRangeBracketType +//--------------------------------------------------------- + +enum class MMRestRangeBracketType { + ///.\{ + BRACKETS, PARENTHESES, NONE + ///\} +}; + //--------------------------------------------------------- // OffsetType //--------------------------------------------------------- @@ -493,6 +504,7 @@ enum class Tid { TEMPO, METRONOME, MEASURE_NUMBER, + MMREST_RANGE, TRANSLATOR, TUPLET, SYSTEM,