MuseScore/libmscore/score.cpp

4442 lines
152 KiB
C++
Raw Normal View History

2012-05-26 14:26:10 +02:00
//=============================================================================
// 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 <assert.h>
#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"
2012-07-06 17:42:20 +02:00
#ifdef OMR
2012-05-26 14:26:10 +02:00
#include "omr/omr.h"
2012-07-06 17:42:20 +02:00
#endif
2012-05-26 14:26:10 +02:00
#include "bracket.h"
#include "audio.h"
2012-06-07 09:24:05 +02:00
#include "instrtemplate.h"
#include "cursor.h"
2013-11-11 16:53:03 +01:00
#include "sym.h"
#include "rehearsalmark.h"
#include "breath.h"
#include "instrchange.h"
2012-05-26 14:26:10 +02:00
2013-05-13 18:49:17 +02:00
namespace Ms {
2016-03-10 10:41:31 +01:00
MasterScore* gscore; ///< system score, used for palettes etc.
2012-05-26 14:26:10 +02:00
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);
}
//---------------------------------------------------------
2015-06-13 17:57:57 +02:00
// remove
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
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;
2017-01-18 14:16:33 +01:00
if (nb->type() == ElementType::HBOX || nb->type() == ElementType::VBOX
|| nb->type() == ElementType::TBOX || nb->type() == ElementType::FBOX)
2012-05-26 14:26:10 +02:00
nb->setSystem(ob->system());
foreach(Element* e, nb->el())
2012-05-26 14:26:10 +02:00
e->setParent(nb);
}
//---------------------------------------------------------
// Score
//---------------------------------------------------------
Score::Score()
2017-01-18 14:16:33 +01:00
: ScoreElement(this), _is(this), _selection(this), _selectionFilter(this)
{
2016-03-10 10:41:31 +01:00
_masterScore = 0;
2016-03-11 12:18:46 +01:00
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;
2017-01-05 11:23:47 +01:00
_style = MScore::defaultStyle();
// accInfo = tr("No selection"); // ??
accInfo = "No selection";
}
2016-03-10 10:41:31 +01:00
Score::Score(MasterScore* parent)
: Score{}
2012-05-26 14:26:10 +02:00
{
2016-03-10 10:41:31 +01:00
_masterScore = parent;
if (MScore::defaultStyleForParts())
_style = *MScore::defaultStyleForParts();
else {
// inherit most style settings from parent
_style = parent->style();
2018-03-27 15:36:00 +02:00
static const Sid styles[] = {
Sid::pageWidth,
Sid::pageHeight,
Sid::pagePrintableWidth,
Sid::pageEvenLeftMargin,
Sid::pageOddLeftMargin,
Sid::pageEvenTopMargin,
Sid::pageEvenBottomMargin,
Sid::pageOddTopMargin,
Sid::pageOddBottomMargin,
Sid::pageTwosided,
Sid::spatium
};
// but borrow defaultStyle page layout settings
for (auto i : styles)
_style.set(i, MScore::defaultStyle().value(i));
// and force some style settings that just make sense for parts
2018-03-27 15:36:00 +02:00
style().set(Sid::concertPitch, false);
style().set(Sid::createMultiMeasureRests, true);
style().set(Sid::dividerLeft, false);
style().set(Sid::dividerRight, false);
}
_synthesizerState = parent->_synthesizerState;
_mscVersion = parent->_mscVersion;
2012-05-26 14:26:10 +02:00
}
2017-01-05 11:23:47 +01:00
Score::Score(MasterScore* parent, const MStyle& s)
2016-03-10 10:41:31 +01:00
: Score{parent}
{
2017-01-05 11:23:47 +01:00
_style = s;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// ~Score
//---------------------------------------------------------
Score::~Score()
{
foreach(MuseScoreView* v, viewer)
v->removeScore();
2013-10-05 14:03:34 +02:00
// deselectAll();
2012-05-26 14:26:10 +02:00
for (MeasureBase* m = _measures.first(); m;) {
MeasureBase* nm = m->next();
delete m;
m = nm;
}
2016-02-04 11:27:47 +01:00
qDeleteAll(_parts);
qDeleteAll(_staves);
qDeleteAll(_systems);
2017-01-05 11:23:47 +01:00
// qDeleteAll(_pages);
2016-03-30 22:33:04 +02:00
_masterScore = 0;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// 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
2013-05-14 16:43:21 +02:00
- inserting or removing a measure
2012-05-26 14:26:10 +02:00
- changing the sigmap
- after inserting/deleting time (changes the sigmap)
*/
void Score::fixTicks()
{
int tick = 0;
2012-05-26 14:26:10 +02:00
Measure* fm = firstMeasure();
if (fm == 0)
return;
2014-05-03 11:33:47 +02:00
for (Staff* staff : _staves)
staff->clearTimeSig();
2014-11-27 13:04:03 +01:00
2012-05-26 14:26:10 +02:00
Fraction sig(fm->len());
Fraction nomSig(fm->timesig());
2014-11-27 13:04:03 +01:00
2016-03-10 10:41:31 +01:00
if (isMaster()) {
2012-05-26 14:26:10 +02:00
tempomap()->clear();
2016-03-10 10:41:31 +01:00
sigmap()->clear();
sigmap()->add(0, SigEvent(fm->len(), fm->timesig(), 0));
2012-05-26 14:26:10 +02:00
}
for (MeasureBase* mb = first(); mb; mb = mb->next()) {
2017-01-18 14:16:33 +01:00
if (mb->type() != ElementType::MEASURE) {
2012-05-26 14:26:10 +02:00
mb->setTick(tick);
continue;
}
2016-06-03 10:17:06 +02:00
Measure* m = toMeasure(mb);
2012-05-26 14:26:10 +02:00
int mtick = m->tick();
int diff = tick - mtick;
int measureTicks = m->ticks();
m->moveTicks(diff);
2013-09-28 12:05:48 +02:00
if (m->mmRest())
m->mmRest()->moveTicks(diff);
2012-05-26 14:26:10 +02:00
2016-01-04 14:48:58 +01:00
//
// implement section break rest
//
if (isMaster() && m->sectionBreak() && m->pause() != 0.0)
setPause(m->tick() + m->ticks(), m->pause());
2016-01-04 14:48:58 +01:00
//
// implement fermata as a tempo change
//
for (Segment* s = m->first(); s; s = s->next()) {
2017-03-08 13:12:26 +01:00
if (isMaster() && s->segmentType() == SegmentType::Breath) {
2016-01-04 14:48:58 +01:00
qreal length = 0.0;
int tick1 = s->tick();
2016-01-04 14:48:58 +01:00
// find longest pause
for (int i = 0, n = ntracks(); i < n; ++i) {
Element* e = s->element(i);
2017-01-18 14:16:33 +01:00
if (e && e->type() == ElementType::BREATH) {
2016-06-03 10:17:06 +02:00
Breath* b = toBreath(e);
2016-01-04 14:48:58 +01:00
length = qMax(length, b->pause());
}
}
2016-01-04 14:48:58 +01:00
if (length != 0.0)
setPause(tick1, length);
2016-01-04 14:48:58 +01:00
}
2017-03-08 13:12:26 +01:00
else if (s->segmentType() == SegmentType::TimeSig) {
2016-01-04 14:48:58 +01:00
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
2016-06-03 10:17:06 +02:00
TimeSig* ts = toTimeSig(s->element(staffIdx * VOICES));
2016-01-04 14:48:58 +01:00
if (ts)
staff(staffIdx)->addTimeSig(ts);
2012-05-26 14:26:10 +02:00
}
2016-01-04 14:48:58 +01:00
}
2016-06-24 11:58:22 +02:00
// TODO: all done in doLayout, getNextMeasure, collectSystem... Do we keep?
2017-03-08 13:12:26 +01:00
else if (isMaster() && (s->segmentType() == SegmentType::ChordRest)) {
for (Element* e : s->annotations()) {
2017-01-18 14:16:33 +01:00
if (e->type() == ElementType::TEMPO_TEXT) {
TempoText* tt = toTempoText(e);
if (tt->isRelative())
tt->updateRelative();
2016-01-04 14:48:58 +01:00
setTempo(tt->segment(), tt->tempo());
}
2016-01-04 14:48:58 +01:00
}
2018-01-17 13:25:23 +01:00
#if 0 // TODO::fermata
2016-01-04 14:48:58 +01:00
qreal stretch = 0.0;
2016-02-09 13:51:19 +01:00
for (unsigned i = 0; i < s->elist().size(); ++i) {
2016-01-04 14:48:58 +01:00
Element* e = s->elist().at(i);
if (!e)
continue;
2016-06-03 10:17:06 +02:00
ChordRest* cr = toChordRest(e);
2016-01-04 14:48:58 +01:00
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;
2012-05-26 14:26:10 +02:00
}
}
2018-01-17 13:25:23 +01:00
#endif
2012-05-26 14:26:10 +02:00
}
2016-01-04 14:48:58 +01:00
}
2012-05-26 14:26:10 +02:00
// 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
2016-01-04 14:48:58 +01:00
if (isMaster() && (!sig.identical(m->len()) || !nomSig.identical(m->timesig()))) {
2012-05-26 14:26:10 +02:00
sig = m->len();
nomSig = m->timesig();
sigmap()->add(tick, SigEvent(sig, nomSig, m->no()));
2012-05-26 14:26:10 +02:00
}
2014-11-27 13:04:03 +01:00
2012-05-26 14:26:10 +02:00
tick += measureTicks;
}
2016-06-24 11:58:22 +02:00
// Now done in getNextMeasure(), do we keep?
2012-05-26 14:26:10 +02:00
if (tempomap()->empty())
tempomap()->setTempo(0, defaultTempo);
2012-05-26 14:26:10 +02:00
}
2012-11-08 12:59:30 +01:00
//---------------------------------------------------------
// 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;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// pos2measure
2017-05-02 14:17:31 +02:00
// Return measure for canvas relative position \a p.
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
2017-05-02 14:17:31 +02:00
Measure* Score::pos2measure(const QPointF& p, int* rst, int* pitch, Segment** seg, QPointF* offset) const
2012-05-26 14:26:10 +02:00
{
Measure* m = searchMeasure(p);
if (m == 0)
return 0;
System* s = m->system();
qreal y = p.y() - s->canvasPos().y();
int i = 0;
for (; i < nstaves();) {
SysStaff* stff = s->staff(i);
if (!stff->show() || !staff(i)->show()) {
2012-05-26 14:26:10 +02:00
++i;
continue;
}
int ni = i;
for (;;) {
++ni;
if (ni == nstaves() || (s->staff(ni)->show() && staff(ni)->show()))
2012-05-26 14:26:10 +02:00
break;
}
qreal sy2;
if (ni != nstaves()) {
SysStaff* nstaff = s->staff(ni);
qreal s1y2 = stff->bbox().y() + stff->bbox().height();
2012-05-26 14:26:10 +02:00
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();
2012-11-08 12:59:30 +01:00
int strack = i * VOICES;
if (!staff(i))
2015-12-11 14:30:54 +01:00
return 0;
// int etrack = staff(i)->part()->nstaves() * VOICES + strack;
int etrack = VOICES + strack;
2012-05-26 14:26:10 +02:00
SysStaff* sstaff = m->system()->staff(i);
2017-03-08 13:12:26 +01:00
SegmentType st = SegmentType::ChordRest;
2012-11-08 12:59:30 +01:00
for (Segment* segment = m->first(st); segment; segment = segment->next(st)) {
if (!validSegment(segment, strack, etrack))
2012-05-26 14:26:10 +02:00
continue;
2012-11-08 12:59:30 +01:00
Segment* ns = segment->next(st);
for (; ns; ns = ns->next(st)) {
if (validSegment(ns, strack, etrack))
2012-05-26 14:26:10 +02:00
break;
}
if (!ns || (pppp.x() < (segment->x() + (ns->x() - segment->x())/2.0))) {
*rst = i;
if (pitch) {
Staff* s1 = _staves[i];
int tick = segment->tick();
ClefType clef = s1->clef(tick);
*pitch = y2pitch(pppp.y() - sstaff->bbox().y(), clef, s1->spatium(tick));
2012-05-26 14:26:10 +02:00
}
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;
2017-03-08 13:12:26 +01:00
SegmentType st = SegmentType::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;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// setShowInvisible
//---------------------------------------------------------
void Score::setShowInvisible(bool v)
{
_showInvisible = v;
2016-03-02 13:20:19 +01:00
setUpdateAll();
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// setShowUnprintable
//---------------------------------------------------------
void Score::setShowUnprintable(bool v)
{
_showUnprintable = v;
2016-03-02 13:20:19 +01:00
setUpdateAll();
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// setShowFrames
//---------------------------------------------------------
void Score::setShowFrames(bool v)
{
_showFrames = v;
2016-03-02 13:20:19 +01:00
setUpdateAll();
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// setShowPageborders
//---------------------------------------------------------
void Score::setShowPageborders(bool v)
{
_showPageborders = v;
2016-03-02 13:20:19 +01:00
setUpdateAll();
2012-05-26 14:26:10 +02:00
}
2016-08-06 11:36:51 +02:00
//---------------------------------------------------------
// setMarkIrregularMeasures
//---------------------------------------------------------
void Score::setMarkIrregularMeasures(bool v)
{
_markIrregularMeasures = v;
setUpdateAll();
}
2014-02-25 14:14:59 +01:00
//---------------------------------------------------------
// dirty
//---------------------------------------------------------
bool Score::dirty() const
{
2016-03-10 10:41:31 +01:00
return !undoStack()->isClean();
2014-02-25 14:14:59 +01:00
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// spell
//---------------------------------------------------------
void Score::spell()
{
for (int i = 0; i < nstaves(); ++i) {
2016-02-06 22:03:43 +01:00
std::vector<Note*> notes;
2017-03-08 13:12:26 +01:00
for (Segment* s = firstSegment(SegmentType::All); s; s = s->next1()) {
2012-05-26 14:26:10 +02:00
int strack = i * VOICES;
int etrack = strack + VOICES;
for (int track = strack; track < etrack; ++track) {
Element* e = s->element(track);
2017-01-18 14:16:33 +01:00
if (e && e->type() == ElementType::CHORD)
2016-02-06 22:03:43 +01:00
notes.insert(notes.end(),
2016-06-03 10:17:06 +02:00
toChord(e)->notes().begin(),
toChord(e)->notes().end());
2012-05-26 14:26:10 +02:00
}
}
spellNotelist(notes);
}
}
void Score::spell(int startStaff, int endStaff, Segment* startSegment, Segment* endSegment)
{
for (int i = startStaff; i < endStaff; ++i) {
2016-02-06 22:03:43 +01:00
std::vector<Note*> notes;
2012-05-26 14:26:10 +02:00
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);
2017-01-18 14:16:33 +01:00
if (e && e->type() == ElementType::CHORD)
2016-02-06 22:03:43 +01:00
notes.insert(notes.end(),
2016-06-03 10:17:06 +02:00
toChord(e)->notes().begin(),
toChord(e)->notes().end());
2012-05-26 14:26:10 +02:00
}
}
spellNotelist(notes);
}
}
//---------------------------------------------------------
// prevNote
//---------------------------------------------------------
Note* prevNote(Note* n)
{
Chord* chord = n->chord();
Segment* seg = chord->segment();
2016-02-06 22:03:43 +01:00
const std::vector<Note*> nl = chord->notes();
auto i = std::find(nl.begin(), nl.end(), n);
2016-02-09 13:51:19 +01:00
if (i != nl.begin())
2016-02-06 22:03:43 +01:00
return *(i-1);
2012-05-26 14:26:10 +02:00
int staff = n->staffIdx();
int startTrack = staff * VOICES + n->voice() - 1;
int endTrack = 0;
while (seg) {
2017-03-08 13:12:26 +01:00
if (seg->segmentType() == SegmentType::ChordRest) {
2012-05-26 14:26:10 +02:00
for (int track = startTrack; track >= endTrack; --track) {
Element* e = seg->element(track);
2017-01-18 14:16:33 +01:00
if (e && e->type() == ElementType::CHORD)
2016-06-03 10:17:06 +02:00
return toChord(e)->upNote();
2012-05-26 14:26:10 +02:00
}
}
seg = seg->prev1();
startTrack = staff * VOICES + VOICES - 1;
}
return n;
}
//---------------------------------------------------------
// nextNote
//---------------------------------------------------------
2016-02-09 09:20:54 +01:00
static Note* nextNote(Note* n)
2012-05-26 14:26:10 +02:00
{
Chord* chord = n->chord();
2016-02-06 22:03:43 +01:00
const std::vector<Note*> nl = chord->notes();
auto i = std::find(nl.begin(), nl.end(), n);
if (i != nl.end()) {
++i;
if (i != nl.end())
return *i;
}
2012-05-26 14:26:10 +02:00
Segment* seg = chord->segment();
int staff = n->staffIdx();
int startTrack = staff * VOICES + n->voice() + 1;
int endTrack = staff * VOICES + VOICES;
while (seg) {
2017-03-08 13:12:26 +01:00
if (seg->segmentType() == SegmentType::ChordRest) {
2012-05-26 14:26:10 +02:00
for (int track = startTrack; track < endTrack; ++track) {
Element* e = seg->element(track);
2017-01-18 14:16:33 +01:00
if (e && e->type() == ElementType::CHORD) {
2012-05-26 14:26:10 +02:00
return ((Chord*)e)->downNote();
}
}
}
seg = seg->next1();
startTrack = staff * VOICES;
}
return n;
}
//---------------------------------------------------------
// spell
//---------------------------------------------------------
void Score::spell(Note* note)
{
2016-02-06 22:03:43 +01:00
std::vector<Note*> notes;
2012-05-26 14:26:10 +02:00
2016-02-06 22:03:43 +01:00
notes.push_back(note);
2012-05-26 14:26:10 +02:00
Note* nn = nextNote(note);
2016-02-06 22:03:43 +01:00
notes.push_back(nn);
2012-05-26 14:26:10 +02:00
nn = nextNote(nn);
2016-02-06 22:03:43 +01:00
notes.push_back(nn);
2012-05-26 14:26:10 +02:00
nn = nextNote(nn);
2016-02-06 22:03:43 +01:00
notes.push_back(nn);
2012-05-26 14:26:10 +02:00
nn = prevNote(note);
2016-02-06 22:03:43 +01:00
notes.insert(notes.begin(), nn);
2012-05-26 14:26:10 +02:00
nn = prevNote(nn);
2016-02-06 22:03:43 +01:00
notes.insert(notes.begin(), nn);
2012-05-26 14:26:10 +02:00
nn = prevNote(nn);
2016-02-06 22:03:43 +01:00
notes.insert(notes.begin(), nn);
2012-05-26 14:26:10 +02:00
2013-05-13 18:49:17 +02:00
int opt = Ms::computeWindow(notes, 0, 7);
note->setTpc(Ms::tpc(3, note->pitch(), opt));
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// appendPart
//---------------------------------------------------------
void Score::appendPart(Part* p)
{
_parts.append(p);
}
//---------------------------------------------------------
// searchPage
// p is in canvas coordinates
//---------------------------------------------------------
Page* Score::searchPage(const QPointF& p) const
{
2015-12-11 14:30:54 +01:00
for (Page* page : pages()) {
QRectF r = page->bbox().translated(page->pos());
if (r.contains(p))
2012-05-26 14:26:10 +02:00
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<System*> Score::searchSystem(const QPointF& pos) const
{
QList<System*> systems;
Page* page = searchPage(pos);
if (page == 0)
return systems;
qreal y = pos.y() - page->pos().y(); // transform to page relative
2016-01-04 14:48:58 +01:00
const QList<System*>* sl = &page->systems();
2012-05-26 14:26:10 +02:00
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 {
2012-05-26 14:26:10 +02:00
qreal sy2 = s->y() + s->bbox().height();
y2 = sy2 + (ns->y() - sy2) * .5;
}
if (y < y2) {
systems.append(s);
for (int iii = i+1; ii < n; ++iii) {
if (sl->at(iii)->y() != s->y())
2012-05-26 14:26:10 +02:00
break;
systems.append(sl->at(iii));
2012-05-26 14:26:10 +02:00
}
return systems;
}
}
return systems;
}
//---------------------------------------------------------
// searchMeasure
// p is in canvas coordinates
//---------------------------------------------------------
Measure* Score::searchMeasure(const QPointF& p) const
{
QList<System*> systems = searchSystem(p);
2017-05-02 14:17:31 +02:00
for (System* system : systems) {
2012-05-26 14:26:10 +02:00
qreal x = p.x() - system->canvasPos().x();
2017-05-02 14:17:31 +02:00
for (MeasureBase* mb : system->measures()) {
if (mb->isMeasure() && (x < (mb->x() + mb->bbox().width())))
2016-06-03 10:17:06 +02:00
return toMeasure(mb);
2012-05-26 14:26:10 +02:00
}
}
return 0;
}
//---------------------------------------------------------
// getNextValidInputSegment
2017-03-08 13:12:26 +01:00
// - s is of type SegmentType::ChordRest
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
static Segment* getNextValidInputSegment(Segment* s, int track, int voice)
{
if (s == 0)
return 0;
2017-03-08 13:12:26 +01:00
Q_ASSERT(s->segmentType() == SegmentType::ChordRest);
2012-05-26 14:26:10 +02:00
// Segment* s1 = s;
ChordRest* cr1 = nullptr;
2017-03-08 13:12:26 +01:00
for (Segment* s1 = s; s1; s1 = s1->prev(SegmentType::ChordRest)) {
2016-06-03 10:17:06 +02:00
cr1 = toChordRest(s1->element(track + voice));
2012-05-26 14:26:10 +02:00
if (cr1)
break;
}
int nextTick = (cr1 == nullptr) ? s->measure()->tick() : cr1->tick() + cr1->actualTicks();
2012-05-26 14:26:10 +02:00
2017-03-08 13:12:26 +01:00
static const SegmentType st { SegmentType::ChordRest };
2012-05-26 14:26:10 +02:00
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)) {
2016-06-03 10:17:06 +02:00
ChordRest* cr = toChordRest(s1->element(track + voice));
2012-05-26 14:26:10 +02:00
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
{
2012-08-07 12:44:19 +02:00
Measure* measure = searchMeasure(p);
if (measure == 0)
2012-05-26 14:26:10 +02:00
return false;
pos->fret = FRET_NONE;
2012-05-26 14:26:10 +02:00
//
// search staff
//
pos->staffIdx = 0;
SysStaff* sstaff = 0;
2012-08-07 12:44:19 +02:00
System* system = measure->system();
2012-05-26 14:26:10 +02:00
qreal y = p.y() - system->pagePos().y();
for (; pos->staffIdx < nstaves(); ++pos->staffIdx) {
2014-07-31 14:12:34 +02:00
Staff* st = staff(pos->staffIdx);
if (!st->part()->show())
2014-07-31 14:12:34 +02:00
continue;
2012-05-26 14:26:10 +02:00
qreal sy2;
SysStaff* ss = system->staff(pos->staffIdx);
if (!ss->show())
continue;
2014-07-31 14:12:34 +02:00
SysStaff* nstaff = 0;
// find next visible staff
for (int i = pos->staffIdx + 1; i < nstaves(); ++i) {
Staff* sti = staff(i);
if (!sti->part()->show())
2014-07-31 14:12:34 +02:00
continue;
nstaff = system->staff(i);
if (!nstaff->show()) {
nstaff = 0;
continue;
}
2014-07-31 14:12:34 +02:00
break;
}
if (nstaff) {
qreal s1y2 = ss->bbox().bottom();
sy2 = system->page()->canvasPos().y() + s1y2 + (nstaff->bbox().y() - s1y2) * .5;
2012-05-26 14:26:10 +02:00
}
else
sy2 = system->page()->canvasPos().y() + system->page()->height() - system->pagePos().y(); // system->height();
2012-05-26 14:26:10 +02:00
if (y < sy2) {
sstaff = ss;
break;
}
}
if (sstaff == 0)
return false;
//
// search segment
//
2012-08-07 12:44:19 +02:00
QPointF pppp(p - measure->canvasPos());
2012-05-26 14:26:10 +02:00
qreal x = pppp.x();
Segment* segment = 0;
pos->segment = 0;
// int track = pos->staffIdx * VOICES + voice;
int track = pos->staffIdx * VOICES;
2017-03-08 13:12:26 +01:00
for (segment = measure->first(SegmentType::ChordRest); segment;) {
2012-05-26 14:26:10 +02:00
segment = getNextValidInputSegment(segment, track, voice);
if (segment == 0)
break;
2017-03-08 13:12:26 +01:00
Segment* ns = getNextValidInputSegment(segment->next(SegmentType::ChordRest), track, voice);
2012-05-26 14:26:10 +02:00
qreal x1 = segment->x();
qreal x2;
qreal d;
if (ns) {
x2 = ns->x();
d = x2 - x1;
}
else {
2012-08-07 12:44:19 +02:00
x2 = measure->bbox().width();
2012-05-26 14:26:10 +02:00
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);
2016-12-23 12:05:18 +01:00
qreal mag = s->mag(segment->tick());
2016-12-13 13:16:17 +01:00
int tick = segment->tick();
// in TABs, step from one string to another; in other staves, step on and between lines
2016-12-13 13:16:17 +01:00
qreal lineDist = s->staffType(tick)->lineDistance().val() * (s->isTabStaff(measure->tick()) ? 1 : .5) * mag * spatium();
2012-05-26 14:26:10 +02:00
pos->line = lrint((pppp.y() - sstaff->bbox().y()) / lineDist);
2016-12-13 13:16:17 +01:00
if (s->isTabStaff(measure->tick())) {
if (pos->line < -1 || pos->line > s->lines(tick)+1)
2012-05-26 14:26:10 +02:00
return false;
if (pos->line < 0)
pos->line = 0;
2016-12-13 13:16:17 +01:00
else if (pos->line >= s->lines(tick))
pos->line = s->lines(tick) - 1;
2012-05-26 14:26:10 +02:00
}
else {
2012-08-07 16:05:37 +02:00
int minLine = absStep(0);
2012-08-07 12:44:19 +02:00
ClefType clef = s->clef(pos->segment->tick());
2012-08-07 16:05:37 +02:00
minLine = relStep(minLine, clef);
int maxLine = absStep(127);
maxLine = relStep(maxLine, clef);
2012-05-26 14:26:10 +02:00
if (pos->line > minLine || pos->line < maxLine)
return false;
}
y = sstaff->y() + pos->line * lineDist;
2012-08-07 12:44:19 +02:00
pos->pos = QPointF(x, y) + measure->canvasPos();
2012-05-26 14:26:10 +02:00
return true;
}
//---------------------------------------------------------
// checkHasMeasures
//---------------------------------------------------------
bool Score::checkHasMeasures() const
{
Page* page = pages().isEmpty() ? 0 : pages().front();
const QList<System*>* sl = page ? &page->systems() : 0;
2012-05-26 14:26:10 +02:00
if (sl == 0 || sl->empty() || sl->front()->measures().empty()) {
2013-07-25 17:22:49 +02:00
qDebug("first create measure, then repeat operation");
2012-05-26 14:26:10 +02:00
return false;
}
return true;
}
2017-03-31 13:03:15 +02:00
#if 0
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// moveBracket
// columns are counted from right to left
//---------------------------------------------------------
void Score::moveBracket(int staffIdx, int srcCol, int dstCol)
{
2016-02-04 11:27:47 +01:00
for (System* system : systems())
system->moveBracket(staffIdx, srcCol, dstCol);
2012-05-26 14:26:10 +02:00
}
2017-03-31 13:03:15 +02:00
#endif
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// 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);
for (Staff* staff : _staves)
2012-05-26 14:26:10 +02:00
staff->spatiumChanged(oldValue, newValue);
2015-11-16 14:24:47 +01:00
_noteHeadWidth = _scoreFont->width(SymId::noteheadBlack, newValue / SPATIUM20);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// updateStyle
//---------------------------------------------------------
static void updateStyle(void*, Element* e)
{
e->styleChanged();
}
//---------------------------------------------------------
// styleChanged
// must be called after every style change
//---------------------------------------------------------
void Score::styleChanged()
{
scanElements(0, updateStyle);
if (headerText())
headerText()->styleChanged();
if (footerText())
footerText()->styleChanged();
setLayoutAll();
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// 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);
2016-03-10 10:41:31 +01:00
Fraction ts = sigmap()->timesig(lastTick).timesig();
2013-07-25 17:22:49 +02:00
// qDebug("getCreateMeasure %d %d/%d", tick, ts.numerator(), ts.denominator());
2012-05-26 14:26:10 +02:00
m->setTick(lastTick);
m->setTimesig(ts);
m->setLen(ts);
2017-12-20 16:49:30 +01:00
measures()->add(toMeasureBase(m));
2012-05-26 14:26:10 +02:00
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)
{
2016-02-04 11:27:47 +01:00
Element* parent = element->parent();
element->triggerLayout();
2016-02-04 11:27:47 +01:00
2016-03-24 12:39:18 +01:00
// qDebug("Score(%p) Element(%p)(%s) parent %p(%s)",
2016-03-02 13:20:19 +01:00
// this, element, element->name(), parent, parent ? parent->name() : "");
2016-02-04 11:27:47 +01:00
2017-01-18 14:16:33 +01:00
ElementType et = element->type();
if (et == ElementType::MEASURE
|| (et == ElementType::HBOX && element->parent()->type() != ElementType::VBOX)
|| et == ElementType::VBOX
|| et == ElementType::TBOX
|| et == ElementType::FBOX
2012-05-26 14:26:10 +02:00
) {
2017-12-20 16:49:30 +01:00
measures()->add(toMeasureBase(element));
2012-05-26 14:26:10 +02:00
return;
}
2016-02-04 11:27:47 +01:00
if (parent)
parent->add(element);
2012-05-26 14:26:10 +02:00
switch (et) {
2017-01-18 14:16:33 +01:00
case ElementType::BEAM:
2014-07-03 15:02:36 +02:00
{
2016-06-03 10:17:06 +02:00
Beam* b = toBeam(element);
2014-07-03 15:02:36 +02:00
int n = b->elements().size();
for (int i = 0; i < n; ++i)
b->elements().at(i)->setBeam(b);
}
break;
2017-01-18 14:16:33 +01:00
case ElementType::SLUR:
addLayoutFlags(LayoutFlag::PLAY_EVENTS);
2013-06-20 18:48:28 +02:00
// fall through
2017-01-18 14:16:33 +01:00
case ElementType::VOLTA:
case ElementType::TRILL:
case ElementType::VIBRATO:
2017-01-18 14:16:33 +01:00
case ElementType::PEDAL:
case ElementType::TEXTLINE:
case ElementType::HAIRPIN:
2017-11-27 09:56:41 +01:00
case ElementType::LET_RING:
case ElementType::PALM_MUTE:
2012-05-26 14:26:10 +02:00
{
2017-12-20 16:49:30 +01:00
Spanner* spanner = toSpanner(element);
2017-01-18 14:16:33 +01:00
if (et == ElementType::TEXTLINE && spanner->anchor() == Spanner::Anchor::NOTE)
2013-07-04 13:40:25 +02:00
break;
2014-07-09 18:05:58 +02:00
addSpanner(spanner);
for (SpannerSegment* ss : spanner->spannerSegments()) {
2012-05-26 14:26:10 +02:00
if (ss->system())
ss->system()->add(ss);
}
}
break;
2017-01-18 14:16:33 +01:00
case ElementType::OTTAVA:
2012-05-26 14:26:10 +02:00
{
2016-06-03 10:17:06 +02:00
Ottava* o = toOttava(element);
2014-07-03 15:02:36 +02:00
addSpanner(o);
2012-05-26 14:26:10 +02:00
foreach(SpannerSegment* ss, o->spannerSegments()) {
if (ss->system())
ss->system()->add(ss);
}
2016-03-18 09:29:16 +01:00
cmdState().layoutFlags |= LayoutFlag::FIX_PITCH_VELO;
2014-07-15 12:49:51 +02:00
o->staff()->updateOttava();
2012-05-26 14:26:10 +02:00
_playlistDirty = true;
}
break;
2017-01-18 14:16:33 +01:00
case ElementType::DYNAMIC:
2016-03-18 09:29:16 +01:00
cmdState().layoutFlags |= LayoutFlag::FIX_PITCH_VELO;
2012-05-26 14:26:10 +02:00
_playlistDirty = true;
break;
2013-07-05 11:23:52 +02:00
2017-01-18 14:16:33 +01:00
case ElementType::TEMPO_TEXT:
2012-05-26 14:26:10 +02:00
{
TempoText* tt = toTempoText(element);
if (tt->isRelative())
tt->updateRelative();
2012-05-26 14:26:10 +02:00
setTempo(tt->segment(), tt->tempo());
}
break;
2013-07-05 11:23:52 +02:00
2017-01-18 14:16:33 +01:00
case ElementType::INSTRUMENT_CHANGE: {
2016-06-03 10:17:06 +02:00
InstrumentChange* ic = toInstrumentChange(element);
2018-07-27 21:55:07 +02:00
ic->part()->setInstrument(ic->instrument(), ic->segment()->tick());
#if 0
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);
2018-07-27 21:55:07 +02:00
#endif
2016-03-11 12:18:46 +01:00
masterScore()->rebuildMidiMapping();
2016-03-18 09:29:16 +01:00
cmdState()._instrumentsChanged = true;
}
2012-05-26 14:26:10 +02:00
break;
2017-01-18 14:16:33 +01:00
case ElementType::CHORD:
2015-01-30 17:03:51 +01:00
setPlaylistDirty();
// create playlist does not work here bc. tremolos may not be complete
2016-06-03 10:17:06 +02:00
// createPlayEvents(toChord(element));
2012-11-21 15:57:35 +01:00
break;
2017-01-18 14:16:33 +01:00
case ElementType::NOTE:
case ElementType::TREMOLO:
case ElementType::ARTICULATION:
case ElementType::ARPEGGIO:
{
2016-02-04 11:27:47 +01:00
Element* cr = parent;
if (cr->isChord())
createPlayEvents(toChord(cr));
}
2016-06-03 10:17:06 +02:00
break;
2012-05-26 14:26:10 +02:00
default:
break;
}
2016-06-03 10:17:06 +02:00
setLayout(element->tick());
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// 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());
2012-05-26 14:26:10 +02:00
2016-03-24 12:39:18 +01:00
// qDebug("Score(%p) Element(%p)(%s) parent %p(%s)",
2016-03-02 13:20:19 +01:00
// this, element, element->name(), parent, parent ? parent->name() : "");
2014-05-21 20:08:37 +02:00
2012-05-26 14:26:10 +02:00
// special for MEASURE, HBOX, VBOX
// their parent is not static
2017-01-18 14:16:33 +01:00
ElementType et = element->type();
2013-06-05 15:47:34 +02:00
2017-01-18 14:16:33 +01:00
if (et == ElementType::MEASURE
|| (et == ElementType::HBOX && !parent->isVBox())
|| et == ElementType::VBOX
|| et == ElementType::TBOX
|| et == ElementType::FBOX
2012-05-26 14:26:10 +02:00
) {
2017-02-15 13:58:54 +01:00
MeasureBase* mb = toMeasureBase(element);
measures()->remove(mb);
System* system = mb->system();
Page* page = system->page();
if (element->isVBox() && system->measures().size() == 1) {
auto i = std::find(page->systems().begin(), page->systems().end(), system);
page->systems().erase(i);
mb->setSystem(0);
if (page->systems().isEmpty()) {
// Remove this page, since it is now empty.
// This involves renumbering and repositioning all subsequent pages.
QPointF pos = page->pos();
auto ii = std::find(pages().begin(), pages().end(), page);
pages().erase(ii);
while (ii != pages().end()) {
page = *ii;
page->setNo(page->no() - 1);
QPointF p = page->pos();
page->setPos(pos);
pos = p;
ii++;
}
}
2017-02-15 13:58:54 +01:00
}
// setLayout(mb->tick());
2012-05-26 14:26:10 +02:00
return;
}
2017-03-14 17:00:38 +01:00
if (et == ElementType::BEAM) { // beam parent does not survive layout
2012-05-26 14:26:10 +02:00
element->setParent(0);
2017-03-14 17:00:38 +01:00
parent = 0;
}
2012-05-26 14:26:10 +02:00
if (parent)
parent->remove(element);
2016-02-09 13:51:19 +01:00
switch (et) {
2017-01-18 14:16:33 +01:00
case ElementType::BEAM:
2016-06-03 10:17:06 +02:00
for (ChordRest* cr : toBeam(element)->elements())
2014-07-03 15:02:36 +02:00
cr->setBeam(0);
break;
2017-01-18 14:16:33 +01:00
case ElementType::SLUR:
addLayoutFlags(LayoutFlag::PLAY_EVENTS);
2013-06-20 18:48:28 +02:00
// fall through
2012-05-26 14:26:10 +02:00
2017-01-18 14:16:33 +01:00
case ElementType::VOLTA:
case ElementType::TRILL:
case ElementType::VIBRATO:
2017-01-18 14:16:33 +01:00
case ElementType::PEDAL:
2017-11-27 09:56:41 +01:00
case ElementType::LET_RING:
case ElementType::PALM_MUTE:
2017-01-18 14:16:33 +01:00
case ElementType::TEXTLINE:
case ElementType::HAIRPIN:
2012-05-26 14:26:10 +02:00
{
2017-12-20 16:49:30 +01:00
Spanner* spanner = toSpanner(element);
2017-01-18 14:16:33 +01:00
if (et == ElementType::TEXTLINE && spanner->anchor() == Spanner::Anchor::NOTE)
2014-07-09 18:05:58 +02:00
break;
2014-07-03 15:02:36 +02:00
removeSpanner(spanner);
2017-02-15 13:58:54 +01:00
//TODO: system is not valid anymore after removing measure
// for (SpannerSegment* ss : spanner->spannerSegments()) {
// if (ss->system())
// ss->system()->remove(ss);
// }
2012-05-26 14:26:10 +02:00
}
break;
2017-01-18 14:16:33 +01:00
case ElementType::OTTAVA:
2012-05-26 14:26:10 +02:00
{
2016-06-03 10:17:06 +02:00
Ottava* o = toOttava(element);
2014-07-03 15:02:36 +02:00
removeSpanner(o);
2012-05-26 14:26:10 +02:00
foreach(SpannerSegment* ss, o->spannerSegments()) {
if (ss->system())
ss->system()->remove(ss);
}
2014-07-15 12:49:51 +02:00
o->staff()->updateOttava();
2016-03-18 09:29:16 +01:00
cmdState().layoutFlags |= LayoutFlag::FIX_PITCH_VELO;
2012-05-26 14:26:10 +02:00
_playlistDirty = true;
}
break;
2017-01-18 14:16:33 +01:00
case ElementType::DYNAMIC:
2016-03-18 09:29:16 +01:00
cmdState().layoutFlags |= LayoutFlag::FIX_PITCH_VELO;
2012-05-26 14:26:10 +02:00
_playlistDirty = true;
break;
2017-01-18 14:16:33 +01:00
case ElementType::CHORD:
case ElementType::REST:
2012-05-26 14:26:10 +02:00
{
2016-06-03 10:17:06 +02:00
ChordRest* cr = toChordRest(element);
2012-05-26 14:26:10 +02:00
if (cr->beam())
cr->beam()->remove(cr);
2016-08-17 12:52:35 +02:00
for (Lyrics* lyr : cr->lyrics())
2016-08-24 14:49:34 +02:00
lyr->removeFromScore();
2014-05-21 20:08:37 +02:00
// TODO: check for tuplet?
2012-05-26 14:26:10 +02:00
}
break;
2017-01-18 14:16:33 +01:00
case ElementType::TEMPO_TEXT:
2012-05-26 14:26:10 +02:00
{
2016-06-03 10:17:06 +02:00
TempoText* tt = toTempoText(element);
2012-05-26 14:26:10 +02:00
int tick = tt->segment()->tick();
tempomap()->delTempo(tick);
}
break;
2017-01-18 14:16:33 +01:00
case ElementType::INSTRUMENT_CHANGE: {
2016-06-03 10:17:06 +02:00
InstrumentChange* ic = toInstrumentChange(element);
2018-07-27 21:55:07 +02:00
ic->part()->removeInstrument(ic->segment()->tick());
#if 0
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);
2018-07-27 21:55:07 +02:00
#endif
2016-03-11 12:18:46 +01:00
masterScore()->rebuildMidiMapping();
2016-03-18 09:29:16 +01:00
cmdState()._instrumentsChanged = true;
}
2012-05-26 14:26:10 +02:00
break;
2012-11-20 20:51:18 +01:00
2017-01-18 14:16:33 +01:00
case ElementType::TREMOLO:
case ElementType::ARTICULATION:
case ElementType::ARPEGGIO:
{
Element* cr = element->parent();
if (cr->isChord())
createPlayEvents(toChord(cr));
}
2016-06-03 10:17:06 +02:00
break;
2012-11-20 20:51:18 +01:00
2012-05-26 14:26:10 +02:00
default:
break;
}
}
//---------------------------------------------------------
// firstMeasure
//---------------------------------------------------------
Measure* Score::firstMeasure() const
{
MeasureBase* mb = _measures.first();
2017-01-18 14:16:33 +01:00
while (mb && mb->type() != ElementType::MEASURE)
2012-05-26 14:26:10 +02:00
mb = mb->next();
2016-06-03 10:17:06 +02:00
return toMeasure(mb);
}
//---------------------------------------------------------
// firstMeasureMM
//---------------------------------------------------------
Measure* Score::firstMeasureMM() const
{
2016-03-02 13:20:19 +01:00
Measure* m = firstMeasure();
2018-03-27 15:36:00 +02:00
if (m && styleB(Sid::createMultiMeasureRests) && m->hasMMRest())
return m->mmRest();
return m;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// firstMM
//---------------------------------------------------------
MeasureBase* Score::firstMM() const
{
MeasureBase* m = _measures.first();
if (m
2017-01-18 14:16:33 +01:00
&& m->type() == ElementType::MEASURE
2018-03-27 15:36:00 +02:00
&& styleB(Sid::createMultiMeasureRests)
2016-06-03 10:17:06 +02:00
&& toMeasure(m)->hasMMRest()) {
return toMeasure(m)->mmRest();
}
return m;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// 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();
2017-01-18 14:16:33 +01:00
while (mb && mb->type() != ElementType::MEASURE)
2012-05-26 14:26:10 +02:00
mb = mb->prev();
2016-06-03 10:17:06 +02:00
return toMeasure(mb);
2012-05-26 14:26:10 +02:00
}
2013-10-06 16:43:43 +02:00
//---------------------------------------------------------
// lastMeasureMM
//---------------------------------------------------------
Measure* Score::lastMeasureMM() const
{
2014-08-17 12:41:44 +02:00
Measure* m = lastMeasure();
2018-03-27 15:36:00 +02:00
if (m && styleB(Sid::createMultiMeasureRests)) {
2016-06-03 10:17:06 +02:00
Measure* m1 = const_cast<Measure*>(toMeasure(m->mmRest1()));
2015-01-28 04:56:28 +01:00
if (m1)
return m1;
}
2014-08-17 12:41:44 +02:00
return m;
2013-10-06 16:43:43 +02:00
}
2017-02-13 17:06:30 +01:00
//---------------------------------------------------------
// endTick
//---------------------------------------------------------
int Score::endTick() const
{
Measure* m = lastMeasure();
return m ? m->endTick() : 0;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// firstSegment
//---------------------------------------------------------
2017-03-08 13:12:26 +01:00
Segment* Score::firstSegment(SegmentType segType) const
2012-05-26 14:26:10 +02:00
{
Segment* seg;
2012-05-26 14:26:10 +02:00
Measure* m = firstMeasure();
if (!m)
seg = 0;
else {
seg = m->first();
if (seg && !(seg->segmentType() & segType))
seg = seg->next1(segType);
}
Fixes the following Q_INVOKABLE methods returning a QObject* by turning them into a property: - Measure: -- firstSegment -- lastSegment - MeasureBase: -- nextMeasure -- nextMeasureMM (new) -- prevMeasure -- prevMeasureMM (new) - Score: -- firstMeasure -- firstMeasureMM (new) -- (for firstSegment(), see special cases below) -- lastMeasure -- lastMeasureMM (new) -- lastSegment - Segment: -- next (renamed from `next1`) -- nextInMeasure (renamed from `next`) -- prev (renamed from `prev1`) -- prevInMeasure (renamed from prev) Special cases: - Cursor: The prototype of the `Q_INVOKABLE Ms::Note* Cursor::addNote(int pitch)` was wrong: corrected in `Q_INVOKABLE void Cursor::addNote(int pitch)`. - QmlPlugin: `Q_INVOKABLE Score* QmlPlugin::readScore()` and `Q_INVOKABLE Score* QmlPlugin::newScore()` has been kept, as they are intended to be called from QML; code has been added to ensure the C++ ownership of the returned object. - Score: `Q_INVOKABLE Segment* Score::firstSegment(Segment::Type segType)` is kept (as it needs a parameters), but code is added to ensure C++ ownership of the returned Segment*. - Segment: `Ms::Element* Segment::element(int track)` has been made NOT Q_INVOKABLE; a variant `Q_INVOKABLE Ms::Element* elementAt(int track)` has been added specifically for QML with code to ensure the C++ ownership of the returned Element* (this was the cause for the crash of the Walk plug-in). - FiguredBass: `Q_INVOKABLE Ms::FiguredBassItem* FiguredBass::addItem()` has been removed; plugin interface for FiguredBass needs to be redesigned anyway. The few occurrences in the supplied plug-ins of the methods whose names did change have been updated.
2014-07-06 01:56:30 +02:00
#ifdef SCRIPT_INTERFACE
// if called from QML/JS, tell QML engine not to garbage collect this object
// if (seg)
// QQmlEngine::setObjectOwnership(seg, QQmlEngine::CppOwnership);
Fixes the following Q_INVOKABLE methods returning a QObject* by turning them into a property: - Measure: -- firstSegment -- lastSegment - MeasureBase: -- nextMeasure -- nextMeasureMM (new) -- prevMeasure -- prevMeasureMM (new) - Score: -- firstMeasure -- firstMeasureMM (new) -- (for firstSegment(), see special cases below) -- lastMeasure -- lastMeasureMM (new) -- lastSegment - Segment: -- next (renamed from `next1`) -- nextInMeasure (renamed from `next`) -- prev (renamed from `prev1`) -- prevInMeasure (renamed from prev) Special cases: - Cursor: The prototype of the `Q_INVOKABLE Ms::Note* Cursor::addNote(int pitch)` was wrong: corrected in `Q_INVOKABLE void Cursor::addNote(int pitch)`. - QmlPlugin: `Q_INVOKABLE Score* QmlPlugin::readScore()` and `Q_INVOKABLE Score* QmlPlugin::newScore()` has been kept, as they are intended to be called from QML; code has been added to ensure the C++ ownership of the returned object. - Score: `Q_INVOKABLE Segment* Score::firstSegment(Segment::Type segType)` is kept (as it needs a parameters), but code is added to ensure C++ ownership of the returned Segment*. - Segment: `Ms::Element* Segment::element(int track)` has been made NOT Q_INVOKABLE; a variant `Q_INVOKABLE Ms::Element* elementAt(int track)` has been added specifically for QML with code to ensure the C++ ownership of the returned Element* (this was the cause for the crash of the Walk plug-in). - FiguredBass: `Q_INVOKABLE Ms::FiguredBassItem* FiguredBass::addItem()` has been removed; plugin interface for FiguredBass needs to be redesigned anyway. The few occurrences in the supplied plug-ins of the methods whose names did change have been updated.
2014-07-06 01:56:30 +02:00
#endif
2014-07-06 02:14:58 +02:00
return seg;
2012-05-26 14:26:10 +02:00
}
2013-10-02 10:26:09 +02:00
//---------------------------------------------------------
// firstSegmentMM
//---------------------------------------------------------
2017-03-08 13:12:26 +01:00
Segment* Score::firstSegmentMM(SegmentType segType) const
2013-10-02 10:26:09 +02:00
{
Measure* m = firstMeasureMM();
return m ? m->first(segType) : 0;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// 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);
2017-01-18 14:16:33 +01:00
if (mb->type() == ElementType::MEASURE) {
2016-06-03 10:17:06 +02:00
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);
}
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// 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()) {
2017-12-20 16:49:30 +01:00
Spanner* spanner = toSpanner(e);
for (SpannerSegment* ss : spanner->spannerSegments()) {
ss->scanElements(data, func, all);
}
}
}
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// setSelection
//---------------------------------------------------------
void Score::setSelection(const Selection& s)
{
deselectAll();
_selection = s;
2013-04-17 10:31:21 +02:00
2012-05-26 14:26:10 +02:00
foreach(Element* e, _selection.elements())
e->setSelected(true);
}
//---------------------------------------------------------
// getText
//---------------------------------------------------------
2018-08-01 11:46:07 +02:00
Text* Score::getText(Tid tid)
2012-05-26 14:26:10 +02:00
{
2012-11-27 13:19:24 +01:00
MeasureBase* m = first();
2018-08-01 11:46:07 +02:00
if (m && m->isVBox()) {
2017-01-16 20:51:12 +01:00
for (Element* e : m->el()) {
2018-08-01 11:46:07 +02:00
if (e->isText() && toText(e)->tid() == tid)
2016-06-03 10:17:06 +02:00
return toText(e);
2012-05-26 14:26:10 +02:00
}
}
return 0;
}
2013-07-19 10:39:32 +02:00
//---------------------------------------------------------
// metaTag
//---------------------------------------------------------
QString Score::metaTag(const QString& s) const
2013-07-19 10:39:32 +02:00
{
2013-07-19 18:03:35 +02:00
if (_metaTags.contains(s))
return _metaTags.value(s);
2016-03-10 10:41:31 +01:00
return _masterScore->_metaTags.value(s);
2013-07-19 10:39:32 +02:00
}
//---------------------------------------------------------
// setMetaTag
//---------------------------------------------------------
void Score::setMetaTag(const QString& tag, const QString& val)
2013-07-19 10:39:32 +02:00
{
2013-07-19 18:03:35 +02:00
_metaTags.insert(tag, val);
2013-07-19 10:39:32 +02:00
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// addExcerpt
//---------------------------------------------------------
2016-10-10 18:37:28 +02:00
void MasterScore::addExcerpt(Excerpt* ex)
2012-05-26 14:26:10 +02:00
{
2016-10-10 18:37:28 +02:00
Score* score = ex->partScore();
for (Staff* s : score->staves()) {
2018-04-27 13:29:20 +02:00
const LinkedElements* ls = s->links();
2012-05-26 14:26:10 +02:00
if (ls == 0)
continue;
2018-04-27 13:29:20 +02:00
for (auto le : *ls) {
Staff* ps = toStaff(le);
2012-05-26 14:26:10 +02:00
if (ps->score() == this) {
ex->parts().append(ps->part());
break;
}
}
}
2016-10-10 18:37:28 +02:00
if (ex->tracks().isEmpty()) { // SHOULDN'T HAPPEN, protected in the UI
QMultiMap<int, int> tracks;
2016-07-31 15:23:11 +02:00
for (Staff* s : score->staves()) {
2018-04-27 13:29:20 +02:00
const LinkedElements* ls = s->links();
2016-07-31 15:23:11 +02:00
if (ls == 0)
continue;
2018-04-27 13:29:20 +02:00
for (auto le : *ls) {
Staff* ps = toStaff(le);
2016-07-31 15:23:11 +02:00
if (ps->primaryStaff()) {
for (int i = 0; i < VOICES; i++)
tracks.insert(ps->idx() * VOICES + i % VOICES, s->idx() * VOICES + i % VOICES);
break;
}
}
}
ex->setTracks(tracks);
}
2016-10-10 18:37:28 +02:00
excerpts().append(ex);
2012-05-26 14:26:10 +02:00
setExcerptsChanged(true);
}
//---------------------------------------------------------
// removeExcerpt
//---------------------------------------------------------
2016-10-10 18:37:28 +02:00
void MasterScore::removeExcerpt(Excerpt* ex)
2012-05-26 14:26:10 +02:00
{
2016-10-10 18:37:28 +02:00
if (excerpts().removeOne(ex)) {
setExcerptsChanged(true);
// delete ex;
2012-05-26 14:26:10 +02:00
}
2016-10-10 18:37:28 +02:00
else
qDebug("removeExcerpt:: ex not found");
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// clone
//---------------------------------------------------------
2016-03-10 10:41:31 +01:00
MasterScore* MasterScore::clone()
2012-05-26 14:26:10 +02:00
{
QBuffer buffer;
buffer.open(QIODevice::WriteOnly);
2016-11-19 11:51:21 +01:00
XmlWriter xml(this, &buffer);
2012-05-26 14:26:10 +02:00
xml.header();
xml.stag("museScore version=\"" MSC_VERSION "\"");
write(xml, false);
xml.etag();
buffer.close();
XmlReader r(buffer.buffer());
2016-03-10 10:41:31 +01:00
MasterScore* score = new MasterScore(style());
2013-01-11 18:10:18 +01:00
score->read1(r, true);
2012-05-26 14:26:10 +02:00
2016-02-10 13:40:34 +01:00
score->addLayoutFlags(LayoutFlag::FIX_PITCH_VELO);
2012-05-26 14:26:10 +02:00
score->doLayout();
return score;
}
//---------------------------------------------------------
// setSynthesizerState
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
void Score::setSynthesizerState(const SynthesizerState& s)
2012-05-26 14:26:10 +02:00
{
// TODO: make undoable
_synthesizerState = s;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// removeAudio
//---------------------------------------------------------
void Score::removeAudio()
{
delete _audio;
_audio = 0;
}
//---------------------------------------------------------
// isScoreLoaded
//---------------------------------------------------------
bool& Score::isScoreLoaded()
{
static bool scoreLoaded = false;
return scoreLoaded;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// appendScore
//---------------------------------------------------------
bool Score::appendScore(Score* score, bool addPageBreak, bool addSectionBreak)
2012-05-26 14:26:10 +02:00
{
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());
2014-11-28 14:49:46 +01:00
return false;
}
2012-05-26 14:26:10 +02:00
if (!last()) {
qDebug("This score doesn't have any MeasureBase objects.");
return false;
}
TieMap tieMap;
int tickOfAppend = last()->endTick();
2014-11-28 14:49:46 +01:00
// 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
}
2014-11-28 14:49:46 +01:00
}
else if (!last()->lineBreak() && !last()->pageBreak())
last()->undoSetBreak(true, LayoutBreak::Type::LINE);
if (addSectionBreak && !last()->sectionBreak())
last()->undoSetBreak(true, LayoutBreak::Type::SECTION);
2014-11-28 14:49:46 +01:00
// match concert pitch states
2018-03-27 15:36:00 +02:00
if (styleB(Sid::concertPitch) != score->styleB(Sid::concertPitch))
score->cmdConcertPitchChanged(styleB(Sid::concertPitch), true);
// convert any "generated" initial clefs into real "non-generated" clefs if clef type changes
Measure* fm = score->firstMeasure();
if (fm) {
2017-03-08 13:12:26 +01:00
Segment* seg = fm->findSegmentR(SegmentType::HeaderClef, 0);
if (seg) {
for (int staffIdx = 0; staffIdx < score->nstaves(); ++staffIdx) {
int track = staffIdx * VOICES;
Staff* staff = score->staff(staffIdx);
Clef* clef = toClef(seg->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 (clef && clef->generated() && clef->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, seg, clef->clefType());
}
}
}
}
2014-11-28 14:49:46 +01:00
// clone the measures
2012-05-26 14:26:10 +02:00
MeasureBaseList* ml = &score->_measures;
for (MeasureBase* mb = ml->first(); mb; mb = mb->next()) {
MeasureBase* nmb;
2017-01-18 14:16:33 +01:00
if (mb->type() == ElementType::MEASURE)
2016-06-03 10:17:06 +02:00
nmb = toMeasure(mb)->cloneMeasure(this, &tieMap);
2012-05-26 14:26:10 +02:00
else
nmb = mb->clone();
nmb->setNext(0);
nmb->setPrev(0);
nmb->setScore(this);
_measures.add(nmb);
}
fixTicks();
Measure* firstAppendedMeasure = tick2measure(tickOfAppend);
2014-11-28 14:49:46 +01:00
// 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;
2017-03-08 13:12:26 +01:00
for (Segment* s = m->first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
for (int v = 0; v < VOICES; ++v) {
2016-06-03 10:17:06 +02:00
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) {
2017-03-08 13:12:26 +01:00
Segment* seg = firstAppendedMeasure->getSegment(SegmentType::KeySig, tickOfAppend);
for (Staff* st : score->staves()) {
2016-09-28 21:13:05 +02:00
int staffIdx = st->idx();
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);
}
2014-11-28 14:49:46 +01:00
}
}
// clone the spanners
for (auto sp : score->spanner()) {
Spanner* spanner = sp.second;
2017-12-20 16:49:30 +01:00
Spanner* ns = toSpanner(spanner->clone());
2014-11-28 14:49:46 +01:00
ns->setScore(this);
ns->setParent(0);
ns->setTick(spanner->tick() + tickOfAppend);
ns->setTick2(spanner->tick2() + tickOfAppend);
2017-01-18 14:16:33 +01:00
if (ns->type() == ElementType::SLUR) {
2014-11-28 14:49:46 +01:00
// 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);
}
2016-03-02 13:20:19 +01:00
setLayoutAll();
2012-05-26 14:26:10 +02:00
return true;
}
//---------------------------------------------------------
// splitStaff
//---------------------------------------------------------
void Score::splitStaff(int staffIdx, int splitPoint)
{
2016-04-11 15:28:32 +02:00
// qDebug("split staff %d point %d", staffIdx, splitPoint);
2012-05-26 14:26:10 +02:00
//
// create second staff
//
Staff* st = staff(staffIdx);
Part* p = st->part();
2014-08-16 13:32:08 +02:00
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);
2012-05-26 14:26:10 +02:00
Clef* clef = new Clef(this);
2013-09-05 16:37:49 +02:00
clef->setClefType(ClefType::F);
2012-05-26 14:26:10 +02:00
clef->setTrack((staffIdx+1) * VOICES);
2017-03-08 13:12:26 +01:00
Segment* seg = firstMeasure()->getSegment(SegmentType::HeaderClef, 0);
2012-05-26 14:26:10 +02:00
clef->setParent(seg);
undoAddElement(clef);
2016-01-04 14:48:58 +01:00
clef->layout();
2012-05-26 14:26:10 +02:00
undoChangeKeySig(ns, 0, st->keySigEvent(0));
2012-05-26 14:26:10 +02:00
2016-03-11 12:18:46 +01:00
masterScore()->rebuildMidiMapping();
2016-03-18 09:29:16 +01:00
cmdState()._instrumentsChanged = true;
2012-05-26 14:26:10 +02:00
doLayout();
//
// move notes
//
select(0, SelectType::SINGLE, 0);
2012-05-26 14:26:10 +02:00
int strack = staffIdx * VOICES;
int dtrack = (staffIdx + 1) * VOICES;
2017-03-08 13:12:26 +01:00
for (Segment* s = firstSegment(SegmentType::ChordRest); s; s = s->next1(SegmentType::ChordRest)) {
2012-05-26 14:26:10 +02:00
for (int voice = 0; voice < VOICES; ++voice) {
2016-06-03 10:49:40 +02:00
Element* e = s->element(strack + voice);
if (!(e && e->isChord()))
2012-05-26 14:26:10 +02:00
continue;
2016-06-03 10:49:40 +02:00
Chord* c = toChord(e);
2012-05-26 14:26:10 +02:00
QList<Note*> removeNotes;
foreach(Note* note, c->notes()) {
if (note->pitch() >= splitPoint)
continue;
2016-06-03 10:17:06 +02:00
Chord* chord = toChord(s->element(dtrack + voice));
Q_ASSERT(!chord || (chord->isChord()));
2012-05-26 14:26:10 +02:00
if (chord == 0) {
chord = new Chord(*c);
2014-08-16 16:10:47 +02:00
qDeleteAll(chord->notes());
2012-05-26 14:26:10 +02:00
chord->notes().clear();
chord->setTrack(dtrack + voice);
undoAddElement(chord);
}
Note* nnote = new Note(*note);
nnote->setTrack(dtrack + voice);
chord->add(nnote);
nnote->updateLine();
2012-05-26 14:26:10 +02:00
removeNotes.append(note);
}
2014-04-09 16:09:21 +02:00
c->sortNotes();
for (Note* note : removeNotes) {
2012-05-26 14:26:10 +02:00
undoRemoveElement(note);
Chord* chord = note->chord();
2016-02-06 22:03:43 +01:00
if (chord->notes().empty()) {
for (auto sp : spanner()) {
2017-12-20 16:49:30 +01:00
Slur* slur = toSlur(sp.second);
2017-01-18 14:16:33 +01:00
if (slur->type() != ElementType::SLUR)
continue;
if (slur->startCR() == chord) {
2018-03-27 15:36:00 +02:00
slur->undoChangeProperty(Pid::TRACK, slur->track()+VOICES);
for (ScoreElement* ee : slur->linkList()) {
2017-12-20 16:49:30 +01:00
Slur* lslur = toSlur(ee);
lslur->setStartElement(0);
}
}
if (slur->endCR() == chord) {
2018-03-27 15:36:00 +02:00
slur->undoChangeProperty(Pid::SPANNER_TRACK2, slur->track2()+VOICES);
for (ScoreElement* ee : slur->linkList()) {
2017-12-20 16:49:30 +01:00
Slur* lslur = toSlur(ee);
lslur->setEndElement(0);
}
}
}
undoRemoveElement(chord);
}
2012-05-26 14:26:10 +02:00
}
}
}
//
// make sure that the timeline for dtrack
// has no gaps
//
int ctick = 0;
for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
2017-03-08 13:12:26 +01:00
for (Segment* s = m->first(SegmentType::ChordRest); s; s = s->next1(SegmentType::ChordRest)) {
2016-06-03 10:17:06 +02:00
ChordRest* cr = toChordRest(s->element(dtrack));
2012-05-26 14:26:10 +02:00
if (cr == 0)
continue;
int rest = s->tick() - ctick;
if (rest) {
// insert Rest
Segment* s1 = tick2segment(ctick);
if (s1 == 0) {
2013-07-25 17:22:49 +02:00
qDebug("no segment at %d", ctick);
2012-05-26 14:26:10 +02:00
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()) {
2017-03-08 13:12:26 +01:00
for (Segment* s = m->first(SegmentType::ChordRest); s; s = s->next1(SegmentType::ChordRest)) {
2016-06-03 10:17:06 +02:00
ChordRest* cr = toChordRest(s->element(strack));
2012-05-26 14:26:10 +02:00
if (cr == 0)
continue;
int rest = s->tick() - ctick;
if (rest) {
// insert Rest
Segment* s1 = tick2segment(ctick);
if (s1 == 0) {
2013-07-25 17:22:49 +02:00
qDebug("no segment at %d", ctick);
2012-05-26 14:26:10 +02:00
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();
2014-08-16 14:18:06 +02:00
2012-05-26 14:26:10 +02:00
for (int i = 0; i < n; ++i)
cmdRemoveStaff(sidx);
2012-05-26 14:26:10 +02:00
undoRemovePart(part, sidx);
}
//---------------------------------------------------------
// insertPart
//---------------------------------------------------------
void Score::insertPart(Part* part, int idx)
{
int staff = 0;
for (QList<Part*>::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
//---------------------------------------------------------
2014-08-11 15:25:55 +02:00
void Score::insertStaff(Staff* staff, int ridx)
2012-05-26 14:26:10 +02:00
{
2014-08-11 15:25:55 +02:00
staff->part()->insertStaff(staff, ridx);
2014-08-16 13:32:08 +02:00
2014-08-16 14:18:06 +02:00
int idx = staffIdx(staff->part()) + ridx;
_staves.insert(idx, staff);
2014-08-16 13:32:08 +02:00
for (auto i = staff->score()->spanner().cbegin(); i != staff->score()->spanner().cend(); ++i) {
Spanner* s = i->second;
2017-06-02 10:27:32 +02:00
if (s->systemFlag())
continue;
if (s->staffIdx() >= idx) {
int t = s->track() + VOICES;
if (t >= ntracks())
t = ntracks() - 1;
s->setTrack(t);
for (SpannerSegment* ss : s->spannerSegments())
ss->setTrack(t);
if (s->track2() != -1) {
t = s->track2() + VOICES;
s->setTrack2(t < ntracks() ? t : s->track());
}
}
}
#if 0
for (Spanner* s : staff->score()->unmanagedSpanners()) {
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());
}
}
}
2017-06-02 10:27:32 +02:00
#endif
2012-05-26 14:26:10 +02:00
}
2014-08-16 14:18:06 +02:00
//---------------------------------------------------------
// removeStaff
//---------------------------------------------------------
void Score::removeStaff(Staff* staff)
{
2016-09-28 21:13:05 +02:00
int idx = staff->idx();
2014-08-16 14:18:06 +02:00
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;
2017-06-02 10:27:32 +02:00
if (t < 0)
t = 0;
s->setTrack(t);
for (SpannerSegment* ss : s->spannerSegments())
ss->setTrack(t);
2014-08-16 14:18:06 +02:00
if (s->track2() != -1) {
t = s->track2() - VOICES;
2017-06-02 10:27:32 +02:00
s->setTrack2(t >= 0 ? t : s->track());
2014-08-16 14:18:06 +02:00
}
}
}
2017-06-02 10:27:32 +02:00
#if 0
for (Spanner* s : staff->score()->unmanagedSpanners()) {
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());
}
}
}
#endif
2014-08-16 14:18:06 +02:00
_staves.removeAll(staff);
staff->part()->removeStaff(staff);
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// adjustBracketsDel
//---------------------------------------------------------
void Score::adjustBracketsDel(int sidx, int eidx)
{
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
Staff* staff = _staves[staffIdx];
2017-03-31 13:03:15 +02:00
for (BracketItem* bi : staff->brackets()) {
int span = bi->bracketSpan();
2012-05-26 14:26:10 +02:00
if ((span == 0) || ((staffIdx + span) < sidx) || (staffIdx > eidx))
continue;
if ((sidx >= staffIdx) && (eidx <= (staffIdx + span)))
2018-03-27 15:36:00 +02:00
bi->undoChangeProperty(Pid::BRACKET_SPAN, span - (eidx-sidx));
2012-05-26 14:26:10 +02:00
}
2016-01-04 14:48:58 +01:00
#if 0 // TODO
2016-02-09 13:51:19 +01:00
int span = staff->barLineSpan();
2012-10-14 00:35:11 +02:00
if ((sidx >= staffIdx) && (eidx <= (staffIdx + span))) {
int newSpan = span - (eidx-sidx) + 1;
2012-10-14 00:35:11 +02:00
int lastSpannedStaffIdx = staffIdx + newSpan - 1;
2017-03-31 13:03:15 +02:00
int tick = 0;
undoChangeBarLineSpan(staff, newSpan, 0, (_staves[lastSpannedStaffIdx]->lines(0)-1)*2);
2012-10-14 00:35:11 +02:00
}
2016-01-04 14:48:58 +01:00
#endif
2012-05-26 14:26:10 +02:00
}
}
//---------------------------------------------------------
// adjustBracketsIns
//---------------------------------------------------------
void Score::adjustBracketsIns(int sidx, int eidx)
{
for (int staffIdx = 0; staffIdx < _staves.size(); ++staffIdx) {
Staff* staff = _staves[staffIdx];
2017-03-31 13:03:15 +02:00
for (BracketItem* bi : staff->brackets()) {
int span = bi->bracketSpan();
2012-05-26 14:26:10 +02:00
if ((span == 0) || ((staffIdx + span) < sidx) || (staffIdx > eidx))
continue;
if ((sidx >= staffIdx) && (eidx < (staffIdx + span)))
2018-03-27 15:36:00 +02:00
bi->undoChangeProperty(Pid::BRACKET_SPAN, span + (eidx-sidx));
2012-05-26 14:26:10 +02:00
}
2016-02-09 13:51:19 +01:00
#if 0 // TODO
2012-05-26 14:26:10 +02:00
int span = staff->barLineSpan();
2014-08-16 17:54:10 +02:00
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);
}
2016-01-04 14:48:58 +01:00
#endif
2012-05-26 14:26:10 +02:00
}
}
//---------------------------------------------------------
// adjustKeySigs
//---------------------------------------------------------
void Score::adjustKeySigs(int sidx, int eidx, KeyList km)
{
for (int staffIdx = sidx; staffIdx < eidx; ++staffIdx) {
Staff* staff = _staves[staffIdx];
2014-06-05 11:37:21 +02:00
for (auto i = km.begin(); i != km.end(); ++i) {
int tick = i->first;
Measure* measure = tick2measure(tick);
if (!measure)
continue;
2016-12-13 13:16:17 +01:00
if (staff->isDrumStaff(tick))
continue;
KeySigEvent oKey = i->second;
KeySigEvent nKey = oKey;
int diff = -staff->part()->instrument(tick)->transpose().chromatic;
2018-03-27 15:36:00 +02:00
if (diff != 0 && !styleB(Sid::concertPitch) && !oKey.custom() && !oKey.isAtonal())
nKey.setKey(transposeKey(nKey.key(), diff));
2014-06-05 11:37:21 +02:00
staff->setKey(tick, nKey);
KeySig* keysig = new KeySig(this);
keysig->setTrack(staffIdx * VOICES);
keysig->setKeySigEvent(nKey);
2017-03-08 13:12:26 +01:00
Segment* s = measure->getSegment(SegmentType::KeySig, tick);
2014-06-05 11:37:21 +02:00
s->add(keysig);
}
}
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// cmdRemoveStaff
//---------------------------------------------------------
void Score::cmdRemoveStaff(int staffIdx)
{
Staff* s = staff(staffIdx);
adjustBracketsDel(staffIdx, staffIdx+1);
2013-09-19 18:24:58 +02:00
QList<Spanner*> sl;
for (auto i = _spanner.cbegin(); i != _spanner.cend(); ++i) {
Spanner* sp = i->second;
if (sp->staffIdx() == staffIdx && (staffIdx != 0 || !sp->systemFlag()))
sl.append(sp);
2013-09-19 18:24:58 +02:00
}
for (Spanner* sp : _unmanagedSpanner) {
if (sp->staffIdx() == staffIdx && (staffIdx != 0 || !sp->systemFlag()))
sl.append(sp);
2017-06-02 10:27:32 +02:00
}
2014-08-07 11:33:05 +02:00
for (auto i : sl) {
printf("remove %p <%s>\n", i, i->name());
2014-08-07 11:33:05 +02:00
i->undoUnlink();
undo(new RemoveElement(i));
}
2013-09-19 18:24:58 +02:00
2014-08-11 15:25:55 +02:00
undoRemoveStaff(s);
// remove linked staff and measures in linked staves in excerpts
// unlink staff in the same score
2018-04-27 13:29:20 +02:00
if (s->links()) {
Staff* sameScoreLinkedStaff = 0;
auto staves = s->links();
for (auto le : *staves) {
Staff* staff = toStaff(le);
if (staff == s)
continue;
Score* lscore = staff->score();
if (lscore != this) {
lscore->undoRemoveStaff(staff);
2018-04-27 13:29:20 +02:00
s->score()->undo(new Unlink(staff));
2014-08-25 19:30:56 +02:00
if (staff->part()->nstaves() == 0) {
int pIndex = lscore->staffIdx(staff->part());
lscore->undoRemovePart(staff->part(), pIndex);
2014-08-25 19:30:56 +02:00
}
}
else // linked staff in the same score
2016-03-21 18:39:29 +01:00
sameScoreLinkedStaff = staff;
}
2016-03-21 18:39:29 +01:00
if (sameScoreLinkedStaff)
2018-04-27 13:29:20 +02:00
// s->score()->undo(new Unlink(sameScoreLinkedStaff)); // once should be enough
s->score()->undo(new Unlink(s)); // once should be enough
}
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// sortStaves
//---------------------------------------------------------
void Score::sortStaves(QList<int>& dst)
{
2016-02-04 11:27:47 +01:00
systems().clear(); //??
2012-05-26 14:26:10 +02:00
_parts.clear();
Part* curPart = 0;
QList<Staff*> dl;
foreach (int idx, dst) {
2012-05-26 14:26:10 +02:00
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()) {
2012-05-26 14:26:10 +02:00
m->sortStaves(dst);
if (m->hasMMRest())
m->mmRest()->sortStaves(dst);
2012-05-26 14:26:10 +02:00
}
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...
}
}
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// cmdConcertPitchChanged
//---------------------------------------------------------
2014-04-09 14:13:22 +02:00
void Score::cmdConcertPitchChanged(bool flag, bool /*useDoubleSharpsFlats*/)
2012-05-26 14:26:10 +02:00
{
2018-03-27 15:36:00 +02:00
undoChangeStyleVal(Sid::concertPitch, flag); // change style flag
2012-05-26 14:26:10 +02:00
2014-04-02 18:11:56 +02:00
for (Staff* staff : _staves) {
2016-12-13 13:16:17 +01:00
if (staff->staffType(0)->group() == StaffGroup::PERCUSSION) // TODO
2012-05-26 14:26:10 +02:00
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)
2012-05-26 14:26:10 +02:00
continue;
if (!flag)
interval.flip();
2014-04-02 18:11:56 +02:00
int staffIdx = staff->idx();
2014-04-02 18:11:56 +02:00
int startTrack = staffIdx * VOICES;
int endTrack = startTrack + VOICES;
transposeKeys(staffIdx, staffIdx + 1, 0, lastSegment()->tick(), interval, true, !flag);
2014-04-02 18:11:56 +02:00
2017-03-08 13:12:26 +01:00
for (Segment* segment = firstSegment(SegmentType::ChordRest); segment; segment = segment->next1(SegmentType::ChordRest)) {
interval = staff->part()->instrument(segment->tick())->transpose();
if (!flag)
interval.flip();
2014-04-02 18:11:56 +02:00
for (Element* e : segment->annotations()) {
if (!e->isHarmony() || (e->track() < startTrack) || (e->track() >= endTrack))
2014-04-02 18:11:56 +02:00
continue;
2016-06-03 10:17:06 +02:00
Harmony* h = toHarmony(e);
int rootTpc = transposeTpc(h->rootTpc(), interval, true);
int baseTpc = transposeTpc(h->baseTpc(), interval, true);
for (ScoreElement* se : h->linkList()) {
// don't transpose all links
// just ones resulting from mmrests
Harmony* he = toHarmony(se); // toHarmony() does not work as e is an ScoreElement
if (he->staff() == h->staff())
undoTransposeHarmony(he, rootTpc, baseTpc);
}
2014-04-02 18:11:56 +02:00
}
2012-05-26 14:26:10 +02:00
}
}
}
//---------------------------------------------------------
// addAudioTrack
//---------------------------------------------------------
void Score::addAudioTrack()
{
// TODO
}
//---------------------------------------------------------
// padToggle
//---------------------------------------------------------
void Score::padToggle(Pad n)
2012-05-26 14:26:10 +02:00
{
2016-08-08 17:45:53 +02:00
int oldDots = _is.duration().dots();
2012-05-26 14:26:10 +02:00
switch (n) {
case Pad::NOTE00:
_is.setDuration(TDuration::DurationType::V_LONG);
2012-05-26 14:26:10 +02:00
break;
case Pad::NOTE0:
_is.setDuration(TDuration::DurationType::V_BREVE);
2012-05-26 14:26:10 +02:00
break;
case Pad::NOTE1:
_is.setDuration(TDuration::DurationType::V_WHOLE);
2012-05-26 14:26:10 +02:00
break;
case Pad::NOTE2:
_is.setDuration(TDuration::DurationType::V_HALF);
2012-05-26 14:26:10 +02:00
break;
case Pad::NOTE4:
_is.setDuration(TDuration::DurationType::V_QUARTER);
2012-05-26 14:26:10 +02:00
break;
case Pad::NOTE8:
_is.setDuration(TDuration::DurationType::V_EIGHTH);
2012-05-26 14:26:10 +02:00
break;
case Pad::NOTE16:
_is.setDuration(TDuration::DurationType::V_16TH);
2012-05-26 14:26:10 +02:00
break;
case Pad::NOTE32:
_is.setDuration(TDuration::DurationType::V_32ND);
2012-05-26 14:26:10 +02:00
break;
case Pad::NOTE64:
_is.setDuration(TDuration::DurationType::V_64TH);
2012-05-26 14:26:10 +02:00
break;
case Pad::NOTE128:
_is.setDuration(TDuration::DurationType::V_128TH);
2012-05-26 14:26:10 +02:00
break;
case Pad::REST:
2013-10-24 12:09:00 +02:00
_is.setRest(!_is.rest());
2012-05-26 14:26:10 +02:00
break;
case Pad::DOT:
if ((_is.duration().dots() == 1) || (_is.duration() == TDuration::DurationType::V_1024TH))
2012-05-26 14:26:10 +02:00
_is.setDots(0);
else
_is.setDots(1);
break;
case Pad::DOTDOT:
if ((_is.duration().dots() == 2)
|| (_is.duration() == TDuration::DurationType::V_512TH)
|| (_is.duration() == TDuration::DurationType::V_1024TH))
2012-05-26 14:26:10 +02:00
_is.setDots(0);
else
_is.setDots(2);
break;
case Pad::DOT3:
if ((_is.duration().dots() == 3)
|| (_is.duration() == TDuration::DurationType::V_256TH)
|| (_is.duration() == TDuration::DurationType::V_512TH)
|| (_is.duration() == TDuration::DurationType::V_1024TH))
_is.setDots(0);
else
_is.setDots(3);
break;
case Pad::DOT4:
if ((_is.duration().dots() == 4)
|| (_is.duration() == TDuration::DurationType::V_128TH)
|| (_is.duration() == TDuration::DurationType::V_256TH)
|| (_is.duration() == TDuration::DurationType::V_512TH)
|| (_is.duration() == TDuration::DurationType::V_1024TH))
_is.setDots(0);
else
_is.setDots(4);
break;
2012-05-26 14:26:10 +02:00
}
if (n >= Pad::NOTE00 && n <= Pad::NOTE128) {
2012-05-26 14:26:10 +02:00
_is.setDots(0);
//
// if in "note enter" mode, reset
// rest flag
//
2016-08-08 17:45:53 +02:00
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);
}
2012-05-26 14:26:10 +02:00
}
if (noteEntryMode() || !selection().isSingle())
2012-05-26 14:26:10 +02:00
return;
//do not allow to add a dot on a full measure rest
Element* e = selection().element();
2016-08-03 17:29:52 +02:00
if (e && e->isRest()) {
2016-06-03 10:17:06 +02:00
Rest* r = toRest(e);
2016-08-03 17:29:52 +02:00
if (r->isFullMeasureRest())
2012-05-26 14:26:10 +02:00
_is.setDots(0);
}
// on measure rest, select the first actual rest
2014-11-22 13:05:23 +01:00
ChordRest* cr = selection().cr();
if (cr && cr->isRest() && cr->measure()->isMMRest()) {
Measure* m = cr->measure()->mmRestFirst();
if (m)
cr = m->findChordRest(0, 0);
}
2014-11-22 13:05:23 +01:00
if (!cr)
2012-05-26 14:26:10 +02:00
return;
2016-08-03 17:29:52 +02:00
if (cr->isChord() && (toChord(cr)->isGrace())) {
2012-05-26 14:26:10 +02:00
//
// handle appoggiatura and acciaccatura
//
undoChangeChordRestLen(cr, _is.duration());
2012-05-26 14:26:10 +02:00
}
else
changeCRlen(cr, _is.duration());
}
//---------------------------------------------------------
// deselect
//---------------------------------------------------------
void Score::deselect(Element* el)
{
2016-03-18 09:29:16 +01:00
addRefresh(el->abbox());
2012-05-26 14:26:10 +02:00
_selection.remove(el);
setSelectionChanged(true);
_selection.update();
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// select
// staffIdx is valid, if element is of type MEASURE
//---------------------------------------------------------
void Score::select(Element* e, SelectType type, int staffIdx)
{
if (e && (e->isNote() || e->isRest())) {
2012-05-26 14:26:10 +02:00
Element* ee = e;
if (ee->isNote())
2012-05-26 14:26:10 +02:00
ee = ee->parent();
2016-06-03 10:17:06 +02:00
int tick = toChordRest(ee)->segment()->tick();
2014-11-17 16:44:05 +01:00
if (playPos() != tick)
setPlayPos(tick);
2012-05-26 14:26:10 +02:00
}
if (MScore::debugMode)
2015-10-27 11:30:09 +01:00
qDebug("select element <%s> type %d(state %d) staff %d",
e ? e->name() : "", int(type), int(selection().state()), e ? e->staffIdx() : -1);
2012-05-26 14:26:10 +02:00
2014-05-24 22:24:48 +02:00
switch (type) {
case SelectType::SINGLE:
selectSingle(e, staffIdx);
break;
case SelectType::ADD:
selectAdd(e);
break;
case SelectType::RANGE:
selectRange(e, staffIdx);
break;
2014-05-24 22:24:48 +02:00
}
2017-03-31 13:03:15 +02:00
setSelectionChanged(true);
2014-05-24 22:24:48 +02:00
}
2012-05-26 14:26:10 +02:00
2014-05-24 22:24:48 +02:00
//---------------------------------------------------------
// 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;
2016-03-02 13:20:19 +01:00
setUpdateAll();
2014-05-24 22:24:48 +02:00
}
else {
2017-03-31 13:03:15 +02:00
if (e->isMeasure()) {
2014-05-24 22:24:48 +02:00
select(e, SelectType::RANGE, staffIdx);
return;
2012-05-26 14:26:10 +02:00
}
2016-03-18 09:29:16 +01:00
addRefresh(e->abbox());
2014-05-24 22:24:48 +02:00
_selection.add(e);
_is.setTrack(e->track());
selState = SelState::LIST;
2017-01-18 14:16:33 +01:00
if (e->type() == ElementType::NOTE) {
2014-05-24 22:24:48 +02:00
e = e->parent();
}
2017-03-31 13:03:15 +02:00
if (e->isChordRest()) {
2014-05-24 22:24:48 +02:00
_is.setLastSegment(_is.segment());
2016-06-03 10:17:06 +02:00
_is.setSegment(toChordRest(e)->segment());
2012-05-26 14:26:10 +02:00
}
}
2014-05-24 22:24:48 +02:00
_selection.setActiveSegment(0);
_selection.setActiveTrack(0);
_selection.setState(selState);
}
//---------------------------------------------------------
// switchToPageMode
//---------------------------------------------------------
void Score::switchToPageMode()
{
if (_layoutMode != LayoutMode::PAGE) {
setLayoutMode(LayoutMode::PAGE);
doLayout();
}
}
2014-05-24 22:24:48 +02:00
//---------------------------------------------------------
// selectAdd
//---------------------------------------------------------
void Score::selectAdd(Element* e)
{
SelState selState = _selection.state();
if (_selection.isRange()) {
select(0, SelectType::SINGLE, 0);
return;
}
if (e->isMeasure()) {
2016-06-03 10:17:06 +02:00
Measure* m = toMeasure(e);
2014-05-24 22:24:48 +02:00
int tick = m->tick();
if (_selection.isNone()) {
_selection.setRange(m->tick2segment(tick),
m == lastMeasure() ? 0 : m->last(),
0,
nstaves());
setUpdateAll();
selState = SelState::RANGE;
_selection.updateSelectedElements();
2012-05-26 14:26:10 +02:00
}
}
2014-05-24 22:24:48 +02:00
else { // None or List
2016-03-18 09:29:16 +01:00
addRefresh(e->abbox());
2014-05-24 22:24:48 +02:00
if (_selection.elements().contains(e))
_selection.remove(e);
else {
selState = SelState::LIST;
_selection.add(e);
}
2014-05-24 22:24:48 +02:00
}
_selection.setState(selState);
}
2014-05-24 12:53:50 +02:00
2014-05-24 22:24:48 +02:00
//---------------------------------------------------------
// 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;
2017-01-18 14:16:33 +01:00
if (e->type() == ElementType::MEASURE) {
2016-06-03 10:17:06 +02:00
Measure* m = toMeasure(e);
2014-05-24 22:24:48 +02:00
int tick = m->tick();
int etick = tick + m->ticks();
activeTrack = staffIdx * VOICES;
if (_selection.isNone()
|| (_selection.isList() && !_selection.isSingle())) {
2014-05-24 12:53:50 +02:00
if (_selection.isList())
deselectAll();
2014-05-24 22:24:48 +02:00
_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);
2014-05-24 22:24:48 +02:00
}
else if (_selection.isSingle()) {
Element* oe = selection().element();
2017-01-18 14:16:33 +01:00
if (oe->isNote() || oe->isChordRest()) {
if (oe->isNote())
oe = oe->parent();
2016-06-03 10:17:06 +02:00
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)
2017-03-08 13:12:26 +01:00
endSegment = cr->nextSegmentAfterCR(SegmentType::ChordRest
| SegmentType::EndBarLine
| SegmentType::Clef);
2014-08-11 15:25:55 +02:00
}
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);
2012-05-26 14:26:10 +02:00
}
else {
deselectAll();
_selection.setRange(m->tick2segment(tick),
m == lastMeasure() ? 0 : m->last(),
staffIdx,
staffIdx + 1);
2012-05-26 14:26:10 +02:00
}
2014-05-24 22:24:48 +02:00
}
else {
2015-10-27 11:30:09 +01:00
qDebug("SELECT_RANGE: measure: sel state %d", int(_selection.state()));
2014-05-24 22:24:48 +02:00
return;
2012-05-26 14:26:10 +02:00
}
2014-05-24 22:24:48 +02:00
}
2017-01-18 14:16:33 +01:00
else if (e->type() == ElementType::NOTE || e->isChordRest()) {
if (e->type() == ElementType::NOTE)
2014-05-24 22:24:48 +02:00
e = e->parent();
2016-06-03 10:17:06 +02:00
ChordRest* cr = toChordRest(e);
2012-05-26 14:26:10 +02:00
2014-05-31 21:14:25 +02:00
if (_selection.isNone()
|| (_selection.isList() && !_selection.isSingle())) {
if (_selection.isList())
deselectAll();
_selection.setRange(cr->segment(),
2017-03-08 13:12:26 +01:00
cr->nextSegmentAfterCR(SegmentType::ChordRest
| SegmentType::EndBarLine
| SegmentType::Clef),
e->staffIdx(),
e->staffIdx() + 1);
2014-05-24 22:24:48 +02:00
activeTrack = cr->track();
}
else if (_selection.isSingle()) {
Element* oe = _selection.element();
2017-01-18 14:16:33 +01:00
if (oe && (oe->type() == ElementType::NOTE || oe->type() == ElementType::REST)) {
if (oe->type() == ElementType::NOTE)
2014-05-24 22:24:48 +02:00
oe = oe->parent();
2016-06-03 10:17:06 +02:00
ChordRest* ocr = toChordRest(oe);
2014-05-31 21:14:25 +02:00
Segment* endSeg = tick2segmentMM(ocr->segment()->tick() + ocr->actualTicks());
2014-05-31 21:14:25 +02:00
if (!endSeg)
endSeg = ocr->segment()->next();
_selection.setRange(ocr->segment(),
2014-05-31 21:14:25 +02:00
endSeg,
oe->staffIdx(),
oe->staffIdx() + 1);
2014-05-31 21:14:25 +02:00
_selection.extendRangeSelection(cr);
2012-05-26 14:26:10 +02:00
}
else {
2014-05-24 22:24:48 +02:00
select(e, SelectType::SINGLE, 0);
return;
2012-05-26 14:26:10 +02:00
}
2014-05-24 22:24:48 +02:00
}
else if (_selection.isRange()) {
2014-05-31 21:14:25 +02:00
_selection.extendRangeSelection(cr);
2012-05-26 14:26:10 +02:00
}
else {
2015-10-27 11:30:09 +01:00
qDebug("sel state %d", int(_selection.state()));
2012-05-26 14:26:10 +02:00
return;
}
if (!endRangeSelected && !_selection.endSegment())
2014-05-24 22:24:48 +02:00
_selection.setEndSegment(cr->segment()->nextCR());
if (!_selection.startSegment())
_selection.setStartSegment(cr->segment());
}
else {
select(e, SelectType::SINGLE, staffIdx);
return;
}
2012-05-26 14:26:10 +02:00
2014-05-24 22:24:48 +02:00
_selection.setActiveTrack(activeTrack);
2012-05-26 14:26:10 +02:00
// doing this in note entry mode can clear selection
if (_selection.startSegment() && !noteEntryMode())
setPlayPos(_selection.startSegment()->tick());
2014-05-24 22:24:48 +02:00
_selection.updateSelectedElements();
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// collectMatch
//---------------------------------------------------------
void Score::collectMatch(void* data, Element* e)
{
ElementPattern* p = static_cast<ElementPattern*>(data);
if (p->type != int(e->type()))
return;
2017-01-18 14:16:33 +01:00
if (p->type == int(ElementType::NOTE)) {
if (p->subtype < 0) {
if (!(toNote(e)->chord()->isGrace()))
return;
}
else if ((toNote(e)->chord()->isGrace()) || (p->subtype != e->subtype()))
return;
}
else if (p->subtypeValid && p->subtype != e->subtype())
return;
2014-09-23 18:07:32 +02:00
if ((p->staffStart != -1)
&& ((p->staffStart > e->staffIdx()) || (p->staffEnd <= e->staffIdx())))
return;
if (p->voice != -1 && p->voice != e->voice())
return;
if (p->system) {
Element* ee = e;
do {
2017-01-18 14:16:33 +01:00
if (ee->type() == ElementType::SYSTEM) {
if (p->system != ee)
return;
break;
}
ee = ee->parent();
} while (ee);
}
if (e->isRest()) {
const Rest* r = toRest(e);
if (p->durationTicks != r->actualTicks())
return;
}
p->el.append(e);
}
//---------------------------------------------------------
// collectNoteMatch
//---------------------------------------------------------
void Score::collectNoteMatch(void* data, Element* e)
{
NotePattern* p = static_cast<NotePattern*>(data);
if (!e->isNote())
return;
Note* n = toNote(e);
if (p->type != NoteType::INVALID && p->type != n->noteType())
return;
if (p->pitch != -1 && p->pitch != n->pitch())
return;
if (p->string != STRING_NONE && p->string != n->string())
return;
if (p->tpc != Tpc::TPC_INVALID && p->tpc != n->tpc())
return;
if (p->notehead != NoteHead::Group::HEAD_INVALID && p->notehead != n->headGroup())
return;
if (p->duration.type() != TDuration::DurationType::V_INVALID && p->duration != n->chord()->actualDurationType())
return;
if ((p->staffStart != -1)
&& ((p->staffStart > e->staffIdx()) || (p->staffEnd <= e->staffIdx())))
return;
if (p->voice != -1 && p->voice != e->voice())
return;
if (p->system && (p->system != n->chord()->segment()->system()))
return;
p->el.append(n);
}
2014-08-15 17:20:20 +02:00
//---------------------------------------------------------
// selectSimilar
//---------------------------------------------------------
void Score::selectSimilar(Element* e, bool sameStaff)
{
2017-01-18 14:16:33 +01:00
ElementType type = e->type();
2014-08-15 17:20:20 +02:00
Score* score = e->score();
ElementPattern pattern;
2014-09-23 18:07:32 +02:00
pattern.type = int(type);
pattern.subtype = 0;
pattern.subtypeValid = false;
2017-01-18 14:16:33 +01:00
if (type == ElementType::NOTE) {
if (toNote(e)->chord()->isGrace())
pattern.subtype = -1; // hack
else
pattern.subtype = e->subtype();
2014-09-23 18:07:32 +02:00
}
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* ee : pattern.el)
score->select(ee, SelectType::ADD, 0);
}
2014-08-15 17:20:20 +02:00
//---------------------------------------------------------
// selectSimilarInRange
//---------------------------------------------------------
void Score::selectSimilarInRange(Element* e)
{
2017-01-18 14:16:33 +01:00
ElementType type = e->type();
Score* score = e->score();
ElementPattern pattern;
pattern.type = int(type);
pattern.subtype = 0;
pattern.subtypeValid = false;
2017-01-18 14:16:33 +01:00
if (type == ElementType::NOTE) {
if (toNote(e)->chord()->isGrace())
pattern.subtype = -1; //hack
else
pattern.subtype = e->subtype();
pattern.subtypeValid = true;
}
pattern.staffStart = selection().staffStart();
pattern.staffEnd = selection().staffEnd();
pattern.voice = -1;
pattern.system = 0;
score->scanElementsInRange(&pattern, collectMatch);
score->select(0, SelectType::SINGLE, 0);
for (Element* ee : pattern.el)
score->select(ee, SelectType::ADD, 0);
}
2014-08-15 17:20:20 +02:00
//---------------------------------------------------------
2012-05-26 14:26:10 +02:00
// lassoSelect
//---------------------------------------------------------
void Score::lassoSelect(const QRectF& bbox)
{
select(0, SelectType::SINGLE, 0);
2012-05-26 14:26:10 +02:00
QRectF fr(bbox.normalized());
2017-01-05 11:23:47 +01:00
foreach(Page* page, pages()) {
2012-05-26 14:26:10 +02:00
QRectF pr(page->bbox());
QRectF frr(fr.translated(-page->pos()));
if (pr.right() < frr.left())
continue;
if (pr.left() > frr.right())
break;
QList<Element*> el = page->items(frr);
2012-05-26 14:26:10 +02:00
for (int i = 0; i < el.size(); ++i) {
Element* e = el.at(i);
2012-05-26 14:26:10 +02:00
if (frr.contains(e->abbox())) {
2017-01-18 14:16:33 +01:00
if (e->type() != ElementType::MEASURE && e->selectable())
select(e, SelectType::ADD, 0);
2012-05-26 14:26:10 +02:00
}
}
}
}
//---------------------------------------------------------
// lassoSelectEnd
//---------------------------------------------------------
void Score::lassoSelectEnd()
{
int noteRestCount = 0;
Segment* startSegment = 0;
Segment* endSegment = 0;
int startStaff = 0x7fffffff;
int endStaff = 0;
const ChordRest* endCR = 0;
2012-05-26 14:26:10 +02:00
2016-02-06 22:03:43 +01:00
if (_selection.elements().empty()) {
2014-05-26 13:21:04 +02:00
_selection.setState(SelState::NONE);
2016-03-02 13:20:19 +01:00
setUpdateAll();
2012-05-26 14:26:10 +02:00
return;
}
2014-05-26 13:21:04 +02:00
_selection.setState(SelState::LIST);
2012-05-26 14:26:10 +02:00
foreach(const Element* e, _selection.elements()) {
2017-01-18 14:16:33 +01:00
if (e->type() != ElementType::NOTE && e->type() != ElementType::REST)
2012-05-26 14:26:10 +02:00
continue;
++noteRestCount;
2017-01-18 14:16:33 +01:00
if (e->type() == ElementType::NOTE)
2012-05-26 14:26:10 +02:00
e = e->parent();
Segment* seg = static_cast<const ChordRest*>(e)->segment();
2012-08-09 12:12:43 +02:00
if ((startSegment == 0) || (*seg < *startSegment))
2012-05-26 14:26:10 +02:00
startSegment = seg;
2012-08-09 12:12:43 +02:00
if ((endSegment == 0) || (*seg > *endSegment)) {
2012-05-26 14:26:10 +02:00
endSegment = seg;
2014-05-31 17:22:45 +02:00
endCR = static_cast<const ChordRest*>(e);
2012-05-26 14:26:10 +02:00
}
int idx = e->staffIdx();
if (idx < startStaff)
startStaff = idx;
if (idx > endStaff)
endStaff = idx;
}
if (noteRestCount > 0) {
2017-03-08 13:12:26 +01:00
endSegment = endCR->nextSegmentAfterCR(SegmentType::ChordRest
| SegmentType::EndBarLine
| SegmentType::Clef);
2012-05-26 14:26:10 +02:00
_selection.setRange(startSegment, endSegment, startStaff, endStaff+1);
2014-05-24 12:53:50 +02:00
if (!_selection.isRange())
2014-05-26 13:21:04 +02:00
_selection.setState(SelState::RANGE);
_selection.updateSelectedElements();
2012-05-26 14:26:10 +02:00
}
2016-03-02 13:20:19 +01:00
setUpdateAll();
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// addLyrics
//---------------------------------------------------------
void Score::addLyrics(int tick, int staffIdx, const QString& txt)
{
if (txt.trimmed().isEmpty())
return;
Measure* measure = tick2measure(tick);
2017-03-08 13:12:26 +01:00
Segment* seg = measure->findSegment(SegmentType::ChordRest, tick);
2012-05-26 14:26:10 +02:00
if (seg == 0) {
2013-07-25 17:22:49 +02:00
qDebug("no segment found for lyrics<%s> at tick %d",
2012-05-26 14:26:10 +02:00
qPrintable(txt), tick);
return;
}
bool lyricsAdded = false;
for (int voice = 0; voice < VOICES; ++voice) {
int track = staffIdx * VOICES + voice;
2016-06-03 10:17:06 +02:00
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;
}
2012-05-26 14:26:10 +02:00
}
if (!lyricsAdded) {
2013-07-25 17:22:49 +02:00
qDebug("no chord/rest for lyrics<%s> at tick %d, staff %d",
qPrintable(txt), tick, staffIdx);
2012-05-26 14:26:10 +02:00
}
}
//---------------------------------------------------------
// 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;
}
//---------------------------------------------------------
// resetTempo
//---------------------------------------------------------
void Score::resetTempo()
{
tempomap()->clear();
tempomap()->setTempo(0, defaultTempo);
sigmap()->clear();
Measure* m = firstMeasure();
if (m)
sigmap()->add(0, SigEvent(m->len(), m->timesig(), 0));
}
//---------------------------------------------------------
2012-05-26 14:26:10 +02:00
// 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
{
2018-03-27 15:36:00 +02:00
return styleD(Sid::pageWidth) * DPI;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// loHeight
//---------------------------------------------------------
qreal Score::loHeight() const
{
2018-03-27 15:36:00 +02:00
return styleD(Sid::pageHeight) * DPI;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// cmdSelectAll
//---------------------------------------------------------
void Score::cmdSelectAll()
{
2013-10-06 16:43:43 +02:00
if (_measures.size() == 0)
return;
2014-08-20 19:18:27 +02:00
deselectAll();
Measure* first = firstMeasureMM();
if (!first)
return;
Measure* last = lastMeasureMM();
selectRange(first, 0);
selectRange(last, nstaves() - 1);
2016-03-02 13:20:19 +01:00
setUpdateAll();
update();
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// 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();
}
2017-01-18 14:16:33 +01:00
while (sm && sm->type() != ElementType::MEASURE)
2012-05-26 14:26:10 +02:00
sm = sm->next();
2017-01-18 14:16:33 +01:00
while (em && em->type() != ElementType::MEASURE)
2012-05-26 14:26:10 +02:00
em = em->next();
if (sm == 0 || em == 0)
return;
2016-06-03 10:17:06 +02:00
_selection.setRange(toMeasure(sm)->first(), toMeasure(em)->last(), 0, nstaves());
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// undo
//---------------------------------------------------------
2017-05-19 09:06:41 +02:00
void Score::undo(UndoCommand* cmd, EditData* ed) const
2012-05-26 14:26:10 +02:00
{
2017-05-19 09:06:41 +02:00
undoStack()->push(cmd, ed);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// linkId
//---------------------------------------------------------
int Score::linkId()
{
2016-03-10 10:41:31 +01:00
return (masterScore()->_linkId)++;
2012-05-26 14:26:10 +02:00
}
2012-09-22 19:40:45 +02:00
// val is a used link id
2012-05-26 14:26:10 +02:00
void Score::linkId(int val)
{
2016-03-10 10:41:31 +01:00
Score* s = masterScore();
2012-09-22 19:40:45 +02:00
if (val >= s->_linkId)
s->_linkId = val + 1; // update unused link id
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// scoreList
// return a list of scores containing the root score
// and all part scores (if there are any)
//---------------------------------------------------------
QList<Score*> Score::scoreList()
{
QList<Score*> scores;
2016-03-10 10:41:31 +01:00
Score* root = masterScore();
2012-05-26 14:26:10 +02:00
scores.append(root);
2014-11-27 14:50:02 +01:00
for (const Excerpt* ex : root->excerpts()) {
if (ex->partScore())
scores.append(ex->partScore());
}
2012-05-26 14:26:10 +02:00
return scores;
}
//---------------------------------------------------------
// switchLayer
//---------------------------------------------------------
bool Score::switchLayer(const QString& s)
{
int layerIdx = 0;
2014-05-31 12:03:21 +02:00
for (const Layer& l : layer()) {
2012-05-26 14:26:10 +02:00
if (s == l.name) {
setCurrentLayer(layerIdx);
return true;
}
++layerIdx;
}
return false;
}
2012-06-07 09:24:05 +02:00
//---------------------------------------------------------
// appendPart
//---------------------------------------------------------
void Score::appendPart(const QString& name)
{
static InstrumentTemplate defaultInstrument;
InstrumentTemplate* t;
t = searchTemplate(name);
2012-06-08 19:06:52 +02:00
if (t == 0) {
qDebug("appendPart: <%s> not found", qPrintable(name));
2012-06-07 09:24:05 +02:00
t = &defaultInstrument;
2012-06-08 19:06:52 +02:00
}
2012-06-07 09:24:05 +02:00
2016-02-06 22:03:43 +01:00
if (t->channel.empty()) {
2012-06-07 09:24:05 +02:00
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
2012-06-07 09:24:05 +02:00
t->channel.append(a);
}
Part* part = new Part(this);
part->initFromInstrTemplate(t);
int n = nstaves();
for (int i = 0; i < t->nstaves(); ++i) {
2014-08-16 13:32:08 +02:00
Staff* staff = new Staff(this);
staff->setPart(part);
2016-12-13 13:16:17 +01:00
staff->setLines(0, t->staffLines[i]);
2016-12-23 12:05:18 +01:00
staff->setSmall(0, t->smallStaff[i]);
2012-06-07 09:24:05 +02:00
if (i == 0) {
2017-03-31 13:03:15 +02:00
staff->setBracketType(0, t->bracket[0]);
staff->setBracketSpan(0, t->nstaves());
2012-06-07 09:24:05 +02:00
}
2015-04-28 22:12:32 +02:00
undoInsertStaff(staff, i);
2012-06-07 09:24:05 +02:00
}
part->staves()->front()->setBarLineSpan(part->nstaves());
2014-08-16 14:18:06 +02:00
undoInsertPart(part, n);
2012-06-07 09:24:05 +02:00
fixTicks();
2016-03-11 12:18:46 +01:00
masterScore()->rebuildMidiMapping();
2012-06-07 09:24:05 +02:00
}
//---------------------------------------------------------
// appendMeasures
//---------------------------------------------------------
void Score::appendMeasures(int n)
{
for (int i = 0; i < n; ++i)
2017-01-18 14:16:33 +01:00
insertMeasure(ElementType::MEASURE, 0, false);
2012-06-07 09:24:05 +02:00
}
#ifdef SCRIPT_INTERFACE
2012-06-07 09:24:05 +02:00
//---------------------------------------------------------
// addText
//---------------------------------------------------------
void Score::addText(const QString& type, const QString& txt)
{
MeasureBase* measure = first();
2017-01-18 14:16:33 +01:00
if (measure == 0 || measure->type() != ElementType::VBOX) {
insertMeasure(ElementType::VBOX, measure);
2012-06-07 09:24:05 +02:00
measure = first();
}
2018-08-01 11:46:07 +02:00
Tid tid = Tid::DEFAULT;
2012-06-07 09:24:05 +02:00
if (type == "title")
2018-08-01 11:46:07 +02:00
tid = Tid::TITLE;
2012-06-07 09:24:05 +02:00
else if (type == "subtitle")
2018-08-01 11:46:07 +02:00
tid = Tid::SUBTITLE;
Text* text = new Text(this, tid);
2012-06-07 09:24:05 +02:00
text->setParent(measure);
text->setXmlText(txt);
2012-06-07 09:24:05 +02:00
undoAddElement(text);
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// newCursor
//---------------------------------------------------------
Cursor* Score::newCursor()
{
return new Cursor(this);
}
#endif
2013-06-20 13:57:15 +02:00
//---------------------------------------------------------
// addSpanner
//---------------------------------------------------------
void Score::addSpanner(Spanner* s)
{
2013-07-05 11:23:52 +02:00
_spanner.addSpanner(s);
2013-06-20 13:57:15 +02:00
}
//---------------------------------------------------------
// removeSpanner
//---------------------------------------------------------
void Score::removeSpanner(Spanner* s)
{
2013-07-05 11:23:52 +02:00
_spanner.removeSpanner(s);
2013-06-20 13:57:15 +02:00
}
2013-06-10 11:03:34 +02:00
//---------------------------------------------------------
// isSpannerStartEnd
// does is spanner start or end at tick position tick
// for track ?
//---------------------------------------------------------
bool Score::isSpannerStartEnd(int tick, int track) const
{
2013-07-05 11:23:52 +02:00
for (auto i : _spanner.map()) {
2013-06-20 13:57:15 +02:00
if (i.second->track() != track)
2013-06-10 11:03:34 +02:00
continue;
2013-06-20 13:57:15 +02:00
if (i.second->tick() == tick || i.second->tick2() == tick)
2013-06-10 11:03:34 +02:00
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);
2013-06-19 16:25:29 +02:00
}
//---------------------------------------------------------
// addUnmanagedSpanner
//---------------------------------------------------------
void Score::addUnmanagedSpanner(Spanner* s)
{
_unmanagedSpanner.insert(s);
}
//---------------------------------------------------------
// removeSpanner
//---------------------------------------------------------
void Score::removeUnmanagedSpanner(Spanner* s)
{
_unmanagedSpanner.erase(s);
}
//---------------------------------------------------------
2013-10-18 12:21:01 +02:00
// setPos
//---------------------------------------------------------
2013-10-18 12:21:01 +02:00
void Score::setPos(POS pos, int tick)
{
2013-10-18 12:21:01 +02:00
if (tick < 0)
tick = 0;
if (tick != _pos[int(pos)])
2013-10-18 12:21:01 +02:00
_pos[int(pos)] = tick;
// even though tick position might not have changed, layout might have
2014-11-17 16:44:05 +01:00
// 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));
}
2014-05-22 16:18:35 +02:00
//---------------------------------------------------------
// uniqueStaves
//---------------------------------------------------------
QList<int> Score::uniqueStaves() const
{
QList<int> sl;
for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
Staff* s = staff(staffIdx);
2018-04-27 13:29:20 +02:00
if (s->links()) {
2014-05-22 16:18:35 +02:00
bool alreadyInList = false;
for (int idx : sl) {
2018-04-27 13:29:20 +02:00
if (s->links()->contains(staff(idx))) {
2014-05-22 16:18:35 +02:00
alreadyInList = true;
break;
}
}
if (alreadyInList)
continue;
}
sl.append(staffIdx);
}
return sl;
}
2014-05-29 12:13:49 +02:00
//---------------------------------------------------------
// findCR
// find chord/rest <= tick in track
//---------------------------------------------------------
ChordRest* Score::findCR(int tick, int track) const
{
Measure* m = tick2measureMM(tick);
2014-07-27 16:48:05 +02:00
if (!m) {
qDebug("findCR: no measure for tick %d", tick);
2014-07-27 16:11:59 +02:00
return nullptr;
2014-07-27 16:48:05 +02:00
}
2014-05-29 12:13:49 +02:00
// attach to first rest all spanner when mmRest
if (m->isMMRest())
tick = m->tick();
2017-03-08 13:12:26 +01:00
Segment* s = m->first(SegmentType::ChordRest);
for (Segment* ns = s; ; ns = ns->next(SegmentType::ChordRest)) {
2014-05-29 12:13:49 +02:00
if (ns == 0 || ns->tick() > tick)
break;
Element* el = ns->element(track);
if (el && el->isRest() && toRest(el)->isGap())
continue;
else if (el)
2014-05-29 12:13:49 +02:00
s = ns;
}
Element* el = s->element(track);
if (el && el->isRest() && toRest(el)->isGap())
s = 0;
2014-05-29 12:13:49 +02:00
if (s)
2016-06-03 10:17:06 +02:00
return toChordRest(s->element(track));
2014-05-29 12:13:49 +02:00
return nullptr;
}
2014-08-18 11:33:17 +02:00
//---------------------------------------------------------
// findCRinStaff
// find last chord/rest on staff that ends before tick
2014-08-18 11:33:17 +02:00
//---------------------------------------------------------
ChordRest* Score::findCRinStaff(int tick, int staffIdx) const
2014-08-18 11:33:17 +02:00
{
int ptick = tick - 1;
Measure* m = tick2measureMM(ptick);
2014-08-18 11:33:17 +02:00
if (!m) {
qDebug("findCRinStaff: no measure for tick %d", ptick);
2014-08-18 11:33:17 +02:00
return nullptr;
}
// attach to first rest all spanner when mmRest
if (m->isMMRest())
ptick = m->tick();
2017-03-08 13:12:26 +01:00
Segment* s = m->first(SegmentType::ChordRest);
int strack = staffIdx * VOICES;
2014-08-18 11:33:17 +02:00
int etrack = strack + VOICES;
int actualTrack = strack;
int lastTick = -1;
2017-03-08 13:12:26 +01:00
for (Segment* ns = s; ; ns = ns->next(SegmentType::ChordRest)) {
if (ns == 0 || ns->tick() > ptick)
2014-08-18 11:33:17 +02:00
break;
// found a segment; now find longest cr on this staff that does not overlap tick
2014-08-18 11:33:17 +02:00
for (int t = strack; t < etrack; ++t) {
2016-06-03 10:17:06 +02:00
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;
}
2014-08-18 11:33:17 +02:00
}
}
}
if (s)
2016-06-03 10:17:06 +02:00
return toChordRest(s->element(actualTrack));
2014-08-18 11:33:17 +02:00
return nullptr;
}
//---------------------------------------------------------
// setSoloMute
// called once at opening file, adds soloMute marks
//---------------------------------------------------------
2016-03-11 12:18:46 +01:00
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;
2015-07-15 18:04:33 +02:00
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;
}
//---------------------------------------------------------
// nmeasure
//---------------------------------------------------------
int Score::nmeasures()
{
int n = 0;
for (Measure* m = firstMeasure(); m; m = m->nextMeasure())
n++;
return n;
}
//---------------------------------------------------------
// hasLyrics
//---------------------------------------------------------
bool Score::hasLyrics()
{
2017-03-08 13:12:26 +01:00
SegmentType st = SegmentType::ChordRest;
for (Segment* seg = firstMeasure()->first(st); seg; seg = seg->next1(st)) {
for (int i = 0; i < ntracks(); ++i) {
2016-08-17 12:52:35 +02:00
ChordRest* cr = toChordRest(seg->element(i));
2016-08-24 14:49:34 +02:00
if (cr && !cr->lyrics().empty())
return true;
}
}
return false;
}
//---------------------------------------------------------
// hasHarmonies
//---------------------------------------------------------
bool Score::hasHarmonies()
{
2017-03-08 13:12:26 +01:00
SegmentType st = SegmentType::ChordRest;
for (Segment* seg = firstMeasure()->first(st); seg; seg = seg->next1(st)) {
for (Element* e : seg->annotations()) {
2017-01-18 14:16:33 +01:00
if (e->type() == ElementType::HARMONY)
return true;
}
}
return false;
}
//---------------------------------------------------------
// lyricCount
//---------------------------------------------------------
int Score::lyricCount()
{
size_t count = 0;
2017-03-08 13:12:26 +01:00
SegmentType st = SegmentType::ChordRest;
for (Segment* seg = firstMeasure()->first(st); seg; seg = seg->next1(st)) {
for (int i = 0; i < ntracks(); ++i) {
2016-08-17 12:52:35 +02:00
ChordRest* cr = toChordRest(seg->element(i));
if (cr)
count += cr->lyrics().size();
}
}
return int(count);
}
//---------------------------------------------------------
// harmonyCount
//---------------------------------------------------------
int Score::harmonyCount()
{
int count = 0;
2017-03-08 13:12:26 +01:00
SegmentType st = SegmentType::ChordRest;
for (Segment* seg = firstMeasure()->first(st); seg; seg = seg->next1(st)) {
for (Element* e : seg->annotations()) {
2017-01-18 14:16:33 +01:00
if (e->type() == ElementType::HARMONY)
count++;
}
}
return count;
}
2016-08-17 12:52:35 +02:00
//---------------------------------------------------------
// extractLyrics
//---------------------------------------------------------
QString Score::extractLyrics()
{
QString result;
updateRepeatList(true);
setPlaylistDirty();
2017-03-08 13:12:26 +01:00
SegmentType st = SegmentType::ChordRest;
for (int track = 0; track < ntracks(); track += VOICES) {
bool found = false;
size_t 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
2016-08-17 12:52:35 +02:00
ChordRest* cr = toChordRest(seg->element(track));
if (!cr || cr->lyrics().empty())
continue;
2016-08-17 12:52:35 +02:00
if (cr->lyrics().size() > maxLyrics)
maxLyrics = cr->lyrics().size();
2016-08-25 09:49:19 +02:00
if (playCount >= int(cr->lyrics().size()))
continue;
2018-01-16 13:38:17 +01:00
Lyrics* l = cr->lyrics(playCount, 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
2016-08-25 09:49:19 +02:00
for (unsigned lyricsNumber = 0; lyricsNumber < maxLyrics; lyricsNumber++) {
for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
2016-08-25 09:49:19 +02:00
unsigned playCount = m->playbackCount();
if (lyricsNumber >= playCount) {
for (Segment* seg = m->first(st); seg; seg = seg->next(st)) {
// consider voice 1 only
2016-08-17 12:52:35 +02:00
ChordRest* cr = toChordRest(seg->element(track));
if (!cr || cr->lyrics().empty())
continue;
2016-08-17 12:52:35 +02:00
if (cr->lyrics().size() > maxLyrics)
maxLyrics = cr->lyrics().size();
if (lyricsNumber >= cr->lyrics().size())
continue;
2018-01-16 13:38:17 +01:00
Lyrics* l = cr->lyrics(lyricsNumber, 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);
2016-12-13 13:16:17 +01:00
if (st->staffType(0)->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;
2018-03-27 15:36:00 +02:00
if (!styleB(Sid::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;
2017-03-08 13:12:26 +01:00
for (Segment* s = firstSegment(SegmentType::All); s; s = s->next1()) {
for (Element* e : s->annotations()) {
2017-01-18 14:16:33 +01:00
if (e && e->type() == ElementType::REHEARSAL_MARK) {
if (s->tick() < tick)
2016-06-03 10:17:06 +02:00
before = toRehearsalMark(e);
else if (s->tick() > tick) {
2016-06-03 10:17:06 +02:00
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;
2015-06-08 17:58:07 +02:00
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<Element*> el;
2015-06-27 20:51:04 +02:00
QList<Element*> oel = selection().elements(); // make copy
for (Element* e : oel) {
2017-01-18 14:16:33 +01:00
if (e->type() == ElementType::NOTE) {
2016-06-03 10:17:06 +02:00
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();
size_t notes = chord->notes().size();
int dstTrack = chord->staffIdx() * VOICES + voice;
2016-06-03 10:17:06 +02:00
ChordRest* dstCR = toChordRest(s->element(dstTrack));
Chord* dstChord = nullptr;
2016-07-31 15:23:11 +02:00
if (excerpt() && excerpt()->tracks().key(dstTrack, -1) == -1)
break;
// set up destination chord
2017-01-18 14:16:33 +01:00
if (dstCR && dstCR->type() == ElementType::CHORD && dstCR->globalDuration() == chord->globalDuration()) {
// existing chord in destination with correct duration;
// can simply move note in
2016-06-03 10:17:06 +02:00
dstChord = toChord(dstCR);
}
2017-01-18 14:16:33 +01:00
else if (dstCR && dstCR->type() == ElementType::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;
2017-03-08 13:12:26 +01:00
for (Segment* s2 = m->first(SegmentType::ChordRest); s2; s2 = s2->next()) {
if (s2->segmentType() != SegmentType::ChordRest)
2015-06-27 20:51:04 +02:00
continue;
2016-06-03 10:17:06 +02:00
ChordRest* cr2 = toChordRest(s2->element(dstTrack));
2017-01-18 14:16:33 +01:00
if (!cr2 || cr2->type() == ElementType::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)
2017-03-08 13:12:26 +01:00
expandVoice(s, /*m->first(SegmentType::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());
}
}
}
}
}
2016-02-06 22:03:43 +01:00
if (!el.empty())
selection().clear();
2015-06-27 20:51:04 +02:00
for (Element* e : el)
select(e, SelectType::ADD, -1);
2016-03-02 13:20:19 +01:00
setLayoutAll();
endCmd();
}
#if 0
//---------------------------------------------------------
// 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();
qreal margin = margins / INCH;
2015-11-16 14:24:47 +01:00
f.setSize(QSizeF((ttbox.width() / DPI) + 2 * margin, (ttbox.height()/ DPI) + 2 * margin));
2015-11-16 14:24:47 +01:00
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());
}
}
}
#endif
//---------------------------------------------------------
// getProperty
//---------------------------------------------------------
QVariant Score::getProperty(Pid /*id*/) const
{
qDebug("Score::getProperty: unhandled id");
return QVariant();
}
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
bool Score::setProperty(Pid /*id*/, const QVariant& /*v*/)
{
qDebug("Score::setProperty: unhandled id");
2016-05-18 15:43:47 +02:00
setLayoutAll();
return true;
}
//---------------------------------------------------------
// propertyDefault
//---------------------------------------------------------
QVariant Score::propertyDefault(Pid /*id*/) const
{
return QVariant();
}
2016-03-02 13:20:19 +01:00
//---------------------------------------------------------
// setStyle
//---------------------------------------------------------
void Score::setStyle(const MStyle& s)
{
2017-01-05 11:23:47 +01:00
style() = s;
2016-03-02 13:20:19 +01:00
}
2016-03-10 10:41:31 +01:00
//---------------------------------------------------------
// MasterScore
//---------------------------------------------------------
MasterScore::MasterScore()
: Score()
{
_tempomap = new TempoMap;
_sigmap = new TimeSigMap();
_repeatList = new RepeatList(this);
_revisions = new Revisions;
setMasterScore(this);
#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));
}
2017-01-05 11:23:47 +01:00
MasterScore::MasterScore(const MStyle& s)
2016-03-10 10:41:31 +01:00
: MasterScore{}
{
2017-01-05 11:23:47 +01:00
_movements = new Movements;
_movements->push_back(this);
setStyle(s);
2016-03-10 10:41:31 +01:00
}
MasterScore::~MasterScore()
{
delete _revisions;
delete _repeatList;
delete _sigmap;
delete _tempomap;
qDeleteAll(_excerpts);
}
2017-01-31 12:21:44 +01:00
//---------------------------------------------------------
// setMovements
//---------------------------------------------------------
void MasterScore::setMovements(Movements* m)
{
_movements = m;
if (_movements)
_movements->push_back(this);
}
2016-03-10 10:41:31 +01:00
//---------------------------------------------------------
// isSavable
//---------------------------------------------------------
bool MasterScore::isSavable() const
{
// TODO: check if file can be created if it does not exist
2016-03-11 12:18:46 +01:00
return fileInfo()->isWritable() || !fileInfo()->exists();
2016-03-10 10:41:31 +01:00
}
//---------------------------------------------------------
// setTempomap
//---------------------------------------------------------
void MasterScore::setTempomap(TempoMap* tm)
{
delete _tempomap;
_tempomap = tm;
}
2016-10-10 18:37:28 +02:00
//---------------------------------------------------------
// removeOmr
//---------------------------------------------------------
void MasterScore::removeOmr()
{
_showOmr = false;
#ifdef OMR
delete _omr;
#endif
_omr = 0;
}
2016-03-10 10:41:31 +01:00
//---------------------------------------------------------
// setName
//---------------------------------------------------------
2016-10-10 18:37:28 +02:00
void MasterScore::setName(const QString& ss)
2016-03-10 10:41:31 +01:00
{
QString s(ss);
s.replace('/', '_'); // for sanity
if (!(s.endsWith(".mscz") || s.endsWith(".mscx")))
s += ".mscz";
info.setFile(s);
}
//---------------------------------------------------------
2016-10-10 18:37:28 +02:00
// title
2016-03-10 10:41:31 +01:00
//---------------------------------------------------------
2016-10-10 18:37:28 +02:00
QString MasterScore::title() const
2016-03-10 10:41:31 +01:00
{
2016-10-10 18:37:28 +02:00
return fileInfo()->completeBaseName();
}
QString Score::title() const
{
return _excerpt->title();
2016-03-10 10:41:31 +01:00
}
2016-03-18 09:29:16 +01:00
//---------------------------------------------------------
// addRefresh
//---------------------------------------------------------
void Score::addRefresh(const QRectF& r)
{
_updateState.refresh |= r;
cmdState().setUpdateMode(UpdateMode::Update);
}
2016-09-28 21:13:05 +02:00
//---------------------------------------------------------
// staffIdx
//
/// Return index for the first staff of \a part.
//---------------------------------------------------------
int Score::staffIdx(const Part* part) const
{
int idx = 0;
for (Part* p : _parts) {
if (p == part)
break;
idx += p->nstaves();
}
return idx;
}
2017-01-05 11:23:47 +01:00
//---------------------------------------------------------
// setUpdateAll
//---------------------------------------------------------
void MasterScore::setUpdateAll()
{
_cmdState.setUpdateMode(UpdateMode::UpdateAll);
}
//---------------------------------------------------------
// setLayoutAll
//---------------------------------------------------------
void MasterScore::setLayoutAll()
{
_cmdState.setTick(0);
_cmdState.setTick(measures()->last() ? measures()->last()->endTick() : 0);
}
//---------------------------------------------------------
// setLayout
//---------------------------------------------------------
void MasterScore::setLayout(int t)
{
2018-02-12 16:34:33 +01:00
if (t >= 0)
_cmdState.setTick(t);
2017-01-05 11:23:47 +01:00
}
//---------------------------------------------------------
// isTopScore
//---------------------------------------------------------
bool Score::isTopScore() const
{
return !(isMaster() && static_cast<const MasterScore*>(this)->prev());
}
//---------------------------------------------------------
// Movements
//---------------------------------------------------------
Movements::Movements()
2017-01-31 12:21:44 +01:00
: std::vector<MasterScore*>()
2017-01-05 11:23:47 +01:00
{
_undo = new UndoStack();
}
Movements::~Movements()
{
qDeleteAll(_pages);
delete _undo;
}
2013-05-13 18:49:17 +02:00
}