diff --git a/src/libmscore/beam.cpp b/src/libmscore/beam.cpp index 59201c851e9b1..94a8136bc3f37 100644 --- a/src/libmscore/beam.cpp +++ b/src/libmscore/beam.cpp @@ -1826,7 +1826,7 @@ void Beam::layout2(std::vector crl, SpannerSegmentType, int frag) for (; i < n; ++i) { ChordRest* c = crl[i]; ChordRest* p = i ? crl[i - 1] : 0; - int l = c->durationType().hooks() - 1; + int l = c->isChord() ? c->durationType().hooks() - 1 : beamLevel; Mode bm = Groups::endBeam(c, p); b32 = (beamLevel >= 1) && (bm == Mode::BEGIN32); diff --git a/src/libmscore/chord.cpp b/src/libmscore/chord.cpp index 5a338dd40020e..f91472747cf75 100644 --- a/src/libmscore/chord.cpp +++ b/src/libmscore/chord.cpp @@ -2759,7 +2759,12 @@ Element* Chord::drop(EditData& data) } } if (tremolo()) { + bool sameType = (e->subtype() == tremolo()->subtype()); score()->undoRemoveElement(tremolo()); + if (sameType) { + delete e; + return 0; + } } e->setParent(this); e->setTrack(track()); diff --git a/src/libmscore/edit.cpp b/src/libmscore/edit.cpp index 751c096f17f18..572f0e65b819e 100644 --- a/src/libmscore/edit.cpp +++ b/src/libmscore/edit.cpp @@ -1016,12 +1016,6 @@ bool Score::rewriteMeasures(Measure* fm, const Fraction& ns, int staffIdx) break; } } - } else { - // this can be hit for local time signatures as well - // (if we are rewriting all staves, but one has a local time signature) - // TODO: detect error conditions better, have clearer error messages - // and perform necessary fixups - MScore::setError(MsError::TUPLET_CROSSES_BAR); } for (Measure* m = fm1; m; m = m->nextMeasure()) { if (m->first(SegmentType::TimeSig)) { @@ -3072,6 +3066,14 @@ void Score::cmdDeleteSelection() tick = toMeasureRepeat(e)->firstMeasureOfGroup()->first()->tick(); } else if (e->isSpannerSegment()) { tick = toSpannerSegment(e)->spanner()->tick(); + } else if (e->isBreath()) { + // we want the tick of the ChordRest that precedes the breath mark (in the same track) + for (Segment* s = toBreath(e)->segment()->prev(); s; s = s->prev()) { + if (s->isChordRestType() && s->element(e->track())) { + tick = s->tick(); + break; + } + } } else if (e->parent() && (e->parent()->isSegment() || e->parent()->isChord() || e->parent()->isNote() || e->parent()->isRest())) { tick = e->parent()->tick(); diff --git a/src/libmscore/layout.cpp b/src/libmscore/layout.cpp index 15a6c2173358c..454fadf1df590 100644 --- a/src/libmscore/layout.cpp +++ b/src/libmscore/layout.cpp @@ -2735,8 +2735,7 @@ void Score::createBeams(LayoutContext& lc, Measure* measure) if (!beamNoContinue(prevCR->beamMode()) && !pm->lineBreak() && !pm->pageBreak() && !pm->sectionBreak() && lc.prevMeasure - && prevCR->durationType().type() >= TDuration::DurationType::V_EIGHTH - && prevCR->durationType().type() <= TDuration::DurationType::V_1024TH) { + && !(prevCR->isChord() && prevCR->durationType().type() <= TDuration::DurationType::V_QUARTER)) { beam = prevCR->beam(); //a1 = beam ? beam->elements().front() : prevCR; a1 = beam ? nullptr : prevCR; // when beam is found, a1 is no longer required. @@ -2801,7 +2800,7 @@ void Score::createBeams(LayoutContext& lc, Measure* measure) bm = Beam::Mode::NONE; } - if ((cr->durationType().type() <= TDuration::DurationType::V_QUARTER) || (bm == Beam::Mode::NONE)) { + if ((cr->isChord() && cr->durationType().type() <= TDuration::DurationType::V_QUARTER) || (bm == Beam::Mode::NONE)) { bool removeBeam = true; if (beam) { beam->layout1(); diff --git a/src/libmscore/layoutbreak.h b/src/libmscore/layoutbreak.h index f55f0a68cc8bb..9fe9607b6ebfb 100644 --- a/src/libmscore/layoutbreak.h +++ b/src/libmscore/layoutbreak.h @@ -56,6 +56,7 @@ class LayoutBreak final : public Element LayoutBreak* clone() const override { return new LayoutBreak(*this); } ElementType type() const override { return ElementType::LAYOUT_BREAK; } + int subtype() const override { return static_cast(_layoutBreakType); } void setLayoutBreakType(Type); Type layoutBreakType() const { return _layoutBreakType; } diff --git a/src/libmscore/mscore.cpp b/src/libmscore/mscore.cpp index 10c8120055fb7..b4852f8367101 100644 --- a/src/libmscore/mscore.cpp +++ b/src/libmscore/mscore.cpp @@ -169,6 +169,8 @@ std::vector MScore::errorList { { MsError::DEST_NO_CR, "p7", QT_TRANSLATE_NOOP("error", "Destination is not a chord or rest") }, { MsError::CANNOT_CHANGE_LOCAL_TIMESIG, "l1", QT_TRANSLATE_NOOP("error", "Cannot change local time signature:\nMeasure is not empty") }, + { MsError::CORRUPTED_MEASURE, "c1", QT_TRANSLATE_NOOP("error", + "Cannot change time signature in front of a corrupted measure") }, }; MsError MScore::_error { MsError::MS_NO_ERROR }; diff --git a/src/libmscore/mscore.h b/src/libmscore/mscore.h index e53322bab563a..ac6ed62d6e574 100644 --- a/src/libmscore/mscore.h +++ b/src/libmscore/mscore.h @@ -1,4 +1,4 @@ -//============================================================================= +//============================================================================= // MuseScore // Music Composition & Notation // @@ -264,6 +264,7 @@ enum class MsError { NO_MIME, DEST_NO_CR, CANNOT_CHANGE_LOCAL_TIMESIG, + CORRUPTED_MEASURE, }; /// \cond PLUGIN_API \private \endcond diff --git a/src/libmscore/range.cpp b/src/libmscore/range.cpp index e15c5153b8f3f..549bd344397d6 100644 --- a/src/libmscore/range.cpp +++ b/src/libmscore/range.cpp @@ -355,16 +355,18 @@ void TrackList::read(const Segment* fs, const Segment* es) // checkRest //--------------------------------------------------------- -static void checkRest(Fraction& rest, Measure*& m, const Fraction& d) +static bool checkRest(Fraction& rest, Measure*& m, const Fraction& d) { if (rest.isZero()) { if (m->nextMeasure()) { m = m->nextMeasure(); rest = m->ticks(); } else { - qFatal("premature end of measure list, rest %d/%d", d.numerator(), d.denominator()); + qWarning("premature end of measure list, rest %d/%d", d.numerator(), d.denominator()); + return false; } } + return true; } //--------------------------------------------------------- @@ -484,7 +486,10 @@ bool TrackList::write(Score* score, const Fraction& tick) const for (Element* e : *this) { if (e->isDurationElement()) { Fraction duration = toDurationElement(e)->ticks(); - checkRest(remains, m, duration); // go to next measure, if necessary + if (!checkRest(remains, m, duration)) { // go to next measure, if necessary + MScore::setError(MsError::CORRUPTED_MEASURE); + return false; + } if (duration > remains && e->isTuplet()) { // experimental: allow tuplet split in the middle if (duration != remains * 2) { @@ -516,7 +521,10 @@ bool TrackList::write(Score* score, const Fraction& tick) const } else if (e->isChordRest()) { Fraction du = qMin(remains, duration); std::vector dl = toDurationList(du, e->isChord()); - Q_ASSERT(!dl.empty()); + if (dl.empty()) { + MScore::setError(MsError::CORRUPTED_MEASURE); + return false; + } for (const TDuration& k : dl) { segment = m->undoGetSegmentR(SegmentType::ChordRest, m->ticks() - remains); ChordRest* cr = toChordRest(e->clone()); @@ -559,7 +567,10 @@ bool TrackList::write(Score* score, const Fraction& tick) const } firstpart = false; if (duration > Fraction(0,1)) { - checkRest(remains, m, duration); // go to next measure, if necessary + if (!checkRest(remains, m, duration)) { // go to next measure, if necessary + MScore::setError(MsError::CORRUPTED_MEASURE); + return false; + } } } } else if (e->isBarLine()) { diff --git a/src/libmscore/segment.cpp b/src/libmscore/segment.cpp index 319a328a681fa..3d5329b3a342e 100644 --- a/src/libmscore/segment.cpp +++ b/src/libmscore/segment.cpp @@ -1125,7 +1125,7 @@ bool Segment::hasElements(int minTrack, int maxTrack) const bool Segment::allElementsInvisible() const { - if (isType(SegmentType::BarLineType | SegmentType::ChordRest | SegmentType::Breath)) { + if (isType(SegmentType::BarLineType | SegmentType::ChordRest)) { return false; } diff --git a/src/libmscore/textbase.cpp b/src/libmscore/textbase.cpp index 739e2045ea47d..27557245a3c61 100644 --- a/src/libmscore/textbase.cpp +++ b/src/libmscore/textbase.cpp @@ -1856,6 +1856,12 @@ void TextBase::createLayout() if (rows() <= cursor.row()) { _layout.append(TextBlock()); } + + if (cursor.row() < rows()) { + if (_layout[cursor.row()].fragments().size() == 0) { + _layout[cursor.row()].insertEmptyFragmentIfNeeded(&cursor); // an empty fragment may be needed on either side of the newline + } + } } else { if (symState) { sym += c; diff --git a/src/libmscore/textedit.cpp b/src/libmscore/textedit.cpp index 02a87722c421c..114073bdb4cb4 100644 --- a/src/libmscore/textedit.cpp +++ b/src/libmscore/textedit.cpp @@ -571,13 +571,10 @@ void SplitJoinText::join(EditData* ed) t->textBlock(line - 1).fragments().append(*fragmentsList); delete fragmentsList; - int lines = t->rows(); - if (line < lines) { - t->textBlock(line).setEol(eol); - } t->textBlockList().removeAt(line); c.setRow(line - 1); + c.curLine().setEol(eol); c.setColumn(col); c.setFormat(*charFmt); // restore orig. format at new line c.clearSelection(); @@ -592,12 +589,13 @@ void SplitJoinText::split(EditData* ed) { TextBase* t = c.text(); int line = c.row(); + bool eol = c.curLine().eol(); t->setTextInvalid(); t->triggerLayout(); CharFormat* charFmt = c.format(); // take current format t->textBlockList().insert(line + 1, c.curLine().split(c.column(), t->cursorFromEditData(*ed))); - c.curLine().setEol(true); + c.curLine().setEol(eol); c.setRow(line + 1); c.curLine().setEol(true); diff --git a/vtest/scores/frametext.mscx b/vtest/scores/frametext.mscx index 2e9e30f5d255a..3d4eca3de6a80 100644 --- a/vtest/scores/frametext.mscx +++ b/vtest/scores/frametext.mscx @@ -154,8 +154,7 @@ 12 right,center - rightCenter - + rightCenter 12