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"
|
2013-10-21 15:55:56 +02:00
|
|
|
|
2014-06-18 12:25:56 +02:00
|
|
|
#include "rest.h"
|
|
|
|
#include "staff.h"
|
|
|
|
#include "measure.h"
|
|
|
|
#include "harmony.h"
|
2013-10-21 15:55:56 +02:00
|
|
|
#include "breath.h"
|
2014-06-18 12:25:56 +02:00
|
|
|
#include "beam.h"
|
2013-10-21 15:55:56 +02:00
|
|
|
#include "figuredbass.h"
|
|
|
|
#include "ottava.h"
|
|
|
|
#include "part.h"
|
2014-06-18 12:25:56 +02:00
|
|
|
#include "lyrics.h"
|
|
|
|
#include "hairpin.h"
|
2013-10-21 15:55:56 +02:00
|
|
|
#include "tie.h"
|
|
|
|
#include "tuplet.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "xml.h"
|
2014-11-21 12:52:07 +01:00
|
|
|
#include "image.h"
|
2014-11-27 11:32:35 +01:00
|
|
|
#include "repeat.h"
|
Lyrics multi-system melisma and dashes
Implements melisma and dash lines for lyrics spanning several systems.
The melisma and dash line is based on the `SLine` class and its segments on the `LineSegment` class. Both the whole line and its segments are not selectable, marked as generated and not saved in the score file, which is not changed in any way.
For very wide dash segments, several dashes are drawn; the distance between the dashes is not configurable.
Lyrics layout code in `Measure` class and in `layout.cpp` file has been commented out as the lyrics line layout is all contained in the lyrics.cpp file
The line is registered with the `Score` (to have its layout delayed until all elements are positioned) with a mechanism similar to other `Spanner`'s, but in a different container (`_unmanagedSpanner`), as the owning `Lyrics` should decide when create, register, unregister and delete its line.
The line segments are registered with the `System` they belong to (to have them drawn), in the same way as other `Spanner`'s.
There is code for using the dash metrics of the lyrics font, but it is turned off via a conditional directive, as there does not seem to be a reliable way to determine the dash metrics; conventional values (determined by trials and errors and based on my taste!) are used when the conditional directive is off.
2015-01-11 10:11:44 +01:00
|
|
|
#include "chord.h"
|
2015-02-25 21:50:45 +01:00
|
|
|
#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)
|
|
|
|
{
|
|
|
|
// 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);
|
2015-03-13 11:16:43 +01:00
|
|
|
Part* part = c->part();
|
|
|
|
Interval dstTranspose = part->instrument()->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
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-11-06 10:44:57 +01:00
|
|
|
bool Score::pasteStaff(XmlReader& e, Segment* dst, int dstStaff)
|
2012-08-09 12:12:43 +02:00
|
|
|
{
|
2014-06-25 11:46:10 +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();
|
2013-06-20 19:01:44 +02:00
|
|
|
bool done = false;
|
2014-06-11 09:35:47 +02:00
|
|
|
bool pasted = false;
|
2014-12-18 20:41:15 +01:00
|
|
|
int tickLen, staves = 0;
|
2013-01-11 18:10:18 +01:00
|
|
|
while (e.readNextStartElement()) {
|
2013-06-20 19:01:44 +02:00
|
|
|
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
|
|
|
}
|
2013-08-05 15:27:52 +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);
|
2014-06-11 09:35:47 +02:00
|
|
|
tickLen = e.intAttribute("len", 0);
|
2014-11-06 10:44:57 +01:00
|
|
|
int staffStart = e.intAttribute("staff", 0);
|
2014-06-11 09:35:47 +02:00
|
|
|
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()) {
|
2013-06-20 20:13:46 +02:00
|
|
|
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-06-20 19:01:44 +02:00
|
|
|
}
|
2013-07-20 16:14:39 +02:00
|
|
|
|
2013-01-21 20:21:41 +01:00
|
|
|
e.tuplets().clear();
|
2013-07-20 16:14:39 +02:00
|
|
|
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());
|
2013-01-07 12:36:06 +01:00
|
|
|
|
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();
|
|
|
|
}
|
2014-06-18 12:25:56 +02:00
|
|
|
else if (tag == "tick") {
|
2013-06-20 20:13:46 +02:00
|
|
|
int tick = e.readInt();
|
2014-07-21 14:16:16 +02:00
|
|
|
e.initTick(tick);
|
2013-07-20 16:14:39 +02:00
|
|
|
int shift = tick - tickStart;
|
2015-04-14 00:56:20 +02:00
|
|
|
if (makeGap && !makeGap1(dstTick, dstStaffIdx, Fraction::fromTicks(tickLen), voiceOffset)) {
|
2013-07-20 16:14:39 +02:00
|
|
|
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
|
2013-06-20 20:13:46 +02:00
|
|
|
}
|
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();
|
2012-08-09 12:12:43 +02:00
|
|
|
Measure* measure = tick2measure(tick);
|
|
|
|
tuplet->setParent(measure);
|
|
|
|
tuplet->setTick(tick);
|
2015-03-14 18:46:04 +01:00
|
|
|
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;
|
|
|
|
return false;
|
|
|
|
}
|
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();
|
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());
|
2014-06-24 18:36:02 +02:00
|
|
|
if (cr->type() == Element::Type::CHORD) {
|
2013-06-19 16:25:29 +02:00
|
|
|
Chord* chord = static_cast<Chord*>(cr);
|
|
|
|
for (int i = 0; i < graceNotes.size(); ++i) {
|
|
|
|
Chord* gc = graceNotes[i];
|
|
|
|
gc->setGraceIndex(i);
|
2014-08-24 10:52:28 +02:00
|
|
|
transposeChord(gc, e.transpose());
|
2013-06-19 16:25:29 +02:00
|
|
|
chord->add(gc);
|
|
|
|
}
|
|
|
|
graceNotes.clear();
|
|
|
|
}
|
2015-02-26 19:53:59 +01:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-08-05 01:12:32 +02:00
|
|
|
//shorten last cr to fit in the space made by makeGap
|
2014-08-24 17:50:21 +02:00
|
|
|
if ((tick - dstTick) + cr->actualTicks() > tickLen) {
|
2014-08-05 01:12:32 +02:00
|
|
|
int newLength = tickLen - (tick - dstTick);
|
2015-02-25 21:50:45 +01:00
|
|
|
// check previous CR on same track, if it has tremolo, delete the tremolo
|
2015-02-26 19:53:59 +01:00
|
|
|
// we don't want a tremolo and two different chord durations
|
2015-02-25 21:50:45 +01:00
|
|
|
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) {
|
2015-02-25 21:50:45 +01:00
|
|
|
Chord* chrt = static_cast<Chord*>(crt);
|
|
|
|
Tremolo* tr = chrt->tremolo();
|
|
|
|
if (tr) {
|
|
|
|
tr->setChords(chrt, static_cast<Chord*>(cr));
|
|
|
|
chrt->remove(tr);
|
2015-02-26 19:53:59 +01:00
|
|
|
delete tr;
|
2015-02-25 21:50:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-03-13 03:38:39 +01:00
|
|
|
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
|
|
|
}
|
2014-07-05 12:05:27 +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());
|
2014-07-05 12:05:27 +02:00
|
|
|
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());
|
2013-06-18 19:11:27 +02:00
|
|
|
// transpose
|
2014-11-06 10:44:57 +01:00
|
|
|
Part* partDest = staff(e.track() / VOICES)->part();
|
2015-03-13 11:16:43 +01:00
|
|
|
Interval interval = partDest->instrument()->transpose();
|
2014-05-26 15:31:36 +02:00
|
|
|
if (!styleB(StyleIdx::concertPitch) && !interval.isZero()) {
|
2013-06-18 19:11:27 +02:00
|
|
|
interval.flip();
|
2014-12-20 00:27:06 +01:00
|
|
|
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);
|
2014-06-25 11:46:10 +02:00
|
|
|
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())) {
|
2014-08-14 12:22:57 +02:00
|
|
|
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()) {
|
2014-08-14 12:22:57 +02:00
|
|
|
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"
|
2013-02-28 23:29:04 +01:00
|
|
|
|| tag == "FiguredBass"
|
2012-08-09 12:12:43 +02:00
|
|
|
) {
|
2013-01-18 10:55:52 +01:00
|
|
|
Element* el = Element::name2Element(tag, this);
|
2015-02-14 12:00:42 +01:00
|
|
|
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);
|
2014-06-25 11:46:10 +02:00
|
|
|
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
|
|
|
|
2013-02-28 23:29:04 +01: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()
|
2015-04-29 18:52:58 +02:00
|
|
|
// preserve *voice* from source, though
|
|
|
|
el->setTrack((e.track() / VOICES) * VOICES + el->voice());
|
2015-02-14 12:00:42 +01:00
|
|
|
|
2015-04-29 18:52:58 +02:00
|
|
|
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();
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment* segment = m->undoGetSegment(Segment::Type::Clef, tick);
|
2012-08-09 12:12:43 +02:00
|
|
|
clef->setParent(segment);
|
2015-02-14 12:00:42 +01:00
|
|
|
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);
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment* segment = m->undoGetSegment(Segment::Type::Breath, tick);
|
2012-08-09 12:12:43 +02:00
|
|
|
breath->setParent(segment);
|
2015-02-14 12:00:42 +01:00
|
|
|
undoChangeElement(segment->element(e.track()), breath);
|
2012-08-09 12:12:43 +02:00
|
|
|
}
|
2013-01-22 11:38:09 +01: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-07 12:36:06 +01:00
|
|
|
|
2013-01-21 20:21:41 +01:00
|
|
|
foreach (Tuplet* tuplet, e.tuplets()) {
|
2012-08-09 12:12:43 +02:00
|
|
|
if (tuplet->elements().isEmpty()) {
|
|
|
|
// 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
|
|
|
|
2014-06-11 09:35:47 +02:00
|
|
|
if (pasted) { //select only if we pasted something
|
|
|
|
Segment* s1 = tick2segment(dstTick);
|
|
|
|
Segment* s2 = tick2segment(dstTick + tickLen);
|
2014-11-06 10:44:57 +01:00
|
|
|
int endStaff = dstStaff + staves;
|
2014-06-11 09:35:47 +02:00
|
|
|
if (endStaff > nstaves())
|
|
|
|
endStaff = nstaves();
|
2014-11-06 10:44:57 +01:00
|
|
|
_selection.setRange(s1, s2, dstStaff, endStaff);
|
2014-06-11 09:35:47 +02:00
|
|
|
_selection.updateSelectedElements();
|
2014-07-03 23:21:28 +02:00
|
|
|
|
|
|
|
//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++) {
|
2014-07-03 23:21:28 +02:00
|
|
|
e = s->element(i);
|
|
|
|
if (e) {
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s = s->next1MM();
|
|
|
|
}
|
|
|
|
|
2014-06-11 09:35:47 +02:00
|
|
|
foreach(MuseScoreView* v, viewer)
|
2014-07-03 23:21:28 +02:00
|
|
|
v->adjustCanvasPosition(e, false);
|
2014-06-11 09:35:47 +02:00
|
|
|
if (!selection().isRange())
|
|
|
|
_selection.setState(SelState::RANGE);
|
|
|
|
}
|
2014-07-23 14:23:37 +02:00
|
|
|
return true;
|
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
|
|
|
{
|
2015-01-26 12:06:27 +01: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);
|
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;
|
|
|
|
|
2015-02-18 04:11:59 +01:00
|
|
|
// 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();
|
2014-06-24 18:36:02 +02:00
|
|
|
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
|
2015-03-13 21:07:45 +01:00
|
|
|
if (!isGrace && !cr->tuplet() && (tick + cr->actualTicks() > measureEnd || convertMeasureRest)) {
|
2014-06-24 18:36:02 +02:00
|
|
|
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();
|
2014-10-08 13:42:34 +02:00
|
|
|
bool firstpart = true;
|
2012-08-09 12:12:43 +02:00
|
|
|
while (rest) {
|
|
|
|
measure = tick2measure(tick);
|
2014-12-18 13:18:38 +01:00
|
|
|
Chord* c2 = firstpart ? c : static_cast<Chord*>(c->clone());
|
2014-10-08 13:42:34 +02:00
|
|
|
int mlen = measure->tick() + measure->ticks() - tick;
|
|
|
|
int len = mlen > rest ? rest : mlen;
|
|
|
|
QList<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);
|
|
|
|
|
|
|
|
QList<Note*> nl1 = c->notes();
|
|
|
|
QList<Note*> nl2 = c2->notes();
|
|
|
|
|
2014-10-08 13:42:34 +02:00
|
|
|
if (!firstpart)
|
|
|
|
for (int 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;
|
2014-10-08 13:42:34 +02:00
|
|
|
firstpart = false;
|
|
|
|
tick += c->actualTicks();
|
2012-08-09 12:12:43 +02:00
|
|
|
}
|
|
|
|
}
|
2014-11-27 11:32:35 +01: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();
|
|
|
|
|
2014-12-18 13:18:38 +01:00
|
|
|
bool firstpart = true;
|
2012-08-09 12:12:43 +02:00
|
|
|
while (!rest.isZero()) {
|
2014-12-18 13:18:38 +01:00
|
|
|
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;
|
2014-10-08 13:42:34 +02:00
|
|
|
QList<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();
|
2014-12-18 13:18:38 +01:00
|
|
|
firstpart = false;
|
2012-08-09 12:12:43 +02:00
|
|
|
}
|
|
|
|
}
|
2014-11-27 11:32:35 +01:00
|
|
|
else if (cr->type() == Element::Type::REPEAT_MEASURE) {
|
|
|
|
RepeatMeasure* rm = static_cast<RepeatMeasure*>(cr);
|
|
|
|
QList<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;
|
|
|
|
QList<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
|
|
|
|
2013-10-21 15:55:56 +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();
|
2013-11-11 11:14:37 +01:00
|
|
|
int destTick = 0; // the tick and track to place the pasted element at
|
2013-10-21 15:55:56 +02:00
|
|
|
int destTrack = 0;
|
|
|
|
bool done = false;
|
2014-02-15 15:21:39 +01:00
|
|
|
int segDelta = 0;
|
2013-10-21 15:55:56 +02:00
|
|
|
Segment* startSegm= currSegm;
|
2013-11-11 11:14:37 +01:00
|
|
|
int startTick = dst->tick(); // the initial tick and track where to start pasting
|
2013-10-21 15:55:56 +02:00
|
|
|
int startTrack = dst->track();
|
|
|
|
int maxTrack = ntracks();
|
|
|
|
|
|
|
|
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)
|
2013-10-21 15:55:56 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
while (e.readNextStartElement()) {
|
|
|
|
if (done)
|
|
|
|
break;
|
|
|
|
const QStringRef& tag(e.name());
|
|
|
|
|
|
|
|
if (tag == "trackOffset") {
|
|
|
|
destTrack = startTrack + e.readInt();
|
|
|
|
currSegm = startSegm;
|
|
|
|
}
|
2013-11-11 11:14:37 +01:00
|
|
|
else if (tag == "tickOffset")
|
|
|
|
destTick = startTick + e.readInt();
|
|
|
|
else if (tag == "segDelta")
|
|
|
|
segDelta = e.readInt();
|
2013-10-21 15:55:56 +02:00
|
|
|
else {
|
2013-11-11 11:14:37 +01:00
|
|
|
|
|
|
|
if (tag == "Harmony") {
|
|
|
|
//
|
|
|
|
// Harmony elements (= chord symbols) are positioned respecting
|
|
|
|
// the original tickOffset: advance to destTick (or near)
|
|
|
|
//
|
|
|
|
Segment* harmSegm;
|
2013-12-26 23:29:50 +01:00
|
|
|
for (harmSegm = startSegm; harmSegm && (harmSegm->tick() < destTick);
|
|
|
|
harmSegm = harmSegm->nextCR())
|
2013-11-11 11:14:37 +01:00
|
|
|
;
|
|
|
|
// if destTick overshot, no dest. segment: create one
|
2013-12-26 23:29:50 +01:00
|
|
|
if (!harmSegm || harmSegm->tick() > destTick) {
|
2013-11-11 11:14:37 +01:00
|
|
|
Measure* meas = tick2measure(destTick);
|
2014-06-25 11:46:10 +02:00
|
|
|
harmSegm = meas->getSegment(Segment::Type::ChordRest, destTick);
|
2013-10-21 15:55:56 +02:00
|
|
|
}
|
2015-04-21 21:36:50 +02:00
|
|
|
if (destTrack >= maxTrack) {
|
|
|
|
qDebug("PasteSymbols: no track for %s", tag.toUtf8().data());
|
|
|
|
e.skipCurrentElement(); // ignore
|
|
|
|
continue;
|
|
|
|
}
|
2013-10-21 15:55:56 +02:00
|
|
|
Harmony* el = new Harmony(this);
|
|
|
|
el->setTrack(trackZeroVoice(destTrack));
|
|
|
|
el->read(e);
|
|
|
|
el->setTrack(trackZeroVoice(destTrack));
|
|
|
|
// transpose
|
|
|
|
Part* partDest = staff(track2staff(destTrack))->part();
|
2015-03-13 11:16:43 +01:00
|
|
|
Interval interval = partDest->instrument()->transpose();
|
2014-05-26 15:31:36 +02:00
|
|
|
if (!styleB(StyleIdx::concertPitch) && !interval.isZero()) {
|
2013-10-21 15:55:56 +02:00
|
|
|
interval.flip();
|
2014-12-20 00:27:06 +01:00
|
|
|
int rootTpc = transposeTpc(el->rootTpc(), interval, true);
|
|
|
|
int baseTpc = transposeTpc(el->baseTpc(), interval, true);
|
2013-10-21 15:55:56 +02:00
|
|
|
undoTransposeHarmony(el, rootTpc, baseTpc);
|
|
|
|
}
|
2013-11-11 11:14:37 +01:00
|
|
|
el->setParent(harmSegm);
|
2013-10-21 15:55:56 +02:00
|
|
|
undoAddElement(el);
|
|
|
|
}
|
2013-11-11 11:14:37 +01:00
|
|
|
else {
|
|
|
|
//
|
|
|
|
// All other elements are positioned respecting the distance in chords
|
|
|
|
//
|
|
|
|
for ( ; currSegm && segDelta > 0; segDelta--)
|
2013-10-21 15:55:56 +02:00
|
|
|
currSegm = currSegm->nextCR(destTrack);
|
2013-11-11 11:14:37 +01:00
|
|
|
// 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
|
2013-10-21 15:55:56 +02:00
|
|
|
continue;
|
|
|
|
}
|
2013-11-11 11:14:37 +01:00
|
|
|
// 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());
|
2013-10-21 15:55:56 +02:00
|
|
|
e.skipCurrentElement();
|
|
|
|
continue;
|
|
|
|
}
|
2013-11-11 11:14:37 +01:00
|
|
|
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);
|
2014-07-25 14:12:12 +02:00
|
|
|
if (!el->isFermata()
|
|
|
|
&& cr->type() == Element::Type::REST) {
|
|
|
|
delete el;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
undoAddElement(el);
|
2013-11-11 11:14:37 +01:00
|
|
|
}
|
|
|
|
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;
|
2014-06-25 11:46:10 +02:00
|
|
|
prevSegm = prevSegm->prev1(Segment::Type::ChordRest);
|
2013-11-11 11:14:37 +01:00
|
|
|
// 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()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (a->type() == Element::Type::FIGURED_BASS && a->track() == destTrack) {
|
2013-11-11 11:14:37 +01:00
|
|
|
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)
|
2014-06-25 11:46:10 +02:00
|
|
|
nextSegm = nextSegm->next1(Segment::Type::ChordRest);
|
2013-11-11 11:14:37 +01:00
|
|
|
if (!nextSegm || nextSegm->tick() > destTick) { // no ChordRest segm at this tick
|
2014-06-25 11:46:10 +02:00
|
|
|
nextSegm = new Segment(prevSegm->measure(), Segment::Type::ChordRest, destTick);
|
2013-11-11 11:14:37 +01:00
|
|
|
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
|
2015-02-16 09:44:21 +01:00
|
|
|
ticks = static_cast<ChordRest*>(currSegm->element(destTrack))->duration().ticks();
|
2013-11-11 11:14:37 +01:00
|
|
|
// 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()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (a->type() == Element::Type::FIGURED_BASS && a->track() == destTrack) {
|
2013-11-11 11:14:37 +01:00
|
|
|
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
|
2014-06-24 18:36:02 +02:00
|
|
|
while (cr->type() != Element::Type::CHORD && currSegm) {
|
2013-11-11 11:14:37 +01:00
|
|
|
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;
|
|
|
|
}
|
2014-06-24 18:36:02 +02:00
|
|
|
if (cr->type() != Element::Type::CHORD) {
|
2013-11-11 11:14:37 +01:00
|
|
|
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()
|
2013-10-21 15:55:56 +02:00
|
|
|
|
2014-06-18 12:25:56 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdPaste
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-13 19:34:54 +02:00
|
|
|
PasteStatus Score::cmdPaste(const QMimeData* ms, MuseScoreView* view)
|
|
|
|
{
|
|
|
|
if (ms == 0) {
|
|
|
|
qDebug("no application mime data");
|
|
|
|
return PasteStatus::NO_MIME;
|
|
|
|
}
|
|
|
|
if ((_selection.isSingle()|| _selection.isList()) && ms->hasFormat(mimeSymbolFormat)) {
|
|
|
|
QByteArray data(ms->data(mimeSymbolFormat));
|
|
|
|
XmlReader e(data);
|
|
|
|
QPointF dragOffset;
|
|
|
|
Fraction duration(1, 4);
|
2014-06-24 18:36:02 +02:00
|
|
|
Element::Type type = Element::readType(e, &dragOffset, &duration);
|
2014-06-13 19:34:54 +02:00
|
|
|
|
|
|
|
QList<Element*> els;
|
2014-08-15 17:20:20 +02:00
|
|
|
if (_selection.isSingle())
|
2014-06-13 19:34:54 +02:00
|
|
|
els.append(_selection.element());
|
|
|
|
else
|
|
|
|
els.append(_selection.elements());
|
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
if (type != Element::Type::INVALID) {
|
2014-06-13 19:34:54 +02:00
|
|
|
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;
|
|
|
|
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()) {
|
2014-06-13 19:34:54 +02:00
|
|
|
qDebug("cannot paste to %s", e->name());
|
|
|
|
return PasteStatus::DEST_NO_CR;
|
|
|
|
}
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->type() == Element::Type::NOTE)
|
2014-06-13 19:34:54 +02:00
|
|
|
e = static_cast<Note*>(e)->chord();
|
|
|
|
cr = static_cast<ChordRest*>(e);
|
|
|
|
}
|
|
|
|
if (cr == 0)
|
|
|
|
return PasteStatus::NO_DEST;
|
|
|
|
else if (cr->tuplet())
|
|
|
|
return PasteStatus::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());
|
2014-06-13 19:34:54 +02:00
|
|
|
XmlReader e(data);
|
2014-07-30 12:34:56 +02:00
|
|
|
e.setPasteMode(true);
|
2014-07-23 14:23:37 +02:00
|
|
|
if (!pasteStaff(e, cr->segment(),cr->staffIdx())) {
|
2014-11-06 10:44:57 +01:00
|
|
|
qDebug("paste failed");
|
2014-07-23 14:23:37 +02:00
|
|
|
return PasteStatus::TUPLET_CROSSES_BAR;
|
|
|
|
}
|
2014-06-13 19:34:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (ms->hasFormat(mimeSymbolListFormat)) {
|
|
|
|
ChordRest* cr = 0;
|
|
|
|
if (_selection.isRange())
|
|
|
|
cr = _selection.firstChordRest();
|
|
|
|
else if (_selection.isSingle()) {
|
|
|
|
Element* e = _selection.element();
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->type() != Element::Type::NOTE && e->type() != Element::Type::REST && e->type() != Element::Type::CHORD) {
|
2014-06-13 19:34:54 +02:00
|
|
|
qDebug("cannot paste to %s", e->name());
|
|
|
|
return PasteStatus::DEST_NO_CR;
|
|
|
|
}
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->type() == Element::Type::NOTE)
|
2014-06-13 19:34:54 +02:00
|
|
|
e = static_cast<Note*>(e)->chord();
|
|
|
|
cr = static_cast<ChordRest*>(e);
|
|
|
|
}
|
|
|
|
if (cr == 0)
|
|
|
|
return PasteStatus::NO_DEST;
|
|
|
|
else if (cr->tuplet())
|
|
|
|
return PasteStatus::DEST_TUPLET;
|
|
|
|
else {
|
|
|
|
QByteArray data(ms->data(mimeSymbolListFormat));
|
2014-08-14 15:23:08 +02:00
|
|
|
if (MScore::debugMode)
|
|
|
|
qDebug("paste <%s>", data.data());
|
2014-06-13 19:34:54 +02:00
|
|
|
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;
|
|
|
|
}
|
2014-06-13 19:34:54 +02:00
|
|
|
else {
|
2015-01-28 14:44:55 +01:00
|
|
|
qDebug("cannot paste selState %hhd staffList %s",
|
|
|
|
_selection.state(), (ms->hasFormat(mimeStaffListFormat))? "true" : "false");
|
2014-06-13 19:34:54 +02:00
|
|
|
foreach(const QString& s, ms->formats())
|
|
|
|
qDebug(" format %s", qPrintable(s));
|
|
|
|
}
|
2014-06-18 08:49:29 +02:00
|
|
|
return PasteStatus::PS_NO_ERROR;
|
2014-06-13 19:34:54 +02:00
|
|
|
}
|
2013-10-21 15:55:56 +02:00
|
|
|
}
|