//============================================================================= // MuseScore // Music Composition & Notation // // Copyright (C) 2002-2013 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 //============================================================================= /** \file Implementation of undo functions. The undo system requires calling startUndo() when starting a GUI command and calling endUndo() when ending the command. All changes to a score in response to a GUI command must be undoable/redoable by executing a sequence of low-level undo operations. This sequence is built by the code handling the command, by calling one or more undoOp()'s between startUndo() and endUndo(). */ #include "undo.h" #include "element.h" #include "note.h" #include "score.h" #include "segment.h" #include "measure.h" #include "system.h" #include "select.h" #include "input.h" #include "slur.h" #include "tie.h" #include "clef.h" #include "staff.h" #include "chord.h" #include "sig.h" #include "key.h" #include "barline.h" #include "volta.h" #include "tuplet.h" #include "harmony.h" #include "pitchspelling.h" #include "part.h" #include "beam.h" #include "dynamic.h" #include "page.h" #include "keysig.h" #include "image.h" #include "hairpin.h" #include "rest.h" #include "bend.h" #include "tremolobar.h" #include "articulation.h" #include "noteevent.h" #include "slur.h" #include "tempotext.h" #include "instrchange.h" #include "box.h" #include "stafftype.h" #include "accidental.h" #include "layoutbreak.h" #include "spanner.h" #include "sequencer.h" #include "breath.h" #include "fingering.h" #include "rehearsalmark.h" #include "excerpt.h" #include "stafftext.h" #include "chordline.h" #include "tremolo.h" #include "sym.h" namespace Ms { extern Measure* tick2measure(int tick); //--------------------------------------------------------- // updateNoteLines // compute line position of note heads after // clef change //--------------------------------------------------------- void updateNoteLines(Segment* segment, int track) { Staff* staff = segment->score()->staff(track / VOICES); if (staff->isDrumStaff() || staff->isTabStaff()) return; for (Segment* s = segment->next1(); s; s = s->next1()) { if (s->segmentType() == Segment::Type::Clef && s->element(track) && !s->element(track)->generated()) break; if (s->segmentType() != Segment::Type::ChordRest) continue; for (int t = track; t < track + VOICES; ++t) { Chord* chord = static_cast(s->element(t)); if (chord && chord->type() == Element::Type::CHORD) { for (Note* n : chord->notes()) n->updateLine(); chord->sortNotes(); for (Chord* gc : chord->graceNotes()) { for (Note* gn : gc->notes()) gn->updateLine(); gc->sortNotes(); } } } } } //--------------------------------------------------------- // UndoCommand //--------------------------------------------------------- UndoCommand::~UndoCommand() { foreach(UndoCommand* c, childList) delete c; } //--------------------------------------------------------- // undo //--------------------------------------------------------- void UndoCommand::undo() { int n = childList.size(); for (int i = n-1; i >= 0; --i) { #ifdef DEBUG_UNDO qDebug(" undo<%s> %p", childList[i]->name(), childList[i]); #endif childList[i]->undo(); } flip(); } //--------------------------------------------------------- // redo //--------------------------------------------------------- void UndoCommand::redo() { int n = childList.size(); for (int i = 0; i < n; ++i) { #ifdef DEBUG_UNDO qDebug(" redo<%s> %p", childList[i]->name(), childList[i]); #endif childList[i]->redo(); } flip(); } //--------------------------------------------------------- // unwind //--------------------------------------------------------- void UndoCommand::unwind() { while (!childList.isEmpty()) { UndoCommand* c = childList.takeLast(); c->undo(); delete c; } } //--------------------------------------------------------- // UndoStack //--------------------------------------------------------- UndoStack::UndoStack() { curCmd = 0; curIdx = 0; cleanIdx = 0; } //--------------------------------------------------------- // UndoStack //--------------------------------------------------------- UndoStack::~UndoStack() { } //--------------------------------------------------------- // beginMacro //--------------------------------------------------------- void UndoStack::beginMacro() { if (curCmd) { qDebug("UndoStack:beginMacro(): already active"); return; } curCmd = new UndoCommand(); if (MScore::debugMode) qDebug("UndoStack::beginMacro %p, UndoStack %p", curCmd, this); } //--------------------------------------------------------- // endMacro //--------------------------------------------------------- void UndoStack::endMacro(bool rollback) { if (MScore::debugMode) qDebug("UndoStack::endMacro %d", rollback); if (curCmd == 0) { qDebug("UndoStack:endMacro(): not active"); return; } if (rollback) delete curCmd; else { while (list.size() > curIdx) { UndoCommand* cmd = list.takeLast(); delete cmd; } list.append(curCmd); ++curIdx; } curCmd = 0; } //--------------------------------------------------------- // push //--------------------------------------------------------- void UndoStack::push(UndoCommand* cmd) { if (!curCmd) { // this can happen for layout() outside of a command (load) // qDebug("UndoStack:push(): no active command, UndoStack %p", this); cmd->redo(); delete cmd; return; } #ifdef DEBUG_UNDO if (strcmp(cmd->name(), "ChangeProperty") == 0) { ChangeProperty* cp = static_cast(cmd); qDebug("UndoStack::push <%s> %p id %d", cmd->name(), cmd, int(cp->getId())); } else { qDebug("UndoStack::push <%s> %p", cmd->name(), cmd); } #endif curCmd->appendChild(cmd); cmd->redo(); } //--------------------------------------------------------- // push1 //--------------------------------------------------------- void UndoStack::push1(UndoCommand* cmd) { if (curCmd) curCmd->appendChild(cmd); else qDebug("UndoStack:push1(): no active command, UndoStack %p", this); } //--------------------------------------------------------- // pop //--------------------------------------------------------- void UndoStack::pop() { if (!curCmd) { qDebug("UndoStack:pop(): no active command"); return; } UndoCommand* cmd = curCmd->removeChild(); cmd->undo(); } //--------------------------------------------------------- // setClean //--------------------------------------------------------- void UndoStack::setClean() { if (cleanIdx != curIdx) { cleanIdx = curIdx; } } //--------------------------------------------------------- // undo //--------------------------------------------------------- void UndoStack::undo() { if (curIdx) { --curIdx; Q_ASSERT(curIdx >= 0); if (MScore::debugMode) qDebug("--undo index %d", curIdx); list[curIdx]->undo(); } } //--------------------------------------------------------- // redo //--------------------------------------------------------- void UndoStack::redo() { if (canRedo()) { if (MScore::debugMode) qDebug("--redo index %d", curIdx); list[curIdx++]->redo(); } } //--------------------------------------------------------- // SaveState //--------------------------------------------------------- SaveState::SaveState(Score* s) : undoInputState(s), redoInputState(s->inputState()) { score = s; redoSelection = score->selection(); } void SaveState::undo() { redoInputState = score->inputState(); redoSelection = score->selection(); score->setInputState(undoInputState); score->setSelection(undoSelection); } void SaveState::redo() { undoInputState = score->inputState(); undoSelection = score->selection(); score->setInputState(redoInputState); score->setSelection(redoSelection); } //--------------------------------------------------------- // undoChangeProperty //--------------------------------------------------------- void Score::undoChangeProperty(Element* e, P_ID t, const QVariant& st, PropertyStyle ps) { if (propertyLink(t)) { if (e->links()) { foreach(Element* e, *e->links()) { if (e->getProperty(t) != st) undo(new ChangeProperty(e, t, st, ps)); } } else if (e->type() == Element::Type::MEASURE) { qDebug("change property for measure"); if (e->getProperty(t) != st) undo(new ChangeProperty(e, t, st, ps)); } else { if (e->getProperty(t) != st) undo(new ChangeProperty(e, t, st, ps)); } } else { if (e->getProperty(t) != st) undo(new ChangeProperty(e, t, st, ps)); } } //--------------------------------------------------------- // undoPropertyChanged //--------------------------------------------------------- void Score::undoPropertyChanged(Element* e, P_ID t, const QVariant& st) { if (propertyLink(t) && e->links()) { foreach (Element* ee, *e->links()) { if (ee == e) { if (ee->getProperty(t) != st) undo()->push1(new ChangeProperty(ee, t, st)); } else { // property in linked element has not changed yet // push() calls redo() to change it if (ee->getProperty(t) != e->getProperty(t)) undo()->push(new ChangeProperty(ee, t, e->getProperty(t))); } } } else { if (e->getProperty(t) != st) { undo()->push1(new ChangeProperty(e, t, st)); } } } //--------------------------------------------------------- // undoChangeElement //--------------------------------------------------------- void Score::undoChangeElement(Element* oldElement, Element* newElement) { undo(new ChangeElement(oldElement, newElement)); } //--------------------------------------------------------- // undoChangePitch //--------------------------------------------------------- void Score::undoChangePitch(Note* note, int pitch, int tpc1, int tpc2) { const LinkedElements* l = note->links(); if (l) { for (Element* e : *l) { Note* n = static_cast(e); undo()->push(new ChangePitch(n, pitch, tpc1, tpc2)); } } else undo()->push(new ChangePitch(note, pitch, tpc1, tpc2)); } //--------------------------------------------------------- // undoChangeKeySig //--------------------------------------------------------- void Score::undoChangeKeySig(Staff* ostaff, int tick, Key key) { KeySig* lks = 0; foreach (Staff* staff, ostaff->staffList()) { Score* score = staff->score(); Measure* measure = score->tick2measure(tick); if (!measure) { qDebug("measure for tick %d not found!", tick); continue; } Segment* s = measure->undoGetSegment(Segment::Type::KeySig, tick); int staffIdx = staff->idx(); int track = staffIdx * VOICES; KeySig* ks = static_cast(s->element(track)); int diff = -staff->part()->instr()->transpose().chromatic; if (diff && !score->styleB(StyleIdx::concertPitch)) key = transposeKey(key, diff); if (ks) { ks->undoChangeProperty(P_ID::GENERATED, false); KeySigEvent kse = ks->keySigEvent(); kse.setKey(key); undo(new ChangeKeySig(ks, kse, ks->showCourtesy())); } else { KeySig* nks = new KeySig(score); nks->setParent(s); nks->setTrack(track); nks->setKey(key); undo(new AddElement(nks)); if (lks) lks->linkTo(nks); else lks = nks; } // // change all following generated keysigs // Measure* lm = measure->nextMeasure(); for (; lm; lm = lm->nextMeasure()) { Segment* s = lm->findSegment(Segment::Type::KeySig | Segment::Type::KeySigAnnounce, lm->tick()); if (!s) continue; KeySig* ks = static_cast(s->element(track)); if (!ks) continue; if (!ks->generated()) break; if (ks->key() != key) { KeySigEvent kse = ks->keySigEvent(); kse.setKey(key); undo(new ChangeKeySig(ks, kse, ks->showCourtesy())); } } } } //--------------------------------------------------------- // undoChangeClef // change clef if seg contains a clef // else // create a clef before segment seg //--------------------------------------------------------- void Score::undoChangeClef(Staff* ostaff, Segment* seg, ClefType st) { bool firstSeg = seg->measure()->first() == seg; Clef* gclef = 0; foreach (Staff* staff, ostaff->staffList()) { if (staff->staffType()->group() != ClefInfo::staffGroup(st)) continue; Score* score = staff->score(); int tick = seg->tick(); Measure* measure = score->tick2measure(tick); if (!measure) { qDebug("measure for tick %d not found!", tick); continue; } Segment* destSeg = measure->findSegment(Segment::Type::Clef, tick); // move measure-initial clef to last segment of prev measure if (firstSeg // if at start of measure && measure->prevMeasure() // and there is a previous measure ) { measure = measure->prevMeasure(); destSeg = measure->findSegment(Segment::Type::Clef, tick); } if (!destSeg) { destSeg = new Segment(measure, Segment::Type::Clef, seg->tick()); score->undoAddElement(destSeg); } int staffIdx = staff->idx(); int track = staffIdx * VOICES; Clef* clef = static_cast(destSeg->element(track)); if (clef) { // // for transposing instruments, differentiate // clef type for concertPitch // Instrument* i = staff->part()->instr(tick); ClefType cp, tp; if (i->transpose().isZero()) { cp = st; tp = st; } else { bool concertPitch = clef->concertPitch(); if (concertPitch) { cp = st; tp = clef->transposingClef(); } else { cp = clef->concertClef(); tp = st; } } clef->setGenerated(false); score->undo(new ChangeClefType(clef, cp, tp)); } else { if (gclef) { clef = static_cast(gclef->linkedClone()); clef->setScore(score); } else { clef = new Clef(score); gclef = clef; } clef->setTrack(track); clef->setClefType(st); clef->setParent(destSeg); score->undo(new AddElement(clef)); } cmdUpdateNotes(); } } //--------------------------------------------------------- // findLinkedVoiceElement //--------------------------------------------------------- static Element* findLinkedVoiceElement(Element* e, Staff* nstaff) { Score* score = nstaff->score(); Segment* segment = static_cast(e->parent()); Measure* measure = segment->measure(); Measure* m = score->tick2measure(measure->tick()); Segment* s = m->findSegment(segment->segmentType(), segment->tick()); int staffIdx = score->staffIdx(nstaff); return s->element(staffIdx * VOICES + e->voice()); } //--------------------------------------------------------- // undoChangeChordRestLen //--------------------------------------------------------- void Score::undoChangeChordRestLen(ChordRest* cr, const TDuration& d) { Staff* ostaff = cr->staff(); LinkedStaves* linkedStaves = ostaff->linkedStaves(); if (linkedStaves) { foreach(Staff* staff, linkedStaves->staves()) { if (staff == cr->staff()) continue; ChordRest* ncr = static_cast(findLinkedVoiceElement(cr, staff)); undo(new ChangeChordRestLen(ncr, d)); } } undo(new ChangeChordRestLen(cr, d)); } //--------------------------------------------------------- // undoChangeEndBarLineType //--------------------------------------------------------- void Score::undoChangeEndBarLineType(Measure* m, BarLineType subtype) { undo(new ChangeEndBarLineType(m, subtype)); } //--------------------------------------------------------- // undoChangeBarLineSpan //--------------------------------------------------------- void Score::undoChangeBarLineSpan(Staff* staff, int span, int spanFrom, int spanTo) { undo(new ChangeBarLineSpan(staff, span, spanFrom, spanTo)); } //--------------------------------------------------------- // undoChangeSingleBarLineSpan //--------------------------------------------------------- void Score::undoChangeSingleBarLineSpan(BarLine* barLine, int span, int spanFrom, int spanTo) { undo(new ChangeSingleBarLineSpan(barLine, span, spanFrom, spanTo)); } //--------------------------------------------------------- // undoTransposeHarmony //--------------------------------------------------------- void Score::undoTransposeHarmony(Harmony* h, int rootTpc, int baseTpc) { undo(new TransposeHarmony(h, rootTpc, baseTpc)); } //--------------------------------------------------------- // undoExchangeVoice //--------------------------------------------------------- void Score::undoExchangeVoice(Measure* measure, int v1, int v2, int staff1, int staff2) { undo(new ExchangeVoice(measure, v1, v2, staff1, staff2)); if (v1 == 0 || v2 == 0) { for (int staffIdx = staff1; staffIdx < staff2; ++staffIdx) { // check for complete timeline of voice 0 int ctick = measure->tick(); int track = staffIdx * VOICES; for (Segment* s = measure->first(Segment::Type::ChordRest); s; s = s->next(Segment::Type::ChordRest)) { ChordRest* cr = static_cast(s->element(track)); if (cr == 0) continue; if (ctick < s->tick()) { // fill gap int ticks = s->tick() - ctick; setRest(ctick, track, Fraction::fromTicks(ticks), false, 0); } ctick = s->tick() + cr->actualTicks(); } int etick = measure->tick() + measure->ticks(); if (ctick < etick) { // fill gap int ticks = etick - ctick; setRest(ctick, track, Fraction::fromTicks(ticks), false, 0); } } } } //--------------------------------------------------------- // 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) { undo(new RemoveStaff(staff)); } //--------------------------------------------------------- // undoInsertStaff // idx - index of staff in part //--------------------------------------------------------- void Score::undoInsertStaff(Staff* staff, int idx) { undo(new InsertStaff(staff, idx)); } //--------------------------------------------------------- // undoChangeVoltaEnding //--------------------------------------------------------- void Score::undoChangeVoltaEnding(Volta* volta, const QList& l) { undo(new ChangeVoltaEnding(volta, l)); } //--------------------------------------------------------- // undoChangeVoltaText //--------------------------------------------------------- void Score::undoChangeVoltaText(Volta* volta, const QString& s) { undo(new ChangeVoltaText(volta, s)); } //--------------------------------------------------------- // undoChangeChordRestSize //--------------------------------------------------------- void Score::undoChangeChordRestSize(ChordRest* cr, bool small) { undo(new ChangeChordRestSize(cr, small)); } //--------------------------------------------------------- // undoChangeChordNoStem //--------------------------------------------------------- void Score::undoChangeChordNoStem(Chord* cr, bool noStem) { undo(new ChangeChordNoStem(cr, noStem)); } //--------------------------------------------------------- // undoChangeBracketSpan //--------------------------------------------------------- void Score::undoChangeBracketSpan(Staff* staff, int column, int span) { undo(new ChangeBracketSpan(staff, column, span)); } //--------------------------------------------------------- // undoChangeInvisible //--------------------------------------------------------- void Score::undoChangeInvisible(Element* e, bool v) { undoChangeProperty(e, P_ID::VISIBLE, v); e->setGenerated(false); } //--------------------------------------------------------- // undoAddElement //--------------------------------------------------------- void Score::undoAddElement(Element* element) { QList staffList; Staff* ostaff = element->staff(); Element::Type et = element->type(); // // some elements are replicated for all parts regardless of // linking: // if ((et == Element::Type::REHEARSAL_MARK) || ((et == Element::Type::STAFF_TEXT) && static_cast(element)->systemFlag()) || (et == Element::Type::JUMP) || (et == Element::Type::MARKER) || (et == Element::Type::TEMPO_TEXT) || (et == Element::Type::VOLTA) ) { foreach(Score* s, scoreList()) staffList.append(s->staff(0)); foreach(Staff* staff, staffList) { Score* score = staff->score(); int staffIdx = score->staffIdx(staff); Element* ne; if (staff == ostaff) ne = element; else { ne = element->linkedClone(); ne->setScore(score); ne->setSelected(false); ne->setTrack(staffIdx * VOICES + element->voice()); } if (et == Element::Type::VOLTA) { Spanner* nsp = static_cast(ne); Spanner* sp = static_cast(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 == Element::Type::MARKER || et == Element::Type::JUMP) { Measure* om = static_cast(element->parent()); Measure* m = score->tick2measure(om->tick()); ne->setTrack(element->track()); ne->setParent(m); undo(new AddElement(ne)); } else { Segment* segment = static_cast(element->parent()); int tick = segment->tick(); Measure* m = score->tick2measure(tick); Segment* seg = m->findSegment(Segment::Type::ChordRest, tick); int ntrack = staffIdx * VOICES + element->voice(); ne->setTrack(ntrack); ne->setParent(seg); undo(new AddElement(ne)); } } return; } if (et == Element::Type::FINGERING || (et == Element::Type::IMAGE && element->parent()->type() != Element::Type::SEGMENT) || (et == Element::Type::SYMBOL && element->parent()->type() != Element::Type::SEGMENT) || et == Element::Type::NOTE || et == Element::Type::TEXT || et == Element::Type::GLISSANDO || et == Element::Type::BEND || (et == Element::Type::CHORD && static_cast(element)->isGrace()) ) { Element* parent = element->parent(); const LinkedElements* links = parent->links(); if (links == 0) { undo(new AddElement(element)); if (element->type() == Element::Type::FINGERING) element->score()->layoutFingering(static_cast(element)); else if (element->type() == Element::Type::CHORD) { #ifndef QT_NO_DEBUG for (Note* n : static_cast(element)->notes()) { // if(n->tpc() == Tpc::TPC_INVALID) // n->setTpcFromPitch(); Q_ASSERT(n->tpc() != Tpc::TPC_INVALID); } #endif element->score()->updateNotes(); } return; } foreach (Element* e, *links) { Element* ne = (e == parent) ? element : element->linkedClone(); ne->setScore(e->score()); ne->setSelected(false); ne->setParent(e); undo(new AddElement(ne)); if (ne->type() == Element::Type::FINGERING) e->score()->layoutFingering(static_cast(ne)); else if (ne->type() == Element::Type::CHORD) { #ifndef QT_NO_DEBUG for (Note* n : static_cast(ne)->notes()) { Q_ASSERT(n->tpc() != Tpc::TPC_INVALID); // n->setTpcFromPitch(); } #endif ne->score()->updateNotes(); } } return; } if (et == Element::Type::LAYOUT_BREAK) { LayoutBreak* lb = static_cast(element); if (lb->layoutBreakType() == LayoutBreak::Type::SECTION) { Measure* m = lb->measure(); foreach(Score* s, scoreList()) { if (s == lb->score()) undo(new AddElement(lb)); else { Element* e = lb->linkedClone(); Measure* nm = s->tick2measure(m->tick()); e->setParent(nm); undo(new AddElement(e)); } } return; } } if (ostaff == 0 || ( et != Element::Type::ARTICULATION && et != Element::Type::CHORDLINE && et != Element::Type::SLUR && et != Element::Type::TIE && et != Element::Type::NOTE && et != Element::Type::INSTRUMENT_CHANGE && et != Element::Type::HAIRPIN && et != Element::Type::OTTAVA && et != Element::Type::TRILL && et != Element::Type::TEXTLINE && et != Element::Type::PEDAL && et != Element::Type::BREATH && et != Element::Type::DYNAMIC && et != Element::Type::STAFF_TEXT && et != Element::Type::TREMOLO && et != Element::Type::ARPEGGIO && et != Element::Type::SYMBOL && et != Element::Type::FRET_DIAGRAM && et != Element::Type::HARMONY) ) { undo(new AddElement(element)); return; } foreach(Staff* staff, ostaff->staffList()) { Score* score = staff->score(); int staffIdx = score->staffIdx(staff); 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 Element::Type::STAFF_TEXT: case Element::Type::FRET_DIAGRAM: case Element::Type::HARMONY: case Element::Type::FIGURED_BASS: case Element::Type::LYRICS: case Element::Type::DYNAMIC: continue; default: break; } } ne = element->linkedClone(); ne->setScore(score); ne->setSelected(false); ne->setTrack(staffIdx * VOICES + element->voice()); } if (element->type() == Element::Type::ARTICULATION) { Articulation* a = static_cast(element); Segment* segment; Segment::Type st; Measure* m; int tick; if (a->parent()->isChordRest()) { ChordRest* cr = a->chordRest(); segment = cr->segment(); st = Segment::Type::ChordRest; tick = segment->tick(); m = score->tick2measure(tick); } else { segment = static_cast(a->parent()->parent()); st = Segment::Type::EndBarLine; tick = segment->tick(); m = score->tick2measure(tick); if (m->tick() == tick) m = m->prevMeasure(); } Segment* seg = m->findSegment(st, tick); if (seg == 0) { qDebug("undoAddSegment: segment not found"); break; } Articulation* na = static_cast(ne); int ntrack = staffIdx * VOICES + a->voice(); na->setTrack(ntrack); if (a->parent()->isChordRest()) { ChordRest* ncr = static_cast(seg->element(ntrack)); na->setParent(ncr); } else { BarLine* bl = static_cast(seg->element(ntrack)); na->setParent(bl); } undo(new AddElement(na)); } else if (element->type() == Element::Type::CHORDLINE) { ChordLine* a = static_cast(element); Segment* segment = a->chord()->segment(); int tick = segment->tick(); Measure* m = score->tick2measure(tick); Segment* seg = m->findSegment(Segment::Type::ChordRest, tick); if (seg == 0) { qDebug("undoAddSegment: segment not found"); break; } int ntrack = staffIdx * VOICES + a->voice(); ne->setTrack(ntrack); ChordRest* ncr = static_cast(seg->element(ntrack)); ne->setParent(ncr); undo(new AddElement(ne)); } // // elements with Segment as parent // else if (element->type() == Element::Type::SYMBOL || element->type() == Element::Type::IMAGE || element->type() == Element::Type::DYNAMIC || element->type() == Element::Type::STAFF_TEXT || element->type() == Element::Type::FRET_DIAGRAM || element->type() == Element::Type::HARMONY) { Segment* segment = static_cast(element->parent()); int tick = segment->tick(); Measure* m = score->tick2measure(tick); Segment* seg = m->undoGetSegment(Segment::Type::ChordRest, tick); int ntrack = staffIdx * VOICES + element->voice(); ne->setTrack(ntrack); ne->setParent(seg); undo(new AddElement(ne)); } else if (element->type() == Element::Type::SLUR || element->type() == Element::Type::HAIRPIN || element->type() == Element::Type::OTTAVA || element->type() == Element::Type::TRILL || element->type() == Element::Type::TEXTLINE || element->type() == Element::Type::PEDAL) { Spanner* sp = static_cast(element); Spanner* nsp = static_cast(ne); int staffIdx1 = sp->track() / VOICES; int staffIdx2 = sp->track2() / VOICES; int diff = staffIdx2 - staffIdx1; nsp->setTrack2((staffIdx + diff) * VOICES + (sp->track2() % VOICES)); // 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->type() == Element::Type::SLUR && sp != nsp) { if (sp->startElement()) { QList sel = sp->startElement()->linkList(); for (Element* e : sel) { if (e->score() == nsp->score() && e->track() == nsp->track()) { nsp->setStartElement(e); break; } } } if (sp->endElement()) { QList eel = sp->endElement()->linkList(); for (Element* e : eel) { if (e->score() == nsp->score() && e->track() == nsp->track2()) { nsp->setEndElement(e); break; } } } } undo(new AddElement(nsp)); } else if (element->type() == Element::Type::TREMOLO && static_cast(element)->twoNotes()) { Tremolo* tremolo = static_cast(element); ChordRest* cr1 = static_cast(tremolo->chord1()); ChordRest* cr2 = static_cast(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 = static_cast(ns1->element(staffIdx * VOICES + cr1->voice())); Chord* c2 = static_cast(ns2->element(staffIdx * VOICES + cr2->voice())); Tremolo* ntremolo = static_cast(ne); ntremolo->setChords(c1, c2); ntremolo->setParent(c1); undo(new AddElement(ntremolo)); } else if ( (element->type() == Element::Type::TREMOLO && !static_cast(element)->twoNotes()) || (element->type() == Element::Type::ARPEGGIO)) { ChordRest* cr = static_cast(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 = static_cast(ns->element(staffIdx * VOICES + cr->voice())); ne->setParent(c1); undo(new AddElement(ne)); } else if (element->type() == Element::Type::TIE) { Tie* tie = static_cast(element); Note* n1 = tie->startNote(); Note* n2 = tie->endNote(); Chord* cr1 = n1->chord(); Chord* cr2 = n2 ? n2->chord() : 0; Segment* s1 = cr1->segment(); Segment* s2 = cr2 ? cr2->segment() : 0; Measure* nm1 = score->tick2measure(s1->tick()); Measure* nm2 = s2 ? score->tick2measure(s2->tick()) : 0; Segment* ns1; Segment* ns2; ns1 = nm1->findSegment(s1->segmentType(), s1->tick()); ns2 = nm2 ? nm2->findSegment(s2->segmentType(), s2->tick()) : 0; Chord* c1 = static_cast(ns1->element(staffIdx * VOICES + cr1->voice())); int sm = 0; if (cr1->staffIdx() != cr2->staffIdx()) sm = cr1->staffMove() + cr2->staffMove(); Chord* c2 = 0; if (ns2) { Element* e = ns2->element((staffIdx + sm) * VOICES + cr2->voice()); if (e->type() == Element::Type::CHORD) c2 = static_cast(e); } Note* nn1 = c1->findNote(n1->pitch()); Note* nn2 = c2 ? c2->findNote(n2->pitch()) : 0; Tie* ntie = static_cast(ne); QList& segments = ntie->spannerSegments(); foreach(SpannerSegment* segment, segments) delete segment; segments.clear(); ntie->setTrack(c1->track()); ntie->setStartNote(nn1); ntie->setEndNote(nn2); undo(new AddElement(ntie)); } else if (element->type() == Element::Type::INSTRUMENT_CHANGE) { InstrumentChange* is = static_cast(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 = static_cast(ne); nis->setParent(ns1); if (is->instrument().channel().isEmpty() || is->instrument().channel(0).program == -1) nis->setInstrument(*staff->part()->instr(s1->tick())); else nis->setInstrument(is->instrument()); undo(new AddElement(nis)); undo(new ChangeInstrument(nis, nis->instrument())); } else if (element->type() == Element::Type::BREATH) { Breath* breath = static_cast(element); int tick = breath->segment()->tick(); Measure* m = score->tick2measure(tick); Segment* seg = m->undoGetSegment(Segment::Type::Breath, tick); Breath* nbreath = static_cast(ne); int ntrack = staffIdx * VOICES + nbreath->voice(); nbreath->setScore(score); nbreath->setTrack(ntrack); nbreath->setParent(seg); undo(new AddElement(nbreath)); } else qDebug("undoAddElement: unhandled: <%s>", element->name()); } } //--------------------------------------------------------- // undoAddCR //--------------------------------------------------------- void Score::undoAddCR(ChordRest* cr, Measure* measure, int tick) { Q_ASSERT(cr->type() != Element::Type::CHORD || !(static_cast(cr)->notes()).isEmpty()); Q_ASSERT(cr->isChordRest()); Staff* ostaff = cr->staff(); Segment::Type segmentType = Segment::Type::ChordRest; Tuplet* t = cr->tuplet(); foreach (Staff* staff, ostaff->staffList()) { Score* score = staff->score(); Measure* m = (score == this) ? measure : score->tick2measure(tick); Segment* seg = m->undoGetSegment(segmentType, tick); Q_ASSERT(seg->segmentType() == segmentType); ChordRest* newcr = (staff == ostaff) ? cr : static_cast(cr->linkedClone()); newcr->setScore(score); int staffIdx = score->staffIdx(staff); int ntrack = staffIdx * VOICES + cr->voice(); newcr->setTrack(ntrack); newcr->setParent(seg); #ifndef QT_NO_DEBUG if (newcr->type() == Element::Type::CHORD) { Chord* chord = static_cast(newcr); // setTpcFromPitch needs to know the note tick position foreach(Note* note, chord->notes()) { // if (note->tpc() == Tpc::TPC_INVALID) // note->setTpcFromPitch(); Q_ASSERT(note->tpc() != Tpc::TPC_INVALID); } } #endif if (t) { Tuplet* nt = 0; if (staff == ostaff) nt = t; else { if (t->elements().empty() || t->elements().front() == cr) { nt = static_cast(t->linkedClone()); nt->setScore(score); } else { const LinkedElements* le = t->links(); // search the linked tuplet foreach(Element* e, *le) { if (e->score() == score && e->track() == ntrack) { nt = static_cast(e); break; } } if (nt == 0) qDebug("linked tuplet not found"); } newcr->setTuplet(nt); } } undo(new AddElement(newcr)); m->cmdUpdateNotes(staffIdx); } } //--------------------------------------------------------- // undoRemoveElement //--------------------------------------------------------- void Score::undoRemoveElement(Element* element) { QList segments; for (Element* e : element->linkList()) { undo(new RemoveElement(e)); if (e->parent() && (e->parent()->type() == Element::Type::SEGMENT)) { Segment* s = static_cast(e->parent()); if (!segments.contains(s)) segments.append(s); } } for (Segment* s : segments) { if (s->isEmpty()) undo(new RemoveElement(s)); } } //--------------------------------------------------------- // undoChangeTuning //--------------------------------------------------------- void Score::undoChangeTuning(Note* n, qreal v) { undoChangeProperty(n, P_ID::TUNING, v); } void Score::undoChangeUserMirror(Note* n, MScore::DirectionH d) { undoChangeProperty(n, P_ID::MIRROR_HEAD, int(d)); } //--------------------------------------------------------- // undoChangePageFormat //--------------------------------------------------------- void Score::undoChangePageFormat(PageFormat* p, qreal v, int pageOffset) { undo(new ChangePageFormat(this, p, v, pageOffset)); } //--------------------------------------------------------- // undoChangeTpc // TODO-TPC: check //--------------------------------------------------------- void Score::undoChangeTpc(Note* note, int v) { note->undoChangeProperty(P_ID::TPC1, v); } //--------------------------------------------------------- // AddElement //--------------------------------------------------------- AddElement::AddElement(Element* e) { element = e; } //--------------------------------------------------------- // undoRemoveTuplet //--------------------------------------------------------- static void undoRemoveTuplet(DurationElement* cr) { if (cr->tuplet()) { cr->tuplet()->remove(cr); if (cr->tuplet()->elements().isEmpty()) undoRemoveTuplet(cr->tuplet()); } } //--------------------------------------------------------- // undoAddTuplet //--------------------------------------------------------- static void undoAddTuplet(DurationElement* cr) { if (cr->tuplet()) { cr->tuplet()->add(cr); if (cr->tuplet()->elements().size() == 1) undoAddTuplet(cr->tuplet()); } } //--------------------------------------------------------- // endUndoRedo //--------------------------------------------------------- void AddElement::endUndoRedo(bool isUndo) const { if (element->type() == Element::Type::TIE) { Tie* tie = static_cast(element); Measure* m1 = tie->startNote()->chord()->measure(); Measure* m2 = 0; if(tie->endNote()) m2 = tie->endNote()->chord()->measure(); if (m1 != m2) { m1->cmdUpdateNotes(tie->staffIdx()); if (m2) m2->cmdUpdateNotes(tie->staffIdx()); // tie->score()->cmdUpdateNotes(); } else m1->cmdUpdateNotes(tie->staffIdx()); } else if (element->isChordRest()) { if (isUndo) undoRemoveTuplet(static_cast(element)); else undoAddTuplet(static_cast(element)); } else if (element->type() == Element::Type::NOTE) { Measure* m = static_cast(element)->chord()->measure(); m->cmdUpdateNotes(element->staffIdx()); } } //--------------------------------------------------------- // undo //--------------------------------------------------------- void AddElement::undo() { // qDebug("AddElement::undo: %s %p parent %s %p", element->name(), element, // element->parent() ? element->parent()->name() : "nil", element->parent()); element->score()->removeElement(element); endUndoRedo(true); } //--------------------------------------------------------- // redo //--------------------------------------------------------- void AddElement::redo() { // qDebug("AddElement::redo: %s %p parent %s %p, score %p", element->name(), element, // element->parent() ? element->parent()->name() : "nil", element->parent(), element->score()); element->score()->addElement(element); endUndoRedo(false); } //--------------------------------------------------------- // name //--------------------------------------------------------- #ifdef DEBUG_UNDO const char* AddElement::name() const { static char buffer[64]; sprintf(buffer, "Add: %s", element->name()); return buffer; } #endif //--------------------------------------------------------- // RemoveElement //--------------------------------------------------------- RemoveElement::RemoveElement(Element* e) { element = e; Score* score = element->score(); if (element->isChordRest()) { bool noteEntryMode = false; Slur* slur = 0; for (Score* sc : score->scoreList()) { if (sc->noteEntryMode()) { noteEntryMode = true; slur = sc->inputState().slur(); break; } } // remove any slurs pointing to this chor/rest if (slur) { QList sl; for (auto i : score->spanner()) { // TODO: dont search whole list Spanner* s = i.second; // do not delete slur if in note entry mode if (noteEntryMode && slur->linkList().contains(s)) { if (s->startElement() == e) s->setStartElement(nullptr); else if (s->endElement() == e) s->setEndElement(nullptr); continue; } if (s->type() == Element::Type::SLUR && (s->startElement() == e || s->endElement() == e)) { sl.append(s); } } for (auto s : sl) score->undo(new RemoveElement(s)); } ChordRest* cr = static_cast(element); if (cr->tuplet() && cr->tuplet()->elements().empty()) score->undo(new RemoveElement(cr->tuplet())); if (e->type() == Element::Type::CHORD) { Chord* chord = static_cast(e); // remove tremolo between 2 notes if (chord->tremolo()) { Tremolo* tremolo = chord->tremolo(); if (tremolo->twoNotes()) score->undo(new RemoveElement(tremolo)); } for (const Note* note : chord->notes()) { if (note->tieFor() && note->tieFor()->endNote()) score->undo(new RemoveElement(note->tieFor())); if (note->tieBack()) score->undo(new RemoveElement(note->tieBack())); } } } } //--------------------------------------------------------- // undo //--------------------------------------------------------- void RemoveElement::undo() { element->score()->addElement(element); if (element->isChordRest()) { if (element->type() == Element::Type::CHORD) { Chord* chord = static_cast(element); foreach(Note* note, chord->notes()) { if (note->tieBack()) note->tieBack()->setEndNote(note); if (note->tieFor() && note->tieFor()->endNote()) note->tieFor()->endNote()->setTieBack(note->tieFor()); } } undoAddTuplet(static_cast(element)); } } //--------------------------------------------------------- // redo //--------------------------------------------------------- void RemoveElement::redo() { element->score()->removeElement(element); if (element->isChordRest()) { undoRemoveTuplet(static_cast(element)); if (element->type() == Element::Type::CHORD) { Chord* chord = static_cast(element); Note* endNote = 0; // find one instance of endNote foreach(Note* note, chord->notes()) { if (note->tieFor() && note->tieFor()->endNote()) { endNote = note->tieFor()->endNote(); note->tieFor()->endNote()->setTieBack(0); } } if (endNote) { // update accidentals in endNotes's measure Chord* eChord = endNote->chord(); Measure* m = eChord->segment()->measure(); m->cmdUpdateNotes(eChord->staffIdx()); } } } } //--------------------------------------------------------- // name //--------------------------------------------------------- #ifdef DEBUG_UNDO const char* RemoveElement::name() const { static char buffer[64]; sprintf(buffer, "Remove: %s", element->name()); return buffer; } #endif //--------------------------------------------------------- // ChangeConcertPitch //--------------------------------------------------------- ChangeConcertPitch::ChangeConcertPitch(Score* s, bool v) { score = s; val = v; } //--------------------------------------------------------- // flip //--------------------------------------------------------- void ChangeConcertPitch::flip() { int oval = int(score->styleB(StyleIdx::concertPitch)); score->style()->set(StyleIdx::concertPitch, val); score->setLayoutAll(true); val = oval; } //--------------------------------------------------------- // InsertPart //--------------------------------------------------------- InsertPart::InsertPart(Part* p, int i) { part = p; idx = i; } void InsertPart::undo() { part->score()->removePart(part); } void InsertPart::redo() { part->score()->insertPart(part, idx); } //--------------------------------------------------------- // RemovePart //--------------------------------------------------------- RemovePart::RemovePart(Part* p, int i) { part = p; idx = i; } void RemovePart::undo() { part->score()->insertPart(part, idx); } void RemovePart::redo() { part->score()->removePart(part); } //--------------------------------------------------------- // InsertStaff //--------------------------------------------------------- InsertStaff::InsertStaff(Staff* p, int i) { staff = p; ridx = i; } void InsertStaff::undo() { staff->score()->removeStaff(staff); } void InsertStaff::redo() { staff->score()->insertStaff(staff, ridx); } //--------------------------------------------------------- // RemoveStaff //--------------------------------------------------------- RemoveStaff::RemoveStaff(Staff* p) { staff = p; ridx = staff->rstaff(); } void RemoveStaff::undo() { staff->score()->insertStaff(staff, ridx); } void RemoveStaff::redo() { staff->score()->removeStaff(staff); } //--------------------------------------------------------- // InsertMStaff //--------------------------------------------------------- InsertMStaff::InsertMStaff(Measure* m, MStaff* ms, int i) { measure = m; mstaff = ms; idx = i; } void InsertMStaff::undo() { measure->removeMStaff(mstaff, idx); } void InsertMStaff::redo() { measure->insertMStaff(mstaff, idx); } //--------------------------------------------------------- // RemoveMStaff //--------------------------------------------------------- RemoveMStaff::RemoveMStaff(Measure* m, MStaff* ms, int i) { measure = m; mstaff = ms; idx = i; } void RemoveMStaff::undo() { measure->insertMStaff(mstaff, idx); } void RemoveMStaff::redo() { measure->removeMStaff(mstaff, idx); } //--------------------------------------------------------- // InsertMeasure //--------------------------------------------------------- void InsertMeasure::undo() { Score* score = measure->score(); score->measures()->remove(measure); score->addLayoutFlags(LayoutFlag::FIX_TICKS); score->setLayoutAll(true); } void InsertMeasure::redo() { Score* score = measure->score(); score->addMeasure(measure, pos); score->addLayoutFlags(LayoutFlag::FIX_TICKS); score->setLayoutAll(true); } //--------------------------------------------------------- // SortStaves //--------------------------------------------------------- SortStaves::SortStaves(Score* s, QList l) { score = s; for(int i=0 ; i < l.size(); i++) { rlist.append(l.indexOf(i)); } list = l; } void SortStaves::redo() { score->sortStaves(list); } void SortStaves::undo() { score->sortStaves(rlist); } //--------------------------------------------------------- // ChangePitch //--------------------------------------------------------- ChangePitch::ChangePitch(Note* _note, int _pitch, int _tpc1, int _tpc2) { note = _note; pitch = _pitch; tpc1 = _tpc1; tpc2 = _tpc2; } void ChangePitch::flip() { int f_pitch = note->pitch(); int f_tpc1 = note->tpc1(); int f_tpc2 = note->tpc2(); if (f_pitch == pitch && f_tpc1 == tpc1 && f_tpc2 == tpc2) { // do not change unless necessary: setting note pitch triggers chord re-fretting on TABs // which triggers ChangePitch(), leading to recursion with negative side effects return; } note->setPitch(pitch, tpc1, tpc2); pitch = f_pitch; tpc1 = f_tpc1; tpc2 = f_tpc2; Chord* chord = note->chord(); chord->measure()->cmdUpdateNotes(chord->staffIdx()); note->score()->setLayoutAll(true); } //--------------------------------------------------------- // ChangeElement //--------------------------------------------------------- ChangeElement::ChangeElement(Element* oe, Element* ne) { oldElement = oe; newElement = ne; } void ChangeElement::flip() { // qDebug("ChangeElement::flip() %s(%p) -> %s(%p) links %d", // oldElement->name(), oldElement, newElement->name(), newElement, // oldElement->links() ? oldElement->links()->size() : -1); const LinkedElements* links = oldElement->links(); if (links) { oldElement->unlink(); oldElement->linkTo(newElement); } Score* score = oldElement->score(); if (oldElement->selected()) score->deselect(oldElement); if (newElement->selected()) score->select(newElement); if (oldElement->parent() == 0) { score->removeElement(oldElement); score->addElement(newElement); } else { oldElement->parent()->change(oldElement, newElement); } if (newElement->type() == Element::Type::KEYSIG) { KeySig* ks = static_cast(newElement); if (!ks->generated()) ks->staff()->setKey(ks->tick(), ks->key()); } else if (newElement->type() == Element::Type::DYNAMIC) newElement->score()->addLayoutFlags(LayoutFlag::FIX_PITCH_VELO); else if (newElement->type() == Element::Type::TEMPO_TEXT) { TempoText* t = static_cast(oldElement); score->setTempo(t->segment(), t->tempo()); } if (newElement->isSegment()) { SpannerSegment* os = static_cast(oldElement); SpannerSegment* ns = static_cast(newElement); if (os->system()) os->system()->remove(os); if (ns->system()) ns->system()->add(ns); } qSwap(oldElement, newElement); score->setLayoutAll(true); } //--------------------------------------------------------- // InsertStaves //--------------------------------------------------------- InsertStaves::InsertStaves(Measure* m, int _a, int _b) { measure = m; a = _a; b = _b; } void InsertStaves::undo() { measure->removeStaves(a, b); } void InsertStaves::redo() { measure->insertStaves(a, b); } //--------------------------------------------------------- // RemoveStaves //--------------------------------------------------------- RemoveStaves::RemoveStaves(Measure* m, int _a, int _b) { measure = m; a = _a; b = _b; } void RemoveStaves::undo() { measure->insertStaves(a, b); } void RemoveStaves::redo() { measure->removeStaves(a, b); } //--------------------------------------------------------- // ChangeKeySig::flip //--------------------------------------------------------- void ChangeKeySig::flip() { KeySigEvent oe = keysig->keySigEvent(); bool sc = keysig->showCourtesy(); keysig->setKeySigEvent(ks); keysig->setShowCourtesy(showCourtesy); int tick = keysig->segment()->tick(); // update keys if keysig was not generated if (!keysig->generated()) keysig->staff()->setKey(tick, ks.key()); showCourtesy = sc; ks = oe; Measure* m = keysig->score()->tick2measure(keysig->staff()->currentKeyTick(tick)); for (; m; m = m->nextMeasure()) { m->cmdUpdateNotes(keysig->staffIdx()); } keysig->score()->setLayoutAll(true); } //--------------------------------------------------------- // ChangeMeasureLen //--------------------------------------------------------- ChangeMeasureLen::ChangeMeasureLen(Measure* m, Fraction l) { measure = m; len = l; } void ChangeMeasureLen::flip() { Fraction oLen = measure->len(); // // move EndBarLine and TimeSigAnnounce // to end of measure: // int endTick = measure->tick() + len.ticks(); for (Segment* segment = measure->first(); segment; segment = segment->next()) { if (segment->segmentType() != Segment::Type::EndBarLine && segment->segmentType() != Segment::Type::TimeSigAnnounce) continue; segment->setTick(endTick); } measure->setLen(len); // measure->score()->addLayoutFlags(LAYOUT_FIX_TICKS); // we need to fix tick immediately! measure->score()->fixTicks(); len = oLen; } //--------------------------------------------------------- // ChangeVoltaEnding //--------------------------------------------------------- ChangeVoltaEnding::ChangeVoltaEnding(Volta* v, const QList& l) { volta = v; list = l; } void ChangeVoltaEnding::flip() { QList l = volta->endings(); volta->setEndings(list); list = l; } //--------------------------------------------------------- // ChangeVoltaText //--------------------------------------------------------- ChangeVoltaText::ChangeVoltaText(Volta* v, const QString& t) { volta = v; text = t; } void ChangeVoltaText::flip() { QString s = volta->text(); volta->setText(text); text = s; } //--------------------------------------------------------- // ChangeChordRestSize //--------------------------------------------------------- ChangeChordRestSize::ChangeChordRestSize(ChordRest* _cr, bool _small) { cr = _cr; small = _small; } void ChangeChordRestSize::flip() { bool s = cr->small(); cr->setSmall(small); small = s; } //--------------------------------------------------------- // ChangeChordNoStem //--------------------------------------------------------- ChangeChordNoStem::ChangeChordNoStem(Chord* c, bool f) { chord = c; noStem = f; } void ChangeChordNoStem::flip() { bool ns = chord->noStem(); chord->setNoStem(noStem); noStem = ns; } //--------------------------------------------------------- // ChangeEndBarLineType //--------------------------------------------------------- ChangeEndBarLineType::ChangeEndBarLineType(Measure* m, BarLineType st) { measure = m; subtype = st; } void ChangeEndBarLineType::flip() { BarLineType typ = measure->endBarLineType(); measure->setEndBarLineType(subtype, false); subtype = typ; } //--------------------------------------------------------- // ChangeBarLineSpan //--------------------------------------------------------- ChangeBarLineSpan::ChangeBarLineSpan(Staff* _staff, int _span, int _spanFrom, int _spanTo) { staff = _staff; span = _span; spanFrom = _spanFrom; spanTo = _spanTo; } void ChangeBarLineSpan::flip() { int nspan = staff->barLineSpan(); int nspanFrom = staff->barLineFrom(); int nspanTo = staff->barLineTo(); staff->setBarLineSpan(span); staff->setBarLineFrom(spanFrom); staff->setBarLineTo(spanTo); span = nspan; spanFrom = nspanFrom; spanTo = nspanTo; // all bar lines of this staff across the whole score needs to be re-laid out and re-drawn staff->score()->setLayoutAll(true); } //--------------------------------------------------------- // ChangeSingleBarLineSpan //--------------------------------------------------------- ChangeSingleBarLineSpan::ChangeSingleBarLineSpan(BarLine* _barLine, int _span, int _spanFrom, int _spanTo) { barLine = _barLine; span = _span; spanFrom = _spanFrom; spanTo = _spanTo; } void ChangeSingleBarLineSpan::flip() { barLine->score()->addRefresh(barLine->canvasBoundingRect()); // area of this bar line needs redraw int nspan = barLine->span(); bool respan = (span != nspan); int nspanFrom = barLine->spanFrom(); int nspanTo = barLine->spanTo(); barLine->setSpan(span); barLine->setSpanFrom(spanFrom); barLine->setSpanTo(spanTo); // barLine->setCustomSpan(true); // let setSpan(), setSpanFrom() and setSpanTo() determine if it is custom or not span = nspan; spanFrom = nspanFrom; spanTo = nspanTo; barLine->layout(); // update bbox // re-create bar lines for other staves, if span of this bar line changed if (respan && barLine->parent() && barLine->parent()->type() == Element::Type::SEGMENT) { Segment * segm = (static_cast(barLine->parent())); Measure * meas = segm->measure(); // if it is a start-reapeat bar line at the beginning of a measure, redo measure start bar lines if (barLine->barLineType() == BarLineType::START_REPEAT && segm->segmentType() == Segment::Type::StartRepeatBarLine) meas->setStartRepeatBarLine(true); // otherwise redo measure end bar lines else meas->createEndBarLines(); } barLine->score()->addRefresh(barLine->canvasBoundingRect()); // new area of this bar line needs redraw } //--------------------------------------------------------- // TransposeHarmony //--------------------------------------------------------- TransposeHarmony::TransposeHarmony(Harmony* h, int rtpc, int btpc) { harmony = h; rootTpc = rtpc; baseTpc = btpc; } void TransposeHarmony::flip() { int baseTpc1 = harmony->baseTpc(); int rootTpc1 = harmony->rootTpc(); harmony->setBaseTpc(baseTpc); harmony->setRootTpc(rootTpc); harmony->render(); rootTpc = rootTpc1; baseTpc = baseTpc1; } //--------------------------------------------------------- // ExchangeVoice //--------------------------------------------------------- ExchangeVoice::ExchangeVoice(Measure* m, int _val1, int _val2, int _staff1, int _staff2) { measure = m; val1 = _val1; val2 = _val2; staff1 = _staff1; staff2 = _staff2; } void ExchangeVoice::undo() { measure->exchangeVoice(val2, val1, staff1, staff2); } void ExchangeVoice::redo() { measure->exchangeVoice(val1, val2, staff1, staff2); } //--------------------------------------------------------- // ChangeInstrumentShort //--------------------------------------------------------- ChangeInstrumentShort::ChangeInstrumentShort(int _tick, Part* p, QList t) { tick = _tick; part = p; text = t; } void ChangeInstrumentShort::flip() { QList s = part->shortNames(tick); part->setShortNames(text, tick); text = s; part->score()->setLayoutAll(true); } //--------------------------------------------------------- // ChangeInstrumentLong //--------------------------------------------------------- ChangeInstrumentLong::ChangeInstrumentLong(int _tick, Part* p, QList t) { tick = _tick; part = p; text = t; } void ChangeInstrumentLong::flip() { QList s = part->longNames(tick); part->setLongNames(text, tick); text = s; part->score()->setLayoutAll(true); } //--------------------------------------------------------- // ChangeChordRestLen //--------------------------------------------------------- ChangeChordRestLen::ChangeChordRestLen(ChordRest* c, const TDuration& _d) : cr(c), d(_d) { Q_ASSERT(c); } void ChangeChordRestLen::flip() { TDuration od = cr->durationType(); cr->setDurationType(d); if (d == TDuration::DurationType::V_MEASURE) { cr->setDuration(cr->measure()->len()); } else { cr->setDuration(d.fraction()); } d = od; cr->score()->setLayoutAll(true); } //--------------------------------------------------------- // ChangeChordRestDuration /// Used to change the duration only. /// Mainly used for full time rest to make them look different for 8/4 and up. //--------------------------------------------------------- ChangeChordRestDuration::ChangeChordRestDuration(ChordRest* c, const Fraction& _f) : cr(c), f(_f) { } void ChangeChordRestDuration::flip() { Fraction od = cr->duration(); cr->setDuration(f); f = od; } //--------------------------------------------------------- // ChangeBracketSpan //--------------------------------------------------------- ChangeBracketSpan::ChangeBracketSpan(Staff* s, int c, int sp) { staff = s; column = c; span = sp; } void ChangeBracketSpan::flip() { int oSpan = staff->bracketSpan(column); staff->setBracketSpan(column, span); span = oSpan; staff->score()->setLayoutAll(true); } //--------------------------------------------------------- // EditText::undo //--------------------------------------------------------- void EditText::undo() { /* if (!text->styled()) { for (int i = 0; i < undoLevel; ++i) text->undo(); } */ undoRedo(); } //--------------------------------------------------------- // EditText::redo //--------------------------------------------------------- void EditText::redo() { /* if (!text->styled()) { for (int i = 0; i < undoLevel; ++i) text->redo(); } */ undoRedo(); } //--------------------------------------------------------- // EditText::undoRedo //--------------------------------------------------------- void EditText::undoRedo() { QString s = text->text(); text->setText(oldText); oldText = s; text->score()->setLayoutAll(true); } //--------------------------------------------------------- // ChangePatch //--------------------------------------------------------- void ChangePatch::flip() { MidiPatch op; op.prog = channel->program; op.bank = channel->bank; op.synti = channel->synti; channel->program = patch.prog; channel->bank = patch.bank; channel->synti = patch.synti; patch = op; if (MScore::seq == 0) { qDebug("ChangePatch: no seq"); return; } NPlayEvent event; event.setType(ME_CONTROLLER); event.setChannel(channel->channel); int hbank = (channel->bank >> 7) & 0x7f; int lbank = channel->bank & 0x7f; event.setController(CTRL_HBANK); event.setValue(hbank); MScore::seq->sendEvent(event); event.setController(CTRL_LBANK); event.setValue(lbank); MScore::seq->sendEvent(event); event.setController(CTRL_PROGRAM); event.setValue(channel->program); MScore::seq->sendEvent(event); } //--------------------------------------------------------- // ChangePageFormat //--------------------------------------------------------- ChangePageFormat::ChangePageFormat(Score* cs, PageFormat* p, qreal s, int po) { score = cs; pf = new PageFormat; pf->copy(*p); spatium = s; pageOffset = po; } ChangePageFormat::~ChangePageFormat() { delete pf; } //--------------------------------------------------------- // flip //--------------------------------------------------------- void ChangePageFormat::flip() { PageFormat f; f.copy(*(score->pageFormat())); qreal os = score->spatium(); int po = score->pageNumberOffset(); score->setPageFormat(*pf); if (os != spatium) { score->setSpatium(spatium); score->spatiumChanged(os, spatium); } score->setPageNumberOffset(pageOffset); score->setLayoutAll(true); pf->copy(f); spatium = os; pageOffset = po; } //--------------------------------------------------------- // ChangeStaff //--------------------------------------------------------- ChangeStaff::ChangeStaff(Staff* _staff, bool _small, bool _invisible, qreal _userDist, QColor _color, bool _neverHide, bool _showIfEmpty) { staff = _staff; small = _small; invisible = _invisible; userDist = _userDist; color = _color; neverHide = _neverHide; showIfEmpty = _showIfEmpty; } //--------------------------------------------------------- // notifyTimeSigs // mark timesigs for layout //--------------------------------------------------------- static void notifyTimeSigs(void*, Element* e) { if (e->type() == Element::Type::TIMESIG) static_cast(e)->setNeedLayout(true); } //--------------------------------------------------------- // flip //--------------------------------------------------------- void ChangeStaff::flip() { bool invisibleChanged = staff->invisible() != invisible; int oldSmall = staff->small(); bool oldInvisible = staff->invisible(); qreal oldUserDist = staff->userDist(); QColor oldColor = staff->color(); bool oldNeverHide = staff->neverHide(); bool oldShowIfEmpty = staff->showIfEmpty(); staff->setSmall(small); staff->setInvisible(invisible); staff->setUserDist(userDist); staff->setColor(color); staff->setNeverHide(neverHide); staff->setShowIfEmpty(showIfEmpty); small = oldSmall; invisible = oldInvisible; userDist = oldUserDist; color = oldColor; neverHide = oldNeverHide; showIfEmpty = oldShowIfEmpty; Score* score = staff->score(); if (invisibleChanged) { int staffIdx = score->staffIdx(staff); for (Measure* m = score->firstMeasure(); m; m = m->nextMeasure()) { MStaff* mstaff = m->mstaff(staffIdx); mstaff->lines->setVisible(!staff->invisible()); } } staff->score()->setLayoutAll(true); staff->score()->rebuildMidiMapping(); staff->score()->setPlaylistDirty(true); score->scanElements(0, notifyTimeSigs); } //--------------------------------------------------------- // ChangeStaffType::undo / redo //--------------------------------------------------------- void ChangeStaffType::redo() { initialClef = staff->initialClefTypeList(); StaffType st = *staff->staffType(); bool updateNotesNeeded = st.group() != staffType.group(); staff->setStaffType(&staffType); if (st.group() != StaffGroup::STANDARD && staffType.group() == StaffGroup::STANDARD) staff->setInitialClef(staff->part()->instr(0)->clefType()); staffType = st; Score* score = staff->score(); if (updateNotesNeeded) score->cmdUpdateNotes(); score->setLayoutAll(true); score->scanElements(0, notifyTimeSigs); } void ChangeStaffType::undo() { StaffType st = *staff->staffType(); bool updateNotesNeeded = st.group() != staffType.group(); staff->setStaffType(&staffType); staffType = st; // restore initial clef, both in the staff clef map... // staff->setClef(0, initialClef); // ...and in the score itself (code mostly copied from undoChangeClef() ) // TODO : add a single function adding/setting a clef change in score? // possibly directly in ClefList? int tick = 0; Score* score = staff->score(); Measure* measure = score->tick2measure(tick); if (!measure) { qDebug("measure for tick %d not found!", tick); return; } Segment* seg = measure->findSegment(Segment::Type::Clef, tick); int track = staff->idx() * VOICES; Clef* clef = static_cast(seg->element(track)); if (clef) { clef->setGenerated(false); clef->setClefType(initialClef); } else { clef = new Clef(score); clef->setTrack(track); clef->setClefType(initialClef); clef->setParent(seg); seg->add(clef); } if (updateNotesNeeded) score->cmdUpdateNotes(); score->setLayoutAll(true); score->scanElements(0, notifyTimeSigs); } //--------------------------------------------------------- // ChangePart //--------------------------------------------------------- ChangePart::ChangePart(Part* _part, const Instrument& i, const QString& s) { instrument = i; part = _part; partName = s; } //--------------------------------------------------------- // flip //--------------------------------------------------------- void ChangePart::flip() { Instrument oi = *part->instr(); QString s = part->partName(); part->setInstrument(instrument); part->setPartName(partName); Score* score = part->score(); score->rebuildMidiMapping(); score->setInstrumentsChanged(true); score->setPlaylistDirty(true); // Interval oint = oi.transpose(); // Interval nint = part->instr()->transpose(); // check if notes need to be updated // true if changing into or away from TAB or from one TAB type to another // bool updateNeeded = oi.stringData() != part->instr()->stringData(); // if (updateNeeded) score->cmdUpdateNotes(); score->setLayoutAll(true); partName = s; instrument = oi; } //--------------------------------------------------------- // ChangeTextStyle //--------------------------------------------------------- ChangeTextStyle::ChangeTextStyle(Score* s, const TextStyle& st) { score = s; style = st; } //--------------------------------------------------------- // updateTextStyle //--------------------------------------------------------- static void updateTextStyle(void* a, Element* e) { QString s = *(QString*)a; if (e->isText()) { Text* text = static_cast(e); if (text->textStyle().name() == s) { text->setTextStyle(text->score()->textStyle(s)); text->textStyleChanged(); } } } //--------------------------------------------------------- // flip //--------------------------------------------------------- void ChangeTextStyle::flip() { TextStyle os = score->style()->textStyle(style.name()); score->style()->setTextStyle(style); QString s(style.name()); score->scanElements(&s, updateTextStyle); style = os; score->setLayoutAll(true); } //--------------------------------------------------------- // AddTextStyle::undo //--------------------------------------------------------- void AddTextStyle::undo() { score->style()->removeTextStyle(style); } //--------------------------------------------------------- // AddTextStyle::redo //--------------------------------------------------------- void AddTextStyle::redo() { score->style()->addTextStyle(style); } //--------------------------------------------------------- // ChangeStretch //--------------------------------------------------------- ChangeStretch::ChangeStretch(Measure* m, qreal s) : measure(m), stretch(s) { } //--------------------------------------------------------- // flip //--------------------------------------------------------- void ChangeStretch::flip() { qreal oStretch = measure->userStretch(); measure->setUserStretch(stretch); measure->score()->setLayoutAll(true); stretch = oStretch; } //--------------------------------------------------------- // ChangeStyle //--------------------------------------------------------- ChangeStyle::ChangeStyle(Score* s, const MStyle& st) : score(s), style(st) { } static void updateTimeSigs(void*, Element* e) { if (e->type() == Element::Type::TIMESIG) { TimeSig* ts = static_cast(e); ts->setNeedLayout(true); } } static void updateTextStyle2(void*, Element* e) { e->styleChanged(); } //--------------------------------------------------------- // flip //--------------------------------------------------------- void ChangeStyle::flip() { MStyle tmp = *score->style(); if (score->style(StyleIdx::concertPitch) != style.value(StyleIdx::concertPitch)) score->cmdConcertPitchChanged(style.value(StyleIdx::concertPitch).toBool(), true); if (score->style(StyleIdx::MusicalSymbolFont) != style.value(StyleIdx::MusicalSymbolFont)) { score->setScoreFont(ScoreFont::fontFactory(style.value(StyleIdx::MusicalSymbolFont).toString())); score->scanElements(0, updateTimeSigs); } if (score->style()->spatium() != style.spatium()) score->spatiumChanged(score->style()->spatium(), style.spatium()); score->setStyle(style); score->scanElements(0, updateTextStyle2); score->setLayoutAll(true); style = tmp; } //--------------------------------------------------------- // ChangeStyleVal::flip //--------------------------------------------------------- void ChangeStyleVal::flip() { QVariant v = score->style(idx); score->style()->set(idx, value); score->setLayoutAll(true); value = v; } //--------------------------------------------------------- // ChangeChordStaffMove //--------------------------------------------------------- ChangeChordStaffMove::ChangeChordStaffMove(Chord* c, int v) : chord(c), staffMove(v) { } void ChangeChordStaffMove::flip() { const LinkedElements* l = chord->links(); int v = chord->staffMove(); if (l) { for (Element* e : *l) { Chord* c = static_cast(e); c->setStaffMove(staffMove); c->measure()->cmdUpdateNotes(c->staffIdx()); c->score()->setLayoutAll(true); } } else { chord->setStaffMove(staffMove); chord->measure()->cmdUpdateNotes(chord->staffIdx()); chord->score()->setLayoutAll(true); } staffMove = v; } //--------------------------------------------------------- // ChangeVelocity //--------------------------------------------------------- ChangeVelocity::ChangeVelocity(Note* n, Note::ValueType t, int o) : note(n), veloType(t), veloOffset(o) { } void ChangeVelocity::flip() { Note::ValueType t = note->veloType(); int o = note->veloOffset(); note->setVeloType(veloType); note->setVeloOffset(veloOffset); veloType = t; veloOffset = o; } //--------------------------------------------------------- // ChangeMStaffProperties //--------------------------------------------------------- ChangeMStaffProperties::ChangeMStaffProperties(MStaff* ms, bool v, bool s) : mstaff(ms), visible(v), slashStyle(s) { } //--------------------------------------------------------- // flip //--------------------------------------------------------- void ChangeMStaffProperties::flip() { bool v = mstaff->visible(); bool s = mstaff->slashStyle(); mstaff->setVisible(visible); mstaff->setSlashStyle(slashStyle); visible = v; slashStyle = s; } //--------------------------------------------------------- // ChangeTimesig //--------------------------------------------------------- ChangeTimesig::ChangeTimesig(TimeSig * _timesig, bool sc, const Fraction& f1, const Fraction& f2, QString numStr, QString denStr, TimeSigType st) { timesig = _timesig; showCourtesy = sc; sig = f1; stretch = f2; numeratorString = numStr; denominatorString = denStr; subtype = st; } //--------------------------------------------------------- // flip //--------------------------------------------------------- void ChangeTimesig::flip() { timesig->score()->addRefresh(timesig->canvasBoundingRect()); bool sc = timesig->showCourtesySig(); Fraction f1 = timesig->sig(); Fraction f2 = timesig->stretch(); QString numStr = timesig->numeratorString(); QString denStr = timesig->denominatorString(); TimeSigType st = timesig->timeSigType(); timesig->setShowCourtesySig(showCourtesy); timesig->setSig(sig, subtype); timesig->setStretch(stretch); timesig->setNumeratorString(numeratorString); timesig->setDenominatorString(denominatorString); showCourtesy = sc; sig = f1; stretch = f2; numeratorString = numStr; denominatorString = denStr; subtype = st; timesig->layout(); timesig->score()->addRefresh(timesig->canvasBoundingRect()); } //--------------------------------------------------------- // undoInsertTime // acts on the linked scores as well //--------------------------------------------------------- void Score::undoInsertTime(int tick, int len) { // qDebug("insertTime %d at %d, spanner %d", len, tick, _spanner.map().size()); if (len == 0) return; QList sl; for (auto i : _spanner.map()) { Spanner* s = i.second; if (s->tick2() < tick) continue; bool append = false; if (len > 0) { if (tick > s->tick() && tick < s->tick2()) append = true; else if (tick <= s->tick()) append = true; } else { int tick2 = tick - len; if (s->tick() >= tick2) append = true; else if ((s->tick() < tick) && (s->tick2() > tick2)) { int 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 > 0) { if (tick > s->tick() && tick < s->tick2()) { // // case a: // +----spanner--------+ // +---add--- // undoChangeProperty(s, P_ID::SPANNER_TICK2, s->tick2() + len); } else if (tick <= s->tick()) { // // case b: // +----spanner-------- // +---add--- // and // +----spanner-------- // +---add---+ undoChangeProperty(s, P_ID::SPANNER_TICK, s->tick() + len); undoChangeProperty(s, P_ID::SPANNER_TICK2, s->tick2() + len); } } else { int tick2 = tick - len; if (s->tick() >= tick2) { // // case A: // +----remove---+ +---spanner---+ // int t = s->tick() + len; if (t < 0) t = 0; undoChangeProperty(s, P_ID::SPANNER_TICK, t); undoChangeProperty(s, P_ID::SPANNER_TICK2, s->tick2() + len); } else if ((s->tick() < tick) && (s->tick2() > tick2)) { // // case B: // +----spanner--------+ // +---remove---+ // int t2 = s->tick2() + len; if (t2 > s->tick()) undoChangeProperty(s, P_ID::SPANNER_TICK2, t2); } else if (s->tick() >= tick && s->tick2() < tick2) { // // case C: // +---spanner---+ // +----remove--------+ // undoRemoveElement(s); } else if (s->tick() > tick && s->tick2() > tick2) { // // case D: // +----spanner--------+ // +---remove---+ // int d1 = s->tick() - tick; int d2 = tick2 - s->tick(); int len = s->tickLen() - d2; if (len == 0) undoRemoveElement(s); else { undoChangeProperty(s, P_ID::SPANNER_TICK, s->tick() - d1); undoChangeProperty(s, P_ID::SPANNER_TICK2, s->tick2() - (tick2-tick)); } } } } // insert time in (key, clef) maps // undo(new InsertTime(this, tick, len)); } //--------------------------------------------------------- // undoRemoveMeasures //--------------------------------------------------------- void Score::undoRemoveMeasures(Measure* m1, Measure* m2) { int tick1 = m1->tick(); int tick2 = m2->endTick(); for (auto i : _spanner.findContained(tick1, tick2)) { undo(new RemoveElement(i.value)); } // // handle ties which start before m1 and end in (m1-m2) // for (Segment* s = m1->first(); s != m2->last(); s = s->next1()) { if (s->segmentType() != Segment::Type::ChordRest) continue; for (int track = 0; track < ntracks(); ++track) { Chord* c = static_cast(s->element(track)); if (c == 0 || c->type() != Element::Type::CHORD) continue; for (Note* n : c->notes()) { Tie* t = n->tieBack(); if (t) { if (t->startNote()->chord()->tick() < m1->tick()) { t->setEndNote(0); n->setTieBack(0); } } } } } undo(new RemoveMeasures(m1, m2)); // int ticks = tick2 - tick1; // undoInsertTime(m1->tick(), -ticks); } //--------------------------------------------------------- // RemoveMeasures //--------------------------------------------------------- RemoveMeasures::RemoveMeasures(Measure* m1, Measure* m2) : fm(m1), lm(m2) { } //--------------------------------------------------------- // undo // insert back measures //--------------------------------------------------------- void RemoveMeasures::undo() { Score* score = fm->score(); QList clefs; for (Segment* s = fm->first(); s != lm->last(); s = s->next1()) { if (s->segmentType() != Segment::Type::Clef) continue; for (int track = 0; track < score->ntracks(); track += VOICES) { Clef* c = static_cast(s->element(track)); if (c == 0 || c->generated()) continue; clefs.append(c); } } score->measures()->insert(fm, lm); score->fixTicks(); score->insertTime(fm->tick(), lm->endTick() - fm->tick()); for (Clef* clef : clefs) clef->staff()->setClef(clef); if (!clefs.empty()) score->cmdUpdateNotes(); score->connectTies(); score->setLayoutAll(true); } //--------------------------------------------------------- // redo // remove measures //--------------------------------------------------------- void RemoveMeasures::redo() { Score* score = fm->score(); bool updateNotesNeeded = false; for (Segment* s = fm->first(); s != lm->last(); s = s->next1()) { if (s->segmentType() != Segment::Type::Clef) continue; for (int track = 0; track < score->ntracks(); track += VOICES) { Clef* clef = static_cast(s->element(track)); if (clef == 0 || clef->generated()) continue; clef->staff()->removeClef(clef); updateNotesNeeded = true; } } score->measures()->remove(fm, lm); score->fixTicks(); score->insertTime(fm->tick(), -(lm->endTick() - fm->tick())); if (updateNotesNeeded) score->cmdUpdateNotes(); score->setLayoutAll(true); } //--------------------------------------------------------- // undo // insert back measures //--------------------------------------------------------- void InsertMeasures::undo() { fm->score()->measures()->remove(fm, lm); fm->score()->fixTicks(); } //--------------------------------------------------------- // redo // remove measures //--------------------------------------------------------- void InsertMeasures::redo() { fm->score()->measures()->insert(fm, lm); fm->score()->fixTicks(); fm->score()->connectTies(); } //--------------------------------------------------------- // flip //--------------------------------------------------------- void ChangeImage::flip() { bool _lockAspectRatio = image->lockAspectRatio(); bool _autoScale = image->autoScale(); int _z = image->z(); image->setLockAspectRatio(lockAspectRatio); image->setAutoScale(autoScale); image->setZ(z); lockAspectRatio = _lockAspectRatio; autoScale = _autoScale; z = _z; } //--------------------------------------------------------- // flip //--------------------------------------------------------- void ChangeHairpin::flip() { int vc = hairpin->veloChange(); Dynamic::Range t = hairpin->dynRange(); bool dg = hairpin->diagonal(); hairpin->setVeloChange(veloChange); hairpin->setDynRange(dynRange); hairpin->setDiagonal(diagonal); veloChange = vc; dynRange = t; diagonal = dg; hairpin->score()->updateHairpin(hairpin); } //--------------------------------------------------------- // flip //--------------------------------------------------------- void ChangeDuration::flip() { Fraction od = cr->duration(); cr->setDuration(d); d = od; } //--------------------------------------------------------- // AddExcerpt::undo //--------------------------------------------------------- void AddExcerpt::undo() { score->parentScore()->removeExcerpt(score); score->parentScore()->setExcerptsChanged(true); } //--------------------------------------------------------- // AddExcerpt::redo //--------------------------------------------------------- void AddExcerpt::redo() { score->parentScore()->addExcerpt(score); score->parentScore()->setExcerptsChanged(true); } //--------------------------------------------------------- // RemoveExcerpt::undo() //--------------------------------------------------------- void RemoveExcerpt::undo() { score->parentScore()->addExcerpt(score); score->parentScore()->setExcerptsChanged(true); } //--------------------------------------------------------- // RemoveExcerpt::redo() //--------------------------------------------------------- void RemoveExcerpt::redo() { score->parentScore()->removeExcerpt(score); score->parentScore()->setExcerptsChanged(true); } //--------------------------------------------------------- // flip //--------------------------------------------------------- void ChangeBend::flip() { QList pv = bend->points(); bend->score()->addRefresh(bend->canvasBoundingRect()); bend->setPoints(points); points = pv; bend->layout(); bend->score()->addRefresh(bend->canvasBoundingRect()); } //--------------------------------------------------------- // flip //--------------------------------------------------------- void ChangeTremoloBar::flip() { QList pv = bend->points(); bend->setPoints(points); points = pv; } //--------------------------------------------------------- // ChangeNoteEvents::flip //--------------------------------------------------------- void ChangeNoteEvents::flip() { /*TODO: QList e = chord->playEvents(); chord->setPlayEvents(events); events = e; */ } //--------------------------------------------------------- // undoChangeBarLine //--------------------------------------------------------- void Score::undoChangeBarLine(Measure* m, BarLineType barType) { foreach(Score* s, scoreList()) { Measure* measure = s->tick2measure(m->tick()); Measure* nm = m->nextMeasure(); Repeat flags = measure->repeatFlags(); switch(barType) { case BarLineType::END: case BarLineType::NORMAL: case BarLineType::DOUBLE: case BarLineType::BROKEN: case BarLineType::DOTTED: { s->undoChangeProperty(measure, P_ID::REPEAT_FLAGS, int(flags) & ~int(Repeat::END)); if (nm) s->undoChangeProperty(nm, P_ID::REPEAT_FLAGS, int(nm->repeatFlags()) & ~int(Repeat::START)); s->undoChangeEndBarLineType(measure, barType); measure->setEndBarLineGenerated (false); } break; case BarLineType::START_REPEAT: s->undoChangeProperty(measure, P_ID::REPEAT_FLAGS, int(flags | Repeat::START)); break; case BarLineType::END_REPEAT: s->undoChangeProperty(measure, P_ID::REPEAT_FLAGS, int(flags | Repeat::END)); if (nm) s->undoChangeProperty(nm, P_ID::REPEAT_FLAGS, int(nm->repeatFlags()) & ~int(Repeat::START)); break; case BarLineType::END_START_REPEAT: s->undoChangeProperty(measure, P_ID::REPEAT_FLAGS, int(flags | Repeat::END)); if (nm) s->undoChangeProperty(nm, P_ID::REPEAT_FLAGS, int(nm->repeatFlags() | Repeat::START)); break; } } } //--------------------------------------------------------- // ChangeInstrument::flip //--------------------------------------------------------- void ChangeInstrument::flip() { Instrument oi = is->instrument(); is->setInstrument(instrument); is->staff()->part()->setInstrument(instrument, is->segment()->tick()); is->score()->rebuildMidiMapping(); is->score()->setInstrumentsChanged(true); is->score()->setLayoutAll(true); instrument = oi; } //--------------------------------------------------------- // ChangeBoxProperties //--------------------------------------------------------- ChangeBoxProperties::ChangeBoxProperties(Box* box, qreal marginLeft, qreal marginTop, qreal marginRight, qreal marginBottom, Spatium height, Spatium width, qreal tg, qreal bg) { _box = box; _marginLeft = marginLeft; _marginTop = marginTop; _marginRight = marginRight; _marginBottom = marginBottom; _height = height; _width = width; _topGap = tg; _bottomGap = bg; } //--------------------------------------------------------- // flip //--------------------------------------------------------- void ChangeBoxProperties::flip() { // flip margins qreal marginLeft = _box->leftMargin(); qreal marginTop = _box->topMargin(); qreal marginRight = _box->rightMargin(); qreal marginBottom = _box->bottomMargin(); qreal tg = _box->topGap(); qreal bg = _box->bottomGap(); _box->setLeftMargin (_marginLeft); _box->setRightMargin (_marginRight); _box->setTopMargin (_marginTop); _box->setBottomMargin(_marginBottom); _box->setTopGap (_topGap); _box->setBottomGap (_bottomGap); _marginLeft = marginLeft; _marginTop = marginTop; _marginRight = marginRight; _marginBottom = marginBottom; _topGap = tg; _bottomGap = bg; // according to box type, flip either height or width (or none) Spatium val; if (_box->type() == Element::Type::VBOX) { val = _box->boxHeight(); _box->setBoxHeight(_height); _height = val; } if (_box->type() == Element::Type::HBOX) { val = _box->boxWidth(); _box->setBoxWidth(_width); _width = val; } } //--------------------------------------------------------- // flip //--------------------------------------------------------- void SwapCR::flip() { Segment* s1 = cr1->segment(); Segment* s2 = cr2->segment(); int track = cr1->track(); Element* cr = s1->element(track); s1->setElement(track, s2->element(track)); s2->setElement(track, cr); cr1->score()->setLayoutAll(true); } //--------------------------------------------------------- // ChangeClefType //--------------------------------------------------------- ChangeClefType::ChangeClefType(Clef* c, ClefType cl, ClefType tc) { clef = c; concertClef = cl; transposingClef = tc; } //--------------------------------------------------------- // ChangeClefType::flip //--------------------------------------------------------- void ChangeClefType::flip() { ClefType ocl = clef->concertClef(); ClefType otc = clef->transposingClef(); clef->setConcertClef(concertClef); clef->setTransposingClef(transposingClef); clef->staff()->setClef(clef); Segment* segment = clef->segment(); updateNoteLines(segment, clef->track()); clef->score()->setLayoutAll(true); concertClef = ocl; transposingClef = otc; } //--------------------------------------------------------- // flip //--------------------------------------------------------- void MoveStaff::flip() { Part* oldPart = staff->part(); int idx = staff->rstaff(); oldPart->removeStaff(staff); part->insertStaff(staff, rstaff); part = oldPart; rstaff = idx; staff->score()->setLayoutAll(true); } //--------------------------------------------------------- // ChangeDurationType::flip //--------------------------------------------------------- void ChangeDurationType::flip() { TDuration type = cr->durationType(); cr->setDurationType(t); t = type; } //--------------------------------------------------------- // ChangeStaffUserDist::flip //--------------------------------------------------------- void ChangeStaffUserDist::flip() { qreal v = staff->userDist(); staff->setUserDist(dist); dist = v; staff->score()->setLayoutAll(true); } //--------------------------------------------------------- // ChangePartProperty::flip //--------------------------------------------------------- void ChangePartProperty::flip() { QVariant v = part->getProperty(id); part->setProperty(id, property); property = v; } //--------------------------------------------------------- // ChangeProperty::flip //--------------------------------------------------------- void ChangeProperty::flip() { #ifdef DEBUG_UNDO qDebug() << "ChangeProperty::flip(): " << propertyName(id) << " " << element->getProperty(id) << " -> " << property ; #endif if (id == P_ID::SPANNER_TICK || id == P_ID::SPANNER_TICK2) element->score()->removeSpanner(static_cast(element)); QVariant v = element->getProperty(id); PropertyStyle ps = element->propertyStyle(id); if (propertyStyle == PropertyStyle::STYLED) element->resetProperty(id); else element->setProperty(id, property); if (id == P_ID::SPANNER_TICK || id == P_ID::SPANNER_TICK2) element->score()->addSpanner(static_cast(element)); property = v; propertyStyle = ps; } //--------------------------------------------------------- // ChangeMetaText::flip //--------------------------------------------------------- void ChangeMetaText::flip() { QString s = score->metaTag(id); score->setMetaTag(id, text); text = s; } PlayEventType eventListType = PlayEventType::User; void flip(); //--------------------------------------------------------- // ChangeEventList //--------------------------------------------------------- ChangeEventList::ChangeEventList(Chord* c, const QList l) : chord(c), events(l) { eventListType = PlayEventType::User; } //--------------------------------------------------------- // ChangeEventList::flip //--------------------------------------------------------- void ChangeEventList::flip() { int n = chord->notes().size(); for (int i = 0; i < n; ++i) { Note* note = chord->notes()[i]; note->playEvents().swap(events[i]); } PlayEventType t = chord->playEventType(); chord->setPlayEventType(eventListType); eventListType = t; } //--------------------------------------------------------- // ChangeSynthesizerState::flip //--------------------------------------------------------- void ChangeSynthesizerState::flip() { std::swap(state, score->_synthesizerState); } //--------------------------------------------------------- // 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->level(), b->bracketType(), b->span())); } void AddBracket::redo() { staff->setBracket(level, type); staff->setBracketSpan(level, span); staff->score()->setLayoutAll(true); } void AddBracket::undo() { staff->setBracket(level, BracketType::NO_BRACKET); staff->score()->setLayoutAll(true); } void RemoveBracket::redo() { staff->setBracket(level, BracketType::NO_BRACKET); staff->score()->setLayoutAll(true); } void RemoveBracket::undo() { staff->setBracket(level, type); staff->setBracketSpan(level, span); staff->score()->setLayoutAll(true); } //--------------------------------------------------------- // ChangeSpannerElements //--------------------------------------------------------- void ChangeSpannerElements::flip() { Element* se = spanner->startElement(); Element* ee = spanner->endElement(); spanner->setStartElement(startElement); spanner->setEndElement(endElement); startElement = se; endElement = ee; if (spanner->type() == Element::Type::TIE) { Tie* tie = static_cast(spanner); static_cast(endElement)->setTieBack(0); tie->endNote()->setTieBack(tie); static_cast(startElement)->setTieFor(0); tie->startNote()->setTieFor(tie); } spanner->score()->setLayoutAll(true); } //--------------------------------------------------------- // ChangeParent //--------------------------------------------------------- void ChangeParent::flip() { Element* p = element->parent(); int si = element->staffIdx(); p->remove(element); element->setParent(parent); element->setTrack(staffIdx * VOICES); parent->add(element); staffIdx = si; parent = p; } //--------------------------------------------------------- // ChangeMMRest //--------------------------------------------------------- void ChangeMMRest::flip() { Measure* mmr = m->mmRest(); m->setMMRest(mmrest); mmrest = mmr; } //--------------------------------------------------------- // InsertTime //--------------------------------------------------------- void InsertTime::redo() { score->insertTime(tick, len); } void InsertTime::undo() { score->insertTime(tick, -len); } //--------------------------------------------------------- // ChangeNoteEvent::flip //--------------------------------------------------------- void ChangeNoteEvent::flip() { note->score()->setPlaylistDirty(true); NoteEvent e = *oldEvent; *oldEvent = newEvent; newEvent = e; // TODO: note->chord()->setPlayEventType(PlayEventType::User); } //--------------------------------------------------------- // Unlink //--------------------------------------------------------- Unlink::Unlink(Element* _e) : e(_e) { Q_ASSERT(e->links()); } //--------------------------------------------------------- // Unlink::undo // (link) //--------------------------------------------------------- void Unlink::undo() { e->linkTo(le); le = nullptr; } //--------------------------------------------------------- // Unlink::redo // (unlink) //--------------------------------------------------------- void Unlink::redo() { Q_ASSERT(le == nullptr); const LinkedElements* l = e->links(); for (Element* ee : *l) { if (e != ee) { le = ee; break; } } Q_ASSERT(le); e->unlink(); } //--------------------------------------------------------- // Link::redo //--------------------------------------------------------- void Link::redo() { e1->linkTo(e2); } //--------------------------------------------------------- // Link::redo //--------------------------------------------------------- void Link::undo() { e2->unlink(); } //--------------------------------------------------------- // LinkStaff::redo //--------------------------------------------------------- void LinkStaff::redo() { s1->linkTo(s2); } //--------------------------------------------------------- // LinkStaff::undo //--------------------------------------------------------- void LinkStaff::undo() { s1->unlink(s2); } //--------------------------------------------------------- // UnlinkStaff::redo //--------------------------------------------------------- void UnlinkStaff::redo() { s1->unlink(s2); } //--------------------------------------------------------- // UnlinkStaff::undo //--------------------------------------------------------- void UnlinkStaff::undo() { s1->linkTo(s2); } //--------------------------------------------------------- // ChangeStartEndSpanner::flip //--------------------------------------------------------- void ChangeStartEndSpanner::flip() { Element* s = spanner->startElement(); Element* e = spanner->endElement(); spanner->setStartElement(start); spanner->setEndElement(end); start = s; end = e; } }