Skip to content

Commit 8d6f0f7

Browse files
wizofausJojo-Schmitz
authored andcommitted
Fix #321809: Fix pasting notes
Backport of musescore#8825, better fix than musescore#8816, better even than my alternative, which this commits reverts. It does however change the way noteheads are pasted: now with the duration of the source chord rather than the duration of the target.
1 parent d6ec03b commit 8d6f0f7

29 files changed

+4305
-45
lines changed

libmscore/chordrest.cpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -507,16 +507,17 @@ Element* ChordRest::drop(EditData& data)
507507

508508
case ElementType::NOTE: {
509509
Note* note = toNote(e);
510-
NoteVal nval;
511-
nval.pitch = note->pitch();
512-
nval.tpc1 = note->tpc1();
513-
nval.headGroup = note->headGroup();
514-
nval.fret = note->fret();
515-
nval.string = note->string();
516-
score()->setNoteRest(segment(), track(), nval, ticks(), Direction::AUTO);
517-
delete e;
510+
Segment* seg = segment();
511+
score()->undoRemoveElement(this);
512+
Chord* chord = new Chord(score());
513+
chord->setTrack(track());
514+
chord->setDurationType(durationType());
515+
chord->setTicks(ticks());
516+
chord->setTuplet(tuplet());
517+
chord->add(note);
518+
score()->undoAddCR(chord, seg->measure(), seg->tick());
519+
return note;
518520
}
519-
break;
520521

521522
case ElementType::HARMONY:
522523
{
@@ -1407,7 +1408,7 @@ void ChordRest::removeMarkings(bool /* keepTremolo */)
14071408
bool ChordRest::isBefore(const ChordRest* o) const
14081409
{
14091410
if (!o || this == o)
1410-
return true;
1411+
return false;
14111412
int otick = o->tick().ticks();
14121413
int t = tick().ticks();
14131414
if (t == otick) { // At least one of the chord is a grace, order the grace notes

libmscore/cmd.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -720,14 +720,18 @@ Segment* Score::setNoteRest(Segment* segment, int track, NoteVal nval, Fraction
720720
Element* nr = 0;
721721
Tie* tie = 0;
722722
ChordRest* cr = toChordRest(segment->element(track));
723-
723+
Tuplet* tuplet = cr && cr->tuplet() && sd <= cr->tuplet()->ticks() ? cr->tuplet() : nullptr;
724724
Measure* measure = 0;
725+
bool targetIsRest = cr && cr->isRest();
725726
for (;;) {
726727
if (track % VOICES)
727728
expandVoice(segment, track);
728-
729+
if (targetIsRest && !cr->isRest()) {
730+
undoRemoveElement(cr);
731+
segment = addRest(segment, track, cr->ticks(), cr->tuplet())->segment();
732+
}
729733
// the returned gap ends at the measure boundary or at tuplet end
730-
Fraction dd = makeGap(segment, track, sd, cr ? cr->tuplet() : 0);
734+
Fraction dd = makeGap(segment, track, sd, tuplet);
731735

732736
if (dd.isZero()) {
733737
qDebug("cannot get gap at %d type: %d/%d", tick.ticks(), sd.numerator(),
@@ -787,7 +791,9 @@ Segment* Score::setNoteRest(Segment* segment, int track, NoteVal nval, Fraction
787791
note->setTieFor(tie);
788792
}
789793
}
790-
ncr->setTuplet(cr ? cr->tuplet() : 0);
794+
if (tuplet && sd <= tuplet->ticks())
795+
ncr->setTuplet(tuplet);
796+
tuplet = 0;
791797
undoAddCR(ncr, measure, tick);
792798
if (addTie)
793799
undoAddElement(addTie);

libmscore/edit.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ Rest* Score::setRest(const Fraction& _tick, int track, const Fraction& _l, bool
336336
// compute list of durations which will fit l
337337
//
338338
std::vector<TDuration> dList;
339-
if (tuplet || staff->isLocalTimeSignature(tick)) {
339+
if (tuplet || staff->isLocalTimeSignature(tick) || f == Fraction(0, 1)) {
340340
dList = toDurationList(l, useDots);
341341
std::reverse(dList.begin(), dList.end());
342342
}

libmscore/note.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1845,6 +1845,11 @@ Element* Note::drop(EditData& data)
18451845
n->setTpc2(Ms::transposeTpc(n->tpc1(), v, true));
18461846
// replace this note with new note
18471847
n->setParent(ch);
1848+
if (this->tieBack()) {
1849+
n->setTieBack(this->tieBack());
1850+
n->tieBack()->setEndNote(n);
1851+
this->setTieBack(nullptr);
1852+
}
18481853
score()->undoRemoveElement(this);
18491854
score()->undoAddElement(n);
18501855
}

libmscore/paste.cpp

Lines changed: 71 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,59 @@ void Score::pasteSymbols(XmlReader& e, ChordRest* dst)
982982
} // inner while readNextstartElement()
983983
} // pasteSymbolList()
984984

985+
static ChordRest* replaceWithRest(ChordRest* target)
986+
{
987+
target->score()->undoRemoveElement(target);
988+
return target->score()->addRest(target->segment(), target->track(), target->ticks(), target->tuplet());
989+
}
990+
991+
static Note* prepareTarget(ChordRest* target, Note* with, const Fraction& duration)
992+
{
993+
if (!target->segment()->element(target->track()))
994+
return nullptr; // target was removed by previous operation, ignore this
995+
if (target->isChord() && target->ticks() > duration)
996+
target = replaceWithRest(target); // prevent unexpected note splitting
997+
Segment* segment = target->segment();
998+
if (segment->measure()->isMMRest()) {
999+
Measure* m = segment->measure()->mmRestFirst();
1000+
segment = m->findSegment(SegmentType::ChordRest, m->tick());
1001+
}
1002+
segment = target->score()->setNoteRest(segment, target->track(),
1003+
with->noteVal(), duration, Direction::AUTO, false, false, &target->score()->inputState());
1004+
return toChord(segment->nextChordRest(target->track()))->upNote();
1005+
}
1006+
1007+
static Element* prepareTarget(Element* target, Note* with, const Fraction& duration)
1008+
{
1009+
if (target->isNote() && toNote(target)->chord()->ticks() != duration)
1010+
return prepareTarget(toNote(target)->chord(), with, duration);
1011+
if (target->isChordRest() && toChordRest(target)->ticks() != duration)
1012+
return prepareTarget(toChordRest(target), with, duration);
1013+
return target;
1014+
}
1015+
1016+
static bool canPasteStaff(XmlReader& reader, const Fraction& scale)
1017+
{
1018+
if (scale != Fraction(1, 1)) {
1019+
while (reader.readNext() && reader.tokenType() != XmlReader::TokenType::EndDocument) {
1020+
QString tag(reader.name().toString());
1021+
int len = reader.intAttribute("len", 0);
1022+
if (len && !TDuration(Fraction::fromTicks(len) * scale).isValid())
1023+
return false;
1024+
if (tag == "durationType")
1025+
if (!TDuration(TDuration(reader.readElementText()).fraction() * scale).isValid())
1026+
return false;
1027+
}
1028+
}
1029+
return true;
1030+
}
1031+
1032+
inline static bool canPasteStaff(const QByteArray& mimeData, const Fraction& scale)
1033+
{
1034+
XmlReader reader(mimeData);
1035+
return canPasteStaff(reader, scale);
1036+
}
1037+
9851038
//---------------------------------------------------------
9861039
// cmdPaste
9871040
//---------------------------------------------------------
@@ -1002,45 +1055,33 @@ void Score::cmdPaste(const QMimeData* ms, MuseScoreView* view, Fraction scale)
10021055

10031056
if (!el)
10041057
return;
1058+
duration *= scale;
1059+
if (!TDuration(duration).isValid())
1060+
return;
10051061

10061062
QList<Element*> els;
10071063
if (_selection.isSingle())
10081064
els.append(_selection.element());
10091065
else
10101066
els.append(_selection.elements());
1011-
1067+
Element* nel = 0;
10121068
for (Element* target : els) {
10131069
el->setTrack(target->track());
1014-
Element* nel = el->clone();
10151070
addRefresh(target->abbox()); // layout() ?!
10161071
EditData ddata(view);
10171072
ddata.view = view;
1018-
ddata.dropElement = nel;
1073+
ddata.dropElement = el.get();
10191074
if (target->acceptDrop(ddata)) {
1020-
if (el->isNote()) {
1021-
// dropping a note replaces and invalidates the target,
1022-
// so we need to deselect it
1023-
ElementType targetType = target->type();
1024-
deselect(target);
1025-
1026-
// perform the drop
1027-
target->drop(ddata);
1028-
1029-
// if the target is a rest rather than a note,
1030-
// a new note is generated, and nel becomes invalid as well
1031-
// (ChordRest::drop() will select it for us)
1032-
if (targetType == ElementType::NOTE)
1033-
select(nel);
1075+
if (!el->isNote() || (target = prepareTarget(target, toNote(el.get()), duration))) {
1076+
ddata.dropElement = nel = el->clone();
1077+
Element* dropped = target->drop(ddata);
1078+
if (dropped)
1079+
nel = dropped;
10341080
}
1035-
else {
1036-
target->drop(ddata);
1037-
}
1038-
if (_selection.element())
1039-
addRefresh(_selection.element()->abbox());
10401081
}
1041-
else
1042-
delete nel;
10431082
}
1083+
if (nel)
1084+
select(nel);
10441085
}
10451086
else if ((_selection.isRange() || _selection.isList()) && ms->hasFormat(mimeStaffListFormat)) {
10461087
ChordRest* cr = 0;
@@ -1069,10 +1110,12 @@ void Score::cmdPaste(const QMimeData* ms, MuseScoreView* view, Fraction scale)
10691110
QByteArray data(ms->data(mimeStaffListFormat));
10701111
if (MScore::debugMode)
10711112
qDebug("paste <%s>", data.data());
1072-
XmlReader e(data);
1073-
e.setPasteMode(true);
1074-
if (!pasteStaff(e, cr->segment(), cr->staffIdx(), scale))
1075-
return;
1113+
if (canPasteStaff(data, scale)) {
1114+
XmlReader e(data);
1115+
e.setPasteMode(true);
1116+
if (!pasteStaff(e, cr->segment(), cr->staffIdx(), scale))
1117+
return;
1118+
}
10761119
}
10771120
}
10781121
else if (ms->hasFormat(mimeSymbolListFormat)) {

libmscore/rest.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ bool Rest::acceptDrop(EditData& data) const
214214
|| (type == ElementType::BAR_LINE)
215215
|| (type == ElementType::BREATH)
216216
|| (type == ElementType::CHORD)
217-
|| (type == ElementType::NOTE && !measure()->isMMRest()) // avoid crash
217+
|| (type == ElementType::NOTE)
218218
|| (type == ElementType::STAFF_STATE)
219219
|| (type == ElementType::INSTRUMENT_CHANGE)
220220
|| (type == ElementType::DYNAMIC)

0 commit comments

Comments
 (0)