2012-05-26 14:26:10 +02:00
|
|
|
//=============================================================================
|
|
|
|
// MuseScore
|
|
|
|
// Music Composition & Notation
|
|
|
|
//
|
2013-09-05 20:05:49 +02:00
|
|
|
// 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>
|
|
|
|
|
2015-02-03 13:42:37 +01:00
|
|
|
#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"
|
2013-12-05 21:37:28 +01:00
|
|
|
#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"
|
2013-09-05 20:05:49 +02:00
|
|
|
#include "tremolo.h"
|
2014-12-09 08:45:30 +01:00
|
|
|
#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)
|
|
|
|
{
|
2016-03-05 10:29:38 +01:00
|
|
|
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.
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-12-22 12:29:48 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-06-14 10:32:34 +02:00
|
|
|
if (rollback)
|
2016-03-10 10:41:31 +01:00
|
|
|
undoStack()->current()->unwind();
|
2015-03-10 18:29:57 +01:00
|
|
|
|
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();
|
2016-04-18 18:11:51 +02:00
|
|
|
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();
|
2016-04-18 18:11:51 +02:00
|
|
|
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());
|
2016-04-18 18:11:51 +02:00
|
|
|
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
|
|
|
}
|
2016-04-18 18:11:51 +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();
|
2014-07-28 23:41:42 +02:00
|
|
|
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
|
2016-06-14 10:32:34 +02:00
|
|
|
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());
|
2013-06-25 14:29:18 +02:00
|
|
|
spanner->setTick2(m->endTick());
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
undoAddElement(spanner);
|
2014-05-27 10:34:08 +02:00
|
|
|
select(spanner, SelectType::SINGLE, 0);
|
2015-06-30 09:06:30 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2015-06-30 09:06:30 +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) {
|
2015-08-11 21:39:12 +02:00
|
|
|
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
|
2016-01-16 19:52:56 +01:00
|
|
|
// 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;
|
|
|
|
|
2016-01-16 19:52:56 +01:00
|
|
|
// find previous segment with cr in this track
|
2012-05-26 14:26:10 +02:00
|
|
|
Segment* ps;
|
2014-06-25 11:46:10 +02:00
|
|
|
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) {
|
2016-06-14 10:32:34 +02:00
|
|
|
ChordRest* cr = toChordRest(ps->element(track));
|
2012-05-26 14:26:10 +02:00
|
|
|
int tick = cr->tick() + cr->actualTicks();
|
|
|
|
if (tick > s->tick()) {
|
2016-01-16 19:52:56 +01:00
|
|
|
// 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()) {
|
2016-01-16 19:52:56 +01:00
|
|
|
// 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
|
2015-02-13 06:57:37 +01:00
|
|
|
ps = ps->measure()->undoGetSegment(Segment::Type::ChordRest, tick);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
//
|
2016-01-16 19:52:56 +01: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);
|
|
|
|
|
|
|
|
//
|
2016-01-16 19:52:56 +01:00
|
|
|
// fill from s->tick() until next chord/rest in measure
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
Segment* ns;
|
2014-06-25 11:46:10 +02:00
|
|
|
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())
|
2014-05-21 15:43:19 +02:00
|
|
|
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();
|
2014-04-09 09:40:25 +02:00
|
|
|
for (Note* on : nl) {
|
2014-11-26 19:00:47 +01:00
|
|
|
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;
|
2014-04-09 09:40:25 +02:00
|
|
|
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());
|
2014-04-09 09:40:25 +02:00
|
|
|
ClefType clef = estaff->clef(tick);
|
2014-06-20 17:07:22 +02:00
|
|
|
Key key = estaff->key(tick);
|
2014-04-09 09:40:25 +02:00
|
|
|
npitch = line2pitch(line, clef, key);
|
2014-04-23 11:08:51 +02:00
|
|
|
|
2014-05-23 21:14:22 +02:00
|
|
|
int ntpc = pitch2tpc(npitch, key, Prefer::NEAREST);
|
2015-09-21 02:13:22 +02:00
|
|
|
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;
|
2014-12-20 00:27:06 +01:00
|
|
|
ntpc2 = Ms::transposeTpc(ntpc, v, true);
|
2014-04-23 11:08:51 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
npitch += v.chromatic;
|
|
|
|
ntpc2 = ntpc;
|
2014-12-20 00:27:06 +01:00
|
|
|
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);
|
2014-04-09 09:40:25 +02:00
|
|
|
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
|
|
|
}
|
2016-01-21 14:33:32 +01: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
|
|
|
|
2014-05-27 10:34:08 +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.
|
2015-01-17 17:38:51 +01:00
|
|
|
/// \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);
|
2015-01-17 22:40:28 +01:00
|
|
|
|
|
|
|
// alow grace notes to be added to other grace notes
|
|
|
|
// by really adding to parent chord
|
|
|
|
if (ch->noteType() != NoteType::NORMAL)
|
2016-06-14 10:32:34 +02:00
|
|
|
ch = toChord(ch->parent());
|
2015-01-17 22:40:28 +01:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
chord->setTrack(ch->track());
|
2013-06-12 14:23:57 +02:00
|
|
|
chord->setParent(ch);
|
2014-04-09 09:40:25 +02:00
|
|
|
chord->add(note);
|
|
|
|
|
|
|
|
note->setPitch(pitch);
|
2015-01-17 17:38:51 +01:00
|
|
|
// 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);
|
2014-05-27 10:34:08 +02:00
|
|
|
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
|
|
|
{
|
2014-06-25 11:46: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;
|
2016-06-14 10:32:34 +02:00
|
|
|
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;
|
2015-06-22 05:29:07 +02:00
|
|
|
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);
|
2015-06-22 05:29:07 +02:00
|
|
|
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);
|
2014-12-15 23:08:33 +01:00
|
|
|
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);
|
2015-06-22 05:29:07 +02:00
|
|
|
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;
|
|
|
|
|
2014-06-25 11:46:10 +02:00
|
|
|
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;
|
|
|
|
|
2016-06-14 10:32:34 +02:00
|
|
|
cr = toChordRest(segment->element(track));
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
if (cr == 0) {
|
|
|
|
if (track % VOICES)
|
2014-05-21 15:43:19 +02:00
|
|
|
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
|
|
|
|
//
|
2016-06-14 10:32:34 +02:00
|
|
|
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());
|
2015-02-12 11:41:39 +01:00
|
|
|
for (ScoreElement* e : _is.slur()->linkList()) {
|
2014-07-29 14:08:52 +02:00
|
|
|
Slur* slur = static_cast<Slur*>(e);
|
2015-02-12 11:41:39 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
2014-05-27 10:34:08 +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
|
|
|
{
|
2014-03-04 13:32:32 +01: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();
|
|
|
|
|
2014-06-25 11:46:10 +02:00
|
|
|
for (Segment* seg = firstSegment; seg; seg = seg->next(Segment::Type::ChordRest)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
// voices != 0 may have gaps:
|
|
|
|
//
|
2016-06-14 10:32:34 +02:00
|
|
|
ChordRest* cr = toChordRest(seg->element(track));
|
2012-05-26 14:26:10 +02:00
|
|
|
if (!cr) {
|
|
|
|
if (seg->tick() < nextTick)
|
|
|
|
continue;
|
2014-06-25 11:46:10 +02:00
|
|
|
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;
|
2013-06-04 18:49:04 +02:00
|
|
|
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());
|
|
|
|
|
2013-09-05 20:05:49 +02:00
|
|
|
// remove tremolo between 2 notes, if present
|
2014-06-24 18:36:02 +02:00
|
|
|
if (cr->type() == Element::Type::CHORD) {
|
2016-06-14 10:32:34 +02:00
|
|
|
Chord* c = toChord(cr);
|
2013-09-05 20:05:49 +02:00
|
|
|
if (c->tremolo()) {
|
|
|
|
Tremolo* tremolo = c->tremolo();
|
|
|
|
if (tremolo->twoNotes())
|
|
|
|
undoRemoveElement(tremolo);
|
|
|
|
}
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
Tuplet* ltuplet = cr->tuplet();
|
2015-04-14 00:56:20 +02:00
|
|
|
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.
|
|
|
|
|
2015-04-14 00:56:20 +02:00
|
|
|
// 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;
|
2016-07-09 17:07:12 +02:00
|
|
|
while (t->elements().back()->isTuplet())
|
|
|
|
t = toTuplet(t->elements().back());
|
|
|
|
seg = toChordRest(t->elements().back())->segment();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2015-04-14 00:56:20 +02:00
|
|
|
// now delete the full tuplet
|
2012-05-26 14:26:10 +02:00
|
|
|
td = ltuplet->duration();
|
|
|
|
cmdDeleteTuplet(ltuplet, false);
|
|
|
|
tuplet = 0;
|
|
|
|
}
|
|
|
|
else {
|
2014-08-14 14:29:35 +02:00
|
|
|
if (seg != firstSegment || !keepChord)
|
2013-05-10 10:51:27 +02:00
|
|
|
undoRemoveElement(cr);
|
2015-04-14 00:56:20 +02:00
|
|
|
// 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) {
|
2016-03-23 12:23:02 +01:00
|
|
|
qDebug("reinstate at %d, %d", tick, d.ticks());
|
2015-04-14 00:56:20 +02:00
|
|
|
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 {
|
2015-04-14 00:56:20 +02:00
|
|
|
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) {
|
2016-03-23 12:23:02 +01:00
|
|
|
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)
|
2014-07-28 18:40:02 +02:00
|
|
|
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);
|
2015-04-14 00:56:20 +02:00
|
|
|
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;
|
2016-06-14 10:32:34 +02:00
|
|
|
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) {
|
2016-03-23 12:23:02 +01:00
|
|
|
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;
|
2014-06-25 11:46:10 +02:00
|
|
|
seg1 = seg1->prev(Segment::Type::ChordRest);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-06-14 10:32:34 +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);
|
2014-11-18 17:09:26 +01:00
|
|
|
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
|
2016-06-14 10:32:34 +02:00
|
|
|
Chord* c = toChord(cr1);
|
2014-11-18 17:09:26 +01:00
|
|
|
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
|
2016-06-14 10:32:34 +02:00
|
|
|
Rest* r = toRest(cr1);
|
2014-11-18 17:09:26 +01:00
|
|
|
for (int i = 1; i < n; ++i) {
|
|
|
|
TDuration d = dList[i];
|
2016-06-14 10:32:34 +02:00
|
|
|
Rest* r2 = toRest(r->clone());
|
2014-11-18 17:09:26 +01:00
|
|
|
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 (;;) {
|
2014-06-25 11:46:10 +02:00
|
|
|
seg1 = seg1->next1(Segment::Type::ChordRest);
|
2013-06-20 15:25:11 +02:00
|
|
|
if (seg1 == 0) {
|
2016-03-23 12:23:02 +01:00
|
|
|
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)) {
|
2016-06-14 10:32:34 +02:00
|
|
|
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) {
|
2016-03-23 12:23:02 +01:00
|
|
|
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()) {
|
2016-03-23 12:23:02 +01:00
|
|
|
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");
|
2014-06-24 18:36:02 +02:00
|
|
|
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
|
2014-09-06 12:50:28 +02:00
|
|
|
Segment* s = m->undoGetSegment(Segment::Type::ChordRest, m->tick());
|
2012-05-26 14:26:10 +02:00
|
|
|
int track = cr->track();
|
2016-06-14 10:32:34 +02:00
|
|
|
cr = toChordRest(s->element(track));
|
2012-05-26 14:26:10 +02:00
|
|
|
if (cr == 0) {
|
2014-05-21 15:43:19 +02:00
|
|
|
addRest(s, track, TDuration(TDuration::DurationType::V_MEASURE), 0);
|
2016-06-14 10:32:34 +02:00
|
|
|
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;
|
2014-06-25 11:46:10 +02:00
|
|
|
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;
|
2014-05-21 15:43:19 +02:00
|
|
|
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;
|
|
|
|
|
2016-05-31 19:48:28 +02:00
|
|
|
int track = cr->track();
|
2012-05-26 14:26:10 +02:00
|
|
|
Tuplet* tuplet = cr->tuplet();
|
2013-06-05 16:48:09 +02:00
|
|
|
|
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();
|
2014-06-24 18:36:02 +02:00
|
|
|
if (cr->type() == Element::Type::CHORD) {
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
2013-09-05 20:05:49 +02:00
|
|
|
// remove ties and tremolo between 2 notes
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
2016-06-14 10:32:34 +02:00
|
|
|
Chord* c = toChord(cr);
|
2013-09-05 20:05:49 +02:00
|
|
|
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;
|
2015-02-17 20:22:24 +01:00
|
|
|
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
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
if (cr->type() == Element::Type::REST) {
|
2012-05-26 14:26:10 +02:00
|
|
|
Fraction timeStretch = cr1->staff()->timeStretch(cr1->tick());
|
2016-06-14 10:32:34 +02:00
|
|
|
Rest* r = toRest(cr);
|
2013-05-18 17:07:20 +02:00
|
|
|
if (first) {
|
2016-02-06 22:03:43 +01:00
|
|
|
std::vector<TDuration> dList = toDurationList(f2, true);
|
2013-05-18 17:07:20 +02:00
|
|
|
undoChangeChordRestLen(cr, dList[0]);
|
2014-12-05 09:18:40 +01:00
|
|
|
int tick2 = cr->tick();
|
2016-02-06 22:03:43 +01:00
|
|
|
for (unsigned i = 1; i < dList.size(); ++i) {
|
2014-12-05 09:18:40 +01:00
|
|
|
tick2 += dList[i-1].ticks();
|
|
|
|
TDuration d = dList[i];
|
|
|
|
setRest(tick2, track, d.fraction() * timeStretch, (d.dots() > 0), tuplet);
|
2013-05-18 17:07:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
r = setRest(tick, track, f2 * timeStretch, (d.dots() > 0), tuplet);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
if (first) {
|
2014-05-27 10:34:08 +02:00
|
|
|
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;
|
2016-06-14 10:32:34 +02:00
|
|
|
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)
|
2014-05-27 10:34:08 +02:00
|
|
|
select(oc, SelectType::SINGLE, 0);
|
2013-06-05 16:48:09 +02:00
|
|
|
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;
|
2016-06-14 10:32:34 +02:00
|
|
|
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);
|
2014-12-12 06:53:00 +01:00
|
|
|
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;
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment* s = m1->first(Segment::Type::ChordRest);
|
2012-05-26 14:26:10 +02:00
|
|
|
expandVoice(s, track);
|
2016-06-14 10:32:34 +02:00
|
|
|
cr1 = toChordRest(s->element(track));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
connectTies();
|
|
|
|
}
|
|
|
|
|
2014-04-10 16:01:26 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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)
|
2014-04-10 16:01:26 +02:00
|
|
|
{
|
|
|
|
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))
|
2014-04-10 16:01:26 +02:00
|
|
|
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);
|
2014-04-10 16:01:26 +02:00
|
|
|
}
|
|
|
|
else {
|
2014-06-20 17:07:22 +02:00
|
|
|
if (tpc2 > Tpc::TPC_A + int(key))
|
2014-04-10 16:01:26 +02:00
|
|
|
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);
|
2014-04-10 16:01:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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))
|
2014-04-10 16:01:26 +02:00
|
|
|
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);
|
2014-04-10 16:01:26 +02:00
|
|
|
}
|
|
|
|
else {
|
2014-06-20 17:07:22 +02:00
|
|
|
if (tpc2 > Tpc::TPC_C + int(key))
|
2014-04-10 16:01:26 +02:00
|
|
|
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);
|
2014-04-10 16:01:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setTpc
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-04-14 10:39:27 +02:00
|
|
|
static void setTpc(Note* oNote, int tpc, int& newTpc1, int& newTpc2)
|
2014-04-10 16:01:26 +02:00
|
|
|
{
|
|
|
|
if (oNote->concertPitch()) {
|
|
|
|
newTpc1 = tpc;
|
2014-04-14 10:39:27 +02:00
|
|
|
newTpc2 = oNote->transposeTpc(tpc);
|
2014-04-10 16:01:26 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
newTpc2 = tpc;
|
2014-04-14 10:39:27 +02:00
|
|
|
newTpc1 = oNote->transposeTpc(tpc);
|
2014-04-10 16:01:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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();
|
2015-02-13 01:09:01 +01:00
|
|
|
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
|
|
|
|
2015-02-13 01:09:01 +01:00
|
|
|
switch (staff->staffType()->group()) {
|
2014-05-27 13:30:23 +02:00
|
|
|
case StaffGroup::PERCUSSION:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2015-03-13 11:16:43 +01:00
|
|
|
const Drumset* ds = part->instrument()->drumset();
|
2015-11-04 16:22:24 +01:00
|
|
|
if (ds) {
|
2014-03-26 11:02:23 +01:00
|
|
|
newPitch = up ? ds->prevPitch(pitch) : ds->nextPitch(pitch);
|
2015-11-04 16:22:24 +01:00
|
|
|
newTpc1 = pitch2tpc(newPitch, Key::C, Prefer::NEAREST);
|
|
|
|
newTpc2 = newTpc1;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
break;
|
2014-05-27 13:30:23 +02:00
|
|
|
case StaffGroup::TAB:
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2015-03-13 11:16:43 +01:00
|
|
|
const StringData* stringData = part->instrument()->stringData();
|
2014-05-13 17:15:32 +02:00
|
|
|
switch (mode) {
|
2014-05-27 12:50:54 +02:00
|
|
|
case UpDownMode::OCTAVE: // move same note to next string, if possible
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2015-02-13 01:09:01 +01:00
|
|
|
StaffType* stt = staff->staffType();
|
2012-09-19 14:15:49 +02:00
|
|
|
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
|
2014-06-28 13:07:36 +02:00
|
|
|
string = stt->visualStringToPhys(string);
|
2015-02-13 01:09:01 +01:00
|
|
|
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;
|
2013-02-07 23:52:13 +01:00
|
|
|
// newPitch and newTpc remain unchanged
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2014-05-27 12:50:54 +02:00
|
|
|
case UpDownMode::DIATONIC: // increase / decrease the pitch,
|
2012-05-26 14:26:10 +02:00
|
|
|
// letting the algorithm to choose fret & string
|
2014-04-10 16:01:26 +02:00
|
|
|
upDownChromatic(up, pitch, oNote, key, tpc1, tpc2, newPitch, newTpc1, newTpc2);
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
|
2014-05-27 12:50:54 +02:00
|
|
|
case UpDownMode::CHROMATIC: // increase / decrease the fret
|
2012-05-26 14:26:10 +02:00
|
|
|
{ // without changing the string
|
2015-02-13 01:09:01 +01:00
|
|
|
// compute new fret
|
|
|
|
if (!stringData->frets()) {
|
2014-05-26 12:10:59 +02:00
|
|
|
qDebug("upDown tab chromatic: no frets?");
|
2015-02-13 01:09:01 +01:00
|
|
|
return;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
fret += (up ? 1 : -1);
|
2015-04-09 11:06:26 +02:00
|
|
|
if (fret < 0 || fret > stringData->frets()) {
|
2015-02-13 01:09:01 +01:00
|
|
|
qDebug("upDown tab in-string: out of fret range");
|
|
|
|
return;
|
2014-05-26 12:10:59 +02:00
|
|
|
}
|
2015-02-13 01:09:01 +01: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);
|
2015-02-13 01:09:01 +01:00
|
|
|
// undoChangeProperty(oNote, P_ID::STRING, string);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2014-05-27 13:30:23 +02:00
|
|
|
case StaffGroup::STANDARD:
|
2016-03-02 13:20:19 +01:00
|
|
|
switch (mode) {
|
2014-05-27 12:50:54 +02:00
|
|
|
case UpDownMode::OCTAVE:
|
2013-02-07 23:52:13 +01:00
|
|
|
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;
|
|
|
|
|
2014-05-27 12:50:54 +02:00
|
|
|
case UpDownMode::CHROMATIC:
|
2014-04-10 16:01:26 +02:00
|
|
|
upDownChromatic(up, pitch, oNote, key, tpc1, tpc2, newPitch, newTpc1, newTpc2);
|
2012-05-26 14:26:10 +02:00
|
|
|
break;
|
|
|
|
|
2014-05-27 12:50:54 +02:00
|
|
|
case UpDownMode::DIATONIC:
|
2014-04-10 16:01:26 +02:00
|
|
|
{
|
|
|
|
int tpc = oNote->tpc();
|
2013-02-07 23:52:13 +01:00
|
|
|
if (up) {
|
2014-06-20 17:07:22 +02:00
|
|
|
if (tpc > Tpc::TPC_A + int(key)) {
|
2013-02-07 23:52:13 +01:00
|
|
|
if (pitch < 127) {
|
|
|
|
newPitch = pitch + 1;
|
2014-04-14 10:39:27 +02:00
|
|
|
setTpc(oNote, tpc - 5, newTpc1, newTpc2);
|
2013-02-07 23:52:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (pitch < 126) {
|
|
|
|
newPitch = pitch + 2;
|
2014-04-14 10:39:27 +02:00
|
|
|
setTpc(oNote, tpc + 2, newTpc1, newTpc2);
|
2013-02-07 23:52:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2014-06-20 17:07:22 +02:00
|
|
|
if (tpc > Tpc::TPC_C + int(key)) {
|
2013-02-07 23:52:13 +01:00
|
|
|
if (pitch > 1) {
|
|
|
|
newPitch = pitch - 2;
|
2014-04-14 10:39:27 +02:00
|
|
|
setTpc(oNote, tpc - 2, newTpc1, newTpc2);
|
2013-02-07 23:52:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (pitch > 0) {
|
|
|
|
newPitch = pitch - 1;
|
2014-04-14 10:39:27 +02:00
|
|
|
setTpc(oNote, tpc + 5, newTpc1, newTpc2);
|
2013-02-07 23:52:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-04-10 16:01:26 +02:00
|
|
|
}
|
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) {
|
2013-03-10 14:13:25 +01:00
|
|
|
// 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-06-18 06:59:56 +02:00
|
|
|
}
|
2014-04-10 13:13:37 +02:00
|
|
|
undoChangePitch(oNote, newPitch, newTpc1, newTpc2);
|
2013-03-10 14:13:25 +01:00
|
|
|
}
|
2014-06-18 06:59:56 +02:00
|
|
|
|
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
|
2015-02-13 01:09:01 +01:00
|
|
|
else if (staff->staffType()->group() == StaffGroup::TAB) {
|
2012-09-19 14:15:49 +02:00
|
|
|
bool refret = false;
|
|
|
|
if (oNote->string() != string) {
|
2014-05-26 18:18:01 +02:00
|
|
|
undoChangeProperty(oNote, P_ID::STRING, string);
|
2012-09-19 14:15:49 +02:00
|
|
|
refret = true;
|
|
|
|
}
|
|
|
|
if (oNote->fret() != fret) {
|
2014-05-26 18:18:01 +02:00
|
|
|
undoChangeProperty(oNote, P_ID::FRET, fret);
|
2012-09-19 14:15:49 +02:00
|
|
|
refret = true;
|
|
|
|
}
|
2014-03-26 11:02:23 +01:00
|
|
|
if (refret) {
|
2015-03-13 11:16:43 +01:00
|
|
|
const StringData* stringData = part->instrument()->stringData();
|
2013-09-15 18:43:48 +02:00
|
|
|
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
|
|
|
}
|
2014-06-18 06:59:56 +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)
|
|
|
|
{
|
2015-03-19 09:57:41 +01:00
|
|
|
QSet<Chord*> set;
|
2012-05-26 14:26:10 +02:00
|
|
|
foreach(Element* el, selection().elements()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (el->type() == Element::Type::NOTE || el->type() == Element::Type::CHORD) {
|
2015-03-19 09:57:41 +01:00
|
|
|
Chord* cr = nullptr;
|
|
|
|
// apply articulation on a given chord only once
|
|
|
|
if (el->type() == Element::Type::NOTE) {
|
2016-06-14 10:32:34 +02:00
|
|
|
cr = toNote(el)->chord();
|
2015-03-19 09:57:41 +01:00
|
|
|
if (set.contains(cr))
|
|
|
|
continue;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
Articulation* na = new Articulation(this);
|
2013-03-05 20:23:59 +01:00
|
|
|
na->setArticulationType(attr);
|
2013-02-21 14:28:16 +01:00
|
|
|
if (!addArticulation(el, na))
|
|
|
|
delete na;
|
2015-03-19 09:57:41 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-09-02 12:13:46 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// changeAccidental2
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-11-27 11:51:16 +01:00
|
|
|
static void changeAccidental2(Note* n, int pitch, int tpc)
|
2013-09-02 12:13:46 +02:00
|
|
|
{
|
|
|
|
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
|
|
|
|
//
|
2015-03-13 11:16:43 +01:00
|
|
|
const StringData* stringData = n->part()->instrument()->stringData();
|
2013-09-15 18:43:48 +02:00
|
|
|
if (stringData)
|
2015-02-13 01:09:01 +01:00
|
|
|
stringData->convertPitch(pitch, st, chord->tick(), &string, &fret);
|
2013-09-02 12:13:46 +02:00
|
|
|
}
|
|
|
|
}
|
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;
|
|
|
|
}
|
2016-06-16 21:31:37 +02:00
|
|
|
|
2013-09-02 12:13:46 +02:00
|
|
|
if (!st->isTabStaff()) {
|
|
|
|
//
|
|
|
|
// handle ties
|
|
|
|
//
|
|
|
|
if (n->tieBack()) {
|
2016-06-16 21:31:37 +02:00
|
|
|
if (pitch != n->pitch()) {
|
|
|
|
score->undoRemoveElement(n->tieBack());
|
|
|
|
if (n->tieFor())
|
|
|
|
score->undoRemoveElement(n->tieFor());
|
|
|
|
}
|
2013-09-02 12:13:46 +02:00
|
|
|
}
|
|
|
|
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));
|
2013-09-02 12:13:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-06-16 21:31:37 +02:00
|
|
|
score->undoChangePitch(n, pitch, tpc1, tpc2);
|
2013-09-02 12:13:46 +02:00
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
2015-01-24 11:30:37 +01:00
|
|
|
Chord* chord = note->chord();
|
2014-08-23 18:13:27 +02:00
|
|
|
if (!chord)
|
|
|
|
return;
|
2012-05-26 14:26:10 +02:00
|
|
|
Segment* segment = chord->segment();
|
2014-08-23 18:13:27 +02:00
|
|
|
if (!segment)
|
|
|
|
return;
|
2012-05-26 14:26:10 +02:00
|
|
|
Measure* measure = segment->measure();
|
2014-08-23 18:13:27 +02:00
|
|
|
if (!measure)
|
|
|
|
return;
|
2015-01-24 11:30:37 +01:00
|
|
|
int tick = segment->tick();
|
|
|
|
Staff* estaff = staff(chord->staffIdx() + chord->staffMove());
|
2014-08-23 18:13:27 +02:00
|
|
|
if (!estaff)
|
|
|
|
return;
|
2015-01-24 11:30:37 +01:00
|
|
|
ClefType clef = estaff->clef(tick);
|
|
|
|
int step = ClefInfo::pitchOffset(clef) - note->line();
|
2012-05-26 14:26:10 +02:00
|
|
|
while (step < 0)
|
|
|
|
step += 7;
|
|
|
|
step %= 7;
|
|
|
|
//
|
|
|
|
// accidental change may result in pitch change
|
|
|
|
//
|
2014-04-14 10:39:27 +02:00
|
|
|
AccidentalVal acc2 = measure->findAccidental(note);
|
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);
|
2014-06-18 06:52:25 +02:00
|
|
|
|
|
|
|
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)
|
2014-06-18 06:52:25 +02:00
|
|
|
forceRemove = true;
|
|
|
|
|
|
|
|
// precautionary or microtonal accidental
|
|
|
|
// either way, we display it unconditionally
|
|
|
|
// both for this note and for any linked notes
|
2016-06-16 21:31:37 +02:00
|
|
|
else if (acc == acc2 || pitch == note->pitch() || accidental > AccidentalType::NATURAL)
|
2014-06-18 06:52:25 +02:00
|
|
|
forceAdd = true;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2015-03-31 17:34:54 +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();
|
2014-06-18 06:52:25 +02:00
|
|
|
if (forceRemove) {
|
|
|
|
if (a)
|
2015-03-31 17:34:54 +02:00
|
|
|
lns->undoRemoveElement(a);
|
2014-06-18 06:52:25 +02:00
|
|
|
}
|
|
|
|
else if (forceAdd) {
|
2015-01-24 11:30:37 +01:00
|
|
|
if (a)
|
|
|
|
undoRemoveElement(a);
|
2015-03-31 17:34:54 +02:00
|
|
|
Accidental* a = new Accidental(lns);
|
|
|
|
a->setParent(ln);
|
2014-06-18 06:52:25 +02:00
|
|
|
a->setAccidentalType(accidental);
|
2015-04-02 10:33:53 +02:00
|
|
|
a->setRole(AccidentalRole::USER);
|
2015-03-31 17:34:54 +02:00
|
|
|
lns->undoAddElement(a);
|
2014-06-18 06:52:25 +02:00
|
|
|
}
|
2015-03-31 17:34:54 +02:00
|
|
|
changeAccidental2(ln, pitch, tpc);
|
2014-06-18 06:52:25 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// addArticulation
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-02-21 14:28:16 +01:00
|
|
|
bool Score::addArticulation(Element* el, Articulation* a)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
ChordRest* cr;
|
2014-06-24 18:36:02 +02:00
|
|
|
if (el->type() == Element::Type::NOTE)
|
2016-06-14 10:32:34 +02:00
|
|
|
cr = toNote(el)->chord();
|
|
|
|
else if (el->isRest() || el->isChord() || el->isRepeatMeasure())
|
|
|
|
cr = toChordRest(el);
|
2013-02-21 14:28:16 +01:00
|
|
|
else
|
|
|
|
return false;
|
|
|
|
Articulation* oa = cr->hasArticulation(a);
|
2012-05-26 14:26:10 +02:00
|
|
|
if (oa) {
|
|
|
|
undoRemoveElement(oa);
|
2013-02-21 14:28:16 +01:00
|
|
|
return false;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2013-02-21 14:28:16 +01:00
|
|
|
a->setParent(cr);
|
2015-06-29 09:43:36 +02:00
|
|
|
a->setTrack(cr->track()); // make sure it propagates between score and parts
|
2013-02-21 14:28:16 +01:00
|
|
|
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
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-10-24 01:28:57 +02:00
|
|
|
void Score::moveUp(ChordRest* cr)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2014-10-24 01:28:57 +02:00
|
|
|
Staff* staff = cr->staff();
|
2014-03-30 21:07:37 +02:00
|
|
|
Part* part = staff->part();
|
|
|
|
int rstaff = staff->rstaff();
|
2014-10-24 01:28:57 +02:00
|
|
|
int staffMove = cr->staffMove();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
if ((staffMove == -1) || (rstaff + staffMove <= 0))
|
|
|
|
return;
|
|
|
|
|
2014-03-30 21:07:37 +02:00
|
|
|
QList<Staff*>* staves = part->staves();
|
|
|
|
// we know that staffMove+rstaff-1 index exists due to the previous condition.
|
2014-05-27 13:30:23 +02:00
|
|
|
if (staff->staffType()->group() != StaffGroup::STANDARD ||
|
|
|
|
staves->at(rstaff+staffMove-1)->staffType()->group() != StaffGroup::STANDARD) {
|
2014-03-30 21:07:37 +02:00
|
|
|
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
|
2014-10-24 01:28:57 +02:00
|
|
|
undo(new ChangeChordStaffMove(cr, staffMove - 1));
|
2014-03-30 21:07:37 +02:00
|
|
|
}
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// moveDown
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-10-24 01:28:57 +02:00
|
|
|
void Score::moveDown(ChordRest* cr)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2014-10-24 01:28:57 +02:00
|
|
|
Staff* staff = cr->staff();
|
2012-05-26 14:26:10 +02:00
|
|
|
Part* part = staff->part();
|
|
|
|
int rstaff = staff->rstaff();
|
2014-10-24 01:28:57 +02:00
|
|
|
int staffMove = cr->staffMove();
|
2014-03-30 21:07:37 +02:00
|
|
|
// 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)) {
|
2014-03-30 21:07:37 +02:00
|
|
|
qDebug("moveDown staffMove==%d rstaff %d rstaves %d", staffMove, rstaff, rstaves);
|
2012-05-26 14:26:10 +02:00
|
|
|
return;
|
|
|
|
}
|
2014-03-30 21:07:37 +02:00
|
|
|
|
|
|
|
QList<Staff*>* staves = part->staves();
|
|
|
|
// we know that staffMove+rstaff+1 index exists due to the previous condition.
|
2014-05-27 13:30:23 +02:00
|
|
|
if (staff->staffType()->group() != StaffGroup::STANDARD ||
|
|
|
|
staves->at(staffMove+rstaff+1)->staffType()->group() != StaffGroup::STANDARD) {
|
2014-03-30 21:07:37 +02:00
|
|
|
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
|
2014-10-24 01:28:57 +02:00
|
|
|
undo(new ChangeChordStaffMove(cr, staffMove + 1));
|
2014-03-30 21:07:37 +02:00
|
|
|
}
|
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;
|
2015-05-30 16:08:46 +02:00
|
|
|
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()
|
|
|
|
{
|
2016-03-02 22:08:20 +01:00
|
|
|
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;
|
|
|
|
}
|
2016-03-02 22:08:20 +01:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
int endTick = selection().tickEnd();
|
|
|
|
|
2016-03-02 22:08:20 +01:00
|
|
|
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) {
|
2016-06-14 10:32:34 +02:00
|
|
|
ChordRest* cr = toChordRest(seg->element(track));
|
2012-05-26 14:26:10 +02:00
|
|
|
if (cr == 0)
|
|
|
|
continue;
|
2014-06-24 18:36:02 +02:00
|
|
|
if (cr->type() == Element::Type::CHORD) {
|
2014-06-26 07:45:09 +02:00
|
|
|
if (cr->beamMode() != Beam::Mode::AUTO)
|
|
|
|
undoChangeProperty(cr, P_ID::BEAM_MODE, int(Beam::Mode::AUTO));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2014-06-24 18:36:02 +02:00
|
|
|
else if (cr->type() == Element::Type::REST) {
|
2014-06-26 07:45:09 +02:00
|
|
|
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()
|
|
|
|
{
|
2016-04-19 14:23:50 +02:00
|
|
|
if (midiInputQueue()->empty())
|
2012-05-26 14:26:10 +02:00
|
|
|
return false;
|
|
|
|
|
2016-04-18 18:11:51 +02:00
|
|
|
if (MScore::debugMode)
|
|
|
|
qDebug("processMidiInput");
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
bool cmdActive = false;
|
2016-04-19 14:23:50 +02:00
|
|
|
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())
|
2012-11-04 09:49:08 +01:00
|
|
|
p = staff(0)->part();
|
2012-05-26 14:26:10 +02:00
|
|
|
else
|
|
|
|
p = staff(staffIdx)->part();
|
2014-08-02 15:09:31 +02:00
|
|
|
if (p) {
|
|
|
|
if (!styleB(StyleIdx::concertPitch)) {
|
2015-03-13 11:16:43 +01:00
|
|
|
ev.pitch += p->instrument(selection().tickStart())->transpose().chromatic;
|
2016-04-18 18:11:51 +02:00
|
|
|
}
|
2014-08-02 15:09:31 +02:00
|
|
|
MScore::seq->startNote(
|
2015-03-13 11:16:43 +01:00
|
|
|
p->instrument()->channel(0)->channel,
|
2014-08-02 15:09:31 +02:00
|
|
|
ev.pitch,
|
2015-04-20 00:24:35 +02:00
|
|
|
ev.velocity,
|
2014-08-02 15:09:31 +02:00
|
|
|
0.0);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else {
|
2015-04-20 00:24:35 +02:00
|
|
|
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
|
|
|
|
2014-12-12 22:19:16 +01:00
|
|
|
// if transposing, interpret MIDI pitch as representing desired written pitch
|
|
|
|
// set pitch based on corresponding sounding pitch
|
|
|
|
if (!styleB(StyleIdx::concertPitch))
|
2015-03-13 11:16:43 +01:00
|
|
|
nval.pitch += st->part()->instrument(inputState().tick())->transpose().chromatic;
|
2014-12-12 22:19:16 +01:00
|
|
|
// 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;
|
2015-01-17 21:29:34 +01:00
|
|
|
if (noteEntryMode()) {
|
|
|
|
// if selection exists and is grace note, use it
|
|
|
|
// otherwise use chord/rest at input position
|
2015-03-08 19:30:58 +01:00
|
|
|
// also use it if we are moving to next chord
|
|
|
|
// to catch up with the cursor and not move the selection by 2 positions
|
2015-01-17 21:29:34 +01:00
|
|
|
cr = selection().cr();
|
2016-05-17 22:14:29 +02:00
|
|
|
if (cr && (cr->isGrace() || cmd == "next-chord" || cmd == "prev-chord"))
|
2015-01-17 21:29:34 +01:00
|
|
|
;
|
|
|
|
else
|
|
|
|
cr = inputState().cr();
|
|
|
|
}
|
2014-08-18 02:05:57 +02:00
|
|
|
else if (selection().activeCR())
|
2012-05-26 14:26:10 +02:00
|
|
|
cr = selection().activeCR();
|
|
|
|
else
|
|
|
|
cr = selection().lastChordRest();
|
2012-12-27 22:07:16 +01:00
|
|
|
|
|
|
|
// 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())
|
2012-12-27 22:07:16 +01:00
|
|
|
return 0;
|
2014-07-30 11:32:46 +02:00
|
|
|
// retrieve last element of section list
|
|
|
|
Element* el = selection().elements().last();
|
|
|
|
Element* trg = 0;
|
|
|
|
|
2012-12-27 22:07:16 +01:00
|
|
|
// 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
|
2012-12-28 15:18:52 +01:00
|
|
|
int track = el->track(); // keep note of element track
|
2012-12-27 22:07:16 +01:00
|
|
|
el = el->parent();
|
2015-01-02 18:17:04 +01:00
|
|
|
// element with no parent (eg, a newly-added line) - no way to find context
|
|
|
|
if (!el)
|
|
|
|
return 0;
|
2012-12-27 22:07:16 +01:00
|
|
|
switch (el->type()) {
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::NOTE: // a note is a valid target
|
2012-12-27 22:07:16 +01:00
|
|
|
trg = el;
|
2016-05-17 22:14:29 +02:00
|
|
|
cr = toNote(el)->chord();
|
2012-12-27 22:07:16 +01:00
|
|
|
break;
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::CHORD: // a chord or a rest are valid targets
|
|
|
|
case Element::Type::REST:
|
2012-12-27 22:07:16 +01:00
|
|
|
trg = el;
|
2016-05-17 22:14:29 +02:00
|
|
|
cr = toChordRest(trg);
|
2012-12-27 22:07:16 +01:00
|
|
|
break;
|
2014-06-24 18:36:02 +02:00
|
|
|
case Element::Type::SEGMENT: { // from segment go to top chordrest in segment
|
2016-05-17 22:14:29 +02:00
|
|
|
Segment* seg = toSegment(el);
|
2012-12-27 22:07:16 +01:00
|
|
|
// if segment is not chord/rest or grace, move to next chord/rest or grace segment
|
2016-05-17 22:14:29 +02:00
|
|
|
if (!seg->isChordRest1()) {
|
2014-06-25 11:46:10 +02:00
|
|
|
seg = seg->next1(Segment::Type::ChordRest);
|
2015-01-02 18:17:04 +01:00
|
|
|
if (seg == 0) // if none found, return failure
|
2012-12-27 22:07:16 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2012-12-28 15:18:52 +01:00
|
|
|
// segment for sure contains chords/rests,
|
2012-12-27 22:07:16 +01:00
|
|
|
int size = seg->elist().size();
|
2012-12-28 15:18:52 +01:00
|
|
|
// 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)) {
|
2012-12-28 15:18:52 +01:00
|
|
|
trg = seg->element(track);
|
2016-05-17 22:14:29 +02:00
|
|
|
cr = toChordRest(trg);
|
2012-12-28 15:18:52 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
// if not, get topmost chord/rest
|
2012-12-27 22:07:16 +01:00
|
|
|
for (int i = 0; i < size; i++)
|
|
|
|
if (seg->element(i)) {
|
|
|
|
trg = seg->element(i);
|
2016-05-17 22:14:29 +02:00
|
|
|
cr = toChordRest(trg);
|
2012-12-27 22:07:16 +01:00
|
|
|
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
|
2014-06-24 18:36:02 +02:00
|
|
|
if (trg->type() == Element::Type::CHORD)
|
2016-05-17 22:14:29 +02:00
|
|
|
trg = toChord(trg)->upNote();
|
2016-03-18 09:29:16 +01:00
|
|
|
setPlayNote(true);
|
2014-05-27 10:34:08 +02:00
|
|
|
select(trg, SelectType::SINGLE, 0);
|
2012-12-27 22:07:16 +01:00
|
|
|
return trg;
|
|
|
|
}
|
|
|
|
// if no chordrest found, do nothing
|
2013-08-02 17:39:45 +02:00
|
|
|
if (cr == 0)
|
2012-12-27 22:07:16 +01:00
|
|
|
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);
|
2016-05-17 22:14:29 +02:00
|
|
|
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)
|
2016-05-17 22:14:29 +02:00
|
|
|
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()) {
|
2016-05-17 22:14:29 +02:00
|
|
|
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();
|
2016-05-17 22:14:29 +02:00
|
|
|
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;
|
2016-05-17 22:14:29 +02:00
|
|
|
}
|
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);
|
2016-05-17 22:14:29 +02:00
|
|
|
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)
|
2016-05-17 22:14:29 +02:00
|
|
|
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
|
|
|
}
|
2012-11-23 15:22:16 +01: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
|
|
|
}
|
2012-11-23 15:22:16 +01: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
|
|
|
}
|
2013-08-02 23:45:45 +02:00
|
|
|
else if (cmd == "next-track") {
|
|
|
|
el = nextTrack(cr);
|
2013-10-24 12:09:00 +02:00
|
|
|
if (noteEntryMode())
|
|
|
|
_is.moveInputPos(el);
|
2013-08-02 23:45:45 +02:00
|
|
|
}
|
|
|
|
else if (cmd == "prev-track") {
|
|
|
|
el = prevTrack(cr);
|
2013-10-24 12:09:00 +02:00
|
|
|
if (noteEntryMode())
|
|
|
|
_is.moveInputPos(el);
|
2013-08-02 23:45:45 +02:00
|
|
|
}
|
2015-08-18 23:00:07 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
if (el) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (el->type() == Element::Type::CHORD)
|
2016-05-17 22:14:29 +02:00
|
|
|
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()) {
|
2015-08-18 04:00:57 +02:00
|
|
|
// 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);
|
2015-08-19 04:32:26 +02:00
|
|
|
for (MuseScoreView* view : viewer)
|
2014-08-17 00:28:47 +02:00
|
|
|
view->moveCursor();
|
|
|
|
}
|
2015-08-18 04:00:57 +02:00
|
|
|
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")
|
2015-02-22 17:07:08 +01:00
|
|
|
el = nextChordRest(cr, true);
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (cmd == "select-prev-chord")
|
2015-02-22 17:07:08 +01:00
|
|
|
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") {
|
2015-01-28 04:25:49 +01:00
|
|
|
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") {
|
2015-01-28 04:25:49 +01:00
|
|
|
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)
|
2014-05-27 10:34:08 +02:00
|
|
|
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) {
|
2014-06-24 18:36:02 +02:00
|
|
|
if (e->type() == Element::Type::NOTE) {
|
2016-06-14 10:32:34 +02:00
|
|
|
Note* note = toNote(e);
|
2012-08-16 11:09:36 +02:00
|
|
|
if (note->staff() && note->staff()->isTabStaff())
|
2015-02-01 11:06:13 +01:00
|
|
|
note->score()->undoChangeProperty(e, P_ID::GHOST, !note->ghost());
|
2012-05-26 14:26:10 +02:00
|
|
|
else {
|
2014-06-26 10:53:57 +02:00
|
|
|
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
|
2014-06-26 10:53:57 +02:00
|
|
|
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;
|
2014-06-24 18:36:02 +02:00
|
|
|
if (el->type() == Element::Type::NOTE)
|
2012-05-26 14:26:10 +02:00
|
|
|
el = el->parent();
|
|
|
|
if (!el->isChordRest())
|
|
|
|
return;
|
|
|
|
|
2016-06-14 10:32:34 +02:00
|
|
|
ChordRest* cr = toChordRest(el);
|
2012-05-26 14:26:10 +02:00
|
|
|
TDuration d = _is.duration().shift(1);
|
2016-06-12 07:55:06 +02:00
|
|
|
if (!d.isValid())
|
2012-05-26 14:26:10 +02:00
|
|
|
return;
|
2016-06-14 10:32:34 +02:00
|
|
|
if (cr->type() == Element::Type::CHORD && (toChord(cr)->noteType() != NoteType::NORMAL)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
// handle appoggiatura and acciaccatura
|
|
|
|
//
|
2014-11-29 08:27:07 +01:00
|
|
|
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;
|
2014-06-24 18:36:02 +02:00
|
|
|
if (el->type() == Element::Type::NOTE)
|
2012-05-26 14:26:10 +02:00
|
|
|
el = el->parent();
|
|
|
|
if (!el->isChordRest())
|
|
|
|
return;
|
|
|
|
|
2016-06-14 10:32:34 +02:00
|
|
|
ChordRest* cr = toChordRest(el);
|
2012-05-26 14:26:10 +02:00
|
|
|
TDuration d = _is.duration().shift(-1);
|
2016-06-12 07:55:06 +02:00
|
|
|
if (!d.isValid())
|
2012-05-26 14:26:10 +02:00
|
|
|
return;
|
2016-06-14 10:32:34 +02:00
|
|
|
if (cr->type() == Element::Type::CHORD && (toChord(cr)->noteType() != NoteType::NORMAL)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
// handle appoggiatura and acciaccatura
|
|
|
|
//
|
2014-11-29 08:27:07 +01:00
|
|
|
undoChangeChordRestLen(cr, d);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
changeCRlen(cr, d);
|
|
|
|
_is.setDuration(d);
|
|
|
|
nextInputPos(cr, false);
|
|
|
|
}
|
|
|
|
|
2014-07-18 00:11:00 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdAddBracket
|
|
|
|
//---------------------------------------------------------
|
2014-07-25 17:13:27 +02:00
|
|
|
|
2014-07-18 00:11:00 +02:00
|
|
|
void Score::cmdAddBracket()
|
|
|
|
{
|
|
|
|
for(Element* el : selection().elements()) {
|
|
|
|
if (el->type() == Element::Type::NOTE) {
|
2016-06-14 10:32:34 +02:00
|
|
|
Note* n = toNote(el);
|
2014-07-18 00:11:00 +02:00
|
|
|
n->addBracket();
|
|
|
|
}
|
|
|
|
else if (el->type() == Element::Type::ACCIDENTAL) {
|
2016-06-14 10:32:34 +02:00
|
|
|
Accidental* acc = toAccidental(el);
|
2014-07-18 00:11:00 +02:00
|
|
|
acc->undoSetHasBracket(true);
|
|
|
|
}
|
2014-07-18 00:25:19 +02:00
|
|
|
else if (el->type() == Element::Type::HARMONY) {
|
2016-06-14 10:32:34 +02:00
|
|
|
Harmony* h = toHarmony(el);
|
2014-07-18 00:25:19 +02:00
|
|
|
h->setLeftParen(true);
|
|
|
|
h->setRightParen(true);
|
|
|
|
h->render();
|
|
|
|
}
|
2014-07-18 00:11:00 +02:00
|
|
|
}
|
|
|
|
}
|
2014-07-25 17:13:27 +02:00
|
|
|
|
2014-07-18 00:11:00 +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();
|
2013-04-04 00:12:37 +02:00
|
|
|
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
|
2014-05-27 12:50:54 +02:00
|
|
|
upDown(true, UpDownMode::CHROMATIC);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2013-04-04 00:12:37 +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
|
2014-05-27 12:50:54 +02:00
|
|
|
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")
|
2014-05-26 21:55:26 +02:00
|
|
|
addArticulation(ArticulationType::Staccato);
|
2014-05-30 13:35:44 +02:00
|
|
|
else if (cmd == "add-tenuto")
|
2014-05-26 21:55:26 +02:00
|
|
|
addArticulation(ArticulationType::Tenuto);
|
2012-09-05 11:49:48 +02:00
|
|
|
else if (cmd == "add-marcato")
|
2014-05-26 21:55:26 +02:00
|
|
|
addArticulation(ArticulationType::Marcato);
|
2015-10-19 09:35:26 +02:00
|
|
|
else if (cmd == "add-sforzato")
|
|
|
|
addArticulation(ArticulationType::Sforzatoaccent);
|
2014-05-30 13:35:44 +02:00
|
|
|
else if (cmd == "add-trill")
|
2014-05-26 21:55:26 +02:00
|
|
|
addArticulation(ArticulationType::Trill);
|
2013-08-12 10:37:30 +02:00
|
|
|
else if (cmd == "add-8va")
|
2014-06-25 22:21:01 +02:00
|
|
|
cmdAddOttava(Ottava::Type::OTTAVA_8VA);
|
2013-08-12 10:37:30 +02:00
|
|
|
else if (cmd == "add-8vb")
|
2014-06-25 22:21:01 +02:00
|
|
|
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
|
|
|
}
|
2013-08-26 14:45:01 +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()));
|
2013-08-26 14:45:01 +02:00
|
|
|
else
|
2014-05-27 12:50:54 +02:00
|
|
|
upDown(true, UpDownMode::OCTAVE);
|
2013-08-26 14:45:01 +02:00
|
|
|
}
|
|
|
|
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()));
|
2013-08-26 14:45:01 +02:00
|
|
|
else
|
2014-05-27 12:50:54 +02:00
|
|
|
upDown(false, UpDownMode::OCTAVE);
|
2013-08-26 14:45:01 +02:00
|
|
|
}
|
2013-04-14 21:27:56 +02:00
|
|
|
else if (cmd == "note-longa" || cmd == "note-longa-TAB")
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE00);
|
2013-04-14 21:27:56 +02:00
|
|
|
else if (cmd == "note-breve" || cmd == "note-breve-TAB")
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE0);
|
2013-04-04 00:12:37 +02:00
|
|
|
else if (cmd == "pad-note-1" || cmd == "pad-note-1-TAB")
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE1);
|
2013-04-04 00:12:37 +02:00
|
|
|
else if (cmd == "pad-note-2" || cmd == "pad-note-2-TAB")
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE2);
|
2013-04-04 00:12:37 +02:00
|
|
|
else if (cmd == "pad-note-4" || cmd == "pad-note-4-TAB")
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE4);
|
2013-04-04 00:12:37 +02:00
|
|
|
else if (cmd == "pad-note-8" || cmd == "pad-note-8-TAB")
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE8);
|
2013-04-04 00:12:37 +02:00
|
|
|
else if (cmd == "pad-note-16" || cmd == "pad-note-16-TAB")
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE16);
|
2013-04-04 00:12:37 +02:00
|
|
|
else if (cmd == "pad-note-32" || cmd == "pad-note-32-TAB")
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE32);
|
2013-04-04 00:12:37 +02:00
|
|
|
else if (cmd == "pad-note-64" || cmd == "pad-note-64-TAB")
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE64);
|
2013-04-04 00:12:37 +02:00
|
|
|
else if (cmd == "pad-note-128" || cmd == "pad-note-128-TAB")
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE128);
|
2013-04-14 21:27:56 +02:00
|
|
|
else if (cmd == "pad-note-increase-TAB") {
|
2013-03-28 11:54:39 +01:00
|
|
|
switch (_is.duration().type() ) {
|
|
|
|
// cycle back from longest to shortest?
|
|
|
|
// case TDuration::V_LONG:
|
2014-05-30 10:16:58 +02:00
|
|
|
// padToggle(Pad::NOTE128);
|
2013-03-28 11:54:39 +01:00
|
|
|
// break;
|
2014-05-21 15:43:19 +02:00
|
|
|
case TDuration::DurationType::V_BREVE:
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE00);
|
2013-03-28 11:54:39 +01:00
|
|
|
break;
|
2014-05-21 15:43:19 +02:00
|
|
|
case TDuration::DurationType::V_WHOLE:
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE0);
|
2013-03-28 11:54:39 +01:00
|
|
|
break;
|
2014-05-21 15:43:19 +02:00
|
|
|
case TDuration::DurationType::V_HALF:
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE1);
|
2013-03-28 11:54:39 +01:00
|
|
|
break;
|
2014-05-21 15:43:19 +02:00
|
|
|
case TDuration::DurationType::V_QUARTER:
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE2);
|
2013-03-28 11:54:39 +01:00
|
|
|
break;
|
2014-08-15 18:32:48 +02:00
|
|
|
case TDuration::DurationType::V_EIGHTH:
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE4);
|
2013-03-28 11:54:39 +01:00
|
|
|
break;
|
2014-05-21 15:43:19 +02:00
|
|
|
case TDuration::DurationType::V_16TH:
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE8);
|
2013-03-28 11:54:39 +01:00
|
|
|
break;
|
2014-05-21 15:43:19 +02:00
|
|
|
case TDuration::DurationType::V_32ND:
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE16);
|
2013-03-28 11:54:39 +01:00
|
|
|
break;
|
2014-05-21 15:43:19 +02:00
|
|
|
case TDuration::DurationType::V_64TH:
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE32);
|
2013-03-28 11:54:39 +01:00
|
|
|
break;
|
2014-05-21 15:43:19 +02:00
|
|
|
case TDuration::DurationType::V_128TH:
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE64);
|
2013-03-28 11:54:39 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-04-14 21:27:56 +02:00
|
|
|
else if (cmd == "pad-note-decrease-TAB") {
|
2013-03-28 11:54:39 +01:00
|
|
|
switch (_is.duration().type() ) {
|
2014-05-21 15:43:19 +02:00
|
|
|
case TDuration::DurationType::V_LONG:
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE0);
|
2013-03-28 11:54:39 +01:00
|
|
|
break;
|
2014-05-21 15:43:19 +02:00
|
|
|
case TDuration::DurationType::V_BREVE:
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE1);
|
2013-03-28 11:54:39 +01:00
|
|
|
break;
|
2014-05-21 15:43:19 +02:00
|
|
|
case TDuration::DurationType::V_WHOLE:
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE2);
|
2013-03-28 11:54:39 +01:00
|
|
|
break;
|
2014-05-21 15:43:19 +02:00
|
|
|
case TDuration::DurationType::V_HALF:
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE4);
|
2013-03-28 11:54:39 +01:00
|
|
|
break;
|
2014-05-21 15:43:19 +02:00
|
|
|
case TDuration::DurationType::V_QUARTER:
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE8);
|
2013-03-28 11:54:39 +01:00
|
|
|
break;
|
2014-08-15 18:32:48 +02:00
|
|
|
case TDuration::DurationType::V_EIGHTH:
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE16);
|
2013-03-28 11:54:39 +01:00
|
|
|
break;
|
2014-05-21 15:43:19 +02:00
|
|
|
case TDuration::DurationType::V_16TH:
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE32);
|
2013-03-28 11:54:39 +01:00
|
|
|
break;
|
2014-05-21 15:43:19 +02:00
|
|
|
case TDuration::DurationType::V_32ND:
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE64);
|
2013-03-28 11:54:39 +01:00
|
|
|
break;
|
2014-05-21 15:43:19 +02:00
|
|
|
case TDuration::DurationType::V_64TH:
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::NOTE128);
|
2013-03-28 11:54:39 +01:00
|
|
|
break;
|
|
|
|
// cycle back from shortest to longest?
|
2014-05-21 15:43:19 +02:00
|
|
|
// case TDuration::DurationType::V_128TH:
|
2014-05-30 10:16:58 +02:00
|
|
|
// padToggle(Pad::NOTE00);
|
2013-03-28 11:54:39 +01:00
|
|
|
// break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (cmd == "pad-rest")
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::REST);
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (cmd == "pad-dot")
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::DOT);
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (cmd == "pad-dotdot")
|
2014-05-30 10:16:58 +02:00
|
|
|
padToggle(Pad::DOTDOT);
|
2016-05-31 19:48:28 +02:00
|
|
|
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")
|
2014-06-26 07:45:09 +02:00
|
|
|
cmdSetBeamMode(Beam::Mode::BEGIN);
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (cmd == "beam-mid")
|
2014-06-26 07:45:09 +02:00
|
|
|
cmdSetBeamMode(Beam::Mode::MID);
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (cmd == "no-beam")
|
2014-06-26 07:45:09 +02:00
|
|
|
cmdSetBeamMode(Beam::Mode::NONE);
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (cmd == "beam-32")
|
2014-06-26 07:45:09 +02:00
|
|
|
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);
|
2016-07-04 09:29:57 +02:00
|
|
|
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") {
|
2014-06-25 13:57:29 +02:00
|
|
|
LayoutBreak::Type type;
|
2012-05-26 14:26:10 +02:00
|
|
|
if (cmd == "system-break")
|
2014-06-25 13:57:29 +02:00
|
|
|
type = LayoutBreak::Type::LINE;
|
2012-05-26 14:26:10 +02:00
|
|
|
else if (cmd == "page-break")
|
2014-06-25 13:57:29 +02:00
|
|
|
type = LayoutBreak::Type::PAGE;
|
2012-05-26 14:26:10 +02:00
|
|
|
else
|
2014-06-25 13:57:29 +02:00
|
|
|
type = LayoutBreak::Type::SECTION;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2014-06-24 18:36:02 +02:00
|
|
|
if (el && el->type() == Element::Type::BAR_LINE && el->parent()->type() == Element::Type::SEGMENT) {
|
2016-06-14 10:32:34 +02:00
|
|
|
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);
|
2013-09-28 11:24:00 +02:00
|
|
|
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));
|
2013-09-28 11:24:00 +02:00
|
|
|
}
|
2014-09-23 18:07:32 +02:00
|
|
|
else if (cmd == "add-brackets")
|
2014-07-18 00:11:00 +02:00
|
|
|
cmdAddBracket();
|
2015-01-17 22:40:28 +01:00
|
|
|
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();
|
2014-11-15 21:28:16 +01:00
|
|
|
else if (cmd == "slash-fill")
|
|
|
|
cmdSlashFill();
|
|
|
|
else if (cmd == "slash-rhythm")
|
|
|
|
cmdSlashRhythm();
|
2014-12-09 08:45:30 +01:00
|
|
|
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;
|
2015-02-12 11:41:39 +01:00
|
|
|
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
|
|
|
|
|
|
|
//
|
2015-01-31 21:41:55 +01: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();
|
2014-08-02 19:49:15 +02:00
|
|
|
Segment* cs = s->prev();
|
2014-08-06 10:25:17 +02:00
|
|
|
int tick = s->tick();
|
2015-01-31 21:41:55 +01:00
|
|
|
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));
|
|
|
|
}
|
2016-06-14 10:32:34 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-01-17 22:40:28 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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) {
|
2016-06-14 10:32:34 +02:00
|
|
|
Note* n = toNote(e);
|
2015-01-17 22:40:28 +01:00
|
|
|
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);
|
2014-12-04 16:45:45 +01:00
|
|
|
if (e && e->type() == Element::Type::CHORD) {
|
2016-06-14 10:32:34 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-12-04 16:45:45 +01:00
|
|
|
// 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
|
2015-01-07 06:18:15 +01:00
|
|
|
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;
|
2016-06-14 10:32:34 +02:00
|
|
|
ChordRest* cr = toChordRest(firstCRSegment->element(track));
|
2014-11-09 02:02:37 +01:00
|
|
|
if (cr) {
|
2014-12-04 16:45:45 +01:00
|
|
|
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) {
|
2016-06-14 10:32:34 +02:00
|
|
|
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
|
2014-12-04 16:45:45 +01:00
|
|
|
/// for single staff, merge voices
|
2014-11-09 02:02:37 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdImplode()
|
|
|
|
{
|
|
|
|
if (!selection().isRange())
|
|
|
|
return;
|
|
|
|
|
|
|
|
int dstStaff = selection().staffStart();
|
2014-12-04 16:45:45 +01:00
|
|
|
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) {
|
2016-06-14 10:32:34 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2014-12-04 16:45:45 +01:00
|
|
|
// 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) {
|
2016-06-14 10:32:34 +02:00
|
|
|
Chord* srcChord = toChord(src);
|
2014-12-06 15:55:26 +01:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-12-04 16:45:45 +01:00
|
|
|
// delete chordrest from source track if possible
|
|
|
|
if (src && src->voice())
|
|
|
|
undoRemoveElement(src);
|
|
|
|
}
|
|
|
|
}
|
2014-12-06 15:55:26 +01:00
|
|
|
else if (dst && trackInc == 1) {
|
|
|
|
// destination track has something, but it isn't a chord
|
|
|
|
// remove everything from other voices if in "voice mode"
|
2014-12-04 16:45:45 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-11-15 21:28:16 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdSlashFill
|
|
|
|
/// fills selected region with slashes
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdSlashFill()
|
|
|
|
{
|
|
|
|
int startStaff = selection().staffStart();
|
|
|
|
int endStaff = selection().staffEnd();
|
|
|
|
Segment* startSegment = selection().startSegment();
|
2014-12-13 17:48:12 +01:00
|
|
|
if (!startSegment) // empty score?
|
|
|
|
return;
|
|
|
|
|
2014-11-15 21:28:16 +01:00
|
|
|
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) {
|
2014-12-31 02:32:58 +01:00
|
|
|
int track = staffIdx * VOICES;
|
|
|
|
int voice = -1;
|
2014-11-15 21:28:16 +01:00
|
|
|
// 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
|
2015-03-16 19:42:23 +01:00
|
|
|
if (s->rtick() % f.ticks())
|
2014-11-15 21:28:16 +01:00
|
|
|
continue;
|
2014-12-31 02:32:58 +01:00
|
|
|
// 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;
|
2016-06-14 10:32:34 +02:00
|
|
|
ChordRest* cr = toChordRest(s->element(track + voice));
|
2014-12-31 02:32:58 +01:00
|
|
|
// 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)) {
|
2016-06-14 10:32:34 +02:00
|
|
|
ChordRest* ncr = toChordRest(ns->element(track + voice));
|
2014-12-31 02:32:58 +01:00
|
|
|
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());
|
|
|
|
}
|
2014-11-15 21:28:16 +01:00
|
|
|
// construct note
|
|
|
|
int line = 0;
|
|
|
|
bool error = false;
|
|
|
|
NoteVal nv;
|
|
|
|
if (staff(staffIdx)->staffType()->group() == StaffGroup::TAB)
|
|
|
|
line = staff(staffIdx)->lines() / 2;
|
|
|
|
else
|
2015-09-01 06:19:20 +02:00
|
|
|
line = staff(staffIdx)->middleLine(); // staff(staffIdx)->lines() - 1;
|
2014-11-15 21:28:16 +01:00
|
|
|
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
|
2014-12-31 02:32:58 +01:00
|
|
|
s = setNoteRest(s, track + voice, nv, f);
|
2016-06-14 10:32:34 +02:00
|
|
|
Chord* c = toChord(s->element(track + voice));
|
2014-11-15 21:28:16 +01:00
|
|
|
if (c->links()) {
|
2015-08-18 18:15:29 +02:00
|
|
|
for (ScoreElement* e : *c->links()) {
|
2014-11-15 21:28:16 +01:00
|
|
|
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
|
|
|
|
2014-11-15 21:28:16 +01:00
|
|
|
void Score::cmdSlashRhythm()
|
|
|
|
{
|
|
|
|
QList<Chord*> chords;
|
|
|
|
// loop through all notes in selection
|
|
|
|
foreach (Element* e, selection().elements()) {
|
2014-11-30 07:47:00 +01:00
|
|
|
if (e->voice() >= 2 && e->type() == Element::Type::REST) {
|
2016-06-14 10:32:34 +02:00
|
|
|
Rest* r = toRest(e);
|
2015-08-18 18:15:29 +02:00
|
|
|
if (r->links()) {
|
|
|
|
for (ScoreElement* e : *r->links()) {
|
|
|
|
Rest* lr = static_cast<Rest*>(e);
|
|
|
|
lr->setAccent(!lr->accent());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
r->setAccent(!r->accent());
|
2014-11-15 21:28:16 +01:00
|
|
|
continue;
|
2014-11-30 07:47:00 +01:00
|
|
|
}
|
|
|
|
else if (e->type() == Element::Type::NOTE) {
|
2016-06-14 10:32:34 +02:00
|
|
|
Note* n = toNote(e);
|
2014-11-30 07:47:00 +01:00
|
|
|
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
|
2015-08-18 18:15:29 +02:00
|
|
|
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);
|
2014-11-30 07:47:00 +01:00
|
|
|
}
|
2014-11-15 21:28:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-09 08:45:30 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdResequenceRehearsalMarks
|
2014-12-13 17:48:12 +01:00
|
|
|
/// resequences rehearsal marks within a range selection
|
|
|
|
/// or, if nothing is selected, the entire score
|
2014-12-09 08:45:30 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdResequenceRehearsalMarks()
|
|
|
|
{
|
2015-03-05 19:42:51 +01:00
|
|
|
bool noSelection = !selection().isRange();
|
2014-12-13 17:48:12 +01:00
|
|
|
|
|
|
|
if (noSelection)
|
2014-12-09 08:45:30 +01:00
|
|
|
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) {
|
2016-06-14 10:32:34 +02:00
|
|
|
RehearsalMark* rm = toRehearsalMark(e);
|
2014-12-09 08:45:30 +01:00
|
|
|
if (last) {
|
|
|
|
QString rmText = nextRehearsalMarkText(last, rm);
|
2015-02-12 11:41:39 +01:00
|
|
|
for (ScoreElement* le : rm->linkList())
|
2014-12-09 08:45:30 +01:00
|
|
|
le->undoChangeProperty(P_ID::TEXT, rmText);
|
|
|
|
}
|
|
|
|
last = rm;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-12-13 17:48:12 +01:00
|
|
|
|
|
|
|
if (noSelection)
|
2015-05-30 16:08:46 +02:00
|
|
|
deselectAll();
|
2014-12-09 08:45:30 +01:00
|
|
|
}
|
|
|
|
|
2015-05-09 18:53:12 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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
|
2015-05-09 18:53:12 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
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())
|
2015-05-30 16:08:46 +02:00
|
|
|
m->undoSetLineBreak(false);
|
2015-05-09 18:53:12 +02:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-11-15 21:28:16 +01:00
|
|
|
}
|