MuseScore/libmscore/edit.cpp
anatoly-os 5d7f50060e Merge pull request #6058 from dmitrio95/305017-plugin-cursor-addnote-regression
fix #305017: Cursor.addNote fails to add notes to existing chords in INPUT_STATE_INDEPENDENT mode
2020-05-12 03:41:04 +02:00

5328 lines
228 KiB
C++

//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2002-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 "note.h"
#include "rest.h"
#include "chord.h"
#include "key.h"
#include "sig.h"
#include "clef.h"
#include "score.h"
#include "slur.h"
#include "tie.h"
#include "hairpin.h"
#include "segment.h"
#include "staff.h"
#include "part.h"
#include "timesig.h"
#include "page.h"
#include "barline.h"
#include "tuplet.h"
#include "lyrics.h"
#include "image.h"
#include "keysig.h"
#include "beam.h"
#include "utils.h"
#include "harmony.h"
#include "system.h"
#include "navigate.h"
#include "articulation.h"
#include "drumset.h"
#include "measure.h"
#include "undo.h"
#include "tupletmap.h"
#include "tiemap.h"
#include "stem.h"
#include "iname.h"
#include "range.h"
#include "hook.h"
#include "repeat.h"
#include "textframe.h"
#include "accidental.h"
#include "ottava.h"
#include "instrchange.h"
#include "bracket.h"
#include "excerpt.h"
#include "breath.h"
#include "glissando.h"
#include "fermata.h"
namespace Ms {
//---------------------------------------------------------
// getSelectedNote
//---------------------------------------------------------
Note* Score::getSelectedNote()
{
Element* el = selection().element();
if (el && el->isNote())
return toNote(el);
MScore::setError(NO_NOTE_SELECTED);
return 0;
}
//---------------------------------------------------------
// getSelectedChordRest
//---------------------------------------------------------
ChordRest* Score::getSelectedChordRest() const
{
Element* el = selection().element();
if (el) {
if (el->isNote())
return toNote(el)->chord();
else if (el->isRest() || el->isRepeatMeasure())
return toRest(el);
else if (el->isChord())
return toChord(el);
}
MScore::setError(NO_NOTE_REST_SELECTED);
return 0;
}
//---------------------------------------------------------
// getSelectedChordRest2
//---------------------------------------------------------
void Score::getSelectedChordRest2(ChordRest** cr1, ChordRest** cr2) const
{
*cr1 = 0;
*cr2 = 0;
for (Element* e : selection().elements()) {
if (e->isNote())
e = e->parent();
if (e->isChordRest()) {
ChordRest* cr = toChordRest(e);
if (*cr1 == 0 || (*cr1)->tick() > cr->tick())
*cr1 = cr;
if (*cr2 == 0 || (*cr2)->tick() < cr->tick())
*cr2 = cr;
}
}
if (*cr1 == 0)
MScore::setError(NO_NOTE_REST_SELECTED);
if (*cr1 == *cr2)
*cr2 = 0;
}
//---------------------------------------------------------
// getSelectedChordRests
//---------------------------------------------------------
QSet<ChordRest*> Score::getSelectedChordRests() const
{
QSet<ChordRest*> set;
for (Element* e : selection().elements()) {
if (e->isNote())
e = e->parent();
if (e->isChordRest()) {
set.insert(toChordRest(e));
}
}
return set;
}
//---------------------------------------------------------
// pos
//---------------------------------------------------------
Fraction Score::pos()
{
Element* el = selection().element();
if (selection().activeCR())
el = selection().activeCR();
if (el) {
switch (el->type()) {
case ElementType::NOTE:
el = el->parent();
// fall through
case ElementType::REPEAT_MEASURE:
case ElementType::REST:
case ElementType::CHORD:
return toChordRest(el)->tick();
default:
break;
}
}
return Fraction(-1, 1);
}
//---------------------------------------------------------
// addRest
// create one Rest at tick with duration d
// create segment if necessary
//---------------------------------------------------------
Rest* Score::addRest(const Fraction& tick, int track, TDuration d, Tuplet* tuplet)
{
Measure* measure = tick2measure(tick);
Rest* rest = new Rest(this, d);
if (d.type() == TDuration::DurationType::V_MEASURE)
rest->setTicks(measure->stretchedLen(staff(track/VOICES)));
else
rest->setTicks(d.fraction());
rest->setTrack(track);
rest->setTuplet(tuplet);
undoAddCR(rest, measure, tick);
return rest;
}
//---------------------------------------------------------
// addRest
//---------------------------------------------------------
Rest* Score::addRest(Segment* s, int track, TDuration d, Tuplet* tuplet)
{
Rest* rest = new Rest(this, d);
if (d.type() == TDuration::DurationType::V_MEASURE)
rest->setTicks(s->measure()->stretchedLen(staff(track/VOICES)));
else
rest->setTicks(d.fraction());
rest->setTrack(track);
rest->setParent(s);
rest->setTuplet(tuplet);
undoAddCR(rest, tick2measure(s->tick()), s->tick());
return rest;
}
//---------------------------------------------------------
// addChord
// Create one Chord at tick with duration d
// - create segment if necessary.
// - Use chord "oc" as prototype;
// - if "genTie" then tie to chord "oc"
//---------------------------------------------------------
Chord* Score::addChord(const Fraction& tick, TDuration d, Chord* oc, bool genTie, Tuplet* tuplet)
{
Measure* measure = tick2measure(tick);
if (measure->endTick() <= tick) {
qDebug("Score::addChord(): end of score?");
return 0;
}
Chord* chord = new Chord(this);
chord->setTuplet(tuplet);
chord->setTrack(oc->track());
chord->setDurationType(d);
chord->setTicks(d.fraction());
for (Note* n : oc->notes()) {
Note* nn = new Note(this);
nn->setPitch(n->pitch());
nn->setTpc1(n->tpc1());
nn->setTpc2(n->tpc2());
chord->add(nn);
}
undoAddCR(chord, measure, tick);
//
// now as both chords are in place
// (have segments as parent) we can add ties:
//
if (genTie) {
size_t n = oc->notes().size();
for(size_t i = 0; i < n; ++i) {
Note* n1 = oc->notes()[i];
Note* n2 = chord->notes()[i];
Tie* tie = new Tie(this);
tie->setStartNote(n1);
tie->setEndNote(n2);
tie->setTrack(n1->track());
undoAddElement(tie);
}
}
return chord;
}
//---------------------------------------------------------
// addClone
//---------------------------------------------------------
ChordRest* Score::addClone(ChordRest* cr, const Fraction& tick, const TDuration& d)
{
ChordRest* newcr;
// change a RepeatMeasure() into an Rest()
if (cr->isRepeatMeasure())
newcr = new Rest(*toRest(cr));
else
newcr = toChordRest(cr->clone());
newcr->rxpos() = 0.0;
newcr->setDurationType(d);
newcr->setTicks(d.fraction());
newcr->setTuplet(cr->tuplet());
newcr->setSelected(false);
undoAddCR(newcr, cr->measure(), tick);
return newcr;
}
//---------------------------------------------------------
// setRest
// create one or more rests to fill "l"
//---------------------------------------------------------
Rest* Score::setRest(const Fraction& _tick, int track, const Fraction& _l, bool useDots, Tuplet* tuplet, bool useFullMeasureRest)
{
Fraction l = _l;
Fraction tick = _tick;
Measure* measure = tick2measure(tick);
Rest* r = 0;
Staff* staff = Score::staff(track / VOICES);
while (!l.isZero()) {
//
// divide into measures
//
Fraction f;
if (tuplet) {
f = tuplet->baseLen().fraction() * tuplet->ratio().numerator();
for (DurationElement* de : tuplet->elements()) {
if (de->tick() >= tick)
break;
f -= de->ticks();
}
//
// restrict to tuplet len
//
if (f < l)
l = f;
}
else {
if (measure->tick() < tick)
f = measure->tick() + measure->ticks() - tick;
else
f = measure->ticks();
f *= staff->timeStretch(tick);
}
if (f > l)
f = l;
if ((track % VOICES) && !measure->hasVoice(track) && (tick == measure->tick())) {
l -= f;
measure = measure->nextMeasure();
if (!measure)
break;
tick = measure->tick();
continue;
}
if ((measure->timesig() == measure->ticks()) // not in pickup measure
&& (measure->tick() == tick)
&& (measure->stretchedLen(staff) == f)
&& !tuplet
&& (useFullMeasureRest)) {
Rest* rest = addRest(tick, track, TDuration(TDuration::DurationType::V_MEASURE), tuplet);
tick += rest->actualTicks();
if (r == 0)
r = rest;
}
else {
//
// compute list of durations which will fit l
//
std::vector<TDuration> dList = toDurationList(f, useDots);
if (dList.empty())
return 0;
Rest* rest = 0;
if (((tick - measure->tick()).ticks() % dList[0].ticks().ticks()) == 0) {
for (const TDuration& d : dList) {
rest = addRest(tick, track, d, tuplet);
if (r == 0)
r = rest;
tick += rest->actualTicks();
}
}
else {
for (size_t i = dList.size(); i > 0; --i) { // loop needs to be in this reverse order
rest = addRest(tick, track, dList[i-1], tuplet);
if (r == 0)
r = rest;
tick += rest->actualTicks();
}
}
}
l -= f;
measure = measure->nextMeasure();
if (!measure)
break;
tick = measure->tick();
}
return r;
}
//---------------------------------------------------------
// addNote from NoteVal
//---------------------------------------------------------
Note* Score::addNote(Chord* chord, const NoteVal& noteVal, bool forceAccidental, InputState* externalInputState)
{
InputState& is = externalInputState ? (*externalInputState) : _is;
Note* note = new Note(this);
note->setParent(chord);
note->setTrack(chord->track());
note->setNval(noteVal);
undoAddElement(note);
if (forceAccidental) {
int tpc = styleB(Sid::concertPitch) ? noteVal.tpc1 : noteVal.tpc2;
AccidentalVal alter = tpc2alter(tpc);
AccidentalType at = Accidental::value2subtype(alter);
Accidental* a = new Accidental(this);
a->setAccidentalType(at);
a->setRole(AccidentalRole::USER);
a->setParent(note);
undoAddElement(a);
}
setPlayNote(true);
setPlayChord(true);
if (externalInputState) {
is.setTrack(note->track());
is.setLastSegment(is.segment());
is.setSegment(note->chord()->segment());
}
else {
select(note, SelectType::SINGLE, 0);
}
if (!chord->staff()->isTabStaff(chord->tick())) {
NoteEntryMethod entryMethod = is.noteEntryMethod();
if (entryMethod != NoteEntryMethod::REALTIME_AUTO && entryMethod != NoteEntryMethod::REALTIME_MANUAL)
is.moveToNextInputPos();
}
return note;
}
//---------------------------------------------------------
// rewriteMeasures
// rewrite all measures from fm to lm (including)
// If staffIdx is valid (>= 0), then rewrite a local
// timesig change.
//---------------------------------------------------------
bool Score::rewriteMeasures(Measure* fm, Measure* lm, const Fraction& ns, int staffIdx)
{
if (staffIdx >= 0) {
// local timesig
// don't actually rewrite, just update measure rest durations
// abort if there is anything other than measure rests in range
int strack = staffIdx * VOICES;
int etrack = strack + VOICES;
for (Measure* m = fm; ; m = m->nextMeasure()) {
for (Segment* s = m->first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
for (int track = strack; track < etrack; ++track) {
ChordRest* cr = toChordRest(s->element(track));
if (!cr)
continue;
if (cr->isRest() && cr->durationType() == TDuration::DurationType::V_MEASURE)
cr->undoChangeProperty(Pid::DURATION, QVariant::fromValue(ns));
else
return false;
}
}
if (m == lm)
break;
}
return true;
}
int measures = 1;
bool fmr = true;
// Format: chord 1 tick, chord 2 tick, tremolo, track
std::vector<std::tuple<Fraction, Fraction, Tremolo*, int>> tremoloChordTicks;
int strack, etrack;
if (staffIdx < 0) {
strack = 0;
etrack = ntracks();
}
else {
strack = staffIdx * VOICES;
etrack = strack + VOICES;
}
for (Measure* m = fm; m; m = m->nextMeasure()) {
if (!m->isFullMeasureRest())
fmr = false;
for (Segment* s = m->first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
for (int track = strack; track < etrack; ++track) {
ChordRest* cr = toChordRest(s->element(track));
if (cr && cr->isChord()) {
Chord* chord = toChord(cr);
if (chord->tremolo() && chord->tremolo()->twoNotes()) {
Tremolo* trem = chord->tremolo();
// Don't add same chord twice
if (trem->chord2() == chord)
continue;
auto newP = std::tuple<Fraction, Fraction, Tremolo*, int>(cr->tick(), trem->chord2()->segment()->tick(), trem, track);
tremoloChordTicks.push_back(newP);
}
}
}
}
if (m == lm)
break;
++measures;
}
if (!fmr) {
// check for local time signatures
for (Measure* m = fm; m; m = m -> nextMeasure()) {
for (int si = 0; si < nstaves(); ++si) {
if (staff(si)->timeStretch(m->tick()) != Fraction(1,1)) {
// we cannot change a staff with a local time signature
return false;
}
if (m == lm)
break;
}
}
}
ScoreRange range;
range.read(fm->first(), lm->last());
//
// calculate number of required measures = nm
//
Fraction k = range.ticks() / ns;
int nm = (k.numerator() + k.denominator() - 1)/ k.denominator();
Fraction nd = ns * Fraction(nm,1);
// evtl. we have to fill the last measure
Fraction fill = nd - range.ticks();
range.fill(fill);
for (Score* s : scoreList()) {
Measure* m1 = s->tick2measure(fm->tick());
Measure* m2 = s->tick2measure(lm->tick());
Fraction tick1 = m1->tick();
Fraction tick2 = m2->endTick();
auto spanners = s->spannerMap().findOverlapping(tick1.ticks(), tick2.ticks());
for (auto i : spanners) {
if (i.value->tick() >= tick1)
undo(new RemoveElement(i.value));
}
s->undoRemoveMeasures(m1, m2);
Measure* nfm = 0;
Measure* nlm = 0;
Fraction tick = { 0, 1};
for (int i = 0; i < nm; ++i) {
Measure* m = new Measure(s);
m->setPrev(nlm);
if (nlm)
nlm->setNext(m);
m->setTimesig(ns);
m->setTicks(ns);
m->setTick(tick);
tick += m->ticks();
nlm = m;
if (nfm == 0)
nfm = m;
}
// nlm->setEndBarLineType(m2->endBarLineType(), m2->endBarLineGenerated(),
// m2->endBarLineVisible(), m2->endBarLineColor());
//
// insert new calculated measures
//
nfm->setPrev(m1->prev());
nlm->setNext(m2->next());
s->undo(new InsertMeasures(nfm, nlm));
}
if (!fill.isZero())
undoInsertTime(lm->endTick(), fill);
if (!range.write(masterScore(), fm->tick()))
return false;
connectTies(true);
// Attempt to move tremolos to correct chords
for (auto tremPair : tremoloChordTicks) {
Fraction chord1Tick = std::get<0>(tremPair);
Fraction chord2Tick = std::get<1>(tremPair);
Tremolo* trem = std::get<2>(tremPair);
int track = std::get<3>(tremPair);
undo(new MoveTremolo(trem->score(), chord1Tick, chord2Tick, trem, track));
}
if (noteEntryMode()) {
// set input cursor to possibly re-written segment
Fraction icTick = inputPos();
Segment* icSegment = tick2segment(icTick, false, SegmentType::ChordRest);
if (!icSegment) {
// this can happen if cursor was on a rest
// and in the rewriting it got subsumed into a full measure rest
Measure* icMeasure = tick2measure(icTick);
if (!icMeasure) // shouldn't happen, but just in case
icMeasure = firstMeasure();
icSegment = icMeasure->first(SegmentType::ChordRest);
}
inputState().setSegment(icSegment);
}
return true;
}
//---------------------------------------------------------
// rewriteMeasures
// rewrite all measures up to the next time signature or section break
//---------------------------------------------------------
bool Score::rewriteMeasures(Measure* fm, const Fraction& ns, int staffIdx)
{
Measure* lm = fm;
Measure* fm1 = fm;
Measure* nm = nullptr;
LayoutBreak* sectionBreak = nullptr;
// disable local time sig modifications in linked staves
if (staffIdx != -1 && excerpts().size() > 0) {
MScore::setError(CANNOT_CHANGE_LOCAL_TIMESIG);
return false;
}
//
// split into Measure segments fm-lm
//
for (MeasureBase* measure = fm; ; measure = measure->next()) {
if (!measure || !measure->isMeasure() || lm->sectionBreak()
|| (toMeasure(measure)->first(SegmentType::TimeSig) && measure != fm))
{
// save section break to reinstate after rewrite
if (lm->sectionBreak())
sectionBreak = new LayoutBreak(*lm->sectionBreakElement());
if (!rewriteMeasures(fm1, lm, ns, staffIdx)) {
if (staffIdx >= 0) {
MScore::setError(CANNOT_CHANGE_LOCAL_TIMESIG);
// restore measure rests that were prematurely modified
Fraction fr(staff(staffIdx)->timeSig(fm->tick())->sig());
for (Measure* m = fm1; m; m = m->nextMeasure()) {
ChordRest* cr = m->findChordRest(m->tick(), staffIdx * VOICES);
if (cr && cr->isRest() && cr->durationType() == TDuration::DurationType::V_MEASURE)
cr->undoChangeProperty(Pid::DURATION, QVariant::fromValue(fr));
else
break;
}
}
else {
// this can be hit for local time signatures as well
// (if we are rewriting all staves, but one has a local time signature)
// TODO: detect error conditions better, have clearer error messages
// and perform necessary fixups
MScore::setError(TUPLET_CROSSES_BAR);
}
for (Measure* m = fm1; m; m = m->nextMeasure()) {
if (m->first(SegmentType::TimeSig))
break;
Fraction fr(ns);
m->undoChangeProperty(Pid::TIMESIG_NOMINAL, QVariant::fromValue(fr));
}
return false;
}
// after rewrite, lm is not necessarily valid
// m is first MeasureBase after rewritten range
// m->prevMeasure () is new last measure of range
// set nm to first true Measure after rewritten range
// we may use this to reinstate time signatures
if (measure && measure->prevMeasure())
nm = measure->prevMeasure()->nextMeasure();
else
nm = nullptr;
if (sectionBreak) {
// reinstate section break, then stop rewriting
if (measure && measure->prevMeasure()) {
sectionBreak->setParent(measure->prevMeasure());
undoAddElement(sectionBreak);
}
else if (!measure) {
sectionBreak->setParent(lastMeasure());
undoAddElement(sectionBreak);
}
else {
qDebug("unable to restore section break");
nm = nullptr;
sectionBreak = nullptr;
}
break;
}
// stop rewriting at end of score
// or at a measure (which means we found a time signature segment)
if (!measure || measure->isMeasure())
break;
// skip frames
while (!measure->isMeasure()) {
if (measure->sectionBreak()) {
// frame has a section break; we can stop skipping ahead
sectionBreak = measure->sectionBreakElement();
break;
}
measure = measure->next();
if (!measure)
break;
}
// stop rewriting if we encountered a section break on a frame
// or if there is a time signature on first measure after the frame
if (sectionBreak || (measure && toMeasure(measure)->first(SegmentType::TimeSig)))
break;
// set up for next range to rewrite
fm1 = toMeasure(measure);
if (fm1 == 0)
break;
}
// if we didn't break the loop already,
// we must have an ordinary measure
// add measure to range to rewrite
lm = toMeasure(measure);
}
// if any staves don't have time signatures at the point where we stopped,
// we need to reinstate their previous time signatures
if (!nm)
return true;
Segment* s = nm->undoGetSegment(SegmentType::TimeSig, nm->tick());
for (int i = 0; i < nstaves(); ++i) {
if (!s->element(i * VOICES)) {
TimeSig* ots = staff(i)->timeSig(nm->tick());
if (ots) {
TimeSig* nts = new TimeSig(*ots);
nts->setParent(s);
if (sectionBreak) {
nts->setGenerated(false);
nts->setShowCourtesySig(false);
}
undoAddElement(nts);
}
}
}
return true;
}
//---------------------------------------------------------
// cmdAddTimeSig
//
// Add or change time signature at measure in response
// to gui command (drop timesig on measure or timesig)
//---------------------------------------------------------
void Score::cmdAddTimeSig(Measure* fm, int staffIdx, TimeSig* ts, bool local)
{
deselectAll();
if (fm->isMMRest())
fm = fm->mmRestFirst();
Fraction ns = ts->sig();
Fraction tick = fm->tick();
TimeSig* lts = staff(staffIdx)->timeSig(tick);
if (local) {
Fraction stretch = (ns / fm->timesig()).reduced();
ts->setStretch(stretch);
}
Fraction stretch;
Fraction lsig; // last signature
if (lts) {
stretch = lts->stretch();
lsig = lts->sig();
}
else {
stretch.set(1,1);
lsig.set(4,4); // set to default
}
int track = staffIdx * VOICES;
Segment* seg = fm->undoGetSegment(SegmentType::TimeSig, tick);
TimeSig* ots = toTimeSig(seg->element(track));
if (ots && (*ots == *ts)) {
//
// ignore if there is already a timesig
// with same values
//
delete ts;
return;
}
if (ots && ots->sig() == ns && ots->stretch() == ts->stretch()) {
//
// the measure duration does not change,
// so its ok to just update the time signatures
//
TimeSig* nts = staff(staffIdx)->nextTimeSig(tick + Fraction::fromTicks(1));
const Fraction lmTick = nts ? nts->segment()->tick() : Fraction(-1,1);
for (Score* score : scoreList()) {
Measure* mf = score->tick2measure(tick);
Measure* lm = (lmTick != Fraction(-1,1)) ? score->tick2measure(lmTick) : nullptr;
for (Measure* m = mf; m != lm; m = m->nextMeasure()) {
bool changeActual = m->ticks() == m->timesig();
m->undoChangeProperty(Pid::TIMESIG_NOMINAL, QVariant::fromValue(ns));
if (changeActual)
m->undoChangeProperty(Pid::TIMESIG_ACTUAL, QVariant::fromValue(ns));
}
}
int n = nstaves();
for (int si = 0; si < n; ++si) {
TimeSig* nsig = toTimeSig(seg->element(si * VOICES));
nsig->undoChangeProperty(Pid::SHOW_COURTESY, ts->showCourtesySig());
nsig->undoChangeProperty(Pid::TIMESIG, QVariant::fromValue(ts->sig()));
nsig->undoChangeProperty(Pid::TIMESIG_TYPE, int(ts->timeSigType()));
nsig->undoChangeProperty(Pid::NUMERATOR_STRING, ts->numeratorString());
nsig->undoChangeProperty(Pid::DENOMINATOR_STRING, ts->denominatorString());
nsig->undoChangeProperty(Pid::TIMESIG_STRETCH, QVariant::fromValue(ts->stretch()));
nsig->undoChangeProperty(Pid::GROUPS, QVariant::fromValue(ts->groups()));
nsig->setSelected(false);
nsig->setDropTarget(0);
}
}
else {
Score* mScore = masterScore();
Measure* mf = mScore->tick2measure(tick);
//
// rewrite all measures up to the next time signature
//
if (mf == mScore->firstMeasure() && mf->nextMeasure() && (mf->ticks() != mf->timesig())) {
// handle upbeat
mf->undoChangeProperty(Pid::TIMESIG_NOMINAL, QVariant::fromValue(ns));
Measure* m = mf->nextMeasure();
Segment* s = m->findSegment(SegmentType::TimeSig, m->tick());
mf = s ? 0 : mf->nextMeasure();
}
else {
if (sigmap()->timesig(seg->tick().ticks()).nominal().identical(ns)) {
// no change to global time signature,
// but we need to rewrite any staves with local time signatures
for (int i = 0; i < nstaves(); ++i) {
if (staff(i)->timeSig(tick) && staff(i)->timeSig(tick)->isLocal()) {
if (!mScore->rewriteMeasures(mf, ns, i)) {
undoStack()->current()->unwind();
return;
}
}
}
mf = 0;
}
}
// try to rewrite the measures first
// we will only add time signatures if this succeeds
// this means, however, that the rewrite cannot depend on the time signatures being in place
if (mf) {
if (!mScore->rewriteMeasures(mf, ns, local ? staffIdx : -1)) {
undoStack()->current()->unwind();
return;
}
}
// add the time signatures
std::map<int, TimeSig*> masterTimeSigs;
for (Score* score : scoreList()) {
Measure* nfm = score->tick2measure(tick);
seg = nfm->undoGetSegment(SegmentType::TimeSig, nfm->tick());
int startStaffIdx, endStaffIdx;
if (local) {
if (score == this) {
startStaffIdx = staffIdx;
endStaffIdx = startStaffIdx + 1;
}
else {
// TODO: get index for this score
qDebug("cmdAddTimeSig: unable to write local time signature change to linked score");
startStaffIdx = 0;
endStaffIdx = 0;
}
}
else {
startStaffIdx = 0;
endStaffIdx = score->nstaves();
}
for (int si = startStaffIdx; si < endStaffIdx; ++si) {
TimeSig* nsig = toTimeSig(seg->element(si * VOICES));
if (nsig == 0) {
nsig = new TimeSig(*ts);
nsig->setScore(score);
nsig->setTrack(si * VOICES);
nsig->setParent(seg);
undoAddElement(nsig);
if (score->excerpt()) {
const int masterTrack = score->excerpt()->tracks().key(nsig->track());
TimeSig* masterTimeSig = masterTimeSigs[masterTrack];
if (masterTimeSig)
undo(new Link(masterTimeSig, nsig));
}
}
else {
nsig->undoChangeProperty(Pid::SHOW_COURTESY, ts->showCourtesySig());
nsig->undoChangeProperty(Pid::TIMESIG_TYPE, int(ts->timeSigType()));
nsig->undoChangeProperty(Pid::TIMESIG, QVariant::fromValue(ts->sig()));
nsig->undoChangeProperty(Pid::NUMERATOR_STRING, ts->numeratorString());
nsig->undoChangeProperty(Pid::DENOMINATOR_STRING, ts->denominatorString());
// HACK do it twice to accommodate undo
nsig->undoChangeProperty(Pid::TIMESIG_TYPE, int(ts->timeSigType()));
nsig->undoChangeProperty(Pid::TIMESIG_STRETCH, QVariant::fromValue(ts->stretch()));
nsig->undoChangeProperty(Pid::GROUPS, QVariant::fromValue(ts->groups()));
nsig->setSelected(false);
nsig->setDropTarget(0); // DEBUG
}
if (score->isMaster())
masterTimeSigs[nsig->track()] = nsig;
}
}
}
delete ts;
}
//---------------------------------------------------------
// cmdRemoveTimeSig
//---------------------------------------------------------
void Score::cmdRemoveTimeSig(TimeSig* ts)
{
if (ts->isLocal() && excerpts().size() > 0) {
MScore::setError(CANNOT_CHANGE_LOCAL_TIMESIG);
return;
}
Measure* m = ts->measure();
Segment* s = ts->segment();
//
// we cannot remove a courtesy time signature
//
if (m->tick() != s->tick())
return;
Fraction tick = m->tick();
// if we remove all time sigs from segment, segment will be already removed by now
// but this would leave us no means of detecting that we have have measures in a local timesig
// in cases where we try deleting the local time sig
// known bug: this means we do not correctly detect non-empty measures when deleting global timesig change after a local one
// see http://musescore.org/en/node/51596
// Delete the time sig segment from the root score, we will rewriteMeasures from it
// since it contains all the music while the part doesn't
Score* rScore = masterScore();
Measure* rm = rScore->tick2measure(m->tick());
Segment* rs = rm->findSegment(SegmentType::TimeSig, s->tick());
rScore->undoRemoveElement(rs);
Measure* pm = m->prevMeasure();
Fraction ns(pm ? pm->timesig() : Fraction(4,4));
if (!rScore->rewriteMeasures(rm, ns, -1)) {
undoStack()->current()->unwind();
}
else {
m = tick2measure(tick); // old m may have been replaced
// hack: fix measure rest durations for staves with local time signatures
// if a time signature was deleted to reveal a previous local one,
// then rewriteMeasures() got the measure rest durations wrong
// (if we fixed it to work for delete, it would fail for add)
// so we will fix measure rest durations here
// TODO: fix rewriteMeasures() to get this right
for (int i = 0; i < nstaves(); ++i) {
TimeSig* tsig = staff(i)->timeSig(tick);
if (tsig && tsig->isLocal()) {
for (Measure* nm = m; nm; nm = nm->nextMeasure()) {
// stop when time signature changes
if (staff(i)->timeSig(nm->tick()) != tsig)
break;
// fix measure rest duration
ChordRest* cr = nm->findChordRest(nm->tick(), i * VOICES);
if (cr && cr->isRest() && cr->durationType() == TDuration::DurationType::V_MEASURE)
cr->undoChangeProperty(Pid::DURATION, QVariant::fromValue(nm->stretchedLen(staff(i))));
//cr->setTicks(nm->stretchedLen(staff(i)));
}
}
}
}
}
//---------------------------------------------------------
// addTiedMidiPitch
//---------------------------------------------------------
Note* Score::addTiedMidiPitch(int pitch, bool addFlag, Chord* prevChord)
{
Note* n = addMidiPitch(pitch, addFlag);
if (prevChord) {
Note* nn = prevChord->findNote(n->pitch());
if (nn) {
Tie* tie = new Tie(this);
tie->setStartNote(nn);
tie->setEndNote(n);
tie->setTrack(n->track());
n->setTieBack(tie);
nn->setTieFor(tie);
undoAddElement(tie);
}
}
return n;
}
//---------------------------------------------------------
// addMidiPitch
//---------------------------------------------------------
Note* Score::addMidiPitch(int pitch, bool addFlag)
{
NoteVal nval(pitch);
Staff* st = staff(inputState().track() / VOICES);
// if transposing, interpret MIDI pitch as representing desired written pitch
// set pitch based on corresponding sounding pitch
if (!styleB(Sid::concertPitch))
nval.pitch += st->part()->instrument(inputState().tick())->transpose().chromatic;
// let addPitch calculate tpc values from pitch
//Key key = st->key(inputState().tick());
//nval.tpc1 = pitch2tpc(nval.pitch, key, Prefer::NEAREST);
return addPitch(nval, addFlag);
}
//---------------------------------------------------------
// searchNote
// search for note or rest before or at tick position tick
// in staff
//---------------------------------------------------------
ChordRest* Score::searchNote(const Fraction& tick, int track) const
{
ChordRest* ipe = 0;
SegmentType st = SegmentType::ChordRest;
for (Segment* segment = firstSegment(st); segment; segment = segment->next1(st)) {
ChordRest* cr = segment->cr(track);
if (!cr)
continue;
if (cr->tick() == tick)
return cr;
if (cr->tick() > tick)
return ipe ? ipe : cr;
ipe = cr;
}
return 0;
}
//---------------------------------------------------------
// regroupNotesAndRests
// * combine consecutive rests into fewer rests of longer duration.
// * combine tied notes/chords into fewer notes of longer duration.
// Only operates on one voice - protects manual layout adjustment, etc.
//---------------------------------------------------------
void Score::regroupNotesAndRests(const Fraction& startTick, const Fraction& endTick, int track)
{
Segment* inputSegment = _is.segment(); // store this so we can get back to it later.
Segment* seg = tick2segment(startTick, true, SegmentType::ChordRest);
for (Measure* msr = seg->measure(); msr && msr->tick() < endTick; msr = msr->nextMeasure()) {
Fraction maxTick = endTick > msr->endTick() ? msr->endTick() : endTick;
if (!seg || seg->measure() != msr)
seg = msr->first(SegmentType::ChordRest);
for (; seg; seg = seg->next(SegmentType::ChordRest)) {
ChordRest* curr = seg->cr(track);
if (!curr)
continue; // this voice is empty here (CR overlaps with CR in other track)
if (seg->tick() + curr->actualTicks() > maxTick)
break; // outside range
if (curr->isRest() && !(curr->tuplet()) && !(toRest(curr)->isGap())) {
// combine consecutive rests
ChordRest* lastRest = curr;
for (Segment* s = seg->next(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
ChordRest* cr = s->cr(track);
if (!cr)
continue; // this voice is empty here
if (!cr->isRest() || s->tick() + cr->actualTicks() > maxTick || toRest(cr)->isGap())
break; // next element in the same voice is not a rest, or it exceeds the selection, or it is a gap
lastRest = cr;
}
Fraction restTicks = lastRest->tick() + lastRest->ticks() - curr->tick();
seg = setNoteRest(seg, curr->track(), NoteVal(), restTicks, Direction::AUTO, false, true);
}
else if (curr->isChord()) {
// combine tied chords
Chord* chord = toChord(curr);
Chord* lastTiedChord = chord;
for (Chord* next = chord->nextTiedChord(); next && next->tick() + next->ticks() <= maxTick; next = next->nextTiedChord()) {
lastTiedChord = next;
}
if (!lastTiedChord)
lastTiedChord = chord;
Fraction noteTicks = lastTiedChord->tick() + lastTiedChord->ticks() - chord->tick();
if (!(curr->tuplet())) {
// store start/end note for backward/forward ties ending/starting on the group of notes being rewritten
size_t numNotes = chord->notes().size();
#if (!defined (_MSCVER) && !defined (_MSC_VER))
Note* tieBack[numNotes];
Note* tieFor[numNotes];
#else
// MSVC does not support VLA. Replace with std::vector. If profiling determines that the
// heap allocation is slow, an optimization might be used.
std::vector<Note *> tieBack(numNotes);
std::vector<Note *> tieFor(numNotes);
#endif
for (size_t i = 0; i < numNotes; i++) {
Note* n = chord->notes()[i];
Note* nn = lastTiedChord->notes()[i];
if (n->tieBack())
tieBack[i] = n->tieBack()->startNote();
else
tieBack[i] = 0;
if (nn->tieFor())
tieFor[i] = nn->tieFor()->endNote();
else
tieFor[i] = 0;
}
Fraction tick = seg->tick();
int tr = chord->track();
Fraction sd = noteTicks;
Tie* tie = 0;
Segment* segment = seg;
ChordRest* cr = toChordRest(segment->element(tr));
Chord* nchord = toChord(chord->clone());
for (size_t i = 0; i < numNotes; i++) { // strip ties from cloned chord
Note* n = nchord->notes()[i];
n->setTieFor(0);
n->setTieBack(0);
}
Chord* startChord = nchord;
Measure* measure = 0;
bool firstpart = true;
for (;;) {
if (tr % VOICES)
expandVoice(segment, tr);
// the returned gap ends at the measure boundary or at tuplet end
Fraction dd = makeGap(segment, tr, sd, cr->tuplet());
if (dd.isZero())
break;
measure = segment->measure();
std::vector<TDuration> dl;
dl = toRhythmicDurationList(dd, false, segment->rtick(), sigmap()->timesig(tick.ticks()).nominal(), measure, 1);
size_t n = dl.size();
for (size_t i = 0; i < n; ++i) {
const TDuration& d = dl[i];
Chord* nchord2 = toChord(nchord->clone());
if (!firstpart)
nchord2->removeMarkings(true);
nchord2->setDurationType(d);
nchord2->setTicks(d.fraction());
std::vector<Note*> nl1 = nchord->notes();
std::vector<Note*> nl2 = nchord2->notes();
if (!firstpart)
for (size_t j = 0; j < nl1.size(); ++j) {
tie = new Tie(this);
tie->setStartNote(nl1[j]);
tie->setEndNote(nl2[j]);
tie->setTrack(tr);
nl1[j]->setTieFor(tie);
nl2[j]->setTieBack(tie);
}
undoAddCR(nchord2, measure, tick);
segment = nchord2->segment();
tick += nchord2->actualTicks();
nchord = nchord2;
firstpart = false;
}
sd -= dd;
if (sd.isZero())
break;
Segment* nseg = tick2segment(tick, false, SegmentType::ChordRest);
if (nseg == 0)
break;
segment = nseg;
cr = toChordRest(segment->element(tr));
if (cr == 0) {
if (tr % VOICES)
cr = addRest(segment, tr, TDuration(TDuration::DurationType::V_MEASURE), 0);
else
break;
}
}
if (_is.slur()) {
// extend slur
_is.slur()->undoChangeProperty(Pid::SPANNER_TICKS, nchord->tick() - _is.slur()->tick());
for (ScoreElement* e : _is.slur()->linkList()) {
Slur* slur = toSlur(e);
for (ScoreElement* ee : nchord->linkList()) {
Element* e1 = static_cast<Element*>(ee);
if (e1->score() == slur->score() && e1->track() == slur->track2()) {
slur->score()->undo(new ChangeSpannerElements(slur, slur->startElement(), e1));
break;
}
}
}
}
// recreate previously stored pending ties
for (size_t i = 0; i < numNotes; i++) {
Note* n = startChord->notes()[i];
Note* nn = nchord->notes()[i];
if (tieBack[i]) {
tie = new Tie(this);
tie->setStartNote(tieBack[i]);
tie->setEndNote(n);
tie->setTrack(track);
n->setTieBack(tie);
tieBack[i]->setTieFor(tie);
undoAddElement(tie);
}
if (tieFor[i]) {
tie = new Tie(this);
tie->setStartNote(nn);
tie->setEndNote(tieFor[i]);
tie->setTrack(track);
n->setTieFor(tie);
tieFor[i]->setTieBack(tie);
undoAddElement(tie);
}
}
if (tie) // at least one tie was created
connectTies();
}
}
}
}
// now put the input state back where it was before
_is.setSegment(inputSegment);
}
//---------------------------------------------------------
// cmdTieNoteList
//---------------------------------------------------------
std::vector<Note*> Score::cmdTieNoteList(const Selection& selection, bool noteEntryMode)
{
Element* el = selection.element();
if (Note* n = InputState::note(el)) {
if (noteEntryMode)
return n->chord()->notes();
else
return { n };
}
else {
ChordRest* cr = InputState::chordRest(el);
if (cr && cr->isChord())
return toChord(cr)->notes();
}
return selection.noteList();
}
//---------------------------------------------------------
// cmdAddTie
//---------------------------------------------------------
void Score::cmdAddTie(bool addToChord)
{
const std::vector<Note*> noteList = cmdTieNoteList(selection(), noteEntryMode());
if (noteList.empty()) {
qDebug("no notes selected");
return;
}
startCmd();
Chord* lastAddedChord = 0;
for (Note* note : noteList) {
if (note->tieFor()) {
qDebug("cmdAddTie: note %p has already tie? noteFor: %p", note, note->tieFor());
continue;
}
if (noteEntryMode()) {
ChordRest* cr = nullptr;
Chord* c = note->chord();
// set cursor at position after note
if (c->isGraceBefore()) {
// tie grace note before to main note
cr = toChord(c->parent());
addToChord = true;
}
else {
_is.setSegment(note->chord()->segment());
_is.moveToNextInputPos();
_is.setLastSegment(_is.segment());
if (_is.cr() == 0)
expandVoice();
cr = _is.cr();
}
if (cr == 0)
break;
bool addFlag = lastAddedChord != nullptr;
// try to re-use existing note or chord
Note* n = nullptr;
if (addToChord && cr->isChord()) {
Chord* chord = toChord(cr);
Note* nn = chord->findNote(note->pitch());
if (nn && nn->tpc() == note->tpc())
n = nn; // re-use note
else
addFlag = true; // re-use chord
}
// if no note to re-use, create one
NoteVal nval(note->noteVal());
if (!n)
n = addPitch(nval, addFlag);
else
select(n);
if (n) {
if (!lastAddedChord)
lastAddedChord = n->chord();
// n is not necessarily next note if duration span over measure
Note* nnote = searchTieNote(note);
while (nnote) {
// DEBUG: if duration spans over measure
// this does not set line for intermediate notes
// tpc was set correctly already
//n->setLine(note->line());
//n->setTpc(note->tpc());
Tie* tie = new Tie(this);
tie->setStartNote(note);
tie->setEndNote(nnote);
tie->setTrack(note->track());
tie->setTick(note->chord()->segment()->tick());
tie->setTicks(nnote->chord()->segment()->tick() - note->chord()->segment()->tick());
undoAddElement(tie);
if (!addFlag || nnote->chord()->tick() >= lastAddedChord->tick() || nnote->chord()->isGrace()) {
break;
}
else {
note = nnote;
_is.setLastSegment(_is.segment());
nnote = addPitch(nval, true);
}
}
}
}
else {
Note* note2 = searchTieNote(note);
if (note2) {
Tie* tie = new Tie(this);
tie->setStartNote(note);
tie->setEndNote(note2);
tie->setTrack(note->track());
tie->setTick(note->chord()->segment()->tick());
tie->setTicks(note2->chord()->segment()->tick() - note->chord()->segment()->tick());
undoAddElement(tie);
}
}
}
if (lastAddedChord)
nextInputPos(lastAddedChord, false);
endCmd();
}
//---------------------------------------------------------
// cmdRemoveTie
//---------------------------------------------------------
void Score::cmdToggleTie()
{
const std::vector<Note*> noteList = cmdTieNoteList(selection(), noteEntryMode());
if (noteList.empty()) {
qDebug("no notes selected");
return;
}
bool canAddTies = false;
const size_t notes = noteList.size();
std::vector<Note*> tieNoteList(notes);
for (size_t i = 0; i < notes; ++i) {
Note* n = noteList[i];
if (n->tieFor()) {
tieNoteList[i] = nullptr;
}
else {
Note* tieNote = searchTieNote(n);
tieNoteList[i] = tieNote;
if (tieNote)
canAddTies = true;
}
}
startCmd();
if (canAddTies) {
for (size_t i = 0; i < notes; ++i) {
Note* note2 = tieNoteList[i];
if (note2) {
Note* note = noteList[i];
Tie* tie = new Tie(this);
tie->setStartNote(note);
tie->setEndNote(note2);
tie->setTrack(note->track());
tie->setTick(note->chord()->segment()->tick());
tie->setTicks(note2->chord()->segment()->tick() - note->chord()->segment()->tick());
undoAddElement(tie);
}
}
}
else {
for (Note* n : noteList) {
Tie* tie = n->tieFor();
if (tie)
undoRemoveElement(tie);
}
}
endCmd();
}
//---------------------------------------------------------
// cmdAddOttava
//---------------------------------------------------------
void Score::cmdAddOttava(OttavaType type)
{
const Selection sel = selection(); // copy selection state before the operation.
// add on each staff if possible
if (sel.isRange() && sel.staffStart() != sel.staffEnd() - 1) {
for (int staffIdx = sel.staffStart() ; staffIdx < sel.staffEnd(); ++staffIdx) {
ChordRest* cr1 = sel.firstChordRest(staffIdx * VOICES);
ChordRest* cr2 = sel.lastChordRest(staffIdx * VOICES);
if (!cr1)
continue;
if (cr2 == 0)
cr2 = cr1;
Ottava* ottava = new Ottava(this);
ottava->setOttavaType(type);
ottava->setTrack(cr1->track());
ottava->setTrack2(cr1->track());
ottava->setTick(cr1->tick());
ottava->setTick2(cr2->tick() + cr2->actualTicks());
undoAddElement(ottava);
}
}
else {
ChordRest* cr1;
ChordRest* cr2;
getSelectedChordRest2(&cr1, &cr2);
if (!cr1)
return;
if (cr2 == 0)
cr2 = cr1;
Ottava* ottava = new Ottava(this);
ottava->setOttavaType(type);
ottava->setTrack(cr1->track());
ottava->setTrack2(cr1->track());
ottava->setTick(cr1->tick());
ottava->setTick2(cr2->tick() + cr2->actualTicks());
undoAddElement(ottava);
if (!noteEntryMode())
select(ottava, SelectType::SINGLE, 0);
}
}
//---------------------------------------------------------
// cmdSetBeamMode
//---------------------------------------------------------
void Score::cmdSetBeamMode(Beam::Mode mode)
{
for (ChordRest* cr : getSelectedChordRests()) {
if (cr)
cr->undoChangeProperty(Pid::BEAM_MODE, int(mode));
}
}
//---------------------------------------------------------
// cmdFlip
//---------------------------------------------------------
void Score::cmdFlip()
{
const QList<Element*>& el = selection().elements();
if (el.empty()) {
MScore::setError(NO_FLIPPABLE_SELECTED);
return;
}
std::set<const Element*> alreadyFlippedElements;
auto flipOnce = [&alreadyFlippedElements](const Element* element, std::function<void()> flipFunction) -> void {
if (alreadyFlippedElements.count(element) == 0) {
alreadyFlippedElements.insert(element);
flipFunction();
}
};
for (Element* e : el) {
if (e->isNote() || e->isStem() || e->isHook()) {
Chord* chord = nullptr;
if (e->isNote()) {
auto note = toNote(e);
chord = note->chord();
}
else if (e->isStem())
chord = toStem(e)->chord();
else
chord = toHook(e)->chord();
if (chord->beam()) {
if (!selection().isRange())
e = chord->beam();
else
continue;
}
else {
flipOnce(chord, [chord](){
Direction dir = chord->up() ? Direction::DOWN : Direction::UP;
chord->undoChangeProperty(Pid::STEM_DIRECTION, QVariant::fromValue<Direction>(dir));
});
}
}
if (e->isBeam()) {
auto beam = toBeam(e);
flipOnce(beam, [beam](){
Direction dir = beam->up() ? Direction::DOWN : Direction::UP;
beam->undoChangeProperty(Pid::STEM_DIRECTION, QVariant::fromValue<Direction>(dir));
});
}
else if (e->isSlurTieSegment()) {
auto slurTieSegment = toSlurTieSegment(e)->slurTie();
flipOnce(slurTieSegment, [slurTieSegment](){
Direction dir = slurTieSegment->up() ? Direction::DOWN : Direction::UP;
slurTieSegment->undoChangeProperty(Pid::SLUR_DIRECTION, QVariant::fromValue<Direction>(dir));
});
}
else if (e->isArticulation()) {
auto articulation = toArticulation(e);
flipOnce(articulation, [articulation](){
ArticulationAnchor articAnchor = articulation->anchor();
switch (articAnchor) {
case ArticulationAnchor::TOP_CHORD:
articAnchor = ArticulationAnchor::BOTTOM_CHORD;
break;
case ArticulationAnchor::BOTTOM_CHORD:
articAnchor = ArticulationAnchor::TOP_CHORD;
break;
case ArticulationAnchor::CHORD:
articAnchor = articulation->up() ? ArticulationAnchor::BOTTOM_CHORD : ArticulationAnchor::TOP_CHORD;
break;
case ArticulationAnchor::TOP_STAFF:
articAnchor = ArticulationAnchor::BOTTOM_STAFF;
break;
case ArticulationAnchor::BOTTOM_STAFF:
articAnchor = ArticulationAnchor::TOP_STAFF;
break;
}
PropertyFlags pf = articulation->propertyFlags(Pid::ARTICULATION_ANCHOR);
if (pf == PropertyFlags::STYLED)
pf = PropertyFlags::UNSTYLED;
articulation->undoChangeProperty(Pid::ARTICULATION_ANCHOR, int(articAnchor), pf);
});
}
else if (e->isTuplet()) {
auto tuplet = toTuplet(e);
flipOnce(tuplet, [tuplet](){
Direction dir = tuplet->isUp() ? Direction::DOWN : Direction::UP;
tuplet->undoChangeProperty(Pid::DIRECTION, QVariant::fromValue<Direction>(dir), PropertyFlags::UNSTYLED);
});
}
else if (e->isNoteDot() && e->parent()->isNote()) {
Note* note = toNote(e->parent());
Direction d = note->dotIsUp() ? Direction::DOWN : Direction::UP;
note->undoChangeProperty(Pid::DOT_POSITION, QVariant::fromValue<Direction>(d));
}
else if (e->isTempoText()
|| e->isSystemText()
|| e->isJump()
|| e->isMarker()
|| e->isStaffText()
|| e->isSticking()
|| e->isFingering()
|| e->isDynamic()
|| e->isHarmony()
|| e->isInstrumentChange()
|| e->isRehearsalMark()
|| e->isMeasureNumber()
|| e->isFretDiagram()
|| e->isHairpin()
|| e->isHairpinSegment()
|| e->isOttavaSegment()
|| e->isTextLineSegment()
|| e->isPedalSegment()
|| e->isLetRingSegment()
|| e->isPalmMuteSegment()
|| e->isFermata()
|| e->isLyrics()
|| e->isTrillSegment()) {
e->undoChangeProperty(Pid::AUTOPLACE, true);
// getProperty() delegates call from spannerSegment to Spanner
Placement p = Placement(e->getProperty(Pid::PLACEMENT).toInt());
p = (p == Placement::ABOVE) ? Placement::BELOW : Placement::ABOVE;
// TODO: undoChangeProperty() should probably do this directly
// see https://musescore.org/en/node/281432
Element* ee = e->propertyDelegate(Pid::PLACEMENT);
if (!ee)
ee = e;
PropertyFlags pf = ee->propertyFlags(Pid::PLACEMENT);
if (pf == PropertyFlags::STYLED)
pf = PropertyFlags::UNSTYLED;
qreal oldDefaultY = ee->propertyDefault(Pid::OFFSET).toPointF().y();
ee->undoChangeProperty(Pid::PLACEMENT, int(p), pf);
// flip and rebase user offset to new default now that placement has changed
qreal newDefaultY = ee->propertyDefault(Pid::OFFSET).toPointF().y();
if (ee->isSpanner()) {
Spanner* spanner = toSpanner(ee);
for (SpannerSegment* ss : spanner->spannerSegments()) {
if (!ss->isStyled(Pid::OFFSET)) {
QPointF off = ss->getProperty(Pid::OFFSET).toPointF();
qreal oldY = off.y() - oldDefaultY;
off.ry() = newDefaultY - oldY;
ss->undoChangeProperty(Pid::OFFSET, off);
ss->setOffsetChanged(false);
}
}
}
else if (!ee->isStyled(Pid::OFFSET)) {
QPointF off = ee->getProperty(Pid::OFFSET).toPointF();
qreal oldY = off.y() - oldDefaultY;
off.ry() = newDefaultY - oldY;
ee->undoChangeProperty(Pid::OFFSET, off);
ee->setOffsetChanged(false);
}
}
}
}
//---------------------------------------------------------
// deleteItem
//---------------------------------------------------------
void Score::deleteItem(Element* el)
{
if (!el)
return;
// cannot remove generated elements
if (el->generated() && !(el->isBracket() || el->isBarLine() || el->isClef() || el->isMeasureNumber()))
return;
// qDebug("%s", el->name());
switch (el->type()) {
case ElementType::INSTRUMENT_NAME: {
Part* part = el->part();
InstrumentName* in = toInstrumentName(el);
if (in->instrumentNameType() == InstrumentNameType::LONG)
undo(new ChangeInstrumentLong(Fraction(0,1), part, QList<StaffName>()));
else if (in->instrumentNameType() == InstrumentNameType::SHORT)
undo(new ChangeInstrumentShort(Fraction(0,1), part, QList<StaffName>()));
}
break;
case ElementType::TIMESIG: {
// timesig might already be removed
TimeSig* ts = toTimeSig(el);
Segment* s = ts->segment();
Measure* m = s->measure();
Segment* ns = m->findSegment(s->segmentType(), s->tick());
if (!ns || (ns->element(ts->track()) != ts)) {
qDebug("deleteItem: not found");
break;
}
cmdRemoveTimeSig(ts);
}
break;
case ElementType::KEYSIG:
{
KeySig* k = toKeySig(el);
undoRemoveElement(k);
for (int i = 0; i < k->part()->nstaves(); i++) {
Staff* staff = k->part()->staff(i);
KeySigEvent e = staff->keySigEvent(k->tick());
updateInstrumentChangeTranspositions(e, staff, k->tick());
}
}
break;
case ElementType::NOTE:
{
Chord* chord = toChord(el->parent());
if (chord->notes().size() > 1) {
undoRemoveElement(el);
select(chord->downNote(), SelectType::SINGLE, 0);
break;
}
// else fall through
el = chord;
}
// fall through
case ElementType::CHORD:
{
Chord* chord = toChord(el);
removeChordRest(chord, false);
// replace with rest
if (chord->noteType() == NoteType::NORMAL) {
Rest* rest = new Rest(this, chord->durationType());
rest->setDurationType(chord->durationType());
rest->setTicks(chord->ticks());
rest->setTrack(el->track());
rest->setParent(chord->parent());
Segment* segment = chord->segment();
undoAddCR(rest, segment->measure(), segment->tick());
Tuplet* tuplet = chord->tuplet();
if (tuplet) {
QList<ScoreElement*> tl = tuplet->linkList();
for (ScoreElement* e : rest->linkList()) {
DurationElement* de = toDurationElement(e);
for (ScoreElement* ee : tl) {
Tuplet* t = toTuplet(ee);
if (t->score() == de->score() && t->track() == de->track()) {
de->setTuplet(t);
t->add(de);
break;
}
}
}
}
//select(rest, SelectType::SINGLE, 0);
}
else {
// remove segment if empty
Segment* seg = chord->segment();
if (seg->empty())
undoRemoveElement(seg);
}
}
break;
case ElementType::REPEAT_MEASURE:
{
RepeatMeasure* rm = toRepeatMeasure(el);
removeChordRest(rm, false);
Rest* rest = new Rest(this);
rest->setDurationType(TDuration::DurationType::V_MEASURE);
rest->setTicks(rm->measure()->stretchedLen(rm->staff()));
rest->setTrack(rm->track());
rest->setParent(rm->parent());
Segment* segment = rm->segment();
undoAddCR(rest, segment->measure(), segment->tick());
}
// fall through
case ElementType::REST:
//
// only allow for voices != 0
// e.g. voice 0 rests cannot be removed
//
{
Rest* rest = toRest(el);
if (rest->tuplet() && rest->tuplet()->elements().empty())
undoRemoveElement(rest->tuplet());
if (el->voice() != 0) {
rest->undoChangeProperty(Pid::GAP, true);
for (ScoreElement* r : el->linkList()) {
Rest* rr = toRest(r);
if (rr->track() % VOICES)
rr->undoChangeProperty(Pid::GAP, true);
}
// delete them really when only gap rests are in the actual measure.
Measure* m = toRest(el)->measure();
int track = el->track();
if (m->isOnlyDeletedRests(track)) {
static const SegmentType st { SegmentType::ChordRest };
for (const Segment* s = m->first(st); s; s = s->next(st)) {
Element* del = s->element(track);
if (s->segmentType() != st || !del)
continue;
if (toRest(del)->isGap())
undoRemoveElement(del);
}
checkSpanner(m->tick(), m->endTick());
}
else {
// check if the other rest could be combined
Segment* s = toRest(el)->segment();
std::vector<Rest*> rests;
// find previous segment with cr in this track
Element* pe = 0;
for (Segment* ps = s->prev(SegmentType::ChordRest); ps; ps = ps->prev(SegmentType::ChordRest)) {
Element* elm = ps->element(track);
if (elm && elm->isRest() && toRest(elm)->isGap()) {
pe = el;
rests.push_back(toRest(elm));
}
else if (elm)
break;
}
// find next segment with cr in this track
Segment* ns;
Element* ne = 0;
for (ns = s->next(SegmentType::ChordRest); ns; ns = ns->next(SegmentType::ChordRest)) {
Element* elm = ns->element(track);
if (elm && elm->isRest() && toRest(elm)->isGap()) {
ne = elm;
rests.push_back(toRest(elm));
}
else if (elm)
break;
}
Fraction stick = pe ? pe->tick() : s->tick();
Fraction ticks = { 0, 1 };
if (ne)
ticks = ne->tick() - stick + toRest(ne)->actualTicks();
else if (ns)
ticks = ns->tick() - stick;
else
ticks = m->ticks() + m->tick() - stick;
if (ticks != m->ticks() && ticks != s->ticks()) {
undoRemoveElement(rest);
for (Rest* r : rests) {
undoRemoveElement(r);
}
Fraction f = ticks;
std::vector<TDuration> dList = toDurationList(f, true);
if (dList.empty())
break;
for (const TDuration& d : dList) {
Rest* rr = new Rest(this);
rr->setTicks(d.fraction());
rr->setDurationType(d);
rr->setTrack(track);
rr->setGap(true);
undoAddCR(rr, m, stick);
}
}
}
// Set input position
// TODO If deleted element is last of a sequence, use prev?
if (noteEntryMode())
score()->move("prev-chord");
}
}
break;
case ElementType::ACCIDENTAL:
if (el->parent()->isNote())
changeAccidental(toNote(el->parent()), AccidentalType::NONE);
else
undoRemoveElement(el);
break;
case ElementType::BAR_LINE: {
BarLine* bl = toBarLine(el);
Segment* s = bl->segment();
Measure* m = s->measure();
if (s->isBeginBarLineType() || s->isBarLineType()) {
undoRemoveElement(el);
}
else {
if (bl->barLineType() == BarLineType::START_REPEAT) {
Measure* m2 = m->isMMRest() ? m->mmRestFirst() : m;
for (Score* lscore : score()->scoreList()) {
Measure* lmeasure = lscore->tick2measure(m2->tick());
if (lmeasure)
lmeasure->undoChangeProperty(Pid::REPEAT_START, false);
}
}
else if (bl->barLineType() == BarLineType::END_REPEAT) {
Measure* m2 = m->isMMRest() ? m->mmRestLast() : m;
for (Score* lscore : score()->scoreList()) {
Measure* lmeasure = lscore->tick2measure(m2->tick());
if (lmeasure)
lmeasure->undoChangeProperty(Pid::REPEAT_END, false);
}
}
else {
bl->undoChangeProperty(Pid::BARLINE_TYPE, QVariant::fromValue(BarLineType::NORMAL));
}
}
}
break;
case ElementType::TUPLET:
cmdDeleteTuplet(toTuplet(el), true);
break;
case ElementType::MEASURE: {
Measure* m = toMeasure(el);
undoRemoveMeasures(m, m);
undoInsertTime(m->tick(), -(m->endTick() - m->tick()));
}
break;
case ElementType::BRACKET:
undoRemoveBracket(toBracket(el));
break;
case ElementType::LAYOUT_BREAK:
{
undoRemoveElement(el);
LayoutBreak* lb = toLayoutBreak(el);
Measure* m = lb->measure();
if (m->isMMRest()) {
// propagate to original measure
m = m->mmRestLast();
for (Element* e : m->el()) {
if (e->isLayoutBreak()) {
undoRemoveElement(e);
break;
}
}
}
}
break;
case ElementType::CLEF:
{
Clef* clef = toClef(el);
Measure* m = clef->measure();
if (m->isMMRest()) {
// propagate to original measure
m = m->mmRestLast();
Segment* s = m->findSegment(SegmentType::Clef, clef->segment()->tick());
if (s && s->element(clef->track())) {
Clef* c = toClef(s->element(clef->track()));
undoRemoveElement(c);
}
}
else {
if (clef->generated()) {
// find the real clef if this is a cautionary one
if (m && m->prevMeasure()) {
Fraction tick = m->tick();
m = m->prevMeasure();
Segment* s = m->findSegment(SegmentType::Clef, tick);
if (s && s->element(clef->track()))
clef = toClef(s->element(clef->track()));
}
}
undoRemoveElement(clef);
}
}
break;
case ElementType::MEASURE_NUMBER:
{
Measure* mea = toMeasure(el->parent());
switch (mea->measureNumberMode()) {
// If the user tries to remove an automatically generated measure number,
// we should force the measure not to show any measure number
case MeasureNumberMode::AUTO:
mea->undoChangeProperty(Pid::MEASURE_NUMBER_MODE, static_cast<int>(MeasureNumberMode::HIDE));
break;
// If the user tries to remove a measure number that he added manually,
// then we should set the MeasureNumberMode to AUTO only if will not show if set to auto.
// If after setting the MeasureNumberMode to AUTO, the measure number still shows,
// We need to force the measure to hide its measure number.
case MeasureNumberMode::SHOW:
if (mea->showsMeasureNumberInAutoMode())
mea->undoChangeProperty(Pid::MEASURE_NUMBER_MODE, static_cast<int>(MeasureNumberMode::HIDE));
else
mea->undoChangeProperty(Pid::MEASURE_NUMBER_MODE, static_cast<int>(MeasureNumberMode::AUTO));
break;
case MeasureNumberMode::HIDE:
break;
}
}
break;
case ElementType::REHEARSAL_MARK:
case ElementType::TEMPO_TEXT:
{
Segment* s = toSegment(el->parent());
Measure* m = s->measure();
if (m->isMMRest()) {
// propagate to original measure/element
m = m->mmRestFirst();
Segment* ns = m->findSegment(SegmentType::ChordRest, s->tick());
for (Element* e : ns->annotations()) {
if (e->type() == el->type() && e->track() == el->track()) {
el = e;
undoRemoveElement(el);
break;
}
}
}
else
undoRemoveElement(el);
}
break;
case ElementType::OTTAVA_SEGMENT:
case ElementType::HAIRPIN_SEGMENT:
case ElementType::TRILL_SEGMENT:
case ElementType::VIBRATO_SEGMENT:
case ElementType::TEXTLINE_SEGMENT:
case ElementType::VOLTA_SEGMENT:
case ElementType::SLUR_SEGMENT:
case ElementType::TIE_SEGMENT:
case ElementType::LYRICSLINE_SEGMENT:
case ElementType::PEDAL_SEGMENT:
case ElementType::GLISSANDO_SEGMENT:
case ElementType::LET_RING_SEGMENT:
case ElementType::PALM_MUTE_SEGMENT:
{
el = toSpannerSegment(el)->spanner();
undoRemoveElement(el);
}
break;
case ElementType::STEM_SLASH: // cannot delete this elements
case ElementType::HOOK:
qDebug("cannot remove %s", el->name());
break;
case ElementType::TEXT:
if (el->parent()->isTBox())
el->undoChangeProperty(Pid::TEXT, QString());
else
undoRemoveElement(el);
break;
case ElementType::INSTRUMENT_CHANGE:
{
InstrumentChange* ic = static_cast<InstrumentChange*>(el);
Fraction tickStart = ic->segment()->tick();
Part* part = ic->part();
Interval oldV = part->instrument(tickStart)->transpose();
undoRemoveElement(el);
for (KeySig* keySig : ic->keySigs())
deleteItem(keySig);
for (Clef* clef : ic->clefs())
deleteItem(clef);
if (part->instrument(tickStart)->transpose() != oldV) {
auto i = part->instruments()->upper_bound(tickStart.ticks());
Fraction tickEnd;
if (i == part->instruments()->end())
tickEnd = Fraction(-1, 1);
else
tickEnd = Fraction::fromTicks(i->first);
transpositionChanged(part, oldV, tickStart, tickEnd);
}
}
break;
case ElementType::MARKER:
{
Measure* m = toMeasure(el->parent());
if (m->isMMRest()) {
// find corresponding marker in underlying measure
bool found = false;
// the marker may be in the first measure...
for (Element* e : m->mmRestFirst()->el()) {
if (e->isMarker() && e->subtype() == el->subtype()) {
undoRemoveElement(e);
found = true;
break;
}
}
if (!found) {
// ...or it may be in the last measure
for (Element* e : m->mmRestLast()->el()) {
if (e->isMarker() && e->subtype() == el->subtype()) {
undoRemoveElement(e);
break;
}
}
}
}
// whether m is an mmrest or not, we still need to remove el
undoRemoveElement(el);
}
break;
case ElementType::JUMP:
{
Measure* m = toMeasure(el->parent());
if (m->isMMRest()) {
// find corresponding jump in underlying measure
for (Element* e : m->mmRestLast()->el()) {
if (e->isJump() && e->subtype() == el->subtype()) {
undoRemoveElement(e);
break;
}
}
}
// whether m is an mmrest or not, we still need to remove el
undoRemoveElement(el);
}
break;
default:
undoRemoveElement(el);
break;
}
}
//---------------------------------------------------------
// deleteMeasures
//---------------------------------------------------------
void Score::deleteMeasures(MeasureBase* is, MeasureBase* ie)
{
// qDebug("deleteMeasures %p %p", is, ie);
#if 0
if (!selection().isRange())
return;
MeasureBase* is = selection().startSegment()->measure();
if (is->isMeasure() && toMeasure(is)->isMMRest())
is = toMeasure(is)->mmRestFirst();
Segment* seg = selection().endSegment();
MeasureBase* ie;
// choose the correct last measure based on the end segment
// this depends on whether a whole measure is selected or only a few notes within it
if (seg)
ie = seg->prev() ? seg->measure() : seg->measure()->prev();
else
ie = lastMeasure();
#endif
select(0, SelectType::SINGLE, 0);
// createEndBar if last measure is deleted
bool createEndBar = false;
if (ie->isMeasure()) {
Measure* iem = toMeasure(ie);
if (iem->isMMRest())
ie = iem = iem->mmRestLast();
//TODO createEndBar = (iem == lastMeasureMM()) && (iem->endBarLineType() == BarLineType::END);
createEndBar = false;
}
// get the last deleted timesig & keysig in order to restore after deletion
KeySigEvent lastDeletedKeySigEvent;
TimeSig* lastDeletedSig = 0;
KeySig* lastDeletedKeySig = 0;
bool transposeKeySigEvent = false;
for (MeasureBase* mb = ie;; mb = mb->prev()) {
if (mb->isMeasure()) {
Measure* m = toMeasure(mb);
Segment* sts = m->findSegment(SegmentType::TimeSig, m->tick());
if (sts && !lastDeletedSig)
lastDeletedSig = toTimeSig(sts->element(0));
sts = m->findSegment(SegmentType::KeySig, m->tick());
if (sts && !lastDeletedKeySig) {
lastDeletedKeySig = toKeySig(sts->element(0));
if (lastDeletedKeySig) {
lastDeletedKeySigEvent = lastDeletedKeySig->keySigEvent();
if (!styleB(Sid::concertPitch) && !lastDeletedKeySigEvent.isAtonal() && !lastDeletedKeySigEvent.custom()) {
// convert to concert pitch
transposeKeySigEvent = true;
Interval v = staff(0)->part()->instrument(m->tick())->transpose();
if (!v.isZero())
lastDeletedKeySigEvent.setKey(transposeKey(lastDeletedKeySigEvent.key(), v, lastDeletedKeySig->part()->preferSharpFlat()));
}
}
}
if (lastDeletedSig && lastDeletedKeySig)
break;
}
if (mb == is)
break;
}
Fraction startTick = is->tick();
Fraction endTick = ie->tick();
undoInsertTime(is->tick(), -(ie->endTick() - is->tick()));
for (Score* score : scoreList()) {
Measure* mis = score->tick2measure(startTick);
Measure* mie = score->tick2measure(endTick);
score->undoRemoveMeasures(mis, mie);
// adjust views
Measure* focusOn = mis->prevMeasure() ? mis->prevMeasure() : score->firstMeasure();
for (MuseScoreView* v : score->viewer)
v->adjustCanvasPosition(focusOn, false);
if (createEndBar) {
// Measure* lastMeasure = score->lastMeasure();
//TODO if (lastMeasure && lastMeasure->endBarLineType() == BarLineType::NORMAL)
// score->undoChangeEndBarLineType(lastMeasure, BarLineType::END);
}
// insert correct timesig after deletion
Measure* mBeforeSel = mis->prevMeasure();
Measure* mAfterSel = mBeforeSel ? mBeforeSel->nextMeasure() : score->firstMeasure();
if (mAfterSel && lastDeletedSig) {
bool changed = true;
if (mBeforeSel) {
if (mBeforeSel->timesig() == mAfterSel->timesig()) {
changed = false;
}
}
Segment* s = mAfterSel->findSegment(SegmentType::TimeSig, mAfterSel->tick());
if (!s && changed) {
Segment* ns = mAfterSel->undoGetSegment(SegmentType::TimeSig, mAfterSel->tick());
for (int staffIdx = 0; staffIdx < score->nstaves(); staffIdx++) {
TimeSig* nts = new TimeSig(score);
nts->setTrack(staffIdx * VOICES);
nts->setParent(ns);
nts->setSig(lastDeletedSig->sig(), lastDeletedSig->timeSigType());
score->undoAddElement(nts);
}
}
}
// insert correct keysig if necessary
if (mAfterSel && !mBeforeSel && lastDeletedKeySig) {
Segment* s = mAfterSel->findSegment(SegmentType::KeySig, mAfterSel->tick());
if (!s) {
Segment* ns = mAfterSel->undoGetSegment(SegmentType::KeySig, mAfterSel->tick());
for (int staffIdx = 0; staffIdx < score->nstaves(); staffIdx++) {
KeySigEvent nkse = lastDeletedKeySigEvent;
if (transposeKeySigEvent) {
Interval v = score->staff(staffIdx)->part()->instrument(Fraction(0,1))->transpose();
v.flip();
nkse.setKey(transposeKey(nkse.key(), v, lastDeletedKeySig->part()->preferSharpFlat()));
}
KeySig* nks = new KeySig(score);
nks->setTrack(staffIdx * VOICES);
nks->setParent(ns);
nks->setKeySigEvent(nkse);
score->undoAddElement(nks);
}
}
}
}
_is.setSegment(0); // invalidate position
}
//---------------------------------------------------------
// deleteSpannersFromRange
/// Deletes spanners in the given range that match the
/// given selection filter.
//---------------------------------------------------------
void Score::deleteSpannersFromRange(const Fraction& t1, const Fraction& t2, int track1, int track2, const SelectionFilter& filter)
{
auto spanners = _spanner.findOverlapping(t1.ticks(), t2.ticks() - 1);
for (auto i : spanners) {
Spanner* sp = i.value;
if (sp->isVolta())
continue;
if (!filter.canSelectVoice(sp->track()))
continue;
if (sp->track() >= track1 && sp->track() < track2) {
if (sp->tick() >= t1 && sp->tick() < t2
&& sp->tick2() >= t1 && sp->tick2() <= t2) {
undoRemoveElement(sp);
}
else if (sp->isSlur() && ((sp->tick() >= t1 && sp->tick() < t2)
|| (sp->tick2() >= t1 && sp->tick2() < t2))) {
undoRemoveElement(sp);
}
}
}
}
//---------------------------------------------------------
// deleteAnnotationsFromRange
/// Deletes annotations in the given range that match the
/// given selection filter.
//---------------------------------------------------------
void Score::deleteAnnotationsFromRange(Segment* s1, Segment* s2, int track1, int track2, const SelectionFilter& filter)
{
if (!s1)
return;
if (s2 && (*s2) < (*s1))
return;
for (int track = track1; track < track2; ++track) {
if (!filter.canSelectVoice(track))
continue;
for (Segment* s = s1; s && s != s2; s = s->next1()) {
const auto annotations = s->annotations(); // make a copy since we alter the list
for (Element* annotation : annotations) {
// skip if not included in selection (eg, filter)
if (!filter.canSelect(annotation))
continue;
if (!annotation->systemFlag() && annotation->track() == track)
deleteItem(annotation);
}
}
}
}
//---------------------------------------------------------
// deleteRange
/// Deletes elements in the given range that match the
/// given selection filter.
/// \return A chord/rest inside the selected range
/// that can be used to establish a selection after this
/// deletion operation.
//---------------------------------------------------------
ChordRest* Score::deleteRange(Segment* s1, Segment* s2, int track1, int track2, const SelectionFilter& filter)
{
ChordRest* cr = nullptr;
if (s1) {
// delete content from measures underlying mmrests
if (s1 && s1->measure() && s1->measure()->isMMRest())
s1 = s1->measure()->mmRestFirst()->first();
if (s2 && s2->measure() && s2->measure()->isMMRest())
s2 = s2->measure()->mmRestLast()->last();
const Fraction stick1 = s1->tick();
const Fraction stick2 = s2 ? s2->tick() : lastMeasure()->endTick();
Segment* ss1 = s1;
if (!ss1->isChordRestType())
ss1 = ss1->next1(SegmentType::ChordRest);
bool fullMeasure = ss1 && (ss1->measure()->first(SegmentType::ChordRest) == ss1)
&& (s2 == 0 || s2->isEndBarLineType());
Fraction tick2 = s2 ? s2->tick() : Fraction(INT_MAX, 1);
deleteSpannersFromRange(stick1, stick2, track1, track2, filter);
for (int track = track1; track < track2; ++track) {
if (!filter.canSelectVoice(track))
continue;
Fraction f;
Fraction tick = Fraction(-1, 1);
Tuplet* tuplet = 0;
for (Segment* s = s1; s && (s->tick() < stick2); s = s->next1()) {
if (s->element(track) && s->isBreathType()) {
deleteItem(s->element(track));
continue;
}
// delete annotations just from this segment and track
deleteAnnotationsFromRange(s, s->next1(), track, track + 1, filter);
Element* e = s->element(track);
if (!e)
continue;
if (!s->isChordRestType()) {
// do not delete TimeSig/KeySig,
// it doesn't make sense to do it, except on full system
if (s->segmentType() != SegmentType::TimeSig && s->segmentType() != SegmentType::KeySig) {
if (!(e->isBarLine()))
undoRemoveElement(e);
}
continue;
}
ChordRest* cr1 = toChordRest(e);
if (tick == Fraction(-1, 1)) {
// first ChordRest found:
Fraction offset = cr1->rtick();
if (cr1->measure()->tick() >= s1->tick() && offset.isNotZero()) {
f = offset;
tick = s->measure()->tick();
}
else {
tick = s->tick();
f = Fraction(0, 1);
}
tuplet = cr1->tuplet();
if (tuplet && (tuplet->tick() == tick) && ((tuplet->tick() + tuplet->actualTicks()) <= tick2) ) {
// remove complete top level tuplet
Tuplet* t = cr1->tuplet();
while (t->tuplet())
t = t->tuplet();
cmdDeleteTuplet(t, false);
f += t->ticks();
tuplet = 0;
continue;
}
}
if (tuplet != cr1->tuplet()) {
Tuplet* t = cr1->tuplet();
if (t && (((t->tick() + t->actualTicks()) <= tick2) || fullMeasure)) {
// remove complete top level tuplet
while (t->tuplet())
t = t->tuplet();
cmdDeleteTuplet(t, false);
f += t->ticks();
tuplet = 0;
continue;
}
if (f.isValid())
setRest(tick, track, f, false, tuplet);
tick = cr1->tick();
tuplet = cr1->tuplet();
removeChordRest(cr1, true);
f = cr1->ticks();
}
else {
removeChordRest(cr1, true);
f += cr1->ticks();
}
}
if (f.isValid() && !f.isZero()) {
if (fullMeasure) {
// handle this as special case to be able to
// fix broken measures:
Staff* staff = Score::staff(track / VOICES);
for (Measure* m = s1->measure(); m; m = m->nextMeasure()) {
Fraction tick3 = m->tick();
Fraction ff = m->stretchedLen(staff);
Rest* r = setRest(tick3, track, ff, false, 0);
if (!cr)
cr = r;
if (s2 && (m == s2->measure()))
break;
}
}
else {
Rest* r = setRest(tick, track, f, false, tuplet);
if (!cr)
cr = r;
}
}
}
}
return cr;
}
//---------------------------------------------------------
// cmdDeleteSelection
//---------------------------------------------------------
void Score::cmdDeleteSelection()
{
ChordRest* cr = 0; // select something after deleting notes
if (selection().isRange()) {
Segment* s1 = selection().startSegment();
Segment* s2 = selection().endSegment();
const Fraction stick1 = selection().tickStart();
const Fraction stick2 = selection().tickEnd();
cr = deleteRange(s1, s2, staff2track(selection().staffStart()), staff2track(selection().staffEnd()), selectionFilter());
s1 = tick2segment(stick1);
s2 = tick2segment(stick2, true);
if (s1 == 0 || s2 == 0)
deselectAll();
else {
_selection.setStartSegment(s1);
_selection.setEndSegment(s2);
_selection.updateSelectedElements();
}
}
else {
// deleteItem modifies selection().elements() list,
// so we need a local copy:
QList<Element*> el = selection().elements();
// keep track of linked elements that are deleted implicitly
// so we don't try to delete them twice if they are also in selection
QList<ScoreElement*> deletedElements;
// Similarly, deleting one spanner segment, will delete all of them
// so we don't try to delete them twice if they are also in selection
QList<Spanner*> deletedSpanners;
for (Element* e : el) {
// these are the linked elements we are about to delete
QList<ScoreElement*> links;
if (e->links())
links = *e->links();
// find location of element to select after deleting notes
// get tick of element itself if that is valid
// or of spanner or parent if that is more valid
Fraction tick = { -1, 1 };
int track = -1;
if (!cr) {
if (e->isNote())
tick = toNote(e)->chord()->tick();
else if (e->isRest())
tick = toRest(e)->tick();
else if (e->isSpannerSegment())
tick = toSpannerSegment(e)->spanner()->tick();
else if (e->parent()
&& (e->parent()->isSegment() || e->parent()->isChord() || e->parent()->isNote() || e->parent()->isRest()))
tick = e->parent()->tick();
//else tick < 0
track = e->track();
}
// delete element if we have not done so already
if (!deletedElements.contains(e)) {
// do not delete two spanner segments from the same spanner
if (e->isSpannerSegment()) {
Spanner* spanner = toSpannerSegment(e)->spanner();
if (deletedSpanners.contains(spanner))
continue;
else {
QList<ScoreElement*> linkedSpanners;
if (spanner->links())
linkedSpanners = *spanner->links();
else
linkedSpanners.append(spanner);
for (ScoreElement* se : linkedSpanners)
deletedSpanners.append(toSpanner(se));
}
}
deleteItem(e);
}
// find element to select
if (!cr && tick >= Fraction(0,1) && track >= 0)
cr = findCR(tick, track);
// add these linked elements to list of already-deleted elements
for (ScoreElement* se : links)
deletedElements.append(se);
}
}
deselectAll();
// make new selection if appropriate
if (noteEntryMode())
cr = _is.cr();
if (cr) {
if (cr->isChord())
select(toChord(cr)->upNote(), SelectType::SINGLE);
else
select(cr, SelectType::SINGLE);
}
}
//---------------------------------------------------------
// cmdFullMeasureRest
//---------------------------------------------------------
void Score::cmdFullMeasureRest()
{
Segment* s1 = nullptr;
Segment* s2 = nullptr;
Fraction stick1 = { -1, 1 };
Fraction stick2 = { -1, 1 };
int track1 = -1;
int track2 = -1;
Rest* r = nullptr;
if (noteEntryMode()) {
s1 = inputState().segment();
if (!s1 || s1->rtick().isNotZero())
return;
Measure* m = s1->measure();
s2 = m->last();
stick1 = s1->tick();
stick2 = s2->tick();
track1 = inputState().track();
track2 = track1 + 1;
}
else if (selection().isRange()) {
s1 = selection().startSegment();
s2 = selection().endSegment();
if (styleB(Sid::createMultiMeasureRests)) {
// use underlying measures
if (s1 && s1->measure()->isMMRest())
s1 = tick2segment(stick1);
if (s2 && s2->measure()->isMMRest())
s2 = tick2segment(stick2, true);
}
stick1 = selection().tickStart();
stick2 = selection().tickEnd();
Segment* ss1 = s1;
if (ss1 && ss1->segmentType() != SegmentType::ChordRest)
ss1 = ss1->next1(SegmentType::ChordRest);
bool fullMeasure = ss1 && (ss1->measure()->first(SegmentType::ChordRest) == ss1)
&& (s2 == 0 || (s2->segmentType() == SegmentType::EndBarLine)
|| (s2->segmentType() == SegmentType::TimeSigAnnounce)
|| (s2->segmentType() == SegmentType::KeySigAnnounce));
if (!fullMeasure) {
return;
}
track1 = selection().staffStart() * VOICES;
track2 = selection().staffEnd() * VOICES;
}
else if (selection().cr()) {
ChordRest* cr = selection().cr();
if (!cr || cr->rtick().isNotZero())
return;
Measure* m = cr->measure();
s1 = m->first();
s2 = m->last();
stick1 = s1->tick();
stick2 = s2->tick();
track1 = selection().cr()->track();
track2 = track1 + 1;
}
else {
return;
}
for (int track = track1; track < track2; ++track) {
if (selection().isRange() && !selectionFilter().canSelectVoice(track))
continue;
// first pass - remove non-initial rests from empty measures/voices
for (Segment* s = s1; s != s2; s = s->next1()) {
if (!(s->measure()->isOnlyRests(track))) // Don't remove anything from measures that contain notes
continue;
if (s->segmentType() != SegmentType::ChordRest || !s->element(track))
continue;
ChordRest* cr = toChordRest(s->element(track));
// keep first rest of measure as placeholder (replaced in second pass)
// but delete all others
if (s->rtick().isNotZero())
removeChordRest(cr, true);
}
// second pass - replace placeholders with full measure rests
for (Measure* m = s1->measure(); m; m = m->nextMeasure()) {
if (m->isOnlyRests(track)) {
ChordRest* cr = m->findChordRest(m->tick(), track);
if (cr) {
removeChordRest(cr, true);
r = addRest(m->tick(), track, TDuration(TDuration::DurationType::V_MEASURE), 0);
}
else if (noteEntryMode()) {
// might be no cr at input position
r = addRest(m->tick(), track, TDuration(TDuration::DurationType::V_MEASURE), 0);
}
}
if (s2 && (m == s2->measure()))
break;
}
}
// selected range is probably empty now and possibly subsumed by an mmrest
// so updating selection requires forcing mmrests to be updated first
//TODO-ws if (styleB(Sid::createMultiMeasureRests))
// createMMRests();
s1 = tick2segmentMM(stick1);
s2 = tick2segmentMM(stick2, true);
if (selection().isRange() && s1 && s2) {
_selection.setStartSegment(s1);
_selection.setEndSegment(s2);
_selection.updateSelectedElements();
}
else if (r) {
// note entry mode
select(r, SelectType::SINGLE);
}
else {
deselectAll();
}
}
//---------------------------------------------------------
// addLyrics
// called from Keyboard Accelerator & menu
//---------------------------------------------------------
Lyrics* Score::addLyrics()
{
Element* el = selection().element();
if (el == 0 || (!el->isNote() && !el->isLyrics() && !el->isRest())) {
MScore::setError(NO_LYRICS_SELECTED);
return 0;
}
ChordRest* cr;
if (el->isNote()) {
cr = toNote(el)->chord();
if(cr->isGrace())
cr = toChordRest(cr->parent());
}
else if (el->isLyrics())
cr = toLyrics(el)->chordRest();
else if (el->isRest())
cr = toChordRest(el);
else
return 0;
int no = int(cr->lyrics().size());
Lyrics* lyrics = new Lyrics(this);
lyrics->setTrack(cr->track());
lyrics->setParent(cr);
lyrics->setNo(no);
undoAddElement(lyrics);
select(lyrics, SelectType::SINGLE, 0);
return lyrics;
}
//---------------------------------------------------------
// addHairpin
//---------------------------------------------------------
Hairpin* Score::addHairpin(HairpinType t, const Fraction& tickStart, const Fraction& tickEnd, int track)
{
Hairpin* pin = new Hairpin(this);
pin->setHairpinType(t);
if (t == HairpinType::CRESC_LINE) {
pin->setBeginText("cresc.");
pin->setContinueText("(cresc.)");
}
else if (t == HairpinType::DECRESC_LINE) {
pin->setBeginText("dim.");
pin->setContinueText("(dim.)");
}
pin->setTrack(track);
pin->setTrack2(track);
pin->setTick(tickStart);
pin->setTick2(tickEnd);
undoAddElement(pin);
return pin;
}
//---------------------------------------------------------
// Score::addHairpin
//---------------------------------------------------------
Hairpin* Score::addHairpin(HairpinType type, ChordRest* cr1, ChordRest* cr2, bool toCr2End)
{
if (!cr1)
return nullptr;
if (!cr2)
cr2 = cr1;
Q_ASSERT(cr1->staffIdx() == cr2->staffIdx());
const Fraction end = toCr2End ? cr2->tick() + cr2->actualTicks() : cr2->tick();
return addHairpin(type, cr1->tick(), end, cr1->track());
}
//---------------------------------------------------------
// cmdCreateTuplet
// replace cr with tuplet
//---------------------------------------------------------
void Score::cmdCreateTuplet(ChordRest* ocr, Tuplet* tuplet)
{
int track = ocr->track();
Measure* measure = ocr->measure();
Fraction tick = ocr->tick();
if (ocr->tuplet())
tuplet->setTuplet(ocr->tuplet());
undoRemoveElement(ocr);
ChordRest* cr;
if (ocr->isChord()) {
cr = new Chord(this);
foreach (Note* oldNote, toChord(ocr)->notes()) {
Note* note = new Note(this);
note->setPitch(oldNote->pitch());
note->setTpc1(oldNote->tpc1());
note->setTpc2(oldNote->tpc2());
cr->add(note);
}
}
else
cr = new Rest(this);
Fraction an = (tuplet->ticks() * tuplet->ratio()) / tuplet->baseLen().fraction();
int actualNotes = an.numerator() / an.denominator();
tuplet->setTrack(track);
cr->setTuplet(tuplet);
cr->setTrack(track);
cr->setDurationType(tuplet->baseLen());
cr->setTicks(tuplet->baseLen().fraction());
undoAddCR(cr, measure, tick);
Fraction ticks = cr->actualTicks();
for (int i = 0; i < (actualNotes-1); ++i) {
tick += ticks;
Rest* rest = new Rest(this);
rest->setTuplet(tuplet);
rest->setTrack(track);
rest->setDurationType(tuplet->baseLen());
rest->setTicks(tuplet->baseLen().fraction());
undoAddCR(rest, measure, tick);
}
}
//---------------------------------------------------------
// colorItem
//---------------------------------------------------------
void Score::colorItem(Element* element)
{
QColor sc(element->color());
QColor c = QColorDialog::getColor(sc);
if (!c.isValid())
return;
for (Element* e : selection().elements()) {
if (e->color() != c) {
e->undoChangeProperty(Pid::COLOR, c);
e->setGenerated(false);
addRefresh(e->abbox());
if (e->isBarLine()) {
// Element* ep = e->parent();
// if (ep->isSegment() && toSegment(ep)->isEndBarLineType()) {
// Measure* m = toSegment(ep)->measure();
// BarLine* bl = toBarLine(e);
// m->setEndBarLineType(bl->barLineType(), false, e->visible(), e->color());
// }
}
}
}
deselectAll();
}
//---------------------------------------------------------
// cmdExchangeVoice
//---------------------------------------------------------
void Score::cmdExchangeVoice(int s, int d)
{
if (!selection().isRange()) {
MScore::setError(NO_STAFF_SELECTED);
return;
}
Fraction t1 = selection().tickStart();
Fraction t2 = selection().tickEnd();
Measure* m1 = tick2measure(t1);
Measure* m2 = tick2measure(t2);
if (selection().score()->excerpt())
return;
if (t2 > m2->tick())
m2 = m2->nextMeasure();
for (;;) {
undoExchangeVoice(m1, s, d, selection().staffStart(), selection().staffEnd());
m1 = m1->nextMeasure();
if ((m1 == 0) || (m2 && (m1->tick() == m2->tick())))
break;
}
}
//---------------------------------------------------------
// cmdEnterRest
//---------------------------------------------------------
void Score::cmdEnterRest(const TDuration& d)
{
if (_is.track() == -1) {
qDebug("cmdEnterRest: track -1");
return;
}
startCmd();
expandVoice();
if (_is.cr() == 0) {
qDebug("cannot enter rest here");
return;
}
int track = _is.track();
NoteVal nval;
setNoteRest(_is.segment(), track, nval, d.fraction(), Direction::AUTO);
_is.moveToNextInputPos();
if (!noteEntryMode() || usingNoteEntryMethod(NoteEntryMethod::STEPTIME))
_is.setRest(false); // continue with normal note entry
endCmd();
}
//---------------------------------------------------------
// removeChordRest
// remove chord or rest
// remove associated segment if empty
// remove beam
// remove slurs
//---------------------------------------------------------
void Score::removeChordRest(ChordRest* cr, bool clearSegment)
{
QList<Segment*> segments;
for (ScoreElement* e : cr->linkList()) {
undo(new RemoveElement(static_cast<Element*>(e)));
if (clearSegment) {
Segment* s = cr->segment();
if (!segments.contains(s))
segments.append(s);
}
}
for (Segment* s : segments) {
if (s->empty())
undo(new RemoveElement(s));
}
if (cr->beam()) {
Beam* beam = cr->beam();
if (beam->generated()) {
beam->parent()->remove(beam);
delete beam;
}
else
undoRemoveElement(beam);
}
}
//---------------------------------------------------------
// cmdDeleteTuplet
// remove tuplet and replace with rest
//---------------------------------------------------------
void Score::cmdDeleteTuplet(Tuplet* tuplet, bool replaceWithRest)
{
foreach(DurationElement* de, tuplet->elements()) {
if (de->isChordRest())
removeChordRest(toChordRest(de), true);
else {
Q_ASSERT(de->isTuplet());
cmdDeleteTuplet(toTuplet(de), false);
}
}
if (replaceWithRest)
setRest(tuplet->tick(), tuplet->track(), tuplet->ticks(), true, tuplet->tuplet());
}
//---------------------------------------------------------
// nextInputPos
//---------------------------------------------------------
void Score::nextInputPos(ChordRest* cr, bool doSelect)
{
ChordRest* ncr = nextChordRest(cr);
if ((ncr == 0) && (_is.track() % VOICES)) {
Segment* s = tick2segment(cr->tick() + cr->actualTicks(), false, SegmentType::ChordRest);
int track = (cr->track() / VOICES) * VOICES;
ncr = s ? toChordRest(s->element(track)) : 0;
}
if (ncr) {
_is.setSegment(ncr->segment());
if (doSelect)
select(ncr, SelectType::SINGLE, 0);
setPlayPos(ncr->tick());
for (MuseScoreView* v : viewer)
v->moveCursor();
}
}
//---------------------------------------------------------
// insertMeasure
// Create a new MeasureBase of type type and insert
// before measure.
// If measure is zero, append new MeasureBase.
//---------------------------------------------------------
void Score::insertMeasure(ElementType type, MeasureBase* measure, bool createEmptyMeasures, bool moveSignaturesClef)
{
Fraction tick;
if (measure) {
if (measure->isMeasure() && toMeasure(measure)->isMMRest()) {
measure = toMeasure(measure)->prev();
measure = measure ? measure->next() : firstMeasure();
deselectAll();
}
tick = measure->tick();
}
else
tick = last() ? last()->endTick() : Fraction(0,1);
Fraction f = sigmap()->timesig(tick.ticks()).nominal(); // use nominal time signature of current measure
Measure* om = 0; // measure base in "this" score
MeasureBase* rmb = 0; // measure base in root score (for linking)
Fraction ticks = { 0, 1 };
for (Score* score : scoreList()) {
MeasureBase* im = 0;
if (measure) {
if (measure->isMeasure())
im = score->tick2measure(tick);
else {
if (!measure->links()) {
if (measure->score() == score)
im = measure;
else
qDebug("no links");
}
else {
for (ScoreElement* m : *measure->links()) {
if (measure->score() == score) {
im = toMeasureBase(m);
break;
}
}
}
}
if (!im)
qDebug("measure not found");
}
MeasureBase* mb = toMeasureBase(Element::create(type, score));
mb->setTick(tick);
mb->setNext(im);
mb->setPrev(im ? im->prev() : score->last());
if (mb->isMeasure()) {
Measure* m = toMeasure(mb);
m->setTimesig(f);
m->setTicks(f);
}
undo(new InsertMeasures(mb, mb));
if (type == ElementType::MEASURE) {
Measure* m = toMeasure(mb); // new measure
ticks = m->ticks();
Measure* mi = nullptr; // insert before
if (im) {
if (im->isMeasure())
mi = toMeasure(im);
else
mi = score->tick2measure(im->tick());
}
if (score->isMaster())
om = m;
QList<TimeSig*> tsl;
QList<KeySig*> ksl;
QList<Clef*> cl;
QList<Clef*> pcl;
//
// remove clef, time and key signatures
//
if (moveSignaturesClef && mi) {
for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
Measure* pm = mi->prevMeasure();
if (pm) {
Segment* ps = pm->findSegment(SegmentType::Clef, tick);
if (ps && ps->enabled()) {
Element* pc = ps->element(staffIdx * VOICES);
if (pc) {
pcl.push_back(toClef(pc));
undo(new RemoveElement(pc));
if (ps->empty())
undoRemoveElement(ps);
}
}
}
for (Segment* s = mi->first(); s && s->rtick().isZero(); s = s->next()) {
if (!s->enabled())
continue;
Element* e = s->element(staffIdx * VOICES);
if (!e || e->generated())
continue;
Element* ee = 0;
if (e->isKeySig()) {
KeySig* ks = toKeySig(e);
ksl.push_back(ks);
ee = e;
}
else if (e->isTimeSig()) {
TimeSig* ts = toTimeSig(e);
tsl.push_back(ts);
ee = e;
}
if (tick.isZero() && e->isClef()) {
Clef* clef = toClef(e);
cl.push_back(clef);
ee = e;
}
if (ee) {
undo(new RemoveElement(ee));
if (s->empty())
undoRemoveElement(s);
}
}
}
}
//
// move clef, time, key signatrues
//
for (TimeSig* ts : tsl) {
TimeSig* nts = new TimeSig(*ts);
Segment* s = m->undoGetSegmentR(SegmentType::TimeSig, Fraction(0,1));
nts->setParent(s);
undoAddElement(nts);
}
for (KeySig* ks : ksl) {
KeySig* nks = new KeySig(*ks);
Segment* s = m->undoGetSegmentR(SegmentType::KeySig, Fraction(0,1));
nks->setParent(s);
undoAddElement(nks);
}
for (Clef* clef : cl) {
Clef* nClef = new Clef(*clef);
Segment* s = m->undoGetSegmentR(SegmentType::HeaderClef, Fraction(0,1));
nClef->setParent(s);
undoAddElement(nClef);
}
Measure* pm = m->prevMeasure();
for (Clef* clef : pcl) {
Clef* nClef = new Clef(*clef);
Segment* s = pm->undoGetSegment(SegmentType::Clef, tick);
nClef->setParent(s);
undoAddElement(nClef);
}
}
else {
// a frame, not a measure
if (score->isMaster())
rmb = mb;
else if (rmb && mb != rmb) {
mb->linkTo(rmb);
if (rmb->isTBox())
toTBox(mb)->text()->linkTo(toTBox(rmb)->text());
}
}
}
undoInsertTime(tick, ticks);
if (om && !createEmptyMeasures) {
//
// fill measure with rest
//
Score* score = om->score();
// add rest to all staves and to all the staves linked to it
for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
int track = staffIdx * VOICES;
Rest* rest = new Rest(score, TDuration(TDuration::DurationType::V_MEASURE));
Fraction timeStretch(score->staff(staffIdx)->timeStretch(om->tick()));
rest->setTicks(om->ticks() * timeStretch);
rest->setTrack(track);
score->undoAddCR(rest, om, tick);
}
}
deselectAll();
}
//---------------------------------------------------------
// checkSpanner
// check if spanners are still valid as anchors may
// have changed or be removed.
// Spanners need to have a start anchor. Slurs need a
// start and end anchor.
//---------------------------------------------------------
void Score::checkSpanner(const Fraction& startTick, const Fraction& endTick)
{
QList<Spanner*> sl; // spanners to remove
QList<Spanner*> sl2; // spanners to shorten
auto spanners = _spanner.findOverlapping(startTick.ticks(), endTick.ticks());
// DEBUG: check all spanner
// there may be spanners outside of score bc. some measures were deleted
Fraction lastTick = lastMeasure()->endTick();
for (auto i : _spanner.map()) {
Spanner* s = i.second;
if (s->isSlur()) {
Segment* seg = tick2segmentMM(s->tick(), false, SegmentType::ChordRest);
if (!seg || !seg->element(s->track()))
sl.append(s);
else {
seg = tick2segmentMM(s->tick2(), false, SegmentType::ChordRest);
if (!seg || !seg->element(s->track2()))
sl.append(s);
}
}
else {
// remove spanner if there is no start element
s->computeStartElement();
if (!s->startElement()) {
sl.append(s);
qDebug("checkSpanner::remove (3)");
}
else {
if (s->tick2() > lastTick)
sl2.append(s); //s->undoChangeProperty(Pid::SPANNER_TICKS, lastTick - s->tick());
else
s->computeEndElement();
}
}
}
for (auto s : sl) // actually remove scheduled spanners
undo(new RemoveElement(s));
for (auto s : sl2) { // shorten spanners that extended past end of score
undo(new ChangeProperty(s, Pid::SPANNER_TICKS, lastTick - s->tick()));
s->computeEndElement();
}
}
static constexpr SegmentType CR_TYPE = SegmentType::ChordRest;
//---------------------------------------------------------
// checkTimeDelete
//---------------------------------------------------------
bool Score::checkTimeDelete(Segment* startSegment, Segment* endSegment)
{
Measure* m = startSegment->measure();
Measure* endMeasure;
if (endSegment)
endMeasure = endSegment->prev() ? endSegment->measure() : endSegment->measure()->prevMeasure();
else
endMeasure = lastMeasure();
Fraction endTick = endSegment ? endSegment->tick() : endMeasure->endTick();
Fraction tick = startSegment->tick();
Fraction etick = (m == endMeasure ? endTick : m->endTick());
bool canDeleteTime = true;
while (canDeleteTime) {
for (int track = 0; canDeleteTime && track < _staves.size() * VOICES; ++track) {
if (m->hasVoice(track)) {
Segment* fs = m->first(CR_TYPE);
for (Segment* s = fs; s; s = s->next(CR_TYPE)) {
if (s->element(track)) {
ChordRest* cr = toChordRest(s->element(track));
Tuplet* t = cr->tuplet();
DurationElement* de = t ? toDurationElement(t) : toDurationElement(cr);
Fraction f = de->tick() + de->actualTicks();
Fraction cetick = f;
if (cetick <= tick)
continue;
if (de->tick() >= etick)
break;
if (t && (t->tick() < tick || cetick > etick)) {
canDeleteTime = false;
break;
}
}
}
}
}
if (m == endMeasure)
break;
m = endMeasure;
tick = m->tick();
etick = endTick;
}
if (!canDeleteTime) {
QMessageBox::information(0, "MuseScore",
tr("Please select the complete tuplet and retry the command"),
QMessageBox::Ok, QMessageBox::NoButton);
return false;
}
return true;
}
//---------------------------------------------------------
// globalTimeDelete
//---------------------------------------------------------
void Score::globalTimeDelete()
{
qDebug("not implemented");
}
//---------------------------------------------------------
// localTimeDelete
//---------------------------------------------------------
void Score::localTimeDelete()
{
Segment* startSegment;
Segment* endSegment;
if (selection().state() != SelState::RANGE) {
Element* el = selection().element();
if (!el)
return;
ChordRest* cr = 0;
if (el->isNote())
cr = toNote(el)->chord();
else if (el->isChordRest())
cr = toChordRest(el);
else
return;
startSegment = cr->segment();
Fraction endTick = startSegment->tick() + cr->actualTicks();
endSegment = tick2measure(endTick)->findSegment(CR_TYPE, endTick);
}
else {
startSegment = selection().startSegment();
endSegment = selection().endSegment();
}
if (!checkTimeDelete(startSegment, endSegment))
return;
MeasureBase* is = startSegment->measure();
if (is->isMeasure() && toMeasure(is)->isMMRest())
is = toMeasure(is)->mmRestFirst();
MeasureBase* ie;
if (endSegment)
ie = endSegment->prev() ? endSegment->measure() : endSegment->measure()->prev();
else
ie = lastMeasure();
Fraction endTick = endSegment ? endSegment->tick() : ie->endTick();
for (;;) {
if (is->tick() != startSegment->tick()) {
Fraction tick = startSegment->tick();
Fraction len;
if (ie == is)
len = endTick - tick;
else
len = is->endTick() - tick;
timeDelete(toMeasure(is), startSegment, len);
if (is == ie)
break;
is = is->next();
}
endTick = endSegment ? endSegment->tick() : ie->endTick();
if (ie->endTick() != endTick) {
Fraction len = endTick - ie->tick();
timeDelete(toMeasure(ie), toMeasure(ie)->first(), len);
if (is == ie)
break;
ie = ie->prev();
}
deleteMeasures(is, ie);
break;
};
deselectAll();
}
//---------------------------------------------------------
// timeDelete
//---------------------------------------------------------
void Score::timeDelete(Measure* m, Segment* startSegment, const Fraction& f)
{
const Fraction tick = startSegment->rtick();
const Fraction len = f;
const Fraction etick = tick + len;
Segment* fs = m->first(CR_TYPE);
for (int track = 0; track < _staves.size() * VOICES; ++track) {
if (m->hasVoice(track)) {
for (Segment* s = fs; s; s = s->next(CR_TYPE)) {
if (s->element(track)) {
ChordRest* cr = toChordRest(s->element(track));
Fraction cetick = cr->rtick() + cr->actualTicks();
if (cetick <= tick)
continue;
if (s->rtick() >= etick)
break;
if (cr->isFullMeasureRest()) {
if (cr->rtick() >= tick) {
// Move full-measure rest from the deleted area
undoRemoveElement(cr);
ChordRest* newCR = toChordRest(cr->clone());
newCR->setTicks(cr->ticks() - f);
undoAddCR(newCR, m, m->tick() + etick);
}
else
cr->undoChangeProperty(Pid::DURATION, cr->ticks() - f);
}
// inside deleted area
else if (s->rtick() >= tick && cetick <= etick) {
// inside
undoRemoveElement(cr);
}
else if (s->rtick() >= tick) {
// running out
Fraction ff = cetick - etick;
undoRemoveElement(cr);
createCRSequence(ff, cr, tick + len);
}
else if (s->rtick() < tick && cetick <= etick) {
// running in
Fraction f1 = tick - s->rtick();
changeCRlen(cr, f1, false);
}
else {
// running in/out
Fraction f1 = cr->ticks() - f;
changeCRlen(cr, f1, false);
}
}
}
}
}
const Fraction abstick = startSegment->tick();
undoInsertTime(abstick, -len);
std::vector<Segment*> emptySegments;
for (Score* score : masterScore()->scoreList()) {
Measure* localMeasure = score->tick2measure(abstick);
undo(new InsertTime(score, abstick, -len));
Fraction updatedTick = tick;
for (Segment* s = localMeasure->first(CR_TYPE); s; s = s->next()) {
if (s->rtick() < etick || s->rtick() == updatedTick)
continue;
s->undoChangeProperty(Pid::TICK, updatedTick);
updatedTick += s->ticks();
if (score->isMaster()) {
if (s->isChordRestType() && !s->hasElements())
emptySegments.push_back(s);
}
}
undo(new ChangeMeasureLen(localMeasure, localMeasure->ticks() - f));
}
for (Segment* s : emptySegments) {
if (Segment* ns = s->next(CR_TYPE)) {
// Move annotations from the empty segment.
// TODO: do we need to preserve annotations at all?
// Maybe only some types (Tempo etc.)?
for (Element* a : s->annotations()) {
Element* a1 = a->clone();
a1->setParent(ns);
undoRemoveElement(a);
undoAddElement(a1);
}
}
}
}
//---------------------------------------------------------
// cloneVoice
//---------------------------------------------------------
void Score::cloneVoice(int strack, int dtrack, Segment* sf, const Fraction& lTick, bool link, bool spanner)
{
Fraction start = sf->tick();
TieMap tieMap;
TupletMap tupletMap; // tuplets cannot cross measure boundaries
Score* score = sf->score();
Tremolo* tremolo = 0;
for (Segment* oseg = sf; oseg && oseg->tick() < lTick; oseg = oseg->next1()) {
Segment* ns = 0; //create segment later, on demand
Measure* dm = tick2measure(oseg->tick());
Element* oe = oseg->element(strack);
if (oe && !oe->generated() && oe->isChordRest()) {
Element* ne;
//does a linked clone to create just this element
//otherwise element will be add in every linked stave
if (link)
ne = oe->linkedClone();
else
ne = oe->clone();
ne->setTrack(dtrack);
//Don't clone gaps to a first voice
if (!(ne->track() % VOICES) && ne->isRest())
toRest(ne)->setGap(false);
ne->setScore(this);
ChordRest* ocr = toChordRest(oe);
ChordRest* ncr = toChordRest(ne);
//Handle beams
if (ocr->beam() && !ocr->beam()->empty() && ocr->beam()->elements().front() == ocr) {
Beam* nb = ocr->beam()->clone();
nb->clear();
nb->setTrack(dtrack);
nb->setScore(this);
nb->add(ncr);
ncr->setBeam(nb);
}
// clone Tuplets
Tuplet* ot = ocr->tuplet();
if (ot) {
ot->setTrack(strack);
Tuplet* nt = tupletMap.findNew(ot);
if (nt == 0) {
if (link)
nt = toTuplet(ot->linkedClone());
else
nt = toTuplet(ot->clone());
nt->setTrack(dtrack);
nt->setParent(dm);
tupletMap.add(ot, nt);
Tuplet* nt1 = nt;
while (ot->tuplet()) {
Tuplet* nt2 = tupletMap.findNew(ot->tuplet());
if (nt2 == 0) {
if (link)
nt2 = toTuplet(ot->tuplet()->linkedClone());
else
nt2 = toTuplet(ot->tuplet()->clone());
nt2->setTrack(dtrack);
nt2->setParent(dm);
tupletMap.add(ot->tuplet(), nt2);
}
nt2->add(nt1);
nt1->setTuplet(nt2);
ot = ot->tuplet();
nt1 = nt2;
}
}
nt->add(ncr);
ncr->setTuplet(nt);
}
// clone additional settings
if (oe->isChordRest()) {
if (oe->isRest()) {
Rest* ore = toRest(ocr);
// If we would clone a full measure rest just don't clone this rest
if (ore->isFullMeasureRest() && (dtrack % VOICES)) {
continue;
}
}
if (oe->isChord()) {
Chord* och = toChord(ocr);
Chord* nch = toChord(ncr);
size_t n = och->notes().size();
for (size_t i = 0; i < n; ++i) {
Note* on = och->notes().at(i);
Note* nn = nch->notes().at(i);
int idx = track2staff(dtrack);
Fraction tick = oseg->tick();
Interval v = staff(idx) ? staff(idx)->part()->instrument(tick)->transpose() : Interval();
nn->setTpc1(on->tpc1());
if (v.isZero())
nn->setTpc2(on->tpc1());
else {
v.flip();
nn->setTpc2(Ms::transposeTpc(nn->tpc1(), v, true));
}
if (on->tieFor()) {
Tie* tie;
if (link)
tie = toTie(on->tieFor()->linkedClone());
else
tie = toTie(on->tieFor()->clone());
tie->setScore(this);
nn->setTieFor(tie);
tie->setStartNote(nn);
tie->setTrack(nn->track());
tie->setEndNote(nn);
tieMap.add(on->tieFor(), tie);
}
if (on->tieBack()) {
Tie* tie = tieMap.findNew(on->tieBack());
if (tie) {
nn->setTieBack(tie);
tie->setEndNote(nn);
}
else {
qDebug("cloneVoices: cannot find tie");
}
}
// add back spanners (going back from end to start spanner element
// makes sure the 'other' spanner anchor element is already set up)
// 'on' is the old spanner end note and 'nn' is the new spanner end note
for (Spanner* oldSp : on->spannerBack()) {
Note* newStart = Spanner::startElementFromSpanner(oldSp, nn);
if (newStart) {
Spanner* newSp;
if (link)
newSp = toSpanner(oldSp->linkedClone());
else
newSp = toSpanner(oldSp->clone());
newSp->setNoteSpan(newStart, nn);
addElement(newSp);
}
else {
qDebug("cloneVoices: cannot find spanner start note");
}
}
}
// two note tremolo
if (och->tremolo() && och->tremolo()->twoNotes()) {
if (och == och->tremolo()->chord1()) {
if (tremolo)
qDebug("unconnected two note tremolo");
if (link)
tremolo = toTremolo(och->tremolo()->linkedClone());
else
tremolo = toTremolo(och->tremolo()->clone());
tremolo->setScore(nch->score());
tremolo->setParent(nch);
tremolo->setTrack(nch->track());
tremolo->setChords(nch, 0);
nch->setTremolo(tremolo);
}
else if (och == och->tremolo()->chord2()) {
if (!tremolo)
qDebug("first note for two note tremolo missing");
else {
tremolo->setChords(tremolo->chord1(), nch);
nch->setTremolo(tremolo);
}
}
else
qDebug("inconsistent two note tremolo");
}
}
// Add element (link -> just in this measure)
if (link) {
if (!ns)
ns = dm->getSegment(oseg->segmentType(), oseg->tick());
ns->add(ne);
}
else {
undoAddCR(toChordRest(ne), dm, oseg->tick());
}
}
}
Segment* tst = dm->segments().firstCRSegment();
if (strack % VOICES && !(dtrack % VOICES) && (!tst || (!tst->element(dtrack)))) {
Rest* rest = new Rest(this);
rest->setTicks(dm->ticks());
rest->setDurationType(TDuration::DurationType::V_MEASURE);
rest->setTrack(dtrack);
if (link) {
Segment* segment = dm->getSegment(SegmentType::ChordRest, dm->tick());
segment->add(rest);
}
else
undoAddCR(toChordRest(rest), dm, dm->tick());
}
}
if (spanner) {
// Find and add corresponding slurs
auto spanners = score->spannerMap().findOverlapping(start.ticks(), lTick.ticks());
for (auto i = spanners.begin(); i < spanners.end(); i++) {
Spanner* sp = i->value;
Fraction spStart = sp->tick();
int track = sp->track();
int track2 = sp->track2();
Fraction spEnd = spStart + sp->ticks();
if (sp->isSlur() && (spStart >= start && spEnd < lTick)) {
if (track == strack && track2 == strack){
Spanner* ns = toSpanner(link ? sp->linkedClone() : sp->clone());
ns->setScore(this);
ns->setParent(0);
ns->setTrack(dtrack);
ns->setTrack2(dtrack);
// set start/end element for slur
ChordRest* cr1 = sp->startCR();
ChordRest* cr2 = sp->endCR();
ns->setStartElement(0);
ns->setEndElement(0);
if (cr1 && cr1->links()) {
for (ScoreElement* e : *cr1->links()) {
ChordRest* cr = toChordRest(e);
if (cr == cr1)
continue;
if ((cr->score() == this) && (cr->tick() == ns->tick()) && cr->track() == dtrack) {
ns->setStartElement(cr);
break;
}
}
}
if (cr2 && cr2->links()) {
for (ScoreElement* e : *cr2->links()) {
ChordRest* cr = toChordRest(e);
if (cr == cr2)
continue;
if ((cr->score() == this) && (cr->tick() == ns->tick2()) && cr->track() == dtrack) {
ns->setEndElement(cr);
break;
}
}
}
undo(new AddElement(ns));
}
}
}
}
//Layout
//TODO ?? doLayoutRange(start, lTick);
}
//---------------------------------------------------------
// undoPropertyChanged
// return true if an property was actually changed
//---------------------------------------------------------
bool Score::undoPropertyChanged(Element* e, Pid t, const QVariant& st, PropertyFlags ps)
{
bool changed = false;
if (propertyLink(t) && e->links()) {
for (ScoreElement* ee : *e->links()) {
if (ee == e) {
if (ee->getProperty(t) != st) {
undoStack()->push1(new ChangeProperty(ee, t, st, ps));
changed = true;
}
}
else {
// property in linked element has not changed yet
// push() calls redo() to change it
if (ee->getProperty(t) != e->getProperty(t)) {
undoStack()->push(new ChangeProperty(ee, t, e->getProperty(t), ps), 0);
changed = true;
}
}
}
}
else {
PropertyFlags po = e->propertyFlags(t);
if ((e->getProperty(t) != st) || (ps != po)) {
e->setPropertyFlags(t, ps);
undoStack()->push1(new ChangeProperty(e, t, st, po));
changed = true;
}
}
return changed;
}
void Score::undoPropertyChanged(ScoreElement* e, Pid t, const QVariant& st, PropertyFlags ps)
{
if (e->getProperty(t) != st)
undoStack()->push1(new ChangeProperty(e, t, st, ps));
}
//---------------------------------------------------------
// undoChangeStyleVal
//---------------------------------------------------------
void Score::undoChangeStyleVal(Sid idx, const QVariant& v)
{
undo(new ChangeStyleVal(this, idx, v));
}
//---------------------------------------------------------
// undoChangePageNumberOffset
//---------------------------------------------------------
void Score::undoChangePageNumberOffset(int po)
{
undo(new ChangePageNumberOffset(this, po));
}
//---------------------------------------------------------
// undoChangeElement
//---------------------------------------------------------
void Score::undoChangeElement(Element* oldElement, Element* newElement)
{
if (!oldElement)
undoAddElement(newElement);
else
undo(new ChangeElement(oldElement, newElement));
}
//---------------------------------------------------------
// undoChangePitch
//---------------------------------------------------------
void Score::undoChangePitch(Note* note, int pitch, int tpc1, int tpc2)
{
for (ScoreElement* e : note->linkList()) {
Note* n = toNote(e);
undoStack()->push(new ChangePitch(n, pitch, tpc1, tpc2), 0);
}
}
//---------------------------------------------------------
// undoChangeFretting
//
// To use with tablatures to force a specific note fretting;
// Pitch, string and fret must be changed all together; otherwise,
// if they are not consistent among themselves, the refretting algorithm may re-assign
// fret and string numbers for (potentially) all the notes of all the chords of a segment.
//---------------------------------------------------------
void Score::undoChangeFretting(Note* note, int pitch, int string, int fret, int tpc1, int tpc2)
{
const LinkedElements* l = note->links();
if (l) {
for (ScoreElement* e : *l) {
Note* n = toNote(e);
undo(new ChangeFretting(n, pitch, string, fret, tpc1, tpc2));
}
}
else
undo(new ChangeFretting(note, pitch, string, fret, tpc1, tpc2));
}
//---------------------------------------------------------
// undoChangeKeySig
//---------------------------------------------------------
void Score::undoChangeKeySig(Staff* ostaff, const Fraction& tick, KeySigEvent key)
{
KeySig* lks = 0;
for (Staff* staff : ostaff->staffList()) {
if (staff->isDrumStaff(tick))
continue;
Score* score = staff->score();
Measure* measure = score->tick2measure(tick);
KeySigEvent currentKeySigEvent = staff->keySigEvent(tick);
if (!measure) {
qWarning("measure for tick %d not found!", tick.ticks());
continue;
}
Segment* s = measure->undoGetSegment(SegmentType::KeySig, tick);
int staffIdx = staff->idx();
int track = staffIdx * VOICES;
KeySig* ks = toKeySig(s->element(track));
Interval interval = staff->part()->instrument(tick)->transpose();
KeySigEvent nkey = key;
bool concertPitch = score->styleB(Sid::concertPitch);
if (interval.chromatic && !concertPitch && !nkey.custom() && !nkey.isAtonal()) {
interval.flip();
nkey.setKey(transposeKey(key.key(), interval, staff->part()->preferSharpFlat()));
}
updateInstrumentChangeTranspositions(key, staff, tick);
if (ks) {
ks->undoChangeProperty(Pid::GENERATED, false);
undo(new ChangeKeySig(ks, nkey, ks->showCourtesy()));
}
else {
// do not create empty keysig unless custom or atonal
if (tick.isNotZero() || nkey.key() != Key::C || nkey.custom() || nkey.isAtonal()) {
KeySig* nks = new KeySig(score);
nks->setParent(s);
nks->setTrack(track);
nks->setKeySigEvent(nkey);
undo(new AddElement(nks));
if (lks)
undo(new Link(lks, nks));
else
lks = nks;
}
}
}
}
void Score::updateInstrumentChangeTranspositions(KeySigEvent& key, Staff* staff, const Fraction& tick)
{
if (!key.forInstrumentChange()) {
KeyList* kl = staff->keyList();
int nextTick = kl->nextKeyTick(tick.ticks());
while (nextTick != -1) {
KeySigEvent e = kl->key(nextTick);
if (e.forInstrumentChange()) {
Measure* m = tick2measure(Fraction::fromTicks(nextTick));
Segment* s = m->tick2segment(Fraction::fromTicks(nextTick), SegmentType::KeySig);
int track = staff->idx() * VOICES;
KeySig* keySig = toKeySig(s->element(track));
if (key.isAtonal() && !e.isAtonal()) {
e.setMode(KeyMode::NONE);
e.setKey(Key::C);
}
else {
e.setMode(key.mode());
Interval transposeInterval = staff->part()->instrument(Fraction::fromTicks(nextTick))->transpose();
Interval previousTranspose = staff->part()->instrument(tick)->transpose();
transposeInterval.flip();
Key nkey = transposeKey(key.key(), transposeInterval);
nkey = transposeKey(nkey, previousTranspose);
e.setKey(nkey);
}
undo(new ChangeKeySig(keySig, e, keySig->showCourtesy()));
nextTick = kl->nextKeyTick(nextTick);
}
else
nextTick = -1;
}
}
}
//---------------------------------------------------------
// undoChangeClef
// change clef if e is a clef
// else
// create a clef before element e
//---------------------------------------------------------
void Score::undoChangeClef(Staff* ostaff, Element* e, ClefType ct, bool forInstrumentChange)
{
bool moveClef = false;
SegmentType st = SegmentType::Clef;
if (e->isMeasure()) {
if (toMeasure(e)->prevMeasure())
moveClef = true;
else
st = SegmentType::HeaderClef;
}
else if (e->isClef()) {
Clef* clef = toClef(e);
if (clef->segment()->isHeaderClefType()) {
if (clef->measure()->prevMeasure())
moveClef = true;
else
st = SegmentType::HeaderClef;
}
else if (clef->rtick() == clef->measure()->ticks())
moveClef = true;
}
Clef* gclef = 0;
Fraction tick = e->tick();
Fraction rtick = e->rtick();
bool small = (st == SegmentType::Clef);
for (Staff* staff : ostaff->staffList()) {
if (staff->staffType(tick)->group() != ClefInfo::staffGroup(ct))
continue;
Score* score = staff->score();
Measure* measure = score->tick2measure(tick);
if (!measure) {
qWarning("measure for tick %d not found!", tick.ticks());
continue;
}
Segment* destSeg;
Fraction rt;
if (moveClef) { // if at start of measure and there is a previous measure
measure = measure->prevMeasure();
rt = measure->ticks();
}
else
rt = rtick;
destSeg = measure->undoGetSegmentR(st, rt);
int staffIdx = staff->idx();
int track = staffIdx * VOICES;
Clef* clef = toClef(destSeg->element(track));
if (clef) {
//
// for transposing instruments, differentiate
// clef type for concertPitch
//
Instrument* i = staff->part()->instrument(tick);
ClefType cp, tp;
if (i->transpose().isZero()) {
cp = ct;
tp = ct;
}
else {
bool concertPitch = clef->concertPitch();
if (concertPitch) {
cp = ct;
tp = clef->transposingClef();
}
else {
cp = clef->concertClef();
tp = ct;
}
}
clef->setGenerated(false);
score->undo(new ChangeClefType(clef, cp, tp));
Clef* oClef = clef->otherClef();
if (oClef && !(oClef->generated()))
score->undo(new ChangeClefType(oClef, cp, tp));
// change the clef in the mmRest if any
if (measure->hasMMRest()) {
Measure* mmMeasure = measure->mmRest();
Segment* mmDestSeg = mmMeasure->findSegment(SegmentType::Clef, tick);
if (mmDestSeg) {
Clef* mmClef = toClef(mmDestSeg->element(clef->track()));
if (mmClef)
score->undo(new ChangeClefType(mmClef, cp, tp));
}
}
}
else {
if (gclef) {
clef = toClef(gclef->linkedClone());
clef->setScore(score);
}
else {
clef = new Clef(score);
gclef = clef;
}
clef->setTrack(track);
clef->setClefType(ct);
clef->setParent(destSeg);
score->undo(new AddElement(clef));
clef->layout();
}
if (forInstrumentChange) {
clef->setForInstrumentChange(true);
}
clef->setSmall(small);
}
}
//---------------------------------------------------------
// findLinkedVoiceElement
//---------------------------------------------------------
static Element* findLinkedVoiceElement(Element* e, Staff* nstaff)
{
Excerpt* se = e->score()->excerpt();
Excerpt* de = nstaff->score()->excerpt();
int strack = e->track();
int dtrack = nstaff->idx() * VOICES + e->voice();
if (se)
strack = se->tracks().key(strack);
if (de) {
QList<int> l = de->tracks().values(strack);
if (l.isEmpty()) {
// simply return the first linked element whose staff is equal to nstaff
for (ScoreElement* ee : e->linkList()) {
Element* el = toElement(ee);
if (el->staff() == nstaff)
return el;
}
return 0;
}
for (int i : l) {
if (nstaff->idx() * VOICES <= i && (nstaff->idx() + 1) * VOICES > i) {
dtrack = i;
break;
}
}
}
Score* score = nstaff->score();
Segment* segment = toSegment(e->parent());
Measure* measure = segment->measure();
Measure* m = score->tick2measure(measure->tick());
Segment* s = m->findSegment(segment->segmentType(), segment->tick());
return s->element(dtrack);
}
//---------------------------------------------------------
// findLinkedChord
//---------------------------------------------------------
static Chord* findLinkedChord(Chord* c, Staff* nstaff)
{
Excerpt* se = c->score()->excerpt();
Excerpt* de = nstaff->score()->excerpt();
int strack = c->track();
int dtrack = nstaff->idx() * VOICES + c->voice();
if (se)
strack = se->tracks().key(strack);
if (de) {
QList<int> l = de->tracks().values(strack);
if (l.isEmpty()) {
// simply return the first linked chord whose staff is equal to nstaff
for (ScoreElement* ee : c->linkList()) {
Chord* ch = toChord(ee);
if (ch->staff() == nstaff)
return ch;
}
return 0;
}
for (int i : l) {
if (nstaff->idx() * VOICES <= i && (nstaff->idx() + 1) * VOICES > i) {
dtrack = i;
break;
}
}
}
Segment* s = c->segment();
Measure* nm = nstaff->score()->tick2measure(s->tick());
Segment* ns = nm->findSegment(s->segmentType(), s->tick());
Element* ne = ns->element(dtrack);
if (!ne->isChord())
return 0;
Chord* nc = toChord(ne);
if (c->isGrace()) {
Chord* pc = toChord(c->parent());
int index = 0;
for (Chord* gc : pc->graceNotes()) {
if (c == gc)
break;
index++;
}
if (index < nc->graceNotes().length())
nc = nc->graceNotes().at(index);
}
return nc;
}
//---------------------------------------------------------
// undoChangeChordRestLen
//---------------------------------------------------------
void Score::undoChangeChordRestLen(ChordRest* cr, const TDuration& d)
{
auto sl = cr->staff()->staffList();
for (Staff* staff : sl) {
ChordRest *ncr;
if (cr->isGrace())
ncr = findLinkedChord(toChord(cr), staff);
else
ncr = toChordRest(findLinkedVoiceElement(cr, staff));
if (!ncr)
continue;
ncr->undoChangeProperty(Pid::DURATION_TYPE, QVariant::fromValue(d));
ncr->undoChangeProperty(Pid::DURATION, QVariant::fromValue(d.fraction()));
}
}
//---------------------------------------------------------
// undoTransposeHarmony
//---------------------------------------------------------
void Score::undoTransposeHarmony(Harmony* h, int rootTpc, int baseTpc)
{
undo(new TransposeHarmony(h, rootTpc, baseTpc));
}
//---------------------------------------------------------
// undoExchangeVoice
//---------------------------------------------------------
void Score::undoExchangeVoice(Measure* measure, int srcVoice, int dstVoice, int srcStaff, int dstStaff)
{
Fraction tick = measure->tick();
for (int staffIdx = srcStaff; staffIdx < dstStaff; ++staffIdx) {
QSet<Staff*> staffList;
for (Staff* s : staff(staffIdx)->staffList())
staffList.insert(s);
int srcStaffTrack = staffIdx * VOICES;
int srcTrack = srcStaffTrack + srcVoice;
int dstTrack = srcStaffTrack + dstVoice;
int trackDiff = dstVoice - srcVoice;
//handle score and complete measures first
undo(new ExchangeVoice(measure, srcTrack, dstTrack, staffIdx));
for (Staff* st : staffList) {
int staffTrack = st->idx() * VOICES;
Measure* measure2 = st->score()->tick2measure(tick);
Excerpt* ex = st->score()->excerpt();
if (ex) {
QMultiMap<int, int> tracks = ex->tracks();
QList<int> srcTrackList = tracks.values(srcTrack);
QList<int> dstTrackList = tracks.values(dstTrack);
for (int srcTrack2 : srcTrackList) {
// don't care about other linked staves
if (!(staffTrack <= srcTrack2) || !(srcTrack2 < staffTrack + VOICES))
continue;
int tempTrack = tracks.key(srcTrack2);
QList<int> testTracks = tracks.values(tempTrack + trackDiff);
bool hasVoice = false;
for (int testTrack : testTracks) {
if (staffTrack <= testTrack && testTrack < staffTrack + VOICES && dstTrackList.contains(testTrack)) {
hasVoice = true;
// voice is simply exchangeable now (deal directly)
undo(new ExchangeVoice(measure2, srcTrack2, testTrack, staffTrack / 4));
}
}
// only source voice is in this staff
if (!hasVoice) {
undo(new CloneVoice(measure->first(), measure2->endTick(), measure2->first(), tempTrack, srcTrack2, tempTrack + trackDiff));
srcTrackList.removeOne(srcTrack2);
}
}
for (int dstTrack2 : dstTrackList) {
// don't care about other linked staves
if (!(staffTrack <= dstTrack2) || !(dstTrack2 < staffTrack + VOICES))
continue;
int tempTrack = tracks.key(dstTrack2);
QList<int> testTracks = tracks.values(tempTrack - trackDiff);
bool hasVoice = false;
for (int testTrack : testTracks) {
if (staffTrack <= testTrack && testTrack < staffTrack + VOICES &&
srcTrackList.contains(testTrack))
hasVoice = true;
}
// only destination voice is in this staff
if (!hasVoice) {
undo(new CloneVoice(measure->first(), measure2->endTick(), measure2->first(), tempTrack, dstTrack2, tempTrack - trackDiff));
dstTrackList.removeOne(dstTrack2);
}
}
}
else if (srcStaffTrack != staffTrack) {
// linked staff in same score (all voices present can be assumed)
undo(new ExchangeVoice(measure2, staffTrack + srcVoice, staffTrack + dstVoice, st->idx()));
}
}
}
// make sure voice 0 is complete
if (srcVoice == 0 || dstVoice == 0) {
for (int staffIdx = srcStaff; staffIdx < dstStaff; ++staffIdx) {
// check for complete timeline of voice 0
Fraction ctick = measure->tick();
int track = staffIdx * VOICES;
for (Segment* s = measure->first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
ChordRest* cr = toChordRest(s->element(track));
if (cr == 0)
continue;
if (cr->isRest()) {
Rest* r = toRest(cr);
if (r->isGap())
r->undoChangeProperty(Pid::GAP, false);
}
if (ctick < s->tick())
setRest(ctick, track, s->tick() - ctick, false, 0); // fill gap
ctick = s->tick() + cr->actualTicks();
}
Fraction etick = measure->endTick();
if (ctick < etick)
setRest(ctick, track, etick - ctick, false, 0); // fill gap
}
}
}
//---------------------------------------------------------
// undoRemovePart
//---------------------------------------------------------
void Score::undoRemovePart(Part* part, int idx)
{
undo(new RemovePart(part, idx));
}
//---------------------------------------------------------
// undoInsertPart
//---------------------------------------------------------
void Score::undoInsertPart(Part* part, int idx)
{
undo(new InsertPart(part, idx));
}
//---------------------------------------------------------
// undoRemoveStaff
// idx - index of staff in part
//---------------------------------------------------------
void Score::undoRemoveStaff(Staff* staff)
{
const int idx = staff->idx();
Q_ASSERT(idx >= 0);
std::vector<Spanner*> toRemove;
for (auto i = _spanner.cbegin(); i != _spanner.cend(); ++i) {
Spanner* s = i->second;
if (s->staffIdx() == idx && (idx != 0 || !s->systemFlag()))
toRemove.push_back(s);
}
for (Spanner* s : _unmanagedSpanner) {
if (s->staffIdx() == idx && (idx != 0 || !s->systemFlag()))
toRemove.push_back(s);
}
for (Spanner* s : toRemove) {
s->undoUnlink();
undo(new RemoveElement(s));
}
//
// adjust measures
//
for (Measure* m = staff->score()->firstMeasure(); m; m = m->nextMeasure()) {
m->cmdRemoveStaves(idx, idx+1);
if (m->hasMMRest())
m->mmRest()->cmdRemoveStaves(idx, idx+1);
}
undo(new RemoveStaff(staff));
}
//---------------------------------------------------------
// undoInsertStaff
// idx - index of staff in part
//---------------------------------------------------------
void Score::undoInsertStaff(Staff* staff, int ridx, bool createRests)
{
undo(new InsertStaff(staff, ridx));
int idx = staffIdx(staff->part()) + ridx;
for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
m->cmdAddStaves(idx, idx+1, createRests);
if (m->hasMMRest())
m->mmRest()->cmdAddStaves(idx, idx+1, false);
}
// when newly adding an instrument,
// this was already set when we created the staff
// we don't have any better info at this point
// and it dooesn't work to adjust bracket & barlines until all staves are added
// TODO: adjust brackets only when appropriate
//adjustBracketsIns(idx, idx+1);
}
//---------------------------------------------------------
// undoChangeInvisible
//---------------------------------------------------------
void Score::undoChangeInvisible(Element* e, bool v)
{
e->undoChangeProperty(Pid::VISIBLE, v);
e->setGenerated(false);
}
//---------------------------------------------------------
// undoAddElement
//---------------------------------------------------------
void Score::undoAddElement(Element* element)
{
QList<Staff* > staffList;
Staff* ostaff = element->staff();
int strack = -1;
if (ostaff) {
strack = ostaff->idx() * VOICES + element->track() % VOICES;
if (ostaff->score()->excerpt() && !ostaff->score()->excerpt()->tracks().isEmpty() && strack > -1)
strack = ostaff->score()->excerpt()->tracks().key(strack, -1);
}
ElementType et = element->type();
//
// some elements are replicated for all parts regardless of
// linking:
//
if ((et == ElementType::REHEARSAL_MARK)
|| (et == ElementType::SYSTEM_TEXT)
|| (et == ElementType::JUMP)
|| (et == ElementType::MARKER)
|| (et == ElementType::TEMPO_TEXT)
|| (et == ElementType::VOLTA)
) {
for (Score* s : scoreList())
staffList.append(s->staff(0));
for (Staff* staff : staffList) {
Score* score = staff->score();
int staffIdx = staff->idx();
int ntrack = staffIdx * VOICES;
Element* ne;
if (staff->score() == ostaff->score())
ne = element;
else {
// only create linked volta for first staff
if (et == ElementType::VOLTA && element->track() != 0)
continue;
ne = element->linkedClone();
ne->setScore(score);
ne->setSelected(false);
ne->setTrack(staffIdx * VOICES + element->voice());
}
if (et == ElementType::VOLTA) {
Spanner* nsp = toSpanner(ne);
Spanner* sp = toSpanner(element);
int staffIdx1 = sp->track() / VOICES;
int staffIdx2 = sp->track2() / VOICES;
int diff = staffIdx2 - staffIdx1;
nsp->setTrack2((staffIdx + diff) * VOICES + (sp->track2() % VOICES));
undo(new AddElement(nsp));
}
else if (et == ElementType::MARKER || et == ElementType::JUMP) {
Measure* om = toMeasure(element->parent());
Measure* m = score->tick2measure(om->tick());
ne->setTrack(element->track());
ne->setParent(m);
undo(new AddElement(ne));
}
else if (et == ElementType::MEASURE_NUMBER) {
toMeasure(element->parent())->undoChangeProperty(Pid::MEASURE_NUMBER_MODE, static_cast<int>(MeasureNumberMode::SHOW));
}
else {
Segment* segment = toSegment(element->parent());
Fraction tick = segment->tick();
Measure* m = score->tick2measure(tick);
Segment* seg = m->undoGetSegment(SegmentType::ChordRest, tick);
ne->setTrack(ntrack);
ne->setParent(seg);
undo(new AddElement(ne));
}
}
return;
}
if (et == ElementType::FINGERING
|| (et == ElementType::IMAGE && !element->parent()->isSegment())
|| (et == ElementType::SYMBOL && !element->parent()->isSegment())
|| et == ElementType::NOTE
|| et == ElementType::TEXT
|| et == ElementType::GLISSANDO
|| et == ElementType::BEND
|| (et == ElementType::CHORD && toChord(element)->isGrace())
) {
Element* parent = element->parent();
const LinkedElements* links = parent->links();
// don't link part name
if (et == ElementType::TEXT) {
Text* t = toText(element);
if (t->tid() == Tid::INSTRUMENT_EXCERPT)
links = 0;
}
if (links == 0) {
undo(new AddElement(element));
return;
}
for (ScoreElement* ee : *links) {
Element* e = static_cast<Element*>(ee);
Element* ne;
if (e == parent)
ne = element;
else {
if (element->isGlissando()) { // and other spanners with Anchor::NOTE
Note* newEnd = Spanner::endElementFromSpanner(toGlissando(element), e);
if (newEnd) {
ne = element->linkedClone();
toSpanner(ne)->setNoteSpan(toNote(e), newEnd);
}
else //couldn't find suitable start note
continue;
}
else if (element->isFingering()) {
bool tabFingering = e->staff()->staffType(e->tick())->showTabFingering();
if (e->staff()->isTabStaff(e->tick()) && !tabFingering)
continue;
ne = element->linkedClone();
}
else
ne = element->linkedClone();
}
ne->setScore(e->score());
ne->setSelected(false);
ne->setParent(e);
undo(new AddElement(ne));
}
return;
}
if (et == ElementType::LAYOUT_BREAK) {
LayoutBreak* lb = toLayoutBreak(element);
if (lb->layoutBreakType() == LayoutBreak::Type::SECTION) {
Measure* m = lb->measure();
for (Score* s : scoreList()) {
if (s == lb->score())
undo(new AddElement(lb));
else {
Element* e = lb->linkedClone();
e->setScore(s);
Measure* nm = s->tick2measure(m->tick());
e->setParent(nm);
undo(new AddElement(e));
}
}
return;
}
}
if (ostaff == 0 || (
et != ElementType::ARTICULATION
&& et != ElementType::CHORDLINE
&& et != ElementType::LYRICS
&& et != ElementType::SLUR
&& et != ElementType::TIE
&& et != ElementType::NOTE
&& et != ElementType::INSTRUMENT_CHANGE
&& et != ElementType::HAIRPIN
&& et != ElementType::OTTAVA
&& et != ElementType::TRILL
&& et != ElementType::VIBRATO
&& et != ElementType::TEXTLINE
&& et != ElementType::PEDAL
&& et != ElementType::BREATH
&& et != ElementType::DYNAMIC
&& et != ElementType::STAFF_TEXT
&& et != ElementType::SYSTEM_TEXT
&& et != ElementType::STICKING
&& et != ElementType::TREMOLO
&& et != ElementType::ARPEGGIO
&& et != ElementType::SYMBOL
&& et != ElementType::TREMOLOBAR
&& et != ElementType::FRET_DIAGRAM
&& et != ElementType::FERMATA
&& et != ElementType::HARMONY)
) {
undo(new AddElement(element));
return;
}
for (Staff* staff : ostaff->staffList()) {
Score* score = staff->score();
int staffIdx = staff->idx();
QList<int> tr;
if ((strack & ~3) != staffIdx) // linked staff ?
tr.append(staffIdx * VOICES + (strack % VOICES));
else if (staff->score()->excerpt() && strack > -1)
tr = staff->score()->excerpt()->tracks().values(strack);
else
tr.append(strack);
// Some elements in voice 1 of a staff should be copied to every track which has a linked voice in this staff
if (tr.isEmpty() && (element->isSymbol()
|| element->isImage()
|| element->isTremoloBar()
|| element->isDynamic()
|| element->isStaffText()
|| element->isSticking()
|| element->isFretDiagram()
|| element->isHarmony()
|| element->isHairpin()
|| element->isOttava()
|| element->isTrill()
|| element->isSlur()
|| element->isVibrato()
|| element->isTextLine()
|| element->isPedal()
|| element->isLyrics())) {
tr.append(staffIdx * VOICES);
}
int it = 0;
for (int ntrack : tr) {
if ((ntrack & ~3) != staffIdx * VOICES) {
it++;
continue;
}
Element* ne;
if (staff == ostaff)
ne = element;
else {
if (staff->rstaff() != ostaff->rstaff()) {
switch (element->type()) {
// exclude certain element types except on corresponding staff in part
// this should be same list excluded in cloneStaff()
case ElementType::STAFF_TEXT:
case ElementType::SYSTEM_TEXT:
case ElementType::FRET_DIAGRAM:
case ElementType::HARMONY:
case ElementType::FIGURED_BASS:
case ElementType::DYNAMIC:
case ElementType::LYRICS: // not normally segment-attached
continue;
default:
break;
}
}
ne = element->linkedClone();
ne->setScore(score);
ne->setSelected(false);
ne->setTrack(staffIdx * VOICES + element->voice());
}
if (element->isArticulation()) {
Articulation* a = toArticulation(element);
Segment* segment;
SegmentType st;
Measure* m;
Fraction tick;
if (a->parent()->isChordRest()) {
ChordRest* cr = a->chordRest();
segment = cr->segment();
st = SegmentType::ChordRest;
tick = segment->tick();
m = score->tick2measure(tick);
}
else {
segment = toSegment(a->parent()->parent());
st = SegmentType::EndBarLine;
tick = segment->tick();
m = score->tick2measure(tick);
if (m->tick() == tick)
m = m->prevMeasure();
}
Segment* seg = m->findSegment(st, tick);
if (seg == 0) {
qWarning("undoAddSegment: segment not found");
break;
}
Articulation* na = toArticulation(ne);
na->setTrack(ntrack);
if (a->parent()->isChordRest()) {
ChordRest* cr = a->chordRest();
ChordRest* ncr;
if (cr->isGrace())
ncr = findLinkedChord(toChord(cr), score->staff(staffIdx));
else
ncr = toChordRest(seg->element(ntrack));
na->setParent(ncr);
}
else {
BarLine* bl = toBarLine(seg->element(ntrack));
na->setParent(bl);
}
undo(new AddElement(na));
}
else if (element->isChordLine() || element->isLyrics()) {
ChordRest* cr = toChordRest(element->parent());
Segment* segment = cr->segment();
Fraction tick = segment->tick();
Measure* m = score->tick2measure(tick);
Segment* seg = m->findSegment(SegmentType::ChordRest, tick);
if (seg == 0) {
qWarning("undoAddSegment: segment not found");
break;
}
ne->setTrack(ntrack);
ChordRest* ncr = toChordRest(seg->element(ntrack));
ne->setParent(ncr);
undo(new AddElement(ne));
}
//
// elements with Segment as parent
//
else if (element->isSymbol()
|| element->isImage()
|| element->isTremoloBar()
|| element->isDynamic()
|| element->isStaffText()
|| element->isSticking()
|| element->isFretDiagram()
|| element->isFermata()
|| element->isHarmony()) {
Segment* segment = element->parent()->isFretDiagram() ? toSegment(element->parent()->parent()) : toSegment(element->parent());
Fraction tick = segment->tick();
Measure* m = score->tick2measure(tick);
if ((segment->segmentType() == SegmentType::EndBarLine) && (m->tick() == tick))
m = m->prevMeasure();
Segment* seg = m->undoGetSegment(segment->segmentType(), tick);
ne->setTrack(ntrack);
ne->setParent(seg);
// make harmony child of fret diagram if possible
if (ne->isHarmony()) {
for (Element* segel : segment->annotations()) {
if (segel && segel->isFretDiagram() && segel->track() == ntrack) {
ne->setTrack(segel->track());
ne->setParent(segel);
break;
}
}
}
else if (ne->isFretDiagram()) {
// update track of child harmony
FretDiagram* fd = toFretDiagram(ne);
if (fd->harmony())
fd->harmony()->setTrack(ntrack);
}
undo(new AddElement(ne));
// transpose harmony if necessary
if (element->isHarmony() && ne != element) {
Harmony* h = toHarmony(ne);
if (score->styleB(Sid::concertPitch) != element->score()->styleB(Sid::concertPitch)) {
Part* partDest = h->part();
Interval interval = partDest->instrument(tick)->transpose();
if (!interval.isZero()) {
if (!score->styleB(Sid::concertPitch))
interval.flip();
int rootTpc = transposeTpc(h->rootTpc(), interval, true);
int baseTpc = transposeTpc(h->baseTpc(), interval, true);
score->undoTransposeHarmony(h, rootTpc, baseTpc);
}
}
}
}
else if (element->isSlur()
|| element->isHairpin()
|| element->isOttava()
|| element->isTrill()
|| element->isVibrato()
|| element->isTextLine()
|| element->isPedal()) {
Spanner* sp = toSpanner(element);
Spanner* nsp = toSpanner(ne);
int staffIdx1 = sp->track() / VOICES;
int tr2 = sp->effectiveTrack2();
int staffIdx2 = tr2 / VOICES;
int diff = staffIdx2 - staffIdx1;
nsp->setTrack2((staffIdx + diff) * VOICES + (tr2 % VOICES));
nsp->setTrack(ntrack);
#if 0 //whatdoesitdo?
QList<int> tl2;
if (staff->score()->excerpt() && element->isSlur()) {
nsp->setTrack(ntrack);
tl2 = staff->score()->excerpt()->tracks().values(sp->track2());
if (tl2.isEmpty()) {
it++;
continue;
}
nsp->setTrack2(tl2.at(it));
}
else if (!element->isSlur())
nsp->setTrack(ntrack & ~3);
#endif
// determine start/end element for slurs
// this is only necessary if start/end element is
// a grace note, otherwise the element can be set to zero
// and will later be calculated from tick/track values
//
if (element->isSlur() && sp != nsp) {
if (sp->startElement()) {
QList<ScoreElement*> sel = sp->startElement()->linkList();
for (ScoreElement* ee : sel) {
Element* e = static_cast<Element*>(ee);
if (e->score() == nsp->score() && e->track() == nsp->track()) {
nsp->setStartElement(e);
break;
}
}
}
if (sp->endElement()) {
QList<ScoreElement*> eel = sp->endElement()->linkList();
for (ScoreElement* ee : eel) {
Element* e = static_cast<Element*>(ee);
if (e->score() == nsp->score() && e->track() == nsp->track2()) {
nsp->setEndElement(e);
break;
}
}
}
}
undo(new AddElement(nsp));
}
else if (et == ElementType::GLISSANDO)
undo(new AddElement(toSpanner(ne)));
else if (element->isTremolo() && toTremolo(element)->twoNotes()) {
Tremolo* tremolo = toTremolo(element);
ChordRest* cr1 = toChordRest(tremolo->chord1());
ChordRest* cr2 = toChordRest(tremolo->chord2());
Segment* s1 = cr1->segment();
Segment* s2 = cr2->segment();
Measure* m1 = s1->measure();
Measure* m2 = s2->measure();
Measure* nm1 = score->tick2measure(m1->tick());
Measure* nm2 = score->tick2measure(m2->tick());
Segment* ns1 = nm1->findSegment(s1->segmentType(), s1->tick());
Segment* ns2 = nm2->findSegment(s2->segmentType(), s2->tick());
Chord* c1 = toChord(ns1->element(staffIdx * VOICES + cr1->voice()));
Chord* c2 = toChord(ns2->element(staffIdx * VOICES + cr2->voice()));
Tremolo* ntremolo = toTremolo(ne);
ntremolo->setChords(c1, c2);
ntremolo->setParent(c1);
undo(new AddElement(ntremolo));
}
else if (element->isTremolo() && !toTremolo(element)->twoNotes()) {
Chord* cr = toChord(element->parent());
Chord* c1 = findLinkedChord(cr, score->staff(staffIdx));
ne->setParent(c1);
undo(new AddElement(ne));
}
else if (element->isArpeggio()) {
ChordRest* cr = toChordRest(element->parent());
Segment* s = cr->segment();
Measure* m = s->measure();
Measure* nm = score->tick2measure(m->tick());
Segment* ns = nm->findSegment(s->segmentType(), s->tick());
Chord* c1 = toChord(ns->element(staffIdx * VOICES + cr->voice()));
ne->setParent(c1);
undo(new AddElement(ne));
}
else if (element->isTie()) {
Tie* tie = toTie(element);
Note* n1 = tie->startNote();
Note* n2 = tie->endNote();
Chord* cr1 = n1->chord();
Chord* cr2 = n2 ? n2->chord() : 0;
// find corresponding notes in linked staff
// accounting for grace notes and cross-staff notation
int sm = 0;
if (cr1->staffIdx() != cr2->staffIdx())
sm = cr2->staffIdx() - cr1->staffIdx();
Chord* c1 = findLinkedChord(cr1, score->staff(staffIdx));
Chord* c2 = findLinkedChord(cr2, score->staff(staffIdx + sm));
Note* nn1 = c1->findNote(n1->pitch(), n1->unisonIndex());
Note* nn2 = c2 ? c2->findNote(n2->pitch(), n2->unisonIndex()) : 0;
// create tie
Tie* ntie = toTie(ne);
ntie->eraseSpannerSegments();
ntie->setTrack(c1->track());
ntie->setStartNote(nn1);
ntie->setEndNote(nn2);
undo(new AddElement(ntie));
}
else if (element->isInstrumentChange()) {
InstrumentChange* is = toInstrumentChange(element);
Segment* s1 = is->segment();
Measure* m1 = s1->measure();
Measure* nm1 = score->tick2measure(m1->tick());
Segment* ns1 = nm1->findSegment(s1->segmentType(), s1->tick());
InstrumentChange* nis = toInstrumentChange(ne);
nis->setParent(ns1);
Fraction tickStart = nis->segment()->tick();
Part* part = nis->part();
Interval oldV = nis->part()->instrument(tickStart)->transpose();
// ws: instrument should not be changed here
if (is->instrument()->channel().empty() || is->instrument()->channel(0)->program() == -1)
nis->setInstrument(*staff->part()->instrument(s1->tick()));
else if (nis != is)
nis->setInstrument(*is->instrument());
undo(new AddElement(nis));
// transpose root score; parts will follow
if (score->isMaster() && part->instrument(tickStart)->transpose() != oldV) {
auto i = part->instruments()->upper_bound(tickStart.ticks());
Fraction tickEnd = i == part->instruments()->end() ? Fraction(-1, 1) : Fraction::fromTicks(i->first);
transpositionChanged(part, oldV, tickStart, tickEnd);
}
}
else if (element->isBreath()) {
Breath* breath = toBreath(element);
Fraction tick = breath->segment()->tick();
Measure* m = score->tick2measure(tick);
// breath appears before barline
if (m->tick() == tick)
m = m->prevMeasure();
Segment* seg = m->undoGetSegment(SegmentType::Breath, tick);
Breath* nbreath = toBreath(ne);
nbreath->setScore(score);
nbreath->setTrack(ntrack);
nbreath->setParent(seg);
undo(new AddElement(nbreath));
}
else
qWarning("undoAddElement: unhandled: <%s>", element->name());
it++;
}
}
}
//---------------------------------------------------------
// undoAddCR
//---------------------------------------------------------
void Score::undoAddCR(ChordRest* cr, Measure* measure, const Fraction& tick)
{
Q_ASSERT(!cr->isChord() || !(toChord(cr)->notes()).empty());
if (!cr->lyrics().empty()) {
// Add chordrest and lyrics separately for correct
// handling of adding lyrics to linked staves.
std::vector<Lyrics*> lyrics;
std::swap(lyrics, cr->lyrics());
undoAddCR(cr, measure, tick);
for (Lyrics* l : lyrics)
undoAddElement(l);
return;
}
Staff* ostaff = cr->staff();
int strack = ostaff->idx() * VOICES + cr->voice();
if (ostaff->score()->excerpt() && !ostaff->score()->excerpt()->tracks().isEmpty())
strack = ostaff->score()->excerpt()->tracks().key(strack, -1);
SegmentType segmentType = SegmentType::ChordRest;
Tuplet* t = cr->tuplet();
for (const Staff* staff : ostaff->staffList()) {
QList<int> tracks;
int staffIdx = staff->idx();
if ((strack & ~3) != staffIdx) // linked staff ?
tracks.append(staffIdx * VOICES + (strack % VOICES));
else if (staff->score()->excerpt() && !staff->score()->excerpt()->tracks().isEmpty())
tracks = staff->score()->excerpt()->tracks().values(strack);
else if (!staff->score()->excerpt())
tracks.append(staffIdx * VOICES + (strack % VOICES));
else
tracks.append(staffIdx * VOICES + cr->voice());
for (int ntrack : tracks) {
if (ntrack < staff->part()->startTrack() || ntrack >= staff->part()->endTrack())
continue;
Score* score = staff->score();
Measure* m = (score == this) ? measure : score->tick2measure(tick);
if (!m) {
qDebug("measure not found");
break;
}
Segment* seg = m->undoGetSegment(segmentType, tick);
Q_ASSERT(seg->segmentType() == segmentType);
ChordRest* newcr = (staff == ostaff) ? cr : toChordRest(cr->linkedClone());
newcr->setScore(score);
newcr->setTrack(ntrack);
newcr->setParent(seg);
#ifndef QT_NO_DEBUG
if (newcr->isChord()) {
Chord* chord = toChord(newcr);
// setTpcFromPitch needs to know the note tick position
for (Note* note : chord->notes()) {
// if (note->tpc() == Tpc::TPC_INVALID)
// note->setTpcFromPitch();
Q_ASSERT(note->tpc() != Tpc::TPC_INVALID);
}
}
#endif
if (t) {
if (staff != ostaff) {
Tuplet* nt = 0;
if (t->elements().empty() || t->elements().front() == cr) {
for (ScoreElement* e : t->linkList()) {
Tuplet* nt1 = toTuplet(e);
if (nt1 == t)
continue;
if (nt1->score() == score && nt1->track() == newcr->track()) {
nt = nt1;
break;
}
}
if (!nt) {
nt = toTuplet(t->linkedClone());
nt->setTuplet(0);
nt->setScore(score);
nt->setTrack(newcr->track());
}
Tuplet* t2 = t;
Tuplet* nt2 = nt;
while (t2->tuplet()) {
Tuplet* t3 = t2->tuplet();
Tuplet* nt3 = 0;
for (auto i : t3->linkList()) {
Tuplet* tt = toTuplet(i);
if (tt != t3 && tt->score() == score && tt->track() == t2->track()) {
nt3 = tt;
break;
}
}
if (nt3 == 0) {
nt3 = toTuplet(t3->linkedClone());
nt3->setScore(score);
nt3->setTrack(nt2->track());
}
nt3->add(nt2);
nt2->setTuplet(nt3);
t2 = t3;
nt2 = nt3;
}
}
else {
const LinkedElements* le = t->links();
// search the linked tuplet
if (le) {
for (ScoreElement* ee : *le) {
Element* e = static_cast<Element*>(ee);
if (e->score() == score && e->track() == ntrack) {
nt = toTuplet(e);
break;
}
}
}
if (nt == 0)
qWarning("linked tuplet not found");
}
if (nt) {
newcr->setTuplet(nt);
nt->setParent(newcr->measure());
}
}
}
if (newcr->isRest() && (toRest(newcr)->isGap()) && !(toRest(newcr)->track() % VOICES))
toRest(newcr)->setGap(false);
undo(new AddElement(newcr));
}
}
}
//---------------------------------------------------------
// undoRemoveElement
//---------------------------------------------------------
void Score::undoRemoveElement(Element* element)
{
QList<Segment*> segments;
for (ScoreElement* ee : element->linkList()) {
Element* e = static_cast<Element*>(ee);
undo(new RemoveElement(e));
if (e->parent() && (e->parent()->isSegment())) {
Segment* s = toSegment(e->parent());
if (!segments.contains(s))
segments.append(s);
}
if (e->parent() && e->parent()->isSystem()) {
e->setParent(0); // systems will be regenerated upon redo, so detach
}
}
for (Segment* s : segments) {
if (s->empty()) {
if (s->header() || s->trailer()) // probably more segment types (system header)
s->setEnabled(false);
else
undo(new RemoveElement(s));
}
}
}
//---------------------------------------------------------
// undoChangeSpannerElements
//---------------------------------------------------------
void Score::undoChangeSpannerElements(Spanner* spanner, Element* startElement, Element* endElement)
{
Element* oldStartElement = spanner->startElement();
Element* oldEndElement = spanner->endElement();
int startDeltaTrack = startElement && oldStartElement ? startElement->track() - oldStartElement->track() : 0;
int endDeltaTrack = endElement && oldEndElement ? endElement->track() - oldEndElement->track() : 0;
// scan all spanners linked to this one
for (ScoreElement* el : spanner->linkList()) {
Spanner* sp = toSpanner(el);
Element* newStartElement = nullptr;
Element* newEndElement = nullptr;
// if not the current spanner, but one linked to it, determine its new start and end elements
// as modifications 'parallel' to the modifications of the current spanner's start and end elements
if (sp != spanner) {
if (startElement) {
// determine the track where to expect the 'parallel' start element
int newTrack = sp->startElement() ? sp->startElement()->track() + startDeltaTrack : 0;
// look in elements linked to new start element for an element with
// same score as linked spanner and appropriate track
for (ScoreElement* ee : startElement->linkList()) {
Element* e = toElement(ee);
if (e->score() == sp->score() && e->track() == newTrack) {
newStartElement = e;
break;
}
}
}
// similarly to determine the 'parallel' end element
if (endElement) {
int newTrack = sp->endElement() ? sp->endElement()->track() + endDeltaTrack : 0;
for (ScoreElement* ee : endElement->linkList()) {
Element* e = toElement(ee);
if (e->score() == sp->score() && e->track() == newTrack) {
newEndElement = e;
break;
}
}
}
}
// if current spanner, just use stored start and end elements
else {
newStartElement = startElement;
newEndElement = endElement;
}
sp->score()->undo(new ChangeSpannerElements(sp, newStartElement, newEndElement));
}
}
//---------------------------------------------------------
// undoChangeTuning
//---------------------------------------------------------
void Score::undoChangeTuning(Note* n, qreal v)
{
n->undoChangeProperty(Pid::TUNING, v);
}
void Score::undoChangeUserMirror(Note* n, MScore::DirectionH d)
{
n->undoChangeProperty(Pid::MIRROR_HEAD, int(d));
}
//---------------------------------------------------------
// undoChangeTpc
// TODO-TPC: check
//---------------------------------------------------------
void Score::undoChangeTpc(Note* note, int v)
{
note->undoChangeProperty(Pid::TPC1, v);
}
//---------------------------------------------------------
// undoAddBracket
//---------------------------------------------------------
void Score::undoAddBracket(Staff* staff, int level, BracketType type, int span)
{
undo(new AddBracket(staff, level, type, span));
}
//---------------------------------------------------------
// undoRemoveBracket
//---------------------------------------------------------
void Score::undoRemoveBracket(Bracket* b)
{
undo(new RemoveBracket(b->staff(), b->column(), b->bracketType(), b->span()));
}
//---------------------------------------------------------
// undoInsertTime
// acts on the linked scores as well
//---------------------------------------------------------
void Score::undoInsertTime(const Fraction& tick, const Fraction& len)
{
if (len.isZero())
return;
QList<Spanner*> sl;
for (auto i : _spanner.map()) {
Spanner* s = i.second;
if (s->tick2() < tick)
continue;
bool append = false;
if (len > Fraction(0, 1)) {
if (tick > s->tick() && tick < s->tick2())
append = true;
else if (tick <= s->tick())
append = true;
}
else {
Fraction tick2 = tick - len;
if (s->tick() >= tick2)
append = true;
else if (s->tick() >= tick && s->tick2() <= tick2)
append = true;
else if ((s->tick() <= tick) && (s->tick2() >= tick2)) {
Fraction t2 = s->tick2() + len;
if (t2 > s->tick())
append = true;
}
else if (s->tick() > tick && s->tick2() > tick2)
append = true;
else if (s->tick() < tick && s->tick2() < tick2)
append = true;
}
for (Spanner* ss : sl) {
if (ss->linkList().contains(s)) {
append = false;
break;
}
}
if (append)
sl.append(s);
}
for (Spanner* s : sl) {
if (len > Fraction(0, 1)) {
if (tick > s->tick() && tick < s->tick2()) {
//
// case a:
// +----spanner--------+
// +---add---
//
s->undoChangeProperty(Pid::SPANNER_TICKS, s->ticks() + len);
}
else if (tick <= s->tick()) {
//
// case b:
// +----spanner--------
// +---add---
// and
// +----spanner--------
// +---add---+
Element* startElement = s->startElement();
Element* endElement = s->endElement();
undoChangeSpannerElements(s, nullptr, nullptr);
s->undoChangeProperty(Pid::SPANNER_TICK, s->tick() + len);
undoChangeSpannerElements(s, startElement, endElement);
}
}
else {
Fraction tick2 = tick - len;
if (s->tick() >= tick2) {
//
// case A:
// +----remove---+ +---spanner---+
//
Fraction t = s->tick() + len;
if (t < Fraction(0,1))
t = Fraction(0,1);
Element* startElement = s->startElement();
Element* endElement = s->endElement();
undoChangeSpannerElements(s, nullptr, nullptr);
s->undoChangeProperty(Pid::SPANNER_TICK, t);
undoChangeSpannerElements(s, startElement, endElement);
}
else if (s->tick() >= tick && s->tick2() <= tick2) {
//
// case B:
// +---spanner---+
// +----remove--------+
//
undoRemoveElement(s);
}
else if ((s->tick() <= tick) && (s->tick2() >= tick2)) {
//
// case C:
// +----spanner--------+
// +---remove---+
//
Fraction t2 = s->tick2() + len;
if (t2 > s->tick())
s->undoChangeProperty(Pid::SPANNER_TICKS, s->ticks() + len);
}
else if (s->tick() > tick && s->tick2() > tick2) {
//
// case D:
// +----spanner--------+
// +---remove---+
//
Fraction d1 = s->tick() - tick;
Fraction d2 = tick2 - s->tick();
Fraction le = s->ticks() - d2;
if (le.isZero())
undoRemoveElement(s);
else {
s->undoChangeProperty(Pid::SPANNER_TICK, s->tick() - d1);
s->undoChangeProperty(Pid::SPANNER_TICKS, le);
}
}
else if (s->tick() < tick && s->tick2() < tick2) {
//
// case E:
// +----spanner--------+
// +---remove---+
//
Fraction d = s->tick2() - tick;
Fraction le = s->ticks() - d;
if (le.isZero())
undoRemoveElement(s);
else
s->undoChangeProperty(Pid::SPANNER_TICKS, le);
}
}
}
undo(new InsertTimeUnmanagedSpanner(this, tick, len));
}
//---------------------------------------------------------
// undoRemoveMeasures
//---------------------------------------------------------
void Score::undoRemoveMeasures(Measure* m1, Measure* m2)
{
Q_ASSERT(m1 && m2);
const Fraction startTick = m1->tick();
const Fraction endTick = m2->endTick();
std::set<Spanner*> spannersToRemove;
//
// handle ties which start before m1 and end in (m1-m2)
//
for (Segment* s = m1->first(); s != m2->last(); s = s->next1()) {
if (!s->isChordRestType())
continue;
for (int track = 0; track < ntracks(); ++track) {
Element* e = s->element(track);
if (!e || !e->isChord())
continue;
Chord* c = toChord(e);
for (Note* n : c->notes()) {
// Remove ties crossing measure range boundaries
Tie* t = n->tieBack();
if (t && (t->startNote()->chord()->tick() < startTick))
undoRemoveElement(t);
t = n->tieFor();
if (t && (t->endNote()->chord()->tick() >= endTick))
undoRemoveElement(t);
// Do the same for other note-anchored spanners (e.g. glissandi).
// Delay actual removing to avoid modifying lists inside loops over them.
for (Spanner* sb : n->spannerBack()) {
if (sb->tick() < startTick)
spannersToRemove.insert(sb);
}
for (Spanner* sf : n->spannerFor()) {
if (sf->tick2() >= endTick)
spannersToRemove.insert(sf);
}
}
}
}
for (Spanner* s : spannersToRemove)
undoRemoveElement(s);
undo(new RemoveMeasures(m1, m2));
}
}