Skip to content

Commit

Permalink
[MU4] fix #8824 - fix pasting notes
Browse files Browse the repository at this point in the history
  • Loading branch information
dylan.nicholson committed Aug 12, 2021
1 parent 5c72065 commit 7245915
Show file tree
Hide file tree
Showing 28 changed files with 4,324 additions and 68 deletions.
21 changes: 11 additions & 10 deletions src/engraving/libmscore/chordrest.cpp
Expand Up @@ -519,16 +519,17 @@ Element* ChordRest::drop(EditData& data)

case ElementType::NOTE: {
Note* note = toNote(e);
NoteVal nval;
nval.pitch = note->pitch();
nval.tpc1 = note->tpc1();
nval.headGroup = note->headGroup();
nval.fret = note->fret();
nval.string = note->string();
score()->setNoteRest(segment(), track(), nval, ticks(), Direction::AUTO);
delete e;
Segment* seg = segment();
score()->undoRemoveElement(this);
Chord* chord = new Chord(score());
chord->setTrack(track());
chord->setDurationType(durationType());
chord->setTicks(ticks());
chord->setTuplet(tuplet());
chord->add(note);
score()->undoAddCR(chord, seg->measure(), seg->tick());
return note;
}
break;

case ElementType::HARMONY:
{
Expand Down Expand Up @@ -1433,7 +1434,7 @@ void ChordRest::removeMarkings(bool /* keepTremolo */)
bool ChordRest::isBefore(const ChordRest* o) const
{
if (!o || this == o) {
return true;
return false;
}
int otick = o->tick().ticks();
int t = tick().ticks();
Expand Down
15 changes: 11 additions & 4 deletions src/engraving/libmscore/cmd.cpp
Expand Up @@ -777,15 +777,19 @@ Segment* Score::setNoteRest(Segment* segment, int track, NoteVal nval, Fraction
Element* nr = nullptr;
Tie* tie = nullptr;
ChordRest* cr = toChordRest(segment->element(track));

Tuplet* tuplet = cr && cr->tuplet() && sd <= cr->tuplet()->ticks() ? cr->tuplet() : nullptr;
Measure* measure = nullptr;
bool targetIsRest = cr && cr->isRest();
for (;;) {
if (track % VOICES) {
expandVoice(segment, track);
}

if (targetIsRest && !cr->isRest()) {
undoRemoveElement(cr);
segment = addRest(segment, track, cr->ticks(), cr->tuplet())->segment();
}
// the returned gap ends at the measure boundary or at tuplet end
Fraction dd = makeGap(segment, track, sd, cr ? cr->tuplet() : 0);
Fraction dd = makeGap(segment, track, sd, tuplet);

if (dd.isZero()) {
qDebug("cannot get gap at %d type: %d/%d", tick.ticks(), sd.numerator(),
Expand Down Expand Up @@ -846,7 +850,10 @@ Segment* Score::setNoteRest(Segment* segment, int track, NoteVal nval, Fraction
note->setTieFor(tie);
}
}
ncr->setTuplet(cr ? cr->tuplet() : 0);
if (tuplet && sd <= tuplet->ticks()) {
ncr->setTuplet(tuplet);
}
tuplet = 0;
undoAddCR(ncr, measure, tick);
if (addTie) {
undoAddElement(addTie);
Expand Down
2 changes: 1 addition & 1 deletion src/engraving/libmscore/edit.cpp
Expand Up @@ -453,7 +453,7 @@ Rest* Score::setRest(const Fraction& _tick, int track, const Fraction& _l, bool
// compute list of durations which will fit l
//
std::vector<TDuration> dList;
if (tuplet || staff->isLocalTimeSignature(tick)) {
if (tuplet || staff->isLocalTimeSignature(tick) || f == Fraction(0, 1)) {
dList = toDurationList(l, useDots);
std::reverse(dList.begin(), dList.end());
} else {
Expand Down
5 changes: 5 additions & 0 deletions src/engraving/libmscore/note.cpp
Expand Up @@ -2009,6 +2009,11 @@ Element* Note::drop(EditData& data)
n->setTpc2(Ms::transposeTpc(n->tpc1(), v, true));
// replace this note with new note
n->setParent(ch);
if (this->tieBack()) {
n->setTieBack(this->tieBack());
n->tieBack()->setEndNote(n);
this->setTieBack(nullptr);
}
score()->undoRemoveElement(this);
score()->undoAddElement(n);
}
Expand Down
110 changes: 74 additions & 36 deletions src/engraving/libmscore/paste.cpp
Expand Up @@ -1031,6 +1031,60 @@ void Score::pasteSymbols(XmlReader& e, ChordRest* dst)
} // inner while readNextstartElement()
} // pasteSymbolList()

static ChordRest* replaceWithRest(ChordRest* target)
{
target->score()->undoRemoveElement(target);
return target->score()->addRest(target->segment(), target->track(), target->ticks(), target->tuplet());
}

static Note* prepareTarget(ChordRest* target, Note* with, const Fraction& duration)
{
if (!target->segment()->element(target->track())) {
return nullptr; // target was removed by previous operation, ignore this
}
if (target->isChord() && target->ticks() > duration) {
target = replaceWithRest(target); // prevent unexpected note splitting
}
Segment* segment = target->segment();
if (segment->measure()->isMMRest()) {
Measure* m = segment->measure()->mmRestFirst();
segment = m->findSegment(SegmentType::ChordRest, m->tick());
}
segment = target->score()->setNoteRest(segment, target->track(),
with->noteVal(), duration, Direction::AUTO, false, {}, false, &target->score()->inputState());
return toChord(segment->nextChordRest(target->track()))->upNote();
}

static Element* prepareTarget(Element* target, Note* with, const Fraction& duration)
{
if (target->isNote() && toNote(target)->chord()->ticks() != duration) {
return prepareTarget(toNote(target)->chord(), with, duration);
}
if (target->isChordRest() && toChordRest(target)->ticks() != duration) {
return prepareTarget(toChordRest(target), with, duration);
}
return target;
}

static bool canPasteStaff(XmlReader& reader, const Fraction& scale)
{
if (scale != Fraction(1, 1)) {
while (reader.readNext() && reader.tokenType() != XmlReader::TokenType::EndDocument) {
QString tag(reader.name().toString());
int len = reader.intAttribute("len", 0);
if (len && !TDuration(Fraction::fromTicks(len) * scale).isValid()) {
return false;
}
if (tag == "durationType") {
if (!TDuration(TDuration(reader.readElementText()).fraction() * scale).isValid()) {
return false;
}
}
}
}
return true;
}

//---------------------------------------------------------
// cmdPaste
//---------------------------------------------------------
Expand All @@ -1052,55 +1106,36 @@ void Score::cmdPaste(const QMimeData* ms, MuseScoreView* view, Fraction scale)
if (!el) {
return;
}
duration *= scale;
if (!TDuration(duration).isValid()) {
return;
}

QList<Element*> els;
if (_selection.isSingle()) {
els.append(_selection.element());
} else {
els.append(_selection.elements());
}

Element* newEl = 0;
for (Element* target : els) {
el->setTrack(target->track());
Element* nel = el->clone();
addRefresh(target->abbox()); // layout() ?!
EditData ddata(view);
ddata.dropElement = nel;
ddata.dropElement = el.get();
if (target->acceptDrop(ddata)) {
if (el->isNote()) {
// dropping a note replaces and invalidates the target,
// so we need to deselect it
ElementType targetType = target->type();
deselect(target);

// perform the drop
target->drop(ddata);

TDuration durationType(duration);
if (target->isChordRest()) {
ChordRest* targetCR = dynamic_cast<ChordRest*>(target);
score()->changeCRlen(targetCR, durationType);
} else if (target->isNote()) {
ChordRest* targetCR = dynamic_cast<Note*>(target)->chord();
score()->changeCRlen(targetCR, durationType);
if (!el->isNote() || (target = prepareTarget(target, toNote(el.get()), duration))) {
ddata.dropElement = newEl = el->clone();
Element* dropped = target->drop(ddata);
if (dropped) {
newEl = dropped;
}

// if the target is a rest rather than a note,
// a new note is generated, and nel becomes invalid as well
// (ChordRest::drop() will select it for us)
if (targetType == ElementType::NOTE) {
select(nel);
}
} else {
target->drop(ddata);
}
if (_selection.element()) {
addRefresh(_selection.element()->abbox());
}
} else {
delete nel;
}
}
if (newEl) {
select(newEl);
}
} else if ((_selection.isRange() || _selection.isList()) && ms->hasFormat(mimeStaffListFormat)) {
ChordRest* cr = 0;
if (_selection.isRange()) {
Expand Down Expand Up @@ -1129,9 +1164,12 @@ void Score::cmdPaste(const QMimeData* ms, MuseScoreView* view, Fraction scale)
qDebug("paste <%s>", data.data());
}
XmlReader e(data);
e.setPasteMode(true);
if (!pasteStaff(e, cr->segment(), cr->staffIdx(), scale)) {
return;
if (canPasteStaff(e, scale)) {
XmlReader e(data);
e.setPasteMode(true);
if (!pasteStaff(e, cr->segment(), cr->staffIdx(), scale)) {
return;
}
}
}
} else if (ms->hasFormat(mimeSymbolListFormat)) {
Expand Down

0 comments on commit 7245915

Please sign in to comment.