2012-05-26 14:26:10 +02:00
|
|
|
//=============================================================================
|
|
|
|
// MuseScore
|
|
|
|
// Music Composition & Notation
|
|
|
|
//
|
|
|
|
// Copyright (C) 2002-2012 Werner Schweer
|
|
|
|
//
|
|
|
|
// This program is free software; you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License version 2
|
|
|
|
// as published by the Free Software Foundation and appearing in
|
|
|
|
// the file LICENCE.GPL
|
|
|
|
//=============================================================================
|
|
|
|
|
|
|
|
#include "note.h"
|
|
|
|
#include "rest.h"
|
|
|
|
#include "chord.h"
|
|
|
|
#include "key.h"
|
|
|
|
#include "sig.h"
|
|
|
|
#include "clef.h"
|
|
|
|
#include "score.h"
|
|
|
|
#include "slur.h"
|
2013-08-22 12:18:14 +02:00
|
|
|
#include "tie.h"
|
2012-05-26 14:26:10 +02:00
|
|
|
#include "hairpin.h"
|
|
|
|
#include "segment.h"
|
|
|
|
#include "staff.h"
|
|
|
|
#include "part.h"
|
|
|
|
#include "timesig.h"
|
|
|
|
#include "page.h"
|
|
|
|
#include "barline.h"
|
|
|
|
#include "tuplet.h"
|
|
|
|
#include "lyrics.h"
|
|
|
|
#include "image.h"
|
|
|
|
#include "keysig.h"
|
|
|
|
#include "beam.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "harmony.h"
|
|
|
|
#include "system.h"
|
|
|
|
#include "navigate.h"
|
|
|
|
#include "articulation.h"
|
|
|
|
#include "drumset.h"
|
|
|
|
#include "measure.h"
|
|
|
|
#include "tempo.h"
|
|
|
|
#include "undo.h"
|
|
|
|
#include "tablature.h"
|
|
|
|
#include "stafftype.h"
|
|
|
|
#include "tupletmap.h"
|
|
|
|
#include "tiemap.h"
|
|
|
|
#include "stem.h"
|
|
|
|
#include "iname.h"
|
|
|
|
#include "range.h"
|
|
|
|
#include "hook.h"
|
2012-08-08 20:46:29 +02:00
|
|
|
#include "pitchspelling.h"
|
2012-10-30 09:06:24 +01:00
|
|
|
#include "tempotext.h"
|
2012-10-31 10:15:45 +01:00
|
|
|
#include "dynamic.h"
|
2013-02-21 13:59:44 +01:00
|
|
|
#include "repeat.h"
|
2013-06-28 17:46:24 +02:00
|
|
|
#include "bracket.h"
|
2013-08-12 10:37:30 +02:00
|
|
|
#include "ottava.h"
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2013-05-13 18:49:17 +02:00
|
|
|
namespace Ms {
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// getSelectedNote
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Note* Score::getSelectedNote()
|
|
|
|
{
|
|
|
|
Element* el = selection().element();
|
|
|
|
if (el) {
|
2012-09-13 18:01:34 +02:00
|
|
|
if (el->type() == Element::NOTE)
|
2012-05-26 14:26:10 +02:00
|
|
|
return static_cast<Note*>(el);
|
|
|
|
}
|
|
|
|
selectNoteMessage();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// getSelectedChordRest
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
ChordRest* Score::getSelectedChordRest() const
|
|
|
|
{
|
|
|
|
Element* el = selection().element();
|
|
|
|
if (el) {
|
2012-09-13 18:01:34 +02:00
|
|
|
if (el->type() == Element::NOTE)
|
2012-05-26 14:26:10 +02:00
|
|
|
return static_cast<Note*>(el)->chord();
|
2012-09-13 18:01:34 +02:00
|
|
|
else if (el->type() == Element::REST || el->type() == Element::REPEAT_MEASURE)
|
2012-05-26 14:26:10 +02:00
|
|
|
return static_cast<Rest*>(el);
|
2012-09-13 18:01:34 +02:00
|
|
|
else if (el->type() == Element::CHORD)
|
2012-05-26 14:26:10 +02:00
|
|
|
return static_cast<Chord*>(el);
|
|
|
|
}
|
|
|
|
selectNoteRestMessage();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// getSelectedChordRest2
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::getSelectedChordRest2(ChordRest** cr1, ChordRest** cr2) const
|
|
|
|
{
|
|
|
|
*cr1 = 0;
|
|
|
|
*cr2 = 0;
|
|
|
|
foreach(Element* e, selection().elements()) {
|
2012-09-13 18:01:34 +02:00
|
|
|
if (e->type() == Element::NOTE)
|
2012-05-26 14:26:10 +02:00
|
|
|
e = e->parent();
|
|
|
|
if (e->isChordRest()) {
|
|
|
|
ChordRest* cr = static_cast<ChordRest*>(e);
|
|
|
|
if (*cr1 == 0 || (*cr1)->tick() > cr->tick())
|
|
|
|
*cr1 = cr;
|
|
|
|
if (*cr2 == 0 || (*cr2)->tick() < cr->tick())
|
|
|
|
*cr2 = cr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (*cr1 == 0)
|
|
|
|
selectNoteRestMessage();
|
|
|
|
if (*cr1 == *cr2)
|
|
|
|
*cr2 = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// pos
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Score::pos()
|
|
|
|
{
|
|
|
|
Element* el = selection().element();
|
|
|
|
if (selection().activeCR())
|
|
|
|
el = selection().activeCR();
|
|
|
|
if (el) {
|
|
|
|
switch(el->type()) {
|
2012-09-13 18:01:34 +02:00
|
|
|
case Element::NOTE:
|
2012-05-26 14:26:10 +02:00
|
|
|
el = el->parent();
|
|
|
|
// fall through
|
2012-09-13 18:01:34 +02:00
|
|
|
case Element::REPEAT_MEASURE:
|
|
|
|
case Element::REST:
|
|
|
|
case Element::CHORD:
|
2012-05-26 14:26:10 +02:00
|
|
|
return static_cast<ChordRest*>(el)->tick();
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// addRest
|
|
|
|
// create one Rest at tick with duration d
|
|
|
|
// create segment if necessary
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Rest* Score::addRest(int tick, int track, TDuration d, Tuplet* tuplet)
|
|
|
|
{
|
|
|
|
Measure* measure = tick2measure(tick);
|
|
|
|
Rest* rest = new Rest(this, d);
|
|
|
|
if (d.type() == TDuration::V_MEASURE)
|
|
|
|
rest->setDuration(measure->stretchedLen(staff(track/VOICES)));
|
|
|
|
else
|
|
|
|
rest->setDuration(d.fraction());
|
|
|
|
rest->setTrack(track);
|
|
|
|
rest->setTuplet(tuplet);
|
|
|
|
undoAddCR(rest, measure, tick);
|
|
|
|
return rest;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// addRest
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Rest* Score::addRest(Segment* s, int track, TDuration d, Tuplet* tuplet)
|
|
|
|
{
|
|
|
|
Rest* rest = new Rest(this, d);
|
|
|
|
if (d.type() == TDuration::V_MEASURE)
|
|
|
|
rest->setDuration(s->measure()->stretchedLen(staff(track/VOICES)));
|
|
|
|
else
|
|
|
|
rest->setDuration(d.fraction());
|
|
|
|
rest->setTrack(track);
|
|
|
|
rest->setParent(s);
|
|
|
|
rest->setTuplet(tuplet);
|
|
|
|
undoAddElement(rest);
|
|
|
|
return rest;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// addChord
|
|
|
|
// Create one Chord at tick with duration d
|
|
|
|
// - create segment if necessary.
|
|
|
|
// - Use chord "oc" as prototype;
|
|
|
|
// - if "genTie" then tie to chord "oc"
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Chord* Score::addChord(int tick, TDuration d, Chord* oc, bool genTie, Tuplet* tuplet)
|
|
|
|
{
|
|
|
|
Measure* measure = tick2measure(tick);
|
|
|
|
if (measure->endTick() <= tick) {
|
|
|
|
qDebug("Score::addChord(): end of score?");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Chord* chord = new Chord(this);
|
|
|
|
chord->setTuplet(tuplet);
|
|
|
|
chord->setTrack(oc->track());
|
|
|
|
chord->setDurationType(d);
|
|
|
|
chord->setDuration(d.fraction());
|
|
|
|
|
|
|
|
foreach(Note* n, oc->notes()) {
|
|
|
|
Note* nn = new Note(this);
|
|
|
|
chord->add(nn);
|
|
|
|
nn->setPitch(n->pitch(), n->tpc());
|
|
|
|
}
|
|
|
|
undoAddCR(chord, measure, tick);
|
|
|
|
|
|
|
|
//
|
|
|
|
// now as both chords are in place
|
|
|
|
// (have segments as parent) we can add ties:
|
|
|
|
//
|
|
|
|
if (genTie) {
|
|
|
|
int n = oc->notes().size();
|
|
|
|
for(int i = 0; i < n; ++i) {
|
|
|
|
Note* n = oc->notes()[i];
|
|
|
|
Note* nn = chord->notes()[i];
|
|
|
|
Tie* tie = new Tie(this);
|
|
|
|
tie->setStartNote(n);
|
|
|
|
tie->setEndNote(nn);
|
|
|
|
tie->setTrack(n->track());
|
|
|
|
undoAddElement(tie);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
updateAccidentals(measure, chord->staffIdx());
|
|
|
|
return chord;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// addClone
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
ChordRest* Score::addClone(ChordRest* cr, int tick, const TDuration& d)
|
|
|
|
{
|
|
|
|
qDebug("addClone %s at %d %s", cr->name(), tick, qPrintable(d.fraction().print()));
|
|
|
|
ChordRest* newcr;
|
|
|
|
// change a RepeatMeasure() into an Rest()
|
2012-09-13 18:01:34 +02:00
|
|
|
if (cr->type() == Element::REPEAT_MEASURE)
|
2012-05-26 14:26:10 +02:00
|
|
|
newcr = new Rest(*static_cast<Rest*>(cr));
|
|
|
|
else
|
|
|
|
newcr = static_cast<ChordRest*>(cr->clone());
|
|
|
|
newcr->rxpos() = 0.0;
|
|
|
|
newcr->setDurationType(d);
|
|
|
|
newcr->setDuration(d.fraction());
|
|
|
|
newcr->setTuplet(cr->tuplet());
|
|
|
|
newcr->setSelected(false);
|
|
|
|
|
|
|
|
undoAddCR(newcr, cr->measure(), tick);
|
|
|
|
return newcr;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setRest
|
|
|
|
// create one or more rests to fill "l"
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Rest* Score::setRest(int tick, int track, Fraction l, bool useDots, Tuplet* tuplet)
|
|
|
|
{
|
|
|
|
Measure* measure = tick2measure(tick);
|
|
|
|
Rest* r = 0;
|
|
|
|
|
|
|
|
while (!l.isZero()) {
|
|
|
|
//
|
|
|
|
// divide into measures
|
|
|
|
//
|
|
|
|
Fraction f;
|
|
|
|
if (tuplet) {
|
|
|
|
int ticks = (tuplet->tick() + tuplet->actualTicks()) - tick;
|
|
|
|
|
|
|
|
f = Fraction::fromTicks(ticks);
|
|
|
|
for (Tuplet* t = tuplet; t; t = t->tuplet())
|
|
|
|
f *= t->ratio();
|
|
|
|
//
|
|
|
|
// restrict to tuplet len
|
|
|
|
//
|
|
|
|
if (f < l)
|
|
|
|
l = f;
|
|
|
|
}
|
|
|
|
else if (measure->tick() < tick)
|
|
|
|
f = Fraction::fromTicks(measure->tick() + measure->ticks() - tick);
|
|
|
|
else
|
|
|
|
f = measure->len();
|
|
|
|
|
|
|
|
if (f > l)
|
|
|
|
f = l;
|
|
|
|
|
|
|
|
if ((track % VOICES) && !measure->hasVoice(track) && (tick == measure->tick())) {
|
|
|
|
l -= f;
|
|
|
|
measure = measure->nextMeasure();
|
|
|
|
if (!measure)
|
|
|
|
break;
|
|
|
|
tick = measure->tick();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((measure->timesig() == measure->len()) // not in pickup measure
|
|
|
|
&& (measure->tick() == tick)
|
|
|
|
&& (measure->timesig() == f)) {
|
|
|
|
Rest* rest = addRest(tick, track, TDuration(TDuration::V_MEASURE), tuplet);
|
|
|
|
tick += measure->timesig().ticks();
|
|
|
|
if (r == 0)
|
|
|
|
r = rest;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
//
|
|
|
|
// compute list of durations which will fit l
|
|
|
|
//
|
2013-06-04 18:29:14 +02:00
|
|
|
QList<TDuration> dList = toDurationList(f, useDots);
|
2012-05-26 14:26:10 +02:00
|
|
|
if (dList.isEmpty())
|
|
|
|
return 0;
|
|
|
|
foreach(TDuration d, dList) {
|
|
|
|
qDebug(" duration %d/%d", d.fraction().numerator(), d.fraction().denominator());
|
|
|
|
}
|
|
|
|
|
|
|
|
Rest* rest = 0;
|
|
|
|
if (((tick - measure->tick()) % dList[0].ticks()) == 0) {
|
|
|
|
foreach(TDuration d, dList) {
|
|
|
|
rest = addRest(tick, track, d, tuplet);
|
|
|
|
if (r == 0)
|
|
|
|
r = rest;
|
|
|
|
tick += rest->actualTicks();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
for (int i = dList.size() - 1; i >= 0; --i) {
|
|
|
|
rest = addRest(tick, track, dList[i], tuplet);
|
|
|
|
if (r == 0)
|
|
|
|
r = rest;
|
|
|
|
tick += rest->actualTicks();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
l -= f;
|
|
|
|
measure = measure->nextMeasure();
|
|
|
|
if (!measure)
|
|
|
|
break;
|
|
|
|
tick = measure->tick();
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2012-08-29 18:42:28 +02:00
|
|
|
// addNote from pitch
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Note* Score::addNote(Chord* chord, int pitch)
|
|
|
|
{
|
|
|
|
Note* note = new Note(this);
|
|
|
|
note->setParent(chord);
|
|
|
|
note->setTrack(chord->track());
|
|
|
|
note->setPitch(pitch);
|
|
|
|
note->setTpcFromPitch();
|
|
|
|
undoAddElement(note);
|
2013-02-26 18:53:29 +01:00
|
|
|
_playNote = true;
|
2012-05-26 14:26:10 +02:00
|
|
|
select(note, SELECT_SINGLE, 0);
|
2013-09-16 13:03:58 +02:00
|
|
|
if (!chord->staff()->isTabStaff())
|
|
|
|
moveToNextInputPos();
|
2012-05-26 14:26:10 +02:00
|
|
|
return note;
|
|
|
|
}
|
|
|
|
|
2012-08-29 18:42:28 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// addNote from NoteVal
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Note* Score::addNote(Chord* chord, NoteVal& noteVal)
|
|
|
|
{
|
|
|
|
Note* note = new Note(this);
|
|
|
|
note->setParent(chord);
|
|
|
|
note->setTrack(chord->track());
|
|
|
|
note->setNval(noteVal);
|
2012-11-23 16:18:40 +01:00
|
|
|
if (note->tpc() == INVALID_TPC)
|
2012-08-29 18:42:28 +02:00
|
|
|
note->setTpcFromPitch();
|
|
|
|
undoAddElement(note);
|
2013-02-26 18:53:29 +01:00
|
|
|
_playNote = true;
|
2012-08-29 18:42:28 +02:00
|
|
|
select(note, SELECT_SINGLE, 0);
|
2013-09-16 13:03:58 +02:00
|
|
|
if (!chord->staff()->isTabStaff())
|
|
|
|
moveToNextInputPos();
|
2012-08-29 18:42:28 +02:00
|
|
|
return note;
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// rewriteMeasures
|
|
|
|
// rewrite all measures up to the next time signature
|
|
|
|
// or section break
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Score::rewriteMeasures(Measure* fm, Measure* lm, const Fraction& ns)
|
|
|
|
{
|
2012-06-26 14:41:49 +02:00
|
|
|
int measures = 1;
|
|
|
|
for (Measure* m = fm; m != lm; m = m -> nextMeasure())
|
|
|
|
++measures;
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
ScoreRange range;
|
|
|
|
range.read(fm->first(), lm->last(), 0, nstaves() * VOICES);
|
|
|
|
if (!range.canWrite(ns))
|
|
|
|
return false;
|
|
|
|
|
2013-06-19 16:25:29 +02:00
|
|
|
undoRemoveMeasures(fm, lm);
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
// calculate number of required measures = nm
|
|
|
|
//
|
|
|
|
Fraction k = range.duration();
|
|
|
|
k /= ns;
|
|
|
|
int nm = (k.numerator() + k.denominator() - 1)/ k.denominator();
|
|
|
|
|
2012-06-26 14:41:49 +02:00
|
|
|
Fraction nd(nm * ns.numerator(), ns.denominator()); // total new duration
|
2013-01-18 14:42:11 +01:00
|
|
|
|
|
|
|
// evtl. we have to fill the last measure
|
2012-06-26 14:41:49 +02:00
|
|
|
Fraction fill = nd - range.duration();
|
2013-01-18 14:42:11 +01:00
|
|
|
if (!fill.isZero())
|
|
|
|
range.fill(fill);
|
2012-06-26 14:41:49 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
Measure* nfm = 0;
|
|
|
|
Measure* nlm = 0;
|
|
|
|
|
2013-01-18 14:42:11 +01:00
|
|
|
// create destination measures
|
2012-05-26 14:26:10 +02:00
|
|
|
int tick = 0;
|
|
|
|
for (int i = 0; i < nm; ++i) {
|
|
|
|
Measure* m = new Measure(this);
|
|
|
|
m->setPrev(nlm);
|
|
|
|
if (nlm)
|
|
|
|
nlm->setNext(m);
|
|
|
|
m->setTimesig(ns);
|
|
|
|
m->setLen(ns);
|
|
|
|
m->setTick(tick);
|
|
|
|
tick += m->ticks();
|
|
|
|
nlm = m;
|
|
|
|
if (nfm == 0)
|
|
|
|
nfm = m;
|
|
|
|
}
|
|
|
|
if (!range.write(0, nfm)) {
|
|
|
|
qDebug("cannot write measures\n");
|
|
|
|
abort();
|
|
|
|
}
|
2012-06-26 14:41:49 +02:00
|
|
|
nlm->setEndBarLineType(lm->endBarLineType(), lm->endBarLineGenerated(),
|
|
|
|
lm->endBarLineVisible(), lm->endBarLineColor());
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
// insert new calculated measures
|
|
|
|
//
|
|
|
|
nfm->setPrev(fm->prev());
|
|
|
|
nlm->setNext(lm->next());
|
|
|
|
undo(new InsertMeasures(nfm, nlm));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// warnTupletCrossing
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static void warnTupletCrossing()
|
|
|
|
{
|
|
|
|
QMessageBox::warning(0,
|
|
|
|
QT_TRANSLATE_NOOP("addRemoveTimeSig", "MuseScore"),
|
|
|
|
QT_TRANSLATE_NOOP("addRemoveTimeSig", "cannot rewrite measures:\n"
|
|
|
|
"tuplet would cross measure")
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// rewriteMeasures
|
|
|
|
// rewrite all measures up to the next time signature
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::rewriteMeasures(Measure* fm, const Fraction& ns)
|
|
|
|
{
|
|
|
|
Measure* lm = fm;
|
|
|
|
Measure* fm1 = fm;
|
|
|
|
//
|
|
|
|
// split into Measure segments fm-lm
|
|
|
|
//
|
|
|
|
for (MeasureBase* m = fm; ; m = m->next()) {
|
2012-09-13 18:01:34 +02:00
|
|
|
if (!m || (m->type() != Element::MEASURE)
|
2012-08-03 15:54:02 +02:00
|
|
|
|| (static_cast<Measure*>(m)->first(Segment::SegTimeSig) && m != fm))
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
if (!rewriteMeasures(fm1, lm, ns)) {
|
|
|
|
warnTupletCrossing();
|
|
|
|
for (Measure* m = fm1; m; m = m->nextMeasure()) {
|
2012-08-03 15:54:02 +02:00
|
|
|
if (m->first(Segment::SegTimeSig))
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
Fraction fr(ns);
|
|
|
|
undoChangeProperty(m, P_TIMESIG_NOMINAL, QVariant::fromValue(fr));
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2012-09-13 18:01:34 +02:00
|
|
|
if (!m || m->type() == Element::MEASURE)
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
2013-02-17 15:40:50 +01:00
|
|
|
while (m->type() != Element::MEASURE) {
|
2012-05-26 14:26:10 +02:00
|
|
|
m = m->next();
|
2013-02-17 15:40:50 +01:00
|
|
|
if (!m)
|
|
|
|
break;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
fm1 = static_cast<Measure*>(m);
|
|
|
|
if (fm1 == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
lm = static_cast<Measure*>(m);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdAddTimeSig
|
|
|
|
//
|
|
|
|
// Add or change time signature at measure in response
|
|
|
|
// to gui command (drop timesig on measure or timesig)
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2012-06-28 09:28:13 +02:00
|
|
|
void Score::cmdAddTimeSig(Measure* fm, int staffIdx, TimeSig* ts, bool local)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
Fraction ns = ts->sig();
|
|
|
|
int tick = fm->tick();
|
|
|
|
TimeSig* lts = staff(staffIdx)->timeSig(tick);
|
2012-06-27 12:53:40 +02:00
|
|
|
Fraction stretch;
|
|
|
|
Fraction lsig; // last signature
|
2012-05-26 14:26:10 +02:00
|
|
|
if (lts) {
|
|
|
|
stretch = lts->stretch();
|
|
|
|
lsig = lts->sig();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
stretch.set(1,1);
|
2012-06-27 12:53:40 +02:00
|
|
|
lsig.set(4,4); // set to default
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int track = staffIdx * VOICES;
|
2012-08-03 15:54:02 +02:00
|
|
|
Segment* seg = fm->undoGetSegment(Segment::SegTimeSig, tick);
|
2012-05-26 14:26:10 +02:00
|
|
|
TimeSig* ots = static_cast<TimeSig*>(seg->element(track));
|
|
|
|
if (ots) {
|
|
|
|
//
|
|
|
|
// ignore if there is already a timesig
|
|
|
|
// with same values
|
|
|
|
//
|
2013-03-05 20:23:59 +01:00
|
|
|
if ((ots->timeSigType() == ts->timeSigType())
|
2012-05-26 14:26:10 +02:00
|
|
|
&& (ots->sig().identical(ts->sig()))
|
|
|
|
&& (ots->stretch() == ts->stretch())) {
|
|
|
|
delete ts;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2012-06-28 09:28:13 +02:00
|
|
|
|
|
|
|
if (local) {
|
|
|
|
ts->setParent(seg);
|
|
|
|
ts->setTrack(track);
|
2012-06-28 15:12:17 +02:00
|
|
|
ts->setStretch(ts->sig() / lsig);
|
2012-06-28 09:28:13 +02:00
|
|
|
undoAddElement(ts);
|
|
|
|
timesigStretchChanged(ts, fm, staffIdx);
|
|
|
|
return;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2012-06-28 09:28:13 +02:00
|
|
|
|
2013-03-01 10:32:07 +01:00
|
|
|
foreach(Score* score, scoreList()) {
|
|
|
|
Measure* fm = score->tick2measure(tick);
|
|
|
|
Measure* nfm = fm;
|
|
|
|
if (ots && ots->sig() == ts->sig() && ots->stretch() == ts->stretch()) {
|
|
|
|
//
|
|
|
|
// Set time signature of all measures up to next
|
|
|
|
// time signature. Do not touch measure contents.
|
|
|
|
//
|
|
|
|
for (Measure* m = fm; m; m = m->nextMeasure()) {
|
|
|
|
if ((m != fm) && m->first(Segment::SegTimeSig))
|
|
|
|
break;
|
|
|
|
bool changeActual = m->len() == m->timesig();
|
|
|
|
undoChangeProperty(m, P_TIMESIG_NOMINAL, QVariant::fromValue(ns));
|
|
|
|
if (changeActual)
|
|
|
|
undoChangeProperty(m, P_TIMESIG_ACTUAL, QVariant::fromValue(ns));
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else {
|
2013-03-01 10:32:07 +01:00
|
|
|
//
|
|
|
|
// rewrite all measures up to the next time signature
|
|
|
|
//
|
|
|
|
if (fm == firstMeasure() && (fm->len() != fm->timesig())) {
|
|
|
|
// handle upbeat
|
|
|
|
undoChangeProperty(fm, P_TIMESIG_NOMINAL, QVariant::fromValue(ns));
|
2013-08-05 09:28:28 +02:00
|
|
|
Measure* m = fm->nextMeasure();
|
|
|
|
Segment* s = m->findSegment(Segment::SegTimeSig, m->tick());
|
|
|
|
if (!s) {
|
|
|
|
// there is something to rewrite
|
|
|
|
score->rewriteMeasures(fm->nextMeasure(), ns);
|
|
|
|
}
|
2013-03-01 10:32:07 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
score->rewriteMeasures(fm, ns);
|
|
|
|
nfm = fm->prev() ? fm->prev()->nextMeasure() : firstMeasure();
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2013-03-01 10:32:07 +01:00
|
|
|
seg = nfm->undoGetSegment(Segment::SegTimeSig, nfm->tick());
|
|
|
|
int n = score->nstaves();
|
|
|
|
for (int staffIdx = 0; staffIdx < n; ++staffIdx) {
|
|
|
|
TimeSig* nsig = static_cast<TimeSig*>(seg->element(staffIdx * VOICES));
|
|
|
|
if (nsig == 0) {
|
|
|
|
nsig = new TimeSig(this);
|
|
|
|
nsig->setTrack(staffIdx * VOICES);
|
|
|
|
nsig->setParent(seg);
|
2013-03-05 20:23:59 +01:00
|
|
|
nsig->setSig(ts->sig(), ts->timeSigType());
|
2013-03-01 10:32:07 +01:00
|
|
|
undoAddElement(nsig);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
undo(new ChangeTimesig(nsig, false, ts->sig(), ts->stretch(),
|
2013-03-05 20:23:59 +01:00
|
|
|
ts->numeratorString(), ts->denominatorString(), ts->timeSigType()));
|
2013-03-01 10:32:07 +01:00
|
|
|
nsig->setDropTarget(0); // DEBUG
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
delete ts;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// timesigStretchChanged
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::timesigStretchChanged(TimeSig* ts, Measure* fm, int staffIdx)
|
|
|
|
{
|
|
|
|
for (Measure* m = fm; m; m = m->nextMeasure()) {
|
2012-08-03 15:54:02 +02:00
|
|
|
if ((m != fm) && m->first(Segment::SegTimeSig))
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
int strack = staffIdx * VOICES;
|
|
|
|
int etrack = strack + VOICES;
|
2012-08-03 15:54:02 +02:00
|
|
|
for (Segment* s = m->first(Segment::SegChordRest); s; s = s->next(Segment::SegChordRest)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int track = strack; track < etrack; ++track) {
|
|
|
|
ChordRest* cr = static_cast<ChordRest*>(s->element(track));
|
|
|
|
if (!cr)
|
|
|
|
continue;
|
2012-09-13 18:01:34 +02:00
|
|
|
if (cr->type() == Element::REST && cr->durationType() == TDuration::V_MEASURE)
|
2012-06-28 15:12:17 +02:00
|
|
|
cr->setDuration(ts->sig());
|
2012-05-26 14:26:10 +02:00
|
|
|
else
|
|
|
|
qDebug("timeSigChanged: not implemented: chord/rest does not fit");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdRemoveTimeSig
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdRemoveTimeSig(TimeSig* ts)
|
|
|
|
{
|
|
|
|
undoRemoveElement(ts->segment());
|
|
|
|
|
|
|
|
Measure* fm = ts->measure();
|
|
|
|
Measure* lm = fm;
|
|
|
|
Measure* pm = fm->prevMeasure();
|
|
|
|
Fraction ns(pm ? pm->timesig() : Fraction(4,4));
|
|
|
|
for (Measure* m = lm; m; m = m->nextMeasure()) {
|
2012-08-03 15:54:02 +02:00
|
|
|
if (m->first(Segment::SegTimeSig))
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
lm = m;
|
|
|
|
}
|
|
|
|
rewriteMeasures(fm, ns);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// putNote
|
|
|
|
// mouse click in state NOTE_ENTRY
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::putNote(const QPointF& pos, bool replace)
|
|
|
|
{
|
|
|
|
Position p;
|
|
|
|
if (!getPosition(&p, pos, _is.voice())) {
|
|
|
|
qDebug("cannot put note here, get position failed");
|
|
|
|
return;
|
|
|
|
}
|
2012-08-07 12:44:19 +02:00
|
|
|
putNote(p, replace);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2012-08-07 12:44:19 +02:00
|
|
|
void Score::putNote(const Position& p, bool replace)
|
|
|
|
{
|
2012-05-26 14:26:10 +02:00
|
|
|
Segment* s = p.segment;
|
|
|
|
int staffIdx = p.staffIdx;
|
|
|
|
int line = p.line;
|
2012-08-07 12:44:19 +02:00
|
|
|
|
|
|
|
int tick = s->tick();
|
2012-05-26 14:26:10 +02:00
|
|
|
Staff* st = staff(staffIdx);
|
2012-08-07 12:44:19 +02:00
|
|
|
ClefType clef = st->clef(tick);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2012-08-07 12:44:19 +02:00
|
|
|
// qDebug("putNote at tick %d staff %d line %d clef %d currentAccidental %d",
|
|
|
|
// tick, staffIdx, line, clef, acci);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
_is.setTrack(staffIdx * VOICES + _is.voice());
|
|
|
|
_is.setSegment(s);
|
2012-11-23 16:18:40 +01:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
const Instrument* instr = st->part()->instr();
|
2012-08-04 15:46:43 +02:00
|
|
|
MScore::Direction stemDirection = MScore::AUTO;
|
2012-05-26 14:26:10 +02:00
|
|
|
NoteVal nval;
|
2013-09-15 18:43:48 +02:00
|
|
|
StringData* stringData = 0;
|
2013-03-28 11:54:39 +01:00
|
|
|
StaffTypeTablature * tab = 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
switch(st->staffType()->group()) {
|
Re-factor presets and staff types.
1) Built-in staff types have been removed.
2) Presets are internally used as source for the staff types of a new score, to match data in Instruments.xml and as reference to check for modifications.
3) Each new score is given by default one staff type for each preset with the same name.
4) The Instrument page of the New Score Wizard lists (under the name of "Staff types") the default staff types applicable to the instrument (actually it lists the preset, as the score does not have any staff type yet).
5) The "Add | Instruments" dlg box lists all the staff types applicable to the instrument: = to the list of 4) + any user-created staff type.
6) The Staff Properties dlg box lists all the staff types applicable to the instrument: = list in 5)
7) The Staff Type Editor lists all the staff types
This should ensure consistency among the several lists of staff types and avoid duplication of similar items
Terminology:
7) A new staff type created in the editor is named by default with the group name ("Standard-", "Perc-", "Tab-") + the index of the new type in its group + the suffix "[*]" marking a user customisation. The user is anyway able to rename it, if he want.
8) The pitched staff type has been renamed everywhere (hopefully!) to "Standard"
9) The term 'preset' have been removed from the UI, except from the Staff Type Editor where it keeps its meaning of ready-made collections of parameters
The commit affects many files, but a fair number of them have only changes in names of literals. The files with significant code changes are:
libmscore/score.cpp
libmscore/stafftype.cpp/.h
mscore/editstafftype.cpp (code for naming a new staff type)
mscore/instrdialog.cpp (building type list)
Note: as score files store staff type indications as integer indices and the number and order of new default staff types is different from the old built-in types, there is a compatibility issue with old 2.0 score which use percussion and tab staves. In Score::read() (libmscore/scorefile.cpp), there is a rudimentary attempt to cope with this.Old scores will need manual fix anyway. There should not be any (new) compatibility issue with 1.x scores, as they did not use staff types.
2013-08-18 11:55:31 +02:00
|
|
|
case PERCUSSION_STAFF_GROUP: {
|
2012-08-05 23:17:53 +02:00
|
|
|
if (_is.rest)
|
|
|
|
break;
|
2012-05-26 14:26:10 +02:00
|
|
|
Drumset* ds = instr->drumset();
|
|
|
|
nval.pitch = _is.drumNote();
|
|
|
|
if (nval.pitch < 0)
|
|
|
|
return;
|
|
|
|
nval.headGroup = ds->noteHead(nval.pitch);
|
|
|
|
if (nval.headGroup < 0)
|
|
|
|
return;
|
|
|
|
stemDirection = ds->stemDirection(nval.pitch);
|
|
|
|
break;
|
|
|
|
}
|
Re-factor presets and staff types.
1) Built-in staff types have been removed.
2) Presets are internally used as source for the staff types of a new score, to match data in Instruments.xml and as reference to check for modifications.
3) Each new score is given by default one staff type for each preset with the same name.
4) The Instrument page of the New Score Wizard lists (under the name of "Staff types") the default staff types applicable to the instrument (actually it lists the preset, as the score does not have any staff type yet).
5) The "Add | Instruments" dlg box lists all the staff types applicable to the instrument: = to the list of 4) + any user-created staff type.
6) The Staff Properties dlg box lists all the staff types applicable to the instrument: = list in 5)
7) The Staff Type Editor lists all the staff types
This should ensure consistency among the several lists of staff types and avoid duplication of similar items
Terminology:
7) A new staff type created in the editor is named by default with the group name ("Standard-", "Perc-", "Tab-") + the index of the new type in its group + the suffix "[*]" marking a user customisation. The user is anyway able to rename it, if he want.
8) The pitched staff type has been renamed everywhere (hopefully!) to "Standard"
9) The term 'preset' have been removed from the UI, except from the Staff Type Editor where it keeps its meaning of ready-made collections of parameters
The commit affects many files, but a fair number of them have only changes in names of literals. The files with significant code changes are:
libmscore/score.cpp
libmscore/stafftype.cpp/.h
mscore/editstafftype.cpp (code for naming a new staff type)
mscore/instrdialog.cpp (building type list)
Note: as score files store staff type indications as integer indices and the number and order of new default staff types is different from the old built-in types, there is a compatibility issue with old 2.0 score which use percussion and tab staves. In Score::read() (libmscore/scorefile.cpp), there is a rudimentary attempt to cope with this.Old scores will need manual fix anyway. There should not be any (new) compatibility issue with 1.x scores, as they did not use staff types.
2013-08-18 11:55:31 +02:00
|
|
|
case TAB_STAFF_GROUP: {
|
2012-08-05 23:17:53 +02:00
|
|
|
if (_is.rest)
|
|
|
|
return;
|
2013-09-15 18:43:48 +02:00
|
|
|
stringData = instr->stringData();
|
2013-03-28 11:54:39 +01:00
|
|
|
tab = (StaffTypeTablature*)st->staffType();
|
2012-08-28 19:36:24 +02:00
|
|
|
int string = tab->VisualStringToPhys(line);
|
2013-09-15 01:07:24 +02:00
|
|
|
if (string < 0 || string >= stringData->strings())
|
2012-08-08 23:58:56 +02:00
|
|
|
return;
|
2012-05-26 14:26:10 +02:00
|
|
|
// build a default NoteVal for that line
|
2012-08-29 18:42:28 +02:00
|
|
|
nval.string = string;
|
2012-09-16 11:56:27 +02:00
|
|
|
if(p.fret != FRET_NONE) // if a fret is given, use it
|
2012-08-29 18:42:28 +02:00
|
|
|
nval.fret = p.fret;
|
2012-09-16 11:56:27 +02:00
|
|
|
else { // if no fret, use 0 as default
|
2012-08-29 18:42:28 +02:00
|
|
|
_is.setString(line);
|
|
|
|
nval.fret = 0;
|
|
|
|
}
|
2013-09-15 01:07:24 +02:00
|
|
|
nval.pitch = stringData->getPitch(string, nval.fret);
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
Re-factor presets and staff types.
1) Built-in staff types have been removed.
2) Presets are internally used as source for the staff types of a new score, to match data in Instruments.xml and as reference to check for modifications.
3) Each new score is given by default one staff type for each preset with the same name.
4) The Instrument page of the New Score Wizard lists (under the name of "Staff types") the default staff types applicable to the instrument (actually it lists the preset, as the score does not have any staff type yet).
5) The "Add | Instruments" dlg box lists all the staff types applicable to the instrument: = to the list of 4) + any user-created staff type.
6) The Staff Properties dlg box lists all the staff types applicable to the instrument: = list in 5)
7) The Staff Type Editor lists all the staff types
This should ensure consistency among the several lists of staff types and avoid duplication of similar items
Terminology:
7) A new staff type created in the editor is named by default with the group name ("Standard-", "Perc-", "Tab-") + the index of the new type in its group + the suffix "[*]" marking a user customisation. The user is anyway able to rename it, if he want.
8) The pitched staff type has been renamed everywhere (hopefully!) to "Standard"
9) The term 'preset' have been removed from the UI, except from the Staff Type Editor where it keeps its meaning of ready-made collections of parameters
The commit affects many files, but a fair number of them have only changes in names of literals. The files with significant code changes are:
libmscore/score.cpp
libmscore/stafftype.cpp/.h
mscore/editstafftype.cpp (code for naming a new staff type)
mscore/instrdialog.cpp (building type list)
Note: as score files store staff type indications as integer indices and the number and order of new default staff types is different from the old built-in types, there is a compatibility issue with old 2.0 score which use percussion and tab staves. In Score::read() (libmscore/scorefile.cpp), there is a rudimentary attempt to cope with this.Old scores will need manual fix anyway. There should not be any (new) compatibility issue with 1.x scores, as they did not use staff types.
2013-08-18 11:55:31 +02:00
|
|
|
case STANDARD_STAFF_GROUP: {
|
2012-08-09 11:18:36 +02:00
|
|
|
AccidentalVal acci = s->measure()->findAccidental(s, staffIdx, line);
|
2012-08-08 20:46:29 +02:00
|
|
|
int step = absStep(line, clef);
|
|
|
|
int octave = step/7;
|
|
|
|
nval.pitch = step2pitch(step) + octave * 12 + acci;
|
|
|
|
nval.tpc = step2tpc(step % 7, acci);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
expandVoice();
|
|
|
|
ChordRest* cr = _is.cr();
|
|
|
|
bool addToChord = false;
|
|
|
|
|
|
|
|
if (cr) {
|
2012-09-17 13:34:59 +02:00
|
|
|
// retrieve total duration of current chord
|
2012-05-26 14:26:10 +02:00
|
|
|
TDuration d = cr->durationType();
|
2012-09-17 13:34:59 +02:00
|
|
|
// if not in replace mode AND chord duration == input duration AND not rest input
|
|
|
|
// we need to add to current chord (otherwise, we will need to replace it or create a new onw)
|
2012-05-26 14:26:10 +02:00
|
|
|
if (!replace
|
|
|
|
&& (d == _is.duration())
|
2012-09-13 18:01:34 +02:00
|
|
|
&& (cr->type() == Element::CHORD)
|
2012-05-26 14:26:10 +02:00
|
|
|
&& !_is.rest)
|
|
|
|
{
|
2012-09-17 13:34:59 +02:00
|
|
|
if (st->isTabStaff()) { // TAB
|
|
|
|
// if a note on same string already exists, update to new pitch/fret
|
|
|
|
foreach(Note * note, static_cast<Chord*>(cr)->notes())
|
2013-09-15 01:07:24 +02:00
|
|
|
if(note->string() == nval.string) { // if string is the same
|
|
|
|
// if adding a new digit will keep fret number within fret limit,
|
|
|
|
// add a digit to existing fret number
|
|
|
|
if (stringData && tab->useNumbers() && note->fret() >= 1) {
|
2013-03-31 00:06:33 +01:00
|
|
|
int fret = note->fret() * 10 + nval.fret;
|
2013-09-15 01:07:24 +02:00
|
|
|
if (fret <= stringData->frets() ) {
|
2013-03-31 00:06:33 +01:00
|
|
|
nval.fret = fret;
|
2013-09-15 01:07:24 +02:00
|
|
|
nval.pitch = stringData->getPitch(nval.string, nval.fret);
|
2013-03-31 00:06:33 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
qDebug("can't increase fret to %d", fret);
|
|
|
|
}
|
2013-09-15 01:07:24 +02:00
|
|
|
// set fret number (orignal or combined) in all linked notes
|
|
|
|
nval.tpc = pitch2tpc(nval.pitch, KEY_C, PREFER_NEAREST);
|
|
|
|
foreach (Element* e, note->linkList()) {
|
|
|
|
Note* linkedNote = static_cast<Note*>(e);
|
|
|
|
Staff* staff = linkedNote->staff();
|
|
|
|
if (staff->isTabStaff()) {
|
|
|
|
(static_cast<Note*>(linkedNote))->undoChangeProperty(P_PITCH, nval.pitch);
|
|
|
|
(static_cast<Note*>(linkedNote))->undoChangeProperty(P_TPC, nval.tpc);
|
|
|
|
(static_cast<Note*>(linkedNote))->undoChangeProperty(P_FRET, nval.fret);
|
|
|
|
(static_cast<Note*>(linkedNote))->undoChangeProperty(P_STRING,nval.string);
|
|
|
|
}
|
|
|
|
else if (staff->isPitchedStaff())
|
|
|
|
undoChangePitch(linkedNote, nval.pitch, nval.tpc, linkedNote->line());
|
|
|
|
}
|
2012-09-17 13:34:59 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else { // not TAB
|
2012-09-16 11:56:27 +02:00
|
|
|
// if a note with the same pitch already exists in the chord, remove it
|
|
|
|
Chord* chord = static_cast<Chord*>(cr);
|
2013-09-08 19:44:44 +02:00
|
|
|
Note* note = chord->findNote(nval.pitch);
|
2012-09-16 11:56:27 +02:00
|
|
|
if (note) {
|
|
|
|
if (chord->notes().size() > 1)
|
|
|
|
undoRemoveElement(note);
|
|
|
|
return;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2012-09-17 13:34:59 +02:00
|
|
|
addToChord = true; // if no special case, add note to chord
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
2012-11-23 16:18:40 +01:00
|
|
|
if (addToChord && cr->type() == Element::CHORD) {
|
|
|
|
// if adding, add!
|
2012-08-29 18:42:28 +02:00
|
|
|
addNote(static_cast<Chord*>(cr), nval);
|
2013-09-16 13:03:58 +02:00
|
|
|
return;
|
2012-11-23 16:18:40 +01:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
else {
|
2012-11-23 16:18:40 +01:00
|
|
|
// if not adding, replace current chord (or create a new one)
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
if (_is.rest)
|
|
|
|
nval.pitch = -1;
|
|
|
|
setNoteRest(_is.segment(), _is.track(), nval, _is.duration().fraction(), stemDirection);
|
|
|
|
}
|
2012-11-23 16:18:40 +01:00
|
|
|
if (!st->isTabStaff())
|
2012-08-29 18:42:28 +02:00
|
|
|
moveToNextInputPos();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2013-04-18 11:41:14 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// repitchNote
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::repitchNote(const Position& p, bool replace)
|
|
|
|
{
|
|
|
|
Segment* s = p.segment;
|
|
|
|
int tick = s->tick();
|
|
|
|
Staff* st = staff(p.staffIdx);
|
|
|
|
ClefType clef = st->clef(tick);
|
|
|
|
|
|
|
|
NoteVal nval;
|
|
|
|
AccidentalVal acci = s->measure()->findAccidental(s, p.staffIdx, p.line);
|
|
|
|
int step = absStep(p.line, clef);
|
|
|
|
int octave = step / 7;
|
|
|
|
nval.pitch = step2pitch(step) + octave * 12 + acci;
|
|
|
|
nval.tpc = step2tpc(step % 7, acci);
|
|
|
|
|
|
|
|
Chord* chord;
|
2013-09-16 13:03:58 +02:00
|
|
|
if (_is.cr()->type() == Element::REST) { //skip rests
|
|
|
|
ChordRest* next = nextChordRest(_is.cr());
|
|
|
|
while(next && next->type() != Element::CHORD)
|
|
|
|
next = nextChordRest(next);
|
|
|
|
if(next)
|
|
|
|
moveInputPos(next->segment());
|
|
|
|
return;
|
2013-04-18 11:41:14 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
chord = static_cast<Chord*>(_is.cr());
|
|
|
|
}
|
|
|
|
Note* note = new Note(this);
|
|
|
|
note->setNval(nval);
|
|
|
|
note->setParent(chord);
|
|
|
|
|
|
|
|
if (replace) {
|
|
|
|
while (!chord->notes().isEmpty())
|
|
|
|
undoRemoveElement(chord->notes().first());
|
|
|
|
}
|
|
|
|
undoAddElement(note);
|
2013-09-16 13:03:58 +02:00
|
|
|
// move to next Chord
|
|
|
|
ChordRest* next = nextChordRest(_is.cr());
|
|
|
|
while(next && next->type() != Element::CHORD)
|
|
|
|
next = nextChordRest(next);
|
|
|
|
if(next)
|
|
|
|
moveInputPos(next->segment());
|
2013-04-18 11:41:14 +02:00
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdAddTie
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdAddTie()
|
|
|
|
{
|
|
|
|
QList<Note*> noteList;
|
|
|
|
Element* el = selection().element();
|
2012-09-13 18:01:34 +02:00
|
|
|
if (el && el->type() == Element::NOTE)
|
2012-05-26 14:26:10 +02:00
|
|
|
noteList.append(static_cast<Note*>(el));
|
2012-09-13 18:01:34 +02:00
|
|
|
else if (el && el->type() == Element::STEM) {
|
2012-05-26 14:26:10 +02:00
|
|
|
Chord* chord = static_cast<Stem*>(el)->chord();
|
|
|
|
noteList = chord->notes();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
noteList = selection().noteList();
|
|
|
|
if (noteList.isEmpty()) {
|
|
|
|
qDebug("no notes selected");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
startCmd();
|
|
|
|
foreach (Note* note, noteList) {
|
2013-08-31 12:44:59 +02:00
|
|
|
if(note->chord() && note->chord()->isGrace())
|
|
|
|
continue;
|
2012-05-26 14:26:10 +02:00
|
|
|
if (note->tieFor()) {
|
2013-07-24 10:02:50 +02:00
|
|
|
qDebug("cmdAddTie: note %p has already tie? noteFor: %p", note, note->tieFor());
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
Chord* chord = note->chord();
|
|
|
|
if (noteEntryMode()) {
|
2013-07-24 10:02:50 +02:00
|
|
|
if (note->chord() == _is.cr()) // if noteentry is started
|
|
|
|
break;
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
if (_is.cr() == 0) {
|
|
|
|
if (MScore::debugMode)
|
|
|
|
qDebug("cmdAddTie: no pos");
|
|
|
|
expandVoice();
|
|
|
|
}
|
|
|
|
if (_is.cr() == 0)
|
|
|
|
break;
|
2012-09-13 18:01:34 +02:00
|
|
|
bool addFlag = _is.cr()->type() == Element::CHORD;
|
2012-05-26 14:26:10 +02:00
|
|
|
Note* n = addPitch(note->pitch(), addFlag);
|
|
|
|
if (n) {
|
2012-08-06 14:34:13 +02:00
|
|
|
// n is not necessarly next note if duration span over measure
|
|
|
|
Note* nnote = searchTieNote(note);
|
|
|
|
if (nnote) {
|
|
|
|
n->setLine(note->line());
|
|
|
|
n->setTpc(note->tpc());
|
|
|
|
Tie* tie = new Tie(this);
|
|
|
|
tie->setStartNote(note);
|
|
|
|
tie->setEndNote(nnote);
|
|
|
|
tie->setTrack(note->track());
|
|
|
|
undoAddElement(tie);
|
|
|
|
nextInputPos(n->chord(), false);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
Note* note2 = searchTieNote(note);
|
|
|
|
Part* part = chord->staff()->part();
|
|
|
|
int strack = part->staves()->front()->idx() * VOICES;
|
|
|
|
int etrack = strack + part->staves()->size() * VOICES;
|
|
|
|
|
2013-06-12 14:23:57 +02:00
|
|
|
for (Segment* seg = chord->segment()->next1(Segment::SegChordRest); seg; seg = seg->next1(Segment::SegChordRest)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
bool noteFound = false;
|
|
|
|
for (int track = strack; track < etrack; ++track) {
|
|
|
|
ChordRest* cr = static_cast<ChordRest*>(seg->element(track));
|
2012-09-13 18:01:34 +02:00
|
|
|
if (cr == 0 || cr->type() != Element::CHORD)
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
int staffIdx = cr->staffIdx() + cr->staffMove();
|
2012-07-05 14:10:43 +02:00
|
|
|
if (staffIdx != chord->staffIdx() + chord->staffMove())
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
foreach(Note* n, static_cast<Chord*>(cr)->notes()) {
|
|
|
|
if (n->pitch() == note->pitch()) {
|
|
|
|
if (note2 == 0 || note->chord()->track() == chord->track())
|
|
|
|
note2 = n;
|
|
|
|
}
|
|
|
|
else if (cr->track() == chord->track())
|
|
|
|
noteFound = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (noteFound || note2)
|
|
|
|
break;
|
|
|
|
}
|
2013-06-16 23:33:37 +02:00
|
|
|
if (note2) {
|
2013-06-05 16:18:31 +02:00
|
|
|
Tie* tie = new Tie(this);
|
|
|
|
tie->setStartNote(note);
|
|
|
|
tie->setEndNote(note2);
|
|
|
|
tie->setTrack(note->track());
|
|
|
|
undoAddElement(tie);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
endCmd();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdAddHairpin
|
|
|
|
// 'H' typed on keyboard
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdAddHairpin(bool decrescendo)
|
|
|
|
{
|
|
|
|
ChordRest* cr1;
|
|
|
|
ChordRest* cr2;
|
|
|
|
getSelectedChordRest2(&cr1, &cr2);
|
|
|
|
if (!cr1)
|
|
|
|
return;
|
|
|
|
if (cr2 == 0)
|
|
|
|
cr2 = nextChordRest(cr1);
|
|
|
|
if (cr2 == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Hairpin* pin = new Hairpin(this);
|
2013-03-05 20:23:59 +01:00
|
|
|
pin->setHairpinType(decrescendo ? Hairpin::DECRESCENDO : Hairpin::CRESCENDO);
|
2012-05-26 14:26:10 +02:00
|
|
|
pin->setTrack(cr1->track());
|
2013-06-10 11:03:34 +02:00
|
|
|
pin->setTick(cr1->segment()->tick());
|
2013-06-25 14:29:18 +02:00
|
|
|
pin->setTick2(cr2->segment()->tick());
|
2012-05-26 14:26:10 +02:00
|
|
|
undoAddElement(pin);
|
|
|
|
if (!noteEntryMode())
|
|
|
|
select(pin, SELECT_SINGLE, 0);
|
|
|
|
}
|
|
|
|
|
2013-08-12 10:37:30 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdAddOttava
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdAddOttava(OttavaType type)
|
|
|
|
{
|
|
|
|
ChordRest* cr1;
|
|
|
|
ChordRest* cr2;
|
|
|
|
getSelectedChordRest2(&cr1, &cr2);
|
|
|
|
if (!cr1)
|
|
|
|
return;
|
|
|
|
if (cr2 == 0)
|
|
|
|
cr2 = cr1;
|
|
|
|
|
|
|
|
Ottava* ottava = new Ottava(this);
|
|
|
|
ottava->setOttavaType(type);
|
|
|
|
|
|
|
|
ottava->setTrack(cr1->track());
|
|
|
|
ottava->setTick(cr1->tick());
|
|
|
|
ottava->setTick2(cr2->tick());
|
|
|
|
undoAddElement(ottava);
|
|
|
|
if (!noteEntryMode())
|
|
|
|
select(ottava, SELECT_SINGLE, 0);
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdSetBeamMode
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-03-05 20:23:59 +01:00
|
|
|
void Score::cmdSetBeamMode(BeamMode mode)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
ChordRest* cr = getSelectedChordRest();
|
|
|
|
if (cr == 0)
|
|
|
|
return;
|
2013-03-05 20:23:59 +01:00
|
|
|
cr->setBeamMode(mode);
|
2012-05-26 14:26:10 +02:00
|
|
|
_layoutAll = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdFlip
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdFlip()
|
|
|
|
{
|
|
|
|
const QList<Element*>& el = selection().elements();
|
|
|
|
if (el.isEmpty()) {
|
|
|
|
selectNoteSlurMessage();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
foreach (Element* e, el) {
|
2012-09-13 18:01:34 +02:00
|
|
|
if (e->type() == Element::NOTE || e->type() == Element::STEM || e->type() == Element::HOOK) {
|
2012-08-09 11:18:36 +02:00
|
|
|
Chord* chord;
|
2012-09-13 18:01:34 +02:00
|
|
|
if (e->type() == Element::NOTE)
|
2012-08-09 11:18:36 +02:00
|
|
|
chord = static_cast<Note*>(e)->chord();
|
2012-09-13 18:01:34 +02:00
|
|
|
else if (e->type() == Element::STEM)
|
2012-05-26 14:26:10 +02:00
|
|
|
chord = static_cast<Stem*>(e)->chord();
|
2012-09-13 18:01:34 +02:00
|
|
|
else if (e->type() == Element::HOOK)
|
2012-05-26 14:26:10 +02:00
|
|
|
chord = static_cast<Hook*>(e)->chord();
|
|
|
|
if (chord->beam())
|
|
|
|
e = chord->beam(); // fall trough
|
|
|
|
else {
|
2012-08-04 15:46:43 +02:00
|
|
|
MScore::Direction dir = chord->up() ? MScore::DOWN : MScore::UP;
|
2012-05-26 14:26:10 +02:00
|
|
|
undoChangeProperty(chord, P_STEM_DIRECTION, dir);
|
|
|
|
}
|
|
|
|
}
|
2012-08-09 11:18:36 +02:00
|
|
|
|
2012-09-13 18:01:34 +02:00
|
|
|
if (e->type() == Element::BEAM) {
|
2012-05-26 14:26:10 +02:00
|
|
|
Beam* beam = static_cast<Beam*>(e);
|
2012-08-04 15:46:43 +02:00
|
|
|
MScore::Direction dir = beam->up() ? MScore::DOWN : MScore::UP;
|
2012-05-26 14:26:10 +02:00
|
|
|
undoChangeProperty(beam, P_STEM_DIRECTION, dir);
|
|
|
|
}
|
2012-09-13 18:01:34 +02:00
|
|
|
else if (e->type() == Element::SLUR_SEGMENT) {
|
2012-05-26 14:26:10 +02:00
|
|
|
SlurTie* slur = static_cast<SlurSegment*>(e)->slurTie();
|
2012-08-04 15:46:43 +02:00
|
|
|
MScore::Direction dir = slur->up() ? MScore::DOWN : MScore::UP;
|
2012-05-26 14:26:10 +02:00
|
|
|
undoChangeProperty(slur, P_SLUR_DIRECTION, dir);
|
|
|
|
}
|
2012-09-13 18:01:34 +02:00
|
|
|
else if (e->type() == Element::HAIRPIN_SEGMENT) {
|
2013-03-05 20:23:59 +01:00
|
|
|
int st = static_cast<Hairpin*>(e)->hairpinType() == 0 ? 1 : 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
e->score()->undoChangeProperty(e, P_SUBTYPE, st);
|
|
|
|
}
|
2012-09-13 18:01:34 +02:00
|
|
|
else if (e->type() == Element::ARTICULATION) {
|
2012-05-26 14:26:10 +02:00
|
|
|
Articulation* a = static_cast<Articulation*>(e);
|
2013-03-05 20:23:59 +01:00
|
|
|
if (a->articulationType() == Articulation_Staccato
|
|
|
|
|| a->articulationType() == Articulation_Tenuto
|
|
|
|
|| a->articulationType() == Articulation_Sforzatoaccent) {
|
2012-05-26 14:26:10 +02:00
|
|
|
ArticulationAnchor aa = a->anchor();
|
|
|
|
if (aa == A_TOP_CHORD)
|
|
|
|
aa = A_BOTTOM_CHORD;
|
|
|
|
else if (aa == A_BOTTOM_CHORD)
|
|
|
|
aa = A_TOP_CHORD;
|
|
|
|
else if (aa == A_CHORD)
|
|
|
|
aa = a->up() ? A_BOTTOM_CHORD : A_TOP_CHORD;
|
|
|
|
if (aa != a->anchor())
|
|
|
|
undoChangeProperty(a, P_ARTICULATION_ANCHOR, aa);
|
|
|
|
}
|
|
|
|
else {
|
2012-08-04 15:46:43 +02:00
|
|
|
MScore::Direction d = a->up() ? MScore::DOWN : MScore::UP;
|
2012-05-26 14:26:10 +02:00
|
|
|
undoChangeProperty(a, P_DIRECTION, d);
|
|
|
|
}
|
|
|
|
return; // no layoutAll
|
|
|
|
}
|
2012-09-13 18:01:34 +02:00
|
|
|
else if (e->type() == Element::TUPLET) {
|
2012-05-26 14:26:10 +02:00
|
|
|
Tuplet* tuplet = static_cast<Tuplet*>(e);
|
2012-08-04 15:46:43 +02:00
|
|
|
MScore::Direction d = tuplet->isUp() ? MScore::DOWN : MScore::UP;
|
2012-05-26 14:26:10 +02:00
|
|
|
undoChangeProperty(tuplet, P_DIRECTION, d);
|
|
|
|
}
|
2012-09-13 18:01:34 +02:00
|
|
|
else if (e->type() == Element::NOTEDOT)
|
2012-05-26 14:26:10 +02:00
|
|
|
undo(new FlipNoteDotDirection(static_cast<Note*>(e->parent())));
|
2012-10-31 14:39:14 +01:00
|
|
|
else if (
|
|
|
|
(e->type() == Element::TEMPO_TEXT)
|
|
|
|
| (e->type() == Element::DYNAMIC)
|
|
|
|
| (e->type() == Element::HAIRPIN)
|
|
|
|
| (e->type() == Element::DYNAMIC)
|
|
|
|
) {
|
|
|
|
Element::Placement p = e->placement() == Element::ABOVE ? Element::BELOW : Element::ABOVE;
|
2012-10-31 10:15:45 +01:00
|
|
|
undoChangeProperty(e, P_PLACEMENT, p);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
_layoutAll = true; // must be set in und/redo
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// deleteItem
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::deleteItem(Element* el)
|
|
|
|
{
|
2013-09-28 11:02:51 +02:00
|
|
|
if (!el)
|
2013-06-12 18:32:48 +02:00
|
|
|
return;
|
2012-05-26 14:26:10 +02:00
|
|
|
switch(el->type()) {
|
2012-09-13 18:01:34 +02:00
|
|
|
case Element::INSTRUMENT_NAME: {
|
2012-05-26 14:26:10 +02:00
|
|
|
Part* part = el->staff()->part();
|
|
|
|
InstrumentName* in = static_cast<InstrumentName*>(el);
|
2013-03-05 20:23:59 +01:00
|
|
|
if (in->instrumentNameType() == INSTRUMENT_NAME_LONG)
|
2012-05-26 14:26:10 +02:00
|
|
|
undo(new ChangeInstrumentLong(0, part, QList<StaffNameDoc>()));
|
2013-03-05 20:23:59 +01:00
|
|
|
else if (in->instrumentNameType() == INSTRUMENT_NAME_SHORT)
|
2012-05-26 14:26:10 +02:00
|
|
|
undo(new ChangeInstrumentShort(0, part, QList<StaffNameDoc>()));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2012-09-13 18:01:34 +02:00
|
|
|
case Element::TIMESIG:
|
2012-05-26 14:26:10 +02:00
|
|
|
cmdRemoveTimeSig(static_cast<TimeSig*>(el));
|
|
|
|
break;
|
|
|
|
|
2012-09-13 18:01:34 +02:00
|
|
|
case Element::OTTAVA_SEGMENT:
|
|
|
|
case Element::HAIRPIN_SEGMENT:
|
|
|
|
case Element::TRILL_SEGMENT:
|
|
|
|
case Element::TEXTLINE_SEGMENT:
|
|
|
|
case Element::VOLTA_SEGMENT:
|
|
|
|
case Element::SLUR_SEGMENT:
|
2013-03-21 18:00:16 +01:00
|
|
|
case Element::PEDAL_SEGMENT:
|
2012-05-26 14:26:10 +02:00
|
|
|
undoRemoveElement(static_cast<SpannerSegment*>(el)->spanner());
|
|
|
|
break;
|
|
|
|
|
2012-09-13 18:01:34 +02:00
|
|
|
case Element::NOTE:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
Chord* chord = static_cast<Chord*>(el->parent());
|
|
|
|
if (chord->notes().size() > 1) {
|
|
|
|
undoRemoveElement(el);
|
2013-09-21 19:09:56 +02:00
|
|
|
select(chord->downNote(), SELECT_SINGLE, 0);
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
// else fall through
|
|
|
|
el = chord;
|
|
|
|
}
|
|
|
|
|
2012-09-13 18:01:34 +02:00
|
|
|
case Element::CHORD:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
Chord* chord = static_cast<Chord*>(el);
|
|
|
|
removeChordRest(chord, false);
|
|
|
|
|
|
|
|
// replace with rest if voice 0 or if in tuplet
|
|
|
|
Tuplet* tuplet = chord->tuplet();
|
|
|
|
// if ((el->voice() == 0 || tuplet) && (chord->noteType() == NOTE_NORMAL)) {
|
|
|
|
if (chord->noteType() == NOTE_NORMAL) {
|
|
|
|
Rest* rest = new Rest(this, chord->durationType());
|
|
|
|
rest->setDurationType(chord->durationType());
|
|
|
|
rest->setDuration(chord->duration());
|
2012-08-08 20:46:29 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
rest->setTrack(el->track());
|
|
|
|
rest->setParent(chord->parent());
|
|
|
|
Segment* segment = chord->segment();
|
|
|
|
undoAddCR(rest, segment->measure(), segment->tick());
|
2012-08-08 20:46:29 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
// undoAddElement(rest);
|
|
|
|
if (tuplet) {
|
|
|
|
tuplet->add(rest);
|
|
|
|
rest->setTuplet(tuplet);
|
|
|
|
rest->setDurationType(chord->durationType());
|
|
|
|
}
|
|
|
|
select(rest, SELECT_SINGLE, 0);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// remove segment if empty
|
|
|
|
Segment* seg = chord->segment();
|
|
|
|
if (seg->isEmpty())
|
|
|
|
undoRemoveElement(seg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2013-02-21 13:59:44 +01:00
|
|
|
case Element::REPEAT_MEASURE:
|
|
|
|
{
|
|
|
|
RepeatMeasure* rm = static_cast<RepeatMeasure*>(el);
|
|
|
|
removeChordRest(rm, false);
|
|
|
|
Rest* rest = new Rest(this);
|
|
|
|
rest->setDurationType(TDuration::V_MEASURE);
|
|
|
|
rest->setDuration(rm->measure()->len());
|
|
|
|
rest->setTrack(rm->track());
|
|
|
|
rest->setParent(rm->parent());
|
|
|
|
Segment* segment = rm->segment();
|
|
|
|
undoAddCR(rest, segment->measure(), segment->tick());
|
|
|
|
}
|
|
|
|
|
2012-09-13 18:01:34 +02:00
|
|
|
case Element::REST:
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
// only allow for voices != 0
|
|
|
|
// e.g. voice 0 rests cannot be removed
|
|
|
|
//
|
|
|
|
{
|
|
|
|
Rest* rest = static_cast<Rest*>(el);
|
|
|
|
if (rest->tuplet() && rest->tuplet()->elements().empty())
|
|
|
|
undoRemoveElement(rest->tuplet());
|
|
|
|
if (el->voice() != 0)
|
|
|
|
undoRemoveElement(el);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2012-09-13 18:01:34 +02:00
|
|
|
case Element::ACCIDENTAL:
|
2013-08-30 16:03:46 +02:00
|
|
|
if (el->parent()->type() == Element::NOTE)
|
|
|
|
changeAccidental(static_cast<Note*>(el->parent()), Accidental::ACC_NONE);
|
|
|
|
else
|
|
|
|
undoRemoveElement(el);
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
|
2012-09-13 18:01:34 +02:00
|
|
|
case Element::BAR_LINE:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
BarLine* bl = static_cast<BarLine*>(el);
|
2012-10-11 11:14:45 +02:00
|
|
|
if (bl->parent()->type() != Element::SEGMENT)
|
|
|
|
break;
|
2013-02-27 14:41:04 +01:00
|
|
|
Segment* seg = static_cast<Segment*>(bl->parent());
|
|
|
|
bool normalBar = seg->measure()->endBarLineType() == NORMAL_BAR;
|
|
|
|
int tick = seg->tick();
|
2013-03-05 20:23:59 +01:00
|
|
|
Segment::SegmentType segType = seg->segmentType();
|
2013-02-27 14:41:04 +01:00
|
|
|
|
|
|
|
foreach(Score* score, scoreList()) {
|
|
|
|
Measure* m = score->tick2measure(tick);
|
|
|
|
if (segType == Segment::SegStartRepeatBarLine)
|
|
|
|
undoChangeProperty(m, P_REPEAT_FLAGS, m->repeatFlags() & ~RepeatStart);
|
|
|
|
else if (segType == Segment::SegBarLine)
|
|
|
|
undoRemoveElement(el);
|
|
|
|
else if (segType == Segment::SegEndBarLine) {
|
2013-03-23 00:57:37 +01:00
|
|
|
// if bar line has custom barLineType, change to barLineType of the whole measure
|
|
|
|
if (bl->customSubtype()) {
|
|
|
|
undoChangeProperty(bl, P_SUBTYPE, seg->measure()->endBarLineType());
|
|
|
|
}
|
|
|
|
// otherwise, if whole measure has special end bar line, change to normal
|
|
|
|
else if (!normalBar) {
|
2013-03-20 17:51:03 +01:00
|
|
|
if (m->tick() >= tick)
|
|
|
|
m = m->prevMeasure();
|
2013-02-27 14:41:04 +01:00
|
|
|
undoChangeProperty(m, P_REPEAT_FLAGS, m->repeatFlags() & ~RepeatEnd);
|
|
|
|
Measure* nm = m->nextMeasure();
|
|
|
|
if (nm)
|
|
|
|
undoChangeProperty(nm, P_REPEAT_FLAGS, nm->repeatFlags() & ~RepeatStart);
|
|
|
|
undoChangeEndBarLineType(m, NORMAL_BAR);
|
|
|
|
m->setEndBarLineGenerated(true);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2013-09-28 11:02:51 +02:00
|
|
|
|
2012-09-13 18:01:34 +02:00
|
|
|
case Element::TUPLET:
|
2012-05-26 14:26:10 +02:00
|
|
|
cmdDeleteTuplet(static_cast<Tuplet*>(el), true);
|
|
|
|
break;
|
|
|
|
|
2013-06-16 23:33:37 +02:00
|
|
|
case Element::MEASURE:
|
2013-06-19 16:25:29 +02:00
|
|
|
undoRemoveMeasures(static_cast<Measure*>(el), static_cast<Measure*>(el));
|
2013-06-16 23:33:37 +02:00
|
|
|
break;
|
|
|
|
|
2013-06-28 17:46:24 +02:00
|
|
|
case Element::BRACKET:
|
|
|
|
undoRemoveBracket(static_cast<Bracket*>(el));
|
|
|
|
break;
|
|
|
|
|
2013-09-27 18:43:25 +02:00
|
|
|
case Element::LAYOUT_BREAK:
|
|
|
|
{
|
|
|
|
undoRemoveElement(el);
|
|
|
|
LayoutBreak* lb = static_cast<LayoutBreak*>(el);
|
|
|
|
Measure* m = lb->measure();
|
|
|
|
if (m->isMMRest()) {
|
2013-09-28 11:02:51 +02:00
|
|
|
// propagate to original measure
|
2013-09-27 18:43:25 +02:00
|
|
|
m = static_cast<Measure*>(m->next()->prev());
|
|
|
|
foreach(Element* e, *m->el()) {
|
|
|
|
if (e->type() == Element::LAYOUT_BREAK) {
|
|
|
|
undoRemoveElement(e);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-09-28 11:02:51 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Element::CLEF:
|
|
|
|
{
|
|
|
|
undoRemoveElement(el);
|
|
|
|
Clef* clef = static_cast<Clef*>(el);
|
|
|
|
Measure* m = clef->measure();
|
|
|
|
if (m->isMMRest()) {
|
|
|
|
// propagate to original measure
|
|
|
|
m = static_cast<Measure*>(m->next()->prev());
|
|
|
|
Segment* s = m->findSegment(Segment::SegClef, clef->segment()->tick());
|
|
|
|
if (s && s->element(clef->track()))
|
|
|
|
undoRemoveElement(s->element(clef->track()));
|
|
|
|
}
|
|
|
|
}
|
2013-09-27 18:43:25 +02:00
|
|
|
break;
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
default:
|
|
|
|
undoRemoveElement(el);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdDeleteSelectedMeasures
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdDeleteSelectedMeasures()
|
|
|
|
{
|
|
|
|
if (selection().state() != SEL_RANGE)
|
|
|
|
return;
|
|
|
|
|
|
|
|
MeasureBase* is = selection().startSegment()->measure();
|
|
|
|
int startIdx = measureIdx(is);
|
|
|
|
Segment* seg = selection().endSegment();
|
2013-02-27 13:05:40 +01:00
|
|
|
MeasureBase* ie;
|
|
|
|
// choose the correct last measure based on the end segment
|
|
|
|
// this depends on whether a whole measure is selected or only a few notes within it
|
|
|
|
if (seg)
|
|
|
|
ie = !seg->prev() ? seg->measure()->prev() : seg->measure();
|
|
|
|
else
|
|
|
|
ie = lastMeasure();
|
2012-05-26 14:26:10 +02:00
|
|
|
int endIdx = measureIdx(ie);
|
2013-02-27 22:34:49 +01:00
|
|
|
Measure* mBeforeSel = is->prevMeasure();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
// createEndBar if last measure is deleted
|
2012-12-09 12:29:36 +01:00
|
|
|
bool createEndBar = false;
|
|
|
|
if (ie->type() == Element::MEASURE) {
|
|
|
|
Measure* iem = static_cast<Measure*>(ie);
|
|
|
|
createEndBar = (iem == lastMeasure()) && (iem->endBarLineType() == END_BAR);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2013-02-27 22:34:49 +01:00
|
|
|
// get the last deleted timesig in order to restore after deletion
|
|
|
|
TimeSig* lastDeletedSig = 0;
|
|
|
|
for (MeasureBase* mb = ie;; mb = mb->prev()) {
|
|
|
|
if (mb->type() == Element::MEASURE) {
|
|
|
|
Measure* m = static_cast<Measure*>(mb);
|
|
|
|
Segment* sts = m->findSegment(Segment::SegTimeSig, m->tick());
|
|
|
|
if (sts) {
|
|
|
|
lastDeletedSig = static_cast<TimeSig*>(sts->element(0));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mb == is)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
QList<Score*> scores = scoreList();
|
2013-06-20 15:25:11 +02:00
|
|
|
int startTick = measure(startIdx)->tick();
|
2013-07-24 14:33:35 +02:00
|
|
|
int endTick = measure(endIdx)->tick();
|
2013-06-16 23:33:37 +02:00
|
|
|
foreach (Score* score, scores) {
|
2013-07-24 14:33:35 +02:00
|
|
|
Measure* is = score->tick2measure(startTick);
|
|
|
|
Measure* ie = score->tick2measure(endTick);
|
2013-06-20 15:25:11 +02:00
|
|
|
mBeforeSel = is->prevMeasure();
|
2013-07-24 14:33:35 +02:00
|
|
|
|
2013-06-19 16:25:29 +02:00
|
|
|
int ticks = 0;
|
2013-07-24 14:33:35 +02:00
|
|
|
for (Measure* m = is; m; m = m->nextMeasure()) {
|
|
|
|
ticks += m->ticks();
|
|
|
|
if (m == ie)
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
}
|
2013-07-24 14:33:35 +02:00
|
|
|
|
2013-07-24 14:46:26 +02:00
|
|
|
#if 0
|
2013-07-24 14:33:35 +02:00
|
|
|
// remove spanner
|
|
|
|
std::list<Spanner*> sl;
|
|
|
|
int tick2 = startTick + ticks;
|
|
|
|
for (auto i : score->_spanner.map()) {
|
|
|
|
Spanner* s = i.second;
|
|
|
|
if (s->tick() >= startTick && s->tick() < tick2)
|
|
|
|
sl.push_back(s);
|
|
|
|
}
|
|
|
|
for (Spanner* s : sl)
|
|
|
|
score->undoRemoveElement(s);
|
2013-07-24 14:46:26 +02:00
|
|
|
#endif
|
2013-07-24 14:33:35 +02:00
|
|
|
undoRemoveMeasures(is, ie);
|
|
|
|
|
2013-06-20 15:25:11 +02:00
|
|
|
if (createEndBar) {
|
2013-07-24 14:33:35 +02:00
|
|
|
Measure* lastMeasure = score->lastMeasure();
|
|
|
|
if (lastMeasure && lastMeasure->endBarLineType() == NORMAL_BAR)
|
|
|
|
undoChangeEndBarLineType(lastMeasure, END_BAR);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2013-07-24 14:33:35 +02:00
|
|
|
|
2013-06-20 15:25:11 +02:00
|
|
|
// insert correct timesig after deletion
|
|
|
|
Measure* mAfterSel = mBeforeSel ? mBeforeSel->nextMeasure() : firstMeasure();
|
|
|
|
if (mAfterSel && lastDeletedSig) {
|
|
|
|
bool changed = true;
|
|
|
|
if (mBeforeSel) {
|
|
|
|
if (mBeforeSel->timesig() == mAfterSel->timesig()) {
|
|
|
|
changed = false;
|
|
|
|
}
|
2013-02-27 22:34:49 +01:00
|
|
|
}
|
2013-06-20 15:25:11 +02:00
|
|
|
Segment* s = mAfterSel->findSegment(Segment::SegTimeSig, mAfterSel->tick());
|
|
|
|
if (!s && changed) {
|
|
|
|
Segment* ns = mAfterSel->undoGetSegment(Segment::SegTimeSig, mAfterSel->tick());
|
|
|
|
for (int staffIdx = 0; staffIdx < score->nstaves(); staffIdx++) {
|
|
|
|
TimeSig* nts = new TimeSig(score);
|
|
|
|
nts->setTrack(staffIdx * VOICES);
|
|
|
|
nts->setParent(ns);
|
|
|
|
nts->setSig(lastDeletedSig->sig(), lastDeletedSig->timeSigType());
|
|
|
|
score->undoAddElement(nts);
|
|
|
|
}
|
2013-02-27 22:34:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
select(0, SELECT_SINGLE, 0);
|
|
|
|
_is.setSegment(0); // invalidate position
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdDeleteSelection
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdDeleteSelection()
|
|
|
|
{
|
|
|
|
if (selection().state() == SEL_RANGE) {
|
|
|
|
Segment* s1 = selection().startSegment();
|
|
|
|
Segment* s2 = selection().endSegment();
|
|
|
|
|
|
|
|
Segment* ss1 = s1;
|
2013-03-05 20:23:59 +01:00
|
|
|
if (ss1->segmentType() != Segment::SegChordRest)
|
2012-08-03 15:54:02 +02:00
|
|
|
ss1 = ss1->next1(Segment::SegChordRest);
|
|
|
|
bool fullMeasure = ss1 && (ss1->measure()->first(Segment::SegChordRest) == ss1)
|
2013-03-05 20:23:59 +01:00
|
|
|
&& (s2 == 0 || (s2->segmentType() == Segment::SegEndBarLine));
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
int tick2 = s2 ? s2->tick() : INT_MAX;
|
|
|
|
int track1 = selection().staffStart() * VOICES;
|
|
|
|
int track2 = selection().staffEnd() * VOICES;
|
|
|
|
for (int track = track1; track < track2; ++track) {
|
|
|
|
Fraction f;
|
|
|
|
int tick = -1;
|
|
|
|
Tuplet* tuplet = 0;
|
|
|
|
for (Segment* s = s1; s != s2; s = s->next1()) {
|
2013-06-12 14:23:57 +02:00
|
|
|
if (s->element(track) && s->segmentType() == Segment::SegBreath) {
|
2012-05-26 14:26:10 +02:00
|
|
|
deleteItem(s->element(track));
|
|
|
|
continue;
|
|
|
|
}
|
2013-03-05 20:23:59 +01:00
|
|
|
if (s->segmentType() != Segment::SegChordRest || !s->element(track))
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
ChordRest* cr = static_cast<ChordRest*>(s->element(track));
|
|
|
|
if (tick == -1) {
|
|
|
|
// first ChordRest found:
|
|
|
|
int offset = cr->tick() - s->measure()->tick();
|
|
|
|
if (cr->measure()->tick() >= s1->tick() && offset) {
|
|
|
|
f = Fraction::fromTicks(offset);
|
|
|
|
tick = s->measure()->tick();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
tick = s->tick();
|
|
|
|
f = Fraction();
|
|
|
|
}
|
|
|
|
tuplet = cr->tuplet();
|
|
|
|
if (tuplet && (tuplet->tick() == tick) && ((tuplet->tick() + tuplet->actualTicks()) < tick2) ) {
|
|
|
|
// remove complete top level tuplet
|
|
|
|
|
|
|
|
Tuplet* t = cr->tuplet();
|
|
|
|
while (t->tuplet())
|
|
|
|
t = t->tuplet();
|
|
|
|
cmdDeleteTuplet(t, false);
|
|
|
|
f += t->duration();
|
|
|
|
tuplet = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (tuplet != cr->tuplet()) {
|
|
|
|
Tuplet* t = cr->tuplet();
|
|
|
|
if (t && (((t->tick() + t->actualTicks()) <= tick2) || fullMeasure)) {
|
|
|
|
// remove complete top level tuplet
|
|
|
|
|
|
|
|
while (t->tuplet())
|
|
|
|
t = t->tuplet();
|
|
|
|
cmdDeleteTuplet(t, false);
|
|
|
|
f += t->duration();
|
|
|
|
tuplet = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (f.isValid())
|
|
|
|
setRest(tick, track, f, false, tuplet);
|
|
|
|
tick = cr->tick();
|
|
|
|
tuplet = cr->tuplet();
|
|
|
|
removeChordRest(cr, true);
|
|
|
|
f = cr->duration();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
removeChordRest(cr, true);
|
|
|
|
f += cr->duration();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (f.isValid() && !f.isZero()) {
|
|
|
|
if (fullMeasure) {
|
|
|
|
// handle this as special case to be able to
|
|
|
|
// fix broken measures:
|
|
|
|
for (Measure* m = s1->measure(); m; m = m->nextMeasure()) {
|
|
|
|
setRest(m->tick(), track, Fraction(m->len()), false, 0);
|
|
|
|
if (s2 && (m == s2->measure()))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
setRest(tick, track, f, false, tuplet);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// deleteItem modifies selection().elements() list,
|
|
|
|
// so we need a local copy:
|
|
|
|
QList<Element*> el(selection().elements());
|
|
|
|
if (el.isEmpty())
|
|
|
|
qDebug("...nothing selected");
|
|
|
|
foreach(Element* e, el)
|
|
|
|
deleteItem(e);
|
|
|
|
if (!noteEntryMode()) // in entry mode deleting note or chord add rest selected
|
|
|
|
deselectAll();
|
|
|
|
}
|
|
|
|
_layoutAll = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// addLyrics
|
|
|
|
// called from Keyboard Accelerator & menue
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Lyrics* Score::addLyrics()
|
|
|
|
{
|
|
|
|
Element* el = selection().element();
|
2012-09-13 18:01:34 +02:00
|
|
|
if (el == 0 || (el->type() != Element::NOTE && el->type() != Element::LYRICS)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
QMessageBox::information(0,
|
|
|
|
QMessageBox::tr("MuseScore:"),
|
|
|
|
QMessageBox::tr("No note or lyrics selected:\n"
|
|
|
|
"Please select a single note or lyrics and retry operation\n"),
|
|
|
|
QMessageBox::Ok, QMessageBox::NoButton);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
ChordRest* cr;
|
2013-09-07 16:22:13 +02:00
|
|
|
if (el->type() == Element::NOTE) {
|
2012-05-26 14:26:10 +02:00
|
|
|
cr = static_cast<Note*>(el)->chord();
|
2013-09-07 16:22:13 +02:00
|
|
|
if(cr->isGrace())
|
|
|
|
cr = static_cast<ChordRest*>(cr->parent());
|
|
|
|
}
|
2012-09-13 18:01:34 +02:00
|
|
|
else if (el->type() == Element::LYRICS)
|
2012-05-26 14:26:10 +02:00
|
|
|
cr = static_cast<Lyrics*>(el)->chordRest();
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
QList<Lyrics*> ll = cr->lyricsList();
|
|
|
|
int no = ll.size();
|
|
|
|
Lyrics* lyrics = new Lyrics(this);
|
|
|
|
lyrics->setTrack(cr->track());
|
|
|
|
lyrics->setParent(cr);
|
|
|
|
lyrics->setNo(no);
|
|
|
|
undoAddElement(lyrics);
|
|
|
|
select(lyrics, SELECT_SINGLE, 0);
|
|
|
|
return lyrics;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdCreateTuplet
|
|
|
|
// replace cr with tuplet
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdCreateTuplet(ChordRest* ocr, Tuplet* tuplet)
|
|
|
|
{
|
2013-07-31 16:32:09 +02:00
|
|
|
qDebug("cmdCreateTuplet at %d <%s> track %d duration <%s> ratio <%s> baseLen <%s>",
|
|
|
|
ocr->tick(), ocr->name(), ocr->track(),
|
2012-05-26 14:26:10 +02:00
|
|
|
qPrintable(ocr->duration().print()),
|
|
|
|
qPrintable(tuplet->ratio().print()),
|
|
|
|
qPrintable(tuplet->baseLen().fraction().print())
|
|
|
|
);
|
|
|
|
|
|
|
|
int track = ocr->track();
|
|
|
|
Measure* measure = ocr->measure();
|
|
|
|
int tick = ocr->tick();
|
|
|
|
|
|
|
|
if (ocr->tuplet())
|
|
|
|
tuplet->setTuplet(ocr->tuplet());
|
|
|
|
undoRemoveElement(ocr);
|
|
|
|
|
|
|
|
ChordRest* cr;
|
2012-09-13 18:01:34 +02:00
|
|
|
if (ocr->type() == Element::CHORD) {
|
2012-05-26 14:26:10 +02:00
|
|
|
cr = new Chord(this);
|
|
|
|
foreach(Note* oldNote, static_cast<Chord*>(ocr)->notes()) {
|
|
|
|
Note* note = new Note(this);
|
|
|
|
note->setPitch(oldNote->pitch(), oldNote->tpc());
|
|
|
|
cr->add(note);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
cr = new Rest(this);
|
|
|
|
|
|
|
|
Fraction an = (tuplet->duration() * tuplet->ratio()) / tuplet->baseLen().fraction();
|
|
|
|
int actualNotes = an.numerator() / an.denominator();
|
|
|
|
|
2013-07-31 16:32:09 +02:00
|
|
|
tuplet->setTrack(track);
|
2012-05-26 14:26:10 +02:00
|
|
|
cr->setTuplet(tuplet);
|
|
|
|
cr->setTrack(track);
|
|
|
|
cr->setDurationType(tuplet->baseLen());
|
|
|
|
cr->setDuration(tuplet->baseLen().fraction());
|
|
|
|
|
2013-07-31 16:32:09 +02:00
|
|
|
qDebug("tuplet note duration %s actualNotes %d ticks %d track %d tuplet track %d",
|
|
|
|
qPrintable(tuplet->baseLen().name()), actualNotes, cr->actualTicks(), track, tuplet->track());
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
undoAddCR(cr, measure, tick);
|
|
|
|
|
|
|
|
int ticks = cr->actualTicks();
|
|
|
|
|
|
|
|
for (int i = 0; i < (actualNotes-1); ++i) {
|
|
|
|
tick += ticks;
|
|
|
|
Rest* rest = new Rest(this);
|
|
|
|
rest->setTuplet(tuplet);
|
|
|
|
rest->setTrack(track);
|
|
|
|
rest->setDurationType(tuplet->baseLen());
|
|
|
|
rest->setDuration(tuplet->baseLen().fraction());
|
|
|
|
undoAddCR(rest, measure, tick);
|
|
|
|
}
|
|
|
|
_layoutAll = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// colorItem
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::colorItem(Element* element)
|
|
|
|
{
|
|
|
|
QColor sc(element->color());
|
|
|
|
QColor c = QColorDialog::getColor(sc);
|
|
|
|
if (!c.isValid())
|
|
|
|
return;
|
|
|
|
|
|
|
|
foreach(Element* e, selection().elements()) {
|
|
|
|
if (e->color() != c) {
|
|
|
|
undoChangeProperty(e, P_COLOR, c);
|
|
|
|
e->setGenerated(false);
|
|
|
|
refresh |= e->abbox();
|
2012-09-13 18:01:34 +02:00
|
|
|
if (e->type() == Element::BAR_LINE) {
|
2012-05-26 14:26:10 +02:00
|
|
|
Element* ep = e->parent();
|
2013-03-05 20:23:59 +01:00
|
|
|
if (ep->type() == Element::SEGMENT && static_cast<Segment*>(ep)->segmentType() == Segment::SegEndBarLine) {
|
2012-05-26 14:26:10 +02:00
|
|
|
Measure* m = static_cast<Segment*>(ep)->measure();
|
|
|
|
BarLine* bl = static_cast<BarLine*>(e);
|
2013-03-05 20:23:59 +01:00
|
|
|
m->setEndBarLineType(bl->barLineType(), false, e->visible(), e->color());
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
deselectAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdExchangeVoice
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdExchangeVoice(int s, int d)
|
|
|
|
{
|
|
|
|
if (selection().state() != SEL_RANGE) {
|
|
|
|
selectStavesMessage();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int t1 = selection().tickStart();
|
|
|
|
int t2 = selection().tickEnd();
|
|
|
|
|
|
|
|
Measure* m1 = tick2measure(t1);
|
|
|
|
Measure* m2 = tick2measure(t2);
|
|
|
|
if (t2 > m2->tick())
|
|
|
|
m2 = 0;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
undoExchangeVoice(m1, s, d, selection().staffStart(), selection().staffEnd());
|
|
|
|
m1 = m1->nextMeasure();
|
|
|
|
if ((m1 == 0) || (m2 && (m1->tick() == m2->tick())))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdEnterRest
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdEnterRest(const TDuration& d)
|
|
|
|
{
|
|
|
|
if (_is.track() == -1) {
|
|
|
|
qDebug("cmdEnterRest: track -1");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
startCmd();
|
|
|
|
expandVoice();
|
|
|
|
if (_is.cr() == 0) {
|
|
|
|
qDebug("cannot enter rest here");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int track = _is.track();
|
|
|
|
NoteVal nval;
|
2012-08-04 15:46:43 +02:00
|
|
|
setNoteRest(_is.segment(), track, nval, d.fraction(), MScore::AUTO);
|
2012-07-20 10:04:51 +02:00
|
|
|
moveToNextInputPos();
|
2012-05-26 14:26:10 +02:00
|
|
|
_is.rest = false; // continue with normal note entry
|
|
|
|
endCmd();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// removeChordRest
|
|
|
|
// remove chord or rest
|
|
|
|
// remove associated segment if empty
|
|
|
|
// remove beam
|
|
|
|
// remove slurs
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::removeChordRest(ChordRest* cr, bool clearSegment)
|
|
|
|
{
|
|
|
|
undoRemoveElement(cr);
|
|
|
|
if (clearSegment) {
|
|
|
|
Segment* seg = cr->segment();
|
|
|
|
if (seg->isEmpty())
|
|
|
|
undoRemoveElement(seg);
|
|
|
|
}
|
|
|
|
if (cr->beam()) {
|
|
|
|
Beam* beam = cr->beam();
|
|
|
|
if (beam->generated()) {
|
|
|
|
beam->parent()->remove(beam);
|
|
|
|
delete beam;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
undoRemoveElement(beam);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdDeleteTuplet
|
|
|
|
// remove tuplet and replace with rest
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdDeleteTuplet(Tuplet* tuplet, bool replaceWithRest)
|
|
|
|
{
|
|
|
|
qDebug("Score::cmdDeleteTuplet elements %d replace %d",
|
|
|
|
tuplet->elements().size(), replaceWithRest);
|
|
|
|
|
|
|
|
foreach(DurationElement* de, tuplet->elements()) {
|
|
|
|
qDebug(" element %s", de->name());
|
|
|
|
if (de->isChordRest())
|
|
|
|
removeChordRest(static_cast<ChordRest*>(de), true);
|
|
|
|
else {
|
2012-09-13 18:01:34 +02:00
|
|
|
Q_ASSERT(de->type() == Element::TUPLET);
|
2012-05-26 14:26:10 +02:00
|
|
|
cmdDeleteTuplet(static_cast<Tuplet*>(de), false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// undoRemoveElement(tuplet);
|
|
|
|
if (replaceWithRest) {
|
|
|
|
Rest* rest = setRest(tuplet->tick(), tuplet->track(), tuplet->duration(), true, 0);
|
|
|
|
if (tuplet->tuplet()) {
|
|
|
|
rest->setTuplet(tuplet->tuplet());
|
|
|
|
tuplet->tuplet()->add(rest);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// nextInputPos
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::nextInputPos(ChordRest* cr, bool doSelect)
|
|
|
|
{
|
|
|
|
ChordRest* ncr = nextChordRest(cr);
|
|
|
|
if ((ncr == 0) && (_is.track() % VOICES)) {
|
|
|
|
Segment* s = tick2segment(cr->tick() + cr->actualTicks());
|
|
|
|
int track = (cr->track() / VOICES) * VOICES;
|
|
|
|
ncr = s ? static_cast<ChordRest*>(s->element(track)) : 0;
|
|
|
|
}
|
|
|
|
_is.setSegment(ncr ? ncr->segment() : 0);
|
|
|
|
if (doSelect)
|
|
|
|
select(ncr, SELECT_SINGLE, 0);
|
|
|
|
// if (ncr)
|
|
|
|
// emit posChanged(ncr->tick());
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// insertMeasure
|
|
|
|
// Create a new MeasureBase of type type and insert
|
|
|
|
// before measure.
|
|
|
|
// If measure is zero, append new MeasureBase.
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2012-09-13 18:01:34 +02:00
|
|
|
MeasureBase* Score::insertMeasure(Element::ElementType type, MeasureBase* measure,
|
2012-05-26 14:26:10 +02:00
|
|
|
bool createEmptyMeasures)
|
|
|
|
{
|
|
|
|
int tick;
|
|
|
|
int idx;
|
|
|
|
if (measure) {
|
2013-09-28 12:05:48 +02:00
|
|
|
if (measure->type() == Element::MEASURE && static_cast<Measure*>(measure)->isMMRest()) {
|
|
|
|
measure = static_cast<Measure*>(measure)->prev()->next();
|
|
|
|
deselectAll();
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
tick = measure->tick();
|
|
|
|
idx = measureIdx(measure);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
measure = last();
|
2012-06-07 09:24:05 +02:00
|
|
|
if (measure)
|
|
|
|
tick = measure->tick() + measure->ticks();
|
|
|
|
else
|
|
|
|
tick = 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
idx = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
MeasureBase* omb = 0;
|
2013-06-12 18:32:48 +02:00
|
|
|
QList<Score*> scorelist;
|
|
|
|
if (type == Element::MEASURE)
|
2013-06-21 15:19:15 +02:00
|
|
|
scorelist = scoreList();
|
2013-06-12 18:32:48 +02:00
|
|
|
else
|
2013-06-21 15:19:15 +02:00
|
|
|
scorelist.append(this);
|
|
|
|
|
2013-06-12 18:32:48 +02:00
|
|
|
foreach(Score* score, scorelist) {
|
2012-05-26 14:26:10 +02:00
|
|
|
MeasureBase* mb = static_cast<MeasureBase*>(Element::create(type, score));
|
|
|
|
MeasureBase* im = idx != -1 ? score->measure(idx) : 0;
|
|
|
|
// insert before im, append if im = 0
|
2013-07-13 19:15:49 +02:00
|
|
|
measure = mb;
|
2012-05-26 14:26:10 +02:00
|
|
|
mb->setTick(tick);
|
|
|
|
if (score == this)
|
|
|
|
omb = mb;
|
|
|
|
|
2012-09-13 18:01:34 +02:00
|
|
|
if (type == Element::MEASURE) {
|
2012-05-26 14:26:10 +02:00
|
|
|
bool createEndBar = false;
|
|
|
|
bool endBarGenerated = false;
|
|
|
|
if (idx == -1) {
|
|
|
|
Measure* lm = score->lastMeasure();
|
|
|
|
if (lm && lm->endBarLineType() == END_BAR) {
|
|
|
|
createEndBar = true;
|
|
|
|
if (!lm->endBarLineGenerated()) {
|
|
|
|
score->undoChangeEndBarLineType(lm, NORMAL_BAR);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
endBarGenerated = true;
|
|
|
|
lm->setEndBarLineType(NORMAL_BAR, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (lm == 0)
|
|
|
|
createEndBar = true;
|
|
|
|
}
|
|
|
|
|
2013-09-26 20:07:19 +02:00
|
|
|
// use nominal time signature of previous measure
|
|
|
|
Fraction f = score->sigmap()->timesig(tick - 1).nominal();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
Measure* m = static_cast<Measure*>(mb);
|
|
|
|
m->setTimesig(f);
|
|
|
|
m->setLen(f);
|
|
|
|
|
|
|
|
QList<TimeSig*> tsl;
|
|
|
|
QList<KeySig*> ksl;
|
|
|
|
QList<Clef*> cl;
|
|
|
|
|
|
|
|
if (tick == 0) {
|
|
|
|
//
|
|
|
|
// remove time and key signatures
|
|
|
|
//
|
|
|
|
for (int staffIdx = 0; staffIdx < nstaves(); ++staffIdx) {
|
|
|
|
for (Segment* s = score->firstSegment(); s && s->tick() == 0; s = s->next()) {
|
|
|
|
Element* e = s->element(staffIdx * VOICES);
|
|
|
|
if (e == 0)
|
|
|
|
continue;
|
2012-09-13 18:01:34 +02:00
|
|
|
if (e->type() == Element::KEYSIG) {
|
2012-05-26 14:26:10 +02:00
|
|
|
KeySig* ks = static_cast<KeySig*>(e);
|
|
|
|
ksl.append(ks);
|
|
|
|
undo(new RemoveElement(ks));
|
|
|
|
if (ks->segment()->isEmpty())
|
|
|
|
undoRemoveElement(ks->segment());
|
|
|
|
}
|
2012-09-13 18:01:34 +02:00
|
|
|
else if (e->type() == Element::TIMESIG) {
|
2012-05-26 14:26:10 +02:00
|
|
|
TimeSig* ts = static_cast<TimeSig*>(e);
|
|
|
|
tsl.append(ts);
|
|
|
|
undo(new RemoveElement(ts));
|
|
|
|
if (ts->segment()->isEmpty())
|
|
|
|
undoRemoveElement(ts->segment());
|
|
|
|
}
|
2012-09-13 18:01:34 +02:00
|
|
|
else if (e->type() == Element::CLEF) {
|
2012-05-26 14:26:10 +02:00
|
|
|
Clef* clef = static_cast<Clef*>(e);
|
|
|
|
cl.append(clef);
|
|
|
|
undo(new RemoveElement(e));
|
|
|
|
if (clef->segment()->isEmpty())
|
|
|
|
undoRemoveElement(clef->segment());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
undo(new InsertMeasure(m, im));
|
|
|
|
|
|
|
|
//
|
|
|
|
// if measure is inserted at tick zero,
|
|
|
|
// create key and time signature
|
|
|
|
//
|
|
|
|
foreach(TimeSig* ts, tsl) {
|
|
|
|
TimeSig* nts = new TimeSig(*ts);
|
2012-08-03 15:54:02 +02:00
|
|
|
Segment* s = m->undoGetSegment(Segment::SegTimeSig, 0);
|
2012-05-26 14:26:10 +02:00
|
|
|
nts->setParent(s);
|
|
|
|
undoAddElement(nts);
|
|
|
|
}
|
|
|
|
foreach(KeySig* ks, ksl) {
|
|
|
|
KeySig* nks = new KeySig(*ks);
|
2012-08-03 15:54:02 +02:00
|
|
|
Segment* s = m->undoGetSegment(Segment::SegKeySig, 0);
|
2012-05-26 14:26:10 +02:00
|
|
|
nks->setParent(s);
|
|
|
|
undoAddElement(nks);
|
|
|
|
}
|
|
|
|
foreach(Clef* clef, cl) {
|
|
|
|
Clef* nClef = new Clef(*clef);
|
2012-08-03 15:54:02 +02:00
|
|
|
Segment* s = m->undoGetSegment(Segment::SegClef, 0);
|
2012-05-26 14:26:10 +02:00
|
|
|
nClef->setParent(s);
|
|
|
|
undoAddElement(nClef);
|
|
|
|
}
|
|
|
|
if (createEndBar) {
|
|
|
|
Measure* lm = score->lastMeasure();
|
|
|
|
if (lm)
|
|
|
|
lm->setEndBarLineType(END_BAR, endBarGenerated);
|
|
|
|
}
|
2013-02-27 18:16:24 +01:00
|
|
|
score->fixTicks();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
undo(new InsertMeasure(mb, im));
|
|
|
|
}
|
|
|
|
}
|
2013-06-21 15:19:15 +02:00
|
|
|
insertTime(tick, measure->ticks());
|
|
|
|
|
2012-09-13 18:01:34 +02:00
|
|
|
if (type == Element::MEASURE && !createEmptyMeasures) {
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
// fill measure with rest
|
|
|
|
//
|
2013-06-24 19:01:31 +02:00
|
|
|
Score* root = rootScore();
|
|
|
|
Measure* m = static_cast<Measure*>(omb);
|
2013-02-27 18:16:24 +01:00
|
|
|
for (int staffIdx = 0; staffIdx < root->nstaves(); ++staffIdx) {
|
|
|
|
int track = staffIdx * VOICES;
|
|
|
|
Segment* s = m->findSegment(Segment::SegChordRest, m->tick());
|
|
|
|
if (s == 0 || s->element(track) == 0) {
|
|
|
|
// add rest to this staff and to all the staves linked to it
|
|
|
|
Rest* rest = new Rest(root, TDuration(TDuration::V_MEASURE));
|
2013-09-10 21:52:59 +02:00
|
|
|
Fraction timeStretch(root->staff(staffIdx)->timeStretch(m->tick()));
|
2013-06-24 19:01:31 +02:00
|
|
|
rest->setDuration(m->len() / timeStretch);
|
2013-02-27 18:16:24 +01:00
|
|
|
rest->setTrack(track);
|
|
|
|
undoAddCR(rest, m, m->tick());
|
|
|
|
}
|
2012-09-18 13:06:53 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
return omb;
|
|
|
|
}
|
|
|
|
|
2013-05-13 18:49:17 +02:00
|
|
|
}
|
|
|
|
|