//============================================================================= // 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" 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; foreach(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; } //--------------------------------------------------------- // pos //--------------------------------------------------------- int 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 -1; } //--------------------------------------------------------- // addRest // create one Rest at tick with duration d // create segment if necessary //--------------------------------------------------------- Rest* Score::addRest(int 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->setDuration(measure->stretchedLen(staff(track/VOICES))); else rest->setDuration(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->setDuration(s->measure()->stretchedLen(staff(track/VOICES))); else rest->setDuration(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(int 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->setDuration(d.fraction()); foreach(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) { int n = oc->notes().size(); for(int i = 0; i < n; ++i) { Note* n = oc->notes()[i]; Note* nn = chord->notes()[i]; Tie* tie = new Tie(this); tie->setStartNote(n); tie->setEndNote(nn); tie->setTrack(n->track()); undoAddElement(tie); } } return chord; } //--------------------------------------------------------- // addClone //--------------------------------------------------------- ChordRest* Score::addClone(ChordRest* cr, int 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->setDuration(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(int tick, int track, Fraction l, bool useDots, Tuplet* tuplet, bool useFullMeasureRest) { Measure* measure = tick2measure(tick); Rest* r = 0; Staff* staff = Score::staff(track / VOICES); while (!l.isZero()) { // // divide into measures // Fraction f; if (tuplet) { int ticks = (tuplet->tick() + tuplet->actualTicks()) - tick; f = Fraction::fromTicks(ticks); for (Tuplet* t = tuplet; t; t = t->tuplet()) f *= t->ratio(); // // restrict to tuplet len // if (f < l) l = f; } else if (measure->tick() < tick) f = Fraction::fromTicks(measure->tick() + measure->ticks() - tick); else f = measure->len(); f *= staff->timeStretch(tick); f.reduce(); 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->len()) // 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 dList = toDurationList(f, useDots); if (dList.empty()) return 0; Rest* rest = 0; if (((tick - measure->tick()) % dList[0].ticks()) == 0) { for (const TDuration& d : dList) { rest = addRest(tick, track, d, tuplet); if (r == 0) r = rest; tick += rest->actualTicks(); } } else { for (int i = dList.size() - 1; i >= 0; --i) { rest = addRest(tick, track, dList[i], 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, NoteVal& noteVal) { Note* note = new Note(this); note->setParent(chord); note->setTrack(chord->track()); note->setNval(noteVal); undoAddElement(note); setPlayNote(true); setPlayChord(true); 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(Segment::Type::ChordRest); s; s = s->next(Segment::Type::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(P_ID::DURATION, QVariant::fromValue(ns)); else return false; } } if (m == lm) break; } return true; } int measures = 1; bool fmr = true; for (Measure* m = fm; m; m = m->nextMeasure()) { if (!m->isFullMeasureRest()) fmr = false; if (m == lm) break; ++measures; } if (!fmr) { // check for local time signatures for (Measure* m = fm; m; m = m -> nextMeasure()) { for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) { if (staff(staffIdx)->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.duration() / ns; int nm = (k.numerator() + k.denominator() - 1)/ k.denominator(); Fraction nd(nm * ns.numerator(), ns.denominator()); // total new duration // evtl. we have to fill the last measure Fraction fill = nd - range.duration(); range.fill(fill); for (Score* s : scoreList()) { Measure* m1 = s->tick2measure(fm->tick()); Measure* m2 = s->tick2measure(lm->tick()); int tick1 = m1->tick(); int tick2 = m2->endTick(); auto spanners = s->spannerMap().findContained(tick1, tick2); for (auto i : spanners) undo(new RemoveElement(i.value)); s->undoRemoveMeasures(m1, m2); Measure* nfm = 0; Measure* nlm = 0; int tick = 0; // bool endBarGenerated = m1->endBarLineGenerated(); for (int i = 0; i < nm; ++i) { Measure* m = new Measure(s); m->setPrev(nlm); if (nlm) nlm->setNext(m); m->setTimesig(ns); m->setLen(ns); m->setTick(tick); // m->setEndBarLineType(BarLineType::NORMAL, endBarGenerated); 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.ticks()); if (!range.write(masterScore(), fm->tick())) return false; connectTies(true); if (noteEntryMode()) { // set input cursor to possibly re-written segment int icTick = inputPos(); Segment* icSegment = tick2segment(icTick, false, Segment::Type::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(Segment::Type::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* m = fm; ; m = m->next()) { if (!m || !m->isMeasure() || lm->sectionBreak() || (toMeasure(m)->first(Segment::Type::TimeSig) && m != 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(P_ID::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(Segment::Type::TimeSig)) break; Fraction fr(ns); undoChangeProperty(m, P_ID::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 (m && m->prevMeasure()) nm = m->prevMeasure()->nextMeasure(); else nm = nullptr; if (sectionBreak) { // reinstate section break, then stop rewriting if (m && m->prevMeasure()) { sectionBreak->setParent(m->prevMeasure()); undoAddElement(sectionBreak); } else if (!m) { 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 (!m || m->isMeasure()) break; // skip frames while (!m->isMeasure()) { if (m->sectionBreak()) { // frame has a section break; we can stop skipping ahead sectionBreak = m->sectionBreakElement(); break; } m = m->next(); if (!m) 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 || (m && toMeasure(m)->first(Segment::Type::TimeSig))) break; // set up for next range to rewrite fm1 = toMeasure(m); 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(m); } // 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(Segment::Type::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(); Fraction ns = ts->sig(); int 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(Segment::Type::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().identical(ns) && ots->stretch() == ts->stretch()) { ots->undoChangeProperty(P_ID::TIMESIG, QVariant::fromValue(ns)); ots->undoChangeProperty(P_ID::GROUPS, QVariant::fromValue(ts->groups())); if (ts->hasCustomText()) { ots->undoChangeProperty(P_ID::NUMERATOR_STRING, ts->numeratorString()); ots->undoChangeProperty(P_ID::DENOMINATOR_STRING, ts->denominatorString()); } foreach (Score* score, scoreList()) { Measure* fm = score->tick2measure(tick); for (Measure* m = fm; m; m = m->nextMeasure()) { if ((m != fm) && m->first(Segment::Type::TimeSig)) break; bool changeActual = m->len() == m->timesig(); undoChangeProperty(m, P_ID::TIMESIG_NOMINAL, QVariant::fromValue(ns)); if (changeActual) undoChangeProperty(m, P_ID::TIMESIG_ACTUAL, QVariant::fromValue(ns)); // undoChangeProperty(ots, P_ID::GROUPS, QVariant::fromValue(ts->groups())); } } int n = nstaves(); for (int staffIdx = 0; staffIdx < n; ++staffIdx) { TimeSig* nsig = toTimeSig(seg->element(staffIdx * VOICES)); undoChangeProperty(nsig, P_ID::TIMESIG_TYPE, int(ts->timeSigType())); if (ts->hasCustomText()) { nsig->undoChangeProperty(P_ID::NUMERATOR_STRING, ts->numeratorString()); nsig->undoChangeProperty(P_ID::DENOMINATOR_STRING, ts->denominatorString()); } } } else { Score* score = masterScore(); Measure* fm = score->tick2measure(tick); // // rewrite all measures up to the next time signature // if (fm == score->firstMeasure() && (fm->len() != fm->timesig())) { // handle upbeat undoChangeProperty(fm, P_ID::TIMESIG_NOMINAL, QVariant::fromValue(ns)); Measure* m = fm->nextMeasure(); Segment* s = m->findSegment(Segment::Type::TimeSig, m->tick()); fm = s ? 0 : fm->nextMeasure(); } else { if (sigmap()->timesig(seg->tick()).timesig().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 (!score->rewriteMeasures(fm, ns, i)) { undoStack()->current()->unwind(); return; } } } fm = 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 (fm) { if (!score->rewriteMeasures(fm, ns, local ? staffIdx : -1)) { undoStack()->current()->unwind(); return; } } // add the time signatures foreach (Score* score, scoreList()) { Measure* nfm = score->tick2measure(tick); seg = nfm->undoGetSegment(Segment::Type::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 staffIdx = startStaffIdx; staffIdx < endStaffIdx; ++staffIdx) { TimeSig* nsig = toTimeSig(seg->element(staffIdx * VOICES)); if (nsig == 0) { nsig = new TimeSig(*ts); nsig->setScore(score); nsig->setTrack(staffIdx * VOICES); nsig->setParent(seg); nsig->setNeedLayout(true); undoAddElement(nsig); } else { nsig->undoChangeProperty(P_ID::SHOW_COURTESY, ts->showCourtesySig()); nsig->undoChangeProperty(P_ID::TIMESIG_TYPE, int(ts->timeSigType())); nsig->undoChangeProperty(P_ID::TIMESIG, QVariant::fromValue(ts->sig())); nsig->undoChangeProperty(P_ID::NUMERATOR_STRING, ts->numeratorString()); nsig->undoChangeProperty(P_ID::DENOMINATOR_STRING, ts->denominatorString()); // HACK do it twice to accommodate undo nsig->undoChangeProperty(P_ID::TIMESIG_TYPE, int(ts->timeSigType())); nsig->undoChangeProperty(P_ID::TIMESIG_STRETCH, QVariant::fromValue(ts->stretch())); nsig->undoChangeProperty(P_ID::GROUPS, QVariant::fromValue(ts->groups())); nsig->setSelected(false); nsig->setDropTarget(0); // DEBUG } } } } 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; int 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 undoRemoveElement(s); Measure* pm = m->prevMeasure(); Fraction ns(pm ? pm->timesig() : Fraction(4,4)); if (!rewriteMeasures(m, 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* ts = staff(i)->timeSig(tick); if (ts && ts->isLocal()) { for (Measure* nm = m; nm; nm = nm->nextMeasure()) { // stop when time signature changes if (staff(i)->timeSig(nm->tick()) != ts) 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(P_ID::DURATION, QVariant::fromValue(nm->stretchedLen(staff(i)))); //cr->setDuration(nm->stretchedLen(staff(i))); } } } } } //--------------------------------------------------------- // noteValForPosition //--------------------------------------------------------- NoteVal Score::noteValForPosition(Position pos, bool &error) { error = false; Segment* s = pos.segment; int line = pos.line; int tick = s->tick(); int staffIdx = pos.staffIdx; Staff* st = staff(staffIdx); ClefType clef = st->clef(tick); const Instrument* instr = st->part()->instrument(s->tick()); NoteVal nval; const StringData* stringData = 0; switch (st->staffType(tick)->group()) { case StaffGroup::PERCUSSION: { if (_is.rest()) { error = true; break; } const Drumset* ds = instr->drumset(); nval.pitch = _is.drumNote(); if (nval.pitch < 0) { error = true; return nval; } nval.headGroup = ds->noteHead(nval.pitch); if (nval.headGroup == NoteHead::Group::HEAD_INVALID) { error = true; return nval; } break; } case StaffGroup::TAB: { if (_is.rest()) { error = true; return nval; } stringData = instr->stringData(); if (line < 0 || line >= stringData->strings()) { error = true; return nval; } // build a default NoteVal for that string nval.string = line; if (pos.fret != FRET_NONE) // if a fret is given, use it nval.fret = pos.fret; else { // if no fret, use 0 as default _is.setString(line); nval.fret = 0; } // reduce within fret limit if (nval.fret > stringData->frets()) nval.fret = stringData->frets(); // for open strings, only accepts fret 0 (strings in StringData are from bottom to top) int strgDataIdx = stringData->strings() - line - 1; if (nval.fret > 0 && stringData->stringList().at(strgDataIdx).open == true) nval.fret = 0; nval.pitch = stringData->getPitch(line, nval.fret, st, tick); break; } case StaffGroup::STANDARD: { AccidentalVal acci = s->measure()->findAccidental(s, staffIdx, line, error); if (error) return nval; int step = absStep(line, clef); int octave = step/7; nval.pitch = step2pitch(step) + octave * 12 + int(acci); if (styleB(StyleIdx::concertPitch)) nval.tpc1 = step2tpc(step % 7, acci); else { nval.pitch += instr->transpose().chromatic; nval.tpc2 = step2tpc(step % 7, acci); Interval v = st->part()->instrument(tick)->transpose(); if (v.isZero()) nval.tpc1 = nval.tpc2; else nval.tpc1 = Ms::transposeTpc(nval.tpc2, v, true); } } break; } return nval; } //--------------------------------------------------------- // 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(StyleIdx::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(int tick, int track) const { ChordRest* ipe = 0; Segment::Type st = Segment::Type::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(int startTick, int endTick, int track) { Segment* inputSegment = _is.segment(); // store this so we can get back to it later. Segment* seg = tick2segment(startTick, true, Segment::Type::ChordRest); for (Measure* msr = seg->measure(); msr && msr->tick() < endTick; msr = msr->nextMeasure()) { int maxTick = endTick > msr->endTick() ? msr->endTick() : endTick; if (!seg || seg->measure() != msr) seg = msr->first(Segment::Type::ChordRest); for (; seg && seg->tick() + seg->ticks() < maxTick; seg = seg->next(Segment::Type::ChordRest)) { ChordRest* curr = seg->cr(track); if (!curr) continue; // voice is empty here if (curr->isRest()) { // combine consecutive rests ChordRest* lastRest = curr; for (Segment* s = seg->next(Segment::Type::ChordRest); s && s->tick() + s->ticks() <= maxTick; s = s->next(Segment::Type::ChordRest)) { ChordRest* cr = s->cr(track); if (!cr || !cr->isRest()) break; lastRest = cr; } int restTicks = lastRest->tick() + lastRest->duration().ticks() - curr->tick(); seg = setNoteRest(seg, curr->track(), NoteVal(), Fraction::fromTicks(restTicks), Direction::AUTO); } else { // combine tied chords Chord* chord = toChord(curr); Chord* lastTiedChord = chord; for (Chord* next = chord->nextTiedChord(); next && next->tick() + next->duration().ticks() <= maxTick; next = next->nextTiedChord()) { lastTiedChord = next; } if (!lastTiedChord) lastTiedChord = chord; int numNotes = chord->notes().size(); int pitches[numNotes]; Tie* tieBack[numNotes]; Tie* tieFor[numNotes]; for (int i = 0; i < numNotes; i++) { Note* n = chord->notes()[i]; Note* nn = lastTiedChord->notes()[i]; pitches[i] = n->pitch(); tieBack[i] = n->tieBack(); tieFor[i] = nn->tieFor(); n->setTieBack(0); nn->setTieFor(0); } int noteTicks = lastTiedChord->tick() + lastTiedChord->duration().ticks() - chord->tick(); seg = setNoteRest(seg, curr->track(), NoteVal(pitches[0]), Fraction::fromTicks(noteTicks), Direction::AUTO); Chord* newChord = toChord(seg->cr(track)); Note* n = newChord->notes().front(); undoRemoveElement(n); for (int i = 0; i < numNotes; i++) { NoteVal nval = NoteVal(pitches[i]); n = addNote(newChord, nval); if (tieBack[i]) { n->setTieBack(tieBack[i]); tieBack[i]->setEndNote(n); } if (tieFor[i]) { n->setTieFor(tieFor[i]); tieFor[i]->setStartNote(n); } } } } } // now put the input state back where it was before _is.setSegment(inputSegment); } //--------------------------------------------------------- // cmdAddTie //--------------------------------------------------------- void Score::cmdAddTie() { std::vector noteList; Element* el = selection().element(); if (el && el->isNote()) { Note* n = toNote(el); if (noteEntryMode()) noteList = n->chord()->notes(); else noteList.push_back(n); } else if (el && el->isStem()) { Chord* chord = toStem(el)->chord(); noteList = chord->notes(); } else noteList = selection().noteList(); if (noteList.empty()) { qDebug("no notes selected"); return; } startCmd(); Chord* lastAddedChord = nullptr; for (Note* note : noteList) { if (note->tieFor()) { qDebug("cmdAddTie: note %p has already tie? noteFor: %p", note, note->tieFor()); continue; } if (noteEntryMode()) { // set cursor at position after note _is.setSegment(note->chord()->segment()); _is.moveToNextInputPos(); _is.setLastSegment(_is.segment()); if (_is.cr() == 0) expandVoice(); ChordRest* cr = _is.cr(); if (cr == 0) break; bool addFlag = lastAddedChord != nullptr; // try to re-use existing note or chord Note* n = nullptr; if (cr->isChord()) { Chord* c = toChord(cr); Note* nn = c->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()); undoAddElement(tie); if (!addFlag || nnote->chord() == lastAddedChord) { break; } else { note = nnote; _is.setLastSegment(_is.segment()); nnote = addPitch(nval, true); } } } } else { Note* note2 = searchTieNote(note); #if 0 // this code appears to mostly duplicate searchTieNote() // not clear if it served any additional purpose // it might have before we supported extended ties? Part* part = chord->part(); int strack = part->staves()->front()->idx() * VOICES; int etrack = strack + part->staves()->size() * VOICES; for (Segment* seg = chord->segment()->next1(Segment::Type::ChordRest); seg; seg = seg->next1(Segment::Type::ChordRest)) { bool noteFound = false; for (int track = strack; track < etrack; ++track) { ChordRest* cr = toChordRest(seg->element(track)); if (cr == 0 || !cr->isChord()) continue; int staffIdx = cr->staffIdx() + cr->staffMove(); if (staffIdx != chord->staffIdx() + chord->staffMove()) continue; foreach(Note* n, toChord(cr)->notes()) { if (n->pitch() == note->pitch()) { if (note2 == 0 || note->chord()->track() == chord->track()) note2 = n; } // this may belong to *previous* if? else if (cr->track() == chord->track()) noteFound = true; } } if (noteFound || note2) break; } #endif if (note2) { Tie* tie = new Tie(this); tie->setStartNote(note); tie->setEndNote(note2); tie->setTrack(note->track()); undoAddElement(tie); } } } if (lastAddedChord) nextInputPos(lastAddedChord, false); endCmd(); } //--------------------------------------------------------- // cmdAddOttava //--------------------------------------------------------- void Score::cmdAddOttava(Ottava::Type type) { Selection sel = selection(); ChordRest* cr1; ChordRest* cr2; // 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 { 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) { ChordRest* cr = getSelectedChordRest(); if (cr == 0) return; cr->setBeamMode(mode); } //--------------------------------------------------------- // cmdFlip //--------------------------------------------------------- void Score::cmdFlip() { const QList& el = selection().elements(); if (el.empty()) { MScore::setError(NO_NOTE_SLUR_SELECTED); return; } for (Element* e : el) { if (e->isNote() || e->isStem() || e->isHook()) { Chord* chord; if (e->isNote()) chord = toNote(e)->chord(); else if (e->isStem()) chord = toStem(e)->chord(); else chord = toHook(e)->chord(); if (chord->beam()) if (!selection().isRange()) e = chord->beam(); // fall through else continue; else { Direction dir = chord->up() ? Direction::DOWN : Direction::UP; undoChangeProperty(chord, P_ID::STEM_DIRECTION, dir); } } else if (e->isBeam()) { Beam* beam = toBeam(e); Direction dir = beam->up() ? Direction::DOWN : Direction::UP; undoChangeProperty(beam, P_ID::STEM_DIRECTION, dir); } else if (e->isSlurTieSegment()) { SlurTie* slur = toSlurTieSegment(e)->slurTie(); Direction dir = slur->up() ? Direction::DOWN : Direction::UP; undoChangeProperty(slur, P_ID::SLUR_DIRECTION, dir); } else if (e->isHairpinSegment()) { Hairpin* h = toHairpinSegment(e)->hairpin(); HairpinType st = h->hairpinType(); switch (st) { case HairpinType::CRESC_HAIRPIN: st = HairpinType::DECRESC_HAIRPIN; break; case HairpinType::DECRESC_HAIRPIN: st = HairpinType::CRESC_HAIRPIN; break; case HairpinType::CRESC_LINE: st = HairpinType::DECRESC_LINE; break; case HairpinType::DECRESC_LINE: st = HairpinType::CRESC_LINE; break; } undoChangeProperty(h, P_ID::HAIRPIN_TYPE, int(st)); } else if (e->isArticulation()) { Articulation* a = toArticulation(e); ArticulationAnchor aa = a->anchor(); switch (aa) { case ArticulationAnchor::TOP_CHORD: aa = ArticulationAnchor::BOTTOM_CHORD; break; case ArticulationAnchor::BOTTOM_CHORD: aa = ArticulationAnchor::TOP_CHORD; break; case ArticulationAnchor::CHORD: aa = a->up() ? ArticulationAnchor::BOTTOM_CHORD : ArticulationAnchor::TOP_CHORD; break; case ArticulationAnchor::TOP_STAFF: aa = ArticulationAnchor::BOTTOM_STAFF; break; case ArticulationAnchor::BOTTOM_STAFF: aa = ArticulationAnchor::TOP_STAFF; break; } // undoChangeProperty(a, P_ID::DIRECTION, a->up() ? Direction::DOWN : Direction::UP); undoChangeProperty(a, P_ID::ARTICULATION_ANCHOR, int(aa)); } else if (e->isTuplet()) { Tuplet* tuplet = toTuplet(e); Direction d = tuplet->isUp() ? Direction::DOWN : Direction::UP; undoChangeProperty(tuplet, P_ID::DIRECTION, d); } else if (e->isNoteDot()) { Note* note = toNote(e->parent()); Direction d = note->dotIsUp() ? Direction::DOWN : Direction::UP; undoChangeProperty(note, P_ID::DOT_POSITION, d); } else if (e->isTempoText() || e->isStaffText() || e->isDynamic() || e->isHairpin() || e->isOttavaSegment() || e->isTextLineSegment() || e->isPedalSegment() || e->isTrillSegment()) { // getProperty() delegates call from spannerSegment to Spanner: Element::Placement p = Element::Placement(e->getProperty(P_ID::PLACEMENT).toInt()); p = (p == Element::Placement::ABOVE) ? Element::Placement::BELOW : Element::Placement::ABOVE; e->undoChangeProperty(P_ID::AUTOPLACE, true); e->undoChangeProperty(P_ID::PLACEMENT, int(p)); } else if (e->isLyrics()) { toLyrics(e)->chordRest()->flipLyrics(toLyrics(e)); } } } //--------------------------------------------------------- // deleteItem //--------------------------------------------------------- void Score::deleteItem(Element* el) { if (!el) return; switch (el->type()) { case ElementType::INSTRUMENT_NAME: { Part* part = el->part(); InstrumentName* in = toInstrumentName(el); if (in->instrumentNameType() == InstrumentNameType::LONG) undo(new ChangeInstrumentLong(0, part, QList())); else if (in->instrumentNameType() == InstrumentNameType::SHORT) undo(new ChangeInstrumentShort(0, part, QList())); } 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: undoRemoveElement(el); 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; } 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->setDuration(chord->duration()); 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 tl = tuplet->linkList(); for (ScoreElement* e : rest->linkList()) { DurationElement* de = static_cast(e); for (ScoreElement* ee : tl) { Tuplet* t = static_cast(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->setDuration(rm->measure()->stretchedLen(rm->staff())); rest->setTrack(rm->track()); rest->setParent(rm->parent()); Segment* segment = rm->segment(); undoAddCR(rest, segment->measure(), segment->tick()); } 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(P_ID::GAP, true); for (ScoreElement* r : el->linkList()) { Rest* rest = static_cast(r); if (rest->track() % VOICES) rest->undoChangeProperty(P_ID::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 Segment::Type st { Segment::Type::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); } } else { // check if the other rest could be combined Segment* s = toRest(el)->segment(); std::vector rests; // find previous segment with cr in this track Element* pe = 0; for (Segment* ps = s->prev(Segment::Type::ChordRest); ps; ps = ps->prev(Segment::Type::ChordRest)) { Element* el = ps->element(track); if (el && el->isRest() && toRest(el)->isGap()) { pe = el; rests.push_back(toRest(el)); } else if (el) break; } // find next segment with cr in this track Segment* ns; Element* ne = 0; for (ns = s->next(Segment::Type::ChordRest); ns; ns = ns->next(Segment::Type::ChordRest)) { Element* el = ns->element(track); if (el && el->isRest() && toRest(el)->isGap()) { ne = el; rests.push_back(toRest(el)); } else if (el) break; } int stick = 0; int ticks = 0; if (pe) stick = pe->tick(); else stick = s->tick(); 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 = Fraction::fromTicks(ticks); std::vector dList = toDurationList(f, true); if (dList.empty()) break; for (const TDuration& d : dList) { Rest* rest = new Rest(this); rest->setDuration(d.fraction()); rest->setDurationType(d); rest->setTrack(track); rest->setGap(true); undoAddCR(rest, m, stick); } } } // Set input position // TODO If deleted element is last of a sequence, use prev? if (_is.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()) { undoRemoveElement(el); } else { if (bl->barLineType() == BarLineType::START_REPEAT) m->undoChangeProperty(P_ID::REPEAT_START, false); else if (bl->barLineType() == BarLineType::END_REPEAT) m->undoChangeProperty(P_ID::REPEAT_END, false); else if (bl->barLineType() == BarLineType::END_START_REPEAT) { m->undoChangeProperty(P_ID::REPEAT_START, false); m->undoChangeProperty(P_ID::REPEAT_END, false); } else undoRemoveElement(el); } } 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(); foreach(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(Segment::Type::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 Measure* m = clef->measure(); if (m && m->prevMeasure()) { int tick = m->tick(); m = m->prevMeasure(); Segment* s = m->findSegment(Segment::Type::Clef, tick); if (s && s->element(clef->track())) clef = toClef(s->element(clef->track())); } } undoRemoveElement(clef); } } 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(Segment::Type::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::TEXTLINE_SEGMENT: case ElementType::VOLTA_SEGMENT: case ElementType::SLUR_SEGMENT: case ElementType::TIE_SEGMENT: case ElementType::PEDAL_SEGMENT: case ElementType::GLISSANDO_SEGMENT: { el = static_cast(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()) undoChangeProperty(el, P_ID::TEXT, QString()); else undoRemoveElement(el); break; default: undoRemoveElement(el); break; } } //--------------------------------------------------------- // deleteMeasures //--------------------------------------------------------- void Score::deleteMeasures(MeasureBase* is, MeasureBase* ie) { printf("deleteMeasures %p %p\n", 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 // 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(Segment::Type::TimeSig, m->tick()); if (sts && !lastDeletedSig) lastDeletedSig = toTimeSig(sts->element(0)); sts = m->findSegment(Segment::Type::KeySig, m->tick()); if (sts && !lastDeletedKeySig) { lastDeletedKeySig = toKeySig(sts->element(0)); if (lastDeletedKeySig) { lastDeletedKeySigEvent = lastDeletedKeySig->keySigEvent(); if (!styleB(StyleIdx::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)); } } } if (lastDeletedSig && lastDeletedKeySig) break; } if (mb == is) break; } int startTick = is->tick(); int endTick = ie->tick(); undoInsertTime(is->tick(), -(ie->endTick() - is->tick())); for (Score* score : scoreList()) { Measure* is = score->tick2measure(startTick); Measure* ie = score->tick2measure(endTick); printf("undoRemoveMeasures %p %p\n", is, ie); score->undoRemoveMeasures(is, ie); // adjust views Measure* focusOn = is->prevMeasure() ? is->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 = is->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(Segment::Type::TimeSig, mAfterSel->tick()); if (!s && changed) { Segment* ns = mAfterSel->undoGetSegment(Segment::Type::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(Segment::Type::KeySig, mAfterSel->tick()); if (!s) { Segment* ns = mAfterSel->undoGetSegment(Segment::Type::KeySig, mAfterSel->tick()); for (int staffIdx = 0; staffIdx < score->nstaves(); staffIdx++) { KeySigEvent nkse = lastDeletedKeySigEvent; if (transposeKeySigEvent) { Interval v = score->staff(staffIdx)->part()->instrument(0)->transpose(); v.flip(); nkse.setKey(transposeKey(nkse.key(), v)); } KeySig* nks = new KeySig(score); nks->setTrack(staffIdx * VOICES); nks->setParent(ns); nks->setKeySigEvent(nkse); score->undoAddElement(nks); } } } } select(0, SelectType::SINGLE, 0); _is.setSegment(0); // invalidate position } //--------------------------------------------------------- // cmdDeleteSelection //--------------------------------------------------------- void Score::cmdDeleteSelection() { ChordRest* cr = nullptr; // select something after deleting notes if (selection().isRange()) { Segment* s1 = selection().startSegment(); Segment* s2 = selection().endSegment(); // 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(); int stick1 = selection().tickStart(); int stick2 = selection().tickEnd(); Segment* ss1 = s1; if (!ss1->isChordRestType()) ss1 = ss1->next1(Segment::Type::ChordRest); bool fullMeasure = ss1 && (ss1->measure()->first(Segment::Type::ChordRest) == ss1) && (s2 == 0 || s2->isEndBarLineType()); int tick2 = s2 ? s2->tick() : INT_MAX; int track1 = selection().staffStart() * VOICES; int track2 = selection().staffEnd() * VOICES; auto spanners = _spanner.findOverlapping(stick1, stick2 - 1); for (auto i : spanners) { Spanner* sp = i.value; if (sp->isVolta()) continue; if (sp->track() >= track1 && sp->track() < track2) { if (sp->tick() >= stick1 && sp->tick() < stick2 && sp->tick2() >= stick1 && sp->tick2() < stick2) { undoRemoveElement(sp); } else if (sp->isSlur() && ((sp->tick() >= stick1 && sp->tick() < stick2) || (sp->tick2() >= stick1 && sp->tick2() < stick2))) { undoRemoveElement(sp); } } } for (int track = track1; track < track2; ++track) { if (!selectionFilter().canSelectVoice(track)) continue; Fraction f; int tick = -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; } foreach (Element* annotation, s->annotations()) { // skip if not included in selection (eg, filter) if (!selectionFilter().canSelect(annotation)) continue; if (!annotation->systemFlag() && annotation->track() == track) undoRemoveElement(annotation); } 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() != Segment::Type::TimeSig && s->segmentType() != Segment::Type::KeySig) undoRemoveElement(e); continue; } ChordRest* cr = toChordRest(e); if (tick == -1) { // first ChordRest found: int offset = cr->rtick(); if (cr->measure()->tick() >= s1->tick() && offset) { f = Fraction::fromTicks(offset); tick = s->measure()->tick(); } else { tick = s->tick(); f = Fraction(); } tuplet = cr->tuplet(); if (tuplet && (tuplet->tick() == tick) && ((tuplet->tick() + tuplet->actualTicks()) <= tick2) ) { // remove complete top level tuplet Tuplet* t = cr->tuplet(); while (t->tuplet()) t = t->tuplet(); cmdDeleteTuplet(t, false); f += t->duration(); tuplet = 0; continue; } } if (tuplet != cr->tuplet()) { Tuplet* t = cr->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->duration(); tuplet = 0; continue; } if (f.isValid()) setRest(tick, track, f, false, tuplet); tick = cr->tick(); tuplet = cr->tuplet(); removeChordRest(cr, true); f = cr->duration(); } else { removeChordRest(cr, true); f += cr->duration(); } } if (f.isValid() && !f.isZero()) { fullMeasure = false; // HACK if (fullMeasure) { // handle this as special case to be able to // fix broken measures: // ws: does not work as TimeSig may be already removed for (Measure* m = s1->measure(); m; m = m->nextMeasure()) { Staff* staff = Score::staff(track / VOICES); int tick = m->tick(); Fraction f = staff->timeSig(tick)->sig(); Rest* r = setRest(tick, track, f, 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; } } } 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 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 deletedElements; for (Element* e : el) { // these are the linked elements we are about to delete QList links; if (e->links()) links = *e->links(); // find location of element to select after deleting notes int tick = -1; int track = -1; if (!cr) { if (e->isNote()) tick = toNote(e)->chord()->tick(); else if (e->isRest()) tick = toRest(e)->tick(); //else tick < 0 track = e->track(); } // delete element if we have not done so already if (!deletedElements.contains(e)) deleteItem(e); // find element to select if (!cr && tick >= 0 && 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 (_is.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; int stick1 = -1; int stick2 = -1; int track1 = -1; int track2 = -1; Rest* r = nullptr; if (inputState().noteEntryMode()) { s1 = inputState().segment(); if (!s1 || s1->rtick() != 0) 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(StyleIdx::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() != Segment::Type::ChordRest) ss1 = ss1->next1(Segment::Type::ChordRest); bool fullMeasure = ss1 && (ss1->measure()->first(Segment::Type::ChordRest) == ss1) && (s2 == 0 || (s2->segmentType() == Segment::Type::EndBarLine) || (s2->segmentType() == Segment::Type::TimeSigAnnounce) || (s2->segmentType() == Segment::Type::KeySigAnnounce)); if (!fullMeasure) { return; } track1 = selection().staffStart() * VOICES; track2 = selection().staffEnd() * VOICES; } else if (selection().cr()) { ChordRest* cr = selection().cr(); if (!cr || cr->rtick() != 0) 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() != Segment::Type::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()) 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 (inputState().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(StyleIdx::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())) { 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 return 0; int no = 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, int tickStart, int 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; } //--------------------------------------------------------- // cmdCreateTuplet // replace cr with tuplet //--------------------------------------------------------- void Score::cmdCreateTuplet(ChordRest* ocr, Tuplet* tuplet) { qDebug("cmdCreateTuplet at %d <%s> track %d duration <%s> ratio <%s> baseLen <%s>", ocr->tick(), ocr->name(), ocr->track(), qPrintable(ocr->duration().print()), qPrintable(tuplet->ratio().print()), qPrintable(tuplet->baseLen().fraction().print()) ); int track = ocr->track(); Measure* measure = ocr->measure(); int 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->duration() * 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->setDuration(tuplet->baseLen().fraction()); qDebug("tuplet note duration %s actualNotes %d ticks %d track %d tuplet track %d", qPrintable(tuplet->baseLen().name()), actualNotes, cr->actualTicks(), track, tuplet->track()); undoAddCR(cr, measure, tick); int 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->setDuration(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; foreach(Element* e, selection().elements()) { if (e->color() != c) { undoChangeProperty(e, P_ID::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; } int t1 = selection().tickStart(); int t2 = selection().tickEnd(); Measure* m1 = tick2measure(t1); Measure* m2 = tick2measure(t2); if (selection().score()->excerpt()) return; if (t2 > m2->tick()) m2 = 0; 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 segments; for (ScoreElement* e : cr->linkList()) { undo(new RemoveElement(static_cast(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->duration(), 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, Segment::Type::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(); } } //--------------------------------------------------------- // searchMeasureBase // search corresponding measure in linked score //--------------------------------------------------------- static MeasureBase* searchMeasureBase(Score* score, MeasureBase* mb) { if (mb == 0) return 0; if (mb->isMeasure()) { for (Measure* m = score->firstMeasure(); m; m = m->nextMeasure()) { if (m->tick() == mb->tick()) return m; } } else { if (!mb->links()) { if (mb->score() == score) return mb; else qDebug("searchMeasureBase: no links"); } else { for (ScoreElement* m : *mb->links()) { if (m->score() == score) return static_cast(m); } } } qDebug("searchMeasureBase: measure not found"); return nullptr; } //--------------------------------------------------------- // insertMeasure // Create a new MeasureBase of type type and insert // before measure. // If measure is zero, append new MeasureBase. //--------------------------------------------------------- MeasureBase* Score::insertMeasure(ElementType type, MeasureBase* measure, bool createEmptyMeasures) { int tick; int ticks = 0; 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() : 0; // use nominal time signature of current measure Fraction f = sigmap()->timesig(tick).nominal(); QList> ml; for (Score* score : scoreList()) ml.append(pair(score, searchMeasureBase(score, measure))); MeasureBase* omb = 0; // measure base in "this" score MeasureBase* rmb = 0; // measure base in root score (for linking) for (pair p : ml) { Score* score = p.first; MeasureBase* im = p.second; MeasureBase* mb = static_cast(Element::create(type, score)); mb->setTick(tick); if (score == this) omb = mb; if (type == ElementType::MEASURE) { if (isMaster()) omb = toMeasure(mb); Measure* m = toMeasure(mb); Measure* mi = im ? score->tick2measure(im->tick()) : 0; m->setTimesig(f); m->setLen(f); ticks = m->ticks(); QList tsl; QList ksl; QList cl; QList pcl; // // remove clef, time and key signatures // if (mi) { for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) { Measure* pm = mi->prevMeasure(); if (pm) { Segment* ps = pm->findSegment(Segment::Type::Clef, tick); if (ps) { 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->tick() == tick; s = s->next()) { Element* e = s->element(staffIdx * VOICES); if (!e) 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 == 0 && e->isClef()) { Clef* clef = toClef(e); cl.push_back(clef); ee = e; } if (ee) { undo(new RemoveElement(ee)); if (s->empty()) undoRemoveElement(s); } } } } m->setNext(im); m->setPrev(im ? im->prev() : score->last()); undo(new InsertMeasures(m, m)); // // move clef, time, key signatrues // for (TimeSig* ts : tsl) { TimeSig* nts = new TimeSig(*ts); Segment* s = m->undoGetSegment(Segment::Type::TimeSig, tick); nts->setParent(s); undoAddElement(nts); } for (KeySig* ks : ksl) { KeySig* nks = new KeySig(*ks); Segment* s = m->undoGetSegment(Segment::Type::KeySig, tick); nks->setParent(s); undoAddElement(nks); } for (Clef* clef : cl) { Clef* nClef = new Clef(*clef); Segment* s = m->undoGetSegment(Segment::Type::Clef, tick); nClef->setParent(s); undoAddElement(nClef); } Measure* pm = m->prevMeasure(); for (Clef* clef : pcl) { Clef* nClef = new Clef(*clef); Segment* s = pm->undoGetSegment(Segment::Type::Clef, tick); nClef->setParent(s); undoAddElement(nClef); } // if (createEndBar) // m->setEndBarLineType(BarLineType::END, false); } else { // a frame, not a measure if (isMaster()) rmb = mb; else if (rmb && mb != rmb) { mb->linkTo(rmb); if (rmb->isTBox()) toTBox(mb)->text()->linkTo(toTBox(rmb)->text()); } mb->setNext(im); mb->setPrev(im ? im->prev() : score->last()); undo(new InsertMeasures(mb, mb)); } } undoInsertTime(tick, ticks); if (omb && type == ElementType::MEASURE && !createEmptyMeasures) { // // fill measure with rest // Score* _root = masterScore(); MeasureBase* rootMeasure = searchMeasureBase(_root, omb); Q_ASSERT(_root == rootMeasure->score()); for (int staffIdx = 0; staffIdx < _root->nstaves(); ++staffIdx) { int track = staffIdx * VOICES; int tick = omb->tick(); Segment* s = toMeasure(rootMeasure)->findSegment(Segment::Type::ChordRest, tick); if (s == 0 || s->element(track) == 0) { // add rest to this staff and to all the staves linked to it Rest* rest = new Rest(_root, TDuration(TDuration::DurationType::V_MEASURE)); Fraction timeStretch(_root->staff(staffIdx)->timeStretch(tick)); rest->setDuration(toMeasure(omb)->len() * timeStretch); rest->setTrack(track); _root->undoAddCR(rest, toMeasure(rootMeasure), tick); } } } deselectAll(); return omb; } //--------------------------------------------------------- // 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(int startTick, int endTick) { QList sl; // spanners to remove QList sl2; // spanners to shorten auto spanners = _spanner.findOverlapping(startTick, endTick); // printf("checkSpanner %d %d\n", startTick, endTick); // for (auto i = spanners.begin(); i < spanners.end(); i++) { // DEBUG: check all spanner // there may be spanners outside of score bc. some measures were deleted int lastTick = lastMeasure()->endTick(); for (auto i : _spanner.map()) { Spanner* s = i.second; if (s->isSlur()) { Segment* seg = tick2segmentMM(s->tick(), false, Segment::Type::ChordRest); if (!seg || !seg->element(s->track())) { qDebug("checkSpanner::remove (1) tick %d seg %p", s->tick(), seg); sl.append(s); } else { seg = tick2segmentMM(s->tick2(), false, Segment::Type::ChordRest); if (!seg || !seg->element(s->track2())) { qDebug("checkSpanner::remove (2) %d - tick %d track %d", s->tick(), s->tick2(), 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(P_ID::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, P_ID::SPANNER_TICKS, lastTick - s->tick())); s->computeEndElement(); } } static constexpr Segment::Type CR_TYPE = Segment::Type::ChordRest; //--------------------------------------------------------- // globalTimeDelete //--------------------------------------------------------- void Score::globalTimeDelete() { qDebug("not implemented"); } //--------------------------------------------------------- // localTimeDelete //--------------------------------------------------------- void Score::localTimeDelete() { printf("local time delete\n"); 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(); int endTick = startSegment->tick() + cr->duration().ticks(); endSegment = cr->measure()->findSegment(CR_TYPE, endTick); if (endSegment == 0) { qDebug("no segment at %d", endTick); return; } } else { startSegment = selection().startSegment(); endSegment = selection().endSegment(); } 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(); printf("A\n"); for (;;) { if (is->tick() != startSegment->tick()) { printf("B\n"); int tick = startSegment->tick(); int len; if (ie == is) len = endSegment->tick() - tick; else len = is->endTick() - tick; timeDelete(toMeasure(is), startSegment, Fraction::fromTicks(len)); if (is == ie) break; is = is->next(); } int endTick = endSegment ? endSegment->tick() : ie->endTick(); if (ie->endTick() != endTick) { printf("C\n"); int len = endSegment->tick() - ie->tick(); timeDelete(toMeasure(ie), toMeasure(ie)->first(), Fraction::fromTicks(len)); if (is == ie) break; ie = ie->prev(); } printf("D\n"); deleteMeasures(is, ie); printf("E\n"); break; }; deselectAll(); } //--------------------------------------------------------- // timeDelete //--------------------------------------------------------- void Score::timeDelete(Measure* m, Segment* startSegment, const Fraction& f) { int tick = startSegment->rtick(); int len = f.ticks(); int 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)); int cetick = s->rtick() + cr->duration().ticks(); if (cetick <= tick) { continue; } if (s->rtick() >= etick) { break; } if (cr->isFullMeasureRest()) { // do nothing } // inside deleted area else if (s->rtick() >= tick && cetick <= etick) { // inside undoRemoveElement(cr); } else if (s->rtick() >= tick) { // running out Fraction ff = cr->duration() - Fraction::fromTicks(cetick - etick); undoRemoveElement(cr); createCRSequence(ff, cr, tick + len); } else if (s->rtick() < tick && cetick <= etick) { // running in Fraction f1 = Fraction::fromTicks(tick - s->tick()); changeCRlen(cr, f1, false); } else { // running in/out Fraction f1 = cr->duration() - f; changeCRlen(cr, f1, false); } } } } } undoInsertTime(tick, -len); undo(new InsertTime(this, tick, -len)); for (Segment* s = startSegment->next(); s; s = s->next()) s->undoChangeProperty(P_ID::TICK, s->rtick() - len); undo(new ChangeMeasureLen(m, m->len() - f)); } //--------------------------------------------------------- // cloneVoice //--------------------------------------------------------- void Score::cloneVoice(int strack, int dtrack, Segment* sf, int lTick, bool link, bool spanner) { int 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* nt = tupletMap.findNew(ot->tuplet()); if (nt == 0) { if (link) nt = toTuplet(ot->tuplet()->linkedClone()); else nt = toTuplet(ot->tuplet()->clone()); nt->setTrack(dtrack); nt->setParent(dm); tupletMap.add(ot->tuplet(), nt); } nt->add(nt1); nt1->setTuplet(nt); ot = ot->tuplet(); nt1 = nt; } } 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); int n = och->notes().size(); for (int i = 0; i < n; ++i) { Note* on = och->notes().at(i); Note* nn = nch->notes().at(i); Interval v = staff(dtrack) ? staff(dtrack)->part()->instrument(dtrack)->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 = static_cast(oldSp->linkedClone()); else newSp = static_cast(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->setDuration(dm->len()); rest->setDurationType(TDuration::DurationType::V_MEASURE); rest->setTrack(dtrack); if (link) { Segment* segment = dm->getSegment(Segment::Type::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, lTick); for (auto i = spanners.begin(); i < spanners.end(); i++) { Spanner* sp = i->value; int spStart = sp->tick(); int track = sp->track(); int track2 = sp->track2(); int spEnd = spStart + sp->ticks(); qDebug("Start %d End %d Diff %d \n Measure Start %d End %d", spStart, spEnd, spEnd-spStart, start, lTick); if (sp->isSlur() && (spStart >= start && spEnd < lTick)) { if (track == strack && track2 == strack){ Spanner* ns; if (link) ns = static_cast(sp->linkedClone()); else ns = static_cast(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 = static_cast(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 = static_cast(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); } }