Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #15726 (Barlines & Line Breaks): Some elements are lost when changing time signature #26256

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
238 changes: 238 additions & 0 deletions src/engraving/dom/range.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,7 @@ bool TrackList::write(Score* score, const Fraction& tick) const
ScoreRange::~ScoreRange()
{
muse::DeleteAll(m_tracks);
deleteBarLines();
}

//---------------------------------------------------------
Expand Down Expand Up @@ -711,6 +712,8 @@ void ScoreRange::read(Segment* first, Segment* last, bool readSpanner)
m_tracks.push_back(dl);
}
}
backupBarLines(first, last);
backupBreaks(first, last);
}

//---------------------------------------------------------
Expand Down Expand Up @@ -769,6 +772,8 @@ bool ScoreRange::write(Score* score, const Fraction& tick) const
score->undoAddElement(a.e);
}
}
restoreBarLines(score, tick);
restoreBreaks(score, tick);
return true;
}

Expand Down Expand Up @@ -827,6 +832,239 @@ Fraction ScoreRange::ticks() const
return m_tracks.empty() ? Fraction() : m_tracks.front()->ticks();
}

//---------------------------------------------------------
// backupBarLines
//---------------------------------------------------------
void ScoreRange::backupBarLines(Segment* first, Segment* last)
{
Measure* fm = first->measure();
Measure* lm = last->measure();

for (Measure* m = fm; m && m != lm->nextMeasure(); m = m->nextMeasure()) {
// Backup BarLines (Segments's sons)
for (Segment* s = m->first(); s; s = s->next1()) {
for (EngravingItem* e : s->elist()) {
if (e && e->isBarLine()) {
BarLinesBackup blBackup;
blBackup.sPosition = s->tick();
blBackup.formerMeasureStartOrEnd
= ((blBackup.sPosition == m->tick()) || (blBackup.sPosition == m->endTick()) ? true : false);
blBackup.bl = toBarLine(e)->clone();
m_barLines.push_back(blBackup);
}
}
// Last segment
if (s == m->last()) {
break;
}
}
// Last Measure
if (m->sectionBreak() || (m->nextMeasure() && (m->nextMeasure()->first(SegmentType::TimeSig)))) {
break;
}
}
}

//---------------------------------------------------------
// insertBarLineIn
//---------------------------------------------------------
bool ScoreRange::insertBarLine(Measure* m, const BarLinesBackup& barLine) const
{
//---------------------------------------------------------
// addBarLine
//---------------------------------------------------------
auto addBarLine = [&](Measure* m, BarLine* bl, SegmentType st, Fraction pos)
{
bool middle = (pos != m->tick()) && (pos != m->endTick());
Segment* seg = m->undoGetSegment(st, pos);
if (seg) {
BarLineType blt = bl->barLineType();
// get existing bar line if it does exist
BarLine* nbl = toBarLine(seg->element(bl->track()));
if (!nbl) {
// no suitable bar line: create a new one
nbl = Factory::createBarLine(seg);
nbl->setParent(seg);
nbl->setTrack(bl->track());
// A BL in the middle of a Measure does have SpanStaff to false
nbl->setSpanStaff(middle ? false : bl->spanStaff());
m->score()->addElement(nbl);
} else {
// We change BarLineType if necessary to keep END_START repeats if in the middle of a meassure
if ((nbl->barLineType() == BarLineType::END_REPEAT) && (bl->barLineType() == BarLineType::START_REPEAT) && middle) {
blt = BarLineType::END_START_REPEAT;
}
}
nbl->setGenerated(false);
nbl->setBarLineType(blt);
nbl->setVisible(bl->visible());
nbl->setColor(bl->color());

// We check if the same BL type is in every Stave
bool blAcrossStaves = false;
// if there is only one stave or spanStaff
if ((m->score()->nstaves() == 1) || bl->spanStaff()) {
blAcrossStaves = true;
}
// If we are in the last stave
else if (bl->staffIdx() == (m->score()->nstaves() - 1)) {
bool sameBL = true;
// We check if and every previous stave has the same type of BL
for (int i = 0; i < (m->score()->nstaves() - 1); ++i) {
BarLine* sbl = toBarLine(seg->element(staff2track(i)));
if (!sbl || (sbl->barLineType() != blt)) {
sameBL = false;
break;
}
}
blAcrossStaves = sameBL;

if (blAcrossStaves && !middle) {
// Set Spanstaff to true if not in the middle and there is the same BL across staves
for (int i = 0; i < (m->score()->nstaves() - 1); ++i) {
BarLine* sbl = toBarLine(seg->element(staff2track(i)));
if (sbl) {
sbl->setSpanStaff(middle ? false : true);
}
}
}
}

// Adding Set repeats
if ((pos == m->tick()) && (bl->barLineType() == BarLineType::START_REPEAT) && blAcrossStaves) {
m->setRepeatStart(true);
} else if ((pos == m->endTick()) && (bl->barLineType() == BarLineType::END_REPEAT) && blAcrossStaves) {
m->setRepeatEnd(true);
}
}
};

bool processed = false;

// if END_START_REPEAT AND at the end of a Measure
if ((barLine.sPosition == m->endTick()) && (barLine.bl->barLineType() == BarLineType::END_START_REPEAT)) {
// Create END_REPEAT and START_REPEAT (and ignore return value)
barLine.bl->setBarLineType(BarLineType::END_REPEAT);
insertBarLine(m, barLine);
// Start Repeat into the next measure
if (m->nextMeasure()) {
barLine.bl->setBarLineType(BarLineType::START_REPEAT);
insertBarLine(m->nextMeasure(), barLine);
}

// Restore initial value just in case
barLine.bl->setBarLineType(BarLineType::END_START_REPEAT);
processed = true;
} else {
// First position
if (barLine.sPosition == m->tick()) {
// Just Start Repeat at the left of the Measure
if (barLine.bl->barLineType() == BarLineType::START_REPEAT) {
addBarLine(m, barLine.bl, SegmentType::StartRepeatBarLine, barLine.sPosition);
processed = true;
}
}
// Last position
else if (barLine.sPosition == m->endTick()) {
// Avoid Start Repeat at the end of the Measure
if (barLine.bl->barLineType() != BarLineType::START_REPEAT) {
addBarLine(m, barLine.bl, SegmentType::EndBarLine, barLine.sPosition);
processed = true;
}
}
// Middle
else {
addBarLine(m, barLine.bl, SegmentType::BarLine, barLine.sPosition);
processed = true;
}
}
return processed;
}

//---------------------------------------------------------
// restoreBarLines
//---------------------------------------------------------

void ScoreRange::restoreBarLines(Score* score, const Fraction& tick) const
{
for (const BarLinesBackup& bbl : m_barLines) {
// We only insert the necessary BarLines
if ((bbl.bl->barLineType() != BarLineType::NORMAL) && (bbl.bl->barLineType() != BarLineType::END)) {
for (Measure* m = score->tick2measure(tick); m; m = m->nextMeasure()) {
// if inserted within a suitable measure ... to the next barline
if (((bbl.sPosition >= m->tick()) && (bbl.sPosition <= m->endTick())) && (insertBarLine(m, bbl))) {
break;
}
// Last Measure
if (m->sectionBreak() || (m->nextMeasure() && (m->nextMeasure()->first(SegmentType::TimeSig)))) {
break;
}
}
}
}
}

//---------------------------------------------------------
// backupBreaks
//---------------------------------------------------------
void ScoreRange::backupBreaks(Segment* first, Segment* last)
{
Measure* fm = first->measure();
Measure* lm = last->measure();
for (Measure* m = fm; m && m != lm->nextMeasure(); m = m->nextMeasure()) {
if (m->lineBreak()) {
BreaksBackup bBackup;
bBackup.sPosition = m->endTick();
bBackup.lBreakType = LayoutBreakType::LINE;
m_breaks.push_back(bBackup);
} else if (m->pageBreak()) {
BreaksBackup bBackup;
bBackup.sPosition = m->endTick();
bBackup.lBreakType = LayoutBreakType::PAGE;
m_breaks.push_back(bBackup);
}

// Last Measure
if (m->sectionBreak() || (m->nextMeasure() && (m->nextMeasure()->first(SegmentType::TimeSig)))) {
break;
}
}
}

//---------------------------------------------------------
// restoreBreaks
//---------------------------------------------------------

void ScoreRange::restoreBreaks(Score* score, const Fraction& tick) const
{
// Break list
for (const BreaksBackup& bb : m_breaks) {
// Look for suitable measure
for (Measure* m = score->tick2measure(tick); m; m = m->nextMeasure()) {
// We keep them as long as they are in the measure after the start tick
if ((bb.sPosition > m->tick()) && (bb.sPosition <= m->endTick())) {
m->undoSetBreak(true, bb.lBreakType);
break;
}
// Last Measure
if (m->sectionBreak() || (m->nextMeasure() && (m->nextMeasure()->first(SegmentType::TimeSig)))) {
break;
}
}
}
}

//---------------------------------------------------------
// deleteBarLines
//---------------------------------------------------------

void ScoreRange::deleteBarLines()
{
for (const BarLinesBackup& bbl : m_barLines) {
delete bbl.bl;
}
}

//---------------------------------------------------------
// dump
//---------------------------------------------------------
Expand Down
22 changes: 21 additions & 1 deletion src/engraving/dom/range.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class Spanner;
class ScoreRange;
class ChordRest;
class Score;
class BarLine;

//---------------------------------------------------------
// TrackList
Expand Down Expand Up @@ -109,12 +110,31 @@ class ScoreRange
std::list<Annotation> m_annotations;

private:

struct BarLinesBackup
{
Fraction sPosition;
bool formerMeasureStartOrEnd;
BarLine* bl = nullptr;
};
void backupBarLines(Segment* first, Segment* last);
bool insertBarLine(Measure* m, const BarLinesBackup& barLine) const;
void restoreBarLines(Score* score, const Fraction& tick) const;
void deleteBarLines();

void backupBreaks(Segment* first, Segment* last);
void restoreBreaks(Score* score, const Fraction& tick) const;
friend class TrackList;

struct BreaksBackup
{
Fraction sPosition;
LayoutBreakType lBreakType;
};
std::list<TrackList*> m_tracks;
Segment* m_first = nullptr;
Segment* m_last = nullptr;
std::list<BarLinesBackup> m_barLines;
std::list<BreaksBackup> m_breaks;
};
} // namespace mu::engraving
#endif
Loading