MuseScore/libmscore/barline.cpp
Marc Sabatella 4eadb70636 fix #287245: allow symbols and images on barlines
It sometimes comes up that the best way to solve some particular problem
would be to attach a symbol to a barline
(eg, think about the commonly-requested multimeasure repeat symbol).
But we don't support symbols on barlines, so you end up settling for
attaching to the first note or rest in the bar then moving manually,
and this adjustment does not necessarily survicve layout changes.

This commit adds direct support for symbols on barlines.
Also images, since the code involved is so similar, they are handled here as well.
Symbols and images are recorded as child elements,
which were already being laid out so no special handling was needed
beyond adding & removing, reading nd writing.
Although I did need to make sure the track & score were managed correctly.
2019-08-29 09:59:58 -06:00

1661 lines
66 KiB
C++

//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2002-2016 Werner Schweer
//
// 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
//=============================================================================
#include "barline.h"
#include "score.h"
#include "sym.h"
#include "staff.h"
#include "part.h"
#include "system.h"
#include "measure.h"
#include "segment.h"
#include "articulation.h"
#include "stafftype.h"
#include "xml.h"
#include "marker.h"
#include "stafflines.h"
#include "spanner.h"
#include "undo.h"
#include "fermata.h"
#include "symbol.h"
#include "image.h"
namespace Ms {
//---------------------------------------------------------
// undoChangeBarLineType
//---------------------------------------------------------
static void undoChangeBarLineType(BarLine* bl, BarLineType barType, bool allStaves)
{
Measure* m = bl->measure();
if (!m)
return;
if (barType == BarLineType::START_REPEAT) {
m = m->nextMeasure();
if (!m)
return;
}
else if (bl->barLineType() == BarLineType::START_REPEAT) {
if (barType != BarLineType::END_REPEAT)
m->undoChangeProperty(Pid::REPEAT_START, false);
m = m->prevMeasure();
if (!m)
return;
}
switch (barType) {
case BarLineType::END:
case BarLineType::NORMAL:
case BarLineType::DOUBLE:
case BarLineType::BROKEN:
case BarLineType::DOTTED: {
Segment* segment = bl->segment();
SegmentType segmentType = segment->segmentType();
if (segmentType == SegmentType::EndBarLine) {
// when setting barline type on mmrest, set for underlying measure (and linked staves)
// createMMRest will then set for the mmrest directly
Measure* m2 = m->isMMRest() ? m->mmRestLast() : m;
bool generated;
if (bl->barLineType() == barType)
generated = bl->generated(); // no change: keep current status
else if (!bl->generated() && (barType == BarLineType::NORMAL))
generated = true; // currently non-generated, changing to normal: assume generated
else
generated = false; // otherwise assume non-generated
if (allStaves) {
// use all staves of master score; we will take care of parts in loop through linked staves below
m2 = bl->masterScore()->tick2measure(m2->tick());
if (!m2)
return; // should never happen
segment = m2->undoGetSegment(segment->segmentType(), segment->tick());
}
const std::vector<Element*>& elist = allStaves ? segment->elist() : std::vector<Element*> { bl };
for (Element* e : elist) {
if (!e || !e->staff() || !e->isBarLine())
continue;
// handle linked staves/parts:
// barlines themselves are not necessarily linked,
// so use staffList to find linked staves
BarLine* sbl = toBarLine(e);
for (Staff* lstaff : sbl->staff()->staffList()) {
Score* lscore = lstaff->score();
int ltrack = lstaff->idx() * VOICES;
// handle mmrests:
// set the barline on the underlying measure
// this will copied to the mmrest during layout, in createMMRest
Measure* lmeasure = lscore->tick2measure(m2->tick());
if (!lmeasure)
continue;
lmeasure->undoChangeProperty(Pid::REPEAT_END, false);
Segment* lsegment = lmeasure->undoGetSegmentR(SegmentType::EndBarLine, lmeasure->ticks());
BarLine* lbl = toBarLine(lsegment->element(ltrack));
if (!lbl) {
lbl = new BarLine(lscore);
lbl->setParent(lsegment);
lbl->setTrack(ltrack);
lbl->setSpanStaff(lstaff->barLineSpan());
lbl->setSpanFrom(lstaff->barLineFrom());
lbl->setSpanTo(lstaff->barLineTo());
lbl->setBarLineType(barType);
lbl->setGenerated(generated);
lscore->addElement(lbl);
if (!generated)
lbl->linkTo(sbl);
}
else {
lscore->undo(new ChangeProperty(lbl, Pid::GENERATED, generated, PropertyFlags::NOSTYLE));
lscore->undo(new ChangeProperty(lbl, Pid::BARLINE_TYPE, QVariant::fromValue(barType), PropertyFlags::NOSTYLE));
// set generated flag before and after so it sticks on type change and also works on undo/redo
lscore->undo(new ChangeProperty(lbl, Pid::GENERATED, generated, PropertyFlags::NOSTYLE));
if (lbl != sbl && !generated && !lbl->links())
lscore->undo(new Link(lbl, sbl));
else if (lbl != sbl && generated && lbl->isLinked(sbl))
lscore->undo(new Unlink(lbl));
}
}
}
}
else if (segmentType == SegmentType::BeginBarLine) {
Segment* segment1 = m->undoGetSegmentR(SegmentType::BeginBarLine, Fraction(0, 1));
for (Element* e : segment1->elist()) {
if (e) {
e->score()->undo(new ChangeProperty(e, Pid::GENERATED, false, PropertyFlags::NOSTYLE));
e->score()->undo(new ChangeProperty(e, Pid::BARLINE_TYPE, QVariant::fromValue(barType), PropertyFlags::NOSTYLE));
// set generated flag before and after so it sticks on type change and also works on undo/redo
e->score()->undo(new ChangeProperty(e, Pid::GENERATED, false, PropertyFlags::NOSTYLE));
}
}
}
}
break;
case BarLineType::START_REPEAT: {
Measure* m2 = m->isMMRest() ? m->mmRestFirst() : m;
for (Score* lscore : m2->score()->scoreList()) {
Measure* lmeasure = lscore->tick2measure(m2->tick());
if (lmeasure)
lmeasure->undoChangeProperty(Pid::REPEAT_START, true);
}
}
break;
case BarLineType::END_REPEAT: {
Measure* m2 = m->isMMRest() ? m->mmRestLast() : m;
for (Score* lscore : m2->score()->scoreList()) {
Measure* lmeasure = lscore->tick2measure(m2->tick());
if (lmeasure)
lmeasure->undoChangeProperty(Pid::REPEAT_END, true);
}
}
break;
case BarLineType::END_START_REPEAT: {
Measure* m2 = m->isMMRest() ? m->mmRestLast() : m;
for (Score* lscore : m2->score()->scoreList()) {
Measure* lmeasure = lscore->tick2measure(m2->tick());
if (lmeasure) {
lmeasure->undoChangeProperty(Pid::REPEAT_END, true);
lmeasure = lmeasure->nextMeasure();
if (lmeasure)
lmeasure->undoChangeProperty(Pid::REPEAT_START, true);
}
}
}
break;
}
}
//---------------------------------------------------------
// BarLineEditData
//---------------------------------------------------------
class BarLineEditData : public ElementEditData {
public:
qreal yoff1;
qreal yoff2;
};
//---------------------------------------------------------
// BarLineTable
//---------------------------------------------------------
const std::vector<BarLineTableItem> BarLine::barLineTable {
{ BarLineType::NORMAL, QT_TRANSLATE_NOOP("Palette", "Normal barline"), "normal" },
{ BarLineType::DOUBLE, QT_TRANSLATE_NOOP("Palette", "Double barline"), "double" },
{ BarLineType::START_REPEAT, QT_TRANSLATE_NOOP("Palette", "Start repeat"), "start-repeat" },
{ BarLineType::END_REPEAT, QT_TRANSLATE_NOOP("Palette", "End repeat"), "end-repeat" },
{ BarLineType::BROKEN, QT_TRANSLATE_NOOP("Palette", "Dashed barline"), "dashed" },
{ BarLineType::END, QT_TRANSLATE_NOOP("Palette", "Final barline"), "end" },
{ BarLineType::END_START_REPEAT, QT_TRANSLATE_NOOP("Palette", "End-start repeat"), "end-start-repeat" },
{ BarLineType::DOTTED, QT_TRANSLATE_NOOP("Palette", "Dotted barline"), "dotted" },
};
//---------------------------------------------------------
// barLineTableItem
//---------------------------------------------------------
const BarLineTableItem* BarLine::barLineTableItem(unsigned i)
{
if (i >= barLineTable.size())
return 0;
return &barLineTable[i];
}
//---------------------------------------------------------
// userTypeName
//---------------------------------------------------------
QString BarLine::userTypeName(BarLineType t)
{
for (const auto& i : barLineTable) {
if (i.type == t)
return qApp->translate("Palette", i.userName);
}
return QString();
}
//---------------------------------------------------------
// barLineTypeName
//
// Instance form returning the name string of the bar line type and
// static form returning the name string for an arbitrary bar line type.
//---------------------------------------------------------
QString BarLine::barLineTypeName() const
{
return barLineTypeName(barLineType());
}
QString BarLine::barLineTypeName(BarLineType t)
{
for (const auto& i : barLineTable) {
if (i.type == t)
return i.name;
}
return QString("??");
}
//---------------------------------------------------------
// setBarLineType
//
// Set the bar line type from the type name string.
// Does not update _customSubtype or _generated flags: to be used when reading from a score file
//---------------------------------------------------------
void BarLine::setBarLineType(const QString& s)
{
_barLineType = barLineType(s);
}
//---------------------------------------------------------
// barLineType
//---------------------------------------------------------
BarLineType BarLine::barLineType(const QString& s)
{
for (const auto& i : barLineTable) {
if (i.name == s)
return i.type;
}
return BarLineType::NORMAL; // silent default
}
//---------------------------------------------------------
// BarLine
//---------------------------------------------------------
BarLine::BarLine(Score* s)
: Element(s)
{
setHeight(4 * spatium()); // for use in palettes
}
BarLine::BarLine(const BarLine& bl)
: Element(bl)
{
_spanStaff = bl._spanStaff;
_spanFrom = bl._spanFrom;
_spanTo = bl._spanTo;
_barLineType = bl._barLineType;
y1 = bl.y1;
y2 = bl.y2;
for (Element* e : bl._el)
add(e->clone());
}
BarLine::~BarLine()
{
qDeleteAll(_el);
}
//---------------------------------------------------------
// canvasPos
//---------------------------------------------------------
QPointF BarLine::canvasPos() const
{
QPointF pos = Element::canvasPos();
if (parent()) {
System* system = measure()->system();
qreal yoff = system ? system->staff(staffIdx())->y() : 0.0;
pos.ry() += yoff;
}
return pos;
}
//---------------------------------------------------------
// pagePos
//---------------------------------------------------------
QPointF BarLine::pagePos() const
{
if (segment() == 0)
return pos();
System* system = segment()->measure()->system();
qreal yp = y();
if (system) {
// get first not hidden staff
int startIdx = staffIdx();
int endIdx = startIdx + (spanStaff() ? 1 : 0);
int staffIdx1 = startIdx;
Staff* staff1 = score()->staff(staffIdx1);
SysStaff* sysStaff1 = system->staff(staffIdx1);
while (staff1 && sysStaff1 && !(sysStaff1->show() && staff1->show())) {
if (++staffIdx1 >= endIdx) {
// no visible staves spanned; just use first
staffIdx1 = startIdx;
break;
}
staff1 = score()->staff(staffIdx1);
sysStaff1 = system->staff(staffIdx1);
}
yp += system->staffYpage(staffIdx1);
}
return QPointF(pageX(), yp);
}
//---------------------------------------------------------
// prevVisiblespannedStaff
//---------------------------------------------------------
int prevVisibleSpannedStaff(const BarLine* bl)
{
Score* score = bl->score();
int staffIdx = bl->staffIdx();
Segment* segment = bl->segment();
for (int i = staffIdx - 1; i >= 0; --i) {
BarLine* nbl = toBarLine(segment->element(i * VOICES));
if (!nbl || !nbl->spanStaff())
break;
Staff* s = score->staff(i);
if (s->part()->show() && bl->measure()->visible(i)) {
return i;
}
}
return staffIdx;
}
//---------------------------------------------------------
// nextVisiblespannedStaff
//---------------------------------------------------------
int nextVisibleSpannedStaff(const BarLine* bl)
{
Score* score = bl->score();
int nstaves = score->nstaves();
int staffIdx = bl->staffIdx();
Segment* segment = bl->segment();
for (int i = staffIdx + 1; i < nstaves; ++i) {
Staff* s = score->staff(i);
if (s->part()->show() && bl->measure()->visible(i))
return i;
BarLine* nbl = toBarLine(segment->element(i * VOICES));
if (!nbl || !nbl->spanStaff())
break;
}
return staffIdx;
}
//---------------------------------------------------------
// getY
//---------------------------------------------------------
void BarLine::getY() const
{
qreal _spatium = spatium();
if (!parent()) {
// for use in palette
y1 = _spanFrom * _spatium * .5;
y2 = (8-_spanTo) * _spatium * .5;
return;
}
int staffIdx1 = staffIdx();
const Staff* staff1 = score()->staff(staffIdx1);
int staffIdx2 = staffIdx1;
int nstaves = score()->nstaves();
Measure* measure = segment()->measure();
if (_spanStaff)
staffIdx2 = nextVisibleSpannedStaff(this);
System* system = measure->system();
if (!system)
return;
// test start and end staff visibility
// base y on top visible staff in barline span
// after skipping ones with hideSystemBarLine set
// and accounting for staves that are shown but have invisible measures
Fraction tick = segment()->measure()->tick();
const StaffType* st1 = staff1->staffType(tick);
int from = _spanFrom;
int to = _spanTo;
int oneLine = st1->lines() == 1;
if (oneLine && _spanFrom == 0) {
from = BARLINE_SPAN_1LINESTAFF_FROM;
if (!_spanStaff || (staffIdx1 == nstaves - 1))
to = BARLINE_SPAN_1LINESTAFF_TO;
}
SysStaff* sysStaff1 = system->staff(staffIdx1);
qreal yp = sysStaff1->y();
qreal spatium1 = st1->spatium(score());
qreal d = st1->lineDistance().val() * spatium1;
qreal yy = measure->staffLines(staffIdx1)->y1() - yp;
qreal lw = score()->styleS(Sid::staffLineWidth).val() * spatium1 * .5;
y1 = yy + from * d * .5 - lw;
if (staffIdx2 != staffIdx1)
y2 = measure->staffLines(staffIdx2)->y1() - yp - to * d * .5;
else
y2 = yy + (st1->lines() * 2 - 2 + to) * d * .5 + lw;
}
//---------------------------------------------------------
// drawDots
//---------------------------------------------------------
void BarLine::drawDots(QPainter* painter, qreal x) const
{
qreal _spatium = spatium();
qreal y1l;
qreal y2l;
if (parent() == 0) { // for use in palette (always Bravura)
//Bravura shifted repeatDot symbol 0.5sp upper in the font itself (1.272)
y1l = 1.5 * _spatium;
y2l = 2.5 * _spatium;
}
else {
const StaffType* st = staffType();
//workaround to make new Bravura font work correctly with repeatDots
qreal offset = score()->scoreFont()->name() == "Bravura" ? 0 : 0.5 * score()->spatium() * mag();
y1l = st->doty1() * _spatium + offset;
y2l = st->doty2() * _spatium + offset;
}
drawSymbol(SymId::repeatDot, painter, QPointF(x, y1l));
drawSymbol(SymId::repeatDot, painter, QPointF(x, y2l));
}
//---------------------------------------------------------
// drawTips
//---------------------------------------------------------
void BarLine::drawTips(QPainter* painter, bool reversed, qreal x) const
{
if (reversed) {
if (isTop())
drawSymbol(SymId::reversedBracketTop, painter, QPointF(x - symWidth(SymId::reversedBracketTop), y1));
if (isBottom())
drawSymbol(SymId::reversedBracketBottom, painter, QPointF(x - symWidth(SymId::reversedBracketBottom), y2));
}
else {
if (isTop())
drawSymbol(SymId::bracketTop, painter, QPointF(x, y1));
if (isBottom())
drawSymbol(SymId::bracketBottom, painter, QPointF(x, y2));
}
}
//---------------------------------------------------------
// isTop
//---------------------------------------------------------
bool BarLine::isTop() const
{
int idx = staffIdx();
if (idx == 0)
return true;
else
return (prevVisibleSpannedStaff(this) == idx);
}
//---------------------------------------------------------
// isBottom
//---------------------------------------------------------
bool BarLine::isBottom() const
{
if (!_spanStaff)
return true;
int idx = staffIdx();
if (idx == score()->nstaves() - 1)
return true;
else
return (nextVisibleSpannedStaff(this) == idx);
}
//---------------------------------------------------------
// draw
//---------------------------------------------------------
void BarLine::draw(QPainter* painter) const
{
switch (barLineType()) {
case BarLineType::NORMAL: {
qreal lw = score()->styleP(Sid::barWidth) * mag();
painter->setPen(QPen(curColor(), lw, Qt::SolidLine, Qt::FlatCap));
painter->drawLine(QLineF(lw * .5, y1, lw * .5, y2));
}
break;
case BarLineType::BROKEN: {
qreal lw = score()->styleP(Sid::barWidth) * mag();
painter->setPen(QPen(curColor(), lw, Qt::DashLine, Qt::FlatCap));
painter->drawLine(QLineF(lw * .5, y1, lw * .5, y2));
}
break;
case BarLineType::DOTTED: {
qreal lw = score()->styleP(Sid::barWidth) * mag();
painter->setPen(QPen(curColor(), lw, Qt::DotLine, Qt::FlatCap));
painter->drawLine(QLineF(lw * .5, y1, lw * .5, y2));
}
break;
case BarLineType::END: {
qreal lw = score()->styleP(Sid::barWidth) * mag();
painter->setPen(QPen(curColor(), lw, Qt::SolidLine, Qt::FlatCap));
qreal x = lw * .5;
painter->drawLine(QLineF(x, y1, x, y2));
qreal lw2 = score()->styleP(Sid::endBarWidth) * mag();
painter->setPen(QPen(curColor(), lw2, Qt::SolidLine, Qt::FlatCap));
x += score()->styleP(Sid::endBarDistance) * mag();
painter->drawLine(QLineF(x, y1, x, y2));
}
break;
case BarLineType::DOUBLE: {
qreal lw2 = score()->styleP(Sid::doubleBarWidth) * mag();
painter->setPen(QPen(curColor(), lw2, Qt::SolidLine, Qt::FlatCap));
qreal x = lw2 * .5;
painter->drawLine(QLineF(x, y1, x, y2));
x += score()->styleP(Sid::doubleBarDistance) * mag();
painter->drawLine(QLineF(x, y1, x, y2));
}
break;
case BarLineType::START_REPEAT: {
qreal lw2 = score()->styleP(Sid::endBarWidth) * mag();
painter->setPen(QPen(curColor(), lw2, Qt::SolidLine, Qt::FlatCap));
qreal x = lw2 * .5;
painter->drawLine(QLineF(x, y1, x, y2));
qreal lw = score()->styleP(Sid::barWidth) * mag();
painter->setPen(QPen(curColor(), lw, Qt::SolidLine, Qt::FlatCap));
x += score()->styleP(Sid::endBarDistance) * mag();
painter->drawLine(QLineF(x, y1, x, y2));
x += score()->styleP(Sid::repeatBarlineDotSeparation) * mag();
x -= symBbox(SymId::repeatDot).width() * .5;
drawDots(painter, x);
if (score()->styleB(Sid::repeatBarTips))
drawTips(painter, false, 0.0);
}
break;
case BarLineType::END_REPEAT: {
qreal lw = score()->styleP(Sid::barWidth) * mag();
painter->setPen(QPen(curColor(), lw, Qt::SolidLine, Qt::FlatCap));
qreal x = 0.0; // symBbox(SymId::repeatDot).width() * .5;
drawDots(painter, x);
x += score()->styleP(Sid::repeatBarlineDotSeparation) * mag();
x += symBbox(SymId::repeatDot).width() * .5;
painter->drawLine(QLineF(x, y1, x, y2));
x += score()->styleP(Sid::endBarDistance) * mag();
qreal lw2 = score()->styleP(Sid::endBarWidth) * mag();
painter->setPen(QPen(curColor(), lw2, Qt::SolidLine, Qt::FlatCap));
painter->drawLine(QLineF(x, y1, x, y2));
if (score()->styleB(Sid::repeatBarTips))
drawTips(painter, true, x + lw2 * .5);
}
break;
case BarLineType::END_START_REPEAT: {
qreal lw = score()->styleP(Sid::barWidth) * mag();
painter->setPen(QPen(curColor(), lw, Qt::SolidLine, Qt::FlatCap));
qreal x = 0.0; // symBbox(SymId::repeatDot).width() * .5;
drawDots(painter, x);
x += score()->styleP(Sid::repeatBarlineDotSeparation) * mag();
x += symBbox(SymId::repeatDot).width() * .5;
painter->drawLine(QLineF(x, y1, x, y2));
x += score()->styleP(Sid::endBarDistance) * mag();
qreal lw2 = score()->styleP(Sid::endBarWidth) * mag();
painter->setPen(QPen(curColor(), lw2, Qt::SolidLine, Qt::FlatCap));
painter->drawLine(QLineF(x, y1, x, y2));
if (score()->styleB(Sid::repeatBarTips))
drawTips(painter, true, x + lw2 * .5);
painter->setPen(QPen(curColor(), lw, Qt::SolidLine, Qt::FlatCap));
x += score()->styleP(Sid::endBarDistance) * mag();
painter->drawLine(QLineF(x, y1, x, y2));
x += score()->styleP(Sid::repeatBarlineDotSeparation) * mag();
x -= symBbox(SymId::repeatDot).width() * .5;
drawDots(painter, x);
if (score()->styleB(Sid::repeatBarTips))
drawTips(painter, false, 0.0);
}
break;
}
Segment* s = segment();
if (s && s->isEndBarLineType() && !score()->printing() && score()->showUnprintable()) {
Measure* m = s->measure();
if (m->isIrregular() && score()->markIrregularMeasures() && !m->isMMRest()) {
painter->setPen(MScore::layoutBreakColor);
QFont f("FreeSerif");
f.setPointSizeF(12 * spatium() * MScore::pixelRatio / SPATIUM20);
f.setBold(true);
QString str = m->ticks() > m->timesig() ? "+" : "-";
QRectF r = QFontMetricsF(f, MScore::paintDevice()).boundingRect(str);
painter->setFont(f);
painter->drawText(-r.width(), 0.0, str);
}
}
}
//---------------------------------------------------------
// drawEditMode
//---------------------------------------------------------
void BarLine::drawEditMode(QPainter* p, EditData& ed)
{
Element::drawEditMode(p, ed);
BarLineEditData* bed = static_cast<BarLineEditData*>(ed.getData(this));
y1 += bed->yoff1;
y2 += bed->yoff2;
QPointF pos(canvasPos());
p->translate(pos);
BarLine::draw(p);
p->translate(-pos);
y1 -= bed->yoff1;
y2 -= bed->yoff2;
}
//---------------------------------------------------------
// write
//---------------------------------------------------------
void BarLine::write(XmlWriter& xml) const
{
xml.stag(this);
writeProperty(xml, Pid::BARLINE_TYPE);
writeProperty(xml, Pid::BARLINE_SPAN);
writeProperty(xml, Pid::BARLINE_SPAN_FROM);
writeProperty(xml, Pid::BARLINE_SPAN_TO);
for (const Element* e : _el)
e->write(xml);
Element::writeProperties(xml);
xml.etag();
}
//---------------------------------------------------------
// read
//---------------------------------------------------------
void BarLine::read(XmlReader& e)
{
resetProperty(Pid::BARLINE_SPAN);
resetProperty(Pid::BARLINE_SPAN_FROM);
resetProperty(Pid::BARLINE_SPAN_TO);
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "subtype")
setBarLineType(e.readElementText());
else if (tag == "span")
_spanStaff = e.readBool();
else if (tag == "spanFromOffset")
_spanFrom = e.readInt();
else if (tag == "spanToOffset")
_spanTo = e.readInt();
else if (tag == "Articulation") {
Articulation* a = new Articulation(score());
a->read(e);
add(a);
}
else if (tag == "Symbol") {
Symbol* s = new Symbol(score());
s->setTrack(track());
s->read(e);
add(s);
}
else if (tag == "Image") {
if (MScore::noImages)
e.skipCurrentElement();
else {
Image* image = new Image(score());
image->setTrack(track());
image->read(e);
add(image);
}
}
else if (!Element::readProperties(e))
e.unknown();
}
}
//---------------------------------------------------------
// acceptDrop
//---------------------------------------------------------
bool BarLine::acceptDrop(EditData& data) const
{
ElementType type = data.dropElement->type();
if (type == ElementType::BAR_LINE) {
return true;
}
else {
return ((type == ElementType::ARTICULATION || type == ElementType::FERMATA || type == ElementType::SYMBOL || type == ElementType::IMAGE)
&& segment()
&& segment()->isEndBarLineType());
}
// Prevent unreachable code warning
// return false;
}
//---------------------------------------------------------
// drop
//---------------------------------------------------------
Element* BarLine::drop(EditData& data)
{
Element* e = data.dropElement;
if (e->isBarLine()) {
BarLine* bl = toBarLine(e);
BarLineType st = bl->barLineType();
// if no change in subtype or no change in span, do nothing
if (st == barLineType() && !bl->spanFrom() && !bl->spanTo()) {
delete e;
return 0;
}
// check if the new property can apply to this single bar line
BarLineType bt = BarLineType::START_REPEAT | BarLineType::END_REPEAT | BarLineType::END_START_REPEAT;
bool oldRepeat = barLineType() & bt;
bool newRepeat = bl->barLineType() & bt;
// if ctrl was used and repeats are not involved,
// or if drop refers to span rather than subtype =>
// single bar line drop
if ((data.control() && !oldRepeat && !newRepeat) || (bl->spanFrom() || bl->spanTo()) ) {
// if drop refers to span, update this bar line span
if (bl->spanFrom() || bl->spanTo()) {
// if dropped spanFrom or spanTo are below the middle of standard staff (5 lines)
// adjust to the number of staff lines
// TODO:barlines
int spanFrom = bl->spanFrom();
int spanTo = bl->spanTo();
undoChangeProperty(Pid::BARLINE_SPAN, false);
undoChangeProperty(Pid::BARLINE_SPAN_FROM, spanFrom);
undoChangeProperty(Pid::BARLINE_SPAN_TO, spanTo);
}
// if drop refers to subtype, update this bar line subtype
else {
undoChangeBarLineType(this, st, false);
}
}
else {
undoChangeBarLineType(this, st, true);
}
delete e;
}
else if (e->isArticulation()) {
Articulation* atr = toArticulation(e);
atr->setParent(this);
atr->setTrack(track());
score()->undoAddElement(atr);
return atr;
}
else if (e->isSymbol() || e->isImage()) {
e->setParent(this);
e->setTrack(track());
score()->undoAddElement(e);
return e;
}
else if (e->isFermata()) {
e->setPlacement(track() & 1 ? Placement::BELOW : Placement::ABOVE);
for (Element* el: segment()->annotations())
if (el->isFermata() && (el->track() == track())) {
if (el->subtype() == e->subtype()) {
delete e;
return el;
}
else {
e->setPlacement(el->placement());
e->setTrack(track());
e->setParent(segment());
score()->undoChangeElement(el, e);
return e;
}
}
e->setTrack(track());
e->setParent(segment());
score()->undoAddElement(e);
return e;
}
return 0;
}
//---------------------------------------------------------
// updateGrips
//---------------------------------------------------------
void BarLine::updateGrips(EditData& ed) const
{
const BarLineEditData* bed = static_cast<const BarLineEditData*>(ed.getData(this));
qreal lw = score()->styleP(Sid::barWidth) * staff()->mag(tick());
getY();
ed.grip[0].translate(QPointF(lw * .5, y1 + bed->yoff1) + pagePos());
ed.grip[1].translate(QPointF(lw * .5, y2 + bed->yoff2) + pagePos());
}
//---------------------------------------------------------
// startEdit
//---------------------------------------------------------
void BarLine::startEdit(EditData& ed)
{
ed.grips = 2;
ed.curGrip = Grip::END;
BarLineEditData* bed = new BarLineEditData();
bed->e = this;
bed->yoff1 = 0;
bed->yoff2 = 0;
ed.addData(bed);
}
//---------------------------------------------------------
// endEdit
//---------------------------------------------------------
void BarLine::endEdit(EditData&)
{
#if 0 // TODO
if (ctrlDrag) { // if single bar line edit
char newSpanStaff = _spanStaff; // copy edited span values
char newSpanFrom = _spanFrom;
char newSpanTo = _spanTo;
_spanStaff = _origSpanStaff; // restore original span values
_spanFrom = _origSpanFrom;
_spanTo = _origSpanTo;
// for mid-measure barline in root score, update parts
if (midMeasure && score()->isMaster() && score()->excerpts().size() > 0) {
int currIdx = staffIdx();
Measure* m = segment()->measure();
// change linked barlines as necessary
int lastIdx = currIdx + qMax(_span, newSpanStaff);
for (int idx = currIdx; idx < lastIdx; ++idx) {
Staff* staff = score()->staff(idx);
LinkedStaves* ls = staff->linkedStaves();
if (ls) {
for (Staff* lstaff : ls->staves()) {
Score* lscore = lstaff->score();
// don't change barlines in root score
if (lscore == staff->score())
continue;
// change barline only in top staff of part
if (lstaff != lscore->staff(0))
continue;
int spannedStaves = qMax(currIdx + newSpanStaff - idx, 0);
int lNewSpan = qMin(spannedStaves, lscore->nstaves());
Measure* lm = lscore->tick2measure(m->tick());
Segment* lseg = lm->undoGetSegmentR(SegmentType::BarLine, rtick());
BarLine* lbl = toBarLine(lseg->element(0));
if (lbl) {
// already a barline here
if (lNewSpan > 0) {
// keep barline, but update span if necessary
if (lbl->span() != lNewSpan)
lbl->undoChangeProperty(Pid::BARLINE_SPAN, lNewSpan);
}
else {
// remove barline
lbl->unlink();
lbl->score()->undoRemoveElement(lbl);
}
}
else {
// new barline needed
lbl = toBarLine(linkedClone());
lbl->setSpan(lNewSpan);
lbl->setTrack(lstaff->idx() * VOICES);
lbl->setScore(lscore);
lbl->setParent(lseg);
lscore->undoAddElement(lbl);
}
}
}
}
}
undoChangeProperty(Pid::BARLINE_SPAN, newSpanStaff);
undoChangeProperty(Pid::BARLINE_SPAN_FROM, newSpanFrom);
undoChangeProperty(Pid::BARLINE_SPAN_FROM, newSpanTo);
return;
}
// if same as staff settings, do nothing
if (staff()->barLineSpan() == _spanStaff && staff()->barLineFrom() == _spanFrom && staff()->barLineTo() == _spanTo)
return;
// int idx1 = staffIdx();
if (_span != staff()->barLineSpan()) {
// if now bar lines span more staves
if (_span > staff()->barLineSpan()) {
int idx2 = idx1 + _span;
// set span 0 to all additional staves
for (int idx = idx1 + 1; idx < idx2; ++idx) {
// Mensurstrich special case:
// if line spans to top line of a stave AND current staff is
// the last spanned staff BUT NOT the last score staff
// keep its bar lines
// otherwise remove them
if (_spanTo > 0 || !(idx == idx2-1 && idx != score()->nstaves()-1)) {
Staff* staff = score()->staff(idx);
staff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, 0);
staff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN_FROM, 0);
staff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN_TO, (staff->lines(tick())-1)*2);
}
}
}
// if now bar lines span fewer staves
else {
int idx1 = staffIdx() + _span;
int idx2 = staffIdx() + staff()->barLineSpan();
// set standard span for each no-longer-spanned staff
for (int idx = idx1; idx < idx2; ++idx) {
Staff* staff = score()->staff(idx);
int lines = staff->lines(tick());
int spanFrom = 0;
int spanTo = 0;
staff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, 1);
staff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN_FROM, spanFrom);
staff->undoChangeProperty(Pid::STAFF_BARLINE_SPAN_TO, spanTo);
}
}
}
// update span for the staff the edited bar line belongs to
staff()->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, _spanStaff);
staff()->undoChangeProperty(Pid::STAFF_BARLINE_SPAN_FROM, _spanFrom);
staff()->undoChangeProperty(Pid::STAFF_BARLINE_SPAN_TO, _spanTo);
#endif
}
//---------------------------------------------------------
// editDrag
//---------------------------------------------------------
void BarLine::editDrag(EditData& ed)
{
BarLineEditData* bed = static_cast<BarLineEditData*>(ed.getData(this));
qreal lineDist = staff()->lineDistance(tick()) * spatium();
getY();
if (ed.curGrip == Grip::START) {
// Start grip moving is useless currently, so disable it
return;
#if 0
// min offset for top grip is line -1 (-2 for 1-line staves)
// max offset is 1 line above bottom grip or 1 below last staff line, whichever comes first
int lines = staff()->lines(tick());
qreal min = (-y1 - lines == 1) ? lineDist * 2 : lineDist;
qreal max = y2 - y1 - lineDist; // 1 line above bottom grip
qreal lastmax = (lines - _spanFrom/2) * lineDist; // 1 line below last staff line
if (lastmax < max)
max = lastmax;
// update yoff1 and bring it within limits
bed->yoff1 += ed.delta.y();
if (bed->yoff1 < min)
bed->yoff1 = min;
if (bed->yoff1 > max)
bed->yoff1 = max;
#endif
}
else {
// min for bottom grip is 1 line below top grip
const qreal min = y1 - y2 + lineDist;
// max is the bottom of the system
const System* system = segment() ? segment()->system() : nullptr;
const int st = staffIdx();
const qreal max = (system && st != -1) ? (system->height() - y2 - system->staff(st)->y()) : std::numeric_limits<qreal>::max();
// update yoff2 and bring it within limit
bed->yoff2 += ed.delta.y();
if (bed->yoff2 < min)
bed->yoff2 = min;
if (bed->yoff2 > max)
bed->yoff2 = max;
}
}
//---------------------------------------------------------
// endEditDrag
// snap to nearest staff / staff line
//---------------------------------------------------------
void BarLine::endEditDrag(EditData& ed)
{
getY();
BarLineEditData* bed = static_cast<BarLineEditData*>(ed.getData(this));
y1 += bed->yoff1;
y2 += bed->yoff2;
qreal ay0 = pagePos().y();
qreal ay2 = ay0 + y2; // absolute (page-relative) bar line bottom coord
int staffIdx1 = staffIdx();
System* syst = segment()->measure()->system();
qreal systTopY = syst->pagePos().y();
// determine new span value
int staffIdx2;
int numOfStaves = syst->staves()->size();
if (staffIdx1 + 1 >= numOfStaves)
// if initial staff is last staff, ending staff must be the same
staffIdx2 = staffIdx1;
else {
// if there are other staves after it, look for staff nearest to bar line bottom coord
qreal staff1TopY = syst->staff(staffIdx1)->y() + systTopY;
for (staffIdx2 = staffIdx1 + 1; staffIdx2 < numOfStaves; ++staffIdx2) {
// compute 1st staff height, absolute top Y of 2nd staff and height of blank between the staves
Staff * staff1 = score()->staff(staffIdx2-1);
qreal staff1Hght = (staff1->lines(tick())-1) * staff1->lineDistance(tick()) * spatium();
qreal staff2TopY = systTopY + syst->staff(staffIdx2)->y();
qreal blnkBtwnStaff = staff2TopY - staff1TopY - staff1Hght;
// if bar line bottom coord is above than mid-way of blank between staves...
if (ay2 < (staff1TopY + staff1Hght + blnkBtwnStaff * .5))
break; // ...staff 1 is ending staff
// if bar line is below, advance to next staff
staff1TopY = staff2TopY;
}
staffIdx2 -= 1;
}
// determine new spanFrom and spanTo values
int newSpanFrom, newSpanTo;
// Staff * staff2 = score()->staff(staffIdx2);
// int staff1lines = staff()->lines(tick());
// int staff2lines = staff2->lines(tick());
#if 0 // TODO
if (shiftDrag) { // if precision dragging
newSpanFrom = _spanFrom;
if (yoff1 != 0.0) {
// round bar line top coord to nearest line of 1st staff (in half line dist units)
newSpanFrom = ((int)floor(y1 / (staff()->lineDistance(tick()) * spatium()) + 0.5 )) * 2;
// min = 1 line dist above 1st staff line | max = 1 line dist below last staff line
// except for 1-line staves
int minFrom = staff1lines == 1 ? BARLINE_SPAN_1LINESTAFF_FROM : MIN_BARLINE_SPAN_FROMTO;
if (newSpanFrom < minFrom)
newSpanFrom = minFrom;
if (newSpanFrom > staff1lines * 2)
newSpanFrom = staff1lines * 2;
}
newSpanTo = _spanTo;
if (yoff2 != 0.0) {
// round bar line bottom coord to nearest line of 2nd staff (in half line dist units)
qreal staff2TopY = systTopY + syst->staff(staffIdx2)->y();
newSpanTo = ((int)floor( (ay2 - staff2TopY) / (staff2->lineDistance(tick()) * spatium()) + 0.5 )) * 2;
// min = 1 line dist above 1st staff line | max = 1 line dist below last staff line
int maxTo = staff2lines == 1 ? BARLINE_SPAN_1LINESTAFF_TO : staff2lines * 2;
if (newSpanTo < MIN_BARLINE_SPAN_FROMTO)
newSpanTo = MIN_BARLINE_SPAN_FROMTO;
if (newSpanTo > maxTo)
newSpanTo = maxTo;
}
}
#endif
// else { // if coarse dragging
{ // if coarse dragging
newSpanFrom = 0;
newSpanTo = 0;
}
bool localDrag = ed.control() || segment()->isBarLine();
if (localDrag) {
Segment* s = segment();
for (int staffIdx = staffIdx1; staffIdx < staffIdx2; ++staffIdx) {
BarLine* b = toBarLine(s->element(staffIdx * VOICES));
if (!b) {
b = toBarLine(linkedClone());
b->setSpanStaff(true);
b->setTrack(staffIdx * VOICES);
b->setScore(score());
b->setParent(s);
score()->undoAddElement(b);
}
b->undoChangeProperty(Pid::BARLINE_SPAN, true);
}
BarLine* b = toBarLine(s->element(staffIdx2 * VOICES));
if (b)
b->undoChangeProperty(Pid::BARLINE_SPAN, false);
}
else {
for (int staffIdx = staffIdx1; staffIdx < staffIdx2; ++staffIdx)
score()->staff(staffIdx)->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, true);
score()->staff(staffIdx2)->undoChangeProperty(Pid::STAFF_BARLINE_SPAN, false);
staff()->undoChangeProperty(Pid::STAFF_BARLINE_SPAN_FROM, newSpanFrom);
staff()->undoChangeProperty(Pid::STAFF_BARLINE_SPAN_TO, newSpanTo);
}
}
//---------------------------------------------------------
// layoutWidth
//---------------------------------------------------------
qreal BarLine::layoutWidth(Score* score, BarLineType type)
{
qreal dotwidth = score->scoreFont()->width(SymId::repeatDot, 1.0);
qreal w {0.0};
switch (type) {
case BarLineType::DOUBLE:
w = score->styleP(Sid::doubleBarWidth) + score->styleP(Sid::doubleBarDistance);
break;
case BarLineType::END_START_REPEAT:
w = score->styleP(Sid::endBarDistance) * 2
+ score->styleP(Sid::repeatBarlineDotSeparation) * 2
+ dotwidth;
break;
case BarLineType::START_REPEAT:
case BarLineType::END_REPEAT:
w = score->styleP(Sid::endBarWidth) * .5
+ score->styleP(Sid::endBarDistance)
+ score->styleP(Sid::repeatBarlineDotSeparation)
+ dotwidth * .5;
break;
case BarLineType::END:
w = (score->styleP(Sid::endBarWidth) + score->styleP(Sid::barWidth)) * .5
+ score->styleP(Sid::endBarDistance);
break;
case BarLineType::BROKEN:
case BarLineType::NORMAL:
case BarLineType::DOTTED:
w = score->styleP(Sid::barWidth);
break;
}
return w;
}
//---------------------------------------------------------
// layoutRect
//---------------------------------------------------------
QRectF BarLine::layoutRect() const
{
QRectF bb = bbox();
if (staff()) {
// actual height may include span to next staff
// but this should not be included in shapes or skylines
qreal sp = spatium();
int span = staff()->lines(tick()) - 1;
int sFrom;
int sTo;
if (span == 0 && _spanTo == 0) {
sFrom = BARLINE_SPAN_1LINESTAFF_FROM;
sTo = _spanStaff ? 0 : BARLINE_SPAN_1LINESTAFF_TO;
}
else {
sFrom = _spanFrom;
sTo = _spanStaff ? 0 : _spanTo;
}
qreal y = sp * sFrom * 0.5;
qreal h = sp * (span + (sTo - sFrom) * 0.5);
if (score()->styleB(Sid::repeatBarTips)) {
switch (barLineType()) {
case BarLineType::START_REPEAT:
case BarLineType::END_REPEAT:
case BarLineType::END_START_REPEAT: {
if (isTop()) {
qreal top = symBbox(SymId::bracketTop).height();
y -= top;
h += top;
}
if (isBottom()) {
qreal bottom = symBbox(SymId::bracketBottom).height();
h += bottom;
}
}
default:
break;
}
}
bb.setY(y);
bb.setHeight(h);
}
return bb;
}
//---------------------------------------------------------
// layout
//---------------------------------------------------------
void BarLine::layout()
{
setPos(QPointF());
// barlines hidden on this staff
if (staff() && segment()) {
if ((!staff()->staffType(tick())->showBarlines() && segment()->segmentType() == SegmentType::EndBarLine)
|| (staff()->hideSystemBarLine() && segment()->segmentType() == SegmentType::BeginBarLine)) {
setbbox(QRectF());
return;
}
}
setMag(score()->styleB(Sid::scaleBarlines) && staff() ? staff()->mag(tick()) : 1.0);
qreal _spatium = spatium();
y1 = _spatium * .5 * _spanFrom;
y2 = _spatium * .5 * (8.0 + _spanTo);
qreal w = layoutWidth(score(), barLineType()) * mag();
QRectF r(0.0, y1, w, y2 - y1);
if (score()->styleB(Sid::repeatBarTips)) {
switch (barLineType()) {
case BarLineType::START_REPEAT:
r |= symBbox(SymId::bracketTop).translated(0, y1);
// r |= symBbox(SymId::bracketBottom).translated(0, y2);
break;
case BarLineType::END_REPEAT: {
qreal w1 = 0.0; //symBbox(SymId::reversedBracketTop).width();
r |= symBbox(SymId::reversedBracketTop).translated(-w1, y1);
// r |= symBbox(SymId::reversedBracketBottom).translated(0, y2);
}
break;
case BarLineType::END_START_REPEAT: {
qreal w1 = 0.0; //symBbox(SymId::reversedBracketTop).width();
r |= symBbox(SymId::reversedBracketTop).translated(-w1, y1);
r |= symBbox(SymId::bracketTop).translated(0, y1);
// r |= symBbox(SymId::reversedBracketBottom).translated(0, y2);
}
break;
default:
break;
}
}
setbbox(r);
for (Element* e : _el) {
e->layout();
if (e->isArticulation()) {
Articulation* a = toArticulation(e);
Direction dir = a->direction();
qreal distance = 0.5 * spatium();
qreal x = width() * .5;
if (dir == Direction::DOWN) {
qreal botY = y2 + distance;
a->setPos(QPointF(x, botY));
}
else {
qreal topY = y1 - distance;
a->setPos(QPointF(x, topY));
}
}
}
}
//---------------------------------------------------------
// layout2
// called after system layout; set vertical dimensions
//---------------------------------------------------------
void BarLine::layout2()
{
// barlines hidden on this staff
if (staff() && segment()) {
if ((!staff()->staffType(tick())->showBarlines() && segment()->segmentType() == SegmentType::EndBarLine)
|| (staff()->hideSystemBarLine() && segment()->segmentType() == SegmentType::BeginBarLine)) {
setbbox(QRectF());
return;
}
}
getY();
bbox().setTop(y1);
bbox().setBottom(y2);
if (score()->styleB(Sid::repeatBarTips)) {
switch (barLineType()) {
case BarLineType::START_REPEAT:
bbox() |= symBbox(SymId::bracketTop).translated(0, y1);
bbox() |= symBbox(SymId::bracketBottom).translated(0, y2);
break;
case BarLineType::END_REPEAT:
{
qreal w1 = 0.0; //symBbox(SymId::reversedBracketTop).width();
bbox() |= symBbox(SymId::reversedBracketTop).translated(-w1, y1);
bbox() |= symBbox(SymId::reversedBracketBottom).translated(-w1, y2);
break;
}
case BarLineType::END_START_REPEAT:
{
qreal w1 = 0.0; //symBbox(SymId::reversedBracketTop).width();
bbox() |= symBbox(SymId::reversedBracketTop).translated(-w1, y1);
bbox() |= symBbox(SymId::reversedBracketBottom).translated(-w1, y2);
bbox() |= symBbox(SymId::bracketTop).translated(0, y1);
bbox() |= symBbox(SymId::bracketBottom).translated(0, y2);
break;
}
default:
break;
}
}
}
//---------------------------------------------------------
// shape
//---------------------------------------------------------
Shape BarLine::shape() const
{
Shape shape;
#ifndef NDEBUG
shape.add(bbox(), name());
#else
shape.add(bbox());
#endif
return shape;
}
//---------------------------------------------------------
// scanElements
//---------------------------------------------------------
void BarLine::scanElements(void* data, void (*func)(void*, Element*), bool all)
{
// if no width (staff has bar lines turned off) and not all requested, do nothing
if (width() == 0.0 && !all)
return;
func(data, this);
for (Element* e : _el)
e->scanElements(data, func, all);
}
//---------------------------------------------------------
// setTrack
//---------------------------------------------------------
void BarLine::setTrack(int t)
{
Element::setTrack(t);
for (Element* e : _el)
e->setTrack(t);
}
//---------------------------------------------------------
// setScore
//---------------------------------------------------------
void BarLine::setScore(Score* s)
{
Element::setScore(s);
for (Element* e : _el)
e->setScore(s);
}
//---------------------------------------------------------
// add
//---------------------------------------------------------
void BarLine::add(Element* e)
{
e->setParent(this);
switch (e->type()) {
case ElementType::ARTICULATION:
case ElementType::SYMBOL:
case ElementType::IMAGE:
_el.push_back(e);
setGenerated(false);
break;
default:
qDebug("BarLine::add() not impl. %s", e->name());
delete e;
break;
}
}
//---------------------------------------------------------
// remove
//---------------------------------------------------------
void BarLine::remove(Element* e)
{
switch(e->type()) {
case ElementType::ARTICULATION:
case ElementType::SYMBOL:
case ElementType::IMAGE:
if (!_el.remove(e))
qDebug("BarLine::remove(): cannot find %s", e->name());
break;
default:
qDebug("BarLine::remove() not impl. %s", e->name());
break;
}
}
//---------------------------------------------------------
// getProperty
//---------------------------------------------------------
QVariant BarLine::getProperty(Pid id) const
{
switch (id) {
case Pid::BARLINE_TYPE:
return QVariant::fromValue(_barLineType);
case Pid::BARLINE_SPAN:
return spanStaff();
case Pid::BARLINE_SPAN_FROM:
return int(spanFrom());
case Pid::BARLINE_SPAN_TO:
return int(spanTo());
default:
break;
}
return Element::getProperty(id);
}
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
bool BarLine::setProperty(Pid id, const QVariant& v)
{
switch (id) {
case Pid::BARLINE_TYPE:
setBarLineType(v.value<BarLineType>());
break;
case Pid::BARLINE_SPAN:
setSpanStaff(v.toBool());
break;
case Pid::BARLINE_SPAN_FROM:
setSpanFrom(v.toInt());
break;
case Pid::BARLINE_SPAN_TO:
setSpanTo(v.toInt());
break;
default:
return Element::setProperty(id, v);
}
setGenerated(false);
triggerLayout();
return true;
}
//---------------------------------------------------------
// undoChangeProperty
//---------------------------------------------------------
void BarLine::undoChangeProperty(Pid id, const QVariant& v, PropertyFlags ps)
{
if (id == Pid::BARLINE_TYPE && segment()) {
const BarLine* bl = this;
BarLineType blType = v.value<BarLineType>();
if (blType == BarLineType::START_REPEAT) { // change next measures endBarLine
if (bl->measure()->nextMeasure())
bl = bl->measure()->nextMeasure()->endBarLine();
else
bl = 0;
}
if (bl)
undoChangeBarLineType(const_cast<BarLine*>(bl), v.value<BarLineType>(), true);
}
else
ScoreElement::undoChangeProperty(id, v, ps);
}
//---------------------------------------------------------
// propertyDefault
//---------------------------------------------------------
QVariant BarLine::propertyDefault(Pid propertyId) const
{
switch (propertyId) {
case Pid::BARLINE_TYPE:
// dynamic default values are a bad idea: writing to xml the value maybe ommited resulting in
// wrong values on read (as the default may be different on read)
// if (segment() && segment()->measure() && !segment()->measure()->nextMeasure())
// return QVariant::fromValue(BarLineType::END);
return QVariant::fromValue(BarLineType::NORMAL);
case Pid::BARLINE_SPAN:
return staff() ? staff()->barLineSpan() : false;
case Pid::BARLINE_SPAN_FROM:
return staff() ? staff()->barLineFrom() : 0;
case Pid::BARLINE_SPAN_TO:
return staff() ? staff()->barLineTo() : 0;
default:
break;
}
return Element::propertyDefault(propertyId);
}
//---------------------------------------------------------
// propertyId
//---------------------------------------------------------
Pid BarLine::propertyId(const QStringRef& name) const
{
if (name == "subtype")
return Pid::BARLINE_TYPE;
return Element::propertyId(name);
}
//---------------------------------------------------------
// nextSegmentElement
//---------------------------------------------------------
Element* BarLine::nextSegmentElement()
{
return segment()->firstInNextSegments(score()->inputState().prevTrack() / VOICES);
}
//---------------------------------------------------------
// prevSegmentElement
//---------------------------------------------------------
Element* BarLine::prevSegmentElement()
{
return segment()->lastInPrevSegments(score()->inputState().prevTrack() / VOICES);
}
//---------------------------------------------------------
// accessibleInfo
//---------------------------------------------------------
QString BarLine::accessibleInfo() const
{
return QString("%1: %2").arg(Element::accessibleInfo()).arg(BarLine::userTypeName(barLineType()));
}
//---------------------------------------------------------
// accessibleExtraInfo
//---------------------------------------------------------
QString BarLine::accessibleExtraInfo() const
{
Segment* seg = segment();
QString rez;
for (const Element* e : *el()) {
if (!score()->selectionFilter().canSelect(e))
continue;
rez = QString("%1 %2").arg(rez).arg(e->screenReaderInfo());
}
for (const Element* e : seg->annotations()) {
if (!score()->selectionFilter().canSelect(e))
continue;
if (e->track() == track())
rez = QString("%1 %2").arg(rez).arg(e->screenReaderInfo());
}
Measure* m = seg->measure();
if (m) { // always true?
//jumps
for (const Element* e : m->el()) {
if (!score()->selectionFilter().canSelect(e)) continue;
if (e->type() == ElementType::JUMP)
rez= QString("%1 %2").arg(rez).arg(e->screenReaderInfo());
if (e->type() == ElementType::MARKER) {
const Marker* m1 = toMarker(e);
if (m1->markerType() == Marker::Type::FINE)
rez = QString("%1 %2").arg(rez).arg(e->screenReaderInfo());
}
}
//markers
Measure* nextM = m->nextMeasureMM();
if (nextM) {
for (const Element* e : nextM->el()) {
if (!score()->selectionFilter().canSelect(e))
continue;
if (e->isMarker()) {
if (toMarker(e)->markerType() == Marker::Type::FINE)
continue; //added above^
rez = QString("%1 %2").arg(rez).arg(e->screenReaderInfo());
}
}
}
}
Fraction tick = seg->tick();
auto spanners = score()->spannerMap().findOverlapping(tick.ticks(), tick.ticks());
for (auto interval : spanners) {
Spanner* s = interval.value;
if (!score()->selectionFilter().canSelect(s))
continue;
if (s->type() == ElementType::VOLTA) {
if (s->tick() == tick)
rez = QObject::tr("%1 Start of %2").arg(rez).arg(s->screenReaderInfo());
if (s->tick2() == tick)
rez = QObject::tr("%1 End of %2").arg(rez).arg(s->screenReaderInfo());
}
}
return rez;
}
}