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

split / join measures in parts #19271

Merged
merged 5 commits into from Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/engraving/dom/chordrest.cpp
Expand Up @@ -168,7 +168,7 @@ EngravingItem* ChordRest::drop(EditData& data)

case ElementType::BAR_LINE:
if (data.control()) {
score()->splitMeasure(segment());
score()->cmdSplitMeasure(this);
} else {
BarLine* bl = toBarLine(e);
bl->setPos(PointF());
Expand Down
64 changes: 44 additions & 20 deletions src/engraving/dom/joinMeasure.cpp
Expand Up @@ -37,6 +37,23 @@ namespace mu::engraving {

void Score::cmdJoinMeasure(Measure* m1, Measure* m2)
{
if (!m1 || !m2) {
return;
}
masterScore()->joinMeasure(m1->tick(), m2->tick());
}

//---------------------------------------------------------
// joinMeasure
// join measures from tick1 upto (including) tick2
// always acts in masterScore
//---------------------------------------------------------

void MasterScore::joinMeasure(const Fraction& tick1, const Fraction& tick2)
{
Measure* m1 = tick2measure(tick1);
Measure* m2 = tick2measure(tick2);

if (!m1 || !m2) {
return;
}
Expand All @@ -55,34 +72,42 @@ void Score::cmdJoinMeasure(Measure* m1, Measure* m2)
if (m2->isMMRest()) {
m2 = m2->mmRestLast();
}
startCmd();

deselectAll();

ScoreRange range;
range.read(m1->first(), m2->last());

Fraction tick1 = m1->tick();
Fraction tick2 = m2->endTick();
Fraction startTick = m1->tick();
Fraction endTick = m2->endTick();

auto spanners = m_spanner.findContained(tick1.ticks(), tick2.ticks());
auto spanners = spannerMap().findContained(startTick.ticks(), endTick.ticks());
for (auto i : spanners) {
undo(new RemoveElement(i.value));
}

for (auto i : spanner()) {
Spanner* s = i.second;
if (s->tick() >= tick1 && s->tick() < tick2) {
if (s->tick() >= startTick && s->tick() < endTick) {
s->setStartElement(0);
}
if (s->tick2() >= tick1 && s->tick2() < tick2) {
if (s->tick2() >= startTick && s->tick2() < endTick) {
s->setEndElement(0);
}
}

deleteMeasures(m1, m2, true);

MeasureBase* next = m2->next();
InsertMeasureOptions options;
options.createEmptyMeasures = true;
options.moveSignaturesClef = false;
insertMeasure(ElementType::MEASURE, next, options);

for (Score* s : scoreList()) {
Measure* sM1 = s->tick2measure(startTick);
Measure* sM2 = s->tick2measure(m2->tick());
s->undoRemoveMeasures(sM1, sM2, true);
}

const Fraction newTimesig = m1->timesig();
Fraction newLen;
for (Measure* mm = m1; mm; mm = mm->nextMeasure()) {
Expand All @@ -92,24 +117,23 @@ void Score::cmdJoinMeasure(Measure* m1, Measure* m2)
}
}

InsertMeasureOptions options;
options.createEmptyMeasures = true;

insertMeasure(ElementType::MEASURE, next, options);
// The loop since measures are not currently linked in MuseScore
for (Score* s : masterScore()->scoreList()) {
Measure* ins = s->tick2measure(tick1);
// change nominal time sig, as inserted measure took it from next measure
for (Score* s : scoreList()) {
Measure* ins = s->tick2measure(startTick);
ins->undoChangeProperty(Pid::TIMESIG_NOMINAL, newTimesig);
// TODO: there was a commented chunk of code regarding setting bar
// line types. Should we handle them here too?
// m->setEndBarLineType(m2->endBarLineType(), m2->endBarLineGenerated(),
// m2->endBarLineVisible(), m2->endBarLineColor());
// set correct barline types if needed
// TODO: handle other end barline types; they may differ per staff
if (m2->endBarLineType() == BarLineType::END_REPEAT) {
ins->undoChangeProperty(Pid::REPEAT_END, true);
}
if (m1->getProperty(Pid::REPEAT_START).toBool()) {
ins->undoChangeProperty(Pid::REPEAT_START, true);
}
}
Measure* inserted = (next ? next->prevMeasure() : lastMeasure());
inserted->adjustToLen(newLen, /* appendRests... */ false);

range.write(this, m1->tick());

endCmd();
}
}
3 changes: 3 additions & 0 deletions src/engraving/dom/masterscore.h
Expand Up @@ -215,6 +215,9 @@ class MasterScore : public Score

MasterScore* unrollRepeats();

void splitMeasure(const Fraction&);
void joinMeasure(const Fraction&, const Fraction&);

IFileInfoProviderPtr fileInfo() const;
void setFileInfoProvider(IFileInfoProviderPtr fileInfoProvider);

Expand Down
1 change: 0 additions & 1 deletion src/engraving/dom/score.h
Expand Up @@ -785,7 +785,6 @@ class Score : public EngravingObject
void setMetaTag(const String& tag, const String& val);

void cmdSplitMeasure(ChordRest*);
void splitMeasure(Segment*);
void cmdJoinMeasure(Measure*, Measure*);

int pageNumberOffset() const { return m_pageNumberOffset; }
Expand Down
38 changes: 30 additions & 8 deletions src/engraving/dom/splitMeasure.cpp
Expand Up @@ -39,17 +39,17 @@ namespace mu::engraving {

void Score::cmdSplitMeasure(ChordRest* cr)
{
startCmd();
splitMeasure(cr->segment());
endCmd();
masterScore()->splitMeasure(cr->tick());
}

//---------------------------------------------------------
// splitMeasure
//---------------------------------------------------------

void Score::splitMeasure(Segment* segment)
void MasterScore::splitMeasure(const Fraction& tick)
{
Segment* segment = tick2segment(tick, false, SegmentType::ChordRest);

if (segment->rtick().isZero()) {
MScore::setError(MsError::CANNOT_SPLIT_MEASURE_FIRST_BEAT);
return;
Expand All @@ -58,7 +58,9 @@ void Score::splitMeasure(Segment* segment)
MScore::setError(MsError::CANNOT_SPLIT_MEASURE_TUPLET);
return;
}
Measure* measure = segment->measure();

Measure* measure = tick2measure(tick);

for (size_t staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
if (measure->isMeasureRepeatGroup(static_cast<int>(staffIdx))) {
MScore::setError(MsError::CANNOT_SPLIT_MEASURE_REPEAT);
Expand Down Expand Up @@ -118,16 +120,21 @@ void Score::splitMeasure(Segment* segment)
insertMeasure(ElementType::MEASURE, m2, options);
Measure* m1 = toMeasure(m2->prev());

undoRemoveMeasures(measure, measure, true);
for (Score* s : scoreList()) {
Measure* m = s->tick2measure(tick);
s->undoRemoveMeasures(m, m, true);
}
undoInsertTime(measure->tick(), -measure->ticks());

Fraction tick = segment->tick();
m1->setTick(measure->tick());
m2->setTick(tick);
Fraction ticks1 = segment->tick() - measure->tick();

Fraction ticks1 = tick - measure->tick();
Fraction ticks2 = measure->ticks() - ticks1;

m1->setTimesig(measure->timesig());
m2->setTimesig(measure->timesig());

ticks1.reduce();
ticks2.reduce();
// Now make sure this reduction doesn't go 'beyond' the original measure's
Expand All @@ -151,8 +158,23 @@ void Score::splitMeasure(Segment* segment)
MScore::setError(MsError::CANNOT_SPLIT_MEASURE_TOO_SHORT);
return;
}

m1->adjustToLen(ticks1, false);
m2->adjustToLen(ticks2, false);

// set correct barline types if needed
for (Score* s : scoreList()) {
Measure* sM1 = s->tick2measure(m1->tick());
Measure* sM2 = s->tick2measure(m2->tick());
// TODO: handle other end barline types; they may differ per staff
if (measure->endBarLineType() == BarLineType::END_REPEAT) {
sM2->undoChangeProperty(Pid::REPEAT_END, true);
}
if (measure->getProperty(Pid::REPEAT_START).toBool()) {
sM1->undoChangeProperty(Pid::REPEAT_START, true);
}
}

range.write(this, m1->tick());

// Restore ties to the beginning of the split measure.
Expand Down
3 changes: 0 additions & 3 deletions src/engraving/tests/join_data/join01-ref.mscx
Expand Up @@ -74,9 +74,6 @@
<transposingClefType>G</transposingClefType>
<isHeader>1</isHeader>
</Clef>
<KeySig>
<concertKey>0</concertKey>
</KeySig>
<TimeSig>
<sigN>4</sigN>
<sigD>4</sigD>
Expand Down
3 changes: 0 additions & 3 deletions src/engraving/tests/join_data/join02-ref.mscx
Expand Up @@ -74,9 +74,6 @@
<transposingClefType>G</transposingClefType>
<isHeader>1</isHeader>
</Clef>
<KeySig>
<concertKey>0</concertKey>
</KeySig>
<TimeSig>
<sigN>4</sigN>
<sigD>4</sigD>
Expand Down
3 changes: 0 additions & 3 deletions src/engraving/tests/join_data/join03-ref.mscx
Expand Up @@ -74,9 +74,6 @@
<transposingClefType>G</transposingClefType>
<isHeader>1</isHeader>
</Clef>
<KeySig>
<concertKey>0</concertKey>
</KeySig>
<TimeSig>
<sigN>4</sigN>
<sigD>4</sigD>
Expand Down
3 changes: 0 additions & 3 deletions src/engraving/tests/join_data/join04-ref.mscx
Expand Up @@ -74,9 +74,6 @@
<transposingClefType>G</transposingClefType>
<isHeader>1</isHeader>
</Clef>
<KeySig>
<concertKey>0</concertKey>
</KeySig>
<TimeSig>
<sigN>4</sigN>
<sigD>4</sigD>
Expand Down
3 changes: 0 additions & 3 deletions src/engraving/tests/join_data/join05-ref.mscx
Expand Up @@ -74,9 +74,6 @@
<transposingClefType>G</transposingClefType>
<isHeader>1</isHeader>
</Clef>
<KeySig>
<concertKey>0</concertKey>
</KeySig>
<TimeSig>
<sigN>4</sigN>
<sigD>4</sigD>
Expand Down
3 changes: 0 additions & 3 deletions src/engraving/tests/join_data/join07-ref.mscx
Expand Up @@ -74,9 +74,6 @@
<transposingClefType>G</transposingClefType>
<isHeader>1</isHeader>
</Clef>
<KeySig>
<concertKey>0</concertKey>
</KeySig>
<TimeSig>
<sigN>4</sigN>
<sigD>4</sigD>
Expand Down
2 changes: 2 additions & 0 deletions src/engraving/tests/join_tests.cpp
Expand Up @@ -61,7 +61,9 @@ void Engraving_JoinTests::join(const char* p1, const char* p2, int index)

EXPECT_NE(m1, m2);

score->startCmd();
score->cmdJoinMeasure(m1, m2);
score->endCmd();

EXPECT_TRUE(ScoreComp::saveCompareScore(score, String::fromUtf8(p1), JOIN_DATA_DIR + String::fromUtf8(p2)));
delete score;
Expand Down
2 changes: 2 additions & 0 deletions src/engraving/tests/split_tests.cpp
Expand Up @@ -52,7 +52,9 @@ void Engraving_SplitTests::split(const char* f1, const char* ref, int index)
}
ChordRest* cr = toChordRest(s->element(0));

score->startCmd();
score->cmdSplitMeasure(cr);
score->endCmd();

EXPECT_TRUE(ScoreComp::saveCompareScore(score, String::fromUtf8(f1), SPLIT_DATA_DIR + String::fromUtf8(ref)));
delete score;
Expand Down