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>
|
|
|
|
|
2018-01-16 13:38:17 +01:00
|
|
|
#include "types.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 "undo.h"
|
|
|
|
#include "timesig.h"
|
|
|
|
#include "repeat.h"
|
|
|
|
#include "tempotext.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"
|
2016-10-06 12:21:28 +02:00
|
|
|
#include "sym.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()
|
|
|
|
{
|
2017-05-12 20:16:02 +02:00
|
|
|
layoutFlags = LayoutFlag::NO_FLAGS;
|
|
|
|
_updateMode = UpdateMode::DoNothing;
|
2019-01-30 15:13:54 +01:00
|
|
|
_startTick = Fraction(-1,1);
|
|
|
|
_endTick = Fraction(-1,1);
|
2019-10-24 15:49:23 +02:00
|
|
|
|
|
|
|
_startStaff = -1;
|
|
|
|
_endStaff = -1;
|
|
|
|
_el = nullptr;
|
|
|
|
_oneElement = true;
|
2019-12-04 01:22:55 +01:00
|
|
|
_mb = nullptr;
|
|
|
|
_oneMeasureBase = true;
|
2019-10-24 15:49:23 +02:00
|
|
|
_locked = false;
|
2016-03-02 13:20:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// setTick
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
void CmdState::setTick(const Fraction& t)
|
2016-03-02 13:20:19 +01:00
|
|
|
{
|
2019-10-24 15:49:23 +02:00
|
|
|
if (_locked)
|
|
|
|
return;
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
if (_startTick == Fraction(-1,1) || t < _startTick)
|
2016-03-02 13:20:19 +01:00
|
|
|
_startTick = t;
|
2019-01-30 15:13:54 +01:00
|
|
|
if (_endTick == Fraction(-1,1) || t > _endTick)
|
2016-03-02 13:20:19 +01:00
|
|
|
_endTick = t;
|
2017-01-05 11:23:47 +01:00
|
|
|
setUpdateMode(UpdateMode::Layout);
|
2016-03-02 13:20:19 +01:00
|
|
|
}
|
|
|
|
|
2019-10-24 15:49:23 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// setStaff
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void CmdState::setStaff(int st)
|
|
|
|
{
|
2019-11-22 15:17:51 +01:00
|
|
|
Q_ASSERT(st > -2);
|
2019-10-24 15:49:23 +02:00
|
|
|
if (_locked || st == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (_startStaff == -1 || st < _startStaff)
|
|
|
|
_startStaff = st;
|
|
|
|
if (_endStaff == -1 || st > _endStaff)
|
|
|
|
_endStaff = st;
|
|
|
|
}
|
|
|
|
|
2019-12-04 01:22:55 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// setMeasureBase
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void CmdState::setMeasureBase(const MeasureBase* mb)
|
|
|
|
{
|
|
|
|
if (!mb || _mb == mb || _locked)
|
|
|
|
return;
|
|
|
|
|
|
|
|
_oneMeasureBase = !_mb;
|
|
|
|
_mb = mb;
|
|
|
|
}
|
|
|
|
|
2019-10-24 15:49:23 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// setElement
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void CmdState::setElement(const Element* e)
|
|
|
|
{
|
|
|
|
if (!e || _el == e || _locked)
|
|
|
|
return;
|
|
|
|
|
2019-12-04 01:22:55 +01:00
|
|
|
_oneElement = !_el;
|
2019-10-24 15:49:23 +02:00
|
|
|
_el = e;
|
2019-12-04 01:22:55 +01:00
|
|
|
|
|
|
|
if (_oneMeasureBase)
|
|
|
|
setMeasureBase(e->findMeasureBase());
|
2019-10-24 15:49:23 +02:00
|
|
|
}
|
|
|
|
|
2016-03-02 13:20:19 +01:00
|
|
|
//---------------------------------------------------------
|
2019-11-06 14:05:59 +01:00
|
|
|
// unsetElement
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void CmdState::unsetElement(const Element* e)
|
|
|
|
{
|
|
|
|
if (_el == e)
|
|
|
|
_el = nullptr;
|
2019-12-04 01:22:55 +01:00
|
|
|
if (_mb == e)
|
|
|
|
_mb = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// element
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
const Element* CmdState::element() const
|
|
|
|
{
|
|
|
|
if (_oneElement)
|
|
|
|
return _el;
|
|
|
|
if (_oneMeasureBase)
|
|
|
|
return _mb;
|
|
|
|
return nullptr;
|
2019-11-06 14:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2016-03-02 13:20:19 +01:00
|
|
|
// setUpdateMode
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2017-02-09 14:57:40 +01:00
|
|
|
void CmdState::_setUpdateMode(UpdateMode m)
|
|
|
|
{
|
|
|
|
_updateMode = m;
|
|
|
|
}
|
|
|
|
|
2016-03-02 13:20:19 +01:00
|
|
|
void CmdState::setUpdateMode(UpdateMode m)
|
|
|
|
{
|
|
|
|
if (int(m) > int(_updateMode))
|
2017-02-09 14:57:40 +01:00
|
|
|
_setUpdateMode(m);
|
2016-03-02 13:20:19 +01:00
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// startCmd
|
|
|
|
/// Start a GUI command by clearing the redraw area
|
2018-10-14 17:58:07 +02:00
|
|
|
/// and starting a user-visible undo.
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2018-12-12 13:56:07 +01:00
|
|
|
undoStack()->beginMacro(this);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2016-06-03 10:17:06 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// undoRedo
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2017-06-13 17:23:11 +02:00
|
|
|
void Score::undoRedo(bool undo, EditData* ed)
|
2016-06-03 10:17:06 +02:00
|
|
|
{
|
2018-08-03 01:15:56 +02:00
|
|
|
if (readOnly())
|
|
|
|
return;
|
2016-06-03 10:17:06 +02:00
|
|
|
cmdState().reset();
|
|
|
|
if (undo)
|
2017-06-13 17:23:11 +02:00
|
|
|
undoStack()->undo(ed);
|
2016-06-03 10:17:06 +02:00
|
|
|
else
|
2017-06-13 17:23:11 +02:00
|
|
|
undoStack()->redo(ed);
|
2019-10-24 12:24:00 +02:00
|
|
|
update(false);
|
2019-05-27 12:26:38 +02:00
|
|
|
masterScore()->setPlaylistDirty(); // TODO: flag all individual operations
|
2016-06-03 10:17:06 +02:00
|
|
|
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;
|
|
|
|
}
|
2018-08-03 01:15:56 +02:00
|
|
|
if (readOnly() || MScore::_error != MS_NO_ERROR)
|
2016-11-28 17:25:26 +01:00
|
|
|
rollback = true;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
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
|
|
|
|
2019-10-24 12:24:00 +02:00
|
|
|
update(false);
|
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());
|
2018-12-12 13:56:07 +01:00
|
|
|
const bool noUndo = undoStack()->current()->empty(); // nothing to undo?
|
2016-03-10 10:41:31 +01:00
|
|
|
undoStack()->endMacro(noUndo);
|
2015-01-30 17:03:51 +01:00
|
|
|
|
|
|
|
if (dirty()) {
|
2019-05-23 19:23:52 +02:00
|
|
|
masterScore()->setPlaylistDirty(); // TODO: flag individual operations
|
|
|
|
masterScore()->setAutosaveDirty(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
|
|
|
}
|
|
|
|
|
2017-02-09 16:07:07 +01:00
|
|
|
#ifndef NDEBUG
|
2017-02-09 14:57:40 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// CmdState::dump
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void CmdState::dump()
|
|
|
|
{
|
2019-01-30 15:13:54 +01:00
|
|
|
qDebug("CmdState: mode %d %d-%d", int(_updateMode), _startTick.ticks(), _endTick.ticks());
|
2017-02-09 14:57:40 +01:00
|
|
|
// bool _excerptsChanged { false };
|
|
|
|
// bool _instrumentsChanged { false };
|
|
|
|
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// update
|
|
|
|
// layout & update
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2019-10-24 12:24:00 +02:00
|
|
|
void Score::update(bool resetCmdState)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2017-02-28 14:26:19 +01:00
|
|
|
bool updateAll = false;
|
2017-01-05 11:23:47 +01:00
|
|
|
for (MasterScore* ms : *movements()) {
|
|
|
|
CmdState& cs = ms->cmdState();
|
2017-02-20 11:43:54 +01:00
|
|
|
ms->deletePostponed();
|
2017-01-05 11:23:47 +01:00
|
|
|
if (cs.layoutRange()) {
|
|
|
|
for (Score* s : ms->scoreList())
|
|
|
|
s->doLayoutRange(cs.startTick(), cs.endTick());
|
2017-02-28 14:26:19 +01:00
|
|
|
updateAll = true;
|
2017-01-05 11:23:47 +01:00
|
|
|
}
|
2017-02-28 14:26:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for (MasterScore* ms : *movements()) {
|
|
|
|
CmdState& cs = ms->cmdState();
|
|
|
|
if (updateAll || cs.updateAll()) {
|
2017-01-05 11:23:47 +01:00
|
|
|
for (Score* s : scoreList()) {
|
2017-02-28 14:26:19 +01:00
|
|
|
for (MuseScoreView* v : s->viewer) {
|
2017-01-05 11:23:47 +01:00
|
|
|
v->updateAll();
|
2017-02-28 14:26:19 +01:00
|
|
|
}
|
2017-01-05 11:23:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (cs.updateRange()) {
|
|
|
|
// updateRange updates only current score
|
|
|
|
qreal d = spatium() * .5;
|
|
|
|
_updateState.refresh.adjust(-d, -d, 2 * d, 2 * d);
|
|
|
|
for (MuseScoreView* v : viewer)
|
|
|
|
v->dataChanged(_updateState.refresh);
|
|
|
|
_updateState.refresh = QRectF();
|
|
|
|
}
|
|
|
|
const InputState& is = inputState();
|
|
|
|
if (is.noteEntryMode() && is.segment()) {
|
|
|
|
setPlayPos(is.segment()->tick());
|
|
|
|
}
|
2019-05-23 19:23:52 +02:00
|
|
|
if (playlistDirty()) {
|
2019-06-24 17:58:47 +02:00
|
|
|
for (Score* s : scoreList())
|
|
|
|
emit s->playlistChanged();
|
2019-05-23 19:23:52 +02:00
|
|
|
masterScore()->setPlaylistClean();
|
2013-06-27 11:02:42 +02:00
|
|
|
}
|
2019-10-24 12:24:00 +02:00
|
|
|
if (resetCmdState)
|
|
|
|
cs.reset();
|
2016-03-18 09:29:16 +01:00
|
|
|
}
|
2018-02-17 21:11:43 +01:00
|
|
|
if (_selection.isRange())
|
|
|
|
_selection.updateSelectedElements();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2017-09-07 13:25:27 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// deletePostponed
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2017-02-20 11:43:54 +01:00
|
|
|
void Score::deletePostponed()
|
|
|
|
{
|
|
|
|
for (ScoreElement* e : _updateState._deleteList) {
|
|
|
|
if (e->isSystem()) {
|
|
|
|
System* s = toSystem(e);
|
|
|
|
for (SpannerSegment* ss : s->spannerSegments()) {
|
|
|
|
if (ss->system() == s)
|
|
|
|
ss->setSystem(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
qDeleteAll(_updateState._deleteList);
|
|
|
|
_updateState._deleteList.clear();
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdAddSpanner
|
|
|
|
// drop VOLTA, OTTAVA, TRILL, PEDAL, DYNAMIC
|
2017-11-27 09:56:41 +01:00
|
|
|
// HAIRPIN, LET_RING, VIBRATO and TEXTLINE
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2019-06-28 02:27:35 +02:00
|
|
|
void Score::cmdAddSpanner(Spanner* spanner, const QPointF& pos, bool firstStaffOnly)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
int staffIdx;
|
|
|
|
Segment* segment;
|
|
|
|
MeasureBase* mb = pos2measure(pos, &staffIdx, 0, &segment, 0);
|
2019-06-28 02:27:35 +02:00
|
|
|
if (firstStaffOnly)
|
|
|
|
staffIdx = 0;
|
2015-03-06 13:38:22 +01:00
|
|
|
// ignore if we do not have a measure
|
2017-01-18 14:16:33 +01:00
|
|
|
if (mb == 0 || mb->type() != ElementType::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());
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction lastTick = lastMeasure()->tick() + lastMeasure()->ticks();
|
|
|
|
Fraction 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());
|
|
|
|
|
2019-06-28 02:27:35 +02:00
|
|
|
if (pos.x() >= (b.x() + b.width() * .5) && m != lastMeasureMM() && m->nextMeasure()->system() == m->system())
|
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
|
|
|
}
|
2018-11-25 23:35:00 +01:00
|
|
|
spanner->eraseSpannerSegments();
|
2016-09-22 17:28:44 +02:00
|
|
|
|
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);
|
2018-12-17 04:53:16 +01:00
|
|
|
for (auto ss : spanner->spannerSegments())
|
|
|
|
ss->setTrack(track);
|
2015-06-30 09:06:30 +02:00
|
|
|
spanner->setTick(startSegment->tick());
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction tick2;
|
2015-06-30 09:06:30 +02:00
|
|
|
if (!endSegment)
|
|
|
|
tick2 = lastSegment()->tick();
|
|
|
|
else if (endSegment == startSegment)
|
|
|
|
tick2 = startSegment->measure()->last()->tick();
|
|
|
|
else
|
|
|
|
tick2 = endSegment->tick();
|
|
|
|
spanner->setTick2(tick2);
|
2017-01-16 20:51:12 +01:00
|
|
|
#if 0 // TODO
|
2017-12-21 14:03:45 +01:00
|
|
|
TextLine* tl = toTextLine(spanner);
|
2016-07-11 09:48:40 +02:00
|
|
|
if (tl) {
|
2017-01-16 20:51:12 +01:00
|
|
|
StyledPropertyListIdx st;
|
2015-08-11 21:39:12 +02:00
|
|
|
Text* t;
|
|
|
|
// begin
|
|
|
|
t = tl->beginTextElement();
|
|
|
|
if (t) {
|
|
|
|
st = t->textStyleType();
|
2017-01-16 20:51:12 +01:00
|
|
|
if (st >= StyledPropertyListIdx::DEFAULT)
|
2017-01-05 11:23:47 +01:00
|
|
|
t->textStyle().restyle(MScore::baseStyle().textStyle(st), textStyle(st));
|
2015-08-11 21:39:12 +02:00
|
|
|
}
|
|
|
|
// continue
|
|
|
|
t = tl->continueTextElement();
|
|
|
|
if (t) {
|
|
|
|
st = t->textStyleType();
|
2017-01-16 20:51:12 +01:00
|
|
|
if (st >= StyledPropertyListIdx::DEFAULT)
|
2017-01-05 11:23:47 +01:00
|
|
|
t->textStyle().restyle(MScore::baseStyle().textStyle(st), textStyle(st));
|
2015-08-11 21:39:12 +02:00
|
|
|
}
|
|
|
|
// end
|
|
|
|
t = tl->endTextElement();
|
|
|
|
if (t) {
|
|
|
|
st = t->textStyleType();
|
2017-01-16 20:51:12 +01:00
|
|
|
if (st >= StyledPropertyListIdx::DEFAULT)
|
2017-01-05 11:23:47 +01:00
|
|
|
t->textStyle().restyle(MScore::baseStyle().textStyle(st), textStyle(st));
|
2015-08-11 21:39:12 +02:00
|
|
|
}
|
|
|
|
}
|
2017-01-16 20:51:12 +01:00
|
|
|
#endif
|
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;
|
2017-03-08 13:12:26 +01:00
|
|
|
for (ps = s; ps; ps = ps->prev(SegmentType::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));
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction tick = cr->tick() + cr->actualTicks();
|
2012-05-26 14:26:10 +02:00
|
|
|
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
|
2017-03-08 13:12:26 +01:00
|
|
|
ps = ps->measure()->undoGetSegment(SegmentType::ChordRest, tick);
|
2015-02-13 06:57:37 +01:00
|
|
|
}
|
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();
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction stick = ps ? ps->tick() : m->tick();
|
|
|
|
Fraction ticks = s->tick() - stick;
|
|
|
|
if (ticks.isNotZero())
|
|
|
|
setRest(stick, track, ticks, false, 0);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
//
|
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;
|
2017-03-08 13:12:26 +01:00
|
|
|
for (ns = s->next(SegmentType::ChordRest); ns; ns = ns->next(SegmentType::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
|
2019-01-30 15:13:54 +01:00
|
|
|
setRest(s->tick(), track, ticks, false, 0);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2019-01-14 16:24:37 +01:00
|
|
|
note->setTrack(chord->track());
|
2012-05-26 14:26:10 +02:00
|
|
|
int valTmp = val < 0 ? val+1 : val-1;
|
|
|
|
|
|
|
|
int npitch;
|
2014-04-23 11:08:51 +02:00
|
|
|
int ntpc1;
|
|
|
|
int ntpc2;
|
2019-12-04 15:19:52 +01:00
|
|
|
bool accidental = _is.noteEntryMode() && _is.accidentalType() != AccidentalType::NONE;
|
|
|
|
bool forceAccidental = false;
|
|
|
|
if (abs(valTmp) != 7 || accidental) {
|
2014-04-09 09:40:25 +02:00
|
|
|
int line = on->line() - valTmp;
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction 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);
|
2019-12-04 15:19:52 +01:00
|
|
|
int ntpc;
|
|
|
|
if (accidental) {
|
|
|
|
AccidentalVal acci = Accidental::subtype2value(_is.accidentalType());
|
|
|
|
int step = absStep(line, clef);
|
|
|
|
int octave = step / 7;
|
|
|
|
npitch = step2pitch(step) + octave * 12 + int(acci);
|
|
|
|
forceAccidental = (npitch == line2pitch(line, clef, key));
|
|
|
|
ntpc = step2tpc(step % 7, acci);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
npitch = line2pitch(line, clef, key);
|
|
|
|
ntpc = pitch2tpc(npitch, key, Prefer::NEAREST);
|
|
|
|
}
|
2014-04-23 11:08:51 +02:00
|
|
|
|
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 {
|
2018-03-27 15:36:00 +02:00
|
|
|
if (styleB(Sid::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;
|
2016-09-22 12:02:27 +02:00
|
|
|
endCmd();
|
2016-01-21 14:33:32 +01:00
|
|
|
return;
|
|
|
|
}
|
2014-04-23 11:08:51 +02:00
|
|
|
note->setPitch(npitch, ntpc1, ntpc2);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
undoAddElement(note);
|
2019-12-04 15:19:52 +01:00
|
|
|
if (forceAccidental) {
|
|
|
|
Accidental* a = new Accidental(this);
|
|
|
|
a->setAccidentalType(_is.accidentalType());
|
|
|
|
a->setRole(AccidentalRole::USER);
|
|
|
|
a->setParent(note);
|
|
|
|
undoAddElement(a);
|
|
|
|
}
|
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
|
|
|
}
|
2019-12-04 15:19:52 +01:00
|
|
|
if (_is.noteEntryMode())
|
|
|
|
_is.setAccidentalType(AccidentalType::NONE);
|
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)
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2017-11-16 09:25:09 +01:00
|
|
|
Note* 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
|
|
|
|
2018-02-14 20:20:21 +01:00
|
|
|
// allow grace notes to be added to other grace notes
|
2015-01-17 22:40:28 +01:00
|
|
|
// 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);
|
2019-01-30 15:13:54 +01:00
|
|
|
chord->setTicks(d.fraction());
|
2012-05-26 14:26:10 +02:00
|
|
|
chord->setNoteType(type);
|
2018-03-27 15:36:00 +02:00
|
|
|
chord->setMag(ch->staff()->mag(chord->tick()) * styleD(Sid::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);
|
2017-11-16 09:25:09 +01:00
|
|
|
return note;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2016-08-04 17:29:07 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// createCRSequence
|
|
|
|
// Create a rest or chord of len f.
|
|
|
|
// If f is not a basic len, create several rests or
|
|
|
|
// tied chords.
|
|
|
|
//
|
|
|
|
// f total len of ChordRest
|
|
|
|
// cr prototype CR
|
|
|
|
// tick start position in measure
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
void Score::createCRSequence(const Fraction& f, ChordRest* cr, const Fraction& t)
|
2016-08-04 17:29:07 +02:00
|
|
|
{
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction tick(t);
|
2016-08-04 17:29:07 +02:00
|
|
|
Measure* measure = cr->measure();
|
|
|
|
ChordRest* ocr = 0;
|
|
|
|
for (TDuration d : toDurationList(f, true)) {
|
|
|
|
ChordRest* ncr = toChordRest(cr->clone());
|
|
|
|
ncr->setDurationType(d);
|
2019-01-30 15:13:54 +01:00
|
|
|
ncr->setTicks(d.fraction());
|
2019-04-05 07:06:58 +02:00
|
|
|
undoAddCR(ncr, measure, measure->tick() + tick);
|
2016-08-04 17:29:07 +02:00
|
|
|
if (cr->isChord() && ocr) {
|
|
|
|
Chord* nc = toChord(ncr);
|
|
|
|
Chord* oc = toChord(ocr);
|
|
|
|
for (unsigned int i = 0; i < oc->notes().size(); ++i) {
|
|
|
|
Note* on = oc->notes()[i];
|
|
|
|
Note* nn = nc->notes()[i];
|
|
|
|
Tie* tie = new Tie(this);
|
|
|
|
tie->setStartNote(on);
|
|
|
|
tie->setEndNote(nn);
|
|
|
|
tie->setTrack(cr->track());
|
|
|
|
on->setTieFor(tie);
|
|
|
|
nn->setTieBack(tie);
|
|
|
|
undoAddElement(tie);
|
|
|
|
}
|
|
|
|
}
|
2019-09-16 08:41:56 +02:00
|
|
|
|
2016-08-04 17:29:07 +02:00
|
|
|
tick += ncr->actualTicks();
|
|
|
|
ocr = ncr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// setNoteRest
|
|
|
|
// pitch == -1 -> set rest
|
|
|
|
// return segment of last created note/rest
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2019-10-11 03:47:55 +02:00
|
|
|
Segment* Score::setNoteRest(Segment* segment, int track, NoteVal nval, Fraction sd, Direction stemDirection, bool forceAccidental, bool rhythmic)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2017-03-08 13:12:26 +01:00
|
|
|
Q_ASSERT(segment->segmentType() == SegmentType::ChordRest);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-08-19 22:58:34 +02:00
|
|
|
bool isRest = nval.pitch == -1;
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction tick = segment->tick();
|
2012-05-26 14:26:10 +02:00
|
|
|
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()) {
|
2019-01-30 15:13:54 +01:00
|
|
|
qDebug("cannot get gap at %d type: %d/%d", tick.ticks(), sd.numerator(),
|
2012-05-26 14:26:10 +02:00
|
|
|
sd.denominator());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
measure = segment->measure();
|
2018-02-13 19:03:02 +01:00
|
|
|
std::vector<TDuration> dl;
|
|
|
|
if (rhythmic)
|
|
|
|
dl = toRhythmicDurationList(dd, isRest, segment->rtick(), sigmap()->timesig(tick).nominal(), measure, 1);
|
|
|
|
else
|
|
|
|
dl = toDurationList(dd, true);
|
2018-09-11 16:56:50 +02:00
|
|
|
size_t n = dl.size();
|
|
|
|
for (size_t 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;
|
2016-08-19 22:58:34 +02:00
|
|
|
if (isRest) {
|
2012-05-26 14:26:10 +02:00
|
|
|
nr = ncr = new Rest(this);
|
|
|
|
nr->setTrack(track);
|
|
|
|
ncr->setDurationType(d);
|
2019-01-30 15:13:54 +01:00
|
|
|
ncr->setTicks(d == TDuration::DurationType::V_MEASURE ? measure->ticks() : d.fraction());
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
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);
|
2019-01-30 15:13:54 +01:00
|
|
|
chord->setTicks(d.fraction());
|
2012-05-26 14:26:10 +02:00
|
|
|
chord->setStemDirection(stemDirection);
|
|
|
|
chord->add(note);
|
2014-12-15 23:08:33 +01:00
|
|
|
note->setNval(nval, tick);
|
2019-10-11 03:47:55 +02:00
|
|
|
if (forceAccidental) {
|
2019-12-04 15:19:52 +01:00
|
|
|
int tpc = styleB(Sid::concertPitch) ? nval.tpc1 : nval.tpc2;
|
2019-10-11 03:47:55 +02:00
|
|
|
AccidentalVal alter = tpc2alter(tpc);
|
|
|
|
AccidentalType at = Accidental::value2subtype(alter);
|
|
|
|
Accidental* a = new Accidental(this);
|
|
|
|
a->setAccidentalType(at);
|
|
|
|
a->setRole(AccidentalRole::USER);
|
|
|
|
note->add(a);
|
|
|
|
}
|
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;
|
|
|
|
|
2017-03-08 13:12:26 +01:00
|
|
|
Segment* nseg = tick2segment(tick, false, SegmentType::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
|
2016-08-19 22:58:34 +02:00
|
|
|
if (!isRest) {
|
2012-05-26 14:26:10 +02:00
|
|
|
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) {
|
2017-01-18 14:16:33 +01:00
|
|
|
if (_is.slur() && nr->type() == ElementType::NOTE) {
|
2019-05-22 15:19:30 +02:00
|
|
|
// If the start element was the same as the end element when the slur was created,
|
|
|
|
// the end grip of the front slur segment was given an x-offset of 3.0 * spatium().
|
|
|
|
// Now that the slur is about to be given a new end element, this should be reset.
|
|
|
|
if (_is.slur()->endElement() == _is.slur()->startElement())
|
|
|
|
_is.slur()->frontSegment()->reset();
|
2014-07-14 13:59:54 +02:00
|
|
|
//
|
|
|
|
// extend slur
|
|
|
|
//
|
2016-06-14 10:32:34 +02:00
|
|
|
Chord* chord = toNote(nr)->chord();
|
2018-03-27 15:36:00 +02:00
|
|
|
_is.slur()->undoChangeProperty(Pid::SPANNER_TICKS, chord->tick() - _is.slur()->tick());
|
2018-09-25 15:55:08 +02:00
|
|
|
for (ScoreElement* se : _is.slur()->linkList()) {
|
|
|
|
Slur* slur = toSlur(se);
|
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();
|
fix various typos
* Found via `codespell -q 3 -S ./share/locale,./thirdparty -L ba,cann,clas,dur,foto,iff,nd,ois,ot,pres,possibile,snaped,strack,tage,te,uint,thru,valu`
* Some revisions made per feedback given during review.
* Follow-up typos for review
* Add revisions per feedback
2019-12-17 21:06:10 +01:00
|
|
|
Fraction accumulated;
|
2012-05-26 14:26:10 +02:00
|
|
|
Fraction sd = _sd;
|
|
|
|
|
|
|
|
//
|
|
|
|
// remember first segment which should
|
|
|
|
// not be deleted (it may contain other elements we want to preserve)
|
|
|
|
//
|
|
|
|
Segment* firstSegment = segment;
|
2019-04-02 15:32:28 +02:00
|
|
|
const Fraction firstSegmentEnd = firstSegment->tick() + firstSegment->ticks();
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction nextTick = segment->tick();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2017-03-08 13:12:26 +01:00
|
|
|
for (Segment* seg = firstSegment; seg; seg = seg->next(SegmentType::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;
|
2017-03-08 13:12:26 +01:00
|
|
|
Segment* seg1 = seg->next(SegmentType::ChordRest);
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction tick2 = seg1 ? seg1->tick() : seg->measure()->tick() + seg->measure()->ticks();
|
2016-08-03 17:29:52 +02:00
|
|
|
segment = seg;
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction td(tick2 - seg->tick());
|
2012-05-26 14:26:10 +02:00
|
|
|
if (td > sd)
|
|
|
|
td = sd;
|
fix various typos
* Found via `codespell -q 3 -S ./share/locale,./thirdparty -L ba,cann,clas,dur,foto,iff,nd,ois,ot,pres,possibile,snaped,strack,tage,te,uint,thru,valu`
* Some revisions made per feedback given during review.
* Follow-up typos for review
* Add revisions per feedback
2019-12-17 21:06:10 +01:00
|
|
|
accumulated += td;
|
2012-05-26 14:26:10 +02:00
|
|
|
sd -= td;
|
|
|
|
if (sd.isZero())
|
2019-04-02 15:32:28 +02:00
|
|
|
break;
|
2013-06-04 18:49:04 +02:00
|
|
|
nextTick = tick2;
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
}
|
2016-08-03 17:29:52 +02:00
|
|
|
if (seg->tick() > nextTick) {
|
|
|
|
// there was a gap
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction td(seg->tick() - nextTick);
|
2016-08-03 17:29:52 +02:00
|
|
|
if (td > sd)
|
|
|
|
td = sd;
|
fix various typos
* Found via `codespell -q 3 -S ./share/locale,./thirdparty -L ba,cann,clas,dur,foto,iff,nd,ois,ot,pres,possibile,snaped,strack,tage,te,uint,thru,valu`
* Some revisions made per feedback given during review.
* Follow-up typos for review
* Add revisions per feedback
2019-12-17 21:06:10 +01:00
|
|
|
accumulated += td;
|
2016-08-03 17:29:52 +02:00
|
|
|
sd -= td;
|
|
|
|
if (sd.isZero())
|
2019-04-02 15:32:28 +02:00
|
|
|
break;
|
2016-08-03 17:29:52 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
//
|
|
|
|
// 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)
|
2019-04-02 15:32:28 +02:00
|
|
|
break;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction td(cr->ticks());
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2013-09-05 20:05:49 +02:00
|
|
|
// remove tremolo between 2 notes, if present
|
2016-08-03 17:29:52 +02:00
|
|
|
if (cr->isChord()) {
|
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
|
2019-01-30 15:13:54 +01:00
|
|
|
td = ltuplet->ticks();
|
2012-05-26 14:26:10 +02:00
|
|
|
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
|
|
|
}
|
2019-11-21 22:05:06 +01:00
|
|
|
Fraction timeStretch = cr->staff()->timeStretch(cr->tick());
|
|
|
|
nextTick += actualTicks(td, tuplet, timeStretch);
|
2012-05-26 14:26:10 +02:00
|
|
|
if (sd < td) {
|
|
|
|
//
|
|
|
|
// we removed too much
|
|
|
|
//
|
fix various typos
* Found via `codespell -q 3 -S ./share/locale,./thirdparty -L ba,cann,clas,dur,foto,iff,nd,ois,ot,pres,possibile,snaped,strack,tage,te,uint,thru,valu`
* Some revisions made per feedback given during review.
* Follow-up typos for review
* Add revisions per feedback
2019-12-17 21:06:10 +01:00
|
|
|
accumulated = _sd;
|
2012-05-26 14:26:10 +02:00
|
|
|
Fraction rd = td - sd;
|
|
|
|
|
2016-02-06 22:03:43 +01:00
|
|
|
std::vector<TDuration> dList = toDurationList(rd, false);
|
|
|
|
if (dList.empty())
|
2019-04-02 15:32:28 +02:00
|
|
|
break;
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2019-11-21 22:05:06 +01:00
|
|
|
Fraction tick = cr->tick() + actualTicks(sd, tuplet, timeStretch);
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
if ((tuplet == 0) && (((measure->tick() - tick).ticks() % dList[0].ticks().ticks()) == 0)) {
|
2016-12-03 13:46:58 +01:00
|
|
|
for (TDuration d : dList) {
|
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 {
|
2018-09-11 16:56:50 +02:00
|
|
|
for (int i = int(dList.size()) - 1; i >= 0; --i) {
|
2015-04-14 00:56:20 +02:00
|
|
|
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
|
|
|
}
|
2019-04-02 15:32:28 +02:00
|
|
|
break;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
fix various typos
* Found via `codespell -q 3 -S ./share/locale,./thirdparty -L ba,cann,clas,dur,foto,iff,nd,ois,ot,pres,possibile,snaped,strack,tage,te,uint,thru,valu`
* Some revisions made per feedback given during review.
* Follow-up typos for review
* Add revisions per feedback
2019-12-17 21:06:10 +01:00
|
|
|
accumulated += td;
|
2012-05-26 14:26:10 +02:00
|
|
|
sd -= td;
|
|
|
|
if (sd.isZero())
|
2019-04-02 15:32:28 +02:00
|
|
|
break;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2019-01-30 15:13:54 +01:00
|
|
|
// Fraction ticks = measure->tick() + measure->ticks() - segment->tick();
|
2012-05-26 14:26:10 +02:00
|
|
|
// Fraction td = Fraction::fromTicks(ticks);
|
|
|
|
// NEEDS REVIEW !!
|
|
|
|
// once the statement below is removed, these two lines do nothing
|
|
|
|
// if (td > sd)
|
|
|
|
// td = sd;
|
fix various typos
* Found via `codespell -q 3 -S ./share/locale,./thirdparty -L ba,cann,clas,dur,foto,iff,nd,ois,ot,pres,possibile,snaped,strack,tage,te,uint,thru,valu`
* Some revisions made per feedback given during review.
* Follow-up typos for review
* Add revisions per feedback
2019-12-17 21:06:10 +01:00
|
|
|
// ??? accumulated should already contain the total value of the created gap: line 749, 811 or 838
|
2012-05-26 14:26:10 +02:00
|
|
|
// 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
|
fix various typos
* Found via `codespell -q 3 -S ./share/locale,./thirdparty -L ba,cann,clas,dur,foto,iff,nd,ois,ot,pres,possibile,snaped,strack,tage,te,uint,thru,valu`
* Some revisions made per feedback given during review.
* Follow-up typos for review
* Add revisions per feedback
2019-12-17 21:06:10 +01:00
|
|
|
// accumulated += td;
|
2019-04-02 15:32:28 +02:00
|
|
|
|
|
|
|
const Fraction t1 = firstSegmentEnd;
|
fix various typos
* Found via `codespell -q 3 -S ./share/locale,./thirdparty -L ba,cann,clas,dur,foto,iff,nd,ois,ot,pres,possibile,snaped,strack,tage,te,uint,thru,valu`
* Some revisions made per feedback given during review.
* Follow-up typos for review
* Add revisions per feedback
2019-12-17 21:06:10 +01:00
|
|
|
const Fraction t2 = firstSegment->tick() + accumulated;
|
2019-04-02 15:32:28 +02:00
|
|
|
if (t1 < t2) {
|
|
|
|
Segment* s1 = tick2rightSegment(t1);
|
|
|
|
Segment* s2 = tick2rightSegment(t2);
|
|
|
|
typedef SelectionFilterType Sel;
|
|
|
|
// chord symbols can exist without chord/rest so they should not be removed
|
|
|
|
constexpr Sel filter = static_cast<Sel>(int(Sel::ALL) & ~int(Sel::CHORD_SYMBOL));
|
|
|
|
deleteAnnotationsFromRange(s1, s2, track, track + 1, filter);
|
|
|
|
deleteSpannersFromRange(t1, t2, track, track + 1, filter);
|
|
|
|
}
|
|
|
|
|
fix various typos
* Found via `codespell -q 3 -S ./share/locale,./thirdparty -L ba,cann,clas,dur,foto,iff,nd,ois,ot,pres,possibile,snaped,strack,tage,te,uint,thru,valu`
* Some revisions made per feedback given during review.
* Follow-up typos for review
* Add revisions per feedback
2019-12-17 21:06:10 +01:00
|
|
|
return accumulated;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
bool Score::makeGap1(const Fraction& baseTick, int staffIdx, const Fraction& len, int voiceOffset[VOICES])
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2017-03-08 13:12:26 +01:00
|
|
|
Segment* seg = tick2segment(baseTick, true, SegmentType::ChordRest);
|
2012-05-26 14:26:10 +02:00
|
|
|
if (!seg) {
|
2019-01-30 15:13:54 +01:00
|
|
|
qDebug("no segment to paste at tick %d", baseTick.ticks());
|
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;
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction tick = baseTick + Fraction::fromTicks(voiceOffset[track-strack]);
|
2014-07-31 08:37:58 +02:00
|
|
|
Measure* m = tick2measure(tick);
|
2016-12-03 13:46:58 +01:00
|
|
|
if ((track % VOICES) && !m->hasVoices(staffIdx))
|
|
|
|
continue;
|
2014-07-31 08:37:58 +02:00
|
|
|
|
|
|
|
Fraction newLen = len - Fraction::fromTicks(voiceOffset[track-strack]);
|
|
|
|
Q_ASSERT(newLen.numerator() != 0);
|
2019-04-01 17:10:06 +02:00
|
|
|
|
|
|
|
if (newLen > Fraction(0,1)) {
|
|
|
|
const Fraction endTick = tick + newLen;
|
|
|
|
deleteAnnotationsFromRange(tick2rightSegment(tick), tick2rightSegment(endTick), track, track + 1, selectionFilter());
|
|
|
|
deleteSpannersFromRange(tick, endTick, track, track + 1, selectionFilter());
|
|
|
|
}
|
|
|
|
|
|
|
|
seg = m->undoGetSegment(SegmentType::ChordRest, tick);
|
2014-07-31 08:37:58 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
bool Score::makeGapVoice(Segment* seg, int track, Fraction len, const Fraction& tick)
|
2014-07-27 11:15:46 +02:00
|
|
|
{
|
|
|
|
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
|
2017-03-08 13:12:26 +01:00
|
|
|
Segment* seg1 = seg->prev(SegmentType::ChordRest);
|
2013-06-12 14:23:57 +02:00
|
|
|
for (;;) {
|
|
|
|
if (seg1 == 0) {
|
2016-12-03 13:46:58 +01:00
|
|
|
if (!(track % VOICES))
|
2019-01-30 15:13:54 +01:00
|
|
|
qDebug("no segment before tick %d", tick.ticks());
|
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;
|
2017-03-08 13:12:26 +01:00
|
|
|
seg1 = seg1->prev(SegmentType::ChordRest);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2016-06-14 10:32:34 +02:00
|
|
|
ChordRest* cr1 = toChordRest(seg1->element(track));
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction srcF = cr1->ticks();
|
|
|
|
Fraction dstF = tick - cr1->tick();
|
2016-02-06 22:03:43 +01:00
|
|
|
std::vector<TDuration> dList = toDurationList(dstF, true);
|
2018-09-11 16:56:50 +02:00
|
|
|
size_t n = dList.size();
|
2014-11-18 17:09:26 +01:00
|
|
|
undoChangeChordRestLen(cr1, TDuration(dList[0]));
|
|
|
|
if (n > 1) {
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction crtick = cr1->tick() + cr1->actualTicks();
|
2014-11-18 17:09:26 +01:00
|
|
|
Measure* measure = tick2measure(crtick);
|
2017-01-18 14:16:33 +01:00
|
|
|
if (cr1->type() == ElementType::CHORD) {
|
2014-11-18 17:09:26 +01:00
|
|
|
// split Chord
|
2016-06-14 10:32:34 +02:00
|
|
|
Chord* c = toChord(cr1);
|
2018-09-11 16:56:50 +02:00
|
|
|
for (size_t i = 1; i < n; ++i) {
|
2014-11-18 17:09:26 +01:00
|
|
|
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);
|
2018-09-11 16:56:50 +02:00
|
|
|
for (size_t i = 1; i < n; ++i) {
|
2014-11-18 17:09:26 +01:00
|
|
|
TDuration d = dList[i];
|
2016-06-14 10:32:34 +02:00
|
|
|
Rest* r2 = toRest(r->clone());
|
2019-01-30 15:13:54 +01:00
|
|
|
r2->setTicks(d.fraction());
|
2014-11-18 17:09:26 +01:00
|
|
|
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 (;;) {
|
2017-03-08 13:12:26 +01:00
|
|
|
seg1 = seg1->next1(SegmentType::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");
|
2017-01-18 14:16:33 +01:00
|
|
|
insertMeasure(ElementType::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
|
2017-03-08 13:12:26 +01:00
|
|
|
Segment* s = m->undoGetSegment(SegmentType::ChordRest, m->tick());
|
2018-08-14 10:03:49 +02:00
|
|
|
int t = cr->track();
|
|
|
|
cr = toChordRest(s->element(t));
|
2012-05-26 14:26:10 +02:00
|
|
|
if (cr == 0) {
|
2018-08-14 10:03:49 +02:00
|
|
|
addRest(s, t, TDuration(TDuration::DurationType::V_MEASURE), 0);
|
|
|
|
cr = toChordRest(s->element(t));
|
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) {
|
2018-03-19 16:17:19 +01:00
|
|
|
Fraction rest = tuplet->elementsDuration();
|
|
|
|
for (DurationElement* de : tuplet->elements()) {
|
|
|
|
if (de == cr)
|
|
|
|
break;
|
2019-01-30 15:13:54 +01:00
|
|
|
rest -= de->ticks();
|
2018-03-19 16:17:19 +01:00
|
|
|
}
|
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();
|
2019-01-30 15:13:54 +01:00
|
|
|
while (gap > Fraction(0,1)) {
|
2012-05-26 14:26:10 +02:00
|
|
|
Measure* m = s->measure();
|
2018-09-20 17:15:37 +02:00
|
|
|
Fraction timeStretch = cr->staff()->timeStretch(s->tick());
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction rest = (m->ticks() - s->rtick()) * timeStretch;
|
2012-05-26 14:26:10 +02:00
|
|
|
if (rest >= gap) {
|
|
|
|
flist.append(gap);
|
|
|
|
return flist;
|
|
|
|
}
|
|
|
|
flist.append(rest);
|
|
|
|
gap -= rest;
|
|
|
|
m = m->nextMeasure();
|
|
|
|
if (m == 0)
|
|
|
|
return flist;
|
2017-03-08 13:12:26 +01:00
|
|
|
s = m->first(SegmentType::ChordRest);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
return flist;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// changeCRlen
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::changeCRlen(ChordRest* cr, const TDuration& d)
|
|
|
|
{
|
|
|
|
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();
|
2016-08-03 17:29:52 +02:00
|
|
|
changeCRlen(cr, dstF);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2016-08-04 17:29:07 +02:00
|
|
|
void Score::changeCRlen(ChordRest* cr, const Fraction& dstF, bool fillWithRest)
|
2016-08-03 17:29:52 +02:00
|
|
|
{
|
2018-10-19 14:13:49 +02:00
|
|
|
if (cr->isRepeatMeasure()) {
|
|
|
|
// it is not clear what should this
|
|
|
|
// operation mean for measure repeats.
|
|
|
|
return;
|
|
|
|
}
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction srcF(cr->ticks());
|
2020-02-11 18:13:51 +01:00
|
|
|
if (srcF == dstF) {
|
|
|
|
if (cr->isFullMeasureRest())
|
|
|
|
undoChangeChordRestLen(cr, dstF);
|
2012-05-26 14:26:10 +02:00
|
|
|
return;
|
2020-02-11 18:13:51 +01:00
|
|
|
}
|
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();
|
2016-08-03 17:29:52 +02:00
|
|
|
if (cr->isChord()) {
|
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());
|
|
|
|
}
|
|
|
|
}
|
2019-11-21 22:05:06 +01:00
|
|
|
Fraction timeStretch = cr->staff()->timeStretch(cr->tick());
|
2016-08-04 17:29:07 +02:00
|
|
|
std::vector<TDuration> dList = toDurationList(dstF, true);
|
|
|
|
undoChangeChordRestLen(cr, dList[0]);
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction tick2 = cr->tick();
|
2016-08-04 17:29:07 +02:00
|
|
|
for (unsigned i = 1; i < dList.size(); ++i) {
|
2019-11-21 22:05:06 +01:00
|
|
|
tick2 += actualTicks(dList[i-1].ticks(), tuplet, timeStretch);
|
2016-08-04 17:29:07 +02:00
|
|
|
TDuration d = dList[i];
|
|
|
|
setRest(tick2, track, d.fraction(), (d.dots() > 0), tuplet);
|
|
|
|
}
|
|
|
|
if (fillWithRest)
|
|
|
|
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
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction tick = cr->tick();
|
2012-05-26 14:26:10 +02:00
|
|
|
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
|
|
|
|
2016-08-03 17:29:52 +02:00
|
|
|
if (cr->isRest()) {
|
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]);
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction tick2 = cr->tick();
|
2016-02-06 22:03:43 +01:00
|
|
|
for (unsigned i = 1; i < dList.size(); ++i) {
|
2019-11-21 22:05:06 +01:00
|
|
|
tick2 += actualTicks(dList[i-1].ticks(), tuplet, timeStretch);
|
2014-12-05 09:18:40 +01:00
|
|
|
TDuration d = dList[i];
|
2019-11-21 22:05:06 +01:00
|
|
|
setRest(tick2, track, d.fraction(), (d.dots() > 0), tuplet);
|
2013-05-18 17:07:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2019-11-21 22:05:06 +01:00
|
|
|
r = setRest(tick, track, f2, false, tuplet);
|
2013-05-18 17:07:20 +02:00
|
|
|
}
|
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;
|
|
|
|
}
|
2019-11-21 22:05:06 +01:00
|
|
|
tick += actualTicks(f2, tuplet, timeStretch);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
else {
|
2016-02-06 22:03:43 +01:00
|
|
|
std::vector<TDuration> dList = toDurationList(f2, true);
|
2016-08-03 17:29:52 +02:00
|
|
|
Measure* measure = tick2measure(tick);
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction etick = measure->tick();
|
2016-08-03 17:29:52 +02:00
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
if (((tick - etick).ticks() % dList[0].ticks().ticks()) == 0) {
|
2016-08-03 17:29:52 +02:00
|
|
|
for (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 {
|
2018-09-11 16:56:50 +02:00
|
|
|
for (int i = int(dList.size()) - 1; i >= 0; --i) {
|
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, 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;
|
2017-03-08 13:12:26 +01:00
|
|
|
Segment* s = m1->first(SegmentType::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.
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2019-05-01 14:58:42 +02:00
|
|
|
void Score::upDown(bool up, UpDownMode mode)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
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) {
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction 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
|
|
|
|
2016-12-13 13:16:17 +01:00
|
|
|
switch (staff->staffType(oNote->chord()->tick())->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) {
|
2018-10-21 10:27:23 +02:00
|
|
|
newPitch = up ? ds->nextPitch(pitch) : ds->prevPitch(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
|
|
|
{
|
2018-11-08 15:45:14 +01:00
|
|
|
const StaffType* stt = staff->staffType(tick);
|
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!
|
2018-03-27 15:36:00 +02:00
|
|
|
oNote->undoChangeProperty(Pid::FRET, fret);
|
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
|
2019-12-02 07:17:14 +01:00
|
|
|
// user added accidentals are removed here
|
|
|
|
// unless it's an octave change
|
|
|
|
// in this case courtesy accidentals are preserved
|
|
|
|
// because they're now harder to be re-entered due to the revised note-input workflow
|
|
|
|
if (mode != UpDownMode::OCTAVE) {
|
|
|
|
auto l = oNote->linkList();
|
|
|
|
for (ScoreElement* e : l) {
|
|
|
|
Note* ln = toNote(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
|
2016-12-13 13:16:17 +01:00
|
|
|
else if (staff->staffType(tick)->group() == StaffGroup::TAB) {
|
2012-09-19 14:15:49 +02:00
|
|
|
bool refret = false;
|
|
|
|
if (oNote->string() != string) {
|
2018-03-27 15:36:00 +02:00
|
|
|
oNote->undoChangeProperty(Pid::STRING, string);
|
2012-09-19 14:15:49 +02:00
|
|
|
refret = true;
|
|
|
|
}
|
|
|
|
if (oNote->fret() != fret) {
|
2018-03-27 15:36:00 +02:00
|
|
|
oNote->undoChangeProperty(Pid::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
|
|
|
}
|
2019-05-31 16:26:12 +02:00
|
|
|
setSelectionChanged(true);
|
2018-10-21 10:27:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// upDownDelta
|
|
|
|
/// Add the delta to the pitch of note.
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2019-05-01 14:58:42 +02:00
|
|
|
void Score::upDownDelta(int pitchDelta)
|
2018-10-21 10:27:23 +02:00
|
|
|
{
|
|
|
|
while (pitchDelta > 0) {
|
2019-05-01 14:58:42 +02:00
|
|
|
upDown(true, UpDownMode::CHROMATIC);
|
2018-10-21 10:27:23 +02:00
|
|
|
pitchDelta--;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (pitchDelta < 0) {
|
2019-05-01 14:58:42 +02:00
|
|
|
upDown(false, UpDownMode::CHROMATIC);
|
2018-10-21 10:27:23 +02:00
|
|
|
pitchDelta++;
|
|
|
|
}
|
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.
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-10-06 12:21:28 +02:00
|
|
|
void Score::addArticulation(SymId attr)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2015-03-19 09:57:41 +01:00
|
|
|
QSet<Chord*> set;
|
2016-10-06 12:21:28 +02:00
|
|
|
for (Element* el : selection().elements()) {
|
|
|
|
if (el->isNote() || el->isChord()) {
|
|
|
|
Chord* cr = 0;
|
2015-03-19 09:57:41 +01:00
|
|
|
// apply articulation on a given chord only once
|
2016-10-06 12:21:28 +02:00
|
|
|
if (el->isNote()) {
|
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);
|
2016-10-06 12:21:28 +02:00
|
|
|
na->setSymId(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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2019-10-11 03:47:55 +02:00
|
|
|
// toggleAccidental
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::toggleAccidental(AccidentalType at, const EditData& ed)
|
|
|
|
{
|
|
|
|
if (_is.accidentalType() == at)
|
|
|
|
at = AccidentalType::NONE;
|
|
|
|
if (noteEntryMode()) {
|
|
|
|
_is.setAccidentalType(at);
|
|
|
|
_is.setRest(false);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (selection().isNone()) {
|
|
|
|
ed.view->startNoteEntryMode();
|
|
|
|
_is.setAccidentalType(at);
|
|
|
|
_is.setDuration(TDuration::DurationType::V_QUARTER);
|
|
|
|
_is.setRest(false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
changeAccidental(at);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------
|
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();
|
|
|
|
|
2016-12-13 13:16:17 +01:00
|
|
|
if (st->isTabStaff(chord->tick())) {
|
2013-09-02 12:13:46 +02:00
|
|
|
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);
|
2018-03-27 15:36:00 +02:00
|
|
|
if (score->styleB(Sid::concertPitch))
|
2014-04-14 10:39:27 +02:00
|
|
|
tpc1 = tpc;
|
|
|
|
else {
|
|
|
|
tpc1 = tpc2;
|
|
|
|
tpc2 = tpc;
|
|
|
|
}
|
2016-06-16 21:31:37 +02:00
|
|
|
|
2016-12-13 13:16:17 +01:00
|
|
|
if (!st->isTabStaff(chord->tick())) {
|
2013-09-02 12:13:46 +02:00
|
|
|
//
|
|
|
|
// 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;
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction tick = segment->tick();
|
2015-01-24 11:30:37 +01:00
|
|
|
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-09-29 01:18:02 +02:00
|
|
|
else if (acc == acc2 || pitch == note->pitch() || Accidental::isMicrotonal(accidental))
|
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()) {
|
2017-12-20 16:49:30 +01:00
|
|
|
Note* ln = toNote(se);
|
2015-03-31 17:34:54 +02:00
|
|
|
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);
|
2018-07-07 21:04:31 +02:00
|
|
|
if (ln->tieBack())
|
|
|
|
continue;
|
2014-06-18 06:52:25 +02:00
|
|
|
}
|
|
|
|
else if (forceAdd) {
|
2015-01-24 11:30:37 +01:00
|
|
|
if (a)
|
|
|
|
undoRemoveElement(a);
|
2018-09-25 15:55:08 +02:00
|
|
|
Accidental* a1 = new Accidental(lns);
|
|
|
|
a1->setParent(ln);
|
|
|
|
a1->setAccidentalType(accidental);
|
|
|
|
a1->setRole(AccidentalRole::USER);
|
|
|
|
lns->undoAddElement(a1);
|
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
|
|
|
}
|
2018-02-15 18:10:50 +01:00
|
|
|
setPlayNote(true);
|
2019-04-02 21:14:23 +02:00
|
|
|
setSelectionChanged(true);
|
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
|
|
|
{
|
2018-01-17 13:25:23 +01:00
|
|
|
Chord* c;
|
2016-10-06 12:21:28 +02:00
|
|
|
if (el->isNote())
|
2018-01-17 13:25:23 +01:00
|
|
|
c = toNote(el)->chord();
|
|
|
|
else if (el->isChord())
|
|
|
|
c = toChord(el);
|
2013-02-21 14:28:16 +01:00
|
|
|
else
|
|
|
|
return false;
|
2018-01-17 13:25:23 +01:00
|
|
|
Articulation* oa = c->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
|
|
|
}
|
2018-01-17 13:25:23 +01:00
|
|
|
a->setParent(c);
|
|
|
|
a->setTrack(c->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()) {
|
2018-03-27 15:36:00 +02:00
|
|
|
m->undoChangeProperty(Pid::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.
|
2016-12-13 13:16:17 +01:00
|
|
|
if (staff->staffType(cr->tick())->group() != StaffGroup::STANDARD ||
|
|
|
|
staves->at(rstaff+staffMove-1)->staffType(cr->tick())->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
|
|
|
}
|
|
|
|
}
|
2016-12-13 13:16:17 +01: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.
|
2016-12-13 13:16:17 +01:00
|
|
|
if (staff->staffType(cr->tick())->group() != StaffGroup::STANDARD ||
|
|
|
|
staves->at(staffMove+rstaff+1)->staffType(cr->tick())->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;
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction startTick = selection().tickStart();
|
|
|
|
Fraction 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;
|
2018-03-27 15:36:00 +02:00
|
|
|
m->undoChangeProperty(Pid::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
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction endTick = selection().tickEnd();
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2017-03-08 13:12:26 +01:00
|
|
|
for (Segment* seg = selection().firstChordRestSegment(); seg && seg->tick() < endTick; seg = seg->next1(SegmentType::ChordRest)) {
|
2016-03-02 22:08:20 +01:00
|
|
|
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;
|
2017-01-18 14:16:33 +01:00
|
|
|
if (cr->type() == ElementType::CHORD) {
|
2014-06-26 07:45:09 +02:00
|
|
|
if (cr->beamMode() != Beam::Mode::AUTO)
|
2018-03-27 15:36:00 +02:00
|
|
|
cr->undoChangeProperty(Pid::BEAM_MODE, int(Beam::Mode::AUTO));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2017-01-18 14:16:33 +01:00
|
|
|
else if (cr->type() == ElementType::REST) {
|
2014-06-26 07:45:09 +02:00
|
|
|
if (cr->beamMode() != Beam::Mode::NONE)
|
2018-03-27 15:36:00 +02:00
|
|
|
cr->undoChangeProperty(Pid::BEAM_MODE, int(Beam::Mode::NONE));
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-01-18 15:53:54 +01:00
|
|
|
if (noSelection)
|
|
|
|
deselectAll();
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2018-10-29 11:03:23 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdResetStyle
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdResetStyle()
|
|
|
|
{
|
|
|
|
style().reset(this);
|
|
|
|
}
|
|
|
|
|
2016-08-19 22:58:23 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdResetNoteAndRestGroupings
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdResetNoteAndRestGroupings()
|
|
|
|
{
|
2019-01-18 15:53:54 +01:00
|
|
|
bool noSelection = selection().isNone();
|
|
|
|
if (noSelection)
|
2016-08-19 22:58:23 +02:00
|
|
|
cmdSelectAll();
|
|
|
|
else if (!selection().isRange()) {
|
|
|
|
qDebug("no system or staff selected");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// save selection values because selection changes during grouping
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction sTick = selection().tickStart();
|
|
|
|
Fraction eTick = selection().tickEnd();
|
2016-08-19 22:58:23 +02:00
|
|
|
int sStaff = selection().staffStart();
|
|
|
|
int eStaff = selection().staffEnd();
|
|
|
|
|
|
|
|
startCmd();
|
|
|
|
for (int staff = sStaff; staff < eStaff; staff++) {
|
|
|
|
int sTrack = staff * VOICES;
|
|
|
|
int eTrack = sTrack + VOICES;
|
|
|
|
for (int track = sTrack; track < eTrack; track++) {
|
|
|
|
if (selectionFilter().canSelectVoice(track))
|
|
|
|
regroupNotesAndRests(sTick, eTick, track);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
endCmd();
|
2019-01-18 15:53:54 +01:00
|
|
|
if (noSelection)
|
|
|
|
deselectAll();
|
2016-08-19 22:58:23 +02:00
|
|
|
}
|
|
|
|
|
2018-10-30 17:24:25 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// resetElementShapePosition
|
|
|
|
// For use with Score::scanElements.
|
|
|
|
// Reset positions and autoplacement for the given
|
|
|
|
// element.
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static void resetElementPosition(void*, Element* e)
|
|
|
|
{
|
2018-12-02 15:27:25 +01:00
|
|
|
if (e->generated())
|
|
|
|
return;
|
2018-10-30 17:24:25 +01:00
|
|
|
e->undoResetProperty(Pid::AUTOPLACE);
|
|
|
|
e->undoResetProperty(Pid::OFFSET);
|
2019-05-09 21:06:59 +02:00
|
|
|
e->setOffsetChanged(false);
|
2018-10-30 17:24:25 +01:00
|
|
|
if (e->isSpanner())
|
|
|
|
e->undoResetProperty(Pid::OFFSET2);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdResetAllPositions
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdResetAllPositions()
|
|
|
|
{
|
|
|
|
startCmd();
|
|
|
|
scanElements(nullptr, resetElementPosition);
|
|
|
|
endCmd();
|
|
|
|
}
|
|
|
|
|
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");
|
|
|
|
|
2016-08-08 17:46:37 +02:00
|
|
|
NoteEntryMethod entryMethod = _is.noteEntryMethod();
|
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();
|
2016-08-08 17:46:37 +02:00
|
|
|
for (auto itr = activeMidiPitches()->begin(); itr != activeMidiPitches()->end();) {
|
|
|
|
if ((*itr).pitch == ev.pitch)
|
|
|
|
itr = activeMidiPitches()->erase(itr);
|
|
|
|
else
|
|
|
|
++itr;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
if (MScore::debugMode)
|
|
|
|
qDebug("<-- !noteentry dequeue %i", ev.pitch);
|
2016-08-08 17:46:37 +02:00
|
|
|
if (!noteEntryMode()
|
|
|
|
|| entryMethod == NoteEntryMethod::REALTIME_AUTO
|
|
|
|
|| entryMethod == NoteEntryMethod::REALTIME_MANUAL) {
|
2012-05-26 14:26:10 +02:00
|
|
|
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) {
|
2018-03-27 15:36:00 +02:00
|
|
|
if (!styleB(Sid::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(
|
fix #275313: rework mixer ui 2
Moving PartEditBase into separate file. Creating new files for
building mixer.
Creating art assets/UI design for new components.
Styling the track control.
Adding track area.
Separating out score from update.
Creating instances of mixer UI.
Creating part per voice now.
Can click on tracks to select them now.
Can now switch bwtewwn tracks.
Setting patch channel now.
Setting enabled off when no track selected.
Improving slider ui.
Turning Channel into a class and adding listener to it.
Somewhat stabalized sharing track objects between interfaces.
Can now apply volume changes to both expanded and collapsed tracks.
Pan knob is now working.
Encapsulating the rest of the fields in Channel.
Mute and solo now working.
Reverb and chorus now working.
Drumkit checkbox now working. Port and channel somewhat working.
Adding support for colors per track.
Part name change now working.
Separating out MixerTrackItem
Finishing moving MixerTrackItem to new file.
Cleaning up code.
Moving PartEditBase into separate file. Creating new files for
building mixer.
Creating art assets/UI design for new components.
Styling the track control.
Adding track area.
Separating out score from update.
Creating instances of mixer UI.
Creating part per voice now.
Can click on tracks to select them now.
Can now switch bwtewwn tracks.
Setting patch channel now.
Setting enabled off when no track selected.
Improving slider ui.
Turning Channel into a class and adding listener to it.
Somewhat stabalized sharing track objects between interfaces.
Can now apply volume changes to both expanded and collapsed tracks.
Pan knob is now working.
Encapsulating the rest of the fields in Channel.
Mute and solo now working.
Reverb and chorus now working.
Drumkit checkbox now working. Port and channel somewhat working.
Adding support for colors per track.
Part name change now working.
Separating out MixerTrackItem
Finishing moving MixerTrackItem to new file.
Cleaning up code.
Setting color in collapsed mode now affects all channels.
Using shared_ptr to track MixerTrackItem. Part changes now affect
all instruments.
Creating new track UI object to handle parts.
Using shard_ptr to track MixerTrackItem objects.
setting port and channel data now.
Changing to horizontal layout.
Fixing knob display. Chaning track control appearance.
Setting init slider window size.
Switchong back to vertical orientation. Fixing a few UI bugs in
the slider.
Tracks now left aligned.
Moving details panel above mixer. Now changing track selection when
user clicks on sliders.
Pan and volume controls now reflect track color.
Showing volume and pan values in tooltips.
Creating a new slider control for mixer.
Switching Channel's volume, pan, reverb and chorus and chaning them
to doubles with a decimal range.
No longer writing out vol, pan, chor, reverb when at default values.
Nolonger writing vol, pan, chorus, reverb as controler values in
output file.
Now testing against default values on write.
More export fixes.
Manually editing test files to reflect new channel parameters.
Manually editing more test files to reflect new channel parameters.
Manually editing more test files to reflect new channel parameters.
More test changes to make Travis happy.
More test changes to make Travis happy.
Importing MusicXML now matches new volume, pan ranges.
Changing range of pan. Fixing a few bugs with calculating MIDI.
Altering test files for Travis.
fix #275313: rework-mixer-ui-2
Moving PartEditBase into separate file. Creating new files for
building mixer.
Creating art assets/UI design for new components.
Styling the track control.
Adding track area.
Separating out score from update.
Creating instances of mixer UI.
Creating part per voice now.
Can click on tracks to select them now.
Can now switch bwtewwn tracks.
Setting patch channel now.
Setting enabled off when no track selected.
Improving slider ui.
Turning Channel into a class and adding listener to it.
Somewhat stabalized sharing track objects between interfaces.
Can now apply volume changes to both expanded and collapsed tracks.
Pan knob is now working.
Encapsulating the rest of the fields in Channel.
Mute and solo now working.
Reverb and chorus now working.
Drumkit checkbox now working. Port and channel somewhat working.
Adding support for colors per track.
Part name change now working.
Separating out MixerTrackItem
Finishing moving MixerTrackItem to new file.
Cleaning up code.
Moving PartEditBase into separate file. Creating new files for
building mixer.
Creating art assets/UI design for new components.
Styling the track control.
Adding track area.
Separating out score from update.
Creating instances of mixer UI.
Creating part per voice now.
Can click on tracks to select them now.
Can now switch bwtewwn tracks.
Setting patch channel now.
Setting enabled off when no track selected.
Improving slider ui.
Turning Channel into a class and adding listener to it.
Somewhat stabalized sharing track objects between interfaces.
Can now apply volume changes to both expanded and collapsed tracks.
Pan knob is now working.
Encapsulating the rest of the fields in Channel.
Mute and solo now working.
Reverb and chorus now working.
Drumkit checkbox now working. Port and channel somewhat working.
Adding support for colors per track.
Part name change now working.
Separating out MixerTrackItem
Finishing moving MixerTrackItem to new file.
Cleaning up code.
Setting color in collapsed mode now affects all channels.
Using shared_ptr to track MixerTrackItem. Part changes now affect
all instruments.
Creating new track UI object to handle parts.
Using shard_ptr to track MixerTrackItem objects.
setting port and channel data now.
Changing to horizontal layout.
Fixing knob display. Chaning track control appearance.
Setting init slider window size.
Switchong back to vertical orientation. Fixing a few UI bugs in
the slider.
Tracks now left aligned.
Moving details panel above mixer. Now changing track selection when
user clicks on sliders.
Pan and volume controls now reflect track color.
Showing volume and pan values in tooltips.
Creating a new slider control for mixer.
Switching Channel's volume, pan, reverb and chorus and chaning them
to doubles with a decimal range.
No longer writing out vol, pan, chor, reverb when at default values.
Nolonger writing vol, pan, chorus, reverb as controler values in
output file.
Now testing against default values on write.
More export fixes.
Manually editing test files to reflect new channel parameters.
Manually editing more test files to reflect new channel parameters.
Manually editing more test files to reflect new channel parameters.
More test changes to make Travis happy.
More test changes to make Travis happy.
Importing MusicXML now matches new volume, pan ranges.
Changing range of pan. Fixing a few bugs with calculating MIDI.
Altering test files for Travis.
Restoring the volume, pan, chorus, reverb to original char data type
& range. UI now shows different 'user friendly' ranges.
Overwriting tests with versions from master.
mtest/libmscore/compat114/clef_missing_first-ref.mscx
mtest/libmscore/compat114/hor_frame_and_mmrest-ref.mscx
mtest/musicxml/io/testInstrumentChangeMIDIportExport_ref.xml
mtest/musicxml/io/testUninitializedDivisions_ref.xml
Restoring test files to original state.
Restoring test files to original state.
Restoring old values for importing files.
Restoring part methods.
mtest/importmidi/simplify_8th_dotted_no_staccato.mscx
mtest/libmscore/compat114/clef_missing_first-ref.mscx
mtest/libmscore/compat114/hor_frame_and_mmrest-ref.mscx
mtest/musicxml/io/testInstrumentChangeMIDIportExport_ref.xml
mtest/musicxml/io/testUninitializedDivisions_ref.xml
Rearranging UI components for better feel.
Improving UI. Fixed crash when changing part name.
Adding support for two lighting modes. Showing part name over
channel expansion.
Adding master gain control to mixer.
Changing color of gain slider.
Adapting to latest source in main.
Changing master gain slider to use decibel calculation.
CSS now set on tracks whenever a Paint event received.
Restoring mixer slider values to refect MIDI ranges. Fixing crash when drumkit checked.
Fixing crash when closing score.
Fixing alignment in mixer details.
Tweaking UI for better appearance.
2018-11-13 18:43:19 +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
|
|
|
}
|
2016-08-08 17:46:37 +02:00
|
|
|
if (noteEntryMode()) {
|
|
|
|
if (ev.velocity == 0) {
|
|
|
|
// delete note in realtime mode
|
2017-12-20 16:49:30 +01:00
|
|
|
//Chord* chord = toChord(_is.cr());
|
2016-08-08 17:46:37 +02:00
|
|
|
//std::vector<Note*> notes = chord->notes();
|
|
|
|
if (entryMethod == NoteEntryMethod::REALTIME_AUTO || entryMethod == NoteEntryMethod::REALTIME_MANUAL) {
|
|
|
|
if (_is.cr()->isChord()) {
|
2017-12-20 16:49:30 +01:00
|
|
|
Note* n = toChord(_is.cr())->findNote(ev.pitch);
|
2016-08-08 17:46:37 +02:00
|
|
|
if (n) {
|
|
|
|
qDebug("Pitches match! Note %i, Pitch %i", n->pitch(), ev.pitch);
|
|
|
|
if (!cmdActive) {
|
|
|
|
startCmd();
|
|
|
|
cmdActive = true;
|
|
|
|
}
|
2018-03-21 01:26:40 +01:00
|
|
|
deleteItem(n->tieBack());
|
|
|
|
deleteItem(n);
|
2016-08-08 17:46:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-04-20 00:24:35 +02:00
|
|
|
continue;
|
2016-08-08 17:46:37 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
if (!cmdActive) {
|
|
|
|
startCmd();
|
|
|
|
cmdActive = true;
|
|
|
|
}
|
2018-03-21 01:26:40 +01:00
|
|
|
if (activeMidiPitches()->empty())
|
|
|
|
ev.chord = false;
|
|
|
|
else
|
|
|
|
ev.chord = true;
|
2018-12-22 19:39:53 +01:00
|
|
|
|
2018-12-28 14:50:35 +01:00
|
|
|
// holding shift while inputting midi will add the new pitch to the prior existing chord
|
|
|
|
if (qApp->keyboardModifiers() & Qt::ShiftModifier) {
|
|
|
|
Element* cr = _is.lastSegment()->element(_is.track());
|
|
|
|
if (cr && cr->isChord())
|
2018-12-22 19:39:53 +01:00
|
|
|
ev.chord = true;
|
|
|
|
}
|
|
|
|
|
2016-08-08 17:46:37 +02:00
|
|
|
// TODO: add shadow note instead of real note in realtime modes
|
|
|
|
// (note becomes real when realtime-advance triggered).
|
|
|
|
addMidiPitch(ev.pitch, ev.chord);
|
|
|
|
activeMidiPitches()->push_back(ev);
|
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()) {
|
2017-01-18 14:16:33 +01:00
|
|
|
case ElementType::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;
|
2017-01-18 14:16:33 +01:00
|
|
|
case ElementType::CHORD: // a chord or a rest are valid targets
|
|
|
|
case ElementType::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;
|
2017-01-18 14:16:33 +01:00
|
|
|
case ElementType::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-12-12 10:31:37 +01:00
|
|
|
if (!seg->isChordRest()) {
|
2017-03-08 13:12:26 +01:00
|
|
|
seg = seg->next1(SegmentType::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,
|
2018-09-11 16:56:50 +02:00
|
|
|
int size = int(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
|
2017-01-18 14:16:33 +01:00
|
|
|
if (trg->type() == ElementType::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();
|
2017-03-08 13:12:26 +01:00
|
|
|
Segment* s = _is.segment()->prev1(SegmentType::ChordRest);
|
2015-08-18 23:00:07 +02:00
|
|
|
int track = _is.track();
|
2017-03-08 13:12:26 +01:00
|
|
|
for (; s; s = s->prev1(SegmentType::ChordRest)) {
|
2019-05-29 10:28:17 +02:00
|
|
|
if (s->element(track) || (s->measure() != m && s->rtick().isZero())) {
|
2016-05-17 22:14:29 +02:00
|
|
|
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) {
|
2017-01-18 14:16:33 +01:00
|
|
|
if (el->type() == ElementType::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)
|
2018-09-25 15:55:08 +02:00
|
|
|
if (_is.cr() || !el->selected())
|
2015-08-18 04:00:57 +02:00
|
|
|
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();
|
2019-02-09 00:20:02 +01:00
|
|
|
for (Element* e : el) {
|
|
|
|
if (e->isNote()) {
|
2016-06-14 10:32:34 +02:00
|
|
|
Note* note = toNote(e);
|
2016-12-13 13:16:17 +01:00
|
|
|
if (note->staff() && note->staff()->isTabStaff(note->chord()->tick()))
|
2018-03-27 15:36:00 +02:00
|
|
|
e->undoChangeProperty(Pid::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);
|
|
|
|
}
|
|
|
|
}
|
2019-02-09 00:20:02 +01:00
|
|
|
else if (e->isHairpinSegment()) {
|
|
|
|
Hairpin* h = toHairpinSegment(e)->hairpin();
|
|
|
|
HairpinType st = h->hairpinType();
|
|
|
|
switch (st) {
|
|
|
|
case HairpinType::CRESC_HAIRPIN:
|
|
|
|
st = HairpinType::DECRESC_HAIRPIN;
|
|
|
|
break;
|
|
|
|
case HairpinType::DECRESC_HAIRPIN:
|
|
|
|
st = HairpinType::CRESC_HAIRPIN;
|
|
|
|
break;
|
|
|
|
case HairpinType::CRESC_LINE:
|
|
|
|
st = HairpinType::DECRESC_LINE;
|
|
|
|
break;
|
|
|
|
case HairpinType::DECRESC_LINE:
|
|
|
|
st = HairpinType::CRESC_LINE;
|
|
|
|
break;
|
|
|
|
case HairpinType::INVALID:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
h->undoChangeProperty(Pid::HAIRPIN_TYPE, int(st));
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2017-02-20 08:54:51 +01:00
|
|
|
// cmdIncDecDuration
|
|
|
|
// When stepDotted is false and nSteps is 1 or -1, will halve or double the duration
|
|
|
|
// When stepDotted is true, will step by nearest dotted or undotted note
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2017-02-20 08:54:51 +01:00
|
|
|
void Score::cmdIncDecDuration(int nSteps, bool stepDotted)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
Element* el = selection().element();
|
|
|
|
if (el == 0)
|
|
|
|
return;
|
2019-01-30 15:13:54 +01:00
|
|
|
if (el->isNote())
|
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
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
// if measure rest is selected as input, then the correct initialDuration will be the
|
|
|
|
// duration of the measure's time signature, else is just the input state's duration
|
2017-02-20 08:54:51 +01:00
|
|
|
TDuration initialDuration = (cr->durationType() == TDuration::DurationType::V_MEASURE) ? TDuration(cr->measure()->timesig()) : _is.duration();
|
|
|
|
TDuration d = initialDuration.shiftRetainDots(nSteps, stepDotted);
|
2016-06-12 07:55:06 +02:00
|
|
|
if (!d.isValid())
|
2012-05-26 14:26:10 +02:00
|
|
|
return;
|
2019-01-30 15:13:54 +01:00
|
|
|
if (cr->isChord() && (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()
|
|
|
|
{
|
2019-04-29 11:01:54 +02:00
|
|
|
for (Element* el : selection().elements()) {
|
|
|
|
if (el->type() == ElementType::ACCIDENTAL) {
|
|
|
|
Accidental* acc = toAccidental(el);
|
|
|
|
acc->undoChangeProperty(Pid::ACCIDENTAL_BRACKET, int(AccidentalBracket::BRACKET));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdAddParentheses
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdAddParentheses()
|
|
|
|
{
|
|
|
|
for (Element* el : selection().elements()) {
|
2017-01-18 14:16:33 +01:00
|
|
|
if (el->type() == ElementType::NOTE) {
|
2016-06-14 10:32:34 +02:00
|
|
|
Note* n = toNote(el);
|
2017-01-31 19:30:49 +01:00
|
|
|
n->addParentheses();
|
2014-07-18 00:11:00 +02:00
|
|
|
}
|
2017-01-18 14:16:33 +01:00
|
|
|
else if (el->type() == ElementType::ACCIDENTAL) {
|
2016-06-14 10:32:34 +02:00
|
|
|
Accidental* acc = toAccidental(el);
|
2018-03-27 15:36:00 +02:00
|
|
|
acc->undoChangeProperty(Pid::ACCIDENTAL_BRACKET, int(AccidentalBracket::PARENTHESIS));
|
2014-07-18 00:11:00 +02:00
|
|
|
}
|
2017-01-18 14:16:33 +01:00
|
|
|
else if (el->type() == ElementType::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();
|
|
|
|
}
|
2019-04-29 11:27:54 +02:00
|
|
|
else if (el->type() == ElementType::TIMESIG) {
|
|
|
|
TimeSig* ts = toTimeSig(el);
|
|
|
|
ts->setLargeParentheses(true);
|
|
|
|
}
|
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
|
|
|
{
|
2018-10-18 11:53:01 +02:00
|
|
|
QPointF pos(rest->offset());
|
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();
|
2018-10-18 11:53:01 +02:00
|
|
|
rest->undoChangeProperty(Pid::OFFSET, 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
|
|
|
{
|
2018-07-19 13:03:25 +02:00
|
|
|
int verse = lyrics->no() + (dir == Direction::UP ? -1 : 1);
|
|
|
|
if (verse < 0)
|
|
|
|
return;
|
|
|
|
lyrics->undoChangeProperty(Pid::VERSE, verse);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
2014-08-06 10:15:58 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdInsertClef
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdInsertClef(ClefType type)
|
|
|
|
{
|
|
|
|
if (!noteEntryMode())
|
|
|
|
return;
|
2018-05-23 16:30:20 +02:00
|
|
|
undoChangeClef(staff(inputTrack()/VOICES), inputState().cr(), type);
|
2014-08-06 10:15:58 +02:00
|
|
|
}
|
|
|
|
|
2014-07-25 17:13:27 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdInsertClef
|
|
|
|
// insert clef before cr
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdInsertClef(Clef* clef, ChordRest* cr)
|
|
|
|
{
|
2018-05-23 16:30:20 +02:00
|
|
|
undoChangeClef(cr->staff(), cr, clef->clefType());
|
2014-07-25 17:13:27 +02:00
|
|
|
delete clef;
|
|
|
|
}
|
|
|
|
|
2015-01-17 22:40:28 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdAddGrace
|
|
|
|
/// adds grace note of specified type to selected notes
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-12-06 20:32:08 +01:00
|
|
|
void Score::cmdAddGrace (NoteType graceType, int duration)
|
|
|
|
{
|
2019-09-23 11:28:56 +02:00
|
|
|
const QList<Element*> copyOfElements = selection().elements();
|
|
|
|
for (Element* e : copyOfElements) {
|
2017-01-18 14:16:33 +01:00
|
|
|
if (e->type() == ElementType::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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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();
|
2016-07-27 23:30:51 +02:00
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction lTick = endMeasure->endTick();
|
2016-07-27 23:30:51 +02:00
|
|
|
bool voice = false;
|
|
|
|
|
|
|
|
for (Measure* m = startMeasure; m && m->tick() != lTick; m = m->nextMeasure()) {
|
|
|
|
if (m->hasVoices(srcStaff)) {
|
|
|
|
voice = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (! voice) {
|
|
|
|
// force complete measures
|
|
|
|
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);
|
2017-01-18 14:16:33 +01:00
|
|
|
if (e && e->type() == ElementType::CHORD) {
|
2016-07-27 23:30:51 +02:00
|
|
|
Chord* c = toChord(e);
|
|
|
|
n = qMax(n, int(c->notes().size()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lastStaff = qMin(nstaves(), srcStaff + n);
|
|
|
|
}
|
|
|
|
|
2018-11-29 14:48:20 +01:00
|
|
|
const QByteArray mimeData(selection().mimeData());
|
2016-07-27 23:30:51 +02:00
|
|
|
// copy to all destination staves
|
|
|
|
Segment* firstCRSegment = startMeasure->tick2segment(startMeasure->tick());
|
|
|
|
for (int i = 1; srcStaff + i < lastStaff; ++i) {
|
|
|
|
int track = (srcStaff + i) * VOICES;
|
|
|
|
ChordRest* cr = toChordRest(firstCRSegment->element(track));
|
|
|
|
if (cr) {
|
2018-11-29 14:48:20 +01:00
|
|
|
XmlReader e(mimeData);
|
2016-07-27 23:30:51 +02:00
|
|
|
e.setPasteMode(true);
|
2016-12-01 16:56:10 +01:00
|
|
|
pasteStaff(e, cr->segment(), cr->staffIdx());
|
2014-11-09 02:02:37 +01:00
|
|
|
}
|
|
|
|
}
|
2016-07-27 23:30:51 +02:00
|
|
|
|
|
|
|
// 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);
|
2017-01-18 14:16:33 +01:00
|
|
|
if (e && e->type() == ElementType::CHORD) {
|
2016-07-27 23:30:51 +02:00
|
|
|
Chord* c = toChord(e);
|
|
|
|
std::vector<Note*> notes = c->notes();
|
2018-09-11 16:56:50 +02:00
|
|
|
int nnotes = int(notes.size());
|
2016-07-27 23:30:51 +02:00
|
|
|
// 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);
|
|
|
|
}
|
2014-11-09 02:02:37 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-07-27 23:30:51 +02:00
|
|
|
else {
|
|
|
|
int sTracks[VOICES];
|
|
|
|
int dTracks[VOICES];
|
|
|
|
if (srcStaff == lastStaff - 1)
|
|
|
|
lastStaff = qMin(nstaves(), srcStaff + VOICES);
|
|
|
|
|
|
|
|
for (int i = 0; i < VOICES; i++) {
|
|
|
|
sTracks[i] = -1;
|
|
|
|
dTracks[i] = -1;
|
|
|
|
}
|
|
|
|
int full = 0;
|
|
|
|
|
|
|
|
for (Segment* seg = startSegment; seg && seg->tick() < lTick; seg = seg->next1()) {
|
2019-05-17 11:57:20 +02:00
|
|
|
for (int i = srcTrack; i < srcTrack + VOICES && full != VOICES; i++) {
|
2016-07-27 23:30:51 +02:00
|
|
|
bool t = true;
|
|
|
|
for (int j = 0; j < VOICES; j++) {
|
|
|
|
if (i == sTracks[j]) {
|
|
|
|
t = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!seg->measure()->hasVoice(i) || seg->measure()->isOnlyRests(i) || !t)
|
|
|
|
continue;
|
|
|
|
sTracks[full] = i;
|
|
|
|
|
|
|
|
for(int j = srcTrack + full * VOICES; j < lastStaff * VOICES; j++) {
|
|
|
|
if (i == j) {
|
|
|
|
dTracks[full] = j;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
for(Measure* m = seg->measure(); m && m->tick() < lTick; m = m->nextMeasure()) {
|
|
|
|
if (!m->hasVoice(j) || (m->hasVoice(j) && m->isOnlyRests(j)))
|
|
|
|
dTracks[full] = j;
|
|
|
|
else {
|
|
|
|
dTracks[full] = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (dTracks[full] != -1)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
full++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = srcTrack, j = 0; i < lastStaff * VOICES && j < VOICES ; i += VOICES, j++) {
|
|
|
|
int strack = sTracks[j % VOICES];
|
|
|
|
int dtrack = dTracks[j % VOICES];
|
|
|
|
if (strack != -1 && strack != dtrack && dtrack != -1)
|
|
|
|
undo(new CloneVoice(startSegment, lTick, startSegment, strack, dtrack, -1, false));
|
|
|
|
}
|
|
|
|
}
|
2014-11-09 02:02:37 +01:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
2016-07-27 23:43:46 +02:00
|
|
|
int dstStaff = selection().staffStart();
|
|
|
|
int endStaff = selection().staffEnd();
|
|
|
|
int dstTrack = dstStaff * VOICES;
|
2014-12-04 16:45:45 +01:00
|
|
|
int startTrack = dstStaff * VOICES;
|
2016-07-27 23:43:46 +02:00
|
|
|
int endTrack = endStaff * 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();
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction startTick = startSegment->tick();
|
|
|
|
Fraction endTick = endSegment ? endSegment->tick() : lastMeasure()->endTick();
|
2018-12-30 05:08:32 +01:00
|
|
|
Q_ASSERT(startMeasure && endMeasure);
|
2014-11-09 02:02:37 +01:00
|
|
|
|
2016-07-27 23:43:46 +02:00
|
|
|
// if single staff selected, combine voices
|
|
|
|
// otherwise combine staves
|
|
|
|
if (dstStaff == endStaff - 1) {
|
|
|
|
|
|
|
|
// loop through segments adding notes to chord on top staff
|
|
|
|
for (Segment* s = startSegment; s && s != endSegment; s = s->next1()) {
|
|
|
|
if (!s->isChordRestType())
|
|
|
|
continue;
|
|
|
|
Element* dst = s->element(dstTrack);
|
|
|
|
if (dst && dst->isChord()) {
|
|
|
|
Chord* dstChord = toChord(dst);
|
|
|
|
// see if we are tying in to this chord
|
|
|
|
Chord* tied = 0;
|
|
|
|
for (Note* n : dstChord->notes()) {
|
|
|
|
if (n->tieBack()) {
|
|
|
|
tied = n->tieBack()->startNote()->chord();
|
|
|
|
break;
|
|
|
|
}
|
2014-11-09 02:02:37 +01:00
|
|
|
}
|
2016-07-27 23:43:46 +02:00
|
|
|
// loop through each subsequent staff (or track within staff)
|
|
|
|
// looking for notes to add
|
|
|
|
for (int srcTrack = startTrack + 1; srcTrack < endTrack; srcTrack++) {
|
|
|
|
Element* src = s->element(srcTrack);
|
|
|
|
if (src && src->isChord()) {
|
|
|
|
Chord* srcChord = toChord(src);
|
|
|
|
// when combining voices, skip if not same duration
|
2019-01-30 15:13:54 +01:00
|
|
|
if (srcChord->ticks() != dstChord->ticks())
|
2014-11-09 02:02:37 +01:00
|
|
|
continue;
|
2016-07-27 23:43:46 +02:00
|
|
|
// add notes
|
|
|
|
for (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
|
|
|
|
for (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-11-09 02:02:37 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-07-27 23:43:46 +02:00
|
|
|
// delete chordrest from source track if possible
|
|
|
|
if (src && src->voice())
|
|
|
|
undoRemoveElement(src);
|
|
|
|
}
|
|
|
|
}
|
2018-12-05 02:09:42 +01:00
|
|
|
// TODO - use first voice that actually has a note and implode remaining voices on it?
|
|
|
|
// see https://musescore.org/en/node/174111
|
2016-07-27 23:43:46 +02:00
|
|
|
else if (dst) {
|
|
|
|
// destination track has something, but it isn't a chord
|
2018-12-05 03:30:41 +01:00
|
|
|
// remove rests from other voices if in "voice mode"
|
2016-07-27 23:43:46 +02:00
|
|
|
for (int i = 1; i < VOICES; ++i) {
|
|
|
|
Element* e = s->element(dstTrack + i);
|
2018-12-05 03:30:41 +01:00
|
|
|
if (e && e->isRest())
|
2016-07-27 23:43:46 +02:00
|
|
|
undoRemoveElement(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-12-30 10:38:28 +01:00
|
|
|
// delete orphaned spanners (TODO: figure out solution to reconnect orphaned spanners to their cloned notes)
|
|
|
|
checkSpanner(startTick, endTick);
|
2016-07-27 23:43:46 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
int tracks[VOICES];
|
|
|
|
for (int i = 0; i < VOICES; i++)
|
|
|
|
tracks[i] = -1;
|
|
|
|
int full = 0;
|
|
|
|
|
2018-12-05 02:09:42 +01:00
|
|
|
// identify tracks to combine, storing the source track numbers in tracks[]
|
|
|
|
// first four non-empty tracks to win
|
|
|
|
for (int track = startTrack; track < endTrack && full < VOICES; ++track) {
|
2018-12-30 05:08:32 +01:00
|
|
|
Measure* m = startMeasure;
|
|
|
|
do {
|
2018-12-05 02:09:42 +01:00
|
|
|
if (m->hasVoice(track) && !m->isOnlyRests(track)) {
|
|
|
|
tracks[full++] = track;
|
|
|
|
break;
|
|
|
|
}
|
2018-12-30 05:08:32 +01:00
|
|
|
} while ((m != endMeasure) && (m = m->nextMeasure()));
|
2014-12-04 16:45:45 +01:00
|
|
|
}
|
2016-07-27 23:43:46 +02:00
|
|
|
|
2018-12-05 02:09:42 +01:00
|
|
|
// clone source tracks into destination
|
2016-07-27 23:43:46 +02:00
|
|
|
for (int i = dstTrack; i < dstTrack + VOICES; i++) {
|
|
|
|
int strack = tracks[i % VOICES];
|
|
|
|
if (strack != -1 && strack != i) {
|
2018-12-30 10:38:28 +01:00
|
|
|
undo( new CloneVoice(startSegment, endTick, startSegment, strack, i, i, false));
|
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();
|
2019-06-14 10:27:51 +02:00
|
|
|
|
|
|
|
// operate on measures underlying mmrests
|
|
|
|
if (startSegment && startSegment->measure() && startSegment->measure()->isMMRest())
|
|
|
|
startSegment = startSegment->measure()->mmRestFirst()->first();
|
|
|
|
if (endSegment && endSegment->measure() && endSegment->measure()->isMMRest())
|
|
|
|
endSegment = endSegment->measure()->mmRestLast()->last();
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction endTick = endSegment ? endSegment->tick() : lastSegment()->tick() + Fraction::fromTicks(1);
|
2014-11-15 21:28:16 +01:00
|
|
|
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()) {
|
2017-03-08 13:12:26 +01:00
|
|
|
if (s->segmentType() != SegmentType::ChordRest)
|
2014-11-15 21:28:16 +01:00
|
|
|
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
|
2019-01-30 15:13:54 +01:00
|
|
|
if (s->rtick().ticks() % 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
|
2019-01-30 15:13:54 +01:00
|
|
|
if (voice == -1 || s->rtick().isZero()) {
|
2014-12-31 02:32:58 +01:00
|
|
|
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
|
2017-01-18 14:16:33 +01:00
|
|
|
else if (cr->type() == ElementType::CHORD)
|
2014-12-31 02:32:58 +01:00
|
|
|
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;
|
2017-03-08 13:12:26 +01:00
|
|
|
for (Segment* ns = s->next(SegmentType::ChordRest); ns && ns != endSegment; ns = ns->next(SegmentType::ChordRest)) {
|
2016-06-14 10:32:34 +02:00
|
|
|
ChordRest* ncr = toChordRest(ns->element(track + voice));
|
2017-01-18 14:16:33 +01:00
|
|
|
if (ncr && ncr->type() == ElementType::CHORD) {
|
2014-12-31 02:32:58 +01:00
|
|
|
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;
|
2016-12-13 13:16:17 +01:00
|
|
|
if (staff(staffIdx)->staffType(s->tick())->group() == StaffGroup::TAB)
|
|
|
|
line = staff(staffIdx)->lines(s->tick()) / 2;
|
2014-11-15 21:28:16 +01:00
|
|
|
else
|
2016-12-13 13:16:17 +01:00
|
|
|
line = staff(staffIdx)->middleLine(s->tick()); // staff(staffIdx)->lines() - 1;
|
|
|
|
if (staff(staffIdx)->staffType(s->tick())->group() == StaffGroup::PERCUSSION) {
|
2014-11-15 21:28:16 +01:00
|
|
|
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
|
2019-10-25 00:36:19 +02:00
|
|
|
nv = noteValForPosition(p, AccidentalType::NONE, error);
|
2014-11-15 21:28:16 +01:00
|
|
|
}
|
|
|
|
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()) {
|
2017-12-20 16:49:30 +01:00
|
|
|
Chord* lc = toChord(e);
|
2014-11-15 21:28:16 +01:00
|
|
|
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()) {
|
2018-10-25 15:43:59 +02:00
|
|
|
if (e->voice() >= 2 && e->isRest()) {
|
2016-06-14 10:32:34 +02:00
|
|
|
Rest* r = toRest(e);
|
2015-08-18 18:15:29 +02:00
|
|
|
if (r->links()) {
|
2018-09-25 15:55:08 +02:00
|
|
|
for (ScoreElement* se : *r->links()) {
|
|
|
|
Rest* lr = toRest(se);
|
2015-08-18 18:15:29 +02:00
|
|
|
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
|
|
|
}
|
2018-10-25 15:43:59 +02:00
|
|
|
else if (e->isNote()) {
|
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()) {
|
2018-09-25 15:55:08 +02:00
|
|
|
for (ScoreElement* se : *c->links()) {
|
|
|
|
Chord* lc = toChord(se);
|
2015-08-18 18:15:29 +02:00
|
|
|
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()) {
|
2017-01-18 14:16:33 +01:00
|
|
|
if (e->type() == ElementType::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())
|
2018-03-27 15:36:00 +02:00
|
|
|
le->undoChangeProperty(Pid::TEXT, rmText);
|
2014-12-09 08:45:30 +01:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-09-30 12:44:27 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdRemoveEmptyTrailingMeasures
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdRemoveEmptyTrailingMeasures()
|
|
|
|
{
|
|
|
|
MasterScore* score = masterScore();
|
|
|
|
Measure* firstMeasure;
|
|
|
|
Measure* lastMeasure = score->lastMeasure();
|
2016-09-30 13:32:48 +02:00
|
|
|
if (!lastMeasure || !lastMeasure->isFullMeasureRest())
|
2016-09-30 12:44:27 +02:00
|
|
|
return;
|
|
|
|
firstMeasure = lastMeasure;
|
|
|
|
for (firstMeasure = lastMeasure;;) {
|
|
|
|
Measure* m = firstMeasure->prevMeasure();
|
|
|
|
if (!m || !m->isFullMeasureRest())
|
|
|
|
break;
|
|
|
|
firstMeasure = m;
|
|
|
|
}
|
|
|
|
deleteMeasures(firstMeasure, lastMeasure);
|
|
|
|
}
|
2016-12-06 20:32:08 +01:00
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdPitchUp
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdPitchUp()
|
|
|
|
{
|
|
|
|
Element* el = selection().element();
|
|
|
|
if (el && el->isLyrics())
|
|
|
|
cmdMoveLyrics(toLyrics(el), Direction::UP);
|
2018-04-18 14:31:36 +02:00
|
|
|
else if (el && (el->isArticulation() || el->isTextBase()))
|
2019-02-08 22:28:12 +01:00
|
|
|
el->undoChangeProperty(Pid::OFFSET, el->offset() + QPointF(0.0, -MScore::nudgeStep * el->spatium()), PropertyFlags::UNSTYLED);
|
2016-12-06 20:32:08 +01:00
|
|
|
else if (el && el->isRest())
|
|
|
|
cmdMoveRest(toRest(el), Direction::UP);
|
|
|
|
else
|
|
|
|
upDown(true, UpDownMode::CHROMATIC);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdPitchDown
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdPitchDown()
|
|
|
|
{
|
|
|
|
Element* el = selection().element();
|
|
|
|
if (el && el->isLyrics())
|
|
|
|
cmdMoveLyrics(toLyrics(el), Direction::DOWN);
|
2018-04-18 14:31:36 +02:00
|
|
|
else if (el && (el->isArticulation() || el->isTextBase()))
|
2019-02-08 22:28:12 +01:00
|
|
|
el->undoChangeProperty(Pid::OFFSET, el->offset() + QPointF(0.0, MScore::nudgeStep * el->spatium()), PropertyFlags::UNSTYLED);
|
2016-12-06 20:32:08 +01:00
|
|
|
else if (el && el->isRest())
|
|
|
|
cmdMoveRest(toRest(el), Direction::DOWN);
|
|
|
|
else
|
|
|
|
upDown(false, UpDownMode::CHROMATIC);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdTimeDelete
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-12-09 15:56:48 +01:00
|
|
|
void Score::cmdTimeDelete()
|
2016-12-06 20:32:08 +01:00
|
|
|
{
|
|
|
|
Element* e = selection().element();
|
|
|
|
if (e && e->isBarLine() && toBarLine(e)->segment()->isEndBarLineType()) {
|
|
|
|
Measure* m = toBarLine(e)->segment()->measure();
|
|
|
|
cmdJoinMeasure(m, m->nextMeasure());
|
|
|
|
}
|
2016-12-09 15:56:48 +01:00
|
|
|
else {
|
|
|
|
if (_is.insertMode())
|
|
|
|
globalTimeDelete();
|
|
|
|
else
|
|
|
|
localTimeDelete();
|
|
|
|
}
|
2016-12-06 20:32:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdPitchUpOctave
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdPitchUpOctave()
|
|
|
|
{
|
|
|
|
Element* el = selection().element();
|
2018-04-18 14:31:36 +02:00
|
|
|
if (el && (el->isArticulation() || el->isTextBase()))
|
2018-12-22 02:58:02 +01:00
|
|
|
el->undoChangeProperty(Pid::OFFSET, el->offset() + QPointF(0.0, -MScore::nudgeStep10 * el->spatium()), PropertyFlags::UNSTYLED);
|
2016-12-06 20:32:08 +01:00
|
|
|
else
|
|
|
|
upDown(true, UpDownMode::OCTAVE);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdPitchDownOctave
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdPitchDownOctave()
|
|
|
|
{
|
|
|
|
Element* el = selection().element();
|
2018-04-18 14:31:36 +02:00
|
|
|
if (el && (el->isArticulation() || el->isTextBase()))
|
2018-12-22 02:58:02 +01:00
|
|
|
el->undoChangeProperty(Pid::OFFSET, el->offset() + QPointF(0.0, MScore::nudgeStep10 * el->spatium()), PropertyFlags::UNSTYLED);
|
2016-12-06 20:32:08 +01:00
|
|
|
else
|
|
|
|
upDown(false, UpDownMode::OCTAVE);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdPadNoteInclreaseTAB
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2019-10-11 03:47:55 +02:00
|
|
|
void Score::cmdPadNoteIncreaseTAB(const EditData& ed)
|
2016-12-06 20:32:08 +01:00
|
|
|
{
|
|
|
|
switch (_is.duration().type() ) {
|
|
|
|
// cycle back from longest to shortest?
|
|
|
|
// case TDuration::V_LONG:
|
2019-10-11 03:47:55 +02:00
|
|
|
// padToggle(Pad::NOTE128, ed);
|
2016-12-06 20:32:08 +01:00
|
|
|
// break;
|
|
|
|
case TDuration::DurationType::V_BREVE:
|
2019-10-11 03:47:55 +02:00
|
|
|
padToggle(Pad::NOTE00, ed);
|
2016-12-06 20:32:08 +01:00
|
|
|
break;
|
|
|
|
case TDuration::DurationType::V_WHOLE:
|
2019-10-11 03:47:55 +02:00
|
|
|
padToggle(Pad::NOTE0, ed);
|
2016-12-06 20:32:08 +01:00
|
|
|
break;
|
|
|
|
case TDuration::DurationType::V_HALF:
|
2019-10-11 03:47:55 +02:00
|
|
|
padToggle(Pad::NOTE1, ed);
|
2016-12-06 20:32:08 +01:00
|
|
|
break;
|
|
|
|
case TDuration::DurationType::V_QUARTER:
|
2019-10-11 03:47:55 +02:00
|
|
|
padToggle(Pad::NOTE2, ed);
|
2016-12-06 20:32:08 +01:00
|
|
|
break;
|
|
|
|
case TDuration::DurationType::V_EIGHTH:
|
2019-10-11 03:47:55 +02:00
|
|
|
padToggle(Pad::NOTE4, ed);
|
2016-12-06 20:32:08 +01:00
|
|
|
break;
|
|
|
|
case TDuration::DurationType::V_16TH:
|
2019-10-11 03:47:55 +02:00
|
|
|
padToggle(Pad::NOTE8, ed);
|
2016-12-06 20:32:08 +01:00
|
|
|
break;
|
|
|
|
case TDuration::DurationType::V_32ND:
|
2019-10-11 03:47:55 +02:00
|
|
|
padToggle(Pad::NOTE16, ed);
|
2016-12-06 20:32:08 +01:00
|
|
|
break;
|
|
|
|
case TDuration::DurationType::V_64TH:
|
2019-10-11 03:47:55 +02:00
|
|
|
padToggle(Pad::NOTE32, ed);
|
2016-12-06 20:32:08 +01:00
|
|
|
break;
|
|
|
|
case TDuration::DurationType::V_128TH:
|
2019-10-11 03:47:55 +02:00
|
|
|
padToggle(Pad::NOTE64, ed);
|
2016-12-06 20:32:08 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdPadNoteDecreaseTAB
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2019-10-11 03:47:55 +02:00
|
|
|
void Score::cmdPadNoteDecreaseTAB(const EditData& ed)
|
2016-12-06 20:32:08 +01:00
|
|
|
{
|
|
|
|
switch (_is.duration().type() ) {
|
|
|
|
case TDuration::DurationType::V_LONG:
|
2019-10-11 03:47:55 +02:00
|
|
|
padToggle(Pad::NOTE0, ed);
|
2016-12-06 20:32:08 +01:00
|
|
|
break;
|
|
|
|
case TDuration::DurationType::V_BREVE:
|
2019-10-11 03:47:55 +02:00
|
|
|
padToggle(Pad::NOTE1, ed);
|
2016-12-06 20:32:08 +01:00
|
|
|
break;
|
|
|
|
case TDuration::DurationType::V_WHOLE:
|
2019-10-11 03:47:55 +02:00
|
|
|
padToggle(Pad::NOTE2, ed);
|
2016-12-06 20:32:08 +01:00
|
|
|
break;
|
|
|
|
case TDuration::DurationType::V_HALF:
|
2019-10-11 03:47:55 +02:00
|
|
|
padToggle(Pad::NOTE4, ed);
|
2016-12-06 20:32:08 +01:00
|
|
|
break;
|
|
|
|
case TDuration::DurationType::V_QUARTER:
|
2019-10-11 03:47:55 +02:00
|
|
|
padToggle(Pad::NOTE8, ed);
|
2016-12-06 20:32:08 +01:00
|
|
|
break;
|
|
|
|
case TDuration::DurationType::V_EIGHTH:
|
2019-10-11 03:47:55 +02:00
|
|
|
padToggle(Pad::NOTE16, ed);
|
2016-12-06 20:32:08 +01:00
|
|
|
break;
|
|
|
|
case TDuration::DurationType::V_16TH:
|
2019-10-11 03:47:55 +02:00
|
|
|
padToggle(Pad::NOTE32, ed);
|
2016-12-06 20:32:08 +01:00
|
|
|
break;
|
|
|
|
case TDuration::DurationType::V_32ND:
|
2019-10-11 03:47:55 +02:00
|
|
|
padToggle(Pad::NOTE64, ed);
|
2016-12-06 20:32:08 +01:00
|
|
|
break;
|
|
|
|
case TDuration::DurationType::V_64TH:
|
2019-10-11 03:47:55 +02:00
|
|
|
padToggle(Pad::NOTE128, ed);
|
2016-12-06 20:32:08 +01:00
|
|
|
break;
|
|
|
|
// cycle back from shortest to longest?
|
|
|
|
// case TDuration::DurationType::V_128TH:
|
2019-10-11 03:47:55 +02:00
|
|
|
// padToggle(Pad::NOTE00, ed);
|
2016-12-06 20:32:08 +01:00
|
|
|
// break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdToggleLayoutBreak
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdToggleLayoutBreak(LayoutBreak::Type type)
|
|
|
|
{
|
2017-07-18 01:01:29 +02:00
|
|
|
// find measure(s)
|
2018-04-18 22:53:23 +02:00
|
|
|
QList<MeasureBase*> mbl;
|
2017-07-18 01:01:29 +02:00
|
|
|
if (selection().isRange()) {
|
|
|
|
Measure* startMeasure = nullptr;
|
|
|
|
Measure* endMeasure = nullptr;
|
|
|
|
if (!selection().measureRange(&startMeasure, &endMeasure))
|
|
|
|
return;
|
|
|
|
if (!startMeasure || !endMeasure)
|
|
|
|
return;
|
|
|
|
#if 1
|
|
|
|
// toggle break on the last measure of the range
|
2018-04-18 22:53:23 +02:00
|
|
|
mbl.append(endMeasure);
|
2017-07-18 01:01:29 +02:00
|
|
|
// if more than one measure selected,
|
|
|
|
// also toggle break *before* the range (to try to fit selection on a single line)
|
2018-04-18 22:53:23 +02:00
|
|
|
if (startMeasure != endMeasure && startMeasure->prev())
|
|
|
|
mbl.append(startMeasure->prev());
|
2017-07-18 01:01:29 +02:00
|
|
|
#else
|
|
|
|
// toggle breaks throughout the selection
|
|
|
|
for (Measure* m = startMeasure; m; m = m->nextMeasure()) {
|
2018-04-18 22:53:23 +02:00
|
|
|
mbl.append(m);
|
2017-07-18 01:01:29 +02:00
|
|
|
if (m == endMeasure)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else {
|
2018-04-18 22:53:23 +02:00
|
|
|
MeasureBase* mb = nullptr;
|
2017-07-18 01:01:29 +02:00
|
|
|
for (Element* el : selection().elements()) {
|
2018-04-18 22:53:23 +02:00
|
|
|
switch (el->type()) {
|
|
|
|
case ElementType::HBOX:
|
|
|
|
case ElementType::VBOX:
|
|
|
|
case ElementType::TBOX:
|
|
|
|
mb = toMeasureBase(el);
|
|
|
|
break;
|
|
|
|
default: {
|
|
|
|
// find measure
|
|
|
|
Measure* measure = toMeasure(el->findMeasure());
|
2019-04-06 20:07:58 +02:00
|
|
|
// for start repeat, attach break to previous measure
|
|
|
|
if (measure && el->isBarLine()) {
|
2019-04-06 07:54:38 +02:00
|
|
|
BarLine* bl = toBarLine(el);
|
|
|
|
if (bl->barLineType() == BarLineType::START_REPEAT)
|
|
|
|
measure = measure->prevMeasure();
|
|
|
|
}
|
2018-04-18 22:53:23 +02:00
|
|
|
// if measure is mmrest, then propagate to last original measure
|
|
|
|
if (measure)
|
|
|
|
mb = measure->isMMRest() ? measure->mmRestLast() : measure;
|
|
|
|
}
|
|
|
|
}
|
2017-07-18 01:01:29 +02:00
|
|
|
}
|
2019-04-06 07:54:38 +02:00
|
|
|
if (mb)
|
|
|
|
mbl.append(mb);
|
2017-07-18 01:01:29 +02:00
|
|
|
}
|
|
|
|
// toggle the breaks
|
2018-04-18 22:53:23 +02:00
|
|
|
for (MeasureBase* mb: mbl) {
|
|
|
|
if (mb) {
|
|
|
|
bool val = false;
|
|
|
|
switch (type) {
|
|
|
|
case LayoutBreak::Type::LINE:
|
|
|
|
val = !mb->lineBreak();
|
|
|
|
mb->undoSetBreak(val, type);
|
|
|
|
// remove page break if appropriate
|
|
|
|
if (val && mb->pageBreak())
|
|
|
|
mb->undoSetBreak(false, LayoutBreak::Type::PAGE);
|
|
|
|
break;
|
|
|
|
case LayoutBreak::Type::PAGE:
|
|
|
|
val = !mb->pageBreak();
|
|
|
|
mb->undoSetBreak(val, type);
|
|
|
|
// remove line break if appropriate
|
|
|
|
if (val && mb->lineBreak())
|
|
|
|
mb->undoSetBreak(false, LayoutBreak::Type::LINE);
|
|
|
|
break;
|
|
|
|
case LayoutBreak::Type::SECTION:
|
|
|
|
val = !mb->sectionBreak();
|
|
|
|
mb->undoSetBreak(val, type);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-12-06 20:32:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdToggleMmrest
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdToggleMmrest()
|
|
|
|
{
|
2018-03-27 15:36:00 +02:00
|
|
|
bool val = !styleB(Sid::createMultiMeasureRests);
|
2016-12-06 20:32:08 +01:00
|
|
|
deselectAll();
|
2018-03-27 15:36:00 +02:00
|
|
|
undo(new ChangeStyleVal(this, Sid::createMultiMeasureRests, val));
|
2016-12-06 20:32:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdToggleHideEmpty
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdToggleHideEmpty()
|
|
|
|
{
|
2018-03-27 15:36:00 +02:00
|
|
|
bool val = !styleB(Sid::hideEmptyStaves);
|
2016-12-06 20:32:08 +01:00
|
|
|
deselectAll();
|
2018-03-27 15:36:00 +02:00
|
|
|
undo(new ChangeStyleVal(this, Sid::hideEmptyStaves, val));
|
2016-12-06 20:32:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdSetVisible
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdSetVisible()
|
|
|
|
{
|
|
|
|
for (Element* e : selection().elements())
|
2018-03-27 15:36:00 +02:00
|
|
|
undo(new ChangeProperty(e, Pid::VISIBLE, true));
|
2016-12-06 20:32:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdUnsetVisible
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdUnsetVisible()
|
|
|
|
{
|
|
|
|
for (Element* e : selection().elements())
|
2018-03-27 15:36:00 +02:00
|
|
|
undo(new ChangeProperty(e, Pid::VISIBLE, false));
|
2016-12-06 20:32:08 +01:00
|
|
|
}
|
|
|
|
|
2017-05-03 16:20:04 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdAddPitch
|
|
|
|
/// insert note or add note to chord
|
|
|
|
// c d e f g a b entered:
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdAddPitch(const EditData& ed, int note, bool addFlag, bool insert)
|
|
|
|
{
|
|
|
|
InputState& is = inputState();
|
|
|
|
if (is.track() == -1) // invalid state
|
|
|
|
return;
|
|
|
|
if (is.segment() == 0) {
|
|
|
|
qDebug("cannot enter notes here (no chord rest at current position)");
|
|
|
|
return;
|
|
|
|
}
|
2017-07-19 16:22:34 +02:00
|
|
|
is.setRest(false);
|
2017-05-03 16:20:04 +02:00
|
|
|
const Drumset* ds = is.drumset();
|
|
|
|
int octave = 4;
|
|
|
|
if (ds) {
|
|
|
|
char note1 = "CDEFGAB"[note];
|
|
|
|
int pitch = -1;
|
|
|
|
int voice = 0;
|
|
|
|
for (int i = 0; i < 127; ++i) {
|
|
|
|
if (!ds->isValid(i))
|
|
|
|
continue;
|
|
|
|
if (ds->shortcut(i) && (ds->shortcut(i) == note1)) {
|
|
|
|
pitch = i;
|
|
|
|
voice = ds->voice(i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pitch == -1) {
|
|
|
|
qDebug(" shortcut %c not defined in drumset", note1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
is.setDrumNote(pitch);
|
|
|
|
is.setTrack((is.track() / VOICES) * VOICES + voice);
|
|
|
|
octave = pitch / 12;
|
|
|
|
if (is.segment()) {
|
|
|
|
Segment* seg = is.segment();
|
|
|
|
while (seg) {
|
|
|
|
if (seg->element(is.track()))
|
|
|
|
break;
|
|
|
|
seg = seg->prev(SegmentType::ChordRest);
|
|
|
|
}
|
|
|
|
if (seg)
|
|
|
|
is.setSegment(seg);
|
|
|
|
else
|
|
|
|
is.setSegment(is.segment()->measure()->first(SegmentType::ChordRest));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
static const int tab[] = { 0, 2, 4, 5, 7, 9, 11 };
|
|
|
|
|
|
|
|
// if adding notes, add above the upNote of the current chord
|
|
|
|
Element* el = selection().element();
|
|
|
|
if (addFlag && el && el->isNote()) {
|
|
|
|
Chord* chord = toNote(el)->chord();
|
|
|
|
Note* n = chord->upNote();
|
|
|
|
octave = n->epitch() / 12;
|
|
|
|
int tpc = n->tpc();
|
|
|
|
if (tpc == Tpc::TPC_C_BB || tpc == Tpc::TPC_C_B)
|
|
|
|
++octave;
|
|
|
|
else if (tpc == Tpc::TPC_B_S || tpc == Tpc::TPC_B_SS)
|
|
|
|
--octave;
|
|
|
|
if (note <= tpc2step(tpc))
|
|
|
|
octave++;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
int curPitch = 60;
|
|
|
|
if (is.segment()) {
|
|
|
|
Staff* staff = Score::staff(is.track() / VOICES);
|
2018-08-09 15:36:37 +02:00
|
|
|
Segment* seg = is.segment()->prev1(SegmentType::ChordRest | SegmentType::Clef | SegmentType::HeaderClef);
|
2017-05-03 16:20:04 +02:00
|
|
|
while (seg) {
|
|
|
|
if (seg->isChordRestType()) {
|
|
|
|
Element* p = seg->element(is.track());
|
|
|
|
if (p && p->isChord()) {
|
|
|
|
curPitch = toChord(p)->downNote()->epitch();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-08-09 15:36:37 +02:00
|
|
|
else if (seg->isClefType() || seg->isHeaderClefType()) {
|
2017-05-03 16:20:04 +02:00
|
|
|
Element* p = seg->element( (is.track() / VOICES) * VOICES); // clef on voice 1
|
|
|
|
if (p && p->isClef()) {
|
|
|
|
Clef* clef = toClef(p);
|
|
|
|
// check if it's an actual change or just a courtesy
|
2019-01-30 15:13:54 +01:00
|
|
|
ClefType ctb = staff->clef(clef->tick() - Fraction::fromTicks(1));
|
|
|
|
if (ctb != clef->clefType() || clef->tick().isZero()) {
|
2017-05-03 16:20:04 +02:00
|
|
|
curPitch = line2pitch(4, clef->clefType(), Key::C); // C 72 for treble clef
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-02-27 00:17:54 +01:00
|
|
|
seg = seg->prev1MM(SegmentType::ChordRest | SegmentType::Clef | SegmentType::HeaderClef);
|
2017-05-03 16:20:04 +02:00
|
|
|
}
|
|
|
|
octave = curPitch / 12;
|
|
|
|
}
|
|
|
|
|
|
|
|
int delta = octave * 12 + tab[note] - curPitch;
|
|
|
|
if (delta > 6)
|
|
|
|
--octave;
|
|
|
|
else if (delta < -6)
|
|
|
|
++octave;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ed.view->startNoteEntryMode();
|
|
|
|
|
|
|
|
int step = octave * 7 + note;
|
2017-05-03 17:31:50 +02:00
|
|
|
cmdAddPitch(step, addFlag, insert);
|
|
|
|
ed.view->adjustCanvasPosition(is.cr(), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Score::cmdAddPitch(int step, bool addFlag, bool insert)
|
|
|
|
{
|
2018-09-27 16:28:42 +02:00
|
|
|
insert = insert || inputState().usingNoteEntryMethod(NoteEntryMethod::TIMEWISE);
|
2017-05-03 16:20:04 +02:00
|
|
|
Position pos;
|
|
|
|
if (addFlag) {
|
|
|
|
Element* el = selection().element();
|
|
|
|
if (el && el->isNote()) {
|
|
|
|
Note* selectedNote = toNote(el);
|
|
|
|
Chord* chord = selectedNote->chord();
|
|
|
|
Segment* seg = chord->segment();
|
|
|
|
pos.segment = seg;
|
|
|
|
pos.staffIdx = selectedNote->track() / VOICES;
|
|
|
|
ClefType clef = staff(pos.staffIdx)->clef(seg->tick());
|
|
|
|
pos.line = relStep(step, clef);
|
|
|
|
bool error;
|
2019-10-25 00:36:19 +02:00
|
|
|
NoteVal nval = noteValForPosition(pos, _is.accidentalType(), error);
|
2017-05-03 16:20:04 +02:00
|
|
|
if (error)
|
|
|
|
return;
|
2019-10-25 00:36:19 +02:00
|
|
|
bool forceAccidental = false;
|
|
|
|
if (_is.accidentalType() != AccidentalType::NONE) {
|
|
|
|
NoteVal nval2 = noteValForPosition(pos, AccidentalType::NONE, error);
|
|
|
|
forceAccidental = (nval.pitch == nval2.pitch);
|
|
|
|
}
|
2019-10-11 03:47:55 +02:00
|
|
|
addNote(chord, nval, forceAccidental);
|
|
|
|
_is.setAccidentalType(AccidentalType::NONE);
|
2017-05-03 16:20:04 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pos.segment = inputState().segment();
|
|
|
|
pos.staffIdx = inputState().track() / VOICES;
|
|
|
|
ClefType clef = staff(pos.staffIdx)->clef(pos.segment->tick());
|
|
|
|
pos.line = relStep(step, clef);
|
|
|
|
|
|
|
|
if (inputState().usingNoteEntryMethod(NoteEntryMethod::REPITCH))
|
|
|
|
repitchNote(pos, !addFlag);
|
|
|
|
else {
|
|
|
|
if (insert)
|
|
|
|
insertChord(pos);
|
|
|
|
else
|
|
|
|
putNote(pos, !addFlag);
|
|
|
|
}
|
2019-10-31 18:52:52 +01:00
|
|
|
_is.setAccidentalType(AccidentalType::NONE);
|
2017-05-03 16:20:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdToggleVisible
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdToggleVisible()
|
|
|
|
{
|
|
|
|
QSet<Element*> spanners;
|
|
|
|
for (Element* e : selection().elements()) {
|
|
|
|
if (e->isBracket()) // ignore
|
|
|
|
continue;
|
2019-05-01 13:00:51 +02:00
|
|
|
if (e->isNoteDot() && selection().elements().contains(e->parent()))
|
2019-05-01 18:30:47 +02:00
|
|
|
// already handled in ScoreElement::undoChangeProperty(); don't toggle twice
|
2019-05-01 13:00:51 +02:00
|
|
|
continue;
|
2017-05-03 16:20:04 +02:00
|
|
|
bool spannerSegment = e->isSpannerSegment();
|
2017-12-20 16:49:30 +01:00
|
|
|
if (!spannerSegment || !spanners.contains(toSpannerSegment(e)->spanner()))
|
2018-03-27 15:36:00 +02:00
|
|
|
e->undoChangeProperty(Pid::VISIBLE, !e->getProperty(Pid::VISIBLE).toBool());
|
2017-05-03 16:20:04 +02:00
|
|
|
if (spannerSegment)
|
2017-12-20 16:49:30 +01:00
|
|
|
spanners.insert(toSpannerSegment(e)->spanner());
|
2017-05-03 16:20:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdAddFret
|
|
|
|
/// insert note with given fret on current string
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdAddFret(int fret)
|
|
|
|
{
|
|
|
|
InputState& is = inputState();
|
|
|
|
if (is.track() == -1) // invalid state
|
|
|
|
return;
|
|
|
|
if (!is.segment()) {
|
|
|
|
qDebug("cannot enter notes here (no chord rest at current position)");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Position pos;
|
|
|
|
pos.segment = is.segment();
|
|
|
|
pos.staffIdx = is.track() / VOICES;
|
2020-01-28 07:49:00 +01:00
|
|
|
pos.line = staff(pos.staffIdx)->staffType(is.tick())->physStringToVisual(is.string());
|
2017-05-03 16:20:04 +02:00
|
|
|
pos.fret = fret;
|
|
|
|
putNote(pos, false);
|
|
|
|
}
|
|
|
|
|
2017-07-17 16:11:21 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdRelayout
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdRelayout()
|
|
|
|
{
|
|
|
|
setLayoutAll();
|
|
|
|
}
|
|
|
|
|
2019-02-25 06:12:17 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmdToggleAutoplace
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Score::cmdToggleAutoplace(bool all)
|
|
|
|
{
|
|
|
|
if (all) {
|
|
|
|
bool val = !styleB(Sid::autoplaceEnabled);
|
|
|
|
undoChangeStyleVal(Sid::autoplaceEnabled, val);
|
|
|
|
setLayoutAll();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
QSet<Element*> spanners;
|
|
|
|
for (Element* e : selection().elements()) {
|
|
|
|
if (e->isSpannerSegment()) {
|
|
|
|
if (Element* ee = e->propertyDelegate(Pid::AUTOPLACE))
|
|
|
|
e = ee;
|
|
|
|
// spanner segments may each have their own autoplace setting
|
|
|
|
// but if they delegate to spanner, only toggle once
|
|
|
|
if (e->isSpanner()) {
|
|
|
|
if (spanners.contains(e))
|
|
|
|
continue;
|
|
|
|
spanners.insert(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PropertyFlags pf = e->propertyFlags(Pid::AUTOPLACE);
|
|
|
|
if (pf == PropertyFlags::STYLED)
|
|
|
|
pf = PropertyFlags::UNSTYLED;
|
|
|
|
e->undoChangeProperty(Pid::AUTOPLACE, !e->getProperty(Pid::AUTOPLACE).toBool(), pf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-06 20:32:08 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// cmd
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2017-05-03 16:20:04 +02:00
|
|
|
void Score::cmd(const QAction* a, EditData& ed)
|
2016-12-06 20:32:08 +01:00
|
|
|
{
|
|
|
|
QString cmd(a ? a->data().toString() : "");
|
|
|
|
if (MScore::debugMode)
|
2016-12-23 12:05:18 +01:00
|
|
|
qDebug("<%s>", qPrintable(cmd));
|
2016-12-06 20:32:08 +01:00
|
|
|
|
|
|
|
struct ScoreCmd {
|
|
|
|
const char* name;
|
|
|
|
std::function<void()> cmd;
|
|
|
|
};
|
2017-03-15 10:42:17 +01:00
|
|
|
const std::vector<ScoreCmd> cmdList {
|
2017-05-03 16:20:04 +02:00
|
|
|
{ "note-c", [this,ed]{ cmdAddPitch(ed, 0, false, false); }},
|
|
|
|
{ "note-d", [this,ed]{ cmdAddPitch(ed, 1, false, false); }},
|
|
|
|
{ "note-e", [this,ed]{ cmdAddPitch(ed, 2, false, false); }},
|
|
|
|
{ "note-f", [this,ed]{ cmdAddPitch(ed, 3, false, false); }},
|
|
|
|
{ "note-g", [this,ed]{ cmdAddPitch(ed, 4, false, false); }},
|
|
|
|
{ "note-a", [this,ed]{ cmdAddPitch(ed, 5, false, false); }},
|
|
|
|
{ "note-b", [this,ed]{ cmdAddPitch(ed, 6, false, false); }},
|
|
|
|
{ "chord-c", [this,ed]{ cmdAddPitch(ed, 0, true, false); }},
|
|
|
|
{ "chord-d", [this,ed]{ cmdAddPitch(ed, 1, true, false); }},
|
|
|
|
{ "chord-e", [this,ed]{ cmdAddPitch(ed, 2, true, false); }},
|
|
|
|
{ "chord-f", [this,ed]{ cmdAddPitch(ed, 3, true, false); }},
|
|
|
|
{ "chord-g", [this,ed]{ cmdAddPitch(ed, 4, true, false); }},
|
|
|
|
{ "chord-a", [this,ed]{ cmdAddPitch(ed, 5, true, false); }},
|
|
|
|
{ "chord-b", [this,ed]{ cmdAddPitch(ed, 6, true, false); }},
|
|
|
|
{ "insert-c", [this,ed]{ cmdAddPitch(ed, 0, false, true); }},
|
|
|
|
{ "insert-d", [this,ed]{ cmdAddPitch(ed, 1, false, true); }},
|
|
|
|
{ "insert-e", [this,ed]{ cmdAddPitch(ed, 2, false, true); }},
|
|
|
|
{ "insert-f", [this,ed]{ cmdAddPitch(ed, 3, false, true); }},
|
|
|
|
{ "insert-g", [this,ed]{ cmdAddPitch(ed, 4, false, true); }},
|
|
|
|
{ "insert-a", [this,ed]{ cmdAddPitch(ed, 5, false, true); }},
|
|
|
|
{ "insert-b", [this,ed]{ cmdAddPitch(ed, 6, false, true); }},
|
|
|
|
{ "fret-0", [this]{ cmdAddFret(0); }},
|
|
|
|
{ "fret-1", [this]{ cmdAddFret(1); }},
|
|
|
|
{ "fret-2", [this]{ cmdAddFret(2); }},
|
|
|
|
{ "fret-3", [this]{ cmdAddFret(3); }},
|
|
|
|
{ "fret-4", [this]{ cmdAddFret(4); }},
|
|
|
|
{ "fret-5", [this]{ cmdAddFret(5); }},
|
|
|
|
{ "fret-6", [this]{ cmdAddFret(6); }},
|
|
|
|
{ "fret-7", [this]{ cmdAddFret(7); }},
|
|
|
|
{ "fret-8", [this]{ cmdAddFret(8); }},
|
|
|
|
{ "fret-9", [this]{ cmdAddFret(9); }},
|
|
|
|
{ "fret-10", [this]{ cmdAddFret(10); }},
|
|
|
|
{ "fret-11", [this]{ cmdAddFret(11); }},
|
|
|
|
{ "fret-12", [this]{ cmdAddFret(12); }},
|
|
|
|
{ "fret-13", [this]{ cmdAddFret(13); }},
|
|
|
|
{ "fret-14", [this]{ cmdAddFret(14); }},
|
|
|
|
{ "toggle-visible", [this]{ cmdToggleVisible(); }},
|
2016-12-06 20:32:08 +01:00
|
|
|
{ "reset-stretch", [this]{ resetUserStretch(); }},
|
|
|
|
{ "mirror-note", [this]{ cmdMirrorNoteHead(); }},
|
2017-03-24 07:09:32 +01:00
|
|
|
{ "double-duration", [this]{ cmdDoubleDuration(); }},
|
|
|
|
{ "half-duration", [this]{ cmdHalfDuration(); }},
|
|
|
|
{ "inc-duration-dotted", [this]{ cmdIncDurationDotted(); }},
|
|
|
|
{ "dec-duration-dotted", [this]{ cmdDecDurationDotted(); }},
|
2016-12-06 20:32:08 +01:00
|
|
|
{ "add-staccato", [this]{ addArticulation(SymId::articStaccatoAbove); }},
|
|
|
|
{ "add-tenuto", [this]{ addArticulation(SymId::articTenutoAbove); }},
|
|
|
|
{ "add-marcato", [this]{ addArticulation(SymId::articMarcatoAbove); }},
|
|
|
|
{ "add-sforzato", [this]{ addArticulation(SymId::articAccentAbove); }},
|
|
|
|
{ "add-trill", [this]{ addArticulation(SymId::ornamentTrill); }},
|
2017-02-15 18:34:59 +01:00
|
|
|
{ "add-up-bow", [this]{ addArticulation(SymId::stringsUpBow); }},
|
|
|
|
{ "add-down-bow", [this]{ addArticulation(SymId::stringsDownBow); }},
|
2017-03-08 14:18:34 +01:00
|
|
|
{ "add-8va", [this]{ cmdAddOttava(OttavaType::OTTAVA_8VA); }},
|
|
|
|
{ "add-8vb", [this]{ cmdAddOttava(OttavaType::OTTAVA_8VB); }},
|
2019-10-11 03:47:55 +02:00
|
|
|
{ "note-longa", [this,ed]{ padToggle(Pad::NOTE00, ed); }},
|
|
|
|
{ "note-longa-TAB", [this,ed]{ padToggle(Pad::NOTE00, ed); }},
|
|
|
|
{ "note-breve", [this,ed]{ padToggle(Pad::NOTE0, ed); }},
|
|
|
|
{ "note-breve-TAB", [this,ed]{ padToggle(Pad::NOTE0, ed); }},
|
|
|
|
{ "pad-note-1", [this,ed]{ padToggle(Pad::NOTE1, ed); }},
|
|
|
|
{ "pad-note-1-TAB", [this,ed]{ padToggle(Pad::NOTE1, ed); }},
|
|
|
|
{ "pad-note-2", [this,ed]{ padToggle(Pad::NOTE2, ed); }},
|
|
|
|
{ "pad-note-2-TAB", [this,ed]{ padToggle(Pad::NOTE2, ed); }},
|
|
|
|
{ "pad-note-4", [this,ed]{ padToggle(Pad::NOTE4, ed); }},
|
|
|
|
{ "pad-note-4-TAB", [this,ed]{ padToggle(Pad::NOTE4, ed); }},
|
|
|
|
{ "pad-note-8", [this,ed]{ padToggle(Pad::NOTE8, ed); }},
|
|
|
|
{ "pad-note-8-TAB", [this,ed]{ padToggle(Pad::NOTE8, ed); }},
|
|
|
|
{ "pad-note-16", [this,ed]{ padToggle(Pad::NOTE16, ed); }},
|
|
|
|
{ "pad-note-16-TAB", [this,ed]{ padToggle(Pad::NOTE16, ed); }},
|
|
|
|
{ "pad-note-32", [this,ed]{ padToggle(Pad::NOTE32, ed); }},
|
|
|
|
{ "pad-note-32-TAB", [this,ed]{ padToggle(Pad::NOTE32, ed); }},
|
|
|
|
{ "pad-note-64", [this,ed]{ padToggle(Pad::NOTE64, ed); }},
|
|
|
|
{ "pad-note-64-TAB", [this,ed]{ padToggle(Pad::NOTE64, ed); }},
|
|
|
|
{ "pad-note-128", [this,ed]{ padToggle(Pad::NOTE128, ed); }},
|
|
|
|
{ "pad-note-128-TAB", [this,ed]{ padToggle(Pad::NOTE128, ed); }},
|
2018-10-29 11:03:23 +01:00
|
|
|
{ "reset-style", [this]{ cmdResetStyle(); }},
|
2016-12-06 20:32:08 +01:00
|
|
|
{ "reset-beammode", [this]{ cmdResetBeamMode(); }},
|
|
|
|
{ "reset-groupings", [this]{ cmdResetNoteAndRestGroupings(); }},
|
|
|
|
{ "clef-violin", [this]{ cmdInsertClef(ClefType::G); }},
|
|
|
|
{ "clef-bass", [this]{ cmdInsertClef(ClefType::F); }},
|
|
|
|
{ "voice-x12", [this]{ cmdExchangeVoice(0, 1); }},
|
|
|
|
{ "voice-x13", [this]{ cmdExchangeVoice(0, 2); }},
|
|
|
|
{ "voice-x14", [this]{ cmdExchangeVoice(0, 3); }},
|
|
|
|
{ "voice-x23", [this]{ cmdExchangeVoice(1, 2); }},
|
|
|
|
{ "voice-x24", [this]{ cmdExchangeVoice(1, 3); }},
|
|
|
|
{ "voice-x34", [this]{ cmdExchangeVoice(2, 3); }},
|
2019-10-11 03:47:55 +02:00
|
|
|
{ "pad-rest", [this,ed]{ padToggle(Pad::REST, ed); }},
|
|
|
|
{ "pad-dot", [this,ed]{ padToggle(Pad::DOT, ed); }},
|
|
|
|
{ "pad-dotdot", [this,ed]{ padToggle(Pad::DOTDOT, ed); }},
|
|
|
|
{ "pad-dot3", [this,ed]{ padToggle(Pad::DOT3, ed); }},
|
|
|
|
{ "pad-dot4", [this,ed]{ padToggle(Pad::DOT4, ed); }},
|
2016-12-06 20:32:08 +01:00
|
|
|
{ "beam-start", [this]{ cmdSetBeamMode(Beam::Mode::BEGIN); }},
|
|
|
|
{ "beam-mid", [this]{ cmdSetBeamMode(Beam::Mode::MID); }},
|
|
|
|
{ "no-beam", [this]{ cmdSetBeamMode(Beam::Mode::NONE); }},
|
2019-09-16 08:41:56 +02:00
|
|
|
{ "beam32", [this]{ cmdSetBeamMode(Beam::Mode::BEGIN32); }},
|
|
|
|
{ "beam64", [this]{ cmdSetBeamMode(Beam::Mode::BEGIN64); }},
|
2020-02-09 21:29:40 +01:00
|
|
|
{ "auto-beam", [this]{ cmdSetBeamMode(Beam::Mode::AUTO); }},
|
2019-10-11 03:47:55 +02:00
|
|
|
{ "sharp2", [this,ed]{ toggleAccidental(AccidentalType::SHARP2, ed); }},
|
|
|
|
{ "sharp", [this,ed]{ toggleAccidental(AccidentalType::SHARP, ed); }},
|
|
|
|
{ "nat", [this,ed]{ toggleAccidental(AccidentalType::NATURAL, ed); }},
|
|
|
|
{ "flat", [this,ed]{ toggleAccidental(AccidentalType::FLAT, ed); }},
|
|
|
|
{ "flat2", [this,ed]{ toggleAccidental(AccidentalType::FLAT2, ed); }},
|
2016-12-06 20:32:08 +01:00
|
|
|
{ "flip", [this]{ cmdFlip(); }},
|
|
|
|
{ "stretch+", [this]{ cmdAddStretch(0.1); }},
|
|
|
|
{ "stretch-", [this]{ cmdAddStretch(-0.1); }},
|
|
|
|
{ "pitch-spell", [this]{ spell(); }},
|
|
|
|
{ "select-all", [this]{ cmdSelectAll(); }},
|
|
|
|
{ "select-section", [this]{ cmdSelectSection(); }},
|
|
|
|
{ "add-brackets", [this]{ cmdAddBracket(); }},
|
2019-04-29 11:01:54 +02:00
|
|
|
{ "add-parentheses", [this]{ cmdAddParentheses(); }},
|
2016-12-06 20:32:08 +01:00
|
|
|
{ "acciaccatura", [this]{ cmdAddGrace(NoteType::ACCIACCATURA, MScore::division / 2); }},
|
|
|
|
{ "appoggiatura", [this]{ cmdAddGrace(NoteType::APPOGGIATURA, MScore::division / 2); }},
|
|
|
|
{ "grace4", [this]{ cmdAddGrace(NoteType::GRACE4, MScore::division); }},
|
|
|
|
{ "grace16", [this]{ cmdAddGrace(NoteType::GRACE16, MScore::division / 4); }},
|
|
|
|
{ "grace32", [this]{ cmdAddGrace(NoteType::GRACE32, MScore::division / 8); }},
|
|
|
|
{ "grace8after", [this]{ cmdAddGrace(NoteType::GRACE8_AFTER, MScore::division / 2); }},
|
|
|
|
{ "grace16after", [this]{ cmdAddGrace(NoteType::GRACE16_AFTER, MScore::division / 4); }},
|
|
|
|
{ "grace32after", [this]{ cmdAddGrace(NoteType::GRACE32_AFTER, MScore::division / 8); }},
|
|
|
|
{ "explode", [this]{ cmdExplode(); }},
|
|
|
|
{ "implode", [this]{ cmdImplode(); }},
|
|
|
|
{ "slash-fill", [this]{ cmdSlashFill(); }},
|
|
|
|
{ "slash-rhythm", [this]{ cmdSlashRhythm(); }},
|
|
|
|
{ "resequence-rehearsal-marks", [this]{ cmdResequenceRehearsalMarks(); }},
|
|
|
|
{ "del-empty-measures", [this]{ cmdRemoveEmptyTrailingMeasures(); }},
|
|
|
|
{ "add-audio", [this]{ addAudioTrack(); }},
|
|
|
|
{ "transpose-up", [this]{ transposeSemitone(1); }},
|
|
|
|
{ "transpose-down", [this]{ transposeSemitone(-1); }},
|
|
|
|
{ "delete", [this]{ cmdDeleteSelection(); }},
|
|
|
|
{ "full-measure-rest", [this]{ cmdFullMeasureRest(); }},
|
|
|
|
{ "toggle-insert-mode", [this]{ _is.setInsertMode(!_is.insertMode()); }},
|
|
|
|
{ "pitch-up", [this]{ cmdPitchUp(); }},
|
|
|
|
{ "pitch-down", [this]{ cmdPitchDown(); }},
|
2016-12-09 15:56:48 +01:00
|
|
|
{ "time-delete", [this]{ cmdTimeDelete(); }},
|
2016-12-06 20:32:08 +01:00
|
|
|
{ "pitch-up-octave", [this]{ cmdPitchUpOctave(); }},
|
|
|
|
{ "pitch-down-octave", [this]{ cmdPitchDownOctave(); }},
|
2019-10-11 03:47:55 +02:00
|
|
|
{ "pad-note-increase", [this,ed]{ cmdPadNoteIncreaseTAB(ed); }},
|
|
|
|
{ "pad-note-decrease", [this,ed]{ cmdPadNoteDecreaseTAB(ed); }},
|
|
|
|
{ "pad-note-increase-TAB", [this,ed]{ cmdPadNoteIncreaseTAB(ed); }},
|
|
|
|
{ "pad-note-decrease-TAB", [this,ed]{ cmdPadNoteDecreaseTAB(ed); }},
|
2016-12-06 20:32:08 +01:00
|
|
|
{ "toggle-mmrest", [this]{ cmdToggleMmrest(); }},
|
|
|
|
{ "toggle-hide-empty", [this]{ cmdToggleHideEmpty(); }},
|
|
|
|
{ "set-visible", [this]{ cmdSetVisible(); }},
|
|
|
|
{ "unset-visible", [this]{ cmdUnsetVisible(); }},
|
|
|
|
{ "system-break", [this]{ cmdToggleLayoutBreak(LayoutBreak::Type::LINE); }},
|
|
|
|
{ "page-break", [this]{ cmdToggleLayoutBreak(LayoutBreak::Type::PAGE); }},
|
|
|
|
{ "section-break", [this]{ cmdToggleLayoutBreak(LayoutBreak::Type::SECTION); }},
|
2017-07-17 16:11:21 +02:00
|
|
|
{ "relayout", [this]{ cmdRelayout(); }},
|
2019-02-25 06:12:17 +01:00
|
|
|
{ "toggle-autoplace", [this]{ cmdToggleAutoplace(false); }},
|
|
|
|
{ "autoplace-enabled", [this]{ cmdToggleAutoplace(true); }},
|
2016-12-06 20:32:08 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
for (const auto& c : cmdList) {
|
|
|
|
if (cmd == c.name) {
|
|
|
|
startCmd();
|
|
|
|
c.cmd();
|
|
|
|
endCmd();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
qDebug("unknown cmd <%s>", qPrintable(cmd));
|
|
|
|
}
|
|
|
|
|
2017-12-21 14:03:45 +01:00
|
|
|
|
2014-11-15 21:28:16 +01:00
|
|
|
}
|
2016-09-30 12:44:27 +02:00
|
|
|
|