//============================================================================= // MuseScore // Music Composition & Notation // // Copyright (C) 2002-2011 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 class Score (partial). */ #include #include "score.h" #include "key.h" #include "sig.h" #include "clef.h" #include "tempo.h" #include "measure.h" #include "page.h" #include "undo.h" #include "system.h" #include "select.h" #include "segment.h" #include "xml.h" #include "text.h" #include "note.h" #include "chord.h" #include "rest.h" #include "slur.h" #include "staff.h" #include "part.h" #include "style.h" #include "tuplet.h" #include "lyrics.h" #include "pitchspelling.h" #include "line.h" #include "volta.h" #include "repeat.h" #include "ottava.h" #include "barline.h" #include "box.h" #include "utils.h" #include "excerpt.h" #include "stafftext.h" #include "repeatlist.h" #include "keysig.h" #include "beam.h" #include "stafftype.h" #include "tempotext.h" #include "articulation.h" #include "revisions.h" #include "tiemap.h" #include "layoutbreak.h" #include "harmony.h" #include "mscore.h" #ifdef OMR #include "omr/omr.h" #endif #include "bracket.h" #include "audio.h" #include "instrtemplate.h" #include "cursor.h" #include "sym.h" #include "rehearsalmark.h" #include "breath.h" #include "instrchange.h" namespace Ms { MasterScore* gscore; ///< system score, used for palettes etc. bool scriptDebug = false; bool noSeq = false; bool noMidi = false; bool midiInputTrace = false; bool midiOutputTrace = false; bool showRubberBand = true; //--------------------------------------------------------- // MeasureBaseList //--------------------------------------------------------- MeasureBaseList::MeasureBaseList() { _first = 0; _last = 0; _size = 0; }; //--------------------------------------------------------- // push_back //--------------------------------------------------------- void MeasureBaseList::push_back(MeasureBase* e) { ++_size; if (_last) { _last->setNext(e); e->setPrev(_last); e->setNext(0); } else { _first = e; e->setPrev(0); e->setNext(0); } _last = e; } //--------------------------------------------------------- // push_front //--------------------------------------------------------- void MeasureBaseList::push_front(MeasureBase* e) { ++_size; if (_first) { _first->setPrev(e); e->setNext(_first); e->setPrev(0); } else { _last = e; e->setPrev(0); e->setNext(0); } _first = e; } //--------------------------------------------------------- // add // insert e before e->next() //--------------------------------------------------------- void MeasureBaseList::add(MeasureBase* e) { MeasureBase* el = e->next(); if (el == 0) { push_back(e); return; } if (el == _first) { push_front(e); return; } ++_size; e->setPrev(el->prev()); el->prev()->setNext(e); el->setPrev(e); } //--------------------------------------------------------- // remove //--------------------------------------------------------- void MeasureBaseList::remove(MeasureBase* el) { --_size; if (el->prev()) el->prev()->setNext(el->next()); else _first = el->next(); if (el->next()) el->next()->setPrev(el->prev()); else _last = el->prev(); } //--------------------------------------------------------- // insert //--------------------------------------------------------- void MeasureBaseList::insert(MeasureBase* fm, MeasureBase* lm) { ++_size; for (MeasureBase* m = fm; m != lm; m = m->next()) ++_size; MeasureBase* pm = fm->prev(); if (pm) pm->setNext(fm); else _first = fm; MeasureBase* nm = lm->next(); if (nm) nm->setPrev(lm); else _last = lm; } //--------------------------------------------------------- // remove //--------------------------------------------------------- void MeasureBaseList::remove(MeasureBase* fm, MeasureBase* lm) { --_size; for (MeasureBase* m = fm; m != lm; m = m->next()) --_size; MeasureBase* pm = fm->prev(); MeasureBase* nm = lm->next(); if (pm) pm->setNext(nm); else _first = nm; if (nm) nm->setPrev(pm); else _last = pm; } //--------------------------------------------------------- // change //--------------------------------------------------------- void MeasureBaseList::change(MeasureBase* ob, MeasureBase* nb) { nb->setPrev(ob->prev()); nb->setNext(ob->next()); if (ob->prev()) ob->prev()->setNext(nb); if (ob->next()) ob->next()->setPrev(nb); if (ob == _last) _last = nb; if (ob == _first) _first = nb; if (nb->type() == Element::Type::HBOX || nb->type() == Element::Type::VBOX || nb->type() == Element::Type::TBOX || nb->type() == Element::Type::FBOX) nb->setSystem(ob->system()); foreach(Element* e, nb->el()) e->setParent(nb); } #if 0 //--------------------------------------------------------- // init //--------------------------------------------------------- void Score::init() { Layer l; l.name = "default"; l.tags = 1; _layer.append(l); _layerTags[0] = "default"; _scoreFont = ScoreFont::fontFactory("emmentaler"); _pos[int(POS::CURRENT)] = 0; _pos[int(POS::LEFT)] = 0; _pos[int(POS::RIGHT)] = 0; _fileDivision = MScore::division; } #endif //--------------------------------------------------------- // Score //--------------------------------------------------------- Score::Score() : QObject(0), ScoreElement(this), _is(this), _selection(this), _selectionFilter(this) { _masterScore = 0; // init(); Layer l; l.name = "default"; l.tags = 1; _layer.append(l); _layerTags[0] = "default"; _scoreFont = ScoreFont::fontFactory("emmentaler"); _pos[int(POS::CURRENT)] = 0; _pos[int(POS::LEFT)] = 0; _pos[int(POS::RIGHT)] = 0; _fileDivision = MScore::division; _style = *(MScore::defaultStyle()); accInfo = tr("No selection"); } Score::Score(MasterScore* parent) : Score{} { _masterScore = parent; if (MScore::defaultStyleForParts()) _style = *MScore::defaultStyleForParts(); else { // inherit most style settings from parent _style = *parent->style(); // but borrow defaultStyle page layout settings const PageFormat* pf = MScore::defaultStyle()->pageFormat(); _style.setPageFormat(*pf); _style.set(StyleIdx::spatium, MScore::defaultStyle()->value(StyleIdx::spatium)); // and force some style settings that just make sense for parts _style.set(StyleIdx::concertPitch, false); _style.set(StyleIdx::createMultiMeasureRests, true); _style.set(StyleIdx::dividerLeft, false); _style.set(StyleIdx::dividerRight, false); } _synthesizerState = parent->_synthesizerState; } Score::Score(MasterScore* parent, const MStyle* s) : Score{parent} { _style = *s; } //--------------------------------------------------------- // ~Score //--------------------------------------------------------- Score::~Score() { foreach(MuseScoreView* v, viewer) v->removeScore(); // deselectAll(); for (MeasureBase* m = _measures.first(); m;) { MeasureBase* nm = m->next(); delete m; m = nm; } qDeleteAll(_parts); qDeleteAll(_staves); qDeleteAll(_systems); qDeleteAll(_pages); _masterScore = 0; } //--------------------------------------------------------- // elementAdjustReadPos //--------------------------------------------------------- static void elementAdjustReadPos(void*, Element* e) { if (e->isMovable()) e->adjustReadPos(); } //--------------------------------------------------------- // addMeasure //--------------------------------------------------------- void Score::addMeasure(MeasureBase* m, MeasureBase* pos) { m->setNext(pos); _measures.add(m); } //--------------------------------------------------------- // fixTicks // update: // - measure ticks // - tempo map // - time signature map //--------------------------------------------------------- /** This is needed after - inserting or removing a measure - changing the sigmap - after inserting/deleting time (changes the sigmap) */ void Score::fixTicks() { int tick = 0; Measure* fm = firstMeasure(); if (fm == 0) return; for (Staff* staff : _staves) staff->clearTimeSig(); Fraction sig(fm->len()); Fraction nomSig(fm->timesig()); if (isMaster()) { tempomap()->clear(); sigmap()->clear(); sigmap()->add(0, SigEvent(fm->len(), fm->timesig(), 0)); } for (MeasureBase* mb = first(); mb; mb = mb->next()) { if (mb->type() != Element::Type::MEASURE) { mb->setTick(tick); continue; } Measure* m = toMeasure(mb); int mtick = m->tick(); int diff = tick - mtick; int measureTicks = m->ticks(); m->moveTicks(diff); if (m->mmRest()) m->mmRest()->moveTicks(diff); // // implement section break rest // if (m->sectionBreak() && m->pause() != 0.0) setPause(m->tick() + m->ticks(), m->pause()); // // implement fermata as a tempo change // for (Segment* s = m->first(); s; s = s->next()) { if (s->segmentType() == Segment::Type::Breath) { qreal length = 0.0; int tick = s->tick(); // find longest pause for (int i = 0, n = ntracks(); i < n; ++i) { Element* e = s->element(i); if (e && e->type() == Element::Type::BREATH) { Breath* b = toBreath(e); length = qMax(length, b->pause()); } } if (length != 0.0) setPause(tick, length); } else if (s->segmentType() == Segment::Type::TimeSig) { for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) { TimeSig* ts = toTimeSig(s->element(staffIdx * VOICES)); if (ts) staff(staffIdx)->addTimeSig(ts); } } // TODO: all done in doLayout, getNextMeasure, collectSystem... Do we keep? else if (isMaster() && (s->segmentType() == Segment::Type::ChordRest)) { for (Element* e : s->annotations()) { if (e->type() == Element::Type::TEMPO_TEXT) { TempoText* tt = toTempoText(e); if (tt->isRelative()) tt->updateRelative(); setTempo(tt->segment(), tt->tempo()); } } qreal stretch = 0.0; for (unsigned i = 0; i < s->elist().size(); ++i) { Element* e = s->elist().at(i); if (!e) continue; ChordRest* cr = toChordRest(e); int nn = cr->articulations().size(); for (int ii = 0; ii < nn; ++ii) stretch = qMax(cr->articulations().at(ii)->timeStretch(), stretch); if (stretch != 0.0 && stretch != 1.0) { qreal otempo = tempomap()->tempo(cr->tick()); qreal ntempo = otempo / stretch; setTempo(cr->tick(), ntempo); int etick = cr->tick() + cr->actualTicks() - 1; auto e = tempomap()->find(etick); if (e == tempomap()->end()) setTempo(etick, otempo); break; } } } } // update time signature map // create event if measure len and time signature are different // even if they are equivalent 4/4 vs 2/2 // also check if nominal time signature has changed if (isMaster() && (!sig.identical(m->len()) || !nomSig.identical(m->timesig()))) { sig = m->len(); nomSig = m->timesig(); sigmap()->add(tick, SigEvent(sig, nomSig, m->no())); } tick += measureTicks; } // Now done in getNextMeasure(), do we keep? if (tempomap()->empty()) tempomap()->setTempo(0, 2.0); } //--------------------------------------------------------- // validSegment //--------------------------------------------------------- static bool validSegment(Segment* s, int startTrack, int endTrack) { for (int track = startTrack; track < endTrack; ++track) { if (s->element(track)) return true; } return false; } //--------------------------------------------------------- // pos2measure //--------------------------------------------------------- /** Return measure for canvas relative position \a p. */ MeasureBase* Score::pos2measure(const QPointF& p, int* rst, int* pitch, Segment** seg, QPointF* offset) const { Measure* m = searchMeasure(p); if (m == 0) return 0; System* s = m->system(); qreal y = p.y() - s->canvasPos().y(); int i; for (i = 0; i < nstaves();) { SysStaff* stff = s->staff(i); if (!stff->show() || !staff(i)->show()) { ++i; continue; } int ni = i; for (;;) { ++ni; if (ni == nstaves() || (s->staff(ni)->show() && staff(ni)->show())) break; } qreal sy2; if (ni != nstaves()) { SysStaff* nstaff = s->staff(ni); qreal s1y2 = stff->bbox().y() + stff->bbox().height(); sy2 = s1y2 + (nstaff->bbox().y() - s1y2)/2; } else sy2 = s->page()->height() - s->pos().y(); // s->height(); if (y > sy2) { i = ni; continue; } break; } // search for segment + offset QPointF pppp = p - m->canvasPos(); int strack = i * VOICES; if (!staff(i)) return 0; int etrack = staff(i)->part()->nstaves() * VOICES + strack; SysStaff* sstaff = m->system()->staff(i); Segment::Type st = Segment::Type::ChordRest; for (Segment* segment = m->first(st); segment; segment = segment->next(st)) { if (!validSegment(segment, strack, etrack)) continue; Segment* ns = segment->next(st); for (; ns; ns = ns->next(st)) { if (validSegment(ns, strack, etrack)) break; } if (!ns || (pppp.x() < (segment->x() + (ns->x() - segment->x())/2.0))) { *rst = i; if (pitch) { Staff* s = _staves[i]; ClefType clef = s->clef(segment->tick()); *pitch = y2pitch(pppp.y() - sstaff->bbox().y(), clef, s->spatium()); } if (offset) *offset = pppp - QPointF(segment->x(), sstaff->bbox().y()); if (seg) *seg = segment; return m; } } return 0; } //--------------------------------------------------------- // dragPosition // on input: // p - canvas relative drag position // rst - current staff index // seg - current segment // on output: // rst - new staff index for drag position // seg - new segment for drag position //--------------------------------------------------------- void Score::dragPosition(const QPointF& p, int* rst, Segment** seg) const { Measure* m = searchMeasure(p); if (m == 0) return; System* s = m->system(); qreal y = p.y() - s->canvasPos().y(); int i; for (i = 0; i < nstaves();) { SysStaff* stff = s->staff(i); if (!stff->show() || !staff(i)->show()) { ++i; continue; } int ni = i; for (;;) { ++ni; if (ni == nstaves() || (s->staff(ni)->show() && staff(ni)->show())) break; } qreal sy2; if (ni != nstaves()) { SysStaff* nstaff = s->staff(ni); qreal s1y2 = stff->bbox().y() + stff->bbox().height(); if (i == *rst) sy2 = s1y2 + (nstaff->bbox().y() - s1y2); else if (ni == *rst) sy2 = s1y2; else sy2 = s1y2 + (nstaff->bbox().y() - s1y2) * .5; } else sy2 = s->page()->height() - s->pos().y(); if (y > sy2) { i = ni; continue; } break; } // search for segment + offset QPointF pppp = p - m->canvasPos(); int strack = i * VOICES; if (!staff(i)) return; int etrack = staff(i)->part()->nstaves() * VOICES + strack; Segment::Type st = Segment::Type::ChordRest; for (Segment* segment = m->first(st); segment; segment = segment->next(st)) { if (!validSegment(segment, strack, etrack)) continue; Segment* ns = segment->next(st); for (; ns; ns = ns->next(st)) { if (validSegment(ns, strack, etrack)) break; } if (!ns) { *rst = i; *seg = segment; return; } if (*seg == segment) { if (pppp.x() < (segment->x() + (ns->x() - segment->x()))) { *rst = i; *seg = segment; return; } } else if (*seg == ns) { if (pppp.x() <= segment->x()) { *rst = i; *seg = segment; return; } } else { if (pppp.x() < (segment->x() + (ns->x() - segment->x())/2.0)) { *rst = i; *seg = segment; return; } } } return; } //--------------------------------------------------------- // staffIdx // /// Return index for the first staff of \a part. //--------------------------------------------------------- int Score::staffIdx(const Part* part) const { int idx = 0; foreach(Part* p, _parts) { if (p == part) break; idx += p->nstaves(); } return idx; } //--------------------------------------------------------- // setShowInvisible //--------------------------------------------------------- void Score::setShowInvisible(bool v) { _showInvisible = v; rebuildBspTree(); setUpdateAll(); update(); } //--------------------------------------------------------- // setShowUnprintable //--------------------------------------------------------- void Score::setShowUnprintable(bool v) { _showUnprintable = v; setUpdateAll(); update(); } //--------------------------------------------------------- // setShowFrames //--------------------------------------------------------- void Score::setShowFrames(bool v) { _showFrames = v; setUpdateAll(); update(); } //--------------------------------------------------------- // setShowPageborders //--------------------------------------------------------- void Score::setShowPageborders(bool v) { _showPageborders = v; setUpdateAll(); update(); } //--------------------------------------------------------- // setMarkIrregularMeasures //--------------------------------------------------------- void Score::setMarkIrregularMeasures(bool v) { _markIrregularMeasures = v; setUpdateAll(); update(); } //--------------------------------------------------------- // dirty //--------------------------------------------------------- bool Score::dirty() const { return !undoStack()->isClean(); } //--------------------------------------------------------- // spell //--------------------------------------------------------- void Score::spell() { for (int i = 0; i < nstaves(); ++i) { std::vector notes; for (Segment* s = firstSegment(); s; s = s->next1()) { int strack = i * VOICES; int etrack = strack + VOICES; for (int track = strack; track < etrack; ++track) { Element* e = s->element(track); if (e && e->type() == Element::Type::CHORD) notes.insert(notes.end(), toChord(e)->notes().begin(), toChord(e)->notes().end()); } } spellNotelist(notes); } } void Score::spell(int startStaff, int endStaff, Segment* startSegment, Segment* endSegment) { for (int i = startStaff; i < endStaff; ++i) { std::vector notes; for (Segment* s = startSegment; s && s != endSegment; s = s->next()) { int strack = i * VOICES; int etrack = strack + VOICES; for (int track = strack; track < etrack; ++track) { Element* e = s->element(track); if (e && e->type() == Element::Type::CHORD) notes.insert(notes.end(), toChord(e)->notes().begin(), toChord(e)->notes().end()); } } spellNotelist(notes); } } //--------------------------------------------------------- // prevNote //--------------------------------------------------------- Note* prevNote(Note* n) { Chord* chord = n->chord(); Segment* seg = chord->segment(); const std::vector nl = chord->notes(); auto i = std::find(nl.begin(), nl.end(), n); if (i != nl.begin()) return *(i-1); int staff = n->staffIdx(); int startTrack = staff * VOICES + n->voice() - 1; int endTrack = 0; while (seg) { if (seg->segmentType() == Segment::Type::ChordRest) { for (int track = startTrack; track >= endTrack; --track) { Element* e = seg->element(track); if (e && e->type() == Element::Type::CHORD) return toChord(e)->upNote(); } } seg = seg->prev1(); startTrack = staff * VOICES + VOICES - 1; } return n; } //--------------------------------------------------------- // nextNote //--------------------------------------------------------- static Note* nextNote(Note* n) { Chord* chord = n->chord(); const std::vector nl = chord->notes(); auto i = std::find(nl.begin(), nl.end(), n); if (i != nl.end()) { ++i; if (i != nl.end()) return *i; } Segment* seg = chord->segment(); int staff = n->staffIdx(); int startTrack = staff * VOICES + n->voice() + 1; int endTrack = staff * VOICES + VOICES; while (seg) { if (seg->segmentType() == Segment::Type::ChordRest) { for (int track = startTrack; track < endTrack; ++track) { Element* e = seg->element(track); if (e && e->type() == Element::Type::CHORD) { return ((Chord*)e)->downNote(); } } } seg = seg->next1(); startTrack = staff * VOICES; } return n; } //--------------------------------------------------------- // spell //--------------------------------------------------------- void Score::spell(Note* note) { std::vector notes; notes.push_back(note); Note* nn = nextNote(note); notes.push_back(nn); nn = nextNote(nn); notes.push_back(nn); nn = nextNote(nn); notes.push_back(nn); nn = prevNote(note); notes.insert(notes.begin(), nn); nn = prevNote(nn); notes.insert(notes.begin(), nn); nn = prevNote(nn); notes.insert(notes.begin(), nn); int opt = Ms::computeWindow(notes, 0, 7); note->setTpc(Ms::tpc(3, note->pitch(), opt)); } //--------------------------------------------------------- // appendPart //--------------------------------------------------------- void Score::appendPart(Part* p) { _parts.append(p); } //--------------------------------------------------------- // searchPage // p is in canvas coordinates //--------------------------------------------------------- Page* Score::searchPage(const QPointF& p) const { for (Page* page : pages()) { if (page->bbox().translated(page->pos()).contains(p)) return page; } return 0; } //--------------------------------------------------------- // searchSystem // return list of systems as there may be more than // one system in a row // p is in canvas coordinates //--------------------------------------------------------- QList Score::searchSystem(const QPointF& pos) const { QList systems; Page* page = searchPage(pos); if (page == 0) return systems; qreal y = pos.y() - page->pos().y(); // transform to page relative const QList* sl = &page->systems(); qreal y2; int n = sl->size(); for (int i = 0; i < n; ++i) { System* s = sl->at(i); System* ns = 0; // next system row int ii = i + 1; for (; ii < n; ++ii) { ns = sl->at(ii); if (ns->y() != s->y()) break; } if ((ii == n) || (ns == 0)) y2 = page->height(); else { qreal sy2 = s->y() + s->bbox().height(); y2 = sy2 + (ns->y() - sy2) * .5; } if (y < y2) { systems.append(s); for (int ii = i+1; ii < n; ++ii) { if (sl->at(ii)->y() != s->y()) break; systems.append(sl->at(ii)); } return systems; } } return systems; } //--------------------------------------------------------- // searchMeasure // p is in canvas coordinates //--------------------------------------------------------- Measure* Score::searchMeasure(const QPointF& p) const { QList systems = searchSystem(p); if (systems.empty()) return 0; foreach(System* system, systems) { qreal x = p.x() - system->canvasPos().x(); foreach(MeasureBase* mb, system->measures()) { if (mb->type() != Element::Type::MEASURE) continue; if (x < (mb->x() + mb->bbox().width())) return toMeasure(mb); } } return 0; } //--------------------------------------------------------- // getNextValidInputSegment // - s is of type Segment::Type::ChordRest //--------------------------------------------------------- static Segment* getNextValidInputSegment(Segment* s, int track, int voice) { if (s == 0) return 0; Q_ASSERT(s->segmentType() == Segment::Type::ChordRest); // Segment* s1 = s; ChordRest* cr1; for (Segment* s1 = s; s1; s1 = s1->prev(Segment::Type::ChordRest)) { cr1 = toChordRest(s1->element(track + voice)); if (cr1) break; } int nextTick = (cr1 == 0) ? s->measure()->tick() : cr1->tick() + cr1->actualTicks(); static const Segment::Type st { Segment::Type::ChordRest }; while (s) { if (s->element(track + voice)) break; if (voice && s->tick() == nextTick) return s; #if 0 int v; for (v = 0; v < VOICES; ++v) { if (s->element(track + v)) break; } if ((v != VOICES) && voice) { int ntick; bool skipChord = false; bool ns = false; for (Segment* s1 = s->measure()->first(st); s1; s1 = s1->next(st)) { ChordRest* cr = toChordRest(s1->element(track + voice)); if (cr) { if (ns) return s1; ntick = s1->tick() + cr->actualTicks(); skipChord = true; } if (s1 == s) ns = true; if (skipChord) { if (s->tick() >= ntick) skipChord = false; } if (!skipChord && ns) return s1; } if (!skipChord) return s; } #endif s = s->next(st); } return s; } //--------------------------------------------------------- // getPosition // return true if valid position found //--------------------------------------------------------- bool Score::getPosition(Position* pos, const QPointF& p, int voice) const { Measure* measure = searchMeasure(p); if (measure == 0) return false; pos->fret = FRET_NONE; // // search staff // pos->staffIdx = 0; SysStaff* sstaff = 0; System* system = measure->system(); qreal y = p.y() - system->pagePos().y(); for (; pos->staffIdx < nstaves(); ++pos->staffIdx) { Staff* st = staff(pos->staffIdx); if (st->invisible() || !st->part()->show()) continue; qreal sy2; SysStaff* ss = system->staff(pos->staffIdx); SysStaff* nstaff = 0; // find next visible staff for (int i = pos->staffIdx + 1; i < nstaves(); ++i) { Staff* st = staff(i); if (st->invisible() || !st->part()->show()) continue; nstaff = system->staff(i); break; } if (nstaff) { qreal s1y2 = ss->bbox().bottom(); sy2 = system->page()->canvasPos().y() + s1y2 + (nstaff->bbox().y() - s1y2) * .5; } else sy2 = system->page()->canvasPos().y() + system->page()->height() - system->pagePos().y(); // system->height(); if (y < sy2) { sstaff = ss; break; } } if (sstaff == 0) return false; // // search segment // QPointF pppp(p - measure->canvasPos()); qreal x = pppp.x(); Segment* segment = 0; pos->segment = 0; // int track = pos->staffIdx * VOICES + voice; int track = pos->staffIdx * VOICES; for (segment = measure->first(Segment::Type::ChordRest); segment;) { segment = getNextValidInputSegment(segment, track, voice); if (segment == 0) break; Segment* ns = getNextValidInputSegment(segment->next(Segment::Type::ChordRest), track, voice); qreal x1 = segment->x(); qreal x2; qreal d; if (ns) { x2 = ns->x(); d = x2 - x1; } else { x2 = measure->bbox().width(); d = (x2 - x1) * 2.0; x = x1; pos->segment = segment; break; } if (x < (x1 + d * .5)) { x = x1; pos->segment = segment; break; } segment = ns; } if (segment == 0) return false; // // TODO: restrict to reasonable values (pitch 0-127) // Staff* s = staff(pos->staffIdx); qreal mag = s->mag(); // in TABs, step from one string to another; in other staves, step on and between lines qreal lineDist = s->staffType()->lineDistance().val() * (s->isTabStaff() ? 1 : .5) * mag * spatium(); pos->line = lrint((pppp.y() - sstaff->bbox().y()) / lineDist); if (s->isTabStaff()) { if (pos->line < -1 || pos->line > s->lines()+1) return false; if (pos->line < 0) pos->line = 0; else if (pos->line >= s->lines()) pos->line = s->lines() - 1; } else { int minLine = absStep(0); ClefType clef = s->clef(pos->segment->tick()); minLine = relStep(minLine, clef); int maxLine = absStep(127); maxLine = relStep(maxLine, clef); if (pos->line > minLine || pos->line < maxLine) return false; } y = sstaff->y() + pos->line * lineDist; pos->pos = QPointF(x, y) + measure->canvasPos(); return true; } //--------------------------------------------------------- // checkHasMeasures //--------------------------------------------------------- bool Score::checkHasMeasures() const { Page* page = pages().front(); const QList* sl = &page->systems(); if (sl == 0 || sl->empty() || sl->front()->measures().empty()) { qDebug("first create measure, then repeat operation"); return false; } return true; } //--------------------------------------------------------- // moveBracket // columns are counted from right to left //--------------------------------------------------------- void Score::moveBracket(int staffIdx, int srcCol, int dstCol) { for (System* system : systems()) system->moveBracket(staffIdx, srcCol, dstCol); } //--------------------------------------------------------- // spatiumHasChanged //--------------------------------------------------------- static void spatiumHasChanged(void* data, Element* e) { qreal* val = (qreal*)data; e->spatiumChanged(val[0], val[1]); } //--------------------------------------------------------- // spatiumChanged //--------------------------------------------------------- void Score::spatiumChanged(qreal oldValue, qreal newValue) { qreal data[2]; data[0] = oldValue; data[1] = newValue; scanElements(data, spatiumHasChanged, true); foreach (Staff* staff, _staves) staff->spatiumChanged(oldValue, newValue); _noteHeadWidth = _scoreFont->width(SymId::noteheadBlack, newValue / SPATIUM20); } //--------------------------------------------------------- // getCreateMeasure // - return Measure for tick // - create new Measure(s) if there is no measure for // this tick //--------------------------------------------------------- Measure* Score::getCreateMeasure(int tick) { Measure* last = lastMeasure(); if (last == 0 || ((last->tick() + last->ticks()) <= tick)) { int lastTick = last ? (last->tick()+last->ticks()) : 0; while (tick >= lastTick) { Measure* m = new Measure(this); Fraction ts = sigmap()->timesig(lastTick).timesig(); // qDebug("getCreateMeasure %d %d/%d", tick, ts.numerator(), ts.denominator()); m->setTick(lastTick); m->setTimesig(ts); m->setLen(ts); measures()->add(static_cast(m)); lastTick += ts.ticks(); } } return tick2measure(tick); } //--------------------------------------------------------- // addElement //--------------------------------------------------------- /** Add \a element to its parent. Several elements (clef, keysig, timesig) need special handling, as they may cause changes throughout the score. */ void Score::addElement(Element* element) { Element* parent = element->parent(); element->triggerLayout(); // qDebug("Score(%p) Element(%p)(%s) parent %p(%s)", // this, element, element->name(), parent, parent ? parent->name() : ""); Element::Type et = element->type(); if (et == Element::Type::MEASURE || (et == Element::Type::HBOX && element->parent()->type() != Element::Type::VBOX) || et == Element::Type::VBOX || et == Element::Type::TBOX || et == Element::Type::FBOX ) { measures()->add(static_cast(element)); return; } if (parent) parent->add(element); switch (et) { case Element::Type::BEAM: { Beam* b = toBeam(element); int n = b->elements().size(); for (int i = 0; i < n; ++i) b->elements().at(i)->setBeam(b); } break; case Element::Type::SLUR: addLayoutFlags(LayoutFlag::PLAY_EVENTS); // fall through case Element::Type::VOLTA: case Element::Type::TRILL: case Element::Type::PEDAL: case Element::Type::TEXTLINE: case Element::Type::HAIRPIN: { Spanner* spanner = static_cast(element); if (et == Element::Type::TEXTLINE && spanner->anchor() == Spanner::Anchor::NOTE) break; addSpanner(spanner); for (SpannerSegment* ss : spanner->spannerSegments()) { if (ss->system()) ss->system()->add(ss); } } break; case Element::Type::OTTAVA: { Ottava* o = toOttava(element); addSpanner(o); foreach(SpannerSegment* ss, o->spannerSegments()) { if (ss->system()) ss->system()->add(ss); } cmdState().layoutFlags |= LayoutFlag::FIX_PITCH_VELO; o->staff()->updateOttava(); _playlistDirty = true; } break; case Element::Type::DYNAMIC: cmdState().layoutFlags |= LayoutFlag::FIX_PITCH_VELO; _playlistDirty = true; break; case Element::Type::TEMPO_TEXT: { TempoText* tt = toTempoText(element); if (tt->isRelative()) tt->updateRelative(); setTempo(tt->segment(), tt->tempo()); } break; case Element::Type::INSTRUMENT_CHANGE: { InstrumentChange* ic = toInstrumentChange(element); int tickStart = ic->segment()->tick(); auto i = ic->part()->instruments()->upper_bound(tickStart); int tickEnd; if (i == ic->part()->instruments()->end()) tickEnd = -1; else tickEnd = i->first; Interval oldV = ic->part()->instrument(tickStart)->transpose(); ic->part()->setInstrument(ic->instrument(), tickStart); transpositionChanged(ic->part(), oldV, tickStart, tickEnd); masterScore()->rebuildMidiMapping(); cmdState()._instrumentsChanged = true; } break; case Element::Type::CHORD: setPlaylistDirty(); // create playlist does not work here bc. tremolos may not be complete // createPlayEvents(toChord(element)); break; case Element::Type::NOTE: case Element::Type::TREMOLO: case Element::Type::ARTICULATION: case Element::Type::ARPEGGIO: { Element* cr = parent; if (cr->isChord()) createPlayEvents(toChord(cr)); } break; default: break; } setLayout(element->tick()); } //--------------------------------------------------------- // removeElement /// Remove \a element from its parent. /// Several elements (clef, keysig, timesig) need special handling, as they may cause /// changes throughout the score. //--------------------------------------------------------- void Score::removeElement(Element* element) { Element* parent = element->parent(); setLayout(element->tick()); // qDebug("Score(%p) Element(%p)(%s) parent %p(%s)", // this, element, element->name(), parent, parent ? parent->name() : ""); // special for MEASURE, HBOX, VBOX // their parent is not static Element::Type et = element->type(); if (et == Element::Type::MEASURE || (et == Element::Type::HBOX && !parent->isVBox()) || et == Element::Type::VBOX || et == Element::Type::TBOX || et == Element::Type::FBOX ) { measures()->remove(static_cast(element)); return; } if (et == Element::Type::BEAM) // beam parent does not survive layout element->setParent(0); if (parent) parent->remove(element); switch (et) { case Element::Type::BEAM: for (ChordRest* cr : toBeam(element)->elements()) cr->setBeam(0); break; case Element::Type::SLUR: addLayoutFlags(LayoutFlag::PLAY_EVENTS); // fall through case Element::Type::VOLTA: case Element::Type::TRILL: case Element::Type::PEDAL: case Element::Type::TEXTLINE: case Element::Type::HAIRPIN: { Spanner* spanner = static_cast(element); if (et == Element::Type::TEXTLINE && spanner->anchor() == Spanner::Anchor::NOTE) break; removeSpanner(spanner); for (SpannerSegment* ss : spanner->spannerSegments()) { if (ss->system()) ss->system()->remove(ss); } } break; case Element::Type::OTTAVA: { Ottava* o = toOttava(element); removeSpanner(o); foreach(SpannerSegment* ss, o->spannerSegments()) { if (ss->system()) ss->system()->remove(ss); } o->staff()->updateOttava(); cmdState().layoutFlags |= LayoutFlag::FIX_PITCH_VELO; _playlistDirty = true; } break; case Element::Type::DYNAMIC: cmdState().layoutFlags |= LayoutFlag::FIX_PITCH_VELO; _playlistDirty = true; break; case Element::Type::CHORD: case Element::Type::REST: { ChordRest* cr = toChordRest(element); if (cr->beam()) cr->beam()->remove(cr); for (Lyrics* lyr : cr->lyrics()) lyr->removeFromScore(); // TODO: check for tuplet? } break; case Element::Type::TEMPO_TEXT: { TempoText* tt = toTempoText(element); int tick = tt->segment()->tick(); tempomap()->delTempo(tick); } break; case Element::Type::INSTRUMENT_CHANGE: { InstrumentChange* ic = toInstrumentChange(element); int tickStart = ic->segment()->tick(); auto i = ic->part()->instruments()->upper_bound(tickStart); int tickEnd; if (i == ic->part()->instruments()->end()) tickEnd = -1; else tickEnd = i->first; Interval oldV = ic->part()->instrument(tickStart)->transpose(); ic->part()->removeInstrument(tickStart); transpositionChanged(ic->part(), oldV, tickStart, tickEnd); masterScore()->rebuildMidiMapping(); cmdState()._instrumentsChanged = true; } break; case Element::Type::TREMOLO: case Element::Type::ARTICULATION: case Element::Type::ARPEGGIO: { Element* cr = element->parent(); if (cr->isChord()) createPlayEvents(toChord(cr)); } break; default: break; } } //--------------------------------------------------------- // firstMeasure //--------------------------------------------------------- Measure* Score::firstMeasure() const { MeasureBase* mb = _measures.first(); while (mb && mb->type() != Element::Type::MEASURE) mb = mb->next(); return toMeasure(mb); } //--------------------------------------------------------- // firstMeasureMM //--------------------------------------------------------- Measure* Score::firstMeasureMM() const { Measure* m = firstMeasure(); if (m && styleB(StyleIdx::createMultiMeasureRests) && m->hasMMRest()) return m->mmRest(); return m; } //--------------------------------------------------------- // firstMM //--------------------------------------------------------- MeasureBase* Score::firstMM() const { MeasureBase* m = _measures.first(); if (m && m->type() == Element::Type::MEASURE && styleB(StyleIdx::createMultiMeasureRests) && toMeasure(m)->hasMMRest()) { return toMeasure(m)->mmRest(); } return m; } //--------------------------------------------------------- // measure //--------------------------------------------------------- MeasureBase* Score::measure(int idx) const { MeasureBase* mb = _measures.first(); for (int i = 0; i < idx; ++i) { mb = mb->next(); if (mb == 0) return 0; } return mb; } //--------------------------------------------------------- // lastMeasure //--------------------------------------------------------- Measure* Score::lastMeasure() const { MeasureBase* mb = _measures.last(); while (mb && mb->type() != Element::Type::MEASURE) mb = mb->prev(); return toMeasure(mb); } //--------------------------------------------------------- // lastMeasureMM //--------------------------------------------------------- Measure* Score::lastMeasureMM() const { Measure* m = lastMeasure(); if (m && styleB(StyleIdx::createMultiMeasureRests)) { Measure* m1 = const_cast(toMeasure(m->mmRest1())); if (m1) return m1; } return m; } //--------------------------------------------------------- // firstSegment //--------------------------------------------------------- Segment* Score::firstSegment(Segment::Type segType) const { Segment* seg; Measure* m = firstMeasure(); if (!m) seg = 0; else { seg = m->first(); if (seg && !(seg->segmentType() & segType)) seg = seg->next1(segType); } #ifdef SCRIPT_INTERFACE // if called from QML/JS, tell QML engine not to garbage collect this object if (seg) QQmlEngine::setObjectOwnership(seg, QQmlEngine::CppOwnership); #endif return seg; } //--------------------------------------------------------- // firstSegment Q_INVOKABLE wrapper //--------------------------------------------------------- Segment* Score::firstSegment(int segType) const { return firstSegment(static_cast(segType)); } //--------------------------------------------------------- // firstSegmentMM //--------------------------------------------------------- Segment* Score::firstSegmentMM(Segment::Type segType) const { Measure* m = firstMeasureMM(); return m ? m->first(segType) : 0; } //--------------------------------------------------------- // lastSegment //--------------------------------------------------------- Segment* Score::lastSegment() const { Measure* m = lastMeasure(); return m ? m->last() : 0; } //--------------------------------------------------------- // utick2utime //--------------------------------------------------------- qreal Score::utick2utime(int tick) const { return repeatList()->utick2utime(tick); } //--------------------------------------------------------- // utime2utick //--------------------------------------------------------- int Score::utime2utick(qreal utime) const { return repeatList()->utime2utick(utime); } //--------------------------------------------------------- // inputPos //--------------------------------------------------------- int Score::inputPos() const { return _is.tick(); } //--------------------------------------------------------- // scanElements // scan all elements //--------------------------------------------------------- void Score::scanElements(void* data, void (*func)(void*, Element*), bool all) { for (MeasureBase* mb = first(); mb; mb = mb->next()) { mb->scanElements(data, func, all); if (mb->type() == Element::Type::MEASURE) { Measure* m = toMeasure(mb); Measure* mmr = m->mmRest(); if (mmr) mmr->scanElements(data, func, all); } } for (Page* page : pages()) { for (System* s :page->systems()) s->scanElements(data, func, all); func(data, page); } } //--------------------------------------------------------- // scanElementsInRange //--------------------------------------------------------- void Score::scanElementsInRange(void* data, void (*func)(void*, Element*), bool all) { Segment* startSeg = _selection.startSegment(); for (Segment* s = startSeg; s && s !=_selection.endSegment(); s = s->next1()) { s->scanElements(data, func, all); Measure* m = s->measure(); if (m && s == m->first()) { Measure* mmr = m->mmRest(); if (mmr) mmr->scanElements(data, func, all); } } for (Element* e : _selection.elements()) { if (e->isSpanner()) { Spanner* spanner = static_cast(e); for (SpannerSegment* ss : spanner->spannerSegments()) { ss->scanElements(data, func, all); } } } } //--------------------------------------------------------- // setSelection //--------------------------------------------------------- void Score::setSelection(const Selection& s) { deselectAll(); _selection = s; foreach(Element* e, _selection.elements()) e->setSelected(true); } //--------------------------------------------------------- // getText //--------------------------------------------------------- Text* Score::getText(TextStyleType subtype) { MeasureBase* m = first(); if (m && m->type() == Element::Type::VBOX) { foreach(Element* e, m->el()) { if (e->type() == Element::Type::TEXT && toText(e)->textStyleType() == subtype) return toText(e); } } return 0; } //--------------------------------------------------------- // metaTag //--------------------------------------------------------- QString Score::metaTag(const QString& s) const { if (_metaTags.contains(s)) return _metaTags.value(s); return _masterScore->_metaTags.value(s); } //--------------------------------------------------------- // setMetaTag //--------------------------------------------------------- void Score::setMetaTag(const QString& tag, const QString& val) { _metaTags.insert(tag, val); } //--------------------------------------------------------- // addExcerpt //--------------------------------------------------------- void MasterScore::addExcerpt(Score* score) { Excerpt* ex = new Excerpt(this); ex->setPartScore(score); excerpts().append(ex); ex->setTitle(score->fileInfo()->completeBaseName()); for (Staff* s : score->staves()) { LinkedStaves* ls = s->linkedStaves(); if (ls == 0) continue; for (Staff* ps : ls->staves()) { if (ps->score() == this) { ex->parts().append(ps->part()); break; } } } setExcerptsChanged(true); } //--------------------------------------------------------- // removeExcerpt //--------------------------------------------------------- void MasterScore::removeExcerpt(Score* score) { for (Excerpt* ex : excerpts()) { if (ex->partScore() == score) { if (excerpts().removeOne(ex)) { setExcerptsChanged(true); delete ex; return; } else qDebug("removeExcerpt:: ex not found"); } } qDebug("Score::removeExcerpt: excerpt not found"); } //--------------------------------------------------------- // clone //--------------------------------------------------------- MasterScore* MasterScore::clone() { QBuffer buffer; buffer.open(QIODevice::WriteOnly); Xml xml(&buffer); xml.header(); xml.stag("museScore version=\"" MSC_VERSION "\""); write(xml, false); xml.etag(); buffer.close(); XmlReader r(buffer.buffer()); MasterScore* score = new MasterScore(style()); score->read1(r, true); score->addLayoutFlags(LayoutFlag::FIX_PITCH_VELO); score->doLayout(); score->scanElements(0, elementAdjustReadPos); //?? return score; } //--------------------------------------------------------- // setSynthesizerState //--------------------------------------------------------- void Score::setSynthesizerState(const SynthesizerState& s) { // TODO: make undoable _synthesizerState = s; } //--------------------------------------------------------- // removeAudio //--------------------------------------------------------- void Score::removeAudio() { delete _audio; _audio = 0; } //--------------------------------------------------------- // appendScore //--------------------------------------------------------- bool Score::appendScore(Score* score, bool addPageBreak, bool addSectionBreak) { if (parts().size() < score->parts().size() || staves().size() < score->staves().size()) { qDebug("Score to append has %d parts and %d staves, but this score only has %d parts and %d staves.", score->parts().size(), score->staves().size(), parts().size(), staves().size()); return false; } if (!last()) { qDebug("This score doesn't have any MeasureBase objects."); return false; } TieMap tieMap; int tickOfAppend = last()->endTick(); // apply Page/Section Breaks if desired if (addPageBreak) { if (!last()->pageBreak()) { last()->undoSetBreak(false, LayoutBreak::Type::LINE); // remove line break if exists last()->undoSetBreak(true, LayoutBreak::Type::PAGE); // apply page break } } else if (!last()->lineBreak() && !last()->pageBreak()) last()->undoSetBreak(true, LayoutBreak::Type::LINE); if (addSectionBreak && !last()->sectionBreak()) last()->undoSetBreak(true, LayoutBreak::Type::SECTION); // match concert pitch states if (styleB(StyleIdx::concertPitch) != score->styleB(StyleIdx::concertPitch)) score->cmdConcertPitchChanged(styleB(StyleIdx::concertPitch), true); // convert any "generated" initial clefs into real "non-generated" clefs if clef type changes if (score->firstMeasure()) { Segment* initialClefSegment = score->firstMeasure()->findSegment(Segment::Type::Clef, 0); // find clefs at first tick of first measure if (initialClefSegment) { for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) { int track = staffIdx * VOICES; Staff* staff = score->staff(staffIdx); Clef* initialClef = toClef(initialClefSegment->element(track)); // if the first clef of score to append is generated and // if the first clef of score to append is of different type than clef at final tick of first score if (initialClef && initialClef->generated() && initialClef->clefType() != this->staff(staffIdx)->clef(tickOfAppend)) { // then convert that generated clef into a real non-generated clef so that its different type will be copied to joined score score->undoChangeClef(staff, initialClefSegment, initialClef->clefType()); } } } } // clone the measures MeasureBaseList* ml = &score->_measures; for (MeasureBase* mb = ml->first(); mb; mb = mb->next()) { MeasureBase* nmb; if (mb->type() == Element::Type::MEASURE) nmb = toMeasure(mb)->cloneMeasure(this, &tieMap); else nmb = mb->clone(); nmb->setNext(0); nmb->setPrev(0); nmb->setScore(this); _measures.add(nmb); } fixTicks(); Measure* firstAppendedMeasure = tick2measure(tickOfAppend); // if the appended score has less staves, // make sure the measures have full measure rest for (Measure* m = firstAppendedMeasure; m; m = m->nextMeasure()) { for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) { Fraction f; for (Segment* s = m->first(Segment::Type::ChordRest); s; s = s->next(Segment::Type::ChordRest)) { for (int v = 0; v < VOICES; ++v) { ChordRest* cr = toChordRest(s->element(staffIdx * VOICES + v)); if (cr == 0) continue; f += cr->actualFraction(); } } if (f.isZero()) addRest(m->tick(), staffIdx*VOICES, TDuration(TDuration::DurationType::V_MEASURE), 0); } } // adjust key signatures if (firstAppendedMeasure) { Segment* seg = firstAppendedMeasure->getSegment(Segment::Type::KeySig, tickOfAppend); for (Staff* st : score->staves()) { int staffIdx = score->staffIdx(st); Staff* joinedStaff = staff(staffIdx); // special case for initial "C" key signature - these have no explicit element if (!seg->element(staffIdx * VOICES)) { // no need to create new initial "C" key sig // if staff already ends in that key if (joinedStaff->key(tickOfAppend - 1) == Key::C) continue; Key key = Key::C; KeySig* ks = new KeySig(this); ks->setTrack(staffIdx * VOICES); ks->setKey(key); ks->setParent(seg); addElement(ks); } // other key signatures (initial other than "C", non-initial) for (auto k : *(st->keyList())) { int tick = k.first; KeySigEvent key = k.second; joinedStaff->setKey(tick + tickOfAppend, key); } } } // clone the spanners for (auto sp : score->spanner()) { Spanner* spanner = sp.second; Spanner* ns = static_cast(spanner->clone()); ns->setScore(this); ns->setParent(0); ns->setTick(spanner->tick() + tickOfAppend); ns->setTick2(spanner->tick2() + tickOfAppend); if (ns->type() == Element::Type::SLUR) { // set start/end element for slur ns->setStartElement(0); ns->setEndElement(0); Measure* sm = tick2measure(ns->tick()); if (sm) ns->setStartElement(sm->findChordRest(ns->tick(), ns->track())); Measure * em = tick2measure(ns->tick2()); if (em) ns->setEndElement(em->findChordRest(ns->tick2(), ns->track2())); if (!ns->startElement()) qDebug("clone Slur: no start element"); if (!ns->endElement()) qDebug("clone Slur: no end element"); } addElement(ns); } setLayoutAll(); return true; } //--------------------------------------------------------- // splitStaff //--------------------------------------------------------- void Score::splitStaff(int staffIdx, int splitPoint) { // qDebug("split staff %d point %d", staffIdx, splitPoint); // // create second staff // Staff* s = staff(staffIdx); Part* p = s->part(); Staff* ns = new Staff(this); ns->setPart(p); // convert staffIdx from score-relative to part-relative int staffIdxPart = staffIdx - p->staff(0)->idx(); undoInsertStaff(ns, staffIdxPart + 1, false); Clef* clef = new Clef(this); clef->setClefType(ClefType::F); clef->setTrack((staffIdx+1) * VOICES); Segment* seg = firstMeasure()->getSegment(Segment::Type::Clef, 0); clef->setParent(seg); undoAddElement(clef); clef->layout(); undoChangeKeySig(ns, 0, s->keySigEvent(0)); masterScore()->rebuildMidiMapping(); cmdState()._instrumentsChanged = true; doLayout(); // // move notes // select(0, SelectType::SINGLE, 0); int strack = staffIdx * VOICES; int dtrack = (staffIdx + 1) * VOICES; for (Segment* s = firstSegment(Segment::Type::ChordRest); s; s = s->next1(Segment::Type::ChordRest)) { for (int voice = 0; voice < VOICES; ++voice) { Element* e = s->element(strack + voice); if (!(e && e->isChord())) continue; Chord* c = toChord(e); QList removeNotes; foreach(Note* note, c->notes()) { if (note->pitch() >= splitPoint) continue; Chord* chord = toChord(s->element(dtrack + voice)); Q_ASSERT(!chord || (chord->type() == Element::Type::CHORD)); if (chord == 0) { chord = new Chord(*c); qDeleteAll(chord->notes()); chord->notes().clear(); chord->setTrack(dtrack + voice); undoAddElement(chord); } Note* nnote = new Note(*note); nnote->setTrack(dtrack + voice); chord->add(nnote); nnote->updateLine(); removeNotes.append(note); } c->sortNotes(); for (Note* note : removeNotes) { undoRemoveElement(note); Chord* chord = note->chord(); if (chord->notes().empty()) { for (auto sp : spanner()) { Slur* slur = static_cast(sp.second); if (slur->type() != Element::Type::SLUR) continue; if (slur->startCR() == chord) { slur->undoChangeProperty(P_ID::TRACK, slur->track()+VOICES); for (ScoreElement* ee : slur->linkList()) { Slur* lslur = static_cast(ee); lslur->setStartElement(0); } } if (slur->endCR() == chord) { slur->undoChangeProperty(P_ID::SPANNER_TRACK2, slur->track2()+VOICES); for (ScoreElement* ee : slur->linkList()) { Slur* lslur = static_cast(ee); lslur->setEndElement(0); } } } undoRemoveElement(chord); } } } } // // make sure that the timeline for dtrack // has no gaps // int ctick = 0; for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) { for (Segment* s = m->first(Segment::Type::ChordRest); s; s = s->next1(Segment::Type::ChordRest)) { ChordRest* cr = toChordRest(s->element(dtrack)); if (cr == 0) continue; int rest = s->tick() - ctick; if (rest) { // insert Rest Segment* s = tick2segment(ctick); if (s == 0) { qDebug("no segment at %d", ctick); continue; } setRest(ctick, dtrack, Fraction::fromTicks(rest), false, 0); } ctick = s->tick() + cr->actualTicks(); } int rest = m->tick() + m->ticks() - ctick; if (rest) { setRest(ctick, dtrack, Fraction::fromTicks(rest), false, 0); ctick += rest; } } // // same for strack // ctick = 0; for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) { for (Segment* s = m->first(Segment::Type::ChordRest); s; s = s->next1(Segment::Type::ChordRest)) { ChordRest* cr = toChordRest(s->element(strack)); if (cr == 0) continue; int rest = s->tick() - ctick; if (rest) { // insert Rest Segment* s = tick2segment(ctick); if (s == 0) { qDebug("no segment at %d", ctick); continue; } setRest(ctick, strack, Fraction::fromTicks(rest), false, 0); } ctick = s->tick() + cr->actualTicks(); } int rest = m->tick() + m->ticks() - ctick; if (rest) { setRest(ctick, strack, Fraction::fromTicks(rest), false, 0); ctick += rest; } } } //--------------------------------------------------------- // cmdRemovePart //--------------------------------------------------------- void Score::cmdRemovePart(Part* part) { int sidx = staffIdx(part); int n = part->nstaves(); for (int i = 0; i < n; ++i) cmdRemoveStaff(sidx); undoRemovePart(part, sidx); } //--------------------------------------------------------- // insertPart //--------------------------------------------------------- void Score::insertPart(Part* part, int idx) { int staff = 0; for (QList::iterator i = _parts.begin(); i != _parts.end(); ++i) { if (staff >= idx) { _parts.insert(i, part); return; } staff += (*i)->nstaves(); } _parts.push_back(part); } //--------------------------------------------------------- // removePart //--------------------------------------------------------- void Score::removePart(Part* part) { _parts.removeAt(_parts.indexOf(part)); } //--------------------------------------------------------- // insertStaff //--------------------------------------------------------- void Score::insertStaff(Staff* staff, int ridx) { staff->part()->insertStaff(staff, ridx); int idx = staffIdx(staff->part()) + ridx; _staves.insert(idx, staff); for (auto i = staff->score()->spanner().cbegin(); i != staff->score()->spanner().cend(); ++i) { Spanner* s = i->second; if (s->systemFlag()) continue; if (s->staffIdx() >= idx) { int t = s->track() + VOICES; s->setTrack(t < ntracks() ? t : ntracks() - 1); if (s->track2() != -1) { t = s->track2() + VOICES; s->setTrack2(t < ntracks() ? t : s->track()); } } } } //--------------------------------------------------------- // removeStaff //--------------------------------------------------------- void Score::removeStaff(Staff* staff) { int idx = staffIdx(staff); for (auto i = staff->score()->spanner().cbegin(); i != staff->score()->spanner().cend(); ++i) { Spanner* s = i->second; if (s->staffIdx() > idx) { int t = s->track() - VOICES; s->setTrack(t >=0 ? t : 0); if (s->track2() != -1) { t = s->track2() - VOICES; s->setTrack2(t >=0 ? t : s->track()); } } } _staves.removeAll(staff); staff->part()->removeStaff(staff); } //--------------------------------------------------------- // adjustBracketsDel //--------------------------------------------------------- void Score::adjustBracketsDel(int sidx, int eidx) { for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) { Staff* staff = _staves[staffIdx]; for (int i = 0; i < staff->bracketLevels(); ++i) { int span = staff->bracketSpan(i); if ((span == 0) || ((staffIdx + span) < sidx) || (staffIdx > eidx)) continue; if ((sidx >= staffIdx) && (eidx <= (staffIdx + span))) undoChangeBracketSpan(staff, i, span - (eidx-sidx)); } #if 0 // TODO int span = staff->barLineSpan(); if ((sidx >= staffIdx) && (eidx <= (staffIdx + span))) { int newSpan = span - (eidx-sidx) + 1; int lastSpannedStaffIdx = staffIdx + newSpan - 1; undoChangeBarLineSpan(staff, newSpan, 0, (_staves[lastSpannedStaffIdx]->lines()-1)*2); } #endif } } //--------------------------------------------------------- // adjustBracketsIns //--------------------------------------------------------- void Score::adjustBracketsIns(int sidx, int eidx) { for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) { Staff* staff = _staves[staffIdx]; int bl = staff->bracketLevels(); for (int i = 0; i < bl; ++i) { int span = staff->bracketSpan(i); if ((span == 0) || ((staffIdx + span) < sidx) || (staffIdx > eidx)) continue; if ((sidx >= staffIdx) && (eidx < (staffIdx + span))) undoChangeBracketSpan(staff, i, span + (eidx-sidx)); } #if 0 // TODO int span = staff->barLineSpan(); if ((sidx >= staffIdx) && (eidx < (staffIdx + span))) { int idx = staffIdx + span - 1; if (idx >= _staves.size()) idx = _staves.size() - 1; undoChangeBarLineSpan(staff, span, 0, (_staves[idx]->lines()-1)*2); } #endif } } //--------------------------------------------------------- // adjustKeySigs //--------------------------------------------------------- void Score::adjustKeySigs(int sidx, int eidx, KeyList km) { for (int staffIdx = sidx; staffIdx < eidx; ++staffIdx) { Staff* staff = _staves[staffIdx]; if (staff->isDrumStaff()) continue; for (auto i = km.begin(); i != km.end(); ++i) { int tick = i->first; Measure* measure = tick2measure(tick); if (!measure) continue; KeySigEvent oKey = i->second; KeySigEvent nKey = oKey; int diff = -staff->part()->instrument(tick)->transpose().chromatic; if (diff != 0 && !styleB(StyleIdx::concertPitch) && !oKey.custom() && !oKey.isAtonal()) nKey.setKey(transposeKey(nKey.key(), diff)); staff->setKey(tick, nKey); KeySig* keysig = new KeySig(this); keysig->setTrack(staffIdx * VOICES); keysig->setKeySigEvent(nKey); Segment* s = measure->getSegment(keysig, tick); s->add(keysig); } } } //--------------------------------------------------------- // cmdRemoveStaff //--------------------------------------------------------- void Score::cmdRemoveStaff(int staffIdx) { Staff* s = staff(staffIdx); adjustBracketsDel(staffIdx, staffIdx+1); QList sl; for (auto i = _spanner.cbegin(); i != _spanner.cend(); ++i) { Spanner* s = i->second; if (s->staffIdx() == staffIdx && (staffIdx != 0 || !s->systemFlag())) sl.append(s); } for (auto i : sl) { i->undoUnlink(); undo(new RemoveElement(i)); } undoRemoveStaff(s); // remove linked staff and measures in linked staves in excerpts // unlink staff in the same score if (s->linkedStaves()) { Staff* sameScoreLinkedStaff = nullptr; auto staves = s->linkedStaves()->staves(); for (Staff* staff : staves) { if (staff == s) continue; Score* lscore = staff->score(); if (lscore != this) { lscore->undoRemoveStaff(staff); s->score()->undo(new UnlinkStaff(s, staff)); if (staff->part()->nstaves() == 0) { int pIndex = lscore->staffIdx(staff->part()); lscore->undoRemovePart(staff->part(), pIndex); } } else // linked staff in the same score sameScoreLinkedStaff = staff; } if (sameScoreLinkedStaff) s->score()->undo(new UnlinkStaff(sameScoreLinkedStaff, s)); // once should be enough } } //--------------------------------------------------------- // sortStaves //--------------------------------------------------------- void Score::sortStaves(QList& dst) { systems().clear(); //?? _parts.clear(); Part* curPart = 0; QList dl; foreach (int idx, dst) { Staff* staff = _staves[idx]; if (staff->part() != curPart) { curPart = staff->part(); curPart->staves()->clear(); _parts.push_back(curPart); } curPart->staves()->push_back(staff); dl.push_back(staff); } _staves = dl; for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) { m->sortStaves(dst); if (m->hasMMRest()) m->mmRest()->sortStaves(dst); } for (auto i : _spanner.map()) { Spanner* sp = i.second; if (sp->systemFlag()) continue; int voice = sp->voice(); int staffIdx = sp->staffIdx(); int idx = dst.indexOf(staffIdx); if (idx >=0) { sp->setTrack(idx * VOICES + voice); if (sp->track2() != -1) sp->setTrack2(idx * VOICES +(sp->track2() % VOICES)); // at least keep the voice... } } } //--------------------------------------------------------- // cmdConcertPitchChanged //--------------------------------------------------------- void Score::cmdConcertPitchChanged(bool flag, bool /*useDoubleSharpsFlats*/) { undo(new ChangeStyleVal(this, StyleIdx::concertPitch, flag)); // change style flag for (Staff* staff : _staves) { if (staff->staffType()->group() == StaffGroup::PERCUSSION) continue; // if this staff has no transposition, and no instrument changes, we can skip it Interval interval = staff->part()->instrument()->transpose(); if (interval.isZero() && staff->part()->instruments()->size() == 1) continue; if (!flag) interval.flip(); int staffIdx = staff->idx(); int startTrack = staffIdx * VOICES; int endTrack = startTrack + VOICES; transposeKeys(staffIdx, staffIdx + 1, 0, lastSegment()->tick(), interval, true, !flag); for (Segment* segment = firstSegment(Segment::Type::ChordRest); segment; segment = segment->next1(Segment::Type::ChordRest)) { interval = staff->part()->instrument(segment->tick())->transpose(); if (!flag) interval.flip(); for (Element* e : segment->annotations()) { if (!e->isHarmony() || (e->track() < startTrack) || (e->track() >= endTrack)) continue; Harmony* h = toHarmony(e); int rootTpc = transposeTpc(h->rootTpc(), interval, true); int baseTpc = transposeTpc(h->baseTpc(), interval, true); for (ScoreElement* e : h->linkList()) { // don't transpose all links // just ones resulting from mmrests Harmony* he = static_cast(e); if (he->staff() == h->staff()) undoTransposeHarmony(he, rootTpc, baseTpc); } } } } } //--------------------------------------------------------- // addAudioTrack //--------------------------------------------------------- void Score::addAudioTrack() { // TODO } //--------------------------------------------------------- // padToggle //--------------------------------------------------------- void Score::padToggle(Pad n) { int oldDots = _is.duration().dots(); switch (n) { case Pad::NOTE00: _is.setDuration(TDuration::DurationType::V_LONG); break; case Pad::NOTE0: _is.setDuration(TDuration::DurationType::V_BREVE); break; case Pad::NOTE1: _is.setDuration(TDuration::DurationType::V_WHOLE); break; case Pad::NOTE2: _is.setDuration(TDuration::DurationType::V_HALF); break; case Pad::NOTE4: _is.setDuration(TDuration::DurationType::V_QUARTER); break; case Pad::NOTE8: _is.setDuration(TDuration::DurationType::V_EIGHTH); break; case Pad::NOTE16: _is.setDuration(TDuration::DurationType::V_16TH); break; case Pad::NOTE32: _is.setDuration(TDuration::DurationType::V_32ND); break; case Pad::NOTE64: _is.setDuration(TDuration::DurationType::V_64TH); break; case Pad::NOTE128: _is.setDuration(TDuration::DurationType::V_128TH); break; case Pad::REST: _is.setRest(!_is.rest()); break; case Pad::DOT: if ((_is.duration().dots() == 1) || (_is.duration() == TDuration::DurationType::V_128TH)) _is.setDots(0); else _is.setDots(1); break; case Pad::DOTDOT: if ((_is.duration().dots() == 2) || (_is.duration() == TDuration::DurationType::V_64TH) || (_is.duration() == TDuration::DurationType::V_128TH)) _is.setDots(0); else _is.setDots(2); break; case Pad::DOT3: if ((_is.duration().dots() == 3) || (_is.duration() == TDuration::DurationType::V_32ND) || (_is.duration() == TDuration::DurationType::V_64TH) || (_is.duration() == TDuration::DurationType::V_128TH)) _is.setDots(0); else _is.setDots(3); break; case Pad::DOT4: if ((_is.duration().dots() == 4) || (_is.duration() == TDuration::DurationType::V_16TH) || (_is.duration() == TDuration::DurationType::V_32ND) || (_is.duration() == TDuration::DurationType::V_64TH) || (_is.duration() == TDuration::DurationType::V_128TH)) _is.setDots(0); else _is.setDots(4); break; } if (n >= Pad::NOTE00 && n <= Pad::NOTE128) { _is.setDots(0); // // if in "note enter" mode, reset // rest flag // if (noteEntryMode()) { if (usingNoteEntryMethod(NoteEntryMethod::RHYTHM)) { switch (oldDots) { case 1: padToggle(Pad::DOT); break; case 2: padToggle(Pad::DOTDOT); break; } NoteVal nval; if (_is.rest()) { // Enter a rest nval = NoteVal(); } else { // Enter a note on the middle staff line Staff* s = staff(_is.track() / VOICES); int tick = _is.tick(); ClefType clef = s->clef(tick); Key key = s->key(tick); nval = NoteVal(line2pitch(4, clef, key)); } setNoteRest(_is.segment(), _is.track(), nval, _is.duration().fraction()); _is.moveToNextInputPos(); } else _is.setRest(false); } } if (noteEntryMode() || !selection().isSingle()) return; //do not allow to add a dot on a full measure rest Element* e = selection().element(); if (e && e->isRest()) { Rest* r = toRest(e); if (r->isFullMeasureRest()) _is.setDots(0); } // on measure rest, select the first actual rest ChordRest* cr = selection().cr(); if (cr && cr->isRest() && cr->measure()->isMMRest()) { Measure* m = cr->measure()->mmRestFirst(); if (m) cr = m->findChordRest(0, 0); } if (!cr) return; if (cr->isChord() && (toChord(cr)->isGrace())) { // // handle appoggiatura and acciaccatura // undoChangeChordRestLen(cr, _is.duration()); } else changeCRlen(cr, _is.duration()); } //--------------------------------------------------------- // deselect //--------------------------------------------------------- void Score::deselect(Element* el) { addRefresh(el->abbox()); _selection.remove(el); setSelectionChanged(true); } //--------------------------------------------------------- // select // staffIdx is valid, if element is of type MEASURE //--------------------------------------------------------- void Score::select(Element* e, SelectType type, int staffIdx) { if (e && (e->type() == Element::Type::NOTE || e->type() == Element::Type::REST)) { Element* ee = e; if (ee->type() == Element::Type::NOTE) ee = ee->parent(); int tick = toChordRest(ee)->segment()->tick(); if (playPos() != tick) setPlayPos(tick); } if (MScore::debugMode) qDebug("select element <%s> type %d(state %d) staff %d", e ? e->name() : "", int(type), int(selection().state()), e ? e->staffIdx() : -1); switch (type) { case SelectType::SINGLE: return selectSingle(e, staffIdx); case SelectType::ADD: return selectAdd(e); case SelectType::RANGE: return selectRange(e, staffIdx); } } //--------------------------------------------------------- // selectSingle // staffIdx is valid, if element is of type MEASURE //--------------------------------------------------------- void Score::selectSingle(Element* e, int staffIdx) { SelState selState = _selection.state(); deselectAll(); if (e == 0) { selState = SelState::NONE; setUpdateAll(); } else { if (e->type() == Element::Type::MEASURE) { select(e, SelectType::RANGE, staffIdx); return; } addRefresh(e->abbox()); _selection.add(e); _is.setTrack(e->track()); selState = SelState::LIST; if (e->type() == Element::Type::NOTE) { e = e->parent(); } if (e->type() == Element::Type::REST || e->type() == Element::Type::CHORD) { _is.setLastSegment(_is.segment()); _is.setSegment(toChordRest(e)->segment()); } } _selection.setActiveSegment(0); _selection.setActiveTrack(0); _selection.setState(selState); } //--------------------------------------------------------- // selectAdd //--------------------------------------------------------- void Score::selectAdd(Element* e) { SelState selState = _selection.state(); if (_selection.isRange()) { select(0, SelectType::SINGLE, 0); return; } if (e->type() == Element::Type::MEASURE) { Measure* m = toMeasure(e); int tick = m->tick(); if (_selection.isNone()) { _selection.setRange(m->tick2segment(tick), m == lastMeasure() ? 0 : m->last(), 0, nstaves()); } else { select(0, SelectType::SINGLE, 0); return; } setUpdateAll(); selState = SelState::RANGE; _selection.updateSelectedElements(); } else { // None or List addRefresh(e->abbox()); if (_selection.elements().contains(e)) _selection.remove(e); else { _selection.add(e); selState = SelState::LIST; } } _selection.setState(selState); } //--------------------------------------------------------- // selectRange // staffIdx is valid, if element is of type MEASURE //--------------------------------------------------------- void Score::selectRange(Element* e, int staffIdx) { int activeTrack = e->track(); // current selection is range extending to end of score? bool endRangeSelected = selection().isRange() && selection().endSegment() == nullptr; if (e->type() == Element::Type::MEASURE) { Measure* m = toMeasure(e); int tick = m->tick(); int etick = tick + m->ticks(); activeTrack = staffIdx * VOICES; if (_selection.isNone() || (_selection.isList() && !_selection.isSingle())) { if (_selection.isList()) deselectAll(); _selection.setRange(m->tick2segment(tick), m == lastMeasure() ? 0 : m->last(), staffIdx, staffIdx + 1); } else if (_selection.isRange()) { _selection.extendRangeSelection(m->tick2segment(tick), m == lastMeasure() ? 0 : m->last(), staffIdx, tick, etick); } else if (_selection.isSingle()) { Element* oe = selection().element(); if (oe->type() == Element::Element::Type::NOTE || oe->isChordRest()) { if (oe->type() == Element::Element::Type::NOTE) oe = oe->parent(); ChordRest* cr = toChordRest(oe); int oetick = cr->segment()->tick(); Segment* startSegment = cr->segment(); Segment* endSegment = m->last(); if (tick < oetick) { startSegment = m->tick2segment(tick); if (etick <= oetick) endSegment = cr->nextSegmentAfterCR(Segment::Type::ChordRest | Segment::Type::EndBarLine | Segment::Type::Clef); } int staffStart = staffIdx; int endStaff = staffIdx + 1; if (staffStart > cr->staffIdx()) staffStart = cr->staffIdx(); else if (cr->staffIdx() >= endStaff) endStaff = cr->staffIdx() + 1; _selection.setRange(startSegment, endSegment, staffStart, endStaff); } else { deselectAll(); _selection.setRange(m->tick2segment(tick), m == lastMeasure() ? 0 : m->last(), staffIdx, staffIdx + 1); } } else { qDebug("SELECT_RANGE: measure: sel state %d", int(_selection.state())); return; } } else if (e->type() == Element::Type::NOTE || e->isChordRest()) { if (e->type() == Element::Type::NOTE) e = e->parent(); ChordRest* cr = toChordRest(e); if (_selection.isNone() || (_selection.isList() && !_selection.isSingle())) { if (_selection.isList()) deselectAll(); _selection.setRange(cr->segment(), cr->nextSegmentAfterCR(Segment::Type::ChordRest | Segment::Type::EndBarLine | Segment::Type::Clef), e->staffIdx(), e->staffIdx() + 1); activeTrack = cr->track(); } else if (_selection.isSingle()) { Element* oe = _selection.element(); if (oe && (oe->type() == Element::Type::NOTE || oe->type() == Element::Type::REST)) { if (oe->type() == Element::Type::NOTE) oe = oe->parent(); ChordRest* ocr = toChordRest(oe); Segment* endSeg = tick2segmentMM(ocr->segment()->tick() + ocr->actualTicks()); if (!endSeg) endSeg = ocr->segment()->next(); _selection.setRange(ocr->segment(), endSeg, oe->staffIdx(), oe->staffIdx() + 1); _selection.extendRangeSelection(cr); } else { select(e, SelectType::SINGLE, 0); return; } } else if (_selection.isRange()) { _selection.extendRangeSelection(cr); } else { qDebug("sel state %d", int(_selection.state())); return; } if (!endRangeSelected && !_selection.endSegment()) _selection.setEndSegment(cr->segment()->nextCR()); if (!_selection.startSegment()) _selection.setStartSegment(cr->segment()); } else { select(e, SelectType::SINGLE, staffIdx); return; } _selection.setActiveTrack(activeTrack); // doing this in note entry mode can clear selection if (_selection.startSegment() && !noteEntryMode()) setPlayPos(_selection.startSegment()->tick()); _selection.updateSelectedElements(); } //--------------------------------------------------------- // collectMatch //--------------------------------------------------------- void Score::collectMatch(void* data, Element* e) { ElementPattern* p = static_cast(data); if (p->type != int(e->type())) return; if (p->subtypeValid) { // HACK: grace note is different from normal note // TODO: this disables the ability to distinguish noteheads in subtype if (p->type == int(Element::Type::NOTE)) { if (p->subtype != toNote(e)->chord()->isGrace()) return; } else { if (p->subtype != e->subtype()) return; } } if ((p->staffStart != -1) && ((p->staffStart > e->staffIdx()) || (p->staffEnd <= e->staffIdx()))) return; if (e->isChord() || e->isRest() || e->isNote() || e->isLyrics() || e->isBeam() || e->isStem() || e->isSlurSegment()) { if (p->voice != -1 && p->voice != e->voice()) return; } if (p->system) { Element* ee = e; do { if (ee->type() == Element::Type::SYSTEM) { if (p->system != ee) return; break; } ee = ee->parent(); } while (ee); } p->el.append(e); } //--------------------------------------------------------- // selectSimilar //--------------------------------------------------------- void Score::selectSimilar(Element* e, bool sameStaff) { Element::Type type = e->type(); Score* score = e->score(); ElementPattern pattern; pattern.type = int(type); if (type == Element::Type::NOTE) { pattern.subtype = toNote(e)->chord()->isGrace(); pattern.subtypeValid = true; } else if (type == Element::Type::SLUR_SEGMENT) { pattern.subtype = static_cast(toSlurSegment(e)->spanner()->type()); pattern.subtypeValid = true; } else { pattern.subtype = 0; pattern.subtypeValid = false; } pattern.staffStart = sameStaff ? e->staffIdx() : -1; pattern.staffEnd = sameStaff ? e->staffIdx()+1 : -1; pattern.voice = -1; pattern.system = 0; score->scanElements(&pattern, collectMatch); score->select(0, SelectType::SINGLE, 0); for (Element* e : pattern.el) score->select(e, SelectType::ADD, 0); } //--------------------------------------------------------- // selectSimilarInRange //--------------------------------------------------------- void Score::selectSimilarInRange(Element* e) { Element::Type type = e->type(); ElementPattern pattern; Score* score = e->score(); pattern.type = int(type); pattern.subtype = 0; pattern.staffStart = selection().staffStart(); pattern.staffEnd = selection().staffEnd(); pattern.voice = -1; pattern.system = 0; pattern.subtypeValid = false; score->scanElementsInRange(&pattern, collectMatch); score->select(0, SelectType::SINGLE, 0); for (Element* e : pattern.el) { score->select(e, SelectType::ADD, 0); } } //--------------------------------------------------------- // lassoSelect //--------------------------------------------------------- void Score::lassoSelect(const QRectF& bbox) { select(0, SelectType::SINGLE, 0); QRectF fr(bbox.normalized()); foreach(Page* page, _pages) { QRectF pr(page->bbox()); QRectF frr(fr.translated(-page->pos())); if (pr.right() < frr.left()) continue; if (pr.left() > frr.right()) break; QList el = page->items(frr); for (int i = 0; i < el.size(); ++i) { Element* e = el.at(i); if (frr.contains(e->abbox())) { if (e->type() != Element::Type::MEASURE && e->selectable()) select(e, SelectType::ADD, 0); } } } } //--------------------------------------------------------- // lassoSelectEnd //--------------------------------------------------------- void Score::lassoSelectEnd() { int noteRestCount = 0; Segment* startSegment = 0; Segment* endSegment = 0; int startStaff = 0x7fffffff; int endStaff = 0; const ChordRest* endCR = 0; if (_selection.elements().empty()) { _selection.setState(SelState::NONE); setUpdateAll(); return; } _selection.setState(SelState::LIST); foreach(const Element* e, _selection.elements()) { if (e->type() != Element::Type::NOTE && e->type() != Element::Type::REST) continue; ++noteRestCount; if (e->type() == Element::Type::NOTE) e = e->parent(); Segment* seg = static_cast(e)->segment(); if ((startSegment == 0) || (*seg < *startSegment)) startSegment = seg; if ((endSegment == 0) || (*seg > *endSegment)) { endSegment = seg; endCR = static_cast(e); } int idx = e->staffIdx(); if (idx < startStaff) startStaff = idx; if (idx > endStaff) endStaff = idx; } if (noteRestCount > 0) { endSegment = endCR->nextSegmentAfterCR(Segment::Type::ChordRest | Segment::Type::EndBarLine | Segment::Type::Clef); _selection.setRange(startSegment, endSegment, startStaff, endStaff+1); if (!_selection.isRange()) _selection.setState(SelState::RANGE); _selection.updateSelectedElements(); } setUpdateAll(); } //--------------------------------------------------------- // addLyrics //--------------------------------------------------------- void Score::addLyrics(int tick, int staffIdx, const QString& txt) { if (txt.trimmed().isEmpty()) return; Measure* measure = tick2measure(tick); Segment* seg = measure->findSegment(Segment::Type::ChordRest, tick); if (seg == 0) { qDebug("no segment found for lyrics<%s> at tick %d", qPrintable(txt), tick); return; } bool lyricsAdded = false; for (int voice = 0; voice < VOICES; ++voice) { int track = staffIdx * VOICES + voice; ChordRest* cr = toChordRest(seg->element(track)); if (cr) { Lyrics* l = new Lyrics(this); l->setXmlText(txt); l->setTrack(track); cr->add(l); lyricsAdded = true; break; } } if (!lyricsAdded) { qDebug("no chord/rest for lyrics<%s> at tick %d, staff %d", qPrintable(txt), tick, staffIdx); } } //--------------------------------------------------------- // setTempo // convenience function to access TempoMap //--------------------------------------------------------- void Score::setTempo(Segment* segment, qreal tempo) { setTempo(segment->tick(), tempo); } void Score::setTempo(int tick, qreal tempo) { tempomap()->setTempo(tick, tempo); _playlistDirty = true; } //--------------------------------------------------------- // removeTempo //--------------------------------------------------------- void Score::removeTempo(int tick) { tempomap()->delTempo(tick); _playlistDirty = true; } //--------------------------------------------------------- // setPause //--------------------------------------------------------- void Score::setPause(int tick, qreal seconds) { tempomap()->setPause(tick, seconds); _playlistDirty = true; } //--------------------------------------------------------- // tempo //--------------------------------------------------------- qreal Score::tempo(int tick) const { return tempomap()->tempo(tick); } //--------------------------------------------------------- // loWidth //--------------------------------------------------------- qreal Score::loWidth() const { return pageFormat()->size().width() * DPI; } //--------------------------------------------------------- // loHeight //--------------------------------------------------------- qreal Score::loHeight() const { return pageFormat()->size().height() * DPI; } //--------------------------------------------------------- // cmdSelectAll //--------------------------------------------------------- void Score::cmdSelectAll() { if (_measures.size() == 0) return; deselectAll(); Measure* first = firstMeasureMM(); if (!first) return; Measure* last = lastMeasureMM(); selectRange(first, 0); selectRange(last, nstaves() - 1); setUpdateAll(); update(); } //--------------------------------------------------------- // cmdSelectSection //--------------------------------------------------------- void Score::cmdSelectSection() { Segment* s = _selection.startSegment(); if (s == 0) return; MeasureBase* sm = s->measure(); MeasureBase* em = sm; while (sm->prev()) { if (sm->prev()->sectionBreak()) break; sm = sm->prev(); } while (em->next()) { if (em->sectionBreak()) break; em = em->next(); } while (sm && sm->type() != Element::Type::MEASURE) sm = sm->next(); while (em && em->type() != Element::Type::MEASURE) em = em->next(); if (sm == 0 || em == 0) return; _selection.setRange(toMeasure(sm)->first(), toMeasure(em)->last(), 0, nstaves()); } //--------------------------------------------------------- // undo //--------------------------------------------------------- void Score::undo(UndoCommand* cmd) const { undoStack()->push(cmd); } //--------------------------------------------------------- // linkId //--------------------------------------------------------- int Score::linkId() { return (masterScore()->_linkId)++; } // val is a used link id void Score::linkId(int val) { Score* s = masterScore(); if (val >= s->_linkId) s->_linkId = val + 1; // update unused link id } //--------------------------------------------------------- // scoreList // return a list of scores containing the root score // and all part scores (if there are any) //--------------------------------------------------------- QList Score::scoreList() { QList scores; Score* root = masterScore(); scores.append(root); for (const Excerpt* ex : root->excerpts()) { if (ex->partScore()) scores.append(ex->partScore()); } return scores; } //--------------------------------------------------------- // switchLayer //--------------------------------------------------------- bool Score::switchLayer(const QString& s) { int layerIdx = 0; for (const Layer& l : layer()) { if (s == l.name) { setCurrentLayer(layerIdx); return true; } ++layerIdx; } return false; } //--------------------------------------------------------- // appendPart //--------------------------------------------------------- void Score::appendPart(const QString& name) { static InstrumentTemplate defaultInstrument; InstrumentTemplate* t; t = searchTemplate(name); if (t == 0) { qDebug("appendPart: <%s> not found", qPrintable(name)); t = &defaultInstrument; } if (t->channel.empty()) { Channel a; a.chorus = 0; a.reverb = 0; a.name = "normal"; a.bank = 0; a.volume = 100; a.pan = 64; // actually 63.5 for center t->channel.append(a); } Part* part = new Part(this); part->initFromInstrTemplate(t); int n = nstaves(); for (int i = 0; i < t->nstaves(); ++i) { Staff* staff = new Staff(this); staff->setPart(part); staff->setLines(t->staffLines[i]); staff->setSmall(t->smallStaff[i]); if (i == 0) { staff->setBracket(0, t->bracket[0]); staff->setBracketSpan(0, t->nstaves()); } undoInsertStaff(staff, i); } part->staves()->front()->setBarLineSpan(part->nstaves()); undoInsertPart(part, n); fixTicks(); masterScore()->rebuildMidiMapping(); } //--------------------------------------------------------- // appendMeasures //--------------------------------------------------------- void Score::appendMeasures(int n) { for (int i = 0; i < n; ++i) insertMeasure(Element::Type::MEASURE, 0, false); } #ifdef SCRIPT_INTERFACE //--------------------------------------------------------- // addText //--------------------------------------------------------- void Score::addText(const QString& type, const QString& txt) { MeasureBase* measure = first(); if (measure == 0 || measure->type() != Element::Type::VBOX) { insertMeasure(Element::Type::VBOX, measure); measure = first(); } Text* text = new Text(this); if (type == "title") text->setTextStyleType(TextStyleType::TITLE); else if (type == "subtitle") text->setTextStyleType(TextStyleType::SUBTITLE); text->setParent(measure); text->setXmlText(txt); undoAddElement(text); } //--------------------------------------------------------- // newCursor //--------------------------------------------------------- Cursor* Score::newCursor() { return new Cursor(this); } #endif //--------------------------------------------------------- // addSpanner //--------------------------------------------------------- void Score::addSpanner(Spanner* s) { _spanner.addSpanner(s); } //--------------------------------------------------------- // removeSpanner //--------------------------------------------------------- void Score::removeSpanner(Spanner* s) { _spanner.removeSpanner(s); } //--------------------------------------------------------- // isSpannerStartEnd // does is spanner start or end at tick position tick // for track ? //--------------------------------------------------------- bool Score::isSpannerStartEnd(int tick, int track) const { for (auto i : _spanner.map()) { if (i.second->track() != track) continue; if (i.second->tick() == tick || i.second->tick2() == tick) return true; } return false; } void Score::insertTime(int tick, int len) { for (Staff* staff : staves()) staff->insertTime(tick, len); for (Part* part : parts()) part->insertTime(tick, len); } //--------------------------------------------------------- // addUnmanagedSpanner //--------------------------------------------------------- void Score::addUnmanagedSpanner(Spanner* s) { _unmanagedSpanner.insert(s); } //--------------------------------------------------------- // removeSpanner //--------------------------------------------------------- void Score::removeUnmanagedSpanner(Spanner* s) { _unmanagedSpanner.erase(s); } //--------------------------------------------------------- // setPos //--------------------------------------------------------- void Score::setPos(POS pos, int tick) { if (tick < 0) tick = 0; if (tick != _pos[int(pos)]) _pos[int(pos)] = tick; // even though tick position might not have changed, layout might have // so we should update cursor here // however, we must be careful not to call setPos() again while handling posChanged, or recursion results emit posChanged(pos, unsigned(tick)); } //--------------------------------------------------------- // uniqueStaves //--------------------------------------------------------- QList Score::uniqueStaves() const { QList sl; for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) { Staff* s = staff(staffIdx); if (s->linkedStaves()) { bool alreadyInList = false; for (int idx : sl) { if (s->linkedStaves()->staves().contains(staff(idx))) { alreadyInList = true; break; } } if (alreadyInList) continue; } sl.append(staffIdx); } return sl; } //--------------------------------------------------------- // findCR // find chord/rest <= tick in track //--------------------------------------------------------- ChordRest* Score::findCR(int tick, int track) const { Measure* m = tick2measureMM(tick); if (!m) { qDebug("findCR: no measure for tick %d", tick); return nullptr; } // attach to first rest all spanner when mmRest if (m->isMMRest()) tick = m->tick(); Segment* s = m->first(Segment::Type::ChordRest); for (Segment* ns = s; ; ns = ns->next(Segment::Type::ChordRest)) { if (ns == 0 || ns->tick() > tick) break; Element* el = ns->element(track); if (el && el->isRest() && toRest(el)->isGap()) continue; else if (el) s = ns; } Element* el = s->element(track); if (el && el->isRest() && toRest(el)->isGap()) s = 0; if (s) return toChordRest(s->element(track)); return nullptr; } //--------------------------------------------------------- // findCRinStaff // find last chord/rest on staff that ends before tick //--------------------------------------------------------- ChordRest* Score::findCRinStaff(int tick, int staffIdx) const { int ptick = tick - 1; Measure* m = tick2measureMM(ptick); if (!m) { qDebug("findCRinStaff: no measure for tick %d", ptick); return nullptr; } // attach to first rest all spanner when mmRest if (m->isMMRest()) ptick = m->tick(); Segment* s = m->first(Segment::Type::ChordRest); int strack = staffIdx * VOICES; int etrack = strack + VOICES; int actualTrack = strack; int lastTick = -1; for (Segment* ns = s; ; ns = ns->next(Segment::Type::ChordRest)) { if (ns == 0 || ns->tick() > ptick) break; // found a segment; now find longest cr on this staff that does not overlap tick for (int t = strack; t < etrack; ++t) { ChordRest* cr = toChordRest(ns->element(t)); if (cr) { int endTick = cr->tick() + cr->actualTicks(); // allow fudge factor for tuplets // TODO: replace with fraction-based calculation int fudge = cr->tuplet() ? 5 : 0; if (endTick + fudge >= lastTick && endTick - fudge <= tick) { s = ns; actualTrack = t; lastTick = endTick; } } } } if (s) return toChordRest(s->element(actualTrack)); return nullptr; } //--------------------------------------------------------- // setSoloMute // called once at opening file, adds soloMute marks //--------------------------------------------------------- void MasterScore::setSoloMute() { for (int i = 0; i < _midiMapping.size(); i++) { Channel* b = _midiMapping[i].articulation; if (b->solo) { b->soloMute = false; for (int j = 0; j < _midiMapping.size(); j++) { Channel* a = _midiMapping[j].articulation; bool sameMidiMapping = _midiMapping[i].port == _midiMapping[j].port && _midiMapping[i].channel == _midiMapping[j].channel; a->soloMute = (i != j && !a->solo && !sameMidiMapping); a->solo = (i == j || a->solo || sameMidiMapping); } } } } //--------------------------------------------------------- // setImportedFilePath //--------------------------------------------------------- void Score::setImportedFilePath(const QString& filePath) { _importedFilePath = filePath; } //--------------------------------------------------------- // title //--------------------------------------------------------- QString Score::title() { QString fn; Text* t = getText(TextStyleType::TITLE); if (t) fn = QTextDocumentFragment::fromHtml(t->xmlText()).toPlainText().replace("&","&").replace(">",">").replace("<","<").replace(""", "\""); if (fn.isEmpty()) fn = metaTag("workTitle"); if (fn.isEmpty()) fn = masterScore()->fileInfo()->completeBaseName(); if (fn.isEmpty()) fn = "Untitled"; return fn.simplified(); } //--------------------------------------------------------- // subtitle //--------------------------------------------------------- QString Score::subtitle() { QString fn; Text* t = getText(TextStyleType::SUBTITLE); if (t) fn = QTextDocumentFragment::fromHtml(t->xmlText()).toPlainText().replace("&","&").replace(">",">").replace("<","<").replace(""", "\""); return fn.simplified(); } //--------------------------------------------------------- // composer //--------------------------------------------------------- QString Score::composer() { QString fn; Text* t = getText(TextStyleType::COMPOSER); if (t) fn = QTextDocumentFragment::fromHtml(t->xmlText()).toPlainText().replace("&","&").replace(">",">").replace("<","<").replace(""", "\""); if (fn.isEmpty()) fn = metaTag("composer"); return fn.simplified(); } //--------------------------------------------------------- // poet //--------------------------------------------------------- QString Score::poet() { QString fn; Text* t = getText(TextStyleType::POET); if (t) fn = QTextDocumentFragment::fromHtml(t->xmlText()).toPlainText().replace("&","&").replace(">",">").replace("<","<").replace(""", "\""); if (fn.isEmpty()) fn = metaTag("lyricist"); if (fn.isEmpty()) fn = ""; return fn.simplified(); } //--------------------------------------------------------- // nmeasure //--------------------------------------------------------- int Score::nmeasures() { int n = 0; for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) n++; return n; } //--------------------------------------------------------- // hasLyrics //--------------------------------------------------------- bool Score::hasLyrics() { Segment::Type st = Segment::Type::ChordRest; for (Segment* seg = firstMeasure()->first(st); seg; seg = seg->next1(st)) { for (int i = 0; i < ntracks(); ++i) { ChordRest* cr = toChordRest(seg->element(i)); if (cr && !cr->lyrics().empty()) return true; } } return false; } //--------------------------------------------------------- // hasHarmonies //--------------------------------------------------------- bool Score::hasHarmonies() { Segment::Type st = Segment::Type::ChordRest; for (Segment* seg = firstMeasure()->first(st); seg; seg = seg->next1(st)) { for (Element* e : seg->annotations()) { if (e->type() == Element::Type::HARMONY) return true; } } return false; } //--------------------------------------------------------- // lyricCount //--------------------------------------------------------- int Score::lyricCount() { int count = 0; Segment::Type st = Segment::Type::ChordRest; for (Segment* seg = firstMeasure()->first(st); seg; seg = seg->next1(st)) { for (int i = 0; i < ntracks(); ++i) { ChordRest* cr = toChordRest(seg->element(i)); if (cr) count += cr->lyrics().size(); } } return count; } //--------------------------------------------------------- // harmonyCount //--------------------------------------------------------- int Score::harmonyCount() { int count = 0; Segment::Type st = Segment::Type::ChordRest; for (Segment* seg = firstMeasure()->first(st); seg; seg = seg->next1(st)) { for (Element* e : seg->annotations()) { if (e->type() == Element::Type::HARMONY) count++; } } return count; } //--------------------------------------------------------- // extractLyrics //--------------------------------------------------------- QString Score::extractLyrics() { QString result; updateRepeatList(true); setPlaylistDirty(); Segment::Type st = Segment::Type::ChordRest; for (int track = 0; track < ntracks(); track += VOICES) { bool found = false; unsigned maxLyrics = 1; for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) { m->setPlaybackCount(0); } // follow the repeat segments for (const RepeatSegment* rs : *repeatList()) { int startTick = rs->tick; int endTick = startTick + rs->len; for (Measure* m = tick2measure(startTick); m; m = m->nextMeasure()) { int playCount = m->playbackCount(); for (Segment* seg = m->first(st); seg; seg = seg->next(st)) { // consider voice 1 only ChordRest* cr = toChordRest(seg->element(track)); if (!cr || cr->lyrics().empty()) continue; if (cr->lyrics().size() > maxLyrics) maxLyrics = cr->lyrics().size(); if (playCount >= int(cr->lyrics().size())) continue; Lyrics* l = cr->lyrics(playCount, Element::Placement::BELOW); // TODO: ABOVE if (!l) continue; found = true; QString lyric = l->plainText().trimmed(); if (l->syllabic() == Lyrics::Syllabic::SINGLE || l->syllabic() == Lyrics::Syllabic::END) result += lyric + " "; else if (l->syllabic() == Lyrics::Syllabic::BEGIN || l->syllabic() == Lyrics::Syllabic::MIDDLE) result += lyric; } m->setPlaybackCount(m->playbackCount() + 1); if (m->tick() + m->ticks() >= endTick) break; } } // consider remaning lyrics for (unsigned lyricsNumber = 0; lyricsNumber < maxLyrics; lyricsNumber++) { for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) { unsigned playCount = m->playbackCount(); if (lyricsNumber >= playCount) { for (Segment* seg = m->first(st); seg; seg = seg->next(st)) { // consider voice 1 only ChordRest* cr = toChordRest(seg->element(track)); if (!cr || cr->lyrics().empty()) continue; if (cr->lyrics().size() > maxLyrics) maxLyrics = cr->lyrics().size(); if (lyricsNumber >= cr->lyrics().size()) continue; Lyrics* l = cr->lyrics(lyricsNumber, Element::Placement::BELOW); // TODO if (!l) continue; found = true; QString lyric = l->plainText().trimmed(); if (l->syllabic() == Lyrics::Syllabic::SINGLE || l->syllabic() == Lyrics::Syllabic::END) result += lyric + " "; else if (l->syllabic() == Lyrics::Syllabic::BEGIN || l->syllabic() == Lyrics:: Syllabic::MIDDLE) result += lyric; } } } } if (found) result += "\n\n"; } return result.trimmed(); } //--------------------------------------------------------- // keysig //--------------------------------------------------------- int Score::keysig() { Key result = Key::C; for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) { Staff* st = staff(staffIdx); Key key = st->key(0); if (st->staffType()->group() == StaffGroup::PERCUSSION || st->keySigEvent(0).custom() || st->keySigEvent(0).isAtonal()) // ignore percussion and custom / atonal key continue; result = key; int diff = st->part()->instrument()->transpose().chromatic; if (!styleB(StyleIdx::concertPitch) && diff) result = transposeKey(key, diff); break; } return int(result); } //--------------------------------------------------------- // duration //--------------------------------------------------------- int Score::duration() { updateRepeatList(true); RepeatSegment* rs = repeatList()->last(); return lrint(utick2utime(rs->utick + rs->len)); } //--------------------------------------------------------- // createRehearsalMarkText //--------------------------------------------------------- QString Score::createRehearsalMarkText(RehearsalMark* current) const { int tick = current->segment()->tick(); RehearsalMark* before = 0; RehearsalMark* after = 0; for (Segment* s = firstSegment(); s; s = s->next1()) { for (Element* e : s->annotations()) { if (e && e->type() == Element::Type::REHEARSAL_MARK) { if (s->tick() < tick) before = toRehearsalMark(e); else if (s->tick() > tick) { after = toRehearsalMark(e); break; } } } if (after) break; } QString s = "A"; QString s1 = before ? before->xmlText() : ""; QString s2 = after ? after->xmlText() : ""; if (s1.isEmpty()) return s; s = nextRehearsalMarkText(before, current); // try to sequence if (s == current->xmlText()) { // no sequence detected (or current happens to be correct) return s; } else if (s == s2) { // next in sequence already present if (s1[0].isLetter()) { if (s1.size() == 2) s = s1[0] + QChar::fromLatin1(s1[1].toLatin1() + 1); // BB, BC, CC else s = s1 + QChar::fromLatin1('1'); // B, B1, C } else { s = s1 + QChar::fromLatin1('A'); // 2, 2A, 3 } } return s; } //--------------------------------------------------------- // nextRehearsalMarkText // finds next rehearsal in sequence established by previous // Alphabetic sequences: // A, B, ..., Y, Z, AA, BB, ..., YY, ZZ // a, b, ..., y, z, aa, bb, ..., yy, zz // Numeric sequences: // 1, 2, 3, ... // If number of previous rehearsal mark matches measure number, assume use of measure numbers throughout //--------------------------------------------------------- QString Score::nextRehearsalMarkText(RehearsalMark* previous, RehearsalMark* current) const { QString previousText = previous->xmlText(); QString fallback = current ? current->xmlText() : previousText + "'"; if (previousText.length() == 1 && previousText[0].isLetter()) { // single letter sequence if (previousText == "Z") return "AA"; else if (previousText == "z") return "aa"; else return QChar::fromLatin1(previousText[0].toLatin1() + 1); } else if (previousText.length() == 2 && previousText[0].isLetter() && previousText[1].isLetter()) { // double letter sequence if (previousText[0] == previousText[1]) { // repeated letter sequence if (previousText.toUpper() != "ZZ") { QString c = QChar::fromLatin1(previousText[0].toLatin1() + 1); return c + c; } else { return fallback; } } else { return fallback; } } else { // try to interpret as number bool ok; int n = previousText.toInt(&ok); if (!ok) { return fallback; } else if (current && n == previous->segment()->measure()->no() + 1) { // use measure number n = current->segment()->measure()->no() + 1; return QString("%1").arg(n); } else { // use number sequence n = previousText.toInt() + 1; return QString("%1").arg(n); } } } //--------------------------------------------------------- // changeVoice // moves selected notes into specified voice if possible //--------------------------------------------------------- void Score::changeVoice(int voice) { startCmd(); QList el; QList oel = selection().elements(); // make copy for (Element* e : oel) { if (e->type() == Element::Type::NOTE) { Note* note = toNote(e); Chord* chord = note->chord(); // TODO - handle ties; for now we skip tied notes if (note->tieFor() || note->tieBack()) continue; // move grace notes with main chord only if (chord->isGrace()) continue; if (chord->voice() != voice) { Segment* s = chord->segment(); Measure* m = s->measure(); int notes = chord->notes().size(); int dstTrack = chord->staffIdx() * VOICES + voice; ChordRest* dstCR = toChordRest(s->element(dstTrack)); Chord* dstChord = nullptr; // set up destination chord if (dstCR && dstCR->type() == Element::Type::CHORD && dstCR->globalDuration() == chord->globalDuration()) { // existing chord in destination with correct duration; // can simply move note in dstChord = toChord(dstCR); } else if (dstCR && dstCR->type() == Element::Type::REST && dstCR->globalDuration() == chord->globalDuration()) { // existing rest in destination with correct duration; // replace with chord, then move note in // this case allows for tuplets, unlike the more general case below dstChord = new Chord(this); dstChord->setTrack(dstTrack); dstChord->setDurationType(chord->durationType()); dstChord->setDuration(chord->duration()); dstChord->setTuplet(dstCR->tuplet()); dstChord->setParent(s); undoRemoveElement(dstCR); } else if (!chord->tuplet()) { // rests or gap in destination // insert new chord if the rests / gap are long enough // then move note in ChordRest* pcr = nullptr; ChordRest* ncr = nullptr; for (Segment* s2 = m->first(Segment::Type::ChordRest); s2; s2 = s2->next()) { if (s2->segmentType() != Segment::Type::ChordRest) continue; ChordRest* cr2 = toChordRest(s2->element(dstTrack)); if (!cr2 || cr2->type() == Element::Type::REST) continue; if (s2->tick() < s->tick()) { pcr = cr2; continue; } else if (s2->tick() >= s->tick()) { ncr = cr2; break; } } int gapStart = pcr ? pcr->tick() + pcr->actualTicks() : m->tick(); int gapEnd = ncr ? ncr->tick() : m->tick() + m->ticks(); if (gapStart <= s->tick() && gapEnd >= s->tick() + chord->actualTicks()) { // big enough gap found dstChord = new Chord(this); dstChord->setTrack(dstTrack); dstChord->setDurationType(chord->durationType()); dstChord->setDuration(chord->duration()); dstChord->setParent(s); // makeGapVoice will not back-fill an empty voice if (voice && !dstCR) expandVoice(s, /*m->first(Segment::Type::ChordRest,*/ dstTrack); makeGapVoice(s, dstTrack, chord->actualFraction(), s->tick()); } } // move note to destination chord if (dstChord) { // create & add new note Note* newNote = new Note(*note); newNote->setSelected(false); newNote->setParent(dstChord); undoAddElement(newNote); el.append(newNote); // add new chord if one was created if (dstChord != dstCR) undoAddCR(dstChord, m, s->tick()); // remove original note if (notes > 1) { undoRemoveElement(note); } else if (notes == 1) { // create rest to leave behind Rest* r = new Rest(this); r->setTrack(chord->track()); r->setDurationType(chord->durationType()); r->setDuration(chord->duration()); r->setTuplet(chord->tuplet()); r->setParent(s); // if there were grace notes, move them for (Chord* gc : chord->graceNotes()) { Chord* ngc = new Chord(*gc); undoRemoveElement(gc); ngc->setParent(dstChord); ngc->setTrack(dstChord->track()); undoAddElement(ngc); } // remove chord, replace with rest undoRemoveElement(chord); undoAddCR(r, m, s->tick()); } } } } } if (!el.empty()) selection().clear(); for (Element* e : el) select(e, SelectType::ADD, -1); setLayoutAll(); endCmd(); } //--------------------------------------------------------- // cropPage - crop a single page score to the content /// margins will be applied on the 4 sides //--------------------------------------------------------- void Score::cropPage(qreal margins) { if (npages() == 1) { Page* page = pages()[0]; if (page) { QRectF ttbox = page->tbbox(); PageFormat* curFormat = pageFormat(); PageFormat f; f.copy(*curFormat); qreal margin = margins / INCH; f.setSize(QSizeF((ttbox.width() / DPI) + 2 * margin, (ttbox.height()/ DPI) + 2 * margin)); qreal offset = curFormat->oddLeftMargin() - ttbox.x() / DPI; if (offset < 0) offset = 0.0; f.setOddLeftMargin(margin + offset); f.setEvenLeftMargin(margin + offset); f.setOddBottomMargin(margin); f.setOddTopMargin(margin); f.setEvenBottomMargin(margin); f.setEvenTopMargin(margin); undoChangePageFormat(&f, spatium(), pageNumberOffset()); } } } //--------------------------------------------------------- // getProperty //--------------------------------------------------------- QVariant Score::getProperty(P_ID id) const { switch (id) { default: qDebug("Score::getProperty: unhandled id"); return QVariant(); } } //--------------------------------------------------------- // setProperty //--------------------------------------------------------- bool Score::setProperty(P_ID id, const QVariant& /*v*/) { switch (id) { default: qDebug("Score::setProperty: unhandled id"); break; } setLayoutAll(); return true; } //--------------------------------------------------------- // propertyDefault //--------------------------------------------------------- QVariant Score::propertyDefault(P_ID id) const { switch (id) { default: return QVariant(); } } //--------------------------------------------------------- // setStyle //--------------------------------------------------------- void Score::setStyle(const MStyle& s) { if (style()->value(StyleIdx::spatium) != s.value(StyleIdx::spatium)) spatiumChanged(style()->value(StyleIdx::spatium).toDouble(), s.value(StyleIdx::spatium).toDouble()); _style = s; } //--------------------------------------------------------- // MasterScore //--------------------------------------------------------- MasterScore::MasterScore() : Score() { _tempomap = new TempoMap; _sigmap = new TimeSigMap(); _undo = new UndoStack(); _repeatList = new RepeatList(this); _revisions = new Revisions; setMasterScore(this); // _undoRedo = false; _omr = 0; _showOmr = false; #if defined(Q_OS_WIN) metaTags().insert("platform", "Microsoft Windows"); #elif defined(Q_OS_MAC) metaTags().insert("platform", "Apple Macintosh"); #elif defined(Q_OS_LINUX) metaTags().insert("platform", "Linux"); #else metaTags().insert("platform", "Unknown"); #endif metaTags().insert("movementNumber", ""); metaTags().insert("movementTitle", ""); metaTags().insert("workNumber", ""); metaTags().insert("workTitle", ""); metaTags().insert("arranger", ""); metaTags().insert("composer", ""); metaTags().insert("lyricist", ""); metaTags().insert("poet", ""); metaTags().insert("translator", ""); metaTags().insert("source", ""); metaTags().insert("copyright", ""); metaTags().insert("creationDate", QDate::currentDate().toString(Qt::ISODate)); } MasterScore::MasterScore(const MStyle* s) : MasterScore{} { setStyle(*s); } MasterScore::~MasterScore() { delete _revisions; delete _repeatList; delete _undo; delete _sigmap; delete _tempomap; qDeleteAll(_excerpts); } //--------------------------------------------------------- // isSavable //--------------------------------------------------------- bool MasterScore::isSavable() const { // TODO: check if file can be created if it does not exist return fileInfo()->isWritable() || !fileInfo()->exists(); } //--------------------------------------------------------- // setTempomap //--------------------------------------------------------- void MasterScore::setTempomap(TempoMap* tm) { delete _tempomap; _tempomap = tm; } //--------------------------------------------------------- // setName //--------------------------------------------------------- void Score::setName(const QString& ss) { QString s(ss); s.replace('/', '_'); // for sanity if (!(s.endsWith(".mscz") || s.endsWith(".mscx"))) s += ".mscz"; info.setFile(s); } //--------------------------------------------------------- // removeOmr //--------------------------------------------------------- void MasterScore::removeOmr() { _showOmr = false; #ifdef OMR delete _omr; #endif _omr = 0; } //--------------------------------------------------------- // addRefresh //--------------------------------------------------------- void Score::addRefresh(const QRectF& r) { _updateState.refresh |= r; cmdState().setUpdateMode(UpdateMode::Update); } }