MuseScore/src/engraving/libmscore/measurenumberbase.cpp

201 lines
6 KiB
C++

/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "measurenumberbase.h"
#include "io/xml.h"
#include "score.h"
#include "measure.h"
#include "staff.h"
using namespace mu;
using namespace mu::engraving;
namespace Ms {
//---------------------------------------------------------
// MeasureNumberBase
//---------------------------------------------------------
MeasureNumberBase::MeasureNumberBase(const ElementType& type, Measure* parent, Tid tid)
: TextBase(type, parent, 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
//---------------------------------------------------------
engraving::PropertyValue 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 PropertyValue& val)
{
switch (id) {
case Pid::HPLACEMENT:
setHPlacement(HPlacement(val.toInt()));
setLayoutInvalid();
triggerLayout();
return true;
default:
return TextBase::setProperty(id, val);
}
}
//---------------------------------------------------------
// propertyDefault
//---------------------------------------------------------
PropertyValue 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(PointF());
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