MuseScore/libmscore/cmd.cpp

2385 lines
87 KiB
C++
Raw Normal View History

2012-05-26 14:26:10 +02:00
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2002-2013 Werner Schweer
2012-05-26 14:26:10 +02:00
//
// 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
Handling of several GUI commands.
*/
#include <assert.h>
#include "score.h"
#include "utils.h"
#include "key.h"
#include "clef.h"
#include "navigate.h"
#include "slur.h"
2013-08-22 12:18:14 +02:00
#include "tie.h"
2012-05-26 14:26:10 +02:00
#include "note.h"
#include "rest.h"
#include "chord.h"
#include "text.h"
#include "sig.h"
#include "staff.h"
#include "part.h"
#include "style.h"
#include "page.h"
#include "barline.h"
#include "tuplet.h"
#include "xml.h"
#include "ottava.h"
#include "trill.h"
#include "pedal.h"
#include "hairpin.h"
#include "textline.h"
#include "keysig.h"
#include "volta.h"
#include "dynamic.h"
#include "box.h"
#include "harmony.h"
#include "system.h"
#include "stafftext.h"
#include "articulation.h"
#include "layoutbreak.h"
#include "drumset.h"
#include "beam.h"
#include "lyrics.h"
#include "pitchspelling.h"
#include "measure.h"
#include "tempo.h"
#include "sig.h"
#include "undo.h"
#include "timesig.h"
#include "repeat.h"
#include "tempotext.h"
#include "clef.h"
#include "noteevent.h"
#include "breath.h"
#include "stringdata.h"
2012-05-26 14:26:10 +02:00
#include "stafftype.h"
#include "segment.h"
#include "chordlist.h"
#include "mscore.h"
#include "accidental.h"
#include "sequencer.h"
#include "tremolo.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
//---------------------------------------------------------
// startCmd
/// Start a GUI command by clearing the redraw area
/// and starting a user-visble undo.
//---------------------------------------------------------
void Score::startCmd()
{
if (MScore::debugMode)
qDebug("===startCmd()");
_layoutAll = true; ///< do a complete relayout
_playNote = false;
// Start collecting low-level undo operations for a
// user-visible undo action.
if (undo()->active()) {
// if (MScore::debugMode)
qDebug("Score::startCmd(): cmd already active");
return;
}
undo()->beginMacro();
undo(new SaveState(this));
}
//---------------------------------------------------------
// endCmd
/// End a GUI command by (if \a undo) ending a user-visble undo
/// and (always) updating the redraw area.
//---------------------------------------------------------
void Score::endCmd()
{
if (!undo()->active()) {
// if (MScore::debugMode)
qDebug("Score::endCmd(): no cmd active");
end();
return;
}
2013-10-29 16:59:04 +01:00
foreach (Score* s, scoreList()) {
2013-06-05 15:47:34 +02:00
if (s->layoutAll()) {
s->_updateAll = true;
s->doLayout();
}
2013-10-29 16:59:04 +01:00
const InputState& is = s->inputState();
if (is.noteEntryMode() && is.segment())
s->setPlayPos(is.segment()->tick());
2014-04-02 10:49:54 +02:00
if (_playlistDirty) {
emit playlistChanged();
_playlistDirty = false;
}
2013-06-05 15:47:34 +02:00
}
2012-05-26 14:26:10 +02:00
bool noUndo = undo()->current()->childCount() <= 1;
if (!noUndo)
setDirty(true);
2012-05-26 14:26:10 +02:00
undo()->endMacro(noUndo);
end(); // DEBUG
}
//---------------------------------------------------------
// end
/// Update the redraw area.
//---------------------------------------------------------
void Score::end()
{
foreach(Score* s, scoreList())
s->end1();
}
//---------------------------------------------------------
// update
// layout & update
//---------------------------------------------------------
void Score::update()
{
foreach(Score* s, scoreList()) {
2013-06-27 11:02:42 +02:00
if (s->layoutAll()) {
s->setUpdateAll(true);
s->doLayout();
}
s->end1();
2012-05-26 14:26:10 +02:00
}
}
//---------------------------------------------------------
// end1
//---------------------------------------------------------
void Score::end1()
{
if (_updateAll) {
foreach(MuseScoreView* v, viewer)
v->updateAll();
}
else {
// update a little more:
qreal d = spatium() * .5;
refresh.adjust(-d, -d, 2 * d, 2 * d);
foreach(MuseScoreView* v, viewer)
v->dataChanged(refresh);
}
refresh = QRectF();
_updateAll = false;
}
//---------------------------------------------------------
// endUndoRedo
/// Common handling for ending undo or redo
//---------------------------------------------------------
void Score::endUndoRedo()
{
updateSelection();
2013-06-05 15:47:34 +02:00
foreach (Score* score, scoreList()) {
2012-05-26 14:26:10 +02:00
if (score->layoutAll()) {
score->setUndoRedo(true);
score->doLayout();
2012-05-26 14:26:10 +02:00
score->setUndoRedo(false);
score->setUpdateAll(true);
}
2013-10-29 16:59:04 +01:00
const InputState& is = score->inputState();
if (is.noteEntryMode() && is.segment())
score->setPlayPos(is.segment()->tick());
2014-04-02 10:49:54 +02:00
if (_playlistDirty) {
emit playlistChanged();
_playlistDirty = false;
}
2012-05-26 14:26:10 +02:00
}
end();
}
//---------------------------------------------------------
// cmdAddSpanner
// drop VOLTA, OTTAVA, TRILL, PEDAL, DYNAMIC
// HAIRPIN, and TEXTLINE
//---------------------------------------------------------
2013-06-10 11:03:34 +02:00
void Score::cmdAddSpanner(Spanner* spanner, const QPointF& pos)
2012-05-26 14:26:10 +02:00
{
int staffIdx;
Segment* segment;
MeasureBase* mb = pos2measure(pos, &staffIdx, 0, &segment, 0);
2012-09-13 18:01:34 +02:00
if (mb == 0 || mb->type() != Element::MEASURE) {
2012-05-26 14:26:10 +02:00
qDebug("cmdAddSpanner: cannot put object here");
delete spanner;
return;
}
int track = staffIdx == -1 ? -1 : staffIdx * VOICES;
spanner->setTrack(track);
2014-04-23 14:50:31 +02:00
spanner->setTrack2(track);
2012-05-26 14:26:10 +02:00
2012-09-12 11:16:36 +02:00
if (spanner->anchor() == Spanner::ANCHOR_SEGMENT) {
2013-06-10 11:03:34 +02:00
spanner->setTick(segment->tick());
2013-07-16 09:03:47 +02:00
int lastTick = lastMeasure()->tick() + lastMeasure()->ticks();
int tick2 = qMin(segment->tick() + segment->measure()->ticks(), lastTick);
spanner->setTick2(tick2);
2012-05-26 14:26:10 +02:00
}
else { // ANCHOR_MEASURE
Measure* m = static_cast<Measure*>(mb);
QRectF b(m->canvasBoundingRect());
if (pos.x() >= (b.x() + b.width() * .5))
m = m->nextMeasure();
2013-06-10 11:03:34 +02:00
spanner->setTick(m->tick());
spanner->setTick2(m->endTick());
2012-05-26 14:26:10 +02:00
}
undoAddElement(spanner);
select(spanner, SELECT_SINGLE, 0);
2012-09-13 18:01:34 +02:00
if (spanner->type() == Element::TRILL) {
2012-05-26 14:26:10 +02:00
Element* e = segment->element(staffIdx * VOICES);
2012-09-13 18:01:34 +02:00
if (e && e->type() == Element::CHORD) {
2012-05-26 14:26:10 +02:00
Chord* chord = static_cast<Chord*>(e);
Fraction l = chord->duration();
if (chord->notes().size() > 1) {
// trill do not work for chords
}
Note* note = chord->upNote();
while (note->tieFor()) {
note = note->tieFor()->endNote();
l += note->chord()->duration();
}
Segment* s = note->chord()->segment();
s = s->next1(Segment::SegChordRest);
2012-05-26 14:26:10 +02:00
while (s) {
Element* e = s->element(staffIdx * VOICES);
if (e)
break;
s = s->next1(Segment::SegChordRest);
2012-05-26 14:26:10 +02:00
}
if (s)
spanner->setTick2(s->tick());
2012-05-26 14:26:10 +02:00
Fraction d(1,32);
Fraction e = l / d;
int n = e.numerator() / e.denominator();
QList<NoteEvent*> events;
int pitch = chord->upNote()->ppitch();
int key = chord->staff()->key(segment->tick()).accidentalType();
int pitch2 = diatonicUpDown(key, pitch, 1);
int dpitch = pitch2 - pitch;
for (int i = 0; i < n; i += 2) {
events.append(new NoteEvent(0, i * 1000 / n, 1000/n));
events.append(new NoteEvent(dpitch, (i+1) *1000 / n, 1000/n));
}
undo(new ChangeNoteEvents(chord, events));
}
}
}
//---------------------------------------------------------
// expandVoice
//---------------------------------------------------------
void Score::expandVoice(Segment* s, int track)
{
if (s->element(track)) {
ChordRest* cr = (ChordRest*)(s->element(track));
qDebug("expand voice: found %s %s", cr->name(), qPrintable(cr->duration().print()));
return;
}
Segment* ps;
for (ps = s; ps; ps = ps->prev(Segment::SegChordRest)) {
2012-05-26 14:26:10 +02:00
if (ps->element(track))
break;
}
if (ps) {
ChordRest* cr = static_cast<ChordRest*>(ps->element(track));
int tick = cr->tick() + cr->actualTicks();
if (tick == s->tick())
return;
if (tick > s->tick()) {
qDebug("expandVoice: cannot insert element here");
return;
}
}
//
// fill upto s->tick() with rests
//
Measure* m = s->measure();
int stick = ps ? ps->tick() : m->tick();
int ticks = s->tick() - stick;
if (ticks)
setRest(stick, track, Fraction::fromTicks(ticks), false, 0);
//
// fill from s->tick() until next chord/rest
//
Segment* ns;
for (ns = s->next(Segment::SegChordRest); ns; ns = ns->next(Segment::SegChordRest)) {
2012-05-26 14:26:10 +02:00
if (ns->element(track))
break;
}
ticks = ns ? (ns->tick() - s->tick()) : (m->ticks() - s->rtick());
if (ticks == m->ticks())
addRest(s, track, TDuration(TDuration::V_MEASURE), 0);
else
setRest(s->tick(), track, Fraction::fromTicks(ticks), false, 0);
}
void Score::expandVoice()
{
Segment* s = _is.segment();
int track = _is.track();
expandVoice(s, track);
}
//---------------------------------------------------------
// addPitch
//---------------------------------------------------------
Note* Score::addPitch(int pitch, bool addFlag)
{
if (addFlag) {
2013-10-24 12:09:00 +02:00
Chord* c = static_cast<Chord*>(_is.lastSegment()->element(_is.track()));
if (c == 0 || c->type() != Element::CHORD) {
qDebug("Score::addPitch: cr %s", c ? c->name() : "zero");
2012-05-26 14:26:10 +02:00
return 0;
2013-10-24 12:09:00 +02:00
}
NoteVal val(pitch);
Note* note = addNote(c, val);
if (_is.lastSegment() == _is.segment())
_is.moveToNextInputPos();
return note;
2012-05-26 14:26:10 +02:00
}
expandVoice();
// insert note
2012-08-04 15:46:43 +02:00
MScore::Direction stemDirection = MScore::AUTO;
NoteHeadGroup headGroup = NoteHeadGroup::HEAD_NORMAL;
2012-05-26 14:26:10 +02:00
int track = _is.track();
if (_is.drumNote() != -1) {
pitch = _is.drumNote();
Drumset* ds = _is.drumset();
headGroup = ds->noteHead(pitch);
stemDirection = ds->stemDirection(pitch);
track = ds->voice(pitch) + (_is.track() / VOICES) * VOICES;
_is.setTrack(track);
expandVoice();
}
if (!_is.cr())
return 0;
NoteVal nval;
nval.pitch = pitch;
nval.headGroup = headGroup;
Fraction duration;
2013-04-18 11:41:14 +02:00
if (_is.repitchMode()) {
2012-05-26 14:26:10 +02:00
duration = _is.cr()->duration();
2013-04-18 11:41:14 +02:00
}
else {
2012-05-26 14:26:10 +02:00
duration = _is.duration().fraction();
2013-04-18 11:41:14 +02:00
}
Note* note = 0;
if (_is.repitchMode() && _is.cr()->type() == Element::CHORD) {
Chord* chord = static_cast<Chord*>(_is.cr());
note = new Note(this);
note->setNval(nval);
note->setParent(chord);
if (!addFlag) {
while (!chord->notes().isEmpty())
undoRemoveElement(chord->notes().first());
}
undoAddElement(note);
}
else {
Segment* seg = setNoteRest(_is.segment(), track, nval, duration, stemDirection);
if (seg) {
note = static_cast<Chord*>(seg->element(track))->upNote();
2013-06-05 15:47:34 +02:00
setLayoutAll(true);
2013-04-18 11:41:14 +02:00
}
2012-05-26 14:26:10 +02:00
}
2013-10-24 12:09:00 +02:00
if (_is.slur()) {
2012-05-26 14:26:10 +02:00
//
// extend slur
//
ChordRest* e = searchNote(_is.tick(), _is.track());
if (e) {
int stick = 0;
2013-10-24 12:09:00 +02:00
Element* ee = _is.slur()->startElement();
2012-05-26 14:26:10 +02:00
if (ee->isChordRest())
stick = static_cast<ChordRest*>(ee)->tick();
2012-09-13 18:01:34 +02:00
else if (ee->type() == Element::NOTE)
2012-05-26 14:26:10 +02:00
stick = static_cast<Note*>(ee)->chord()->tick();
if (stick == e->tick()) {
2013-10-24 12:09:00 +02:00
_is.slur()->setTick(stick);
2012-05-26 14:26:10 +02:00
}
else
2013-10-24 12:09:00 +02:00
_is.slur()->setTick2(e->tick());
2012-05-26 14:26:10 +02:00
}
else
qDebug("addPitch: cannot find slur note");
setLayoutAll(true);
}
2013-10-24 12:09:00 +02:00
_is.moveToNextInputPos();
2012-05-26 14:26:10 +02:00
return note;
}
//---------------------------------------------------------
// cmdAddInterval
//---------------------------------------------------------
void Score::cmdAddInterval(int val, const QList<Note*>& nl)
{
startCmd();
for (Note* on : nl) {
2012-05-26 14:26:10 +02:00
Note* note = new Note(*on);
Chord* chord = on->chord();
note->setParent(chord);
int valTmp = val < 0 ? val+1 : val-1;
int npitch;
2014-04-23 11:08:51 +02:00
int ntpc1;
int ntpc2;
if (abs(valTmp) != 7) {
int line = on->line() - valTmp;
int tick = chord->tick();
2012-05-26 14:26:10 +02:00
Staff* estaff = staff(on->staffIdx() + chord->staffMove());
ClefType clef = estaff->clef(tick);
int key = estaff->key(tick).accidentalType();
npitch = line2pitch(line, clef, key);
2014-04-23 11:08:51 +02:00
int ntpc = pitch2tpc(npitch, key, PREFER_NEAREST);
Interval v = on->staff()->part()->instr()->transpose();
if (v.isZero())
ntpc1 = ntpc2 = ntpc;
else {
if (styleB(ST_concertPitch)) {
v.flip();
ntpc1 = ntpc;
ntpc2 = Ms::transposeTpc(ntpc, v, false);
}
else {
npitch += v.chromatic;
ntpc2 = ntpc;
ntpc1 = Ms::transposeTpc(ntpc, v, false);
}
}
2012-05-26 14:26:10 +02:00
}
else { //special case for octave
Interval interval(7, 12);
if (val < 0)
2012-05-26 14:26:10 +02:00
interval.flip();
2014-04-23 11:08:51 +02:00
transposeInterval(on->pitch(), on->tpc(), &npitch, &ntpc1, interval, false);
ntpc1 = on->tpc1();
ntpc2 = on->tpc2();
2012-05-26 14:26:10 +02:00
}
2014-04-23 11:08:51 +02:00
note->setPitch(npitch, ntpc1, ntpc2);
2012-05-26 14:26:10 +02:00
undoAddElement(note);
_playNote = true;
select(note, SELECT_SINGLE, 0);
}
2014-04-23 11:08:51 +02:00
Chord* c = nl.front()->chord();
c->measure()->cmdUpdateNotes(c->staffIdx());
setLayoutAll(true);
2013-10-24 12:09:00 +02:00
_is.moveToNextInputPos();
2012-05-26 14:26:10 +02:00
endCmd();
}
//---------------------------------------------------------
// setGraceNote
/// Create a grace note in front of a normal note.
/// \arg chord is the normal note
/// \arg pitch is the pitch of the grace note
/// \arg is the grace note type
/// \len is the visual duration of the grace note (1/16 or 1/32)
//---------------------------------------------------------
void Score::setGraceNote(Chord* ch, int pitch, NoteType type, bool /*behind*/, int len)
2012-05-26 14:26:10 +02:00
{
Note* note = new Note(this);
Chord* chord = new Chord(this);
chord->setTrack(ch->track());
2013-06-12 14:23:57 +02:00
chord->setParent(ch);
chord->add(note);
note->setPitch(pitch);
note->setTpcFromPitch();
2012-05-26 14:26:10 +02:00
TDuration d;
d.setVal(len);
chord->setDurationType(d);
chord->setDuration(d.fraction());
chord->setNoteType(type);
chord->setMag(ch->staff()->mag() * styleD(ST_graceNoteMag));
2013-06-12 14:23:57 +02:00
undoAddElement(chord);
2012-05-26 14:26:10 +02:00
select(note, SELECT_SINGLE, 0);
}
//---------------------------------------------------------
// setNoteRest
// pitch == -1 -> set rest
// return segment of last created note/rest
//---------------------------------------------------------
Segment* Score::setNoteRest(Segment* segment, int track, NoteVal nval, Fraction sd,
2012-08-04 15:46:43 +02:00
MScore::Direction stemDirection)
2012-05-26 14:26:10 +02:00
{
Q_ASSERT(segment->segmentType() == Segment::SegChordRest);
2012-05-26 14:26:10 +02:00
int tick = segment->tick();
Element* nr = 0;
Tie* tie = 0;
ChordRest* cr = static_cast<ChordRest*>(segment->element(track));
Measure* measure = 0;
for (;;) {
if (track % VOICES)
expandVoice(segment, track);
// the returned gap ends at the measure boundary or at tuplet end
Fraction dd = makeGap(segment, track, sd, cr ? cr->tuplet() : 0);
if (dd.isZero()) {
qDebug("cannot get gap at %d type: %d/%d", tick, sd.numerator(),
sd.denominator());
break;
}
QList<TDuration> dl = toDurationList(dd, true);
measure = segment->measure();
int n = dl.size();
for (int i = 0; i < n; ++i) {
TDuration d = dl[i];
ChordRest* ncr;
Note* note = 0;
if (nval.pitch == -1) {
nr = ncr = new Rest(this);
nr->setTrack(track);
ncr->setDurationType(d);
ncr->setDuration(d.fraction());
}
else {
nr = note = new Note(this);
if (tie) {
tie->setEndNote(note);
note->setTieBack(tie);
}
Chord* chord = new Chord(this);
chord->setTrack(track);
chord->setDurationType(d);
chord->setDuration(d.fraction());
chord->setStemDirection(stemDirection);
chord->add(note);
note->setNval(nval);
ncr = chord;
if (i+1 < n) {
tie = new Tie(this);
tie->setStartNote(note);
tie->setTrack(track);
note->setTieFor(tie);
}
}
ncr->setTuplet(cr ? cr->tuplet() : 0);
undoAddCR(ncr, measure, tick);
_playNote = true;
segment = ncr->segment();
tick += ncr->actualTicks();
}
sd -= dd;
if (sd.isZero())
break;
Segment* nseg = tick2segment(tick, false, Segment::SegChordRest);
2012-05-26 14:26:10 +02:00
if (nseg == 0) {
qDebug("reached end of score");
break;
}
segment = nseg;
cr = static_cast<ChordRest*>(segment->element(track));
if (cr == 0) {
if (track % VOICES)
cr = addRest(segment, track, TDuration(TDuration::V_MEASURE), 0);
else {
qDebug("no rest in voice 0");
break;
}
}
//
// Note does not fit on current measure, create Tie to
// next part of note
if (nval.pitch != -1) {
tie = new Tie(this);
tie->setStartNote((Note*)nr);
tie->setTrack(nr->track());
((Note*)nr)->setTieFor(tie);
}
}
if (tie)
connectTies();
if (nr)
select(nr, SELECT_SINGLE, 0);
return segment;
}
//---------------------------------------------------------
// makeGap
// make time gap at tick by removing/shortening
// chord/rest
//
2013-05-10 10:51:27 +02:00
// if keepChord, the chord at tick is not removed
//
2012-05-26 14:26:10 +02:00
// gap does not exceed measure or scope of tuplet
//
// return size of actual gap
//---------------------------------------------------------
2013-05-10 10:51:27 +02:00
Fraction Score::makeGap(Segment* segment, int track, const Fraction& _sd, Tuplet* tuplet, bool keepChord)
2012-05-26 14:26:10 +02:00
{
Q_ASSERT(_sd.numerator());
2012-05-26 14:26:10 +02:00
Measure* measure = segment->measure();
2013-06-05 15:47:34 +02:00
setLayoutAll(true);
2012-05-26 14:26:10 +02:00
Fraction akkumulated;
Fraction sd = _sd;
//
// remember first segment which should
// not be deleted (it may contain other elements we want to preserve)
//
Segment* firstSegment = segment;
int nextTick = segment->tick();
2013-06-12 14:23:57 +02:00
for (Segment* seg = firstSegment; seg; seg = seg->next(Segment::SegChordRest)) {
2012-05-26 14:26:10 +02:00
//
// voices != 0 may have gaps:
//
ChordRest* cr = static_cast<ChordRest*>(seg->element(track));
if (!cr) {
if (seg->tick() < nextTick)
continue;
Segment* seg1 = seg->next(Segment::SegChordRest);
2012-05-26 14:26:10 +02:00
int tick2 = seg1 ? seg1->tick() : seg->measure()->tick() + seg->measure()->ticks();
Fraction td(Fraction::fromTicks(tick2 - seg->tick()));
segment = seg;
if (td > sd)
td = sd;
akkumulated += td;
sd -= td;
if (sd.isZero())
return akkumulated;
nextTick = tick2;
2012-05-26 14:26:10 +02:00
continue;
}
//
// limit to tuplet level
//
if (tuplet) {
bool tupletEnd = true;
Tuplet* t = cr->tuplet();
while (t) {
if (cr->tuplet() == tuplet) {
tupletEnd = false;
break;
}
t = t->tuplet();
}
2013-06-20 17:23:24 +02:00
if (tupletEnd)
2012-05-26 14:26:10 +02:00
return akkumulated;
}
Fraction td(cr->duration());
// remove tremolo between 2 notes, if present
if (cr->type() == Element::CHORD) {
Chord* c = static_cast<Chord*>(cr);
if (c->tremolo()) {
Tremolo* tremolo = c->tremolo();
if (tremolo->twoNotes())
undoRemoveElement(tremolo);
}
}
2012-05-26 14:26:10 +02:00
Tuplet* ltuplet = cr->tuplet();
if (cr->tuplet() != tuplet) {
//
// Current location points to the start of a (nested)tuplet.
// We have to remove the complete tuplet.
Tuplet* t = ltuplet;
2012-09-13 18:01:34 +02:00
while (t->elements().last()->type() == Element::TUPLET)
2012-05-26 14:26:10 +02:00
t = static_cast<Tuplet*>(t->elements().last());
seg = static_cast<ChordRest*>(t->elements().last())->segment();
td = ltuplet->duration();
cmdDeleteTuplet(ltuplet, false);
tuplet = 0;
}
else {
2013-05-10 10:51:27 +02:00
if(seg != firstSegment || !keepChord)
undoRemoveElement(cr);
if (seg != firstSegment && seg->isEmpty() && seg->annotations().size() == 0)
2012-05-26 14:26:10 +02:00
undoRemoveElement(seg);
}
nextTick += td.ticks();
if (sd < td) {
//
// we removed too much
//
akkumulated = _sd;
Fraction rd = td - sd;
QList<TDuration> dList = toDurationList(rd, false);
if (dList.isEmpty())
return akkumulated;
Fraction f(cr->staff()->timeStretch(cr->tick()) * sd);
for (Tuplet* t = tuplet; t; t = t->tuplet())
f /= t->ratio();
int tick = cr->tick() + f.ticks();
if ((tuplet == 0) && (((measure->tick() - tick) % dList[0].ticks()) == 0)) {
foreach(TDuration d, dList) {
qDebug(" addClone at %d, %d", tick, d.ticks());
tick += addClone(cr, tick, d)->actualTicks();
}
}
else {
for (int i = dList.size() - 1; i >= 0; --i)
tick += addClone(cr, tick, dList[i])->actualTicks();
}
return akkumulated;
}
akkumulated += td;
sd -= td;
if (sd.isZero())
return akkumulated;
}
// int ticks = measure->tick() + measure->ticks() - segment->tick();
// Fraction td = Fraction::fromTicks(ticks);
// NEEDS REVIEW !!
// once the statement below is removed, these two lines do nothing
// if (td > sd)
// td = sd;
// ??? akkumulated should already contain the total value of the created gap: line 749, 811 or 838
// this line creates a qreal-sized gap if the needed gap crosses a measure boundary
// by adding again the duration already added in line 838
// akkumulated += td;
return akkumulated;
}
//---------------------------------------------------------
// makeGap1
// make time gap at tick by removing/shortening
// chord/rest
// - cr is top level (not part of a tuplet)
// - do not stop at measure end
//---------------------------------------------------------
bool Score::makeGap1(int tick, int staffIdx, Fraction len)
{
ChordRest* cr = 0;
2013-06-12 14:23:57 +02:00
Segment* seg = tick2segment(tick, true, Segment::SegChordRest);
2012-05-26 14:26:10 +02:00
if (!seg) {
qDebug("1:makeGap1: no segment at %d", tick);
return false;
}
int track = staffIdx * VOICES;
cr = static_cast<ChordRest*>(seg->element(track));
if (!cr) {
2013-06-12 14:23:57 +02:00
// check if we are in the middle of a chord/rest
Segment* seg1 = seg->prev(Segment::SegChordRest);;
2013-06-12 14:23:57 +02:00
for (;;) {
if (seg1 == 0) {
qDebug("1:makeGap1: no segment at %d", tick);
2012-05-26 14:26:10 +02:00
return false;
}
2013-06-12 14:23:57 +02:00
if (seg1->element(track))
break;
seg1 = seg1->prev(Segment::SegChordRest);
2012-05-26 14:26:10 +02:00
}
2013-06-12 14:23:57 +02:00
ChordRest* cr1 = static_cast<ChordRest*>(seg1->element(track));
2013-06-20 15:25:11 +02:00
Fraction srcF = cr1->duration();
2013-06-12 14:23:57 +02:00
Fraction dstF = Fraction::fromTicks(tick - cr1->tick());
undoChangeChordRestLen(cr1, TDuration(dstF));
2013-06-20 15:25:11 +02:00
setRest(tick, track, srcF - dstF, true, 0);
2013-06-12 14:23:57 +02:00
for (;;) {
2013-06-20 15:25:11 +02:00
seg1 = seg1->next1(Segment::SegChordRest);
if (seg1 == 0) {
2013-06-12 14:23:57 +02:00
qDebug("2:makeGap1: no segment");
return false;
2012-05-26 14:26:10 +02:00
}
2013-06-20 15:25:11 +02:00
if (seg1->element(track)) {
tick = seg1->tick();
cr = static_cast<ChordRest*>(seg1->element(track));
2013-06-12 14:23:57 +02:00
break;
2012-05-26 14:26:10 +02:00
}
}
}
for (;;) {
if (!cr) {
qDebug("makeGap1: cannot make gap");
return false;
}
Fraction l = makeGap(cr->segment(), cr->track(), len, 0);
if (l.isZero()) {
qDebug("makeGap1: makeGap returns zero gap");
return false;
}
len -= l;
if (len.isZero())
break;
// go to next cr
Measure* m = cr->measure()->nextMeasure();
if (m == 0) {
qDebug("EOS reached");
2012-09-13 18:01:34 +02:00
insertMeasure(Element::MEASURE, 0, false);
2012-05-26 14:26:10 +02:00
m = cr->measure()->nextMeasure();
if (m == 0) {
qDebug("===EOS reached");
return true;
}
}
2013-06-28 17:46:24 +02:00
Segment* s = m->first(Segment::SegChordRest);
2012-05-26 14:26:10 +02:00
int track = cr->track();
cr = static_cast<ChordRest*>(s->element(track));
if (cr == 0) {
addRest(s, track, TDuration(TDuration::V_MEASURE), 0);
cr = static_cast<ChordRest*>(s->element(track));
}
}
return true;
}
//---------------------------------------------------------
// splitGapToMeasureBoundaries
// cr - start of gap
// gap - gap len
//---------------------------------------------------------
QList<Fraction> Score::splitGapToMeasureBoundaries(ChordRest* cr, Fraction gap)
{
QList<Fraction> flist;
Tuplet* tuplet = cr->tuplet();
if (tuplet) {
2013-05-10 10:51:27 +02:00
if(tuplet->tuplet())
return flist; // do no deal with nested tuplets
Fraction rest = Fraction::fromTicks(tuplet->tick() + tuplet->duration().ticks() - cr->segment()->tick()) * tuplet->ratio();
2012-05-26 14:26:10 +02:00
if (rest < gap)
qDebug("does not fit in tuplet");
else
flist.append(gap);
return flist;
}
Segment* s = cr->segment();
while (gap > Fraction(0)) {
Measure* m = s->measure();
Fraction rest = Fraction::fromTicks(m->ticks() - s->rtick());
if (rest >= gap) {
flist.append(gap);
return flist;
}
flist.append(rest);
gap -= rest;
m = m->nextMeasure();
if (m == 0)
return flist;
s = m->first(Segment::SegChordRest);
2012-05-26 14:26:10 +02:00
}
return flist;
}
//---------------------------------------------------------
// changeCRlen
//---------------------------------------------------------
void Score::changeCRlen(ChordRest* cr, const TDuration& d)
{
Fraction srcF(cr->duration());
Fraction dstF;
if (d.type() == TDuration::V_MEASURE)
dstF = cr->measure()->stretchedLen(cr->staff());
else
dstF = d.fraction();
qDebug("changeCRlen: %d/%d -> %d/%d", srcF.numerator(), srcF.denominator(),
dstF.numerator(), dstF.denominator());
if (srcF == dstF)
return;
int track = cr->track();
Tuplet* tuplet = cr->tuplet();
//keep selected note if any
Note* selNote = 0;
if(selection().isSingle()) {
Element* el = getSelectedElement();
if(el->type() == Element::NOTE)
selNote = static_cast<Note*>(el);
}
2012-05-26 14:26:10 +02:00
if (srcF > dstF) {
//
// make shorter and fill with rest
//
2013-05-10 10:51:27 +02:00
deselectAll();
2012-09-13 18:01:34 +02:00
if (cr->type() == Element::CHORD) {
2012-05-26 14:26:10 +02:00
//
// remove ties and tremolo between 2 notes
2012-05-26 14:26:10 +02:00
//
Chord* c = static_cast<Chord*>(cr);
if (c->tremolo()) {
Tremolo* tremolo = c->tremolo();
if (tremolo->twoNotes())
undoRemoveElement(tremolo);
}
2012-05-26 14:26:10 +02:00
foreach(Note* n, c->notes()) {
if (n->tieFor())
undoRemoveElement(n->tieFor());
}
}
undoChangeChordRestLen(cr, TDuration(dstF));
2013-06-04 18:29:14 +02:00
qDebug(" setRest at %d+%d, %d/%d",
cr->tick(), cr->actualTicks(), (srcF-dstF).numerator(), (srcF-dstF).denominator());
2012-05-26 14:26:10 +02:00
setRest(cr->tick() + cr->actualTicks(), track, srcF - dstF, false, tuplet);
if(!selNote)
select(cr, SELECT_SINGLE, 0);
else
select(selNote, SELECT_SINGLE, 0);
2012-05-26 14:26:10 +02:00
return;
}
//
// make longer
//
// split required len into Measures
QList<Fraction> flist = splitGapToMeasureBoundaries(cr, dstF);
if (flist.isEmpty())
return;
2013-05-10 10:51:27 +02:00
deselectAll();
2012-05-26 14:26:10 +02:00
qDebug("ChangeCRLen::List:");
foreach (Fraction f, flist)
qDebug(" %d/%d", f.numerator(), f.denominator());
int tick = cr->tick();
Fraction f = dstF;
ChordRest* cr1 = cr;
Chord* oc = 0;
bool first = true;
foreach (Fraction f2, flist) {
f -= f2;
2013-05-10 10:51:27 +02:00
makeGap(cr1->segment(), cr1->track(), f2, tuplet, first);
2012-05-26 14:26:10 +02:00
2012-09-13 18:01:34 +02:00
if (cr->type() == Element::REST) {
2012-05-26 14:26:10 +02:00
qDebug(" +ChangeCRLen::setRest %d/%d", f2.numerator(), f2.denominator());
Fraction timeStretch = cr1->staff()->timeStretch(cr1->tick());
Rest* r = static_cast<Rest*>(cr);
if (first) {
QList<TDuration> dList = toDurationList(f2, true);
undoChangeChordRestLen(cr, dList[0]);
if(dList.size() > 1) {
TDuration remain = TDuration(f2) - dList[0];
setRest(tick +dList[0].ticks(), track, remain.fraction() * timeStretch, (remain.dots() > 0), tuplet);
}
}
else {
r = setRest(tick, track, f2 * timeStretch, (d.dots() > 0), tuplet);
}
2012-05-26 14:26:10 +02:00
if (first) {
select(r, SELECT_SINGLE, 0);
first = false;
}
qDebug(" ChangeCRLen:: %d += %d(actual=%d)", tick, f2.ticks(), f2.ticks() * timeStretch.numerator() / timeStretch.denominator());
tick += f2.ticks() * timeStretch.numerator() / timeStretch.denominator();
}
else {
QList<TDuration> dList = toDurationList(f2, true);
Measure* measure = tick2measure(tick);
int etick = measure->tick();
// if (measure->tick() != tick)
// etick += measure->ticks();
if (((tick - etick) % dList[0].ticks()) == 0) {
2013-05-10 10:51:27 +02:00
foreach(TDuration du, dList) {
2012-05-26 14:26:10 +02:00
bool genTie;
Chord* cc;
if (oc) {
genTie = true;
cc = oc;
2013-05-10 10:51:27 +02:00
oc = addChord(tick, du, cc, genTie, tuplet);
2012-05-26 14:26:10 +02:00
}
else {
genTie = false;
cc = static_cast<Chord*>(cr);
2013-05-10 10:51:27 +02:00
undoChangeChordRestLen(cr, du);
oc = cc;
2012-05-26 14:26:10 +02:00
}
if (oc && first) {
if(!selNote)
select(oc, SELECT_SINGLE, 0);
else
select(selNote, SELECT_SINGLE, 0);
2012-05-26 14:26:10 +02:00
first = false;
}
if (oc)
tick += oc->actualTicks();
}
}
else {
for (int i = dList.size() - 1; i >= 0; --i) {
bool genTie;
Chord* cc;
if (oc) {
genTie = true;
cc = oc;
2013-05-10 10:51:27 +02:00
oc = addChord(tick, dList[i], cc, genTie, tuplet);
2012-05-26 14:26:10 +02:00
}
else {
genTie = false;
cc = static_cast<Chord*>(cr);
2013-05-10 10:51:27 +02:00
undoChangeChordRestLen(cr, dList[i]);
oc = cc;
2012-05-26 14:26:10 +02:00
}
if (first) {
select(oc, SELECT_SINGLE, 0);
first = false;
}
tick += oc->actualTicks();
}
}
}
Measure* m = cr1->measure();
Measure* m1 = m->nextMeasure();
if (m1 == 0)
break;
2013-06-28 17:46:24 +02:00
Segment* s = m1->first(Segment::SegChordRest);
2012-05-26 14:26:10 +02:00
expandVoice(s, track);
cr1 = static_cast<ChordRest*>(s->element(track));
}
connectTies();
}
//---------------------------------------------------------
// upDownChromatic
//---------------------------------------------------------
static void upDownChromatic(bool up, int pitch, Note* n, int key, int tpc1, int tpc2, int& newPitch, int& newTpc1, int& newTpc2)
{
if (up && pitch < 127) {
newPitch = pitch + 1;
if (n->concertPitch()) {
if (tpc1 > TPC_A + key)
newTpc1 = tpc1 - 5; // up semitone diatonic
else
newTpc1 = tpc1 + 7; // up semitone chromatic
2014-04-14 10:39:27 +02:00
newTpc2 = n->transposeTpc(newTpc1);
}
else {
if (tpc2 > TPC_A + key)
newTpc2 = tpc2 - 5; // up semitone diatonic
else
newTpc2 = tpc2 + 7; // up semitone chromatic
2014-04-14 10:39:27 +02:00
newTpc1 = n->transposeTpc(newTpc2);
}
}
else if (!up && pitch > 0) {
newPitch = pitch - 1;
if (n->concertPitch()) {
if (tpc1 > TPC_C + key)
newTpc1 = tpc1 - 7; // down semitone chromatic
else
newTpc1 = tpc1 + 5; // down semitone diatonic
2014-04-14 10:39:27 +02:00
newTpc2 = n->transposeTpc(newTpc1);
}
else {
if (tpc2 > TPC_C + key)
newTpc2 = tpc2 - 7; // down semitone chromatic
else
newTpc2 = tpc2 + 5; // down semitone diatonic
2014-04-14 10:39:27 +02:00
newTpc1 = n->transposeTpc(newTpc2);
}
}
}
//---------------------------------------------------------
// setTpc
//---------------------------------------------------------
2014-04-14 10:39:27 +02:00
static void setTpc(Note* oNote, int tpc, int& newTpc1, int& newTpc2)
{
if (oNote->concertPitch()) {
newTpc1 = tpc;
2014-04-14 10:39:27 +02:00
newTpc2 = oNote->transposeTpc(tpc);
}
else {
newTpc2 = tpc;
2014-04-14 10:39:27 +02:00
newTpc1 = oNote->transposeTpc(tpc);
}
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// upDown
/// Increment/decrement pitch of note by one or by an octave.
//---------------------------------------------------------
void Score::upDown(bool up, UpDownMode mode)
{
QList<Note*> el;
2014-03-26 11:02:23 +01:00
for (Note* note : selection().noteList()) {
2012-05-26 14:26:10 +02:00
while (note->tieBack())
note = note->tieBack()->startNote();
for (; note; note = note->tieFor() ? note->tieFor()->endNote() : 0) {
2014-03-26 11:02:23 +01:00
if (!el.contains(note))
2012-05-26 14:26:10 +02:00
el.append(note);
}
}
2014-03-26 11:02:23 +01:00
2012-05-26 14:26:10 +02:00
if (el.empty())
return;
2014-03-26 11:02:23 +01:00
foreach (Note* oNote, el) {
int tick = oNote->chord()->tick();
2013-06-12 14:23:57 +02:00
Part* part = oNote->staff()->part();
int key = oNote->staff()->key(tick).accidentalType();
2014-04-10 13:13:37 +02:00
int tpc1 = oNote->tpc1();
int tpc2 = oNote->tpc2();
2013-06-12 14:23:57 +02:00
int pitch = oNote->pitch();
2014-04-10 13:13:37 +02:00
int newTpc1 = tpc1; // default to unchanged
int newTpc2 = tpc2; // default to unchanged
int newPitch = pitch; // default to unchanged
2013-06-12 14:23:57 +02:00
int string = oNote->string();
int fret = oNote->fret();
2012-05-26 14:26:10 +02:00
2014-03-26 11:02:23 +01:00
switch (oNote->staff()->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-05-26 14:26:10 +02:00
{
Drumset* ds = part->instr()->drumset();
if (ds)
2014-03-26 11:02:23 +01:00
newPitch = up ? ds->prevPitch(pitch) : ds->nextPitch(pitch);
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 TAB_STAFF_GROUP:
2012-05-26 14:26:10 +02:00
{
2014-03-26 11:02:23 +01:00
StringData* stringData = part->instr()->stringData();
2012-05-26 14:26:10 +02:00
switch(mode) {
case UP_DOWN_OCTAVE: // move same note to next string, if possible
{
2014-03-26 11:02:23 +01:00
StaffTypeTablature* stt = static_cast<StaffTypeTablature*>(oNote->staff()->staffType());
string = stt->physStringToVisual(string);
2012-05-26 14:26:10 +02:00
string += (up ? -1 : 1);
2014-03-26 11:02:23 +01:00
if (string < 0 || string >= stringData->strings())
2012-05-26 14:26:10 +02:00
return; // no next string to move to
string = stt->VisualStringToPhys(string);
fret = stringData->fret(pitch, string);
2014-03-26 11:02:23 +01:00
if (fret == -1) // can't have that note on that string
2012-05-26 14:26:10 +02:00
return;
// newPitch and newTpc remain unchanged
2012-05-26 14:26:10 +02:00
}
break;
case UP_DOWN_DIATONIC: // increase / decrease the pitch,
2012-05-26 14:26:10 +02:00
// letting the algorithm to choose fret & string
upDownChromatic(up, pitch, oNote, key, tpc1, tpc2, newPitch, newTpc1, newTpc2);
2012-05-26 14:26:10 +02:00
break;
case UP_DOWN_CHROMATIC: // increase / decrease the fret
2012-05-26 14:26:10 +02:00
{ // without changing the string
fret += (up ? 1 : -1);
if (fret < 0)
fret = 0;
else if (fret >= stringData->frets())
fret = stringData->frets() - 1;
newPitch = stringData->getPitch(string, fret);
int nTpc = pitch2tpc(newPitch, key, up ? PREFER_SHARPS : PREFER_FLATS);
if (oNote->concertPitch()) {
newTpc1 = nTpc;
newTpc2 = oNote->tpc2default(newPitch);
}
else {
newTpc2 = nTpc;
newTpc1 = oNote->tpc1default(newPitch);
}
2012-05-26 14:26:10 +02:00
// store the fretting change before undoChangePitch() chooses
// a fretting of its own liking!
2012-08-06 09:29:11 +02:00
undoChangeProperty(oNote, P_FRET, fret);
undoChangeProperty(oNote, P_STRING, string);
2012-05-26 14:26:10 +02:00
}
break;
}
}
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-05-26 14:26:10 +02:00
switch(mode) {
case UP_DOWN_OCTAVE:
if (up) {
if (pitch < 116)
newPitch = pitch + 12;
}
else {
if (pitch > 11)
newPitch = pitch - 12;
}
// newTpc remains unchanged
2012-05-26 14:26:10 +02:00
break;
case UP_DOWN_CHROMATIC:
upDownChromatic(up, pitch, oNote, key, tpc1, tpc2, newPitch, newTpc1, newTpc2);
2012-05-26 14:26:10 +02:00
break;
case UP_DOWN_DIATONIC:
{
int tpc = oNote->tpc();
if (up) {
if (tpc > TPC_A + key) {
if (pitch < 127) {
newPitch = pitch + 1;
2014-04-14 10:39:27 +02:00
setTpc(oNote, tpc - 5, newTpc1, newTpc2);
}
}
else {
if (pitch < 126) {
newPitch = pitch + 2;
2014-04-14 10:39:27 +02:00
setTpc(oNote, tpc + 2, newTpc1, newTpc2);
}
}
}
else {
if (tpc > TPC_C + key) {
if (pitch > 1) {
newPitch = pitch - 2;
2014-04-14 10:39:27 +02:00
setTpc(oNote, tpc - 2, newTpc1, newTpc2);
}
}
else {
if (pitch > 0) {
newPitch = pitch - 1;
2014-04-14 10:39:27 +02:00
setTpc(oNote, tpc + 5, newTpc1, newTpc2);
}
}
}
}
2012-05-26 14:26:10 +02:00
break;
}
break;
}
2014-04-10 13:13:37 +02:00
if ((oNote->pitch() != newPitch) || (oNote->tpc1() != newTpc1) || oNote->tpc2() != newTpc2) {
// remove accidental if present to make sure
// user added accidentals are removed here.
if (oNote->accidental())
undoRemoveElement(oNote->accidental());
2014-04-10 13:13:37 +02:00
undoChangePitch(oNote, newPitch, newTpc1, newTpc2);
}
2012-05-26 14:26:10 +02:00
// store fret change only if undoChangePitch has not been called,
// as undoChangePitch() already manages fret changes, if necessary
2014-03-26 11:02:23 +01:00
else if (oNote->staff()->staffType()->group() == TAB_STAFF_GROUP) {
bool refret = false;
if (oNote->string() != string) {
2012-08-06 09:29:11 +02:00
undoChangeProperty(oNote, P_STRING, string);
refret = true;
}
if (oNote->fret() != fret) {
2012-08-06 09:29:11 +02:00
undoChangeProperty(oNote, P_FRET, fret);
refret = true;
}
2014-03-26 11:02:23 +01:00
if (refret) {
StringData* stringData = part->instr()->stringData();
stringData->fretChords(oNote->chord());
2014-03-26 11:02:23 +01:00
}
2012-08-06 09:29:11 +02:00
}
2012-05-26 14:26:10 +02:00
// play new note with velocity 80 for 0.3 sec:
_playNote = true;
}
2013-02-18 19:52:24 +01:00
_selection.clear();
2014-04-10 13:13:37 +02:00
for (Note* note : el)
2013-02-18 19:52:24 +01:00
_selection.add(note);
2012-05-26 14:26:10 +02:00
_selection.updateState(); // accidentals may have changed
}
//---------------------------------------------------------
// addArticulation
/// Add attribute \a attr to all selected notes/rests.
///
/// Called from padToggle() to add note prefix/accent.
//---------------------------------------------------------
void Score::addArticulation(ArticulationType attr)
{
foreach(Element* el, selection().elements()) {
2012-09-13 18:01:34 +02:00
if (el->type() == Element::NOTE || el->type() == Element::CHORD) {
2012-05-26 14:26:10 +02:00
Articulation* na = new Articulation(this);
na->setArticulationType(attr);
if (!addArticulation(el, na))
delete na;
2012-05-26 14:26:10 +02:00
}
}
}
//---------------------------------------------------------
// changeAccidental
/// Change accidental to subtype \a idx for all selected
/// notes.
//---------------------------------------------------------
void Score::changeAccidental(Accidental::AccidentalType idx)
2012-05-26 14:26:10 +02:00
{
2013-11-27 11:51:16 +01:00
foreach(Note* note, selection().noteList())
2012-05-26 14:26:10 +02:00
changeAccidental(note, idx);
}
//---------------------------------------------------------
// changeAccidental2
//---------------------------------------------------------
2013-11-27 11:51:16 +01:00
static void changeAccidental2(Note* n, int pitch, int tpc)
{
Score* score = n->score();
Chord* chord = n->chord();
int staffIdx = chord->staffIdx();
Staff* st = chord->staff();
int fret = n->fret();
int string = n->string();
if (st->isTabStaff()) {
if (pitch != n->pitch()) {
//
// as pitch has changed, calculate new
// string & fret
//
StringData* stringData = n->staff()->part()->instr()->stringData();
if (stringData)
stringData->convertPitch(pitch, &string, &fret);
}
}
2014-04-14 10:39:27 +02:00
int tpc1;
int tpc2 = n->transposeTpc(tpc);
if (score->styleB(ST_concertPitch))
tpc1 = tpc;
else {
tpc1 = tpc2;
tpc2 = tpc;
}
score->undoChangePitch(n, pitch, tpc1, tpc2);
if (!st->isTabStaff()) {
//
// handle ties
//
if (n->tieBack()) {
score->undoRemoveElement(n->tieBack());
if (n->tieFor())
score->undoRemoveElement(n->tieFor());
}
else {
Note* nn = n;
while (nn->tieFor()) {
nn = nn->tieFor()->endNote();
score->undo(new ChangePitch(nn, pitch, tpc, nn->line()));
}
}
}
//
// recalculate needed accidentals for
// whole measure
//
2014-04-22 17:02:03 +02:00
chord->measure()->cmdUpdateNotes(staffIdx);
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// changeAccidental
/// Change accidental to subtype \accidental for
/// note \a note.
//---------------------------------------------------------
void Score::changeAccidental(Note* note, Accidental::AccidentalType accidental)
2012-05-26 14:26:10 +02:00
{
QList<Staff*> staffList;
Staff* ostaff = note->chord()->staff();
LinkedStaves* linkedStaves = ostaff->linkedStaves();
if (linkedStaves)
staffList = linkedStaves->staves();
else
staffList.append(ostaff);
Chord* chord = note->chord();
Segment* segment = chord->segment();
Measure* measure = segment->measure();
int tick = segment->tick();
Staff* estaff = staff(chord->staffIdx() + chord->staffMove());
2013-09-05 16:37:49 +02:00
ClefType clef = estaff->clef(tick);
int step = ClefInfo::pitchOffset(clef) - note->line();
2012-05-26 14:26:10 +02:00
while (step < 0)
step += 7;
step %= 7;
//
// accidental change may result in pitch change
//
2014-04-14 10:39:27 +02:00
AccidentalVal acc2 = measure->findAccidental(note);
AccidentalVal acc = (accidental == Accidental::ACC_NONE) ? acc2 : Accidental::subtype2value(accidental);
2012-05-26 14:26:10 +02:00
2014-04-14 10:39:27 +02:00
int pitch = line2pitch(note->line(), clef, 0) + acc;
if (!note->concertPitch())
pitch += note->transposition();
int tpc = step2tpc(step, acc);
if (accidental == Accidental::ACC_NONE) {
2012-05-26 14:26:10 +02:00
//
// delete accidentals
//
// check if there's accidentals left, previously set as
// precautionary accidentals
Accidental* a = note->accidental();
if (a)
2012-05-26 14:26:10 +02:00
undoRemoveElement(note->accidental());
}
else {
2014-04-14 10:39:27 +02:00
if (acc == acc2) {
2012-05-26 14:26:10 +02:00
//
// this is a precautionary accidental
//
Accidental* a = new Accidental(this);
a->setParent(note);
a->setAccidentalType(accidental);
a->setRole(Accidental::ACC_USER);
undoAddElement(a);
2012-05-26 14:26:10 +02:00
}
}
if (note->links()) {
for (Element* e : *note->links())
changeAccidental2(static_cast<Note*>(e), pitch, tpc);
2012-05-26 14:26:10 +02:00
}
else
changeAccidental2(note, pitch, tpc);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// addArticulation
//---------------------------------------------------------
bool Score::addArticulation(Element* el, Articulation* a)
2012-05-26 14:26:10 +02:00
{
ChordRest* cr;
2012-09-13 18:01:34 +02:00
if (el->type() == Element::NOTE)
cr = static_cast<ChordRest*>(static_cast<Note*>(el)->chord());
else if (el->type() == Element::REST
|| el->type() == Element::CHORD
|| el->type() == Element::REPEAT_MEASURE)
2012-05-26 14:26:10 +02:00
cr = static_cast<ChordRest*>(el);
else
return false;
Articulation* oa = cr->hasArticulation(a);
2012-05-26 14:26:10 +02:00
if (oa) {
undoRemoveElement(oa);
return false;
2012-05-26 14:26:10 +02:00
}
a->setParent(cr);
undoAddElement(a);
return true;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// resetUserStretch
//---------------------------------------------------------
void Score::resetUserStretch()
{
Measure* m1;
Measure* m2;
// retrieve span of selection
Segment* s1 = _selection.startSegment();
Segment* s2 = _selection.endSegment();
// if either segment is not returned by the selection
// (for instance, no selection) fall back to first/last measure
if(!s1)
m1 = firstMeasure();
else
m1 = s1->measure();
if(!s2)
m2 = lastMeasure();
else
m2 = s2->measure();
if(!m1 || !m2) // should not happen!
return;
for (Measure* m = m1; m; m = m->nextMeasure()) {
2013-03-18 15:14:05 +01:00
undo(new ChangeStretch(m, 1.0));
2012-05-26 14:26:10 +02:00
if (m == m2)
break;
}
_layoutAll = true;
}
//---------------------------------------------------------
// moveUp
//---------------------------------------------------------
void Score::moveUp(Chord* chord)
{
Staff* staff = chord->staff();
Part* part = staff->part();
int rstaff = staff->rstaff();
2012-05-26 14:26:10 +02:00
int staffMove = chord->staffMove();
if ((staffMove == -1) || (rstaff + staffMove <= 0))
return;
QList<Staff*>* staves = part->staves();
// we know that staffMove+rstaff-1 index exists due to the previous condition.
if (staff->staffType()->group() != STANDARD_STAFF_GROUP ||
staves->at(rstaff+staffMove-1)->staffType()->group() != STANDARD_STAFF_GROUP) {
qDebug("User attempted to move a note from/to a staff which does not use standard notation - ignoring.");
}
else {
// move the chord up a staff
undo(new ChangeChordStaffMove(chord, staffMove - 1));
}
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// moveDown
//---------------------------------------------------------
void Score::moveDown(Chord* chord)
{
Staff* staff = chord->staff();
Part* part = staff->part();
int rstaff = staff->rstaff();
int staffMove = chord->staffMove();
// calculate the number of staves available so that we know whether there is another staff to move down to
int rstaves = part->nstaves();
2012-05-26 14:26:10 +02:00
if ((staffMove == 1) || (rstaff + staffMove >= rstaves - 1)) {
qDebug("moveDown staffMove==%d rstaff %d rstaves %d", staffMove, rstaff, rstaves);
2012-05-26 14:26:10 +02:00
return;
}
QList<Staff*>* staves = part->staves();
// we know that staffMove+rstaff+1 index exists due to the previous condition.
if (staff->staffType()->group() != STANDARD_STAFF_GROUP ||
staves->at(staffMove+rstaff+1)->staffType()->group() != STANDARD_STAFF_GROUP) {
qDebug("User attempted to move a note from/to a staff which does not use standard notation - ignoring.");
}
else {
// move the chord down a staff
undo(new ChangeChordStaffMove(chord, staffMove + 1));
}
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// cmdAddStretch
//---------------------------------------------------------
void Score::cmdAddStretch(qreal val)
{
if (selection().state() != SEL_RANGE)
return;
int startTick = selection().tickStart();
int endTick = selection().tickEnd();
2014-04-16 11:41:06 +02:00
for (Measure* m = firstMeasureMM(); m; m = m->nextMeasureMM()) {
2012-05-26 14:26:10 +02:00
if (m->tick() < startTick)
continue;
if (m->tick() >= endTick)
break;
qreal stretch = m->userStretch();
stretch += val;
undo(new ChangeStretch(m, stretch));
}
_layoutAll = true;
}
//---------------------------------------------------------
// cmdInsertClef
//---------------------------------------------------------
void Score::cmdInsertClef(ClefType type)
{
if (!noteEntryMode())
return;
undoChangeClef(staff(inputTrack()/VOICES), inputState().segment(), type);
}
//---------------------------------------------------------
// cmdResetBeamMode
//---------------------------------------------------------
void Score::cmdResetBeamMode()
{
if (selection().state() != SEL_RANGE) {
qDebug("no system or staff selected");
return;
}
int startTick = selection().tickStart();
int endTick = selection().tickEnd();
2013-06-12 14:23:57 +02:00
Segment::SegmentTypes st = Segment::SegChordRest;
2012-05-26 14:26:10 +02:00
for (Segment* seg = firstMeasure()->first(st); seg; seg = seg->next1(st)) {
if (seg->tick() < startTick)
continue;
if (seg->tick() >= endTick)
break;
for (int track = 0; track < nstaves() * VOICES; ++track) {
ChordRest* cr = static_cast<ChordRest*>(seg->element(track));
if (cr == 0)
continue;
2012-09-13 18:01:34 +02:00
if (cr->type() == Element::CHORD) {
if (cr->beamMode() != BeamMode::AUTO)
undoChangeProperty(cr, P_BEAM_MODE, int(BeamMode::AUTO));
2012-05-26 14:26:10 +02:00
}
2012-09-13 18:01:34 +02:00
else if (cr->type() == Element::REST) {
2013-03-26 23:11:24 +01:00
if (cr->beamMode() != BeamMode::NONE)
undoChangeProperty(cr, P_BEAM_MODE, int(BeamMode::NONE));
2012-05-26 14:26:10 +02:00
}
}
}
_layoutAll = true;
}
//---------------------------------------------------------
// processMidiInput
//---------------------------------------------------------
bool Score::processMidiInput()
{
if (MScore::debugMode)
qDebug("processMidiInput");
if (midiInputQueue.isEmpty())
return false;
bool cmdActive = false;
Note* n = 0;
while (!midiInputQueue.isEmpty()) {
MidiInputEvent ev = midiInputQueue.dequeue();
if (MScore::debugMode)
qDebug("<-- !noteentry dequeue %i", ev.pitch);
if (!noteEntryMode()) {
int staffIdx = selection().staffStart();
Part* p;
if (staffIdx < 0 || staffIdx >= nstaves())
p = staff(0)->part();
2012-05-26 14:26:10 +02:00
else
p = staff(staffIdx)->part();
if (p)
2012-06-28 14:50:47 +02:00
MScore::seq->startNote(p->instr()->channel(0).channel, ev.pitch, 80,
2012-05-26 14:26:10 +02:00
MScore::defaultPlayDuration, 0.0);
}
else {
if (!cmdActive) {
startCmd();
cmdActive = true;
}
Note* n2 = addPitch(ev.pitch, ev.chord);
if (n2)
n = n2;
2012-05-26 14:26:10 +02:00
}
}
if (cmdActive) {
_layoutAll = true;
endCmd();
//after relayout
2013-01-17 20:42:44 +01:00
if (n) {
foreach(MuseScoreView* v, viewer)
v->adjustCanvasPosition(n, false);
}
2012-05-26 14:26:10 +02:00
return true;
}
return false;
}
//---------------------------------------------------------
// move
// move current selection
//---------------------------------------------------------
Element* Score::move(const QString& cmd)
{
ChordRest* cr;
if (selection().activeCR())
cr = selection().activeCR();
else
cr = selection().lastChordRest();
2013-10-24 12:09:00 +02:00
if (cr == 0 && noteEntryMode())
2012-05-26 14:26:10 +02:00
cr = inputState().cr();
// no chord/rest found? look for another type of element
if (cr == 0) {
Element* el = 0;
Element* trg = 0;
// retrieve last element of section list
if (!selection().elements().isEmpty())
el = selection().elements().last();
2013-08-02 17:39:45 +02:00
if (!el) // no element, no party!
return 0;
// get parent of element and process accordingly:
// trg is the element to select on "next-chord" cmd
// cr is the ChordRest to move from on other cmd's
int track = el->track(); // keep note of element track
el = el->parent();
switch (el->type()) {
case Element::NOTE: // a note is a valid target
trg = el;
cr = static_cast<Note*>(el)->chord();
break;
case Element::CHORD: // a chord or a rest are valid targets
case Element::REST:
trg = el;
cr = static_cast<ChordRest*>(trg);
break;
case Element::SEGMENT: { // from segment go to top chordrest in segment
Segment* seg = static_cast<Segment*>(el);
// if segment is not chord/rest or grace, move to next chord/rest or grace segment
2013-06-12 14:23:57 +02:00
if (!seg->isChordRest()) {
seg = seg->next1(Segment::SegChordRest);
if (seg == 0) // if none found, reutrn failure
return 0;
}
// segment for sure contains chords/rests,
int size = seg->elist().size();
// if segment has a chord/rest in original element track, use it
if(track > -1 && track < size && seg->element(track)) {
trg = seg->element(track);
cr = static_cast<ChordRest*>(trg);
break;
}
// if not, get topmost chord/rest
for (int i = 0; i < size; i++)
if (seg->element(i)) {
trg = seg->element(i);
cr = static_cast<ChordRest*>(trg);
break;
}
break;
}
default: // on anything else, return failure
return 0;
}
// if something found and command is forward, the element found is the destination
if (trg && cmd == "next-chord") {
// if chord, go to topmost note
if (trg->type() == Element::CHORD)
trg = static_cast<Chord*>(trg)->upNote();
_playNote = true;
select(trg, SELECT_SINGLE, 0);
return trg;
}
// if no chordrest found, do nothing
2013-08-02 17:39:45 +02:00
if (cr == 0)
return 0;
// if some chordrest found, continue with default processing
}
2012-05-26 14:26:10 +02:00
Element* el = 0;
if (cmd == "next-chord") {
if (noteEntryMode())
2013-10-24 12:09:00 +02:00
_is.moveToNextInputPos();
2013-06-12 14:23:57 +02:00
el = nextChordRest(cr);
2012-05-26 14:26:10 +02:00
}
else if (cmd == "prev-chord") {
if (noteEntryMode()) {
Segment* s = _is.segment()->prev1();
//
// if _is._segment is first chord/rest segment in measure
// make sure "m" points to previous measure
//
while (s && s->segmentType() != Segment::SegChordRest)
2012-05-26 14:26:10 +02:00
s = s->prev1();
if (s == 0)
return 0;
Measure* m = s->measure();
int track = _is.track();
for (; s; s = s->prev1()) {
if (s->segmentType() != Segment::SegChordRest)
2012-05-26 14:26:10 +02:00
continue;
if (s->element(track) || s->measure() != m)
break;
}
if (s && !s->element(track))
2013-06-28 17:46:24 +02:00
s = m->first(Segment::SegChordRest);
2013-10-24 12:09:00 +02:00
_is.moveInputPos(s);
2012-05-26 14:26:10 +02:00
}
2013-06-12 14:23:57 +02:00
el = prevChordRest(cr);
2012-05-26 14:26:10 +02:00
}
else if (cmd == "next-measure") {
2012-05-26 14:26:10 +02:00
el = nextMeasure(cr);
2013-10-24 12:09:00 +02:00
if (noteEntryMode())
_is.moveInputPos(el);
2012-05-26 14:26:10 +02:00
}
else if (cmd == "prev-measure") {
2012-05-26 14:26:10 +02:00
el = prevMeasure(cr);
2013-10-24 12:09:00 +02:00
if (noteEntryMode())
_is.moveInputPos(el);
2012-05-26 14:26:10 +02:00
}
else if (cmd == "next-track") {
el = nextTrack(cr);
2013-10-24 12:09:00 +02:00
if (noteEntryMode())
_is.moveInputPos(el);
}
else if (cmd == "prev-track") {
el = prevTrack(cr);
2013-10-24 12:09:00 +02:00
if (noteEntryMode())
_is.moveInputPos(el);
}
2012-05-26 14:26:10 +02:00
if (el) {
2012-09-13 18:01:34 +02:00
if (el->type() == Element::CHORD)
el = static_cast<Chord*>(el)->upNote(); // originally downNote
2012-05-26 14:26:10 +02:00
_playNote = true;
select(el, SELECT_SINGLE, 0);
}
return el;
}
//---------------------------------------------------------
// selectMove
//---------------------------------------------------------
Element* Score::selectMove(const QString& cmd)
{
ChordRest* cr;
if (selection().activeCR())
cr = selection().activeCR();
else
cr = selection().lastChordRest();
2013-10-24 12:09:00 +02:00
if (cr == 0 && noteEntryMode())
2012-05-26 14:26:10 +02:00
cr = inputState().cr();
if (cr == 0)
return 0;
ChordRest* el = 0;
if (cmd == "select-next-chord")
el = nextChordRest(cr);
else if (cmd == "select-prev-chord")
el = prevChordRest(cr);
else if (cmd == "select-next-measure")
el = nextMeasure(cr, true);
else if (cmd == "select-prev-measure")
el = prevMeasure(cr);
else if (cmd == "select-begin-line") {
Measure* measure = cr->segment()->measure()->system()->firstMeasure();
if (!measure)
return 0;
el = measure->first()->nextChordRest(cr->track());
}
else if (cmd == "select-end-line") {
Measure* measure = cr->segment()->measure()->system()->lastMeasure();
if (!measure)
return 0;
el = measure->last()->nextChordRest(cr->track(), true);
}
else if (cmd == "select-begin-score") {
Measure* measure = first()->system()->firstMeasure();
if (!measure)
return 0;
el = measure->first()->nextChordRest(cr->track());
}
else if (cmd == "select-end-score") {
Measure* measure = last()->system()->lastMeasure();
if (!measure)
return 0;
el = measure->last()->nextChordRest(cr->track(), true);
}
else if (cmd == "select-staff-above")
el = upStaff(cr);
else if (cmd == "select-staff-below")
el = downStaff(cr);
if (el)
select(el, SELECT_RANGE, el->staffIdx());
return el;
}
//---------------------------------------------------------
// cmdMirrorNoteHead
//---------------------------------------------------------
void Score::cmdMirrorNoteHead()
{
const QList<Element*>& el = selection().elements();
foreach(Element* e, el) {
2012-09-13 18:01:34 +02:00
if (e->type() == Element::NOTE) {
2012-05-26 14:26:10 +02:00
Note* note = static_cast<Note*>(e);
if (note->staff() && note->staff()->isTabStaff())
2012-05-26 14:26:10 +02:00
note->score()->undoChangeProperty(e, P_GHOST, true);
else {
2012-08-04 15:46:43 +02:00
MScore::DirectionH d = note->userMirror();
if (d == MScore::DH_AUTO)
d = note->chord()->up() ? MScore::DH_RIGHT : MScore::DH_LEFT;
2012-05-26 14:26:10 +02:00
else
2012-08-04 15:46:43 +02:00
d = d == MScore::DH_LEFT ? MScore::DH_RIGHT : MScore::DH_LEFT;
2012-05-26 14:26:10 +02:00
undoChangeUserMirror(note, d);
}
}
}
}
//---------------------------------------------------------
// cmdHalfDuration
//---------------------------------------------------------
void Score::cmdHalfDuration()
{
Element* el = selection().element();
if (el == 0)
return;
2012-09-13 18:01:34 +02:00
if (el->type() == Element::NOTE)
2012-05-26 14:26:10 +02:00
el = el->parent();
if (!el->isChordRest())
return;
ChordRest* cr = static_cast<ChordRest*>(el);
TDuration d = _is.duration().shift(1);
if (!d.isValid() || (d.type() > TDuration::V_64TH))
return;
2012-09-13 18:01:34 +02:00
if (cr->type() == Element::CHORD && (static_cast<Chord*>(cr)->noteType() != NOTE_NORMAL)) {
2012-05-26 14:26:10 +02:00
//
// handle appoggiatura and acciaccatura
//
cr->setDurationType(d);
}
else
changeCRlen(cr, d);
_is.setDuration(d);
nextInputPos(cr, false);
}
//---------------------------------------------------------
// cmdDoubleDuration
//---------------------------------------------------------
void Score::cmdDoubleDuration()
{
Element* el = selection().element();
if (el == 0)
return;
2012-09-13 18:01:34 +02:00
if (el->type() == Element::NOTE)
2012-05-26 14:26:10 +02:00
el = el->parent();
if (!el->isChordRest())
return;
ChordRest* cr = static_cast<ChordRest*>(el);
TDuration d = _is.duration().shift(-1);
if (!d.isValid() || (d.type() < TDuration::V_WHOLE))
return;
2012-09-13 18:01:34 +02:00
if (cr->type() == Element::CHORD && (static_cast<Chord*>(cr)->noteType() != NOTE_NORMAL)) {
2012-05-26 14:26:10 +02:00
//
// handle appoggiatura and acciaccatura
//
cr->setDurationType(d);
}
else
changeCRlen(cr, d);
_is.setDuration(d);
nextInputPos(cr, false);
}
//---------------------------------------------------------
// cmdMoveRest
//---------------------------------------------------------
2012-08-04 15:46:43 +02:00
void Score::cmdMoveRest(Rest* rest, MScore::Direction dir)
2012-05-26 14:26:10 +02:00
{
QPointF pos(rest->userOff());
2012-08-04 15:46:43 +02:00
if (dir == MScore::UP)
2012-05-26 14:26:10 +02:00
pos.ry() -= spatium();
2012-08-04 15:46:43 +02:00
else if (dir == MScore::DOWN)
2012-05-26 14:26:10 +02:00
pos.ry() += spatium();
2012-08-23 17:40:04 +02:00
undoChangeProperty(rest, P_USER_OFF, pos);
2012-05-26 14:26:10 +02:00
setLayoutAll(false);
}
//---------------------------------------------------------
// cmdMoveLyrics
//---------------------------------------------------------
2012-08-04 15:46:43 +02:00
void Score::cmdMoveLyrics(Lyrics* lyrics, MScore::Direction dir)
2012-05-26 14:26:10 +02:00
{
ChordRest* cr = lyrics->chordRest();
QList<Lyrics*>& ll = cr->lyricsList();
int no = lyrics->no();
2012-08-04 15:46:43 +02:00
if (dir == MScore::UP) {
2012-05-26 14:26:10 +02:00
if (no) {
if (ll[no-1] == 0) {
ll[no-1] = ll[no];
ll[no] = 0;
lyrics->setNo(no-1);
}
}
}
else {
if (no == ll.size()-1) {
ll.append(ll[no]);
ll[no] = 0;
lyrics->setNo(no+1);
}
else if (ll[no + 1] == 0) {
ll[no+1] = ll[no];
ll[no] = 0;
lyrics->setNo(no+1);
}
}
}
//---------------------------------------------------------
// cmd
//---------------------------------------------------------
void Score::cmd(const QAction* a)
{
QString cmd(a ? a->data().toString() : "");
if (MScore::debugMode)
qDebug("Score::cmd <%s>", qPrintable(cmd));
//
// Hack for moving articulations while selected
//
Element* el = selection().element();
if (cmd == "pitch-up") {
if (el && (el->type() == Element::ARTICULATION || el->isText()))
2012-05-26 14:26:10 +02:00
undoMove(el, el->userOff() + QPointF(0.0, -MScore::nudgeStep * el->spatium()));
2012-09-13 18:01:34 +02:00
else if (el && el->type() == Element::REST)
2012-08-04 15:46:43 +02:00
cmdMoveRest(static_cast<Rest*>(el), MScore::UP);
2012-09-13 18:01:34 +02:00
else if (el && el->type() == Element::LYRICS)
2012-08-04 15:46:43 +02:00
cmdMoveLyrics(static_cast<Lyrics*>(el), MScore::UP);
2012-05-26 14:26:10 +02:00
else
upDown(true, UP_DOWN_CHROMATIC);
}
else if (cmd == "pitch-down") {
if (el && (el->type() == Element::ARTICULATION || el->isText()))
2012-05-26 14:26:10 +02:00
undoMove(el, el->userOff() + QPointF(0.0, MScore::nudgeStep * el->spatium()));
2012-09-13 18:01:34 +02:00
else if (el && el->type() == Element::REST)
2012-08-04 15:46:43 +02:00
cmdMoveRest(static_cast<Rest*>(el), MScore::DOWN);
2012-09-13 18:01:34 +02:00
else if (el && el->type() == Element::LYRICS)
2012-08-04 15:46:43 +02:00
cmdMoveLyrics(static_cast<Lyrics*>(el), MScore::DOWN);
2012-05-26 14:26:10 +02:00
else
upDown(false, UP_DOWN_CHROMATIC);
}
else if (cmd == "add-staccato")
addArticulation(Articulation_Staccato);
else if (cmd == "add-tenuto")
addArticulation(Articulation_Tenuto);
else if (cmd == "add-marcato")
addArticulation(Articulation_Marcato);
2012-05-26 14:26:10 +02:00
else if (cmd == "add-trill")
addArticulation(Articulation_Trill);
else if (cmd == "add-hairpin")
cmdAddHairpin(false);
else if (cmd == "add-hairpin-reverse")
cmdAddHairpin(true);
else if (cmd == "add-8va")
cmdAddOttava(OttavaType::OTTAVA_8VA);
else if (cmd == "add-8vb")
cmdAddOttava(OttavaType::OTTAVA_8VB);
2012-05-26 14:26:10 +02:00
else if (cmd == "delete-measures")
cmdDeleteSelectedMeasures();
else if (cmd == "time-delete") {
// TODO:
// remove measures if stave-range is 0-nstaves()
cmdDeleteSelectedMeasures();
}
else if (cmd == "pitch-up-octave") {
if (el && (el->type() == Element::ARTICULATION || el->isText()))
undoMove(el, el->userOff() + QPointF(0.0, -MScore::nudgeStep10 * el->spatium()));
else
upDown(true, UP_DOWN_OCTAVE);
}
else if (cmd == "pitch-down-octave") {
if (el && (el->type() == Element::ARTICULATION || el->isText()))
undoMove(el, el->userOff() + QPointF(0.0, MScore::nudgeStep10 * el->spatium()));
else
upDown(false, UP_DOWN_OCTAVE);
}
2012-05-26 14:26:10 +02:00
else if (cmd == "pitch-up-diatonic")
upDown(true, UP_DOWN_DIATONIC);
else if (cmd == "pitch-down-diatonic")
upDown(false, UP_DOWN_DIATONIC);
else if (cmd == "move-up") {
setLayoutAll(false);
2012-09-13 18:01:34 +02:00
if (el && el->type() == Element::NOTE) {
2012-05-26 14:26:10 +02:00
Note* note = static_cast<Note*>(el);
moveUp(note->chord());
}
}
else if (cmd == "move-down") {
setLayoutAll(false);
2012-09-13 18:01:34 +02:00
if (el && el->type() == Element::NOTE) {
2012-05-26 14:26:10 +02:00
Note* note = static_cast<Note*>(el);
moveDown(note->chord());
}
}
else if (cmd == "up-chord") {
2012-09-13 18:01:34 +02:00
if (el && (el->type() == Element::NOTE || el->type() == Element::REST)) {
2012-05-26 14:26:10 +02:00
Element* e = upAlt(el);
if (e) {
2012-09-13 18:01:34 +02:00
if (e->type() == Element::NOTE) {
2012-05-26 14:26:10 +02:00
_playNote = true;
}
select(e, SELECT_SINGLE, 0);
}
}
setLayoutAll(false);
}
else if (cmd == "down-chord") {
2012-09-13 18:01:34 +02:00
if (el && (el->type() == Element::NOTE || el->type() == Element::REST)) {
2012-05-26 14:26:10 +02:00
Element* e = downAlt(el);
if (e) {
2012-09-13 18:01:34 +02:00
if (e->type() == Element::NOTE) {
2012-05-26 14:26:10 +02:00
_playNote = true;
}
select(e, SELECT_SINGLE, 0);
}
}
setLayoutAll(false);
}
else if (cmd == "top-chord" ) {
2012-09-13 18:01:34 +02:00
if (el && el->type() == Element::NOTE) {
2012-05-26 14:26:10 +02:00
Element* e = upAltCtrl(static_cast<Note*>(el));
if (e) {
2012-09-13 18:01:34 +02:00
if (e->type() == Element::NOTE) {
2012-05-26 14:26:10 +02:00
_playNote = true;
}
select(e, SELECT_SINGLE, 0);
}
}
setLayoutAll(false);
}
else if (cmd == "bottom-chord") {
2012-09-13 18:01:34 +02:00
if (el && el->type() == Element::NOTE) {
2012-05-26 14:26:10 +02:00
Element* e = downAltCtrl(static_cast<Note*>(el));
if (e) {
2012-09-13 18:01:34 +02:00
if (e->type() == Element::NOTE) {
2012-05-26 14:26:10 +02:00
_playNote = true;
}
select(e, SELECT_SINGLE, 0);
}
}
setLayoutAll(false);
}
else if (cmd == "note-longa" || cmd == "note-longa-TAB")
2012-05-26 14:26:10 +02:00
padToggle(PAD_NOTE00);
else if (cmd == "note-breve" || cmd == "note-breve-TAB")
2012-05-26 14:26:10 +02:00
padToggle(PAD_NOTE0);
else if (cmd == "pad-note-1" || cmd == "pad-note-1-TAB")
2012-05-26 14:26:10 +02:00
padToggle(PAD_NOTE1);
else if (cmd == "pad-note-2" || cmd == "pad-note-2-TAB")
2012-05-26 14:26:10 +02:00
padToggle(PAD_NOTE2);
else if (cmd == "pad-note-4" || cmd == "pad-note-4-TAB")
2012-05-26 14:26:10 +02:00
padToggle(PAD_NOTE4);
else if (cmd == "pad-note-8" || cmd == "pad-note-8-TAB")
2012-05-26 14:26:10 +02:00
padToggle(PAD_NOTE8);
else if (cmd == "pad-note-16" || cmd == "pad-note-16-TAB")
2012-05-26 14:26:10 +02:00
padToggle(PAD_NOTE16);
else if (cmd == "pad-note-32" || cmd == "pad-note-32-TAB")
2012-05-26 14:26:10 +02:00
padToggle(PAD_NOTE32);
else if (cmd == "pad-note-64" || cmd == "pad-note-64-TAB")
2012-05-26 14:26:10 +02:00
padToggle(PAD_NOTE64);
else if (cmd == "pad-note-128" || cmd == "pad-note-128-TAB")
2012-05-26 14:26:10 +02:00
padToggle(PAD_NOTE128);
else if (cmd == "pad-note-increase-TAB") {
switch (_is.duration().type() ) {
// cycle back from longest to shortest?
// case TDuration::V_LONG:
// padToggle(PAD_NOTE128);
// break;
case TDuration::V_BREVE:
padToggle(PAD_NOTE00);
break;
case TDuration::V_WHOLE:
padToggle(PAD_NOTE0);
break;
case TDuration::V_HALF:
padToggle(PAD_NOTE1);
break;
case TDuration::V_QUARTER:
padToggle(PAD_NOTE2);
break;
case TDuration::V_EIGHT:
padToggle(PAD_NOTE4);
break;
case TDuration::V_16TH:
padToggle(PAD_NOTE8);
break;
case TDuration::V_32ND:
padToggle(PAD_NOTE16);
break;
case TDuration::V_64TH:
padToggle(PAD_NOTE32);
break;
case TDuration::V_128TH:
padToggle(PAD_NOTE64);
break;
default:
break;
}
}
else if (cmd == "pad-note-decrease-TAB") {
switch (_is.duration().type() ) {
case TDuration::V_LONG:
padToggle(PAD_NOTE0);
break;
case TDuration::V_BREVE:
padToggle(PAD_NOTE1);
break;
case TDuration::V_WHOLE:
padToggle(PAD_NOTE2);
break;
case TDuration::V_HALF:
padToggle(PAD_NOTE4);
break;
case TDuration::V_QUARTER:
padToggle(PAD_NOTE8);
break;
case TDuration::V_EIGHT:
padToggle(PAD_NOTE16);
break;
case TDuration::V_16TH:
padToggle(PAD_NOTE32);
break;
case TDuration::V_32ND:
padToggle(PAD_NOTE64);
break;
case TDuration::V_64TH:
padToggle(PAD_NOTE128);
break;
// cycle back from shortest to longest?
// case TDuration::V_128TH:
// padToggle(PAD_NOTE00);
// break;
default:
break;
}
}
2012-05-26 14:26:10 +02:00
else if (cmd == "pad-rest")
padToggle(PAD_REST);
else if (cmd == "pad-dot")
padToggle(PAD_DOT);
else if (cmd == "pad-dotdot")
padToggle(PAD_DOTDOT);
else if (cmd == "beam-start")
cmdSetBeamMode(BeamMode::BEGIN);
2012-05-26 14:26:10 +02:00
else if (cmd == "beam-mid")
cmdSetBeamMode(BeamMode::MID);
2012-05-26 14:26:10 +02:00
else if (cmd == "no-beam")
2013-03-26 23:11:24 +01:00
cmdSetBeamMode(BeamMode::NONE);
2012-05-26 14:26:10 +02:00
else if (cmd == "beam-32")
cmdSetBeamMode(BeamMode::BEGIN32);
2012-05-26 14:26:10 +02:00
else if (cmd == "sharp2")
changeAccidental(Accidental::ACC_SHARP2);
2012-05-26 14:26:10 +02:00
else if (cmd == "sharp")
changeAccidental(Accidental::ACC_SHARP);
2012-05-26 14:26:10 +02:00
else if (cmd == "nat")
changeAccidental(Accidental::ACC_NATURAL);
2012-05-26 14:26:10 +02:00
else if (cmd == "flat")
changeAccidental(Accidental::ACC_FLAT);
2012-05-26 14:26:10 +02:00
else if (cmd == "flat2")
changeAccidental(Accidental::ACC_FLAT2);
2012-05-26 14:26:10 +02:00
else if (cmd == "repitch")
_is.setRepitchMode(a->isChecked());
else if (cmd == "flip")
cmdFlip();
else if (cmd == "stretch+")
cmdAddStretch(0.1);
else if (cmd == "stretch-")
cmdAddStretch(-0.1);
else if (cmd == "pitch-spell")
spell();
else if (cmd == "select-all")
cmdSelectAll();
else if (cmd == "select-section")
cmdSelectSection();
else if (cmd == "concert-pitch") {
if (styleB(ST_concertPitch) != a->isChecked())
cmdConcertPitchChanged(a->isChecked(), true);
}
else if (cmd == "reset-beammode")
cmdResetBeamMode();
else if (cmd == "clef-violin")
2013-09-05 16:37:49 +02:00
cmdInsertClef(ClefType::G);
2012-05-26 14:26:10 +02:00
else if (cmd == "clef-bass")
2013-09-05 16:37:49 +02:00
cmdInsertClef(ClefType::F);
2012-05-26 14:26:10 +02:00
else if (cmd == "voice-x12")
cmdExchangeVoice(0, 1);
else if (cmd == "voice-x13")
cmdExchangeVoice(0, 2);
else if (cmd == "voice-x14")
cmdExchangeVoice(0, 3);
else if (cmd == "voice-x23")
cmdExchangeVoice(1, 2);
else if (cmd == "voice-x24")
cmdExchangeVoice(1, 3);
else if (cmd == "voice-x34")
cmdExchangeVoice(2, 3);
else if (cmd == "system-break" || cmd == "page-break" || cmd == "section-break") {
2013-10-05 23:13:33 +02:00
LayoutBreak::LayoutBreakType type;
2012-05-26 14:26:10 +02:00
if (cmd == "system-break")
2013-10-05 23:13:33 +02:00
type = LayoutBreak::LINE;
2012-05-26 14:26:10 +02:00
else if (cmd == "page-break")
2013-10-05 23:13:33 +02:00
type = LayoutBreak::PAGE;
2012-05-26 14:26:10 +02:00
else
2013-10-05 23:13:33 +02:00
type = LayoutBreak::SECTION;
2012-05-26 14:26:10 +02:00
2013-10-29 14:34:02 +01:00
if (el && el->type() == Element::BAR_LINE && el->parent()->type() == Element::SEGMENT) {
Measure* measure = static_cast<Measure*>(el->parent()->parent());
2013-09-27 18:43:25 +02:00
if (measure->isMMRest()) {
// if measure is mm rest, then propagate to last original measure
measure = measure->nextMeasure();
if (measure)
measure = measure->prevMeasure();
}
if (measure)
measure->undoSetBreak(!measure->lineBreak(), type);
2012-05-26 14:26:10 +02:00
}
}
else if (cmd == "reset-stretch")
resetUserStretch();
else if (cmd == "mirror-note")
cmdMirrorNoteHead();
else if (cmd == "double-duration")
cmdDoubleDuration();
else if (cmd == "half-duration")
cmdHalfDuration();
else if (cmd == "") { //Midi note received only?
if (!noteEntryMode())
setLayoutAll(false);
}
else if (cmd == "add-audio")
addAudioTrack();
else if (cmd == "transpose-up")
transposeSemitone(1);
else if (cmd == "transpose-down")
transposeSemitone(-1);
else if (cmd == "toggle-mmrest") {
bool val = !styleB(ST_createMultiMeasureRests);
undo(new ChangeStyleVal(this, ST_createMultiMeasureRests, val));
}
2012-05-26 14:26:10 +02:00
else
2014-04-23 14:02:33 +02:00
qDebug("unknown cmd <%s>", qPrintable(cmd));
2012-05-26 14:26:10 +02:00
}
2013-05-13 18:49:17 +02:00
}