MuseScore/libmscore/paste.cpp

975 lines
51 KiB
C++
Raw Normal View History

2012-08-09 12:12:43 +02:00
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2012 Werner Schweer
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation and appearing in
// the file LICENCE.GPL
//=============================================================================
#include "score.h"
2014-06-18 12:25:56 +02:00
#include "rest.h"
#include "staff.h"
#include "measure.h"
#include "harmony.h"
#include "breath.h"
2014-06-18 12:25:56 +02:00
#include "beam.h"
#include "figuredbass.h"
#include "ottava.h"
#include "part.h"
2014-06-18 12:25:56 +02:00
#include "lyrics.h"
#include "hairpin.h"
#include "tie.h"
#include "tuplet.h"
#include "utils.h"
#include "xml.h"
2014-11-21 12:52:07 +01:00
#include "image.h"
#include "repeat.h"
#include "chord.h"
#include "tremolo.h"
2012-08-09 12:12:43 +02:00
2013-05-13 18:49:17 +02:00
namespace Ms {
2014-08-24 10:52:28 +02:00
//---------------------------------------------------------
// transposeChord
//---------------------------------------------------------
static void transposeChord(Chord* c, Interval srcTranspose, int tick)
2014-08-24 10:52:28 +02:00
{
// set note track
// check if staffMove moves a note to a
// nonexistant staff
//
int track = c->track();
int nn = (track / VOICES) + c->staffMove();
if (nn < 0 || nn >= c->score()->nstaves())
c->setStaffMove(0);
Part* part = c->part();
Interval dstTranspose = part->instrument(tick)->transpose();
2014-08-24 10:52:28 +02:00
if (srcTranspose != dstTranspose) {
if (!dstTranspose.isZero()) {
dstTranspose.flip();
for (Note* n : c->notes()) {
int npitch;
int ntpc;
transposeInterval(n->pitch(), n->tpc1(), &npitch, &ntpc, dstTranspose, true);
n->setTpc2(ntpc);
}
}
else {
for (Note* n : c->notes())
n->setTpc2(n->tpc1());
}
}
}
2012-08-09 12:12:43 +02:00
//---------------------------------------------------------
// pasteStaff
2014-07-23 14:23:37 +02:00
// return false if paste fails
2012-08-09 12:12:43 +02:00
//---------------------------------------------------------
2016-03-02 13:20:19 +01:00
PasteState Score::pasteStaff(XmlReader& e, Segment* dst, int dstStaff)
2012-08-09 12:12:43 +02:00
{
Q_ASSERT(dst->segmentType() == Segment::Type::ChordRest);
2013-06-19 16:25:29 +02:00
QList<Chord*> graceNotes;
2012-08-09 12:12:43 +02:00
int dstTick = dst->tick();
bool done = false;
bool pasted = false;
int tickLen = 0, staves = 0;
2013-01-11 18:10:18 +01:00
while (e.readNextStartElement()) {
if (done)
break;
2013-01-11 18:10:18 +01:00
if (e.name() != "StaffList") {
e.unknown();
2013-01-17 12:56:14 +01:00
break;
2012-08-09 12:12:43 +02:00
}
QString version = e.attribute("version", "NONE");
2014-11-06 10:44:57 +01:00
if (!MScore::testMode) {
if (version != MSC_VERSION) {
qDebug("pasteStaff: bad version");
break;
}
}
2013-01-11 18:10:18 +01:00
int tickStart = e.intAttribute("tick", 0);
tickLen = e.intAttribute("len", 0);
2014-11-06 10:44:57 +01:00
int staffStart = e.intAttribute("staff", 0);
staves = e.intAttribute("staves", 0);
2014-07-31 08:37:58 +02:00
int voiceOffset[VOICES];
std::fill(voiceOffset,voiceOffset+VOICES,-1);
2014-07-21 14:16:16 +02:00
e.setTickOffset(dstTick - tickStart);
e.initTick(0);
2012-08-09 12:12:43 +02:00
2013-01-11 18:10:18 +01:00
while (e.readNextStartElement()) {
if (done)
break;
2013-01-11 18:10:18 +01:00
if (e.name() != "Staff") {
e.unknown();
2013-01-17 12:56:14 +01:00
break;
2012-08-09 12:12:43 +02:00
}
2014-05-05 15:38:40 +02:00
e.setTransposeChromatic(0);
e.setTransposeDiatonic(0);
2013-01-11 18:10:18 +01:00
int srcStaffIdx = e.attribute("id", "0").toInt();
2014-11-06 10:44:57 +01:00
e.setTrack(srcStaffIdx * VOICES);
e.setTrackOffset((dstStaff - staffStart) * VOICES);
int dstStaffIdx = e.track() / VOICES;
if (dstStaffIdx >= dst->score()->nstaves()) {
qDebug("paste beyond staves");
done = true;
2012-08-09 12:12:43 +02:00
break;
}
2013-01-21 20:21:41 +01:00
e.tuplets().clear();
bool makeGap = true;
2013-01-11 18:10:18 +01:00
while (e.readNextStartElement()) {
2012-08-09 12:12:43 +02:00
pasted = true;
2013-01-11 18:10:18 +01:00
const QStringRef& tag(e.name());
2014-06-18 12:25:56 +02:00
if (tag == "transposeChromatic")
e.setTransposeChromatic(e.readInt());
else if (tag == "transposeDiatonic")
e.setTransposeDiatonic(e.readInt());
2014-07-31 08:37:58 +02:00
else if (tag == "voice") {
int voiceId = e.attribute("id", "-1").toInt();
Q_ASSERT(voiceId >= 0 && voiceId < VOICES);
voiceOffset[voiceId] = e.readInt();
}
else if (tag == "move") {
int tick = e.readFraction().ticks();
e.initTick(tick);
int shift = tick - tickStart;
if (makeGap && !makeGap1(dstTick, dstStaffIdx, Fraction::fromTicks(tickLen), voiceOffset)) {
qDebug("cannot make gap in staff %d at tick %d", dstStaffIdx, dstTick + shift);
done = true; // break main loop, cannot make gap
break;
}
makeGap = false; // create gap only once per staff
}
2014-06-18 12:25:56 +02:00
else if (tag == "tick") {
int tick = e.readInt();
2014-07-21 14:16:16 +02:00
e.initTick(tick);
int shift = tick - tickStart;
if (makeGap && !makeGap1(dstTick, dstStaffIdx, Fraction::fromTicks(tickLen), voiceOffset)) {
qDebug("cannot make gap in staff %d at tick %d", dstStaffIdx, dstTick + shift);
done = true; // break main loop, cannot make gap
break;
}
makeGap = false; // create gap only once per staff
}
2012-08-09 12:12:43 +02:00
else if (tag == "Tuplet") {
Tuplet* tuplet = new Tuplet(this);
2013-01-21 20:21:41 +01:00
tuplet->setTrack(e.track());
2013-01-11 18:10:18 +01:00
tuplet->read(e);
2014-07-21 14:16:16 +02:00
int tick = e.tick();
// no paste into local time signature
if (staff(dstStaffIdx)->isLocalTimeSignature(tick)) {
qDebug("paste into local time signature");
2016-03-02 13:20:19 +01:00
return PasteState::DEST_LOCAL_TIME_SIGNATURE;
}
2012-08-09 12:12:43 +02:00
Measure* measure = tick2measure(tick);
tuplet->setParent(measure);
tuplet->setTick(tick);
int ticks = tuplet->actualTicks();
2014-07-23 14:23:37 +02:00
int rticks = measure->endTick() - tick;
if (rticks < ticks) {
qDebug("tuplet does not fit in measure");
delete tuplet;
2016-03-02 13:20:19 +01:00
return PasteState::TUPLET_CROSSES_BAR;
2014-07-23 14:23:37 +02:00
}
2013-01-21 20:21:41 +01:00
e.addTuplet(tuplet);
2012-08-09 12:12:43 +02:00
}
else if (tag == "Chord" || tag == "Rest" || tag == "RepeatMeasure") {
2013-01-18 10:55:52 +01:00
ChordRest* cr = static_cast<ChordRest*>(Element::name2Element(tag, this));
2013-01-21 20:21:41 +01:00
cr->setTrack(e.track());
2013-01-11 18:10:18 +01:00
cr->read(e);
2012-08-09 12:12:43 +02:00
cr->setSelected(false);
2014-07-21 14:16:16 +02:00
int tick = e.tick();
// no paste into local time signature
if (staff(dstStaffIdx)->isLocalTimeSignature(tick)) {
qDebug("paste into local time signature");
2016-03-02 13:20:19 +01:00
return PasteState::DEST_LOCAL_TIME_SIGNATURE;;
}
2013-06-19 16:25:29 +02:00
if (cr->isGrace())
graceNotes.push_back(static_cast<Chord*>(cr));
else {
2014-07-21 14:16:16 +02:00
e.incTick(cr->actualTicks());
if (cr->type() == Element::Type::CHORD) {
2013-06-19 16:25:29 +02:00
Chord* chord = static_cast<Chord*>(cr);
// disallow tie across barline within two-note tremolo
// tremolos can potentially still straddle the barline if no tie is required
// but these will be removed later
if (chord->tremolo() && chord->tremolo()->twoNotes()) {
Measure* m = tick2measure(tick);
int ticks = cr->actualTicks();
int rticks = m->endTick() - tick;
if (rticks < ticks || (rticks != ticks && rticks < ticks * 2)) {
qDebug("tremolo does not fit in measure");
2016-03-02 13:20:19 +01:00
return PasteState::DEST_TREMOLO;
}
}
2013-06-19 16:25:29 +02:00
for (int i = 0; i < graceNotes.size(); ++i) {
Chord* gc = graceNotes[i];
gc->setGraceIndex(i);
transposeChord(gc, e.transpose(), tick);
2013-06-19 16:25:29 +02:00
chord->add(gc);
}
graceNotes.clear();
}
// delete pending ties, they are not selected when copy
if ((tick - dstTick) + cr->actualTicks() >= tickLen) {
if (cr->type() == Element::Type::CHORD) {
Chord* c = static_cast<Chord*>(cr);
for (Note* note: c->notes()) {
Tie* tie = note->tieFor();
if (tie) {
note->setTieFor(0);
delete tie;
}
}
}
}
// shorten last cr to fit in the space made by makeGap
if ((tick - dstTick) + cr->actualTicks() > tickLen) {
2014-08-05 01:12:32 +02:00
int newLength = tickLen - (tick - dstTick);
// check previous CR on same track, if it has tremolo, delete the tremolo
// we don't want a tremolo and two different chord durations
if (cr->type() == Element::Type::CHORD) {
Segment* s = tick2leftSegment(tick - 1);
if (s) {
ChordRest* crt = static_cast<ChordRest*>(s->element(cr->track()));
2015-02-26 12:09:43 +01:00
if (!crt)
crt = s->nextChordRest(cr->track(), true);
if (crt && crt->type() == Element::Type::CHORD) {
Chord* chrt = static_cast<Chord*>(crt);
Tremolo* tr = chrt->tremolo();
if (tr) {
tr->setChords(chrt, static_cast<Chord*>(cr));
chrt->remove(tr);
delete tr;
}
}
}
}
if (!cr->tuplet()/*|| cr->actualTicks() - newLength > (cr->tuplet()->ratio().numerator() + 1 ) / 2*/) {
// shorten duration
// exempt notes in tuplets, since we don't allow copy of partial tuplet anyhow
// TODO: figure out a reasonable fudge factor to make sure shorten tuplets appropriately if we do ever copy a partial tuplet
cr->setDuration(Fraction::fromTicks(newLength));
cr->setDurationType(newLength);
}
2014-08-05 01:12:32 +02:00
}
2014-05-05 15:38:40 +02:00
pasteChordRest(cr, tick, e.transpose());
2013-06-19 16:25:29 +02:00
}
2012-08-09 12:12:43 +02:00
}
else if (tag == "HairPin"
|| tag == "Pedal"
|| tag == "Ottava"
|| tag == "Trill"
|| tag == "TextLine"
|| tag == "Volta") {
2013-01-18 10:55:52 +01:00
Spanner* sp = static_cast<Spanner*>(Element::name2Element(tag, this));
2014-05-26 20:48:27 +02:00
sp->setAnchor(Spanner::Anchor::SEGMENT);
2013-10-14 11:56:54 +02:00
sp->read(e);
2014-11-06 10:44:57 +01:00
sp->setTrack(e.track());
sp->setTrack2(e.track());
2014-07-21 14:16:16 +02:00
sp->setTick(e.tick());
2013-06-20 17:23:24 +02:00
addSpanner(sp);
2012-08-09 12:12:43 +02:00
}
else if (tag == "Slur") {
Spanner* sp = static_cast<Spanner*>(Element::name2Element(tag, this));
sp->read(e);
2014-11-06 10:44:57 +01:00
sp->setTrack(e.track());
2014-07-21 14:16:16 +02:00
sp->setTick(e.tick());
// check if we saw endSpanner / stop element already
int id = e.spannerId(sp);
const SpannerValues* sv = e.spannerValues(id);
if (sv) {
sp->setTick2(sv->tick2);
sp->setTrack2(sv->track2);
}
undoAddElement(sp);
}
2012-08-09 12:12:43 +02:00
else if (tag == "endSpanner") {
2013-01-11 18:10:18 +01:00
int id = e.intAttribute("id");
2014-07-21 13:24:21 +02:00
Spanner* spanner = e.findSpanner(id);
2013-01-17 12:56:14 +01:00
if (spanner) {
2013-06-20 17:23:24 +02:00
// e.spanner().removeOne(spanner);
2014-07-21 14:16:16 +02:00
spanner->setTick2(e.tick());
2013-06-20 18:48:28 +02:00
removeSpanner(spanner);
2013-01-17 12:56:14 +01:00
undoAddElement(spanner);
2014-07-15 12:49:51 +02:00
if (spanner->type() == Element::Type::OTTAVA)
spanner->staff()->updateOttava();
2012-08-09 12:12:43 +02:00
}
2013-01-17 12:56:14 +01:00
e.readNext();
2012-08-09 12:12:43 +02:00
}
else if (tag == "Harmony") {
Harmony* harmony = new Harmony(this);
2013-01-21 20:21:41 +01:00
harmony->setTrack(e.track());
2013-01-11 18:10:18 +01:00
harmony->read(e);
2014-11-06 10:44:57 +01:00
harmony->setTrack(e.track());
// transpose
2014-11-06 10:44:57 +01:00
Part* partDest = staff(e.track() / VOICES)->part();
Interval interval = partDest->instrument(e.tick())->transpose();
2014-05-26 15:31:36 +02:00
if (!styleB(StyleIdx::concertPitch) && !interval.isZero()) {
interval.flip();
int rootTpc = transposeTpc(harmony->rootTpc(), interval, true);
int baseTpc = transposeTpc(harmony->baseTpc(), interval, true);
2012-08-09 12:12:43 +02:00
undoTransposeHarmony(harmony, rootTpc, baseTpc);
}
2014-07-21 14:16:16 +02:00
int tick = e.tick();
2012-08-09 12:12:43 +02:00
Measure* m = tick2measure(tick);
Segment* seg = m->undoGetSegment(Segment::Type::ChordRest, tick);
2014-11-06 10:44:57 +01:00
if (seg->findAnnotationOrElement(Element::Type::HARMONY, e.track(), e.track())) {
QList<Element*> elements;
foreach (Element* el, seg->annotations()) {
if (el->type() == Element::Type::HARMONY
2014-11-06 10:44:57 +01:00
&& el->track() == e.track()) {
elements.append(el);
}
}
foreach (Element* el, elements)
undoRemoveElement(el);
}
2012-08-09 12:12:43 +02:00
harmony->setParent(seg);
undoAddElement(harmony);
}
else if (tag == "Dynamic"
|| tag == "Symbol"
|| tag == "FretDiagram"
2014-08-26 15:07:24 +02:00
|| tag == "TremoloBar"
2012-08-09 12:12:43 +02:00
|| tag == "Marker"
|| tag == "Jump"
|| tag == "Image"
|| tag == "Text"
|| tag == "StaffText"
|| tag == "TempoText"
|| tag == "FiguredBass"
2012-08-09 12:12:43 +02:00
) {
2013-01-18 10:55:52 +01:00
Element* el = Element::name2Element(tag, this);
el->setTrack(e.track()); // a valid track might be necessary for el->read() to work
el->read(e);
2012-08-09 12:12:43 +02:00
2014-07-21 14:16:16 +02:00
int tick = e.tick();
2012-08-09 12:12:43 +02:00
Measure* m = tick2measure(tick);
Segment* seg = m->undoGetSegment(Segment::Type::ChordRest, tick);
2013-01-11 18:10:18 +01:00
el->setParent(seg);
2012-08-09 12:12:43 +02:00
// be sure to paste the element in the destination track;
// setting track needs to be repeated, as it might have been overwritten by el->read()
// preserve *voice* from source, though
el->setTrack((e.track() / VOICES) * VOICES + el->voice());
undoAddElement(el);
2012-08-09 12:12:43 +02:00
}
else if (tag == "Clef") {
Clef* clef = new Clef(this);
2013-01-11 18:10:18 +01:00
clef->read(e);
2014-11-06 10:44:57 +01:00
clef->setTrack(e.track());
2014-07-21 14:16:16 +02:00
int tick = e.tick();
2012-08-09 12:12:43 +02:00
Measure* m = tick2measure(tick);
if (m->tick() && m->tick() == tick)
m = m->prevMeasure();
Segment* segment = m->undoGetSegment(Segment::Type::Clef, tick);
2012-08-09 12:12:43 +02:00
clef->setParent(segment);
undoChangeElement(segment->element(e.track()), clef);
2012-08-09 12:12:43 +02:00
}
else if (tag == "Breath") {
Breath* breath = new Breath(this);
2013-01-11 18:10:18 +01:00
breath->read(e);
2014-11-06 10:44:57 +01:00
breath->setTrack(e.track());
2014-07-21 14:16:16 +02:00
int tick = e.tick();
2012-08-09 12:12:43 +02:00
Measure* m = tick2measure(tick);
Segment* segment = m->undoGetSegment(Segment::Type::Breath, tick);
2012-08-09 12:12:43 +02:00
breath->setParent(segment);
undoChangeElement(segment->element(e.track()), breath);
2012-08-09 12:12:43 +02:00
}
else if (tag == "Beam") {
Beam* beam = new Beam(this);
beam->setTrack(e.track());
beam->read(e);
beam->setParent(0);
e.addBeam(beam);
}
2012-08-09 12:12:43 +02:00
else if (tag == "BarLine") {
2013-01-17 12:56:14 +01:00
e.skipCurrentElement(); // ignore bar line
2012-08-09 12:12:43 +02:00
}
2013-01-22 11:42:52 +01:00
else {
qDebug("PasteStaff: element %s not handled", tag.toUtf8().data());
e.skipCurrentElement(); // ignore
}
2012-08-09 12:12:43 +02:00
}
2013-01-21 20:21:41 +01:00
foreach (Tuplet* tuplet, e.tuplets()) {
2016-02-06 22:03:43 +01:00
if (tuplet->elements().empty()) {
2012-08-09 12:12:43 +02:00
// this should not happen and is a sign of input file corruption
qDebug("Measure:pasteStaff(): empty tuplet");
delete tuplet;
}
else {
Measure* measure = tick2measure(tuplet->tick());
tuplet->setParent(measure);
tuplet->sortElements();
}
}
}
}
2013-06-10 11:03:34 +02:00
foreach (Score* s, scoreList()) // for all parts
s->connectTies();
2013-07-24 10:20:15 +02:00
if (pasted) { //select only if we pasted something
2016-01-04 14:48:58 +01:00
//TODO? if (styleB(StyleIdx::createMultiMeasureRests))
// createMMRests();
2015-08-29 00:12:02 +02:00
Segment* s1 = tick2segmentMM(dstTick);
Segment* s2 = tick2segmentMM(dstTick + tickLen);
2014-11-06 10:44:57 +01:00
int endStaff = dstStaff + staves;
if (endStaff > nstaves())
endStaff = nstaves();
//check and add truly invisible rests insted of gaps
//TODO: look if this could be done different
Measure* dstM = tick2measureMM(dstTick);
Measure* endM = tick2measureMM(dstTick + tickLen);
for (int i = dstStaff; i < endStaff; i++) {
for (Measure* m = dstM; m && m != endM->nextMeasureMM(); m = m->nextMeasureMM())
m->checkMeasure(i);
}
2014-11-06 10:44:57 +01:00
_selection.setRange(s1, s2, dstStaff, endStaff);
_selection.updateSelectedElements();
//finding the first element that has a track
//the canvas position will be set to this element
Element* e = 0;
Segment* s = s1;
bool found = false;
2014-07-20 19:19:57 +02:00
if (s2)
s2 = s2->next1MM();
while (!found && s != s2) {
2014-11-06 10:44:57 +01:00
for (int i = dstStaff * VOICES; i < (endStaff + 1) * VOICES; i++) {
e = s->element(i);
if (e) {
found = true;
break;
}
}
s = s->next1MM();
}
foreach(MuseScoreView* v, viewer)
v->adjustCanvasPosition(e, false);
if (!selection().isRange())
_selection.setState(SelState::RANGE);
}
2016-03-02 13:20:19 +01:00
return PasteState::PS_NO_ERROR;
2012-08-09 12:12:43 +02:00
}
//---------------------------------------------------------
// pasteChordRest
//---------------------------------------------------------
2014-05-05 15:38:40 +02:00
void Score::pasteChordRest(ChordRest* cr, int tick, const Interval& srcTranspose)
2012-08-09 12:12:43 +02:00
{
// qDebug("pasteChordRest %s at %d, len %d/%d", cr->name(), tick, cr->duration().numerator(), cr->duration().denominator() );
2014-08-24 10:52:28 +02:00
if (cr->type() == Element::Type::CHORD)
transposeChord(static_cast<Chord*>(cr), srcTranspose, tick);
2012-08-09 12:12:43 +02:00
Measure* measure = tick2measure(tick);
2013-06-19 16:25:29 +02:00
if (!measure)
2012-08-09 12:12:43 +02:00
return;
// we can paste a measure rest as such only at start of measure
// and only if the lengths of the rest and measure match
// otherwise, we need to convert to duration rest(s)
// and potentially split the rest up (eg, 5/4 => whole + quarter)
bool convertMeasureRest;
if (cr->type() == Element::Type::REST
&& cr->durationType().type() == TDuration::DurationType::V_MEASURE
&& (tick != measure->tick() || cr->duration() != measure->len())) {
convertMeasureRest = true;
}
else
convertMeasureRest = false;
2013-06-19 16:25:29 +02:00
int measureEnd = measure->endTick();
bool isGrace = (cr->type() == Element::Type::CHORD) && (((Chord*)cr)->noteType() != NoteType::NORMAL);
2015-03-14 22:45:16 +01:00
// if note is too long to fit in measure, split it up with a tie across the barline
// exclude tuplets from consideration
// we have already disallowed a tuplet from crossing the barline, so there is no problem here
// but due to rounding, it might appear from actualTicks() that the last note is too long by a couple of ticks
if (!isGrace && !cr->tuplet() && (tick + cr->actualTicks() > measureEnd || convertMeasureRest)) {
if (cr->type() == Element::Type::CHORD) {
2012-08-09 12:12:43 +02:00
// split Chord
Chord* c = static_cast<Chord*>(cr);
int rest = c->actualTicks();
bool firstpart = true;
2012-08-09 12:12:43 +02:00
while (rest) {
measure = tick2measure(tick);
Chord* c2 = firstpart ? c : static_cast<Chord*>(c->clone());
if (!firstpart)
c2->removeMarkings(true);
int mlen = measure->tick() + measure->ticks() - tick;
int len = mlen > rest ? rest : mlen;
2016-02-06 22:03:43 +01:00
std::vector<TDuration> dl = toDurationList(Fraction::fromTicks(len), true);
TDuration d = dl[0];
2012-08-09 12:12:43 +02:00
c2->setDurationType(d);
2014-08-01 09:55:06 +02:00
c2->setDuration(d.fraction());
2014-08-01 11:32:44 +02:00
rest -= c2->actualTicks();
2012-08-09 12:12:43 +02:00
undoAddCR(c2, measure, tick);
2016-02-06 22:03:43 +01:00
std::vector<Note*> nl1 = c->notes();
std::vector<Note*> nl2 = c2->notes();
2012-08-09 12:12:43 +02:00
if (!firstpart)
2016-02-06 22:03:43 +01:00
for (unsigned i = 0; i < nl1.size(); ++i) {
Tie* tie = new Tie(this);
tie->setStartNote(nl1[i]);
tie->setEndNote(nl2[i]);
tie->setTrack(c->track());
Tie* tie2 = nl1[i]->tieFor();
if (tie2) {
nl2[i]->setTieFor(nl1[i]->tieFor());
tie2->setStartNote(nl2[i]);
}
nl1[i]->setTieFor(tie);
nl2[i]->setTieBack(tie);
2012-08-09 12:12:43 +02:00
}
c = c2;
firstpart = false;
tick += c->actualTicks();
2012-08-09 12:12:43 +02:00
}
}
else if (cr->type() == Element::Type::REST) {
2012-08-09 12:12:43 +02:00
// split Rest
Rest* r = static_cast<Rest*>(cr);
Fraction rest = r->duration();
bool firstpart = true;
2012-08-09 12:12:43 +02:00
while (!rest.isZero()) {
Rest* r2 = firstpart ? r : static_cast<Rest*>(r->clone());
2012-08-09 12:12:43 +02:00
measure = tick2measure(tick);
Fraction mlen = Fraction::fromTicks(measure->tick() + measure->ticks() - tick);
Fraction len = rest > mlen ? mlen : rest;
2016-02-06 22:03:43 +01:00
std::vector<TDuration> dl = toDurationList(len, false);
TDuration d = dl[0];
2014-08-01 11:32:44 +02:00
r2->setDuration(d.fraction());
r2->setDurationType(d);
2012-08-09 12:12:43 +02:00
undoAddCR(r2, measure, tick);
2014-08-01 11:32:44 +02:00
rest -= d.fraction();
2012-08-09 12:12:43 +02:00
tick += r2->actualTicks();
firstpart = false;
2012-08-09 12:12:43 +02:00
}
}
else if (cr->type() == Element::Type::REPEAT_MEASURE) {
RepeatMeasure* rm = static_cast<RepeatMeasure*>(cr);
2016-02-06 22:03:43 +01:00
std::vector<TDuration> list = toDurationList(rm->actualDuration(), true);
for (auto dur : list) {
Rest* r = new Rest(this, dur);
r->setTrack(cr->track());
Fraction rest = r->duration();
while (!rest.isZero()) {
Rest* r2 = static_cast<Rest*>(r->clone());
measure = tick2measure(tick);
Fraction mlen = Fraction::fromTicks(measure->tick() + measure->ticks() - tick);
Fraction len = rest > mlen ? mlen : rest;
2016-02-06 22:03:43 +01:00
std::vector<TDuration> dl = toDurationList(len, false);
TDuration d = dl[0];
r2->setDuration(d.fraction());
r2->setDurationType(d);
undoAddCR(r2, measure, tick);
rest -= d.fraction();
tick += r2->actualTicks();
}
delete r;
}
delete cr;
}
2012-08-09 12:12:43 +02:00
}
else {
undoAddCR(cr, measure, tick);
}
}
2013-01-23 14:14:09 +01:00
2013-05-13 18:49:17 +02:00
//---------------------------------------------------------
// pasteSymbols
//
// pastes a list of symbols into cr and following ChordRest's
//
// (Note: info about delta ticks is currently ignored)
//---------------------------------------------------------
void Score::pasteSymbols(XmlReader& e, ChordRest* dst)
{
Segment* currSegm = dst->segment();
int destTick = 0; // the tick and track to place the pasted element at
int destTrack = 0;
bool done = false;
2014-02-15 15:21:39 +01:00
int segDelta = 0;
Segment* startSegm= currSegm;
int startTick = dst->tick(); // the initial tick and track where to start pasting
int startTrack = dst->track();
int maxTrack = ntracks();
int lastTick = lastSegment()->tick();
while (e.readNextStartElement()) {
if (done)
break;
if (e.name() != "SymbolList") {
e.unknown();
break;
}
QString version = e.attribute("version", "NONE");
2014-08-15 17:20:20 +02:00
if (version != MSC_VERSION)
break;
while (e.readNextStartElement()) {
if (done)
break;
const QStringRef& tag(e.name());
if (tag == "trackOffset") {
destTrack = startTrack + e.readInt();
currSegm = startSegm;
}
else if (tag == "tickOffset")
destTick = startTick + e.readInt();
else if (tag == "segDelta")
segDelta = e.readInt();
else {
if (tag == "Harmony") {
//
// Harmony elements (= chord symbols) are positioned respecting
// the original tickOffset: advance to destTick (or near)
//
Segment* harmSegm;
for (harmSegm = startSegm; harmSegm && (harmSegm->tick() < destTick);
harmSegm = harmSegm->nextCR())
;
// if destTick overshot, no dest. segment: create one
if (destTick >= lastTick) {
harmSegm = nullptr;
}
else if (!harmSegm || harmSegm->tick() > destTick) {
Measure* meas = tick2measure(destTick);
harmSegm = meas ? meas->undoGetSegment(Segment::Type::ChordRest, destTick) : nullptr;
}
if (destTrack >= maxTrack || harmSegm == nullptr) {
qDebug("PasteSymbols: no track or segment for %s", tag.toUtf8().data());
e.skipCurrentElement(); // ignore
continue;
}
Harmony* el = new Harmony(this);
el->setTrack(trackZeroVoice(destTrack));
el->read(e);
el->setTrack(trackZeroVoice(destTrack));
// transpose
Part* partDest = staff(track2staff(destTrack))->part();
Interval interval = partDest->instrument(destTick)->transpose();
2014-05-26 15:31:36 +02:00
if (!styleB(StyleIdx::concertPitch) && !interval.isZero()) {
interval.flip();
int rootTpc = transposeTpc(el->rootTpc(), interval, true);
int baseTpc = transposeTpc(el->baseTpc(), interval, true);
undoTransposeHarmony(el, rootTpc, baseTpc);
}
el->setParent(harmSegm);
undoAddElement(el);
}
else {
//
// All other elements are positioned respecting the distance in chords
//
for ( ; currSegm && segDelta > 0; segDelta--)
currSegm = currSegm->nextCR(destTrack);
// check the intended dest. track and segment exist
if (destTrack >= maxTrack || currSegm == nullptr) {
qDebug("PasteSymbols: no track or segment for %s", tag.toUtf8().data());
e.skipCurrentElement(); // ignore
continue;
}
// check there is a segment element in the required track
if (currSegm->element(destTrack) == nullptr) {
qDebug("PasteSymbols: no track element for %s", tag.toUtf8().data());
e.skipCurrentElement();
continue;
}
ChordRest* cr = static_cast<ChordRest*>(currSegm->element(destTrack));
if (tag == "Articulation") {
Articulation* el = new Articulation(this);
el->read(e);
el->setTrack(destTrack);
el->setParent(cr);
if (!el->isFermata()
&& cr->type() == Element::Type::REST) {
delete el;
}
else
undoAddElement(el);
}
else if (tag == "FiguredBass") {
// FiguredBass always belongs to first staff voice
destTrack = trackZeroVoice(destTrack);
int ticks;
FiguredBass* el = new FiguredBass(this);
el->setTrack(destTrack);
el->read(e);
el->setTrack(destTrack);
// if f.b. is off-note, we have to locate a place before currSegm
// where an on-note f.b. element could (potentially) be
// (while having an off-note f.b. without an on-note one before it
// is un-idiomatic, possible mismatch in rhythmic patterns between
// copy source and paste destination does not allow to be too picky)
if (!el->onNote()) {
FiguredBass* onNoteFB = nullptr;
Segment* prevSegm = currSegm;
bool done = false;
while (prevSegm) {
if (done)
break;
prevSegm = prevSegm->prev1(Segment::Type::ChordRest);
// if there is a ChordRest in the dest. track
// this segment is a (potential) f.b. location
if (prevSegm->element(destTrack) != nullptr) {
done = true;
}
// in any case, look for a f.b. in annotations:
// if there is a f.b. element in the right track,
// this is an (actual) f.b. location
foreach (Element* a, prevSegm->annotations()) {
if (a->type() == Element::Type::FIGURED_BASS && a->track() == destTrack) {
onNoteFB = static_cast<FiguredBass*>(a);
done = true;
}
}
}
if (!prevSegm) {
qDebug("PasteSymbols: can't place off-note FiguredBass");
delete el;
continue;
}
// by default, split on-note duration in half: half on-note and half off-note
int totTicks = currSegm->tick() - prevSegm->tick();
int destTick = prevSegm->tick() + totTicks / 2;
ticks = totTicks / 2;
if (onNoteFB)
onNoteFB->setTicks(totTicks / 2);
// look for a segment at this tick; if none, create one
Segment * nextSegm = prevSegm;
while (nextSegm && nextSegm->tick() < destTick)
nextSegm = nextSegm->next1(Segment::Type::ChordRest);
if (!nextSegm || nextSegm->tick() > destTick) { // no ChordRest segm at this tick
nextSegm = new Segment(prevSegm->measure(), Segment::Type::ChordRest, destTick);
if (!nextSegm) {
qDebug("PasteSymbols: can't find or create destination segment for FiguredBass");
delete el;
continue;
}
undoAddElement(nextSegm);
}
currSegm = nextSegm;
}
else
// by default, assign to FiguredBass element the duration of the chord it refers to
ticks = static_cast<ChordRest*>(currSegm->element(destTrack))->duration().ticks();
// in both cases, look for an existing f.b. element in segment and remove it, if found
FiguredBass* oldFB = nullptr;
foreach (Element* a, currSegm->annotations()) {
if (a->type() == Element::Type::FIGURED_BASS && a->track() == destTrack) {
oldFB = static_cast<FiguredBass*>(a);
break;
}
}
if (oldFB)
undoRemoveElement(oldFB);
el->setParent(currSegm);
el->setTicks(ticks);
undoAddElement(el);
}
else if (tag == "Lyrics") {
// with lyrics, skip rests
while (cr->type() != Element::Type::CHORD && currSegm) {
currSegm = currSegm->nextCR(destTrack);
if (currSegm)
cr = static_cast<ChordRest*>(currSegm->element(destTrack));
else
break;
}
if (currSegm == nullptr) {
qDebug("PasteSymbols: no segment for Lyrics");
e.skipCurrentElement();
continue;
}
if (cr->type() != Element::Type::CHORD) {
qDebug("PasteSymbols: can't paste Lyrics to rest");
e.skipCurrentElement();
continue;
}
Lyrics* el = new Lyrics(this);
el->setTrack(destTrack);
el->read(e);
el->setTrack(destTrack);
el->setParent(cr);
undoAddElement(el);
}
else {
qDebug("PasteSymbols: element %s not handled", tag.toUtf8().data());
e.skipCurrentElement(); // ignore
}
} // if !Harmony
} // if element
} // outer while readNextstartElement()
} // inner while readNextstartElement()
} // pasteSymbolList()
2014-06-18 12:25:56 +02:00
//---------------------------------------------------------
// cmdPaste
//---------------------------------------------------------
2016-03-02 13:20:19 +01:00
PasteState Score::cmdPaste(const QMimeData* ms, MuseScoreView* view)
{
if (ms == 0) {
qDebug("no application mime data");
2016-03-02 13:20:19 +01:00
return PasteState::NO_MIME;
}
if ((_selection.isSingle() || _selection.isList()) && ms->hasFormat(mimeSymbolFormat)) {
QByteArray data(ms->data(mimeSymbolFormat));
XmlReader e(data);
QPointF dragOffset;
Fraction duration(1, 4);
Element::Type type = Element::readType(e, &dragOffset, &duration);
QList<Element*> els;
2014-08-15 17:20:20 +02:00
if (_selection.isSingle())
els.append(_selection.element());
else
els.append(_selection.elements());
if (type != Element::Type::INVALID) {
Element* el = Element::create(type, this);
if (el) {
el->read(e);
if (el) {
for (Element* target : els) {
Element* nel = el->clone();
addRefresh(target->abbox()); // layout() ?!
DropData ddata;
ddata.view = view;
ddata.element = nel;
ddata.duration = duration;
if (target->acceptDrop(ddata)) {
target->drop(ddata);
if (_selection.element())
addRefresh(_selection.element()->abbox());
}
}
}
delete el;
}
}
else
qDebug("cannot read type");
}
else if ((_selection.isRange() || _selection.isList()) && ms->hasFormat(mimeStaffListFormat)) {
ChordRest* cr = 0;
if (_selection.isRange())
cr = _selection.firstChordRest();
else if (_selection.isSingle()) {
Element* e = _selection.element();
2014-08-14 12:36:54 +02:00
if (e->type() != Element::Type::NOTE && !e->isChordRest()) {
qDebug("cannot paste to %s", e->name());
2016-03-02 13:20:19 +01:00
return PasteState::DEST_NO_CR;
}
if (e->type() == Element::Type::NOTE)
e = static_cast<Note*>(e)->chord();
cr = static_cast<ChordRest*>(e);
}
if (cr == 0)
2016-03-02 13:20:19 +01:00
return PasteState::NO_DEST;
else if (cr->tuplet())
2016-03-02 13:20:19 +01:00
return PasteState::DEST_TUPLET;
else {
QByteArray data(ms->data(mimeStaffListFormat));
2014-08-15 17:20:20 +02:00
if (MScore::debugMode)
2014-08-14 15:23:08 +02:00
qDebug("paste <%s>", data.data());
XmlReader e(data);
2014-07-30 12:34:56 +02:00
e.setPasteMode(true);
2016-03-02 13:20:19 +01:00
PasteState ps = pasteStaff(e, cr->segment(), cr->staffIdx());
if (ps != PasteState::PS_NO_ERROR) {
2014-11-06 10:44:57 +01:00
qDebug("paste failed");
2016-03-02 13:20:19 +01:00
return PasteState::TUPLET_CROSSES_BAR;
2014-07-23 14:23:37 +02:00
}
}
}
else if (ms->hasFormat(mimeSymbolListFormat)) {
ChordRest* cr = 0;
if (_selection.isRange())
cr = _selection.firstChordRest();
else if (_selection.isSingle()) {
Element* e = _selection.element();
if (e->type() != Element::Type::NOTE && e->type() != Element::Type::REST && e->type() != Element::Type::CHORD) {
qDebug("cannot paste to %s", e->name());
2016-03-02 13:20:19 +01:00
return PasteState::DEST_NO_CR;
}
if (e->type() == Element::Type::NOTE)
e = static_cast<Note*>(e)->chord();
cr = static_cast<ChordRest*>(e);
}
if (cr == 0)
2016-03-02 13:20:19 +01:00
return PasteState::NO_DEST;
else if (cr->tuplet())
2016-03-02 13:20:19 +01:00
return PasteState::DEST_TUPLET;
else {
QByteArray data(ms->data(mimeSymbolListFormat));
2014-08-14 15:23:08 +02:00
if (MScore::debugMode)
qDebug("paste <%s>", data.data());
XmlReader e(data);
pasteSymbols(e, cr);
}
}
2014-11-21 12:52:07 +01:00
else if (ms->hasImage()) {
QImage im = qvariant_cast<QImage>(ms->imageData());
QByteArray ba;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
im.save(&buffer, "PNG");
Image* image = new Image(this);
image->setImageType(ImageType::RASTER);
image->loadFromData("dragdrop", ba);
QList<Element*> els;
if (_selection.isSingle())
els.append(_selection.element());
else
els.append(_selection.elements());
for (Element* target : els) {
Element* nel = image->clone();
addRefresh(target->abbox()); // layout() ?!
DropData ddata;
ddata.view = view;
ddata.element = nel;
// ddata.duration = duration;
target->drop(ddata);
if (_selection.element())
addRefresh(_selection.element()->abbox());
}
delete image;
}
else {
2015-10-27 11:30:09 +01:00
qDebug("cannot paste selState %d staffList %s",
int(_selection.state()), (ms->hasFormat(mimeStaffListFormat))? "true" : "false");
foreach(const QString& s, ms->formats())
qDebug(" format %s", qPrintable(s));
}
2016-03-02 13:20:19 +01:00
return PasteState::PS_NO_ERROR;
}
}