Skip to content

Commit

Permalink
Fix #15726 (Barlines & Jumps & Marks): Some elements are lost when ch…
Browse files Browse the repository at this point in the history
…anging time signature
  • Loading branch information
pacebes committed Jan 27, 2025
1 parent 97e23fb commit f26e342
Show file tree
Hide file tree
Showing 2 changed files with 206 additions and 0 deletions.
191 changes: 191 additions & 0 deletions src/engraving/dom/range.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#include "range.h"

#include "barline.h"
#include "jump.h"
#include "marker.h"
#include "chord.h"
#include "excerpt.h"
#include "factory.h"
Expand Down Expand Up @@ -711,6 +713,7 @@ void ScoreRange::read(Segment* first, Segment* last, bool readSpanner)
m_tracks.push_back(dl);
}
}
backupJumpsMarkersBarLines(first, last);
}

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

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

//---------------------------------------------------------
// finalMesPosition
//---------------------------------------------------------

bool ScoreRange::finalMesPosition(EngravingItem* e) const
{
bool result = false;

if (e->isMarker()
&& ((muse::contains(Marker::RIGHT_MARKERS, toMarker(e)->markerType()) || toMarker(e)->markerType() == MarkerType::FINE))) {
result = true;
} else if (e->isJump()) {
result = true;
}
return result;
}

//---------------------------------------------------------
// backupJumpsMarkersBarLines
//---------------------------------------------------------

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

for (Measure* m = fm; m && m != lm->nextMeasure(); m = m->nextMeasure()) {
// Backup Markers and Jumps (Measures's son)
for (EngravingItem* e : m->el()) {
if (e && (e->isMarker() || e->isJump())) {
BJMBackup mBackup;
mBackup.sPosition = (finalMesPosition(e) ? m->endTick() : m->tick());
mBackup.e = e->clone();
m_bjm.push_back(mBackup);
}
}

// Backup BarLines (Segments's sons)
for (Segment* s = m->first(); s; s = s->next1()) {
for (EngravingItem* e : s->elist()) {
if (e && e->isBarLine()) {
BJMBackup mBackup;
mBackup.sPosition = s->tick();
mBackup.e = e->clone();
m_bjm.push_back(mBackup);
}
}

// Last segment
if (s == m->last()) {
break;
}
}
}
}

//---------------------------------------------------------
// insertJumpAndMarker
//---------------------------------------------------------

void ScoreRange::insertJumpAndMarker(Measure* fMeasure, const BJMBackup& element) const
{
for (Measure* m = fMeasure; m; m = m->nextMeasure()) {
// Markers: we keep them as long as they are in the measure before the final tick
// Jumps: we keep them as long as they are in the measure after the start tick

if (((finalMesPosition(element.e)) && ((element.sPosition > m->tick()) && (element.sPosition <= m->endTick())))
|| ((!finalMesPosition(element.e)) && ((element.sPosition >= m->tick()) && (element.sPosition < m->endTick())))) {
EngravingItem* ce = element.e->clone();
ce->setParent(m);
ce->setTrack(0);
m->add(ce);
}

// Double check to deal only with suitable Measures
if (m->sectionBreak() || (m->nextMeasure() && (m->nextMeasure()->first(SegmentType::TimeSig)))) {
break;
}
}
}

//---------------------------------------------------------
// insertBarLineIn
//---------------------------------------------------------

void ScoreRange::insertBarLine(Measure* fMeasure, const BJMBackup& barLine) const
{
//---------------------------------------------------------
// addBarLine
//---------------------------------------------------------
auto addBarLine = [&](Measure* m, BarLine* bl, SegmentType st, Fraction pos)
{
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());
nbl->setSpanStaff(bl->spanStaff());
m->score()->addElement(nbl);
} else {
// We change BarLineType if necessary to keep END_START repeats
if ((nbl->barLineType() == BarLineType::END_REPEAT) && (bl->barLineType() == BarLineType::START_REPEAT)) {
blt = BarLineType::END_START_REPEAT;
}
}
nbl->setGenerated(false);
nbl->setBarLineType(blt);
nbl->setVisible(bl->visible());
nbl->setColor(bl->color());

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

BarLine* bl = toBarLine(barLine.e);
bool blProcessed = false;
for (Measure* m = fMeasure; m && !blProcessed; m = m->nextMeasure()) {
// Within the Measure
if ((barLine.sPosition >= m->tick()) && (barLine.sPosition <= m->endTick())) {
// Duplicate the Barline END_START_REPEAT if at the end of a Measure
if (barLine.sPosition == m->endTick() && bl->barLineType() == BarLineType::END_START_REPEAT) {
bl->setBarLineType(BarLineType::END_REPEAT);
insertBarLine(fMeasure, barLine);
bl->setBarLineType(BarLineType::START_REPEAT);
insertBarLine(fMeasure, barLine);

bl->setBarLineType(BarLineType::END_START_REPEAT);
blProcessed = true;
} else {
// First position
if (barLine.sPosition == m->tick()) {
// Just Start Repeat at the left of the Measure
if (bl->barLineType() == BarLineType::START_REPEAT) {
addBarLine(m, bl, SegmentType::StartRepeatBarLine, barLine.sPosition);
blProcessed = true;
}
}
// Last position
else if (barLine.sPosition == m->endTick()) {
// Avoid Start Repeat at the end of the Measure
if (bl->barLineType() != BarLineType::START_REPEAT) {
addBarLine(m, bl, SegmentType::EndBarLine, barLine.sPosition);
blProcessed = true;
}
}
// Middle
else {
addBarLine(m, bl, SegmentType::BarLine, barLine.sPosition);
blProcessed = true;
}
}
}
// Last Measure
if (m->sectionBreak() || (m->nextMeasure() && (m->nextMeasure()->first(SegmentType::TimeSig)))) {
break;
}
}
}

//---------------------------------------------------------
// restoreJumpsMarkersBarLines
//---------------------------------------------------------

void ScoreRange::restoreJumpsMarkersBarLines(Score* score, const Fraction& tick) const
{
for (const BJMBackup& bjm : m_bjm) {
if (bjm.e->isMarker() || bjm.e->isJump()) {
insertJumpAndMarker(score->tick2measure(tick), bjm);
} else if ((bjm.e->isBarLine()) && (toBarLine(bjm.e)->barLineType() != BarLineType::NORMAL)
&& (toBarLine(bjm.e)->barLineType() != BarLineType::END)) {
insertBarLine(score->tick2measure(tick), bjm);
}
}
}

//---------------------------------------------------------
// dump
//---------------------------------------------------------
Expand Down
15 changes: 15 additions & 0 deletions src/engraving/dom/range.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ class Spanner;
class ScoreRange;
class ChordRest;
class Score;
class Marker;
class BarLine;
class Jump;

//---------------------------------------------------------
// TrackList
Expand Down Expand Up @@ -111,10 +114,22 @@ class ScoreRange
private:

friend class TrackList;
struct BJMBackup
{
Fraction sPosition;
EngravingItem* e = nullptr;
};

bool finalMesPosition(EngravingItem* e) const;
void backupJumpsMarkersBarLines(Segment* first, Segment* last);
void insertJumpAndMarker(Measure* fMeasure, const BJMBackup& element) const;
void insertBarLine(Measure* fMeasure, const BJMBackup& barLine) const;
void restoreJumpsMarkersBarLines(Score*, const Fraction&) const;

std::list<TrackList*> m_tracks;
Segment* m_first = nullptr;
Segment* m_last = nullptr;
std::list<BJMBackup> m_bjm;
};
} // namespace mu::engraving
#endif

0 comments on commit f26e342

Please sign in to comment.