MuseScore/libmscore/cmd.cpp

3030 lines
117 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 "musescoreCore.h"
2012-05-26 14:26:10 +02:00
#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"
#include "rehearsalmark.h"
2012-05-26 14:26:10 +02:00
2013-05-13 18:49:17 +02:00
namespace Ms {
2016-03-02 13:20:19 +01:00
//---------------------------------------------------------
// reset
//---------------------------------------------------------
void CmdState::reset()
{
layoutFlags = LayoutFlag::NO_FLAGS;
_updateMode = UpdateMode::DoNothing;
_startTick = -1;
_endTick = -1;
}
//---------------------------------------------------------
// setTick
//---------------------------------------------------------
void CmdState::setTick(int t)
{
if (_updateMode == UpdateMode::LayoutAll)
return;
2016-03-02 13:20:19 +01:00
if (_startTick == -1 || t < _startTick)
_startTick = t;
if (_endTick == -1 || t > _endTick)
_endTick = t;
2016-03-18 09:29:16 +01:00
setUpdateMode(UpdateMode::LayoutRange);
2016-03-02 13:20:19 +01:00
}
//---------------------------------------------------------
// setUpdateMode
//---------------------------------------------------------
void CmdState::setUpdateMode(UpdateMode m)
{
if (int(m) > int(_updateMode))
_updateMode = m;
}
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()");
2016-03-02 13:20:19 +01:00
2016-03-18 09:29:16 +01:00
cmdState().reset();
2012-05-26 14:26:10 +02:00
// Start collecting low-level undo operations for a
// user-visible undo action.
2016-03-10 10:41:31 +01:00
if (undoStack()->active()) {
2012-05-26 14:26:10 +02:00
qDebug("Score::startCmd(): cmd already active");
return;
}
2016-03-10 10:41:31 +01:00
undoStack()->beginMacro();
2012-05-26 14:26:10 +02:00
undo(new SaveState(this));
}
2016-06-03 10:17:06 +02:00
//---------------------------------------------------------
// undoRedo
//---------------------------------------------------------
void Score::undoRedo(bool undo)
{
cmdState().reset();
if (undo)
undoStack()->undo();
else
undoStack()->redo();
update();
updateSelection();
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// endCmd
/// End a GUI command by (if \a undo) ending a user-visble undo
/// and (always) updating the redraw area.
//---------------------------------------------------------
void Score::endCmd(bool rollback)
2012-05-26 14:26:10 +02:00
{
2016-03-10 10:41:31 +01:00
if (!undoStack()->active()) {
2016-03-02 13:20:19 +01:00
qDebug("Score::endCmd(): no cmd active");
2016-03-18 09:29:16 +01:00
update();
2012-05-26 14:26:10 +02:00
return;
}
if (rollback)
2016-03-10 10:41:31 +01:00
undoStack()->current()->unwind();
2016-03-18 09:29:16 +01:00
update();
2012-05-26 14:26:10 +02:00
2014-07-31 16:30:04 +02:00
if (MScore::debugMode)
2016-03-10 10:41:31 +01:00
qDebug("===endCmd() %d", undoStack()->current()->childCount());
bool noUndo = undoStack()->current()->childCount() <= 1; // nothing to undo?
undoStack()->endMacro(noUndo);
2015-01-30 17:03:51 +01:00
if (dirty()) {
2016-03-10 10:41:31 +01:00
masterScore()->_playlistDirty = true; // TODO: flag individual operations
masterScore()->_autosaveDirty = true;
2015-01-30 17:03:51 +01:00
}
2016-05-18 16:32:53 +02:00
MuseScoreCore::mscoreCore->endCmd();
cmdState().reset();
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// update
// layout & update
//---------------------------------------------------------
void Score::update()
{
2016-03-18 09:29:16 +01:00
CmdState& cs = cmdState();
if (cs.layoutAll()) {
for (Score* s : scoreList())
s->doLayout();
cs._setUpdateMode(UpdateMode::UpdateAll);
2016-03-18 09:29:16 +01:00
}
else if (cs.layoutRange()) {
for (Score* s : scoreList())
s->doLayoutRange(cs.startTick(), cs.endTick());
cs._setUpdateMode(UpdateMode::UpdateAll);
2016-03-18 09:29:16 +01:00
}
2016-03-18 14:35:15 +01:00
if (cs.updateAll()) {
2016-03-18 09:29:16 +01:00
for (Score* s : scoreList()) {
for (MuseScoreView* v : s->viewer)
v->updateAll();
2013-06-27 11:02:42 +02:00
}
cs._setUpdateMode(UpdateMode::DoNothing);
2012-05-26 14:26:10 +02:00
}
2016-03-18 09:29:16 +01:00
else if (cs.updateRange()) {
// updateRange updates only current score
2012-05-26 14:26:10 +02:00
qreal d = spatium() * .5;
2016-03-18 09:29:16 +01:00
_updateState.refresh.adjust(-d, -d, 2 * d, 2 * d);
2014-05-31 12:03:21 +02:00
for (MuseScoreView* v : viewer)
2016-03-18 09:29:16 +01:00
v->dataChanged(_updateState.refresh);
_updateState.refresh = QRectF();
}
const InputState& is = inputState();
if (is.noteEntryMode() && is.segment()) {
setPlayPos(is.segment()->tick());
2012-05-26 14:26:10 +02:00
}
2016-03-18 09:29:16 +01:00
if (_playlistDirty) {
emit playlistChanged();
_playlistDirty = false;
}
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// 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);
2015-03-06 13:38:22 +01:00
// ignore if we do not have a measure
if (mb == 0 || mb->type() != Element::Type::MEASURE) {
2012-05-26 14:26:10 +02:00
qDebug("cmdAddSpanner: cannot put object here");
delete spanner;
return;
}
2014-08-18 11:33:17 +02:00
// all spanners live in voice 0 (except slurs/ties)
2012-05-26 14:26:10 +02:00
int track = staffIdx == -1 ? -1 : staffIdx * VOICES;
2014-08-18 11:33:17 +02:00
2012-05-26 14:26:10 +02:00
spanner->setTrack(track);
2014-04-23 14:50:31 +02:00
spanner->setTrack2(track);
2012-05-26 14:26:10 +02:00
2014-05-26 20:48:27 +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->measure()->tick() + segment->measure()->ticks(), lastTick);
2013-07-16 09:03:47 +02:00
spanner->setTick2(tick2);
2012-05-26 14:26:10 +02:00
}
Fix #29986 : Ottavas in TABs - Solution A) Ottavas are not used in TAB staves, as they do not really make sense in TAB's. Yet, currently ottavas: - can be dropped on TAB staves - ottavas added to standard staves are duplicated in TAB staves linked to them Fix: - Ottavas cannot be dropped on TAB staves any longer. Attempting to drop an Ottava on a TAB, results in the action being ignored. - If an `Ottava` is added to a standard staff linked to TAB staves, the standard staff operation is carried over exactly as before, but the Ottava element is not replicated in the TAB linked staff; instead, the actual pitches of corresponding notes in the TAB staff are shifted by the pitchOffset of the Ottava. - If the `Ottava` span is later edited (in the standard staff, of course), the pitches of the corresponding TAB staff notes are updated accordingly. Regarding adding ottavas directly to TAB staves, either linked, there is no difference between Solution A) and Solution B): with both, ottavas **cannot** be added to TAB staves. When TABs are linked to standard staves and ottavas are added to the standard staff, the differences between Solution A) and B) are: - A) does not create **any** `Ottava` element in TABs; B) creates hidden `Ottava` elements in the linked TAB. - A) modifies the TAB note pitches to render the ottava effect; B) does not change the stored pitches, only the fretting. I am not very fond of the hidden `Ottava` trick of Solution B), but that solution seems more in tune with the current code and easier to understand (and maintain). This solution A) seems to me less tricky, but probably less clear to the unaware developer, as each modification of the 'master' `Ottava` has to be cloned into the linked TAB, at the proper place and time with respect to undo/redo machinery. My 'instinctive' preference is for Solution B), but advice is welcome!
2015-02-25 23:59:41 +01:00
else { // Anchor::MEASURE, Anchor::CHORD, Anchor::NOTE
Measure* m = toMeasure(mb);
2012-05-26 14:26:10 +02:00
QRectF b(m->canvasBoundingRect());
2014-07-23 15:41:06 +02:00
if (pos.x() >= (b.x() + b.width() * .5) && m != lastMeasureMM())
2012-05-26 14:26:10 +02:00
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, SelectType::SINGLE, 0);
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// cmdAddSpanner
// used when applying a spanner to a selection
//---------------------------------------------------------
void Score::cmdAddSpanner(Spanner* spanner, int staffIdx, Segment* startSegment, Segment* endSegment)
{
int track = staffIdx * VOICES;
spanner->setTrack(track);
spanner->setTrack2(track);
spanner->setTick(startSegment->tick());
int tick2;
if (!endSegment)
tick2 = lastSegment()->tick();
else if (endSegment == startSegment)
tick2 = startSegment->measure()->last()->tick();
else
tick2 = endSegment->tick();
spanner->setTick2(tick2);
2016-07-11 09:48:40 +02:00
TextLine* tl = dynamic_cast<TextLine*>(spanner);
if (tl) {
TextStyleType st;
Text* t;
// begin
t = tl->beginTextElement();
if (t) {
st = t->textStyleType();
if (st >= TextStyleType::DEFAULT)
t->textStyle().restyle(MScore::baseStyle()->textStyle(st), textStyle(st));
}
// continue
t = tl->continueTextElement();
if (t) {
st = t->textStyleType();
if (st >= TextStyleType::DEFAULT)
t->textStyle().restyle(MScore::baseStyle()->textStyle(st), textStyle(st));
}
// end
t = tl->endTextElement();
if (t) {
st = t->textStyleType();
if (st >= TextStyleType::DEFAULT)
t->textStyle().restyle(MScore::baseStyle()->textStyle(st), textStyle(st));
}
}
2015-10-28 19:43:55 +01:00
undoAddElement(spanner);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// expandVoice
// fills gaps in voice with rests,
// from previous cr (or beginning of measure) to next cr (or end of measure)
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
void Score::expandVoice(Segment* s, int track)
{
2015-08-27 11:33:23 +02:00
if (!s) {
qDebug("expand voice: no segment");
return;
}
2016-08-02 17:00:49 +02:00
if (s->element(track))
2012-05-26 14:26:10 +02:00
return;
// find previous segment with cr in this track
2012-05-26 14:26:10 +02:00
Segment* ps;
for (ps = s; ps; ps = ps->prev(Segment::Type::ChordRest)) {
2012-05-26 14:26:10 +02:00
if (ps->element(track))
break;
}
if (ps) {
ChordRest* cr = toChordRest(ps->element(track));
2012-05-26 14:26:10 +02:00
int tick = cr->tick() + cr->actualTicks();
if (tick > s->tick()) {
// previous cr extends past current segment
2012-05-26 14:26:10 +02:00
qDebug("expandVoice: cannot insert element here");
return;
}
2016-08-02 17:00:49 +02:00
if (cr->isChord()) {
// previous cr ends on or before current segment
// for chords, move ps to just after cr ends
// so we can fill any gap that might exist
// but don't move ps if previous cr is a rest
// this will be combined with any new rests needed to fill up to s->tick() below
ps = ps->measure()->undoGetSegment(Segment::Type::ChordRest, tick);
}
2012-05-26 14:26:10 +02:00
}
//
// fill up to s->tick() with rests
2012-05-26 14:26:10 +02:00
//
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 in measure
2012-05-26 14:26:10 +02:00
//
Segment* ns;
for (ns = s->next(Segment::Type::ChordRest); ns; ns = ns->next(Segment::Type::ChordRest)) {
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::DurationType::V_MEASURE), 0);
2012-05-26 14:26:10 +02:00
else
setRest(s->tick(), track, Fraction::fromTicks(ticks), false, 0);
}
void Score::expandVoice()
{
Segment* s = _is.segment();
int track = _is.track();
expandVoice(s, track);
}
//---------------------------------------------------------
// cmdAddInterval
//---------------------------------------------------------
2016-02-06 22:03:43 +01:00
void Score::cmdAddInterval(int val, const std::vector<Note*>& nl)
2012-05-26 14:26:10 +02:00
{
startCmd();
for (Note* on : nl) {
Note* note = new Note(this);
2012-05-26 14:26:10 +02:00
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);
2014-06-20 17:07:22 +02:00
Key key = estaff->key(tick);
npitch = line2pitch(line, clef, key);
2014-04-23 11:08:51 +02:00
int ntpc = pitch2tpc(npitch, key, Prefer::NEAREST);
Interval v = on->part()->instrument(tick)->transpose();
2014-04-23 11:08:51 +02:00
if (v.isZero())
ntpc1 = ntpc2 = ntpc;
else {
2014-05-26 15:31:36 +02:00
if (styleB(StyleIdx::concertPitch)) {
2014-04-23 11:08:51 +02:00
v.flip();
ntpc1 = ntpc;
ntpc2 = Ms::transposeTpc(ntpc, v, true);
2014-04-23 11:08:51 +02:00
}
else {
npitch += v.chromatic;
ntpc2 = ntpc;
ntpc1 = Ms::transposeTpc(ntpc, v, true);
2014-04-23 11:08:51 +02:00
}
}
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
}
if (npitch < 0 || npitch > 127) {
delete note;
return;
}
2014-04-23 11:08:51 +02:00
note->setPitch(npitch, ntpc1, ntpc2);
2012-05-26 14:26:10 +02:00
undoAddElement(note);
2016-03-18 09:29:16 +01:00
setPlayNote(true);
2012-05-26 14:26:10 +02:00
select(note, SelectType::SINGLE, 0);
2012-05-26 14:26:10 +02:00
}
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 ch is the chord of the normal note
2012-05-26 14:26:10 +02:00
/// \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)
//---------------------------------------------------------
2014-05-06 17:36:20 +02:00
void Score::setGraceNote(Chord* ch, int pitch, NoteType type, int len)
2012-05-26 14:26:10 +02:00
{
Note* note = new Note(this);
Chord* chord = new Chord(this);
// alow grace notes to be added to other grace notes
// by really adding to parent chord
if (ch->noteType() != NoteType::NORMAL)
ch = toChord(ch->parent());
2012-05-26 14:26:10 +02:00
chord->setTrack(ch->track());
2013-06-12 14:23:57 +02:00
chord->setParent(ch);
chord->add(note);
note->setPitch(pitch);
// find corresponding note within chord and use its tpc information
for (Note* n : ch->notes()) {
if (n->pitch() == pitch) {
note->setTpc1(n->tpc1());
note->setTpc2(n->tpc2());
break;
}
}
// note with same pitch not found, derive tpc from pitch / key
if (!tpcIsValid(note->tpc1()) || !tpcIsValid(note->tpc2()))
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);
2014-05-26 15:31:36 +02:00
chord->setMag(ch->staff()->mag() * styleD(StyleIdx::graceNoteMag));
2012-05-26 14:26:10 +02:00
2013-06-12 14:23:57 +02:00
undoAddElement(chord);
select(note, SelectType::SINGLE, 0);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// setNoteRest
// pitch == -1 -> set rest
// return segment of last created note/rest
//---------------------------------------------------------
Segment* Score::setNoteRest(Segment* segment, int track, NoteVal nval, Fraction sd,
2016-03-02 13:20:19 +01:00
Direction stemDirection)
2012-05-26 14:26:10 +02:00
{
Q_ASSERT(segment->segmentType() == Segment::Type::ChordRest);
2012-05-26 14:26:10 +02:00
int tick = segment->tick();
Element* nr = 0;
Tie* tie = 0;
ChordRest* cr = toChordRest(segment->element(track));
2012-05-26 14:26:10 +02:00
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;
}
measure = segment->measure();
2016-02-06 22:03:43 +01:00
std::vector<TDuration> dl = toDurationList(dd, true);
2012-05-26 14:26:10 +02:00
int n = dl.size();
for (int i = 0; i < n; ++i) {
2016-02-06 22:03:43 +01:00
const TDuration& d = dl[i];
2012-05-26 14:26:10 +02:00
ChordRest* ncr;
Note* note = 0;
Tie* addTie = 0;
2012-05-26 14:26:10 +02:00
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);
addTie = tie;
2012-05-26 14:26:10 +02:00
}
Chord* chord = new Chord(this);
chord->setTrack(track);
chord->setDurationType(d);
chord->setDuration(d.fraction());
chord->setStemDirection(stemDirection);
chord->add(note);
note->setNval(nval, tick);
2012-05-26 14:26:10 +02:00
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);
if (addTie)
undoAddElement(addTie);
2016-03-18 09:29:16 +01:00
setPlayNote(true);
2012-05-26 14:26:10 +02:00
segment = ncr->segment();
tick += ncr->actualTicks();
}
sd -= dd;
if (sd.isZero())
break;
Segment* nseg = tick2segment(tick, false, Segment::Type::ChordRest);
2012-05-26 14:26:10 +02:00
if (nseg == 0) {
qDebug("reached end of score");
break;
}
segment = nseg;
cr = toChordRest(segment->element(track));
2012-05-26 14:26:10 +02:00
if (cr == 0) {
if (track % VOICES)
cr = addRest(segment, track, TDuration(TDuration::DurationType::V_MEASURE), 0);
2012-05-26 14:26:10 +02:00
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();
2014-07-14 13:59:54 +02:00
if (nr) {
if (_is.slur() && nr->type() == Element::Type::NOTE) {
//
// extend slur
//
Chord* chord = toNote(nr)->chord();
2014-08-13 15:42:40 +02:00
_is.slur()->undoChangeProperty(P_ID::SPANNER_TICKS, chord->tick() - _is.slur()->tick());
for (ScoreElement* e : _is.slur()->linkList()) {
2014-07-29 14:08:52 +02:00
Slur* slur = static_cast<Slur*>(e);
for (ScoreElement* ee : chord->linkList()) {
Element* e = static_cast<Element*>(ee);
2014-07-29 14:08:52 +02:00
if (e->score() == slur->score() && e->track() == slur->track2()) {
slur->score()->undo(new ChangeSpannerElements(slur, slur->startElement(), e));
break;
}
}
2014-07-14 13:59:54 +02:00
}
}
select(nr, SelectType::SINGLE, 0);
2014-07-14 13:59:54 +02:00
}
2012-05-26 14:26:10 +02:00
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();
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();
for (Segment* seg = firstSegment; seg; seg = seg->next(Segment::Type::ChordRest)) {
2012-05-26 14:26:10 +02:00
//
// voices != 0 may have gaps:
//
ChordRest* cr = toChordRest(seg->element(track));
2012-05-26 14:26:10 +02:00
if (!cr) {
if (seg->tick() < nextTick)
continue;
Segment* seg1 = seg->next(Segment::Type::ChordRest);
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::Type::CHORD) {
Chord* c = toChord(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 (ltuplet != tuplet) {
2012-05-26 14:26:10 +02:00
//
// Current location points to the start of a (nested)tuplet.
// We have to remove the complete tuplet.
// get top level tuplet
while (ltuplet->tuplet())
ltuplet = ltuplet->tuplet();
// get last segment of tuplet, drilling down to leaf nodes as necessary
2012-05-26 14:26:10 +02:00
Tuplet* t = ltuplet;
while (t->elements().back()->isTuplet())
t = toTuplet(t->elements().back());
seg = toChordRest(t->elements().back())->segment();
2012-05-26 14:26:10 +02:00
// now delete the full tuplet
2012-05-26 14:26:10 +02:00
td = ltuplet->duration();
cmdDeleteTuplet(ltuplet, false);
tuplet = 0;
}
else {
if (seg != firstSegment || !keepChord)
2013-05-10 10:51:27 +02:00
undoRemoveElement(cr);
// even if there was a tuplet, we didn't remove it
ltuplet = 0;
2012-05-26 14:26:10 +02:00
}
nextTick += td.ticks();
if (sd < td) {
//
// we removed too much
//
akkumulated = _sd;
Fraction rd = td - sd;
2016-02-06 22:03:43 +01:00
std::vector<TDuration> dList = toDurationList(rd, false);
if (dList.empty())
2012-05-26 14:26:10 +02:00
return akkumulated;
2014-07-28 15:54:47 +02:00
Fraction f = sd / cr->staff()->timeStretch(cr->tick());
2012-05-26 14:26:10 +02:00
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("reinstate at %d, %d", tick, d.ticks());
if (ltuplet) {
// take care not to recreate tuplet we just deleted
Rest* r = setRest(tick, track, d.fraction(), false, 0, false);
tick += r->actualTicks();
}
else {
tick += addClone(cr, tick, d)->actualTicks();
}
2012-05-26 14:26:10 +02:00
}
}
else {
for (int i = dList.size() - 1; i >= 0; --i) {
if (ltuplet) {
// take care not to recreate tuplet we just deleted
Rest* r = setRest(tick, track, dList[i].fraction(), false, 0, false);
tick += r->actualTicks();
}
else {
tick += addClone(cr, tick, dList[i])->actualTicks();
}
}
2012-05-26 14:26:10 +02:00
}
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
2014-07-31 08:37:58 +02:00
// make time gap for each voice
// starting at tick+voiceOffset[voice] by removing/shortening
2012-05-26 14:26:10 +02:00
// chord/rest
// - cr is top level (not part of a tuplet)
// - do not stop at measure end
//---------------------------------------------------------
2014-07-31 08:37:58 +02:00
bool Score::makeGap1(int baseTick, int staffIdx, Fraction len, int voiceOffset[VOICES])
2012-05-26 14:26:10 +02:00
{
2014-07-31 08:37:58 +02:00
Segment* seg = tick2segment(baseTick, true, Segment::Type::ChordRest);
2012-05-26 14:26:10 +02:00
if (!seg) {
qDebug("no segment to paste at tick %d", baseTick);
2012-05-26 14:26:10 +02:00
return false;
}
2014-07-27 11:15:46 +02:00
int strack = staffIdx * VOICES;
2014-07-31 08:37:58 +02:00
for (int track = strack; track < strack + VOICES; track++) {
if (voiceOffset[track-strack] == -1)
continue;
2014-07-31 08:37:58 +02:00
int tick = baseTick + voiceOffset[track-strack];
Measure* m = tick2measure(tick);
seg = m->undoGetSegment(Segment::Type::ChordRest, tick);
Fraction newLen = len - Fraction::fromTicks(voiceOffset[track-strack]);
Q_ASSERT(newLen.numerator() != 0);
bool result = makeGapVoice(seg, track, newLen, tick);
if (track == strack && !result) // makeGap failed for first voice
2014-07-27 11:15:46 +02:00
return false;
}
return true;
}
bool Score::makeGapVoice(Segment* seg, int track, Fraction len, int tick)
{
ChordRest* cr = 0;
cr = toChordRest(seg->element(track));
2012-05-26 14:26:10 +02:00
if (!cr) {
2013-06-12 14:23:57 +02:00
// check if we are in the middle of a chord/rest
2014-11-10 13:41:25 +01:00
Segment* seg1 = seg->prev(Segment::Type::ChordRest);
2013-06-12 14:23:57 +02:00
for (;;) {
if (seg1 == 0) {
qDebug("no segment before tick %d", tick);
2014-07-31 08:37:58 +02:00
// this happens only for voices other than voice 1
expandVoice(seg, track);
return makeGapVoice(seg,track,len,tick);
2012-05-26 14:26:10 +02:00
}
2013-06-12 14:23:57 +02:00
if (seg1->element(track))
break;
seg1 = seg1->prev(Segment::Type::ChordRest);
2012-05-26 14:26:10 +02:00
}
ChordRest* cr1 = toChordRest(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());
2016-02-06 22:03:43 +01:00
std::vector<TDuration> dList = toDurationList(dstF, true);
int n = dList.size();
undoChangeChordRestLen(cr1, TDuration(dList[0]));
if (n > 1) {
int crtick = cr1->tick() + cr1->actualTicks();
Measure* measure = tick2measure(crtick);
if (cr1->type() == Element::Type::CHORD) {
// split Chord
Chord* c = toChord(cr1);
for (int i = 1; i < n; ++i) {
TDuration d = dList[i];
Chord* c2 = addChord(crtick, d, c, true, c->tuplet());
c = c2;
seg1 = c->segment();
crtick += c->actualTicks();
}
}
else {
// split Rest
Rest* r = toRest(cr1);
for (int i = 1; i < n; ++i) {
TDuration d = dList[i];
Rest* r2 = toRest(r->clone());
r2->setDuration(d.fraction());
r2->setDurationType(d);
undoAddCR(r2, measure, crtick);
seg1 = r2->segment();
crtick += r2->actualTicks();
}
}
}
2013-06-20 15:25:11 +02:00
setRest(tick, track, srcF - dstF, true, 0);
2013-06-12 14:23:57 +02:00
for (;;) {
seg1 = seg1->next1(Segment::Type::ChordRest);
2013-06-20 15:25:11 +02:00
if (seg1 == 0) {
qDebug("no segment");
2013-06-12 14:23:57 +02:00
return false;
2012-05-26 14:26:10 +02:00
}
2013-06-20 15:25:11 +02:00
if (seg1->element(track)) {
cr = toChordRest(seg1->element(track));
2013-06-12 14:23:57 +02:00
break;
2012-05-26 14:26:10 +02:00
}
}
}
for (;;) {
if (!cr) {
qDebug("cannot make gap");
2012-05-26 14:26:10 +02:00
return false;
}
Fraction l = makeGap(cr->segment(), cr->track(), len, 0);
if (l.isZero()) {
qDebug("returns zero gap");
2012-05-26 14:26:10 +02:00
return false;
}
len -= l;
if (len.isZero())
break;
// go to next cr
Measure* m = cr->measure()->nextMeasure();
if (m == 0) {
qDebug("EOS reached");
insertMeasure(Element::Type::MEASURE, 0, false);
2012-05-26 14:26:10 +02:00
m = cr->measure()->nextMeasure();
if (m == 0) {
qDebug("===EOS reached");
return true;
}
}
2014-07-31 08:37:58 +02:00
// first segment in measure was removed, have to recreate it
Segment* s = m->undoGetSegment(Segment::Type::ChordRest, m->tick());
2012-05-26 14:26:10 +02:00
int track = cr->track();
cr = toChordRest(s->element(track));
2012-05-26 14:26:10 +02:00
if (cr == 0) {
addRest(s, track, TDuration(TDuration::DurationType::V_MEASURE), 0);
cr = toChordRest(s->element(track));
2012-05-26 14:26:10 +02:00
}
}
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::Type::ChordRest);
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::DurationType::V_MEASURE)
2012-05-26 14:26:10 +02:00
dstF = cr->measure()->stretchedLen(cr->staff());
else
dstF = d.fraction();
if (srcF == dstF)
return;
2014-06-19 12:54:13 +02:00
//keep selected element if any
Element* selElement = selection().isSingle() ? getSelectedElement() : 0;
int track = cr->track();
2012-05-26 14:26:10 +02:00
Tuplet* tuplet = cr->tuplet();
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();
if (cr->type() == Element::Type::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 = toChord(cr);
if (c->tremolo()) {
Tremolo* tremolo = c->tremolo();
if (tremolo->twoNotes())
undoRemoveElement(tremolo);
}
2014-06-19 12:54:13 +02:00
foreach (Note* n, c->notes()) {
2012-05-26 14:26:10 +02:00
if (n->tieFor())
undoRemoveElement(n->tieFor());
}
}
undoChangeChordRestLen(cr, TDuration(dstF));
setRest(cr->tick() + cr->actualTicks(), track, srcF - dstF, false, tuplet);
2014-06-19 12:54:13 +02:00
if (selElement)
select(selElement, SelectType::SINGLE, 0);
2012-05-26 14:26:10 +02:00
return;
}
//
// make longer
//
// split required len into Measures
QList<Fraction> flist = splitGapToMeasureBoundaries(cr, dstF);
2016-02-06 22:03:43 +01:00
if (flist.empty())
2012-05-26 14:26:10 +02:00
return;
2013-05-10 10:51:27 +02:00
deselectAll();
2012-05-26 14:26:10 +02:00
int tick = cr->tick();
Fraction f = dstF;
ChordRest* cr1 = cr;
Chord* oc = 0;
bool first = true;
for (Fraction f2 : flist) {
2012-05-26 14:26:10 +02:00
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
if (cr->type() == Element::Type::REST) {
2012-05-26 14:26:10 +02:00
Fraction timeStretch = cr1->staff()->timeStretch(cr1->tick());
Rest* r = toRest(cr);
if (first) {
2016-02-06 22:03:43 +01:00
std::vector<TDuration> dList = toDurationList(f2, true);
undoChangeChordRestLen(cr, dList[0]);
int tick2 = cr->tick();
2016-02-06 22:03:43 +01:00
for (unsigned i = 1; i < dList.size(); ++i) {
tick2 += dList[i-1].ticks();
TDuration d = dList[i];
setRest(tick2, track, d.fraction() * timeStretch, (d.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, SelectType::SINGLE, 0);
2012-05-26 14:26:10 +02:00
first = false;
}
tick += f2.ticks() * timeStretch.numerator() / timeStretch.denominator();
}
else {
2016-02-06 22:03:43 +01:00
std::vector<TDuration> dList = toDurationList(f2, true);
2012-05-26 14:26:10 +02:00
Measure* measure = tick2measure(tick);
int etick = measure->tick();
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 = toChord(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) {
2014-06-19 12:54:13 +02:00
if (!selElement)
select(oc, SelectType::SINGLE, 0);
else
2014-06-19 12:54:13 +02:00
select(selElement, SelectType::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 = toChord(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) {
2014-11-22 13:05:23 +01:00
// select(oc, SelectType::SINGLE, 0);
if (selElement)
select(selElement, SelectType::SINGLE, 0);
2012-05-26 14:26:10 +02:00
first = false;
}
tick += oc->actualTicks();
}
}
}
Measure* m = cr1->measure();
Measure* m1 = m->nextMeasure();
if (m1 == 0)
break;
Segment* s = m1->first(Segment::Type::ChordRest);
2012-05-26 14:26:10 +02:00
expandVoice(s, track);
cr1 = toChordRest(s->element(track));
2012-05-26 14:26:10 +02:00
}
connectTies();
}
//---------------------------------------------------------
// upDownChromatic
//---------------------------------------------------------
2014-06-20 17:07:22 +02:00
static void upDownChromatic(bool up, int pitch, Note* n, Key key, int tpc1, int tpc2, int& newPitch, int& newTpc1, int& newTpc2)
{
if (up && pitch < 127) {
newPitch = pitch + 1;
if (n->concertPitch()) {
2014-06-20 17:07:22 +02:00
if (tpc1 > Tpc::TPC_A + int(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 {
2014-06-20 17:07:22 +02:00
if (tpc2 > Tpc::TPC_A + int(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()) {
2014-06-20 17:07:22 +02:00
if (tpc1 > Tpc::TPC_C + int(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 {
2014-06-20 17:07:22 +02:00
if (tpc2 > Tpc::TPC_C + int(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)
{
2014-05-26 12:10:59 +02:00
QList<Note*> el = selection().uniqueNotes();
2012-05-26 14:26:10 +02:00
2016-03-02 13:20:19 +01:00
for (Note* oNote : el) {
2014-03-26 11:02:23 +01:00
int tick = oNote->chord()->tick();
Staff* staff = oNote->staff();
Part* part = staff->part();
Key key = staff->key(tick);
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
switch (staff->staffType()->group()) {
case StaffGroup::PERCUSSION:
2012-05-26 14:26:10 +02:00
{
const Drumset* ds = part->instrument()->drumset();
if (ds) {
2014-03-26 11:02:23 +01:00
newPitch = up ? ds->prevPitch(pitch) : ds->nextPitch(pitch);
newTpc1 = pitch2tpc(newPitch, Key::C, Prefer::NEAREST);
newTpc2 = newTpc1;
}
2012-05-26 14:26:10 +02:00
}
break;
case StaffGroup::TAB:
2012-05-26 14:26:10 +02:00
{
const StringData* stringData = part->instrument()->stringData();
2014-05-13 17:15:32 +02:00
switch (mode) {
case UpDownMode::OCTAVE: // move same note to next string, if possible
2012-05-26 14:26:10 +02:00
{
StaffType* stt = 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, staff, tick);
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 UpDownMode::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 UpDownMode::CHROMATIC: // increase / decrease the fret
2012-05-26 14:26:10 +02:00
{ // without changing the string
// compute new fret
if (!stringData->frets()) {
2014-05-26 12:10:59 +02:00
qDebug("upDown tab chromatic: no frets?");
return;
}
2012-05-26 14:26:10 +02:00
fret += (up ? 1 : -1);
if (fret < 0 || fret > stringData->frets()) {
qDebug("upDown tab in-string: out of fret range");
return;
2014-05-26 12:10:59 +02:00
}
// update pitch and tpc's and check it matches stringData
upDownChromatic(up, pitch, oNote, key, tpc1, tpc2, newPitch, newTpc1, newTpc2);
if (newPitch != stringData->getPitch(string, fret, staff, tick) ) {
// oh-oh: something went very wrong!
qDebug("upDown tab in-string: pitch mismatch");
return;
}
2012-05-26 14:26:10 +02:00
// store the fretting change before undoChangePitch() chooses
// a fretting of its own liking!
2014-05-26 18:18:01 +02:00
undoChangeProperty(oNote, P_ID::FRET, fret);
// undoChangeProperty(oNote, P_ID::STRING, string);
2012-05-26 14:26:10 +02:00
}
break;
}
}
break;
case StaffGroup::STANDARD:
2016-03-02 13:20:19 +01:00
switch (mode) {
case UpDownMode::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 UpDownMode::CHROMATIC:
upDownChromatic(up, pitch, oNote, key, tpc1, tpc2, newPitch, newTpc1, newTpc2);
2012-05-26 14:26:10 +02:00
break;
case UpDownMode::DIATONIC:
{
int tpc = oNote->tpc();
if (up) {
2014-06-20 17:07:22 +02:00
if (tpc > Tpc::TPC_A + int(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 {
2014-06-20 17:07:22 +02:00
if (tpc > Tpc::TPC_C + int(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.
2016-03-02 13:20:19 +01:00
auto l = oNote->linkList();
for (ScoreElement* e : l) {
Note* ln = static_cast<Note*>(e);
if (ln->accidental())
undo(new RemoveElement(ln->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
else if (staff->staffType()->group() == StaffGroup::TAB) {
bool refret = false;
if (oNote->string() != string) {
2014-05-26 18:18:01 +02:00
undoChangeProperty(oNote, P_ID::STRING, string);
refret = true;
}
if (oNote->fret() != fret) {
2014-05-26 18:18:01 +02:00
undoChangeProperty(oNote, P_ID::FRET, fret);
refret = true;
}
2014-03-26 11:02:23 +01:00
if (refret) {
const StringData* stringData = part->instrument()->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:
2016-03-18 09:29:16 +01:00
setPlayNote(true);
2012-05-26 14:26:10 +02:00
}
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
}
//---------------------------------------------------------
// addArticulation
/// Add attribute \a attr to all selected notes/rests.
///
/// Called from padToggle() to add note prefix/accent.
//---------------------------------------------------------
void Score::addArticulation(ArticulationType attr)
{
QSet<Chord*> set;
2012-05-26 14:26:10 +02:00
foreach(Element* el, selection().elements()) {
if (el->type() == Element::Type::NOTE || el->type() == Element::Type::CHORD) {
Chord* cr = nullptr;
// apply articulation on a given chord only once
if (el->type() == Element::Type::NOTE) {
cr = toNote(el)->chord();
if (set.contains(cr))
continue;
}
2012-05-26 14:26:10 +02:00
Articulation* na = new Articulation(this);
na->setArticulationType(attr);
if (!addArticulation(el, na))
delete na;
if (cr)
set.insert(cr);
2012-05-26 14:26:10 +02:00
}
}
}
//---------------------------------------------------------
// changeAccidental
/// Change accidental to subtype \a idx for all selected
/// notes.
//---------------------------------------------------------
2015-04-02 10:33:53 +02:00
void Score::changeAccidental(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();
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
//
const StringData* stringData = n->part()->instrument()->stringData();
if (stringData)
stringData->convertPitch(pitch, st, chord->tick(), &string, &fret);
}
}
2014-04-14 10:39:27 +02:00
int tpc1;
int tpc2 = n->transposeTpc(tpc);
2014-05-26 15:31:36 +02:00
if (score->styleB(StyleIdx::concertPitch))
2014-04-14 10:39:27 +02:00
tpc1 = tpc;
else {
tpc1 = tpc2;
tpc2 = tpc;
}
if (!st->isTabStaff()) {
//
// handle ties
//
if (n->tieBack()) {
if (pitch != n->pitch()) {
score->undoRemoveElement(n->tieBack());
if (n->tieFor())
score->undoRemoveElement(n->tieFor());
}
}
else {
Note* nn = n;
while (nn->tieFor()) {
nn = nn->tieFor()->endNote();
2014-05-05 14:02:26 +02:00
score->undo(new ChangePitch(nn, pitch, tpc1, tpc2));
}
}
}
score->undoChangePitch(n, pitch, tpc1, tpc2);
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// changeAccidental
/// Change accidental to subtype \accidental for
/// note \a note.
//---------------------------------------------------------
2015-04-02 10:33:53 +02:00
void Score::changeAccidental(Note* note, AccidentalType accidental)
2012-05-26 14:26:10 +02:00
{
Chord* chord = note->chord();
if (!chord)
return;
2012-05-26 14:26:10 +02:00
Segment* segment = chord->segment();
if (!segment)
return;
2012-05-26 14:26:10 +02:00
Measure* measure = segment->measure();
if (!measure)
return;
int tick = segment->tick();
Staff* estaff = staff(chord->staffIdx() + chord->staffMove());
if (!estaff)
return;
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);
2015-04-02 10:33:53 +02:00
AccidentalVal acc = (accidental == AccidentalType::NONE) ? acc2 : Accidental::subtype2value(accidental);
2012-05-26 14:26:10 +02:00
2014-06-20 17:07:22 +02:00
int pitch = line2pitch(note->line(), clef, Key::C) + int(acc);
2014-04-14 10:39:27 +02:00
if (!note->concertPitch())
pitch += note->transposition();
2014-05-05 14:02:26 +02:00
2014-04-14 10:39:27 +02:00
int tpc = step2tpc(step, acc);
bool forceRemove = false;
bool forceAdd = false;
// delete accidental
// both for this note and for any linked notes
2015-04-02 10:33:53 +02:00
if (accidental == AccidentalType::NONE)
forceRemove = true;
// precautionary or microtonal accidental
// either way, we display it unconditionally
// both for this note and for any linked notes
else if (acc == acc2 || pitch == note->pitch() || accidental > AccidentalType::NATURAL)
forceAdd = true;
2012-05-26 14:26:10 +02:00
for (ScoreElement* se : note->linkList()) {
Note* ln = static_cast<Note*>(se);
if (ln->concertPitch() != note->concertPitch())
continue;
Score* lns = ln->score();
Accidental* a = ln->accidental();
if (forceRemove) {
if (a)
lns->undoRemoveElement(a);
}
else if (forceAdd) {
if (a)
undoRemoveElement(a);
Accidental* a = new Accidental(lns);
a->setParent(ln);
a->setAccidentalType(accidental);
2015-04-02 10:33:53 +02:00
a->setRole(AccidentalRole::USER);
lns->undoAddElement(a);
}
changeAccidental2(ln, 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;
if (el->type() == Element::Type::NOTE)
cr = toNote(el)->chord();
else if (el->isRest() || el->isChord() || el->isRepeatMeasure())
cr = toChordRest(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);
a->setTrack(cr->track()); // make sure it propagates between score and parts
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
2015-05-19 02:12:37 +02:00
if (!s1)
m1 = firstMeasureMM();
2012-05-26 14:26:10 +02:00
else
m1 = s1->measure();
2015-05-19 02:12:37 +02:00
if (!s2)
m2 = lastMeasureMM();
2012-05-26 14:26:10 +02:00
else
m2 = s2->measure();
2015-05-19 02:12:37 +02:00
if (!m1 || !m2) // should not happen!
2012-05-26 14:26:10 +02:00
return;
2015-05-19 02:12:37 +02:00
for (Measure* m = m1; m; m = m->nextMeasureMM()) {
2015-06-17 09:21:09 +02:00
m->undoChangeProperty(P_ID::USER_STRETCH, 1.0);
2012-05-26 14:26:10 +02:00
if (m == m2)
break;
}
}
//---------------------------------------------------------
// moveUp
//---------------------------------------------------------
void Score::moveUp(ChordRest* cr)
2012-05-26 14:26:10 +02:00
{
Staff* staff = cr->staff();
Part* part = staff->part();
int rstaff = staff->rstaff();
int staffMove = cr->staffMove();
2012-05-26 14:26:10 +02:00
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() != StaffGroup::STANDARD ||
staves->at(rstaff+staffMove-1)->staffType()->group() != StaffGroup::STANDARD) {
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(cr, staffMove - 1));
}
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// moveDown
//---------------------------------------------------------
void Score::moveDown(ChordRest* cr)
2012-05-26 14:26:10 +02:00
{
Staff* staff = cr->staff();
2012-05-26 14:26:10 +02:00
Part* part = staff->part();
int rstaff = staff->rstaff();
int staffMove = cr->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() != StaffGroup::STANDARD ||
staves->at(staffMove+rstaff+1)->staffType()->group() != StaffGroup::STANDARD) {
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(cr, staffMove + 1));
}
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// cmdAddStretch
//---------------------------------------------------------
void Score::cmdAddStretch(qreal val)
{
2014-05-24 12:53:50 +02:00
if (!selection().isRange())
2012-05-26 14:26:10 +02:00
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;
if (stretch < 0)
stretch = 0;
2015-06-17 09:21:09 +02:00
m->undoChangeProperty(P_ID::USER_STRETCH, stretch);
2012-05-26 14:26:10 +02:00
}
}
//---------------------------------------------------------
// cmdResetBeamMode
//---------------------------------------------------------
void Score::cmdResetBeamMode()
{
bool noSelection = selection().isNone();
if (noSelection)
cmdSelectAll();
else if (!selection().isRange()) {
2012-05-26 14:26:10 +02:00
qDebug("no system or staff selected");
return;
}
2012-05-26 14:26:10 +02:00
int endTick = selection().tickEnd();
for (Segment* seg = selection().firstChordRestSegment(); seg && seg->tick() < endTick; seg = seg->next1(Segment::Type::ChordRest)) {
for (int track = selection().staffStart() * VOICES; track < selection().staffEnd() * VOICES; ++track) {
ChordRest* cr = toChordRest(seg->element(track));
2012-05-26 14:26:10 +02:00
if (cr == 0)
continue;
if (cr->type() == Element::Type::CHORD) {
if (cr->beamMode() != Beam::Mode::AUTO)
undoChangeProperty(cr, P_ID::BEAM_MODE, int(Beam::Mode::AUTO));
2012-05-26 14:26:10 +02:00
}
else if (cr->type() == Element::Type::REST) {
if (cr->beamMode() != Beam::Mode::NONE)
undoChangeProperty(cr, P_ID::BEAM_MODE, int(Beam::Mode::NONE));
2012-05-26 14:26:10 +02:00
}
}
}
}
//---------------------------------------------------------
// processMidiInput
//---------------------------------------------------------
bool Score::processMidiInput()
{
if (midiInputQueue()->empty())
2012-05-26 14:26:10 +02:00
return false;
if (MScore::debugMode)
qDebug("processMidiInput");
2012-05-26 14:26:10 +02:00
bool cmdActive = false;
while (!midiInputQueue()->empty()) {
MidiInputEvent ev = midiInputQueue()->dequeue();
2012-05-26 14:26:10 +02:00
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) {
if (!styleB(StyleIdx::concertPitch)) {
ev.pitch += p->instrument(selection().tickStart())->transpose().chromatic;
}
MScore::seq->startNote(
p->instrument()->channel(0)->channel,
ev.pitch,
ev.velocity,
0.0);
}
2012-05-26 14:26:10 +02:00
}
else {
if (ev.velocity == 0)
continue;
2012-05-26 14:26:10 +02:00
if (!cmdActive) {
startCmd();
cmdActive = true;
}
2014-06-26 11:41:18 +02:00
NoteVal nval(ev.pitch);
Staff* st = staff(inputState().track() / VOICES);
2014-09-22 14:31:28 +02:00
// if transposing, interpret MIDI pitch as representing desired written pitch
// set pitch based on corresponding sounding pitch
if (!styleB(StyleIdx::concertPitch))
nval.pitch += st->part()->instrument(inputState().tick())->transpose().chromatic;
// let addPitch calculate tpc values from pitch
//Key key = st->key(inputState().tick());
//nval.tpc1 = pitch2tpc(nval.pitch, key, Prefer::NEAREST);
2014-09-22 14:31:28 +02:00
2014-06-26 11:41:18 +02:00
addPitch(nval, ev.chord);
2012-05-26 14:26:10 +02:00
}
}
if (cmdActive) {
endCmd();
//after relayout
2014-06-26 11:41:18 +02:00
Element* e = inputState().cr();
if (e) {
for(MuseScoreView* v : viewer)
v->adjustCanvasPosition(e, false);
2013-01-17 20:42:44 +01:00
}
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 (noteEntryMode()) {
// if selection exists and is grace note, use it
// otherwise use chord/rest at input position
// also use it if we are moving to next chord
// to catch up with the cursor and not move the selection by 2 positions
cr = selection().cr();
if (cr && (cr->isGrace() || cmd == "next-chord" || cmd == "prev-chord"))
;
else
cr = inputState().cr();
}
else if (selection().activeCR())
2012-05-26 14:26:10 +02:00
cr = selection().activeCR();
else
cr = selection().lastChordRest();
// no chord/rest found? look for another type of element
if (cr == 0) {
2016-02-06 22:03:43 +01:00
if (selection().elements().empty())
return 0;
2014-07-30 11:32:46 +02:00
// retrieve last element of section list
Element* el = selection().elements().last();
Element* trg = 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();
// element with no parent (eg, a newly-added line) - no way to find context
if (!el)
return 0;
switch (el->type()) {
case Element::Type::NOTE: // a note is a valid target
trg = el;
cr = toNote(el)->chord();
break;
case Element::Type::CHORD: // a chord or a rest are valid targets
case Element::Type::REST:
trg = el;
cr = toChordRest(trg);
break;
case Element::Type::SEGMENT: { // from segment go to top chordrest in segment
Segment* seg = toSegment(el);
// if segment is not chord/rest or grace, move to next chord/rest or grace segment
if (!seg->isChordRest1()) {
seg = seg->next1(Segment::Type::ChordRest);
if (seg == 0) // if none found, return 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
2015-08-18 23:00:07 +02:00
if (track > -1 && track < size && seg->element(track)) {
trg = seg->element(track);
cr = toChordRest(trg);
break;
}
// if not, get topmost chord/rest
for (int i = 0; i < size; i++)
if (seg->element(i)) {
trg = seg->element(i);
cr = toChordRest(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::Type::CHORD)
trg = toChord(trg)->upNote();
2016-03-18 09:29:16 +01:00
setPlayNote(true);
select(trg, SelectType::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;
2015-08-18 23:00:07 +02:00
Segment* ois = noteEntryMode() ? _is.segment() : nullptr;
Measure* oim = ois ? ois->measure() : nullptr;
2012-05-26 14:26:10 +02:00
if (cmd == "next-chord") {
2015-08-18 23:00:07 +02:00
// note input cursor
2012-05-26 14:26:10 +02:00
if (noteEntryMode())
2013-10-24 12:09:00 +02:00
_is.moveToNextInputPos();
2015-08-18 23:00:07 +02:00
// selection "cursor"
// find next chordrest, which might be a grace note
// this may override note input cursor
2013-06-12 14:23:57 +02:00
el = nextChordRest(cr);
while (el && el->isRest() && toRest(el)->isGap())
el = nextChordRest(toChordRest(el));
2015-08-18 23:00:07 +02:00
if (el && noteEntryMode()) {
// do not use if not in original or new measure (don't skip measures)
Measure* m = toChordRest(el)->measure();
2015-08-18 23:00:07 +02:00
Segment* nis = _is.segment();
Measure* nim = nis ? nis->measure() : nullptr;
if (m != oim && m != nim)
el = cr;
// do not use if new input segment is current cr
// this means input cursor just caught up to current selection
else if (cr && nis == cr->segment())
el = cr;
}
else if (!el)
el = cr;
2012-05-26 14:26:10 +02:00
}
else if (cmd == "prev-chord") {
2015-08-18 23:00:07 +02:00
// note input cursor
2014-08-12 16:13:12 +02:00
if (noteEntryMode() && _is.segment()) {
Measure* m = _is.segment()->measure();
Segment* s = _is.segment()->prev1(Segment::Type::ChordRest);
2015-08-18 23:00:07 +02:00
int track = _is.track();
for (; s; s = s->prev1(Segment::Type::ChordRest)) {
if (s->element(track) || s->measure() != m) {
if (s->element(track)) {
if (s->element(track)->isRest() && toRest(s->element(track))->isGap())
continue;
}
2012-05-26 14:26:10 +02:00
break;
}
2012-05-26 14:26:10 +02:00
}
2013-10-24 12:09:00 +02:00
_is.moveInputPos(s);
2012-05-26 14:26:10 +02:00
}
2015-08-18 23:00:07 +02:00
// selection "cursor"
// find previous chordrest, which might be a grace note
// this may override note input cursor
el = prevChordRest(cr);
while (el && el->isRest() && toRest(el)->isGap())
el = prevChordRest(toChordRest(el));
2015-08-18 23:00:07 +02:00
if (el && noteEntryMode()) {
// do not use if not in original or new measure (don't skip measures)
Measure* m = toChordRest(el)->measure();
2015-08-18 23:00:07 +02:00
Segment* nis = _is.segment();
Measure* nim = nis ? nis->measure() : nullptr;
if (m != oim && m != nim)
el = cr;
// do not use if new input segment is current cr
// this means input cursor just caught up to current selection
else if (cr && nis == cr->segment())
el = cr;
}
else if (!el)
el = 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);
}
2015-08-18 23:00:07 +02:00
2012-05-26 14:26:10 +02:00
if (el) {
if (el->type() == Element::Type::CHORD)
el = toChord(el)->upNote(); // originally downNote
2016-03-18 09:29:16 +01:00
setPlayNote(true);
2014-08-17 00:28:47 +02:00
if (noteEntryMode()) {
// if cursor moved into a gap, selection cannot follow
// only select & play el if it was not already selected (does not normally happen)
ChordRest* cr = _is.cr();
if (cr || !el->selected())
select(el, SelectType::SINGLE, 0);
else
2016-03-18 09:29:16 +01:00
setPlayNote(false);
for (MuseScoreView* view : viewer)
2014-08-17 00:28:47 +02:00
view->moveCursor();
}
else {
select(el, SelectType::SINGLE, 0);
}
2012-05-26 14:26:10 +02:00
}
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, true);
2012-05-26 14:26:10 +02:00
else if (cmd == "select-prev-chord")
el = prevChordRest(cr, true);
2012-05-26 14:26:10 +02:00
else if (cmd == "select-next-measure")
2014-11-25 16:53:43 +01:00
el = nextMeasure(cr, true, true);
2012-05-26 14:26:10 +02:00
else if (cmd == "select-prev-measure")
2014-11-25 16:53:43 +01:00
el = prevMeasure(cr, true);
2012-05-26 14:26:10 +02:00
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 = firstMeasureMM();
2012-05-26 14:26:10 +02:00
if (!measure)
return 0;
el = measure->first()->nextChordRest(cr->track());
}
else if (cmd == "select-end-score") {
Measure* measure = lastMeasureMM();
2012-05-26 14:26:10 +02:00
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, SelectType::RANGE, el->staffIdx());
2012-05-26 14:26:10 +02:00
return el;
}
//---------------------------------------------------------
// cmdMirrorNoteHead
//---------------------------------------------------------
void Score::cmdMirrorNoteHead()
{
const QList<Element*>& el = selection().elements();
foreach(Element* e, el) {
if (e->type() == Element::Type::NOTE) {
Note* note = toNote(e);
if (note->staff() && note->staff()->isTabStaff())
note->score()->undoChangeProperty(e, P_ID::GHOST, !note->ghost());
2012-05-26 14:26:10 +02:00
else {
MScore::DirectionH d = note->userMirror();
if (d == MScore::DirectionH::AUTO)
d = note->chord()->up() ? MScore::DirectionH::RIGHT : MScore::DirectionH::LEFT;
2012-05-26 14:26:10 +02:00
else
d = d == MScore::DirectionH::LEFT ? MScore::DirectionH::RIGHT : MScore::DirectionH::LEFT;
2012-05-26 14:26:10 +02:00
undoChangeUserMirror(note, d);
}
}
}
}
//---------------------------------------------------------
// cmdHalfDuration
//---------------------------------------------------------
void Score::cmdHalfDuration()
{
Element* el = selection().element();
if (el == 0)
return;
if (el->type() == Element::Type::NOTE)
2012-05-26 14:26:10 +02:00
el = el->parent();
if (!el->isChordRest())
return;
ChordRest* cr = toChordRest(el);
2012-05-26 14:26:10 +02:00
TDuration d = _is.duration().shift(1);
if (!d.isValid())
2012-05-26 14:26:10 +02:00
return;
if (cr->type() == Element::Type::CHORD && (toChord(cr)->noteType() != NoteType::NORMAL)) {
2012-05-26 14:26:10 +02:00
//
// handle appoggiatura and acciaccatura
//
undoChangeChordRestLen(cr, d);
2012-05-26 14:26:10 +02:00
}
else
changeCRlen(cr, d);
_is.setDuration(d);
nextInputPos(cr, false);
}
//---------------------------------------------------------
// cmdDoubleDuration
//---------------------------------------------------------
void Score::cmdDoubleDuration()
{
Element* el = selection().element();
if (el == 0)
return;
if (el->type() == Element::Type::NOTE)
2012-05-26 14:26:10 +02:00
el = el->parent();
if (!el->isChordRest())
return;
ChordRest* cr = toChordRest(el);
2012-05-26 14:26:10 +02:00
TDuration d = _is.duration().shift(-1);
if (!d.isValid())
2012-05-26 14:26:10 +02:00
return;
if (cr->type() == Element::Type::CHORD && (toChord(cr)->noteType() != NoteType::NORMAL)) {
2012-05-26 14:26:10 +02:00
//
// handle appoggiatura and acciaccatura
//
undoChangeChordRestLen(cr, d);
2012-05-26 14:26:10 +02:00
}
else
changeCRlen(cr, d);
_is.setDuration(d);
nextInputPos(cr, false);
}
//---------------------------------------------------------
// cmdAddBracket
//---------------------------------------------------------
2014-07-25 17:13:27 +02:00
void Score::cmdAddBracket()
{
for(Element* el : selection().elements()) {
if (el->type() == Element::Type::NOTE) {
Note* n = toNote(el);
n->addBracket();
}
else if (el->type() == Element::Type::ACCIDENTAL) {
Accidental* acc = toAccidental(el);
acc->undoSetHasBracket(true);
}
else if (el->type() == Element::Type::HARMONY) {
Harmony* h = toHarmony(el);
h->setLeftParen(true);
h->setRightParen(true);
h->render();
}
}
}
2014-07-25 17:13:27 +02:00
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// cmdMoveRest
//---------------------------------------------------------
2016-03-02 13:20:19 +01:00
void Score::cmdMoveRest(Rest* rest, Direction dir)
2012-05-26 14:26:10 +02:00
{
QPointF pos(rest->userOff());
2016-03-02 13:20:19 +01:00
if (dir == Direction::UP)
2012-05-26 14:26:10 +02:00
pos.ry() -= spatium();
2016-03-02 13:20:19 +01:00
else if (dir == Direction::DOWN)
2012-05-26 14:26:10 +02:00
pos.ry() += spatium();
2014-05-26 18:18:01 +02:00
undoChangeProperty(rest, P_ID::USER_OFF, pos);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// cmdMoveLyrics
//---------------------------------------------------------
2016-03-02 13:20:19 +01:00
void Score::cmdMoveLyrics(Lyrics* lyrics, Direction dir)
2012-05-26 14:26:10 +02:00
{
2016-02-06 22:03:43 +01:00
ChordRest* cr = lyrics->chordRest();
QVector<Lyrics*>& ll = cr->lyricsList();
int no = lyrics->no();
2016-03-02 13:20:19 +01:00
if (dir == Direction::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") {
2016-03-02 13:20:19 +01:00
if (el && (el->isArticulation() || el->isText()))
2014-07-31 16:30:04 +02:00
el->undoChangeProperty(P_ID::USER_OFF, el->userOff() + QPointF(0.0, -MScore::nudgeStep * el->spatium()));
2016-03-02 13:20:19 +01:00
else if (el && el->isRest())
cmdMoveRest(toRest(el), Direction::UP);
else if (el && el->isLyrics())
cmdMoveLyrics(toLyrics(el), Direction::UP);
2012-05-26 14:26:10 +02:00
else
upDown(true, UpDownMode::CHROMATIC);
2012-05-26 14:26:10 +02:00
}
else if (cmd == "pitch-down") {
2016-03-02 13:20:19 +01:00
if (el && (el->isArticulation() || el->isText()))
2014-07-31 16:30:04 +02:00
el->undoChangeProperty(P_ID::USER_OFF, el->userOff() + QPointF(0.0, MScore::nudgeStep * el->spatium()));
2016-03-02 13:20:19 +01:00
else if (el && el->isRest())
cmdMoveRest(toRest(el), Direction::DOWN);
else if (el && el->isLyrics())
cmdMoveLyrics(toLyrics(el), Direction::DOWN);
2012-05-26 14:26:10 +02:00
else
upDown(false, UpDownMode::CHROMATIC);
2012-05-26 14:26:10 +02:00
}
2014-05-30 13:35:44 +02:00
else if (cmd == "add-staccato")
addArticulation(ArticulationType::Staccato);
2014-05-30 13:35:44 +02:00
else if (cmd == "add-tenuto")
addArticulation(ArticulationType::Tenuto);
else if (cmd == "add-marcato")
addArticulation(ArticulationType::Marcato);
else if (cmd == "add-sforzato")
addArticulation(ArticulationType::Sforzatoaccent);
2014-05-30 13:35:44 +02:00
else if (cmd == "add-trill")
addArticulation(ArticulationType::Trill);
else if (cmd == "add-8va")
cmdAddOttava(Ottava::Type::OTTAVA_8VA);
else if (cmd == "add-8vb")
cmdAddOttava(Ottava::Type::OTTAVA_8VB);
2012-05-26 14:26:10 +02:00
else if (cmd == "delete-measures")
cmdDeleteSelectedMeasures();
else if (cmd == "time-delete") {
2016-08-02 17:00:49 +02:00
if (selection().state() == SelState::RANGE)
cmdDeleteSelectedMeasures();
else
cmdTimeDelete();
2012-05-26 14:26:10 +02:00
}
else if (cmd == "pitch-up-octave") {
2016-03-02 13:20:19 +01:00
if (el && (el->isArticulation() || el->isText()))
2014-07-31 16:30:04 +02:00
el->undoChangeProperty(P_ID::USER_OFF, el->userOff() + QPointF(0.0, -MScore::nudgeStep10 * el->spatium()));
else
upDown(true, UpDownMode::OCTAVE);
}
else if (cmd == "pitch-down-octave") {
2016-03-02 13:20:19 +01:00
if (el && (el->isArticulation() || el->isText()))
2014-07-31 16:30:04 +02:00
el->undoChangeProperty(P_ID::USER_OFF, el->userOff() + QPointF(0.0, MScore::nudgeStep10 * el->spatium()));
else
upDown(false, UpDownMode::OCTAVE);
}
else if (cmd == "note-longa" || cmd == "note-longa-TAB")
padToggle(Pad::NOTE00);
else if (cmd == "note-breve" || cmd == "note-breve-TAB")
padToggle(Pad::NOTE0);
else if (cmd == "pad-note-1" || cmd == "pad-note-1-TAB")
padToggle(Pad::NOTE1);
else if (cmd == "pad-note-2" || cmd == "pad-note-2-TAB")
padToggle(Pad::NOTE2);
else if (cmd == "pad-note-4" || cmd == "pad-note-4-TAB")
padToggle(Pad::NOTE4);
else if (cmd == "pad-note-8" || cmd == "pad-note-8-TAB")
padToggle(Pad::NOTE8);
else if (cmd == "pad-note-16" || cmd == "pad-note-16-TAB")
padToggle(Pad::NOTE16);
else if (cmd == "pad-note-32" || cmd == "pad-note-32-TAB")
padToggle(Pad::NOTE32);
else if (cmd == "pad-note-64" || cmd == "pad-note-64-TAB")
padToggle(Pad::NOTE64);
else if (cmd == "pad-note-128" || cmd == "pad-note-128-TAB")
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::DurationType::V_BREVE:
padToggle(Pad::NOTE00);
break;
case TDuration::DurationType::V_WHOLE:
padToggle(Pad::NOTE0);
break;
case TDuration::DurationType::V_HALF:
padToggle(Pad::NOTE1);
break;
case TDuration::DurationType::V_QUARTER:
padToggle(Pad::NOTE2);
break;
case TDuration::DurationType::V_EIGHTH:
padToggle(Pad::NOTE4);
break;
case TDuration::DurationType::V_16TH:
padToggle(Pad::NOTE8);
break;
case TDuration::DurationType::V_32ND:
padToggle(Pad::NOTE16);
break;
case TDuration::DurationType::V_64TH:
padToggle(Pad::NOTE32);
break;
case TDuration::DurationType::V_128TH:
padToggle(Pad::NOTE64);
break;
default:
break;
}
}
else if (cmd == "pad-note-decrease-TAB") {
switch (_is.duration().type() ) {
case TDuration::DurationType::V_LONG:
padToggle(Pad::NOTE0);
break;
case TDuration::DurationType::V_BREVE:
padToggle(Pad::NOTE1);
break;
case TDuration::DurationType::V_WHOLE:
padToggle(Pad::NOTE2);
break;
case TDuration::DurationType::V_HALF:
padToggle(Pad::NOTE4);
break;
case TDuration::DurationType::V_QUARTER:
padToggle(Pad::NOTE8);
break;
case TDuration::DurationType::V_EIGHTH:
padToggle(Pad::NOTE16);
break;
case TDuration::DurationType::V_16TH:
padToggle(Pad::NOTE32);
break;
case TDuration::DurationType::V_32ND:
padToggle(Pad::NOTE64);
break;
case TDuration::DurationType::V_64TH:
padToggle(Pad::NOTE128);
break;
// cycle back from shortest to longest?
// case TDuration::DurationType::V_128TH:
// padToggle(Pad::NOTE00);
// break;
default:
break;
}
}
2012-05-26 14:26:10 +02:00
else if (cmd == "pad-rest")
padToggle(Pad::REST);
2012-05-26 14:26:10 +02:00
else if (cmd == "pad-dot")
padToggle(Pad::DOT);
2012-05-26 14:26:10 +02:00
else if (cmd == "pad-dotdot")
padToggle(Pad::DOTDOT);
else if (cmd == "pad-dot3")
padToggle(Pad::DOT3);
else if (cmd == "pad-dot4")
padToggle(Pad::DOT4);
2012-05-26 14:26:10 +02:00
else if (cmd == "beam-start")
cmdSetBeamMode(Beam::Mode::BEGIN);
2012-05-26 14:26:10 +02:00
else if (cmd == "beam-mid")
cmdSetBeamMode(Beam::Mode::MID);
2012-05-26 14:26:10 +02:00
else if (cmd == "no-beam")
cmdSetBeamMode(Beam::Mode::NONE);
2012-05-26 14:26:10 +02:00
else if (cmd == "beam-32")
cmdSetBeamMode(Beam::Mode::BEGIN32);
2012-05-26 14:26:10 +02:00
else if (cmd == "sharp2")
2015-04-02 10:33:53 +02:00
changeAccidental(AccidentalType::SHARP2);
2012-05-26 14:26:10 +02:00
else if (cmd == "sharp")
2015-04-02 10:33:53 +02:00
changeAccidental(AccidentalType::SHARP);
2012-05-26 14:26:10 +02:00
else if (cmd == "nat")
2015-04-02 10:33:53 +02:00
changeAccidental(AccidentalType::NATURAL);
2012-05-26 14:26:10 +02:00
else if (cmd == "flat")
2015-04-02 10:33:53 +02:00
changeAccidental(AccidentalType::FLAT);
2012-05-26 14:26:10 +02:00
else if (cmd == "flat2")
2015-04-02 10:33:53 +02:00
changeAccidental(AccidentalType::FLAT2);
else if (cmd == "note-input-repitch")
2012-05-26 14:26:10 +02:00
_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") {
2014-05-26 15:31:36 +02:00
if (styleB(StyleIdx::concertPitch) != a->isChecked())
2012-05-26 14:26:10 +02:00
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") {
LayoutBreak::Type type;
2012-05-26 14:26:10 +02:00
if (cmd == "system-break")
type = LayoutBreak::Type::LINE;
2012-05-26 14:26:10 +02:00
else if (cmd == "page-break")
type = LayoutBreak::Type::PAGE;
2012-05-26 14:26:10 +02:00
else
type = LayoutBreak::Type::SECTION;
2012-05-26 14:26:10 +02:00
if (el && el->type() == Element::Type::BAR_LINE && el->parent()->type() == Element::Type::SEGMENT) {
Measure* measure = toMeasure(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();
2016-03-02 13:20:19 +01:00
else if (cmd == "") //Midi note received only?
;
2012-05-26 14:26:10 +02:00
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") {
2014-05-26 15:31:36 +02:00
bool val = !styleB(StyleIdx::createMultiMeasureRests);
2014-08-17 12:41:44 +02:00
deselectAll();
2014-05-26 15:31:36 +02:00
undo(new ChangeStyleVal(this, StyleIdx::createMultiMeasureRests, val));
}
2014-09-23 18:07:32 +02:00
else if (cmd == "add-brackets")
cmdAddBracket();
else if (cmd == "acciaccatura")
cmdAddGrace(NoteType::ACCIACCATURA, MScore::division / 2);
else if (cmd == "appoggiatura")
cmdAddGrace(NoteType::APPOGGIATURA, MScore::division / 2);
else if (cmd == "grace4")
cmdAddGrace(NoteType::GRACE4, MScore::division);
else if (cmd == "grace16")
cmdAddGrace(NoteType::GRACE16, MScore::division / 4);
else if (cmd == "grace32")
cmdAddGrace(NoteType::GRACE32, MScore::division / 8);
else if (cmd == "grace8after")
cmdAddGrace(NoteType::GRACE8_AFTER, MScore::division / 2);
else if (cmd == "grace16after")
cmdAddGrace(NoteType::GRACE16_AFTER, MScore::division / 4);
else if (cmd == "grace32after")
cmdAddGrace(NoteType::GRACE32_AFTER, MScore::division / 8);
2014-11-09 02:02:37 +01:00
else if (cmd == "explode")
cmdExplode();
else if (cmd == "implode")
cmdImplode();
else if (cmd == "slash-fill")
cmdSlashFill();
else if (cmd == "slash-rhythm")
cmdSlashRhythm();
else if (cmd == "resequence-rehearsal-marks")
cmdResequenceRehearsalMarks();
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
}
2014-07-25 17:13:27 +02:00
2014-08-06 10:15:58 +02:00
//---------------------------------------------------------
// cmdInsertClef
//---------------------------------------------------------
void Score::cmdInsertClef(ClefType type)
{
if (!noteEntryMode())
return;
undoChangeClef(staff(inputTrack()/VOICES), inputState().segment(), type);
}
2014-07-25 17:13:27 +02:00
//---------------------------------------------------------
// cmdInsertClef
// insert clef before cr
//---------------------------------------------------------
void Score::cmdInsertClef(Clef* clef, ChordRest* cr)
{
Clef* gclef = 0;
for (ScoreElement* e : cr->linkList()) {
2014-07-25 17:13:27 +02:00
ChordRest* cr = static_cast<ChordRest*>(e);
2014-08-06 10:25:17 +02:00
Score* score = cr->score();
2014-07-25 17:13:27 +02:00
//
// create a clef segment before cr if needed
2014-07-25 17:13:27 +02:00
//
2014-08-06 10:25:17 +02:00
Segment* s = cr->segment();
Segment* cs = s->prev();
2014-08-06 10:25:17 +02:00
int tick = s->tick();
bool createSegment = true;
#if 1
// re-use a preceding clef segment containing no clef or a non-generated one,
// but still allow addition of a "mid-measure" change even at the start of a measure/system
// this is useful for cues, for example
if (cs && cs->segmentType() == Segment::Type::Clef) {
Element* e = cs->element(cr->staffIdx() * VOICES);
if (!e || !e->generated())
createSegment = false;
}
#else
// automatically convert a clef added on first chordrest of measure
// into a "regular" clef change at end of previous bar
// this defeats the ability to have a "mid-measure" clef change at the start of a bar for cues
Measure* m = s->measure();
if (s == m->first(Segment::Type::ChordRest)) {
if (m->prevMeasure())
m = m->prevMeasure();
cs = m->undoGetSegment(Segment::Type::Clef, tick);
createSegment = false;
}
#endif
if (createSegment) {
2014-08-06 10:25:17 +02:00
cs = new Segment(cr->measure(), Segment::Type::Clef, tick);
2014-07-25 17:13:27 +02:00
cs->setNext(s);
score->undo(new AddElement(cs));
}
Clef* c = toClef(gclef ? gclef->linkedClone() : clef->clone());
2014-07-25 17:13:27 +02:00
gclef = c;
c->setParent(cs);
2014-08-06 10:15:58 +02:00
c->setScore(score);
2014-07-25 17:13:27 +02:00
c->setTrack(cr->staffIdx() * VOICES);
if (cs->element(c->track()))
score->undo(new RemoveElement(cs->element(c->track())));
score->undo(new AddElement(c));
}
delete clef;
}
//---------------------------------------------------------
// cmdAddGrace
/// adds grace note of specified type to selected notes
//---------------------------------------------------------
void Score::cmdAddGrace (NoteType graceType, int duration) {
startCmd();
for (Element* e : selection().elements()) {
if (e->type() == Element::Type::NOTE) {
Note* n = toNote(e);
setGraceNote(n->chord(), n->pitch(), graceType, duration);
}
}
endCmd();
}
2014-11-09 02:02:37 +01:00
//---------------------------------------------------------
// cmdExplode
/// explodes contents of top selected staff into subsequent staves
//---------------------------------------------------------
void Score::cmdExplode()
{
if (!selection().isRange())
return;
int srcStaff = selection().staffStart();
int lastStaff = selection().staffEnd();
int srcTrack = srcStaff * VOICES;
// reset selection to top staff only
// force complete measures
Segment* startSegment = selection().startSegment();
Segment* endSegment = selection().endSegment();
Measure* startMeasure = startSegment->measure();
Measure* endMeasure = endSegment ? endSegment->measure() : lastMeasure();
deselectAll();
select(startMeasure, SelectType::RANGE, srcStaff);
select(endMeasure, SelectType::RANGE, srcStaff);
startSegment = selection().startSegment();
endSegment = selection().endSegment();
if (srcStaff == lastStaff - 1) {
// only one staff was selected up front - determine number of staves
// loop through all chords looking for maximum number of notes
int n = 0;
for (Segment* s = startSegment; s && s != endSegment; s = s->next1()) {
Element* e = s->element(srcTrack);
if (e && e->type() == Element::Type::CHORD) {
Chord* c = toChord(e);
2016-02-06 22:03:43 +01:00
n = qMax(n, int(c->notes().size()));
2014-11-09 02:02:37 +01:00
}
}
lastStaff = qMin(nstaves(), srcStaff + n);
}
// make our own copy of selection, since pasting modifies actual selection
Selection srcSelection(selection());
2014-11-09 02:02:37 +01:00
// copy to all destination staves
Segment* firstCRSegment = startMeasure->tick2segment(startMeasure->tick());
2014-11-09 02:02:37 +01:00
for (int i = 1; srcStaff + i < lastStaff; ++i) {
int track = (srcStaff + i) * VOICES;
ChordRest* cr = toChordRest(firstCRSegment->element(track));
2014-11-09 02:02:37 +01:00
if (cr) {
XmlReader e(srcSelection.mimeData());
2014-11-09 02:02:37 +01:00
e.setPasteMode(true);
2016-03-02 13:20:19 +01:00
if (pasteStaff(e, cr->segment(), cr->staffIdx()) != PasteState::PS_NO_ERROR)
2014-11-09 02:02:37 +01:00
qDebug("explode: paste failed");
}
}
// loop through each staff removing all but one note from each chord
for (int i = 0; srcStaff + i < lastStaff; ++i) {
int track = (srcStaff + i) * VOICES;
for (Segment* s = startSegment; s && s != endSegment; s = s->next1()) {
Element* e = s->element(track);
if (e && e->type() == Element::Type::CHORD) {
Chord* c = toChord(e);
2016-02-06 22:03:43 +01:00
std::vector<Note*> notes = c->notes();
2014-11-09 02:02:37 +01:00
int nnotes = notes.size();
// keep note "i" from top, which is backwards from nnotes - 1
// reuse notes if there are more instruments than notes
int stavesPerNote = qMax((lastStaff - srcStaff) / nnotes, 1);
int keepIndex = qMax(nnotes - 1 - (i / stavesPerNote), 0);
Note* keepNote = c->notes()[keepIndex];
foreach (Note* n, notes) {
if (n != keepNote)
undoRemoveElement(n);
}
}
}
}
// select exploded region
deselectAll();
select(startMeasure, SelectType::RANGE, srcStaff);
select(endMeasure, SelectType::RANGE, lastStaff - 1);
}
//---------------------------------------------------------
// cmdImplode
/// implodes contents of selected staves into top staff
/// for single staff, merge voices
2014-11-09 02:02:37 +01:00
//---------------------------------------------------------
void Score::cmdImplode()
{
if (!selection().isRange())
return;
int dstStaff = selection().staffStart();
int endStaff = selection().staffEnd();
int startTrack = dstStaff * VOICES;
int endTrack;
int trackInc;
// if single staff selected, combine voices
// otherwise combine staves
if (dstStaff == endStaff - 1) {
endTrack = startTrack + VOICES;
trackInc = 1;
}
else {
endTrack = endStaff * VOICES;
trackInc = VOICES;
}
2014-11-09 02:02:37 +01:00
Segment* startSegment = selection().startSegment();
Segment* endSegment = selection().endSegment();
Measure* startMeasure = startSegment->measure();
Measure* endMeasure = endSegment ? endSegment->measure() : lastMeasure();
// loop through segments adding notes to chord on top staff
int dstTrack = dstStaff * VOICES;
for (Segment* s = startSegment; s && s != endSegment; s = s->next1()) {
if (s->segmentType() != Segment::Type::ChordRest)
continue;
Element* dst = s->element(dstTrack);
if (dst && dst->type() == Element::Type::CHORD) {
Chord* dstChord = toChord(dst);
2014-11-09 02:02:37 +01:00
// see if we are tying in to this chord
Chord* tied = 0;
foreach (Note* n, dstChord->notes()) {
if (n->tieBack()) {
tied = n->tieBack()->startNote()->chord();
break;
}
}
// loop through each subsequent staff (or track within staff)
// looking for notes to add
for (int srcTrack = startTrack + trackInc; srcTrack < endTrack; srcTrack += trackInc) {
2014-11-09 02:02:37 +01:00
Element* src = s->element(srcTrack);
if (src && src->type() == Element::Type::CHORD) {
Chord* srcChord = toChord(src);
// when combining voices, skip if not same duration
if ((trackInc == 1) && (srcChord->duration() != dstChord->duration()))
continue;
2014-11-09 02:02:37 +01:00
// add notes
foreach (Note* n, srcChord->notes()) {
NoteVal nv(n->pitch());
nv.tpc1 = n->tpc1();
// skip duplicates
if (dstChord->findNote(nv.pitch))
continue;
Note* nn = addNote(dstChord, nv);
// add tie to this note if original chord was tied
if (tied) {
// find note to tie to
foreach (Note *tn, tied->notes()) {
if (nn->pitch() == tn->pitch() && nn->tpc() == tn->tpc() && !tn->tieFor()) {
// found note to tie
Tie* tie = new Tie(this);
tie->setStartNote(tn);
tie->setEndNote(nn);
tie->setTrack(tn->track());
undoAddElement(tie);
}
}
}
}
}
// delete chordrest from source track if possible
if (src && src->voice())
undoRemoveElement(src);
}
}
else if (dst && trackInc == 1) {
// destination track has something, but it isn't a chord
// remove everything from other voices if in "voice mode"
for (int i = 1; i < VOICES; ++i) {
Element* e = s->element(dstTrack + i);
if (e)
undoRemoveElement(e);
2014-11-09 02:02:37 +01:00
}
}
}
// select destination staff only
deselectAll();
select(startMeasure, SelectType::RANGE, dstStaff);
select(endMeasure, SelectType::RANGE, dstStaff);
}
//---------------------------------------------------------
// cmdSlashFill
/// fills selected region with slashes
//---------------------------------------------------------
void Score::cmdSlashFill()
{
int startStaff = selection().staffStart();
int endStaff = selection().staffEnd();
Segment* startSegment = selection().startSegment();
if (!startSegment) // empty score?
return;
Segment* endSegment = selection().endSegment();
int endTick = endSegment ? endSegment->tick() : lastSegment()->tick() + 1;
Chord* firstSlash = 0;
Chord* lastSlash = 0;
// loop through staves in selection
for (int staffIdx = startStaff; staffIdx < endStaff; ++staffIdx) {
int track = staffIdx * VOICES;
int voice = -1;
// loop through segments adding slashes on each beat
for (Segment* s = startSegment; s && s->tick() < endTick; s = s->next1()) {
if (s->segmentType() != Segment::Type::ChordRest)
continue;
// determine beat type based on time signature
int d = s->measure()->timesig().denominator();
int n = (d > 4 && s->measure()->timesig().numerator() % 3 == 0) ? 3 : 1;
Fraction f(n, d);
// skip over any leading segments before next (first) beat
if (s->rtick() % f.ticks())
continue;
// determine voice to use - first available voice for this measure / staff
if (voice == -1 || s->rtick() == 0) {
bool needGap[VOICES];
for (voice = 0; voice < VOICES; ++voice) {
needGap[voice] = false;
ChordRest* cr = toChordRest(s->element(track + voice));
// no chordrest == treat as ordinary rest for purpose of determining availbility of voice
// but also, we will need to make a gap for this voice if we do end up choosing it
if (!cr)
needGap[voice] = true;
// chord == keep looking for an available voice
else if (cr->type() == Element::Type::CHORD)
continue;
// full measure rest == OK to use voice
else if (cr->durationType() == TDuration::DurationType::V_MEASURE)
break;
// no chordrest or ordinary rest == OK to use voice
// if there are nothing but rests for duration of measure / selection
bool ok = true;
for (Segment* ns = s->next(Segment::Type::ChordRest); ns && ns != endSegment; ns = ns->next(Segment::Type::ChordRest)) {
ChordRest* ncr = toChordRest(ns->element(track + voice));
if (ncr && ncr->type() == Element::Type::CHORD) {
ok = false;
break;
}
}
if (ok)
break;
}
// no available voices, just use voice 0
if (voice == VOICES)
voice = 0;
// no cr was found in segment for this voice, so make gap
if (needGap[voice])
makeGapVoice(s, track + voice, f, s->tick());
}
// construct note
int line = 0;
bool error = false;
NoteVal nv;
if (staff(staffIdx)->staffType()->group() == StaffGroup::TAB)
line = staff(staffIdx)->lines() / 2;
else
line = staff(staffIdx)->middleLine(); // staff(staffIdx)->lines() - 1;
if (staff(staffIdx)->staffType()->group() == StaffGroup::PERCUSSION) {
nv.pitch = 0;
nv.headGroup = NoteHead::Group::HEAD_SLASH;
}
else {
Position p;
p.segment = s;
p.staffIdx = staffIdx;
p.line = line;
p.fret = FRET_NONE;
_is.setRest(false); // needed for tab
nv = noteValForPosition(p, error);
}
if (error)
continue;
// insert & turn into slash
s = setNoteRest(s, track + voice, nv, f);
Chord* c = toChord(s->element(track + voice));
if (c->links()) {
for (ScoreElement* e : *c->links()) {
Chord* lc = static_cast<Chord*>(e);
lc->setSlash(true, true);
}
}
else
c->setSlash(true, true);
lastSlash = c;
if (!firstSlash)
firstSlash = c;
}
}
// re-select the slashes
deselectAll();
if (firstSlash && lastSlash) {
select(firstSlash, SelectType::RANGE);
select(lastSlash, SelectType::RANGE);
}
}
//---------------------------------------------------------
// cmdSlashRhythm
/// converts rhythms in selected region to slashes
//---------------------------------------------------------
2013-05-13 18:49:17 +02:00
void Score::cmdSlashRhythm()
{
QList<Chord*> chords;
// loop through all notes in selection
foreach (Element* e, selection().elements()) {
if (e->voice() >= 2 && e->type() == Element::Type::REST) {
Rest* r = toRest(e);
if (r->links()) {
for (ScoreElement* e : *r->links()) {
Rest* lr = static_cast<Rest*>(e);
lr->setAccent(!lr->accent());
}
}
else
r->setAccent(!r->accent());
continue;
}
else if (e->type() == Element::Type::NOTE) {
Note* n = toNote(e);
if (n->noteType() != NoteType::NORMAL)
continue;
Chord* c = n->chord();
// check for duplicates (chords with multiple notes)
if (chords.contains(c))
continue;
chords.append(c);
// toggle slash setting
if (c->links()) {
for (ScoreElement* e : *c->links()) {
Chord* lc = static_cast<Chord*>(e);
lc->setSlash(!lc->slash(), false);
}
}
else
c->setSlash(!c->slash(), false);
}
}
}
//---------------------------------------------------------
// cmdResequenceRehearsalMarks
/// resequences rehearsal marks within a range selection
/// or, if nothing is selected, the entire score
//---------------------------------------------------------
void Score::cmdResequenceRehearsalMarks()
{
bool noSelection = !selection().isRange();
if (noSelection)
cmdSelectAll();
else if (!selection().isRange())
return;
RehearsalMark* last = 0;
for (Segment* s = selection().startSegment(); s && s != selection().endSegment(); s = s->next1()) {
for (Element* e : s->annotations()) {
if (e->type() == Element::Type::REHEARSAL_MARK) {
RehearsalMark* rm = toRehearsalMark(e);
if (last) {
QString rmText = nextRehearsalMarkText(last, rm);
for (ScoreElement* le : rm->linkList())
le->undoChangeProperty(P_ID::TEXT, rmText);
}
last = rm;
}
}
}
if (noSelection)
deselectAll();
}
//---------------------------------------------------------
// addRemoveBreaks
2016-03-24 12:39:18 +01:00
// interval lock
// 0 false remove all linebreaks
// > 0 false add linebreak every interval measure
// d.c. true add linebreak at every system end
//---------------------------------------------------------
void Score::addRemoveBreaks(int interval, bool lock)
{
Segment* startSegment = selection().startSegment();
if (!startSegment) // empty score?
return;
Segment* endSegment = selection().endSegment();
Measure* startMeasure = startSegment->measure();
Measure* endMeasure = endSegment ? endSegment->measure() : lastMeasureMM();
Measure* lastMeasure = lastMeasureMM();
// loop through measures in selection
// count mmrests as a single measure
int count = 0;
for (Measure* mm = startMeasure; mm; mm = mm->nextMeasureMM()) {
// even though we are counting mmrests as a single measure,
// we need to find last real measure within mmrest for the actual break
Measure* m = mm->isMMRest() ? mm->mmRestLast() : mm;
if (lock) {
// skip last measure of score
if (mm == lastMeasure)
break;
// skip if it already has a break
if (m->lineBreak() || m->pageBreak())
continue;
// add break if last measure of system
if (mm->system() && mm->system()->lastMeasure() == mm)
m->undoSetLineBreak(true);
}
else {
if (interval == 0) {
// remove line break if present
if (m->lineBreak())
m->undoSetLineBreak(false);
}
else {
if (++count == interval) {
// skip last measure of score
if (mm == lastMeasure)
break;
// found place for break; add if not already one present
if (!(m->lineBreak() || m->pageBreak()))
m->undoSetLineBreak(true);
// reset count
count = 0;
}
else if (m->lineBreak()) {
// remove line break if present in wrong place
m->undoSetLineBreak(false);
}
}
}
if (mm == endMeasure)
break;
}
}
}