4452 lines
161 KiB
C++
4452 lines
161 KiB
C++
//=============================================================================
|
||
// MuseScore
|
||
// Music Composition & Notation
|
||
//
|
||
// Copyright (C) 2002-2017 Werner Schweer & others
|
||
//
|
||
// This program is free software; you can redistribute it and/or modify
|
||
// it under the terms of the GNU General Public License version 2
|
||
// as published by the Free Software Foundation and appearing in
|
||
// the file LICENCE.GPL
|
||
//=============================================================================
|
||
|
||
#include "scoreview.h"
|
||
|
||
#include "breaksdialog.h"
|
||
#include "continuouspanel.h"
|
||
#include "drumroll.h"
|
||
#include "editdrumset.h"
|
||
#include "editstaff.h"
|
||
#include "globals.h"
|
||
#include "magbox.h"
|
||
#include "measureproperties.h"
|
||
#include "musescore.h"
|
||
#include "navigator.h"
|
||
#include "preferences.h"
|
||
#include "scoretab.h"
|
||
#include "seq.h"
|
||
#include "splitstaff.h"
|
||
#include "textcursor.h"
|
||
#include "textpalette.h"
|
||
#include "texttools.h"
|
||
#include "fotomode.h"
|
||
#include "tourhandler.h"
|
||
|
||
#include "inspector/inspector.h"
|
||
|
||
#include "libmscore/articulation.h"
|
||
#include "libmscore/barline.h"
|
||
#include "libmscore/box.h"
|
||
#include "libmscore/chord.h"
|
||
#include "libmscore/clef.h"
|
||
#include "libmscore/dynamic.h"
|
||
#include "libmscore/excerpt.h"
|
||
#include "libmscore/figuredbass.h"
|
||
#include "libmscore/fingering.h"
|
||
#include "libmscore/hairpin.h"
|
||
#include "libmscore/harmony.h"
|
||
#include "libmscore/fret.h"
|
||
#include "libmscore/icon.h"
|
||
#include "libmscore/image.h"
|
||
#include "libmscore/instrchange.h"
|
||
#include "libmscore/keysig.h"
|
||
#include "libmscore/lasso.h"
|
||
#include "libmscore/lyrics.h"
|
||
#include "libmscore/measure.h"
|
||
#include "libmscore/navigate.h"
|
||
#include "libmscore/notedot.h"
|
||
#include "libmscore/note.h"
|
||
#include "libmscore/noteline.h"
|
||
#include "libmscore/ottava.h"
|
||
#include "libmscore/page.h"
|
||
#include "libmscore/part.h"
|
||
#include "libmscore/pedal.h"
|
||
#include "libmscore/pitchspelling.h"
|
||
#include "libmscore/rehearsalmark.h"
|
||
#include "libmscore/repeatlist.h"
|
||
#include "libmscore/rest.h"
|
||
#include "libmscore/score.h"
|
||
#include "libmscore/segment.h"
|
||
#include "libmscore/shadownote.h"
|
||
#include "libmscore/slur.h"
|
||
#include "libmscore/spanner.h"
|
||
#include "libmscore/staff.h"
|
||
#include "libmscore/stafftext.h"
|
||
#include "libmscore/stafftype.h"
|
||
#include "libmscore/stringdata.h"
|
||
#include "libmscore/sym.h"
|
||
#include "libmscore/system.h"
|
||
#include "libmscore/systemtext.h"
|
||
#include "libmscore/textframe.h"
|
||
#include "libmscore/text.h"
|
||
#include "libmscore/timesig.h"
|
||
#include "libmscore/trill.h"
|
||
#include "libmscore/tuplet.h"
|
||
#include "libmscore/undo.h"
|
||
#include "libmscore/utils.h"
|
||
#include "libmscore/volta.h"
|
||
#include "libmscore/xml.h"
|
||
#include "libmscore/textline.h"
|
||
#include "libmscore/shape.h"
|
||
|
||
namespace Ms {
|
||
|
||
extern QErrorMessage* errorMessage;
|
||
|
||
//---------------------------------------------------------
|
||
// ScoreView
|
||
//---------------------------------------------------------
|
||
|
||
ScoreView::ScoreView(QWidget* parent)
|
||
: QWidget(parent), editData(this)
|
||
{
|
||
setObjectName("scoreview");
|
||
setStatusTip("scoreview");
|
||
setAcceptDrops(true);
|
||
#ifndef Q_OS_MAC
|
||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||
#endif
|
||
setAttribute(Qt::WA_NoSystemBackground);
|
||
setFocusPolicy(Qt::ClickFocus);
|
||
setAttribute(Qt::WA_InputMethodEnabled);
|
||
setAttribute(Qt::WA_KeyCompression);
|
||
setAttribute(Qt::WA_StaticContents);
|
||
setAutoFillBackground(false);
|
||
|
||
state = ViewState::NORMAL;
|
||
_score = 0;
|
||
_omrView = 0;
|
||
dropTarget = 0;
|
||
|
||
realtimeTimer = new QTimer(this);
|
||
realtimeTimer->setTimerType(Qt::PreciseTimer);
|
||
connect(realtimeTimer, SIGNAL(timeout()), this, SLOT(triggerCmdRealtimeAdvance()));
|
||
extendNoteTimer = new QTimer(this);
|
||
extendNoteTimer->setTimerType(Qt::PreciseTimer);
|
||
extendNoteTimer->setSingleShot(true);
|
||
connect(extendNoteTimer, SIGNAL(timeout()), this, SLOT(extendCurrentNote()));
|
||
|
||
setContextMenuPolicy(Qt::DefaultContextMenu);
|
||
|
||
double mag = preferences.getDouble(PREF_SCORE_MAGNIFICATION) * (mscore->physicalDotsPerInch() / DPI);
|
||
_matrix = QTransform(mag, 0.0, 0.0, mag, 0.0, 0.0);
|
||
imatrix = _matrix.inverted();
|
||
_magIdx = preferences.getDouble(PREF_SCORE_MAGNIFICATION) == 1.0 ? MagIdx::MAG_100 : MagIdx::MAG_FREE;
|
||
focusFrame = 0;
|
||
// dragElement = 0;
|
||
_bgColor = Qt::darkBlue;
|
||
_fgColor = Qt::white;
|
||
_fgPixmap = 0;
|
||
_bgPixmap = 0;
|
||
|
||
editData.curGrip = Grip::NO_GRIP;
|
||
editData.grips = 0;
|
||
editData.element = 0;
|
||
|
||
lasso = new Lasso(_score);
|
||
_foto = 0;// new Lasso(_score);
|
||
|
||
_cursor = new PositionCursor(this);
|
||
_cursor->setType(CursorType::POS);
|
||
_continuousPanel = new ContinuousPanel(this);
|
||
_continuousPanel->setActive(true);
|
||
|
||
shadowNote = 0;
|
||
|
||
_curLoopIn = new PositionCursor(this);
|
||
_curLoopIn->setType(CursorType::LOOP_IN);
|
||
_curLoopOut = new PositionCursor(this);
|
||
_curLoopOut->setType(CursorType::LOOP_OUT);
|
||
|
||
if (converterMode) // HACK
|
||
return;
|
||
|
||
grabGesture(Qt::PinchGesture); // laptop pad (Mac) and touchscreen
|
||
|
||
//-----------------------------------------------------------------------
|
||
|
||
if (MScore::debugMode)
|
||
setMouseTracking(true);
|
||
|
||
if (preferences.getBool(PREF_UI_CANVAS_BG_USECOLOR))
|
||
setBackground(MScore::bgColor);
|
||
else {
|
||
QPixmap* pm = new QPixmap(preferences.getString(PREF_UI_CANVAS_BG_WALLPAPER));
|
||
setBackground(pm);
|
||
}
|
||
if (preferences.getBool(PREF_UI_CANVAS_FG_USECOLOR))
|
||
setForeground(preferences.getColor(PREF_UI_CANVAS_FG_COLOR));
|
||
else {
|
||
QPixmap* pm = new QPixmap(preferences.getString(PREF_UI_CANVAS_FG_WALLPAPER));
|
||
if (pm == 0 || pm->isNull())
|
||
qDebug("no valid pixmap %s", qPrintable(preferences.getString(PREF_UI_CANVAS_FG_WALLPAPER)));
|
||
setForeground(pm);
|
||
}
|
||
|
||
connect(getAction("loop"), SIGNAL(toggled(bool)), SLOT(loopToggled(bool)));
|
||
if (seq)
|
||
connect(seq, SIGNAL(stopped()), SLOT(seqStopped()));
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// setScore
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::setScore(Score* s)
|
||
{
|
||
if (_score) {
|
||
if (_score->isMaster()) {
|
||
MasterScore* ms = static_cast<MasterScore*>(s);
|
||
for (MasterScore* _ms : *ms->movements()) {
|
||
_ms->removeViewer(this);
|
||
disconnect(s, SIGNAL(posChanged(POS, int)), this, SLOT(posChanged(POS,int)));
|
||
}
|
||
}
|
||
else {
|
||
_score->removeViewer(this);
|
||
disconnect(s, SIGNAL(posChanged(POS, int)), this, SLOT(posChanged(POS,int)));
|
||
}
|
||
}
|
||
|
||
_score = s;
|
||
if (_score) {
|
||
if (_score->isMaster()) {
|
||
MasterScore* ms = static_cast<MasterScore*>(s);
|
||
for (MasterScore* _ms : *ms->movements()) {
|
||
_ms->addViewer(this);
|
||
}
|
||
}
|
||
else
|
||
_score->addViewer(this);
|
||
}
|
||
|
||
if (shadowNote == 0) {
|
||
shadowNote = new ShadowNote(_score);
|
||
shadowNote->setVisible(false);
|
||
}
|
||
else
|
||
shadowNote->setScore(_score);
|
||
lasso->setScore(s);
|
||
_continuousPanel->setScore(_score);
|
||
|
||
if (_score) {
|
||
_curLoopIn->move(s->pos(POS::LEFT));
|
||
_curLoopOut->move(s->pos(POS::RIGHT));
|
||
loopToggled(getAction("loop")->isChecked());
|
||
|
||
connect(s, SIGNAL(posChanged(POS,unsigned)), SLOT(posChanged(POS,unsigned)));
|
||
connect(this, SIGNAL(viewRectChanged()), this, SLOT(updateContinuousPanel()));
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// ScoreView
|
||
//---------------------------------------------------------
|
||
|
||
ScoreView::~ScoreView()
|
||
{
|
||
if (_score)
|
||
_score->removeViewer(this);
|
||
delete lasso;
|
||
delete _foto;
|
||
delete _cursor;
|
||
delete _continuousPanel;
|
||
delete _curLoopIn;
|
||
delete _curLoopOut;
|
||
delete _bgPixmap;
|
||
delete _fgPixmap;
|
||
delete shadowNote;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// objectPopup
|
||
// the menu can be extended by Elements with
|
||
// genPropertyMenu()/propertyAction() methods
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::objectPopup(const QPoint& pos, Element* obj)
|
||
{
|
||
// show tuplet properties if number is clicked:
|
||
if (obj->isText() && obj->parent() && obj->parent()->isTuplet()) {
|
||
obj = obj->parent();
|
||
if (!obj->selected())
|
||
obj->score()->select(obj, SelectType::SINGLE, 0);
|
||
}
|
||
|
||
QMenu* popup = new QMenu(this);
|
||
popup->setSeparatorsCollapsible(false);
|
||
QAction* a = popup->addSeparator();
|
||
|
||
// Set Slur or Tie according to the selected object
|
||
if (obj->type() != ElementType::SLUR_SEGMENT) {
|
||
if ((obj->type() == ElementType::STAFF_TEXT) && (toStaffText(obj)->systemFlag()))
|
||
a->setText(tr("System Text"));
|
||
else
|
||
a->setText(obj->userName());
|
||
}
|
||
else if (static_cast<SlurSegment*>(obj)->spanner()->type() == ElementType::SLUR)
|
||
a->setText(tr("Slur"));
|
||
else if (static_cast<SlurSegment*>(obj)->spanner()->type() == ElementType::TIE)
|
||
a->setText(tr("Tie"));
|
||
|
||
popup->addAction(getAction("cut"));
|
||
popup->addAction(getAction("copy"));
|
||
popup->addAction(getAction("paste"));
|
||
popup->addAction(getAction("swap"));
|
||
popup->addAction(getAction("delete"));
|
||
|
||
QMenu* selMenu = popup->addMenu(tr("Select"));
|
||
selMenu->addAction(getAction("select-similar"));
|
||
selMenu->addAction(getAction("select-similar-staff"));
|
||
selMenu->addAction(getAction("select-similar-range"));
|
||
a = selMenu->addAction(tr("More..."));
|
||
a->setData("select-dialog");
|
||
|
||
popup->addSeparator();
|
||
a = getAction("edit-element");
|
||
popup->addAction(a);
|
||
a->setEnabled(obj->isEditable());
|
||
|
||
createElementPropertyMenu(obj, popup);
|
||
|
||
popup->addSeparator();
|
||
a = popup->addAction(tr("Help"));
|
||
a->setData("help");
|
||
|
||
#ifndef NDEBUG
|
||
popup->addSeparator();
|
||
popup->addAction("Debugger")->setData("list");
|
||
#endif
|
||
|
||
a = popup->exec(pos);
|
||
if (a == 0)
|
||
return;
|
||
const QByteArray& cmd(a->data().toByteArray());
|
||
if (cmd == "cut" || cmd =="copy" || cmd == "paste" || cmd == "swap" || cmd == "delete") {
|
||
// these actions are already activated
|
||
return;
|
||
}
|
||
if (cmd == "list")
|
||
mscore->showElementContext(obj);
|
||
else if (cmd == "help")
|
||
mscore->showHelp(QString("element:%1").arg(obj->name()));
|
||
else if (cmd == "edit-element") {
|
||
if (obj->isEditable()) {
|
||
if (obj->score())
|
||
obj->score()->select(obj);
|
||
startEditMode(obj);
|
||
return;
|
||
}
|
||
}
|
||
else if (cmd == "select-similar")
|
||
mscore->selectSimilar(obj, false);
|
||
else if (cmd == "select-similar-staff")
|
||
mscore->selectSimilar(obj, true);
|
||
else if (cmd == "select-similar-range")
|
||
mscore->selectSimilarInRange(obj);
|
||
else if (cmd == "select-dialog")
|
||
mscore->selectElementDialog(obj);
|
||
else {
|
||
_score->startCmd();
|
||
elementPropertyAction(cmd, obj);
|
||
if (score()->undoStack()->active())
|
||
_score->endCmd();
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// measurePopup
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::measurePopup(QContextMenuEvent* ev, Measure* obj)
|
||
{
|
||
int staffIdx;
|
||
int pitch;
|
||
Segment* seg;
|
||
|
||
QPoint gpos = ev->globalPos();
|
||
|
||
if (!_score->pos2measure(editData.startMove, &staffIdx, &pitch, &seg, 0))
|
||
return;
|
||
if (staffIdx == -1) {
|
||
qDebug("ScoreView::measurePopup: staffIdx == -1!");
|
||
return;
|
||
}
|
||
|
||
Staff* staff = _score->staff(staffIdx);
|
||
|
||
QMenu* popup = new QMenu(this);
|
||
popup->setSeparatorsCollapsible(false);
|
||
|
||
QAction* a = popup->addSeparator();
|
||
a->setText(tr("Staff"));
|
||
a = popup->addAction(tr("Edit Drumset..."));
|
||
a->setData("edit-drumset");
|
||
a->setEnabled(staff->part()->instrument()->drumset() != 0);
|
||
|
||
a = popup->addAction(tr("Piano Roll Editor..."));
|
||
a->setData("pianoroll");
|
||
|
||
a = popup->addAction(tr("Staff/Part Properties..."));
|
||
a->setData("staff-properties");
|
||
a = popup->addAction(tr("Split Staff..."));
|
||
a->setData("staff-split");
|
||
|
||
a = popup->addSeparator();
|
||
a->setText(tr("Measure"));
|
||
popup->addAction(getAction("cut"));
|
||
popup->addAction(getAction("copy"));
|
||
popup->addAction(getAction("paste"));
|
||
popup->addAction(getAction("swap"));
|
||
popup->addAction(getAction("delete"));
|
||
|
||
QMenu* menuAdd = popup->addMenu(tr("Add"));
|
||
menuAdd->addAction(getAction("insert-measure"));
|
||
menuAdd->addAction(getAction("insert-measures"));
|
||
menuAdd->addAction(getAction("insert-hbox"));
|
||
menuAdd->addAction(getAction("insert-vbox"));
|
||
menuAdd->addAction(getAction("insert-textframe"));
|
||
|
||
a = popup->addAction(tr("Delete Selected Measures"));
|
||
a->setData("delete-selected-measures");
|
||
|
||
popup->addSeparator();
|
||
|
||
a = popup->addAction(tr("Measure Properties..."));
|
||
a->setData("props");
|
||
a->setEnabled(!obj->isMMRest());
|
||
popup->addSeparator();
|
||
|
||
#ifndef NDEBUG
|
||
popup->addAction("Object Debugger")->setData("list");
|
||
#endif
|
||
|
||
a = popup->exec(gpos);
|
||
if (a == 0)
|
||
return;
|
||
QString cmd(a->data().toString());
|
||
if (cmd == "cut" || cmd =="copy" || cmd == "paste" || cmd == "swap"
|
||
|| cmd == "insert-measure" || cmd == "select-similar"
|
||
|| cmd == "delete") {
|
||
// these actions are already activated
|
||
return;
|
||
}
|
||
_score->startCmd();
|
||
if (cmd == "list")
|
||
mscore->showElementContext(obj);
|
||
else if (cmd == "color")
|
||
_score->colorItem(obj);
|
||
else if (cmd == "edit") {
|
||
if (obj->isEditable()) {
|
||
startEditMode(obj);
|
||
return;
|
||
}
|
||
}
|
||
else if (cmd == "edit-drumset") {
|
||
EditDrumset drumsetEdit(staff->part()->instrument()->drumset(), this);
|
||
if (drumsetEdit.exec()) {
|
||
_score->undo(new ChangeDrumset(staff->part()->instrument(), drumsetEdit.drumset()));
|
||
mscore->updateDrumTools(drumsetEdit.drumset());
|
||
}
|
||
}
|
||
else if (cmd == "drumroll") {
|
||
_score->endCmd();
|
||
mscore->editInDrumroll(staff);
|
||
}
|
||
else if (cmd == "pianoroll") {
|
||
_score->endCmd();
|
||
QPointF p = toLogical(ev->pos());
|
||
Position pp;
|
||
bool foundPos = _score->getPosition(&pp, p, 0);
|
||
mscore->editInPianoroll(staff, foundPos ? &pp : 0);
|
||
}
|
||
else if (cmd == "staff-properties") {
|
||
int tick = obj ? obj->tick() : -1;
|
||
EditStaff editStaff(staff, tick, this);
|
||
connect(&editStaff, SIGNAL(instrumentChanged()), mscore, SLOT(instrumentChanged()));
|
||
editStaff.exec();
|
||
}
|
||
else if (cmd == "staff-split") {
|
||
SplitStaff splitStaff(this);
|
||
if (splitStaff.exec())
|
||
_score->splitStaff(staffIdx, splitStaff.getSplitPoint());
|
||
}
|
||
else if (cmd == "props") {
|
||
MeasureProperties im(obj);
|
||
im.exec();
|
||
}
|
||
else if (cmd == "delete-selected-measures") {
|
||
_score->cmdTimeDelete();
|
||
}
|
||
if (_score->undoStack()->active())
|
||
_score->endCmd();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// setBackground
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::setBackground(QPixmap* pm)
|
||
{
|
||
delete _bgPixmap;
|
||
_bgPixmap = pm;
|
||
update();
|
||
}
|
||
|
||
void ScoreView::setBackground(const QColor& color)
|
||
{
|
||
delete _bgPixmap;
|
||
_bgPixmap = 0;
|
||
_bgColor = color;
|
||
update();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// setForeground
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::setForeground(QPixmap* pm)
|
||
{
|
||
delete _fgPixmap;
|
||
_fgPixmap = pm;
|
||
update();
|
||
}
|
||
|
||
void ScoreView::setForeground(const QColor& color)
|
||
{
|
||
delete _fgPixmap;
|
||
_fgPixmap = 0;
|
||
_fgColor = color;
|
||
update();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// dataChanged
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::dataChanged(const QRectF& r)
|
||
{
|
||
update(_matrix.mapRect(r).toRect()); // generate paint event
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// moveCursor
|
||
// move cursor during playback
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::moveCursor(int tick)
|
||
{
|
||
Measure* measure = score()->tick2measureMM(tick);
|
||
if (measure == 0)
|
||
return;
|
||
|
||
qreal x = 0.0;
|
||
Segment* s;
|
||
for (s = measure->first(SegmentType::ChordRest); s;) {
|
||
int t1 = s->tick();
|
||
int x1 = s->canvasPos().x();
|
||
qreal x2;
|
||
int t2;
|
||
Segment* ns = s->next(SegmentType::ChordRest);
|
||
if (ns) {
|
||
t2 = ns->tick();
|
||
x2 = ns->canvasPos().x();
|
||
}
|
||
else {
|
||
t2 = measure->endTick();
|
||
// measure->width is not good enough because of courtesy keysig, timesig
|
||
Segment* seg = measure->findSegment(SegmentType::EndBarLine, measure->tick() + measure->ticks());
|
||
if(seg)
|
||
x2 = seg->canvasPos().x();
|
||
else
|
||
x2 = measure->canvasPos().x() + measure->width(); //safety, should not happen
|
||
}
|
||
if (tick >= t1 && tick < t2) {
|
||
int dt = t2 - t1;
|
||
qreal dx = x2 - x1;
|
||
x = x1 + dx * (tick-t1) / dt;
|
||
break;
|
||
}
|
||
s = ns;
|
||
}
|
||
if (s == 0)
|
||
return;
|
||
|
||
QColor c(MScore::selectColor[0]);
|
||
c.setAlpha(50);
|
||
_cursor->setColor(c);
|
||
_cursor->setTick(tick);
|
||
|
||
System* system = measure->system();
|
||
if (system == 0)
|
||
return;
|
||
double y = system->staffYpage(0) + system->page()->pos().y();
|
||
double _spatium = score()->spatium();
|
||
|
||
update(_matrix.mapRect(_cursor->rect()).toRect().adjusted(-1,-1,1,1));
|
||
|
||
qreal mag = _spatium / SPATIUM20;
|
||
double w = _spatium * 2.0 + score()->scoreFont()->width(SymId::noteheadBlack, mag);
|
||
double h = 6 * _spatium;
|
||
//
|
||
// set cursor height for whole system
|
||
//
|
||
double y2 = 0.0;
|
||
|
||
for (int i = 0; i < _score->nstaves(); ++i) {
|
||
SysStaff* ss = system->staff(i);
|
||
if (!ss->show() || !_score->staff(i)->show())
|
||
continue;
|
||
y2 = ss->bbox().bottom();
|
||
}
|
||
h += y2;
|
||
x -= _spatium;
|
||
y -= 3 * _spatium;
|
||
|
||
_cursor->setRect(QRectF(x, y, w, h));
|
||
update(_matrix.mapRect(_cursor->rect()).toRect().adjusted(-1,-1,1,1));
|
||
if (mscore->state() == ScoreState::STATE_PLAY && mscore->panDuringPlayback())
|
||
adjustCanvasPosition(measure, true);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// moveCursor
|
||
// move cursor in note input mode
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::moveCursor()
|
||
{
|
||
const InputState& is = _score->inputState();
|
||
Segment* segment = is.segment();
|
||
if (segment && score()->styleB(Sid::createMultiMeasureRests) && segment->measure()->hasMMRest()) {
|
||
Measure* m = segment->measure()->mmRest();
|
||
segment = m->findSegment(SegmentType::ChordRest, m->tick());
|
||
}
|
||
if (!segment)
|
||
return;
|
||
|
||
int track = is.track() == -1 ? 0 : is.track();
|
||
int voice = track % VOICES;
|
||
int staffIdx = track / VOICES;
|
||
|
||
QColor c(MScore::selectColor[voice]);
|
||
c.setAlpha(50);
|
||
_cursor->setColor(c);
|
||
_cursor->setTick(segment->tick());
|
||
|
||
System* system = segment->measure()->system();
|
||
if (system == 0) {
|
||
// a new measure was appended but no layout took place
|
||
// or this measure was skipped by a multi measure rest
|
||
return;
|
||
}
|
||
double x = segment->canvasPos().x();
|
||
double y = system->staffYpage(staffIdx) + system->page()->pos().y();
|
||
double _spatium = score()->spatium();
|
||
x -= qMin(segment->pos().x() - score()->styleP(Sid::barNoteDistance), 0.0);
|
||
|
||
update(_matrix.mapRect(_cursor->rect()).toRect().adjusted(-1,-1,1,1));
|
||
|
||
double h;
|
||
qreal mag = _spatium / SPATIUM20;
|
||
double w = _spatium * 2.0 + score()->scoreFont()->width(SymId::noteheadBlack, mag);
|
||
Staff* staff = score()->staff(staffIdx);
|
||
const StaffType* staffType = staff->staffType(is.tick());
|
||
double lineDist = staffType->lineDistance().val() * _spatium;
|
||
int lines = staffType->lines();
|
||
int strg = is.string(); // strg refers to an instrument physical string
|
||
x -= _spatium;
|
||
int instrStrgs = staff->part()->instrument()->stringData()->strings();
|
||
// if on a TAB staff and InputState::_string makes sense,
|
||
// draw cursor around single string
|
||
if (staff->isTabStaff(is.tick()) && strg >= 0 && strg <= instrStrgs) {
|
||
h = lineDist; // cursor height is one full line distance
|
||
y += staffType->physStringToYOffset(strg) * _spatium;
|
||
// if frets are on lines, centre on string; if frets are above lines, 'sit' above string
|
||
y -= (staffType->onLines() ? lineDist * 0.5 : lineDist);
|
||
// look for a note on this string in this staff
|
||
// if found, it will be selected, to synchronize the 'new note input cursor' and the 'current note cursor'
|
||
// i.e. the point where a new note would be added and the existing note which receives any editing
|
||
// (like pitch change or articulation addition)
|
||
bool done = false;
|
||
Segment* seg = is.segment();
|
||
int minTrack = (is.track() / VOICES) * VOICES;
|
||
int maxTrack = minTrack + VOICES;
|
||
// get selected chord, if one exists and is in this segment
|
||
ChordRest* scr = _score->selection().cr();
|
||
if (scr && (scr->type() != ElementType::CHORD || scr->segment() != seg))
|
||
scr = nullptr;
|
||
// get the physical string corresponding to current visual string
|
||
for (int t = minTrack; t < maxTrack; t++) {
|
||
Element* e = seg->element(t);
|
||
if (e != nullptr && e->type() == ElementType::CHORD) {
|
||
// if there is a selected chord in this segment on this track but it is not e
|
||
// then the selected chord must be a grace note chord, and we should use it
|
||
if (scr && scr->track() == t && scr != e)
|
||
e = scr;
|
||
// search notes looking for one on current string
|
||
for (Note* n : static_cast<Chord*>(e)->notes())
|
||
// if note found on this string, make it current
|
||
if (n->string() == strg) {
|
||
if (!n->selected()) {
|
||
_score->select(n);
|
||
// restore input state after selection
|
||
_score->inputState().setTrack(track);
|
||
}
|
||
#if 0
|
||
// if using this code, we can delete the setTrack() call above
|
||
// the code below forces input state & cursor to match current note
|
||
_score->inputState().setTrack(t);
|
||
QColor c(MScore::selectColor[t % VOICES]);
|
||
c.setAlpha(50);
|
||
_cursor->setColor(c);
|
||
#endif
|
||
done = true;
|
||
break;
|
||
}
|
||
}
|
||
if (done)
|
||
break;
|
||
}
|
||
}
|
||
// otherwise, draw cursor across whole staff
|
||
else {
|
||
h = (lines - 1) * lineDist + 4 * _spatium;
|
||
y -= 2.0 * _spatium;
|
||
}
|
||
_cursor->setRect(QRectF(x, y, w, h));
|
||
update(_matrix.mapRect(_cursor->rect()).toRect().adjusted(-1,-1,1,1));
|
||
if (is.cr())
|
||
adjustCanvasPosition(is.cr(), false);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cursorTick
|
||
//---------------------------------------------------------
|
||
|
||
int ScoreView::cursorTick() const
|
||
{
|
||
return _cursor->tick();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// setCursorOn
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::setCursorOn(bool val)
|
||
{
|
||
if (_cursor && (_cursor->visible() != val)) {
|
||
_cursor->setVisible(val);
|
||
update(_matrix.mapRect(_cursor->rect()).toRect().adjusted(-1,-1,1,1));
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// setLoopCursor
|
||
// adjust the cursor shape and position to mark the loop
|
||
// isInPos is used to adjust the x position of In vs Out mark
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::setLoopCursor(PositionCursor *curLoop, int tick, bool isInPos)
|
||
{
|
||
//
|
||
// set mark height for whole system
|
||
//
|
||
Measure* measure = score()->tick2measure(tick);
|
||
if (measure == 0)
|
||
return;
|
||
qreal x = 0.0;
|
||
|
||
Segment* s;
|
||
for (s = measure->first(SegmentType::ChordRest); s;) {
|
||
int t1 = s->tick();
|
||
int x1 = s->canvasPos().x();
|
||
qreal x2;
|
||
int t2;
|
||
Segment* ns = s->next(SegmentType::ChordRest);
|
||
if (ns) {
|
||
t2 = ns->tick();
|
||
x2 = ns->canvasPos().x();
|
||
}
|
||
else {
|
||
t2 = measure->endTick();
|
||
x2 = measure->canvasPos().x() + measure->width();
|
||
}
|
||
if (tick >= t1 && tick < t2) {
|
||
int dt = t2 - t1;
|
||
qreal dx = x2 - x1;
|
||
x = x1 + dx * (tick-t1) / dt;
|
||
break;
|
||
}
|
||
s = ns;
|
||
}
|
||
if (s == 0)
|
||
return;
|
||
|
||
System* system = measure->system();
|
||
if (system == 0)
|
||
return;
|
||
double y = system->staffYpage(0) + system->page()->pos().y();
|
||
double _spatium = score()->spatium();
|
||
|
||
qreal mag = _spatium / SPATIUM20;
|
||
double w = (_spatium * 2.0 + score()->scoreFont()->width(SymId::noteheadBlack, mag))/3;
|
||
double h = 6 * _spatium;
|
||
//
|
||
// set cursor height for whole system
|
||
//
|
||
double y2 = 0.0;
|
||
|
||
for (int i = 0; i < _score->nstaves(); ++i) {
|
||
SysStaff* ss = system->staff(i);
|
||
if (!ss->show() || !_score->staff(i)->show())
|
||
continue;
|
||
y2 = ss->y() + ss->bbox().height();
|
||
}
|
||
h += y2;
|
||
y -= 3 * _spatium;
|
||
|
||
if (isInPos) {
|
||
x = x - _spatium + w/1.5;
|
||
}
|
||
else {
|
||
x = x - _spatium;
|
||
}
|
||
curLoop->setTick(tick);
|
||
update(_matrix.mapRect(curLoop->rect()).toRect().adjusted(-1,-1,1,1));
|
||
curLoop->setRect(QRectF(x, y, w, h));
|
||
update(_matrix.mapRect(curLoop->rect()).toRect().adjusted(-1,-1,1,1));
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// setShadowNote
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::setShadowNote(const QPointF& p)
|
||
{
|
||
const InputState& is = _score->inputState();
|
||
Position pos;
|
||
if (!score()->getPosition(&pos, p, is.voice())) {
|
||
shadowNote->setVisible(false);
|
||
return;
|
||
}
|
||
// in any empty measure, pos will be right next to barline
|
||
// so pad this by barNoteDistance
|
||
qreal mag = score()->staff(pos.staffIdx)->mag(0);
|
||
qreal relX = pos.pos.x() - pos.segment->measure()->canvasPos().x();
|
||
pos.pos.rx() -= qMin(relX - score()->styleP(Sid::barNoteDistance) * mag, 0.0);
|
||
|
||
shadowNote->setVisible(true);
|
||
Staff* staff = score()->staff(pos.staffIdx);
|
||
shadowNote->setMag(staff->mag(0));
|
||
const Instrument* instr = staff->part()->instrument();
|
||
NoteHead::Group noteheadGroup = NoteHead::Group::HEAD_NORMAL;
|
||
int line = pos.line;
|
||
NoteHead::Type noteHead = is.duration().headType();
|
||
|
||
if (instr->useDrumset()) {
|
||
const Drumset* ds = instr->drumset();
|
||
int pitch = is.drumNote();
|
||
if (pitch >= 0 && ds->isValid(pitch)) {
|
||
line = ds->line(pitch);
|
||
noteheadGroup = ds->noteHead(pitch);
|
||
}
|
||
}
|
||
|
||
shadowNote->setLine(line);
|
||
|
||
int voice;
|
||
if (is.drumNote() != -1 && is.drumset() && is.drumset()->isValid(is.drumNote()))
|
||
voice = is.drumset()->voice(is.drumNote());
|
||
else
|
||
voice = is.voice();
|
||
|
||
SymId symNotehead;
|
||
TDuration d(is.duration());
|
||
|
||
if (is.rest()) {
|
||
int yo;
|
||
Rest rest(gscore, d.type());
|
||
rest.setDuration(d.fraction());
|
||
symNotehead = rest.getSymbol(is.duration().type(), 0, staff->lines(pos.segment->tick()), &yo);
|
||
shadowNote->setState(symNotehead, voice, d, true);
|
||
}
|
||
else {
|
||
if (NoteHead::Group::HEAD_CUSTOM == noteheadGroup)
|
||
symNotehead = instr->drumset()->noteHeads(is.drumNote(), noteHead);
|
||
else
|
||
symNotehead = Note::noteHead(0, noteheadGroup, noteHead);
|
||
|
||
shadowNote->setState(symNotehead, voice, d);
|
||
}
|
||
|
||
shadowNote->layout();
|
||
shadowNote->setPos(pos.pos);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// paintEvent
|
||
// Note: desktop background and paper background are not
|
||
// scaled
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::paintEvent(QPaintEvent* ev)
|
||
{
|
||
if (!_score)
|
||
return;
|
||
QPainter vp(this);
|
||
vp.setRenderHint(QPainter::Antialiasing, preferences.getBool(PREF_UI_CANVAS_MISC_ANTIALIASEDDRAWING));
|
||
vp.setRenderHint(QPainter::TextAntialiasing, true);
|
||
|
||
paint(ev->rect(), vp);
|
||
|
||
vp.setTransform(_matrix);
|
||
vp.setClipping(false);
|
||
|
||
_curLoopIn->paint(&vp);
|
||
_curLoopOut->paint(&vp);
|
||
_cursor->paint(&vp);
|
||
|
||
if (_score->layoutMode() == LayoutMode::LINE)
|
||
_continuousPanel->paint(ev->rect(), vp);
|
||
|
||
if (!lasso->bbox().isEmpty())
|
||
lasso->draw(&vp);
|
||
shadowNote->draw(&vp);
|
||
|
||
if (!dropAnchor.isNull()) {
|
||
QPen pen(QBrush(QColor(80, 0, 0)), 2.0 / vp.worldTransform().m11(), Qt::DotLine);
|
||
vp.setPen(pen);
|
||
vp.drawLine(dropAnchor);
|
||
|
||
qreal d = 4.0 / vp.worldTransform().m11();
|
||
QRectF r(-d, -d, 2 * d, 2 * d);
|
||
|
||
vp.setBrush(QBrush(QColor(80, 0, 0)));
|
||
vp.setPen(Qt::NoPen);
|
||
r.moveCenter(dropAnchor.p1());
|
||
vp.drawEllipse(r);
|
||
r.moveCenter(dropAnchor.p2());
|
||
vp.drawEllipse(r);
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// drawBackground
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::drawBackground(QPainter* p, const QRectF& r) const
|
||
{
|
||
if (score()->printing()) {
|
||
p->fillRect(r, Qt::white);
|
||
return;
|
||
}
|
||
if (_fgPixmap == 0 || _fgPixmap->isNull())
|
||
p->fillRect(r, _fgColor);
|
||
else {
|
||
p->drawTiledPixmap(r, *_fgPixmap, r.topLeft()
|
||
- QPoint(lrint(_matrix.dx()), lrint(_matrix.dy())));
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// paintPageBorder
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::paintPageBorder(QPainter& p, Page* page)
|
||
{
|
||
//add a black border to pages
|
||
QRectF r(page->canvasBoundingRect());
|
||
p.setBrush(Qt::NoBrush);
|
||
p.setPen(QPen(QColor(0,0,0,102), 1));
|
||
p.drawRect(r);
|
||
|
||
if (_score->showPageborders()) {
|
||
// show page margins
|
||
p.setBrush(Qt::NoBrush);
|
||
p.setPen(MScore::frameMarginColor);
|
||
QRectF f(page->canvasBoundingRect());
|
||
f.adjust(page->lm(), page->tm(), -page->rm(), -page->bm());
|
||
p.drawRect(f);
|
||
if (!page->isOdd())
|
||
p.drawLine(f.right(), 0.0, f.right(), f.bottom());
|
||
}
|
||
}
|
||
|
||
#ifndef NDEBUG
|
||
//---------------------------------------------------------
|
||
// drawDebugInfo
|
||
//---------------------------------------------------------
|
||
|
||
static void drawDebugInfo(QPainter& p, const Element* _e)
|
||
{
|
||
if (!MScore::showBoundingRect)
|
||
return;
|
||
const Element* e = _e;
|
||
//
|
||
// draw bounding box rectangle for all
|
||
// selected Elements
|
||
//
|
||
QPointF pos(e->pagePos());
|
||
p.translate(pos);
|
||
p.setBrush(Qt::NoBrush);
|
||
|
||
p.setPen(QPen(Qt::red, 0.0));
|
||
// p.drawRect(e->bbox());
|
||
e->shape().paint(p);
|
||
|
||
p.setPen(QPen(Qt::red, 0.0)); // red x at 0,0 of bbox
|
||
qreal w = 5.0 / p.matrix().m11();
|
||
qreal h = w;
|
||
qreal x = 0; // e->bbox().x();
|
||
qreal y = 0; // e->bbox().y();
|
||
p.drawLine(QLineF(x-w, y-h, x+w, y+h));
|
||
p.drawLine(QLineF(x+w, y-h, x-w, y+h));
|
||
|
||
p.translate(-pos);
|
||
if (e->parent()) {
|
||
const Element* ee = e->parent();
|
||
if (e->isNote())
|
||
ee = toNote(e)->chord()->segment();
|
||
else if (e->isClef())
|
||
ee = toClef(e)->segment();
|
||
|
||
p.setPen(QPen(Qt::green, 0.0));
|
||
|
||
p.drawRect(ee->pageBoundingRect());
|
||
|
||
if (ee->isSegment()) {
|
||
QPointF pt = ee->pagePos();
|
||
p.setPen(QPen(Qt::blue, 0.0));
|
||
p.drawLine(QLineF(pt.x()-w, pt.y()-h, pt.x()+w, pt.y()+h));
|
||
p.drawLine(QLineF(pt.x()+w, pt.y()-h, pt.x()-w, pt.y()+h));
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
//---------------------------------------------------------
|
||
// drawElements
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::drawElements(QPainter& painter, QList<Element*>& el, Element* editElement)
|
||
{
|
||
qStableSort(el.begin(), el.end(), elementLessThan);
|
||
for (const Element* e : el) {
|
||
e->itemDiscovered = 0;
|
||
|
||
// harmony element representation is different in edit mode, so don't
|
||
// all normal draw(). Complete drawing is done in drawEditMode()
|
||
if (e == editElement)
|
||
continue;
|
||
|
||
if (!e->visible() && (score()->printing() || !score()->showInvisible()))
|
||
continue;
|
||
if (e->isRest() && toRest(e)->isGap())
|
||
continue;
|
||
QPointF pos(e->pagePos());
|
||
painter.translate(pos);
|
||
e->draw(&painter);
|
||
painter.translate(-pos);
|
||
#ifndef NDEBUG
|
||
if (e->selected())
|
||
drawDebugInfo(painter, e);
|
||
#endif
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// paint
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::paint(const QRect& r, QPainter& p)
|
||
{
|
||
p.save();
|
||
if (_fgPixmap == 0 || _fgPixmap->isNull())
|
||
p.fillRect(r, _fgColor);
|
||
else {
|
||
p.drawTiledPixmap(r, *_fgPixmap, r.topLeft()
|
||
- QPoint(lrint(_matrix.dx()), lrint(_matrix.dy())));
|
||
}
|
||
|
||
p.setTransform(_matrix);
|
||
QRectF fr = imatrix.mapRect(QRectF(r));
|
||
|
||
Element* editElement = 0;
|
||
Element* lassoToDraw = 0;
|
||
if (editData.element) {
|
||
switch (state) {
|
||
case ViewState::NORMAL:
|
||
case ViewState::DRAG:
|
||
case ViewState::DRAG_OBJECT:
|
||
case ViewState::LASSO:
|
||
case ViewState::NOTE_ENTRY:
|
||
case ViewState::PLAY:
|
||
case ViewState::ENTRY_PLAY:
|
||
break;
|
||
case ViewState::EDIT:
|
||
case ViewState::DRAG_EDIT:
|
||
case ViewState::FOTO:
|
||
case ViewState::FOTO_DRAG:
|
||
case ViewState::FOTO_DRAG_EDIT:
|
||
case ViewState::FOTO_DRAG_OBJECT:
|
||
case ViewState::FOTO_LASSO:
|
||
if (editData.element->_name() == "Lasso") // There is no isLasso() method
|
||
lassoToDraw = editData.element;
|
||
else
|
||
editData.element->drawEditMode(&p, editData);
|
||
|
||
if (editData.element->isHarmony())
|
||
editElement = editData.element; // do not call paint() method
|
||
break;
|
||
}
|
||
}
|
||
|
||
QRegion r1(r);
|
||
if ((_score->layoutMode() == LayoutMode::LINE) || (_score->layoutMode() == LayoutMode::SYSTEM)) {
|
||
if (_score->pages().size() > 0) {
|
||
Page* page = _score->pages().front();
|
||
QList<Element*> ell = page->items(fr);
|
||
drawElements(p, ell, editElement);
|
||
}
|
||
}
|
||
else {
|
||
for (Page* page : _score->pages()) {
|
||
QRectF pr(page->abbox().translated(page->pos()));
|
||
if (pr.right() < fr.left())
|
||
continue;
|
||
if (pr.left() > fr.right())
|
||
break;
|
||
|
||
if (!score()->printing())
|
||
paintPageBorder(p, page);
|
||
QList<Element*> ell = page->items(fr.translated(-page->pos()));
|
||
QPointF pos(page->pos());
|
||
p.translate(pos);
|
||
drawElements(p, ell, editElement);
|
||
|
||
#ifndef NDEBUG
|
||
if (!score()->printing()) {
|
||
if (MScore::showSystemBoundingRect) {
|
||
for (const System* system : page->systems()) {
|
||
QPointF pt(system->ipos());
|
||
qreal h = system->minBottom() + system->minTop();
|
||
p.translate(pt);
|
||
QRectF rect(0.0, -system->minTop(), system->width(), h);
|
||
p.drawRect(rect);
|
||
p.translate(-pt);
|
||
}
|
||
}
|
||
if (MScore::showSegmentShapes) {
|
||
for (const System* system : page->systems()) {
|
||
for (const MeasureBase* mb : system->measures()) {
|
||
if (mb->type() == ElementType::MEASURE) {
|
||
const Measure* m = static_cast<const Measure*>(mb);
|
||
p.setBrush(Qt::NoBrush);
|
||
p.setPen(QPen(QBrush(Qt::darkYellow), 0.5));
|
||
for (const Segment* s = m->first(); s; s = s->next()) {
|
||
for (int i = 0; i < score()->nstaves(); ++i) {
|
||
QPointF pt(s->pos().x() + m->pos().x() + system->pos().x(),
|
||
system->staffYpage(i));
|
||
p.translate(pt);
|
||
s->shapes().at(i).paint(p);
|
||
p.translate(-pt);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (MScore::showSkylines) {
|
||
for (const System* system : page->systems()) {
|
||
for (SysStaff* ss : *system->staves()) {
|
||
QPointF pt(system->ipos().x(), system->ipos().y() + ss->y());
|
||
p.translate(pt);
|
||
ss->skyline().paint(p);
|
||
p.translate(-pt);
|
||
}
|
||
}
|
||
}
|
||
if (MScore::showCorruptedMeasures) {
|
||
double _spatium = score()->spatium();
|
||
QPen pen;
|
||
pen.setColor(Qt::red);
|
||
pen.setWidthF(4);
|
||
pen.setStyle(Qt::SolidLine);
|
||
p.setPen(pen);
|
||
p.setBrush(Qt::NoBrush);
|
||
for (const System* system : page->systems()) {
|
||
for (const MeasureBase* mb : system->measures()) {
|
||
if (mb->type() == ElementType::MEASURE) {
|
||
const Measure* m = static_cast<const Measure*>(mb);
|
||
for (int staffIdx = 0; staffIdx < m->score()->nstaves(); staffIdx++) {
|
||
if (m->corrupted(staffIdx)) {
|
||
p.drawRect(m->staffabbox(staffIdx).adjusted(0, -_spatium, 0, _spatium));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
p.translate(-pos);
|
||
r1 -= _matrix.mapRect(pr).toAlignedRect();
|
||
}
|
||
}
|
||
if (dropRectangle.isValid())
|
||
p.fillRect(dropRectangle, QColor(80, 0, 0, 80));
|
||
|
||
const Selection& sel = _score->selection();
|
||
if (sel.isRange()) {
|
||
Segment* ss = sel.startSegment();
|
||
Segment* es = sel.endSegment();
|
||
|
||
if (!ss)
|
||
return;
|
||
|
||
if (!ss->measure()->system()) {
|
||
// segment is in a measure that has not been laid out yet
|
||
// this can happen in mmrests
|
||
// first chordrest segment of mmrest instead
|
||
const Measure* mmr = ss->measure()->mmRest1();
|
||
if (mmr && mmr->system())
|
||
ss = mmr->first(SegmentType::ChordRest);
|
||
else
|
||
return; // still no system?
|
||
if (!ss)
|
||
return; // no chordrest segment?
|
||
}
|
||
|
||
p.setBrush(Qt::NoBrush);
|
||
|
||
QPen pen;
|
||
pen.setColor(MScore::selectColor[0]);
|
||
pen.setWidthF(2.0 / p.matrix().m11());
|
||
|
||
pen.setStyle(Qt::SolidLine);
|
||
|
||
p.setPen(pen);
|
||
double _spatium = score()->spatium();
|
||
double x2 = ss->pagePos().x() - _spatium;
|
||
int staffStart = sel.staffStart();
|
||
int staffEnd = sel.staffEnd();
|
||
|
||
System* system2 = ss->measure()->system();
|
||
QPointF pt = ss->pagePos();
|
||
double y = pt.y();
|
||
SysStaff* ss1 = system2->staff(staffStart);
|
||
|
||
// find last visible staff:
|
||
int lastStaff = 0;
|
||
for (int i = staffEnd-1; i >= 0; --i) {
|
||
if (score()->staff(i)->show()) {
|
||
lastStaff = i;
|
||
break;
|
||
}
|
||
}
|
||
SysStaff* ss2 = system2->staff(lastStaff);
|
||
|
||
double y1 = ss1->y() - 2 * score()->staff(staffStart)->spatium(0) + y;
|
||
double y2 = ss2->y() + ss2->bbox().height() + 2 * score()->staff(lastStaff)->spatium(0) + y;
|
||
|
||
// drag vertical start line
|
||
p.drawLine(QLineF(x2, y1, x2, y2).translated(system2->page()->pos()));
|
||
|
||
System* system1 = system2;
|
||
double x1;
|
||
|
||
for (Segment* s = ss; s && (s != es); ) {
|
||
Segment* ns = s->next1MM();
|
||
system1 = system2;
|
||
system2 = s->measure()->system();
|
||
if (!system2) {
|
||
// as before, use mmrest if necessary
|
||
const Measure* mmr = s->measure()->mmRest1();
|
||
if (mmr)
|
||
system2 = mmr->system();
|
||
if (!system2)
|
||
break;
|
||
// extend rectangle to end of mmrest
|
||
pt = mmr->last()->pagePos();
|
||
}
|
||
else
|
||
pt = s->pagePos();
|
||
x1 = x2;
|
||
x2 = pt.x() + _spatium * 2;
|
||
|
||
if (ns == 0 || ns == es) { // last segment?
|
||
// if any staff in selection has measure rest or repeat measure in last measure,
|
||
// extend rectangle to bar line
|
||
Segment* fs = s->measure()->first(SegmentType::ChordRest);
|
||
if (fs) {
|
||
for (int i = staffStart; i < staffEnd; ++i) {
|
||
if (!score()->staff(i)->show())
|
||
continue;
|
||
ChordRest* cr = static_cast<ChordRest*>(fs->element(i * VOICES));
|
||
if (cr && (cr->type() == ElementType::REPEAT_MEASURE || cr->durationType() == TDuration::DurationType::V_MEASURE)) {
|
||
x2 = s->measure()->abbox().right() - _spatium * 0.5;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (system2 != system1)
|
||
x1 = x2 - 2 * _spatium;
|
||
y = pt.y();
|
||
ss1 = system2->staff(staffStart);
|
||
ss2 = system2->staff(lastStaff);
|
||
y1 = ss1->y() - 2 * score()->staff(staffStart)->spatium(s->tick()) + y;
|
||
y2 = ss2->y() + ss2->bbox().height() + 2 * score()->staff(lastStaff)->spatium(s->tick()) + y;
|
||
p.drawLine(QLineF(x1, y1, x2, y1).translated(system2->page()->pos()));
|
||
p.drawLine(QLineF(x1, y2, x2, y2).translated(system2->page()->pos()));
|
||
s = ns;
|
||
}
|
||
//
|
||
// draw vertical end line
|
||
//
|
||
p.drawLine(QLineF(x2, y1, x2, y2).translated(system2->page()->pos()));
|
||
}
|
||
|
||
// Draw foto lasso to ensure that it is above everything else
|
||
if (lassoToDraw)
|
||
lassoToDraw->drawEditMode(&p, editData);
|
||
|
||
p.setMatrixEnabled(false);
|
||
if (_score->layoutMode() != LayoutMode::LINE && _score->layoutMode() != LayoutMode::SYSTEM && !r1.isEmpty()) {
|
||
p.setClipRegion(r1); // only background
|
||
if (_bgPixmap == 0 || _bgPixmap->isNull())
|
||
p.fillRect(r, _bgColor);
|
||
else
|
||
p.drawTiledPixmap(r, *_bgPixmap, r.topLeft() - QPoint(_matrix.m31(), _matrix.m32()));
|
||
}
|
||
p.restore();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// zoomStep: zoom in or out by some number of steps
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::zoomStep(qreal step, const QPoint& pos)
|
||
{
|
||
qreal _mag = lmag();
|
||
|
||
_mag *= qPow(1.1, step);
|
||
|
||
zoom(_mag, QPointF(pos));
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// zoom: zoom to some absolute zoom level
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::zoom(qreal _mag, const QPointF& pos)
|
||
{
|
||
QPointF p1 = imatrix.map(pos);
|
||
|
||
if (_mag > 16.0)
|
||
_mag = 16.0;
|
||
else if (_mag < 0.05)
|
||
_mag = 0.05;
|
||
|
||
mscore->setMag(_mag);
|
||
|
||
double m = _mag * mscore->physicalDotsPerInch() / DPI;
|
||
|
||
setMag(m);
|
||
|
||
_magIdx = MagIdx::MAG_FREE;
|
||
QPointF p2 = imatrix.map(pos);
|
||
QPointF p3 = p2 - p1;
|
||
int dx = lrint(p3.x() * m);
|
||
int dy = lrint(p3.y() * m);
|
||
|
||
constraintCanvas(&dx, &dy);
|
||
|
||
_matrix.setMatrix(_matrix.m11(), _matrix.m12(), _matrix.m13(), _matrix.m21(),
|
||
_matrix.m22(), _matrix.m23(), _matrix.dx()+dx, _matrix.dy()+dy, _matrix.m33());
|
||
imatrix = _matrix.inverted();
|
||
scroll(dx, dy, QRect(0, 0, width(), height()));
|
||
emit viewRectChanged();
|
||
emit offsetChanged(_matrix.dx(), _matrix.dy());
|
||
update();
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// constraintCanvas
|
||
//-----------------------------------------------------------------------------
|
||
|
||
void ScoreView::constraintCanvas (int* dxx, int* dyy)
|
||
{
|
||
if (score()->layoutMode() == LayoutMode::SYSTEM)
|
||
return;
|
||
if (score()->pages().isEmpty())
|
||
return;
|
||
int dx = *dxx;
|
||
int dy = *dyy;
|
||
QRectF rect = QRectF(0, 0, width(), height());
|
||
|
||
Page* firstPage = score()->pages().front();
|
||
Page* lastPage = score()->pages().back();
|
||
|
||
if (firstPage && lastPage) {
|
||
QPointF offsetPt(xoffset(), yoffset());
|
||
QRectF firstPageRect(firstPage->pos().x() * mag(),
|
||
firstPage->pos().y() * mag(),
|
||
firstPage->width() * mag(),
|
||
firstPage->height() * mag());
|
||
QRectF lastPageRect(lastPage->pos().x() * mag(),
|
||
lastPage->pos().y() * mag(),
|
||
lastPage->width() * mag(),
|
||
lastPage->height() * mag());
|
||
QRectF pagesRect = firstPageRect.united(lastPageRect).translated(offsetPt);
|
||
bool limitScrollArea = preferences.getBool(PREF_UI_CANVAS_SCROLL_LIMITSCROLLAREA);
|
||
if (!limitScrollArea) {
|
||
qreal hmargin = this->width() * 0.75;
|
||
qreal vmargin = this->height() * 0.75;
|
||
pagesRect.adjust(-hmargin, -vmargin, hmargin, vmargin);
|
||
}
|
||
QRectF toPagesRect = pagesRect.translated(dx, dy);
|
||
|
||
if (limitScrollArea) {
|
||
if (pagesRect.width() <= rect.width()) {
|
||
if (score()->layoutMode() == LayoutMode::LINE)
|
||
// keep score fixed in place horizontally
|
||
dx = 0;
|
||
else
|
||
// center horizontally on screen
|
||
dx = (rect.width() - pagesRect.width()) / 2 - pagesRect.left();
|
||
}
|
||
else if (toPagesRect.left() > rect.left())
|
||
// get rid of the left margin
|
||
dx = rect.left() - pagesRect.left();
|
||
else if (toPagesRect.right() < rect.right())
|
||
// get rid of the right margin
|
||
dx = rect.right() - pagesRect.right();
|
||
}
|
||
else if (dx > 0) { // move right
|
||
if (toPagesRect.right() > rect.right() && toPagesRect.left() > rect.left()) {
|
||
if(pagesRect.width() <= rect.width()) {
|
||
dx = rect.right() - pagesRect.right();
|
||
}
|
||
else {
|
||
dx = rect.left() - pagesRect.left();
|
||
}
|
||
}
|
||
}
|
||
else { // move left, dx < 0
|
||
if (toPagesRect.left() < rect.left() && toPagesRect.right() < rect.right()) {
|
||
if (pagesRect.width() <= rect.width()) {
|
||
dx = rect.left() - pagesRect.left();
|
||
}
|
||
else {
|
||
dx = rect.right() - pagesRect.right();
|
||
}
|
||
}
|
||
}
|
||
|
||
if (limitScrollArea) {
|
||
if (pagesRect.height() <= rect.height()) {
|
||
if (score()->layoutMode() == LayoutMode::LINE)
|
||
// keep score fixed in place vertically
|
||
dy = 0;
|
||
else
|
||
// center vertically on screen
|
||
dy = (rect.height() - pagesRect.height()) / 2 - pagesRect.top();
|
||
}
|
||
else if (toPagesRect.top() > rect.top())
|
||
// get rid of the top margin
|
||
dy = rect.top() - pagesRect.top();
|
||
else if (toPagesRect.bottom() < rect.bottom())
|
||
// get rid of the bottom margin
|
||
dy = rect.bottom() - pagesRect.bottom();
|
||
}
|
||
else if (dy > 0) { // move down
|
||
if (toPagesRect.bottom() > rect.bottom() && toPagesRect.top() > rect.top()) {
|
||
if (pagesRect.height() <= rect.height()) {
|
||
dy = rect.bottom() - pagesRect.bottom();
|
||
}
|
||
else {
|
||
dy = rect.top() - pagesRect.top();
|
||
}
|
||
}
|
||
}
|
||
else { // move up, dy < 0
|
||
if (toPagesRect.top() < rect.top() && toPagesRect.bottom() < rect.bottom()) {
|
||
if (pagesRect.height() <= rect.height()) {
|
||
dy = rect.top() - pagesRect.top();
|
||
}
|
||
else {
|
||
dy = rect.bottom() - pagesRect.bottom();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
*dxx = dx;
|
||
*dyy = dy;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// setMag
|
||
// nmag - physical scale
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::setMag(qreal nmag)
|
||
{
|
||
qreal m = _matrix.m11();
|
||
|
||
if (nmag == m)
|
||
return;
|
||
double deltamag = nmag / m;
|
||
|
||
_matrix.setMatrix(nmag, _matrix.m12(), _matrix.m13(), _matrix.m21(),
|
||
nmag, _matrix.m23(), _matrix.dx()*deltamag, _matrix.dy()*deltamag, _matrix.m33());
|
||
imatrix = _matrix.inverted();
|
||
emit scaleChanged(nmag * score()->spatium());
|
||
if (editData.grips) {
|
||
qreal w = 8.0 / nmag;
|
||
qreal h = 8.0 / nmag;
|
||
QRectF r(-w*.5, -h*.5, w, h);
|
||
for (int i = 0; i < editData.grips; ++i) {
|
||
QPointF p(editData.grip[i].center());
|
||
editData.grip[i] = r.translated(p);
|
||
}
|
||
}
|
||
update();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// setMag
|
||
// mag - logical scale
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::setMag(MagIdx idx, double mag)
|
||
{
|
||
_magIdx = idx;
|
||
setMag(mag * (mscore->physicalDotsPerInch() / DPI));
|
||
int dx = 0, dy = 0;
|
||
constraintCanvas(&dx, &dy);
|
||
if (dx != 0 || dy != 0) {
|
||
_matrix.setMatrix(_matrix.m11(), _matrix.m12(), _matrix.m13(), _matrix.m21(),
|
||
_matrix.m22(), _matrix.m23(), _matrix.dx()+dx, _matrix.dy()+dy, _matrix.m33());
|
||
imatrix = _matrix.inverted();
|
||
scroll(dx, dy, QRect(0, 0, width(), height()));
|
||
emit offsetChanged(_matrix.dx(), _matrix.dy());
|
||
}
|
||
emit viewRectChanged();
|
||
update();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// setFocusRect
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::setFocusRect()
|
||
{
|
||
if (mscore->splitScreen()) {
|
||
if (!focusFrame) {
|
||
focusFrame = new QFocusFrame;
|
||
QPalette p(focusFrame->palette());
|
||
p.setColor(QPalette::WindowText, MScore::selectColor[0]);
|
||
focusFrame->setPalette(p);
|
||
}
|
||
focusFrame->setWidget(static_cast<QWidget*>(this));
|
||
focusFrame->show();
|
||
}
|
||
else {
|
||
if (focusFrame)
|
||
focusFrame->setWidget(0);
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// editCopy
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::editCopy()
|
||
{
|
||
if (editData.element)
|
||
editData.element->editCopy(editData);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// editCut
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::editCut()
|
||
{
|
||
_score->startCmd();
|
||
if (editData.element)
|
||
editData.element->editCut(editData);
|
||
_score->endCmd();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// checkCopyOrCut
|
||
//---------------------------------------------------------
|
||
|
||
bool ScoreView::checkCopyOrCut()
|
||
{
|
||
if (!_score->selection().canCopy()) {
|
||
QMessageBox::information(0, "MuseScore",
|
||
tr("Please select the complete tuplet/tremolo and retry the command"),
|
||
QMessageBox::Ok, QMessageBox::NoButton);
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// normalCopy
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::normalCopy()
|
||
{
|
||
if (!checkCopyOrCut())
|
||
return;
|
||
QString mimeType = _score->selection().mimeType();
|
||
if (!mimeType.isEmpty()) {
|
||
QMimeData* mimeData = new QMimeData;
|
||
mimeData->setData(mimeType, _score->selection().mimeData());
|
||
if (MScore::debugMode)
|
||
qDebug("cmd copy: <%s>", mimeData->data(mimeType).data());
|
||
QApplication::clipboard()->setMimeData(mimeData);
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// normalCut
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::normalCut()
|
||
{
|
||
if (!checkCopyOrCut())
|
||
return;
|
||
_score->startCmd();
|
||
normalCopy();
|
||
_score->cmdDeleteSelection();
|
||
_score->endCmd();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// editSwap
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::editSwap()
|
||
{
|
||
#if 0 // TODO
|
||
if (editData.element && editData.element->isText() && !editData.element->isLyrics()) {
|
||
Text* text = toText(editData.element);
|
||
QString s = text->selectedText();
|
||
text->paste(this);
|
||
if (!s.isEmpty())
|
||
QApplication::clipboard()->setText(s, QClipboard::Clipboard);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// editPaste
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::editPaste()
|
||
{
|
||
if (editData.element) {
|
||
if (editData.element->isLyrics())
|
||
toLyrics(editData.element)->paste(editData);
|
||
else if (editData.element->isTextBase())
|
||
toTextBase(editData.element)->paste(editData);
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// normalSwap
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::normalSwap()
|
||
{
|
||
if (!checkCopyOrCut())
|
||
return;
|
||
QString mimeType = _score->selection().mimeType();
|
||
const QMimeData* ms = QApplication::clipboard()->mimeData();
|
||
if (mimeType == mimeStaffListFormat) { // determine size of clipboard selection
|
||
int tickLen = 0, staves = 0;
|
||
QByteArray d(ms->data(mimeStaffListFormat));
|
||
XmlReader e(d);
|
||
e.readNextStartElement();
|
||
if (e.name() == "StaffList") {
|
||
tickLen = e.intAttribute("len", 0);
|
||
staves = e.intAttribute("staves", 0);
|
||
}
|
||
if (tickLen > 0) { // attempt to extend selection to match clipboard size
|
||
Segment* seg = _score->selection().startSegment();
|
||
int tick = _score->selection().tickStart() + tickLen;
|
||
Segment* segAfter = _score->tick2leftSegment(tick);
|
||
int staffIdx = _score->selection().staffStart() + staves - 1;
|
||
if (staffIdx >= _score->nstaves())
|
||
staffIdx = _score->nstaves() - 1;
|
||
tick = _score->selection().tickStart();
|
||
int etick = tick + tickLen;
|
||
if (MScore::debugMode)
|
||
_score->selection().dump();
|
||
_score->selection().extendRangeSelection(seg, segAfter, staffIdx, tick, etick);
|
||
_score->selection().update();
|
||
if (MScore::debugMode)
|
||
_score->selection().dump();
|
||
if (!checkCopyOrCut())
|
||
return;
|
||
ms = QApplication::clipboard()->mimeData();
|
||
}
|
||
}
|
||
QByteArray d(_score->selection().mimeData());
|
||
if (this->normalPaste()) {
|
||
QMimeData* mimeData = new QMimeData;
|
||
mimeData->setData(mimeType, d);
|
||
QApplication::clipboard()->setMimeData(mimeData);
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// normalPaste
|
||
//---------------------------------------------------------
|
||
|
||
bool ScoreView::normalPaste()
|
||
{
|
||
_score->startCmd();
|
||
const QMimeData* ms = QApplication::clipboard()->mimeData();
|
||
_score->cmdPaste(ms, this);
|
||
bool rv = MScore::_error == MS_NO_ERROR;
|
||
_score->endCmd();
|
||
return rv;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmdGotoElement
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmdGotoElement(Element* e)
|
||
{
|
||
if (e) {
|
||
if (e->type() == ElementType::NOTE)
|
||
score()->setPlayNote(true);
|
||
score()->select(e, SelectType::SINGLE, 0);
|
||
if (e)
|
||
adjustCanvasPosition(e, false);
|
||
moveCursor();
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// ticksTab
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::ticksTab(int ticks)
|
||
{
|
||
if (editData.element->isHarmony())
|
||
harmonyTicksTab(ticks);
|
||
else if (editData.element->isFiguredBass())
|
||
figuredBassTicksTab(ticks);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmd
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmd(const QAction* a)
|
||
{
|
||
const char* s = a ? a->data().toByteArray().constData() : "";
|
||
cmd(s);
|
||
}
|
||
|
||
void ScoreView::cmd(const char* s)
|
||
{
|
||
const QByteArray cmd(s);
|
||
|
||
shadowNote->setVisible(false);
|
||
if (MScore::debugMode)
|
||
qDebug("ScoreView::cmd <%s>", s);
|
||
|
||
if (cmd == "escape")
|
||
escapeCmd();
|
||
else if (cmd == "note-input") {
|
||
if (state == ViewState::NORMAL)
|
||
changeState(ViewState::NOTE_ENTRY);
|
||
else if (state == ViewState::NOTE_ENTRY)
|
||
changeState(ViewState::NORMAL);
|
||
}
|
||
else if (cmd == "copy") {
|
||
if (fotoMode())
|
||
fotoModeCopy();
|
||
else if (state == ViewState::NORMAL)
|
||
normalCopy();
|
||
else if (state == ViewState::EDIT)
|
||
editCopy();
|
||
}
|
||
else if (cmd == "cut") {
|
||
if (state == ViewState::NORMAL)
|
||
normalCut();
|
||
else if (state == ViewState::EDIT)
|
||
editCut();
|
||
}
|
||
else if (cmd == "paste") {
|
||
if (state == ViewState::NORMAL)
|
||
normalPaste();
|
||
else if (state == ViewState::EDIT)
|
||
editPaste();
|
||
}
|
||
else if (cmd == "swap") {
|
||
if (state == ViewState::NORMAL)
|
||
normalSwap();
|
||
else if (state == ViewState::EDIT)
|
||
editSwap();
|
||
}
|
||
else if (cmd == "lyrics") {
|
||
_score->startCmd();
|
||
Lyrics* lyrics = _score->addLyrics();
|
||
_score->endCmd();
|
||
if (lyrics) {
|
||
startEditMode(lyrics);
|
||
return;
|
||
}
|
||
}
|
||
else if (cmd == "figured-bass") {
|
||
FiguredBass* fb = _score->addFiguredBass();
|
||
if (fb) {
|
||
startEditMode(fb);
|
||
return;
|
||
}
|
||
}
|
||
else if (cmd == "mag") {
|
||
// ??
|
||
}
|
||
else if (cmd == "play") {
|
||
if (seq && seq->canStart()) {
|
||
if (state == ViewState::NORMAL || state == ViewState::NOTE_ENTRY)
|
||
changeState(ViewState::PLAY);
|
||
else if (state == ViewState::PLAY)
|
||
changeState(ViewState::NORMAL);
|
||
}
|
||
else
|
||
getAction("play")->setChecked(false);
|
||
}
|
||
else if (cmd == "fotomode") {
|
||
if (state == ViewState::NORMAL)
|
||
changeState(ViewState::FOTO);
|
||
else if (fotoMode())
|
||
changeState(ViewState::NORMAL);
|
||
}
|
||
else if (cmd == "add-slur")
|
||
addSlur();
|
||
else if (cmd == "add-hairpin")
|
||
cmdAddHairpin(HairpinType::CRESC_HAIRPIN);
|
||
else if (cmd == "add-hairpin-reverse")
|
||
cmdAddHairpin(HairpinType::DECRESC_HAIRPIN);
|
||
else if (cmd == "add-noteline")
|
||
cmdAddNoteLine();
|
||
else if (cmd == "chord-text") {
|
||
changeState(ViewState::NORMAL);
|
||
cmdAddChordName();
|
||
}
|
||
else if (cmd == "title-text")
|
||
cmdAddText(Tid::TITLE);
|
||
else if (cmd == "subtitle-text")
|
||
cmdAddText(Tid::SUBTITLE);
|
||
else if (cmd == "composer-text")
|
||
cmdAddText(Tid::COMPOSER);
|
||
else if (cmd == "poet-text")
|
||
cmdAddText(Tid::POET);
|
||
else if (cmd == "part-text")
|
||
cmdAddText(Tid::INSTRUMENT_EXCERPT);
|
||
else if (cmd == "system-text")
|
||
cmdAddText(Tid::SYSTEM);
|
||
else if (cmd == "staff-text")
|
||
cmdAddText(Tid::STAFF);
|
||
else if (cmd == "expression-text")
|
||
cmdAddText(Tid::EXPRESSION);
|
||
else if (cmd == "rehearsalmark-text")
|
||
cmdAddText(Tid::REHEARSAL_MARK);
|
||
else if (cmd == "instrument-change-text")
|
||
cmdAddText(Tid::INSTRUMENT_CHANGE);
|
||
else if (cmd == "fingering-text")
|
||
cmdAddText(Tid::FINGERING);
|
||
|
||
else if (cmd == "edit-element") {
|
||
Element* e = _score->selection().element();
|
||
if (e && e->isEditable()) {
|
||
startEditMode(e);
|
||
}
|
||
}
|
||
else if (cmd == "select-similar") {
|
||
if (_score->selection().isSingle()) {
|
||
Element* e = _score->selection().element();
|
||
mscore->selectSimilar(e, false);
|
||
}
|
||
}
|
||
else if (cmd == "select-similar-staff") {
|
||
if (_score->selection().isSingle()) {
|
||
Element* e = _score->selection().element();
|
||
mscore->selectSimilar(e, true);
|
||
}
|
||
}
|
||
else if (cmd == "select-dialog") {
|
||
if (_score->selection().isSingle()) {
|
||
Element* e = _score->selection().element();
|
||
mscore->selectElementDialog(e);
|
||
}
|
||
}
|
||
// else if (cmd == "find")
|
||
// ; // TODO:state sm->postEvent(new CommandEvent(cmd));
|
||
else if (cmd == "page-prev")
|
||
pagePrev();
|
||
else if (cmd == "page-next")
|
||
pageNext();
|
||
else if (cmd == "page-top")
|
||
pageTop();
|
||
else if (cmd == "page-end")
|
||
pageEnd();
|
||
else if (cmd == "select-next-chord"
|
||
|| cmd == "select-prev-chord"
|
||
|| cmd == "select-next-measure"
|
||
|| cmd == "select-prev-measure"
|
||
|| cmd == "select-begin-line"
|
||
|| cmd == "select-end-line"
|
||
|| cmd == "select-begin-score"
|
||
|| cmd == "select-end-score"
|
||
|| cmd == "select-staff-above"
|
||
|| cmd == "select-staff-below") {
|
||
Element* el = _score->selectMove(cmd);
|
||
if (el)
|
||
adjustCanvasPosition(el, false);
|
||
updateAll();
|
||
}
|
||
else if (cmd == "next-chord"
|
||
|| cmd == "prev-chord"
|
||
|| cmd == "next-track"
|
||
|| cmd == "prev-track"
|
||
|| cmd == "next-measure"
|
||
|| cmd == "prev-measure") {
|
||
Element* el = score()->selection().element();
|
||
if (el && (el->isTextBase())) {
|
||
score()->startCmd();
|
||
const PropertyFlags pf = PropertyFlags::UNSTYLED;
|
||
if (cmd == "prev-chord")
|
||
el->undoChangeProperty(Pid::OFFSET, el->offset() - QPointF (MScore::nudgeStep * el->spatium(), 0.0), pf);
|
||
else if (cmd == "next-chord")
|
||
el->undoChangeProperty(Pid::OFFSET, el->offset() + QPointF (MScore::nudgeStep * el->spatium(), 0.0), pf);
|
||
else if (cmd == "prev-measure")
|
||
el->undoChangeProperty(Pid::OFFSET, el->offset() - QPointF (MScore::nudgeStep10 * el->spatium(), 0.0), pf);
|
||
else if (cmd == "next-measure")
|
||
el->undoChangeProperty(Pid::OFFSET, el->offset() + QPointF (MScore::nudgeStep10 * el->spatium(), 0.0), pf);
|
||
score()->endCmd();
|
||
}
|
||
else {
|
||
Element* ele = _score->move(cmd);
|
||
if (ele)
|
||
adjustCanvasPosition(ele, false);
|
||
updateAll();
|
||
}
|
||
}
|
||
else if (cmd == "pitch-up-diatonic")
|
||
score()->upDown(true, UpDownMode::DIATONIC);
|
||
else if (cmd == "pitch-down-diatonic")
|
||
score()->upDown(false, UpDownMode::DIATONIC);
|
||
else if (cmd == "move-up") {
|
||
QList<Element*> el = score()->selection().uniqueElements();
|
||
foreach (Element* e, el) {
|
||
ChordRest* cr = nullptr;
|
||
if (e->type() == ElementType::NOTE)
|
||
cr = static_cast<Note*>(e)->chord();
|
||
else if (e->type() == ElementType::REST)
|
||
cr = static_cast<Rest*>(e);
|
||
if (cr)
|
||
score()->moveUp(cr);
|
||
}
|
||
}
|
||
else if (cmd == "move-down") {
|
||
QList<Element*> el = score()->selection().uniqueElements();
|
||
foreach (Element* e, el) {
|
||
ChordRest* cr = nullptr;
|
||
if (e->type() == ElementType::NOTE)
|
||
cr = static_cast<Note*>(e)->chord();
|
||
else if (e->type() == ElementType::REST)
|
||
cr = static_cast<Rest*>(e);
|
||
if (cr)
|
||
score()->moveDown(cr);
|
||
}
|
||
}
|
||
else if (cmd == "up-chord") {
|
||
Element* el = score()->selection().element();
|
||
Element* oel = el;
|
||
if (el && (el->isNote() || el->isRest()))
|
||
cmdGotoElement(score()->upAlt(el));
|
||
el = score()->selection().element();
|
||
while (el && el->isRest() && toRest(el)->isGap()) {
|
||
if (score()->upAlt(el) == el) {
|
||
cmdGotoElement(oel);
|
||
break;
|
||
}
|
||
el = score()->upAlt(el);
|
||
cmdGotoElement(el);
|
||
}
|
||
}
|
||
else if (cmd == "down-chord") {
|
||
Element* el = score()->selection().element();
|
||
Element* oel = el;
|
||
if (el && (el->isNote() || el->isRest()))
|
||
cmdGotoElement(score()->downAlt(el));
|
||
el = score()->selection().element();
|
||
while (el && el->isRest() && toRest(el)->isGap()) {
|
||
if (score()->downAlt(el) == el) {
|
||
cmdGotoElement(oel);
|
||
break;
|
||
}
|
||
el = score()->downAlt(el);
|
||
cmdGotoElement(el);
|
||
}
|
||
}
|
||
else if (cmd == "top-chord" ) {
|
||
Element* el = score()->selection().element();
|
||
if (el && el->type() == ElementType::NOTE)
|
||
cmdGotoElement(score()->upAltCtrl(static_cast<Note*>(el)));
|
||
}
|
||
else if (cmd == "bottom-chord") {
|
||
Element* el = score()->selection().element();
|
||
if (el && el->type() == ElementType::NOTE)
|
||
cmdGotoElement(score()->downAltCtrl(static_cast<Note*>(el)));
|
||
}
|
||
else if (cmd == "next-segment-element") {
|
||
Element* el = score()->selection().element();
|
||
if (!el && !score()->selection().elements().isEmpty() )
|
||
el = score()->selection().elements().first();
|
||
|
||
if (el)
|
||
cmdGotoElement(el->nextSegmentElement());
|
||
else
|
||
cmdGotoElement(score()->firstElement());
|
||
}
|
||
else if (cmd == "prev-segment-element") {
|
||
Element* el = score()->selection().element();
|
||
if (!el && !score()->selection().elements().isEmpty())
|
||
el = score()->selection().elements().last();
|
||
|
||
if (el)
|
||
cmdGotoElement(el->prevSegmentElement());
|
||
else
|
||
cmdGotoElement(score()->lastElement());
|
||
}
|
||
else if (cmd == "next-element") {
|
||
Element* el = score()->selection().element();
|
||
if (!el && !score()->selection().elements().isEmpty() )
|
||
el = score()->selection().elements().first();
|
||
|
||
if (el)
|
||
cmdGotoElement(score()->nextElement());
|
||
else
|
||
cmdGotoElement(score()->firstElement());
|
||
}
|
||
else if (cmd == "prev-element") {
|
||
Element* el = score()->selection().element();
|
||
if (!el && !score()->selection().elements().isEmpty())
|
||
el = score()->selection().elements().last();
|
||
|
||
if (el)
|
||
cmdGotoElement(score()->prevElement());
|
||
else
|
||
cmdGotoElement(score()->lastElement());
|
||
}
|
||
else if (cmd == "first-element") {
|
||
cmdGotoElement(score()->firstElement());
|
||
}
|
||
else if (cmd == "last-element") {
|
||
cmdGotoElement(score()->lastElement());
|
||
}
|
||
else if (cmd == "rest" || cmd == "rest-TAB")
|
||
cmdEnterRest();
|
||
else if (cmd == "rest-1")
|
||
cmdEnterRest(TDuration(TDuration::DurationType::V_WHOLE));
|
||
else if (cmd == "rest-2")
|
||
cmdEnterRest(TDuration(TDuration::DurationType::V_HALF));
|
||
else if (cmd == "rest-4")
|
||
cmdEnterRest(TDuration(TDuration::DurationType::V_QUARTER));
|
||
else if (cmd == "rest-8")
|
||
cmdEnterRest(TDuration(TDuration::DurationType::V_EIGHTH));
|
||
else if (cmd.startsWith("interval")) {
|
||
int n = cmd.mid(8).toInt();
|
||
std::vector<Note*> nl = _score->selection().noteList();
|
||
if (!nl.empty()) {
|
||
//if (!noteEntryMode())
|
||
// ; // TODO: state sm->postEvent(new CommandEvent("note-input"));
|
||
_score->cmdAddInterval(n, nl);
|
||
}
|
||
}
|
||
else if (cmd == "tie") {
|
||
_score->cmdAddTie();
|
||
moveCursor();
|
||
}
|
||
else if (cmd == "chord-tie") {
|
||
_score->cmdAddTie(true);
|
||
moveCursor();
|
||
}
|
||
else if (cmd == "duplet")
|
||
cmdTuplet(2);
|
||
else if (cmd == "triplet")
|
||
cmdTuplet(3);
|
||
else if (cmd == "quadruplet")
|
||
cmdTuplet(4);
|
||
else if (cmd == "quintuplet")
|
||
cmdTuplet(5);
|
||
else if (cmd == "sextuplet")
|
||
cmdTuplet(6);
|
||
else if (cmd == "septuplet")
|
||
cmdTuplet(7);
|
||
else if (cmd == "octuplet")
|
||
cmdTuplet(8);
|
||
else if (cmd == "nonuplet")
|
||
cmdTuplet(9);
|
||
else if (cmd == "tuplet-dialog") {
|
||
_score->startCmd();
|
||
Tuplet* tuplet = mscore->tupletDialog();
|
||
if (tuplet)
|
||
cmdCreateTuplet(_score->getSelectedChordRest(), tuplet);
|
||
_score->endCmd();
|
||
moveCursor();
|
||
}
|
||
else if (cmd == "repeat-sel")
|
||
cmdRepeatSelection();
|
||
else if (cmd == "voice-1")
|
||
changeVoice(0);
|
||
else if (cmd == "voice-2")
|
||
changeVoice(1);
|
||
else if (cmd == "voice-3")
|
||
changeVoice(2);
|
||
else if (cmd == "voice-4")
|
||
changeVoice(3);
|
||
else if (cmd == "enh-both")
|
||
cmdChangeEnharmonic(true);
|
||
else if (cmd == "enh-current")
|
||
cmdChangeEnharmonic(false);
|
||
else if (cmd == "revision") {
|
||
Score* sc = _score->masterScore();
|
||
sc->createRevision();
|
||
}
|
||
else if (cmd == "append-measure")
|
||
cmdAppendMeasures(1, ElementType::MEASURE);
|
||
else if (cmd == "insert-measure")
|
||
cmdInsertMeasures(1, ElementType::MEASURE);
|
||
else if (cmd == "insert-hbox")
|
||
cmdInsertMeasures(1, ElementType::HBOX);
|
||
else if (cmd == "insert-vbox")
|
||
cmdInsertMeasures(1, ElementType::VBOX);
|
||
else if (cmd == "append-hbox") {
|
||
MeasureBase* mb = appendMeasure(ElementType::HBOX);
|
||
_score->select(mb, SelectType::SINGLE, 0);
|
||
}
|
||
else if (cmd == "append-vbox") {
|
||
MeasureBase* mb = appendMeasure(ElementType::VBOX);
|
||
_score->select(mb, SelectType::SINGLE, 0);
|
||
}
|
||
else if (cmd == "insert-textframe")
|
||
cmdInsertMeasure(ElementType::TBOX);
|
||
else if (cmd == "append-textframe") {
|
||
MeasureBase* mb = appendMeasure(ElementType::TBOX);
|
||
if (mb) {
|
||
TBox* tf = static_cast<TBox*>(mb);
|
||
Text* text = 0;
|
||
foreach(Element* e, tf->el()) {
|
||
if (e->type() == ElementType::TEXT) {
|
||
text = static_cast<Text*>(e);
|
||
break;
|
||
}
|
||
}
|
||
if (text) {
|
||
_score->select(text, SelectType::SINGLE, 0);
|
||
startEditMode(text);
|
||
}
|
||
}
|
||
}
|
||
else if (cmd == "insert-fretframe" && enableExperimental)
|
||
cmdInsertMeasure(ElementType::FBOX);
|
||
else if (cmd == "move-left")
|
||
cmdMoveCR(true);
|
||
else if (cmd == "move-right")
|
||
cmdMoveCR(false);
|
||
else if (cmd == "reset") {
|
||
if (editMode()) {
|
||
editData.element->reset();
|
||
updateGrips();
|
||
_score->update();
|
||
}
|
||
else {
|
||
_score->startCmd();
|
||
for (Element* e : _score->selection().elements()) {
|
||
e->reset();
|
||
if (e->isSpanner()) {
|
||
Spanner* sp = toSpanner(e);
|
||
for (SpannerSegment* ss : sp->spannerSegments())
|
||
ss->reset();
|
||
}
|
||
}
|
||
_score->endCmd();
|
||
}
|
||
}
|
||
#ifdef OMR
|
||
else if (cmd == "show-omr") {
|
||
if (_score->masterScore()->omr())
|
||
showOmr(!_score->masterScore()->showOmr());
|
||
}
|
||
#endif
|
||
else if (cmd == "split-measure") {
|
||
Element* e = _score->selection().element();
|
||
if (!(e && (e->isNote() || e->isRest())))
|
||
MScore::setError(NO_CHORD_REST_SELECTED);
|
||
else {
|
||
if (e->isNote())
|
||
e = toNote(e)->chord();
|
||
ChordRest* cr = toChordRest(e);
|
||
_score->cmdSplitMeasure(cr);
|
||
}
|
||
}
|
||
else if (cmd == "join-measures") {
|
||
Measure* m1;
|
||
Measure* m2;
|
||
if (!_score->selection().measureRange(&m1, &m2) || m1 == m2) {
|
||
QMessageBox::warning(0, "MuseScore",
|
||
tr("No measures selected:\n"
|
||
"Please select a range of measures to join and try again"));
|
||
}
|
||
else {
|
||
_score->cmdJoinMeasure(m1, m2);
|
||
}
|
||
}
|
||
else if (cmd == "next-lyric" || cmd == "prev-lyric")
|
||
editCmd(cmd);
|
||
else if (cmd == "add-remove-breaks")
|
||
cmdAddRemoveBreaks();
|
||
else if (cmd == "copy-lyrics-to-clipboard")
|
||
cmdCopyLyricsToClipboard();
|
||
|
||
// STATE_NOTE_ENTRY_REALTIME actions (auto or manual)
|
||
|
||
else if (cmd == "realtime-advance")
|
||
realtimeAdvance(true);
|
||
|
||
// STATE_HARMONY_FIGBASS_EDIT actions
|
||
|
||
else if (cmd == "advance-longa")
|
||
ticksTab(MScore::division << 4);
|
||
else if (cmd == "advance-breve")
|
||
ticksTab(MScore::division << 3);
|
||
else if (cmd == "advance-1")
|
||
ticksTab(MScore::division << 2);
|
||
else if (cmd == "advance-2")
|
||
ticksTab(MScore::division << 1);
|
||
else if (cmd == "advance-4")
|
||
ticksTab(MScore::division);
|
||
else if (cmd == "advance-8")
|
||
ticksTab(MScore::division >> 1);
|
||
else if (cmd == "advance-16")
|
||
ticksTab(MScore::division >> 2);
|
||
else if (cmd == "advance-32")
|
||
ticksTab(MScore::division >> 3);
|
||
else if (cmd == "advance-64")
|
||
ticksTab(MScore::division >> 4);
|
||
else if (cmd == "prev-measure-TEXT") {
|
||
if (editData.element->isHarmony())
|
||
harmonyTab(true);
|
||
else if (editData.element->isFiguredBass())
|
||
figuredBassTab(true, true);
|
||
}
|
||
else if (cmd == "next-measure-TEXT") {
|
||
if (editData.element->isHarmony())
|
||
harmonyTab(false);
|
||
else if (editData.element->isFiguredBass())
|
||
figuredBassTab(true, false);
|
||
}
|
||
else if (cmd == "prev-beat-TEXT") {
|
||
if (editData.element->isHarmony())
|
||
harmonyBeatsTab(false, true);
|
||
}
|
||
else if (cmd == "next-beat-TEXT") {
|
||
if (editData.element->isHarmony())
|
||
harmonyBeatsTab(false, false);
|
||
}
|
||
|
||
// STATE_NOTE_ENTRY_TAB actions
|
||
|
||
// move input state string up or down, within the number of strings of the instrument;
|
||
// this may move the input state cursor outside of the tab line range to accommodate
|
||
// instrument strings not represented in the tab (e.g.: lute bass strings):
|
||
// the appropriate visual rendition of the input cursor in those cases will be managed by moveCursor()
|
||
else if(cmd == "string-above" || cmd == "string-below") {
|
||
InputState& is = _score->inputState();
|
||
Staff* staff = _score->staff(is.track() / VOICES);
|
||
int instrStrgs = staff->part()->instrument()->stringData()->strings();
|
||
// assume "string-below": if tab is upside-down, 'below' means toward instrument top (-1)
|
||
// if not, 'below' means toward instrument bottom (+1)
|
||
int delta = (staff->staffType(is.tick())->upsideDown() ? -1 : +1);
|
||
if (cmd == "string-above") // if "above", reverse delta
|
||
delta = -delta;
|
||
int strg = is.string() + delta; // dest. physical string
|
||
if (strg >= 0 && strg < instrStrgs) { // if dest. string within instrument limits
|
||
is.setString(strg); // update status
|
||
moveCursor();
|
||
}
|
||
}
|
||
else if (cmd == "text-word-left")
|
||
toTextBase(editData.element)->movePosition(editData, QTextCursor::WordLeft);
|
||
else if (cmd == "text-word-right")
|
||
toTextBase(editData.element)->movePosition(editData, QTextCursor::NextWord);
|
||
else if (cmd == "concert-pitch") {
|
||
QAction* a = getAction(cmd);
|
||
if (_score->styleB(Sid::concertPitch) != a->isChecked()) {
|
||
_score->startCmd();
|
||
_score->cmdConcertPitchChanged(a->isChecked(), true);
|
||
_score->endCmd();
|
||
}
|
||
}
|
||
else {
|
||
editData.view = this;
|
||
QAction* a = getAction(cmd);
|
||
_score->cmd(a, editData);
|
||
}
|
||
if (_score->processMidiInput())
|
||
mscore->endCmd();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// showOmr
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::showOmr(bool flag)
|
||
{
|
||
_score->masterScore()->setShowOmr(flag);
|
||
ScoreTab* t = mscore->getTab1();
|
||
if (t->view() != this)
|
||
t = mscore->getTab2();
|
||
if (t->view() == this)
|
||
t->setCurrentIndex(t->currentIndex());
|
||
else
|
||
qDebug("view not found");
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// startNoteEntry
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::startNoteEntry()
|
||
{
|
||
InputState& is = _score->inputState();
|
||
|
||
is.setSegment(0);
|
||
Note* note = 0;
|
||
|
||
if (_score->selection().isNone()) {
|
||
// no selection
|
||
// choose page in current view (favor top left quadrant if possible)
|
||
// select first (top/left) chordrest of that page in current view
|
||
Page* p = nullptr;
|
||
QList<QPointF> points;
|
||
points.append(toLogical(QPoint(width() * 0.25, height() * 0.25)));
|
||
points.append(toLogical(QPoint(0.0, 0.0)));
|
||
points.append(toLogical(QPoint(0.0, height())));
|
||
points.append(toLogical(QPoint(width(), 0.0)));
|
||
points.append(toLogical(QPoint(width(), height())));
|
||
int i = 0;
|
||
while (!p && i < points.size()) {
|
||
p = point2page(points[i]);
|
||
i++;
|
||
}
|
||
if (p) {
|
||
ChordRest* topLeft = nullptr;
|
||
qreal tlY = 0.0;
|
||
int tlTick = 0;
|
||
QRectF viewRect = toLogical(QRectF(0.0, 0.0, width(), height()));
|
||
QRectF pageRect = p->bbox().translated(p->x(), p->y());
|
||
QRectF intersect = viewRect & pageRect;
|
||
intersect.translate(-p->x(), -p->y());
|
||
QList<Element*> el = p->items(intersect);
|
||
for (Element* e : el) {
|
||
// loop through visible elements
|
||
// looking for the CR in voice 1 with earliest tick and highest staff position
|
||
ElementType et = e->type();
|
||
if (et == ElementType::NOTE || et == ElementType::REST) {
|
||
if (e->voice())
|
||
continue;
|
||
ChordRest* cr;
|
||
if (et == ElementType::NOTE) {
|
||
cr = static_cast<ChordRest*>(e->parent());
|
||
if (!cr)
|
||
continue;
|
||
}
|
||
else {
|
||
cr = static_cast<ChordRest*>(e);
|
||
}
|
||
// compare ticks rather than x position
|
||
// to make sure we favor earlier rather than later systems
|
||
// even though later system might have note farther to left
|
||
int crTick = 0;
|
||
if (cr->segment())
|
||
crTick = cr->segment()->tick();
|
||
else
|
||
continue;
|
||
// compare staff Y position rather than note Y position
|
||
// to be sure we do not reject earliest note
|
||
// just because it is lower in pitch than subsequent notes
|
||
qreal crY = 0.0;
|
||
if (cr->measure() && cr->measure()->system())
|
||
crY = cr->measure()->system()->staffYpage(cr->staffIdx());
|
||
else
|
||
continue;
|
||
if (topLeft) {
|
||
if (crTick <= tlTick && crY <= tlY) {
|
||
topLeft = cr;
|
||
tlTick = crTick;
|
||
tlY = crY;
|
||
}
|
||
}
|
||
else {
|
||
topLeft = cr;
|
||
tlTick = crTick;
|
||
tlY = crY;
|
||
}
|
||
}
|
||
}
|
||
if (topLeft)
|
||
_score->select(topLeft, SelectType::SINGLE);
|
||
}
|
||
}
|
||
|
||
Element* el = _score->selection().activeCR() ? _score->selection().activeCR() : _score->selection().element();
|
||
if (!el)
|
||
el = _score->selection().firstChordRest();
|
||
if (el == 0 || (el->type() != ElementType::CHORD && el->type() != ElementType::REST && el->type() != ElementType::NOTE)) {
|
||
// if no note/rest is selected, start with voice 0
|
||
int track = is.track() == -1 ? 0 : (is.track() / VOICES) * VOICES;
|
||
// try to find an appropriate measure to start in
|
||
while (el && el->type() != ElementType::MEASURE)
|
||
el = el->parent();
|
||
int tick = el ? static_cast<Measure*>(el)->tick() : 0;
|
||
el = _score->searchNote(tick, track);
|
||
if (!el)
|
||
el = _score->searchNote(0, track);
|
||
}
|
||
if (!el)
|
||
return;
|
||
if (el->type() == ElementType::CHORD) {
|
||
Chord* c = static_cast<Chord*>(el);
|
||
note = c->selectedNote();
|
||
if (note == 0)
|
||
note = c->upNote();
|
||
el = note;
|
||
}
|
||
TDuration d(is.duration());
|
||
if (!d.isValid() || d.isZero() || d.type() == TDuration::DurationType::V_MEASURE)
|
||
is.setDuration(TDuration(TDuration::DurationType::V_QUARTER));
|
||
|
||
_score->select(el, SelectType::SINGLE, 0);
|
||
is.update(el);
|
||
is.setRest(false);
|
||
is.setNoteEntryMode(true);
|
||
adjustCanvasPosition(el, false);
|
||
|
||
getAction("pad-rest")->setChecked(false);
|
||
setMouseTracking(true);
|
||
shadowNote->setVisible(true);
|
||
_score->setUpdateAll();
|
||
_score->update();
|
||
|
||
Staff* staff = _score->staff(is.track() / VOICES);
|
||
switch (staff->staffType(is.tick())->group()) {
|
||
case StaffGroup::STANDARD:
|
||
break;
|
||
case StaffGroup::TAB: {
|
||
int strg = 0; // assume topmost string as current string
|
||
// if entering note entry with a note selected and the note has a string
|
||
// set InputState::_string to note physical string
|
||
if (el->type() == ElementType::NOTE) {
|
||
strg = (static_cast<Note*>(el))->string();
|
||
}
|
||
is.setString(strg);
|
||
break;
|
||
}
|
||
case StaffGroup::PERCUSSION:
|
||
break;
|
||
}
|
||
// set cursor after setting the stafftype-dependent state
|
||
moveCursor();
|
||
mscore->updateInputState(_score);
|
||
shadowNote->setVisible(false);
|
||
setCursorOn(true);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// endNoteEntry
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::endNoteEntry()
|
||
{
|
||
InputState& is = _score->inputState();
|
||
is.setNoteEntryMode(false);
|
||
if (is.slur()) {
|
||
const std::vector<SpannerSegment*>& el = is.slur()->spannerSegments();
|
||
if (!el.empty())
|
||
el.front()->setSelected(false);
|
||
is.setSlur(0);
|
||
}
|
||
setMouseTracking(false);
|
||
shadowNote->setVisible(false);
|
||
setCursorOn(false);
|
||
_score->setUpdateAll();
|
||
_score->update();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// dragScoreView
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::dragScoreView(QMouseEvent* ev)
|
||
{
|
||
QPoint d = ev->pos() - _matrix.map(editData.startMove).toPoint();
|
||
int dx = d.x();
|
||
int dy = d.y();
|
||
|
||
if (dx == 0 && dy == 0)
|
||
return;
|
||
|
||
constraintCanvas(&dx, &dy);
|
||
|
||
TourHandler::startTour("navigate-tour");
|
||
|
||
_matrix.setMatrix(_matrix.m11(), _matrix.m12(), _matrix.m13(), _matrix.m21(),
|
||
_matrix.m22(), _matrix.m23(), _matrix.dx()+dx, _matrix.dy()+dy, _matrix.m33());
|
||
imatrix = _matrix.inverted();
|
||
scroll(dx, dy, QRect(0, 0, width(), height()));
|
||
// scroll schedules an update which is probably too small
|
||
// hack around:
|
||
if (dx > 0)
|
||
update(-10, 0, dx + 50, height());
|
||
else if (dx < 0)
|
||
update(width() - 50 + dx, 0, width() + 10, height());
|
||
emit offsetChanged(_matrix.dx(), _matrix.dy());
|
||
emit viewRectChanged();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// doDragLasso
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::doDragLasso(QMouseEvent* ev)
|
||
{
|
||
TourHandler::startTour("select-tour");
|
||
QPointF p = toLogical(ev->pos());
|
||
_score->addRefresh(lasso->canvasBoundingRect());
|
||
QRectF r;
|
||
r.setCoords(editData.startMove.x(), editData.startMove.y(), p.x(), p.y());
|
||
lasso->setbbox(r.normalized());
|
||
QRectF _lassoRect(lasso->bbox());
|
||
r = _matrix.mapRect(_lassoRect);
|
||
QSize sz(r.size().toSize());
|
||
mscore->statusBar()->showMessage(QString("%1 x %2").arg(sz.width()).arg(sz.height()), 3000);
|
||
_score->addRefresh(lasso->canvasBoundingRect());
|
||
_score->lassoSelect(lasso->bbox());
|
||
_score->update();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// endLasso
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::endLasso()
|
||
{
|
||
_score->addRefresh(lasso->canvasBoundingRect());
|
||
lasso->setbbox(QRectF());
|
||
_score->lassoSelectEnd();
|
||
_score->update();
|
||
mscore->endCmd();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// deselectAll
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::deselectAll()
|
||
{
|
||
_score->deselectAll();
|
||
_score->update();
|
||
mscore->endCmd();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// inputMethodQuery
|
||
//---------------------------------------------------------
|
||
|
||
QVariant ScoreView::inputMethodQuery(Qt::InputMethodQuery query) const
|
||
{
|
||
// qDebug("0x%x %s", int(query), editData.element ? editData.element->name() : "-no element-");
|
||
if (editData.element && editData.element->isTextBase()) {
|
||
TextBase* text = toTextBase(editData.element);
|
||
switch (query) {
|
||
case Qt::ImCursorRectangle: {
|
||
QRectF r;
|
||
if (editMode()) {
|
||
TextCursor* cursor = text->cursor(editData);
|
||
r = toPhysical(cursor->cursorRect().translated(text->canvasPos()));
|
||
r.setWidth(1); // InputMethod doesn't display properly if width left at 0
|
||
}
|
||
else
|
||
r = toPhysical(text->canvasBoundingRect());
|
||
r.setHeight(r.height() + 10); // add a little margin under the cursor
|
||
qDebug("ScoreView::inputMethodQuery() updating cursorRect to: (%3f, %3f) + (%3f, %3f)", r.x(), r.y(), r.width(), r.height());
|
||
return QVariant(r);
|
||
}
|
||
case Qt::ImEnabled:
|
||
return true; // TextBase will always accept input method input
|
||
case Qt::ImHints:
|
||
return Qt::ImhNone; // No hints for now, but maybe in future will give hints
|
||
case Qt::ImInputItemClipRectangle: // maybe give clip rect hint in future, but for now use default parent clipping rect
|
||
default:
|
||
return QWidget::inputMethodQuery(query); // fall back to QWidget's version as default
|
||
}
|
||
}
|
||
QVariant d = QWidget::inputMethodQuery(query); // fall back to QWidget's version as default
|
||
// qDebug() << " " << d;
|
||
return d;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// lmag
|
||
//---------------------------------------------------------
|
||
|
||
qreal ScoreView::lmag() const
|
||
{
|
||
return _matrix.m11() / (mscore->physicalDotsPerInch() / DPI);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// mag
|
||
//---------------------------------------------------------
|
||
|
||
qreal ScoreView::mag() const
|
||
{
|
||
return _matrix.m11();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// setOffset
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::setOffset(qreal x, qreal y)
|
||
{
|
||
_matrix.setMatrix(_matrix.m11(), _matrix.m12(), _matrix.m13(), _matrix.m21(),
|
||
_matrix.m22(), _matrix.m23(), x, y, _matrix.m33());
|
||
imatrix = _matrix.inverted();
|
||
emit viewRectChanged();
|
||
emit offsetChanged(x, y);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// xoffset
|
||
//---------------------------------------------------------
|
||
|
||
qreal ScoreView::xoffset() const
|
||
{
|
||
return _matrix.dx();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// yoffset
|
||
//---------------------------------------------------------
|
||
|
||
qreal ScoreView::yoffset() const
|
||
{
|
||
return _matrix.dy();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// fsize
|
||
//---------------------------------------------------------
|
||
|
||
QSizeF ScoreView::fsize() const
|
||
{
|
||
QSize s = size();
|
||
return QSizeF(s.width() * imatrix.m11(), s.height() * imatrix.m22());
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// pageNext
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::pageNext()
|
||
{
|
||
if (score()->pages().empty())
|
||
return;
|
||
if (score()->layoutMode() == LayoutMode::LINE) {
|
||
qreal x = xoffset() - width() * .8;
|
||
MeasureBase* lm = score()->last();
|
||
qreal lx = (lm->pos().x() + lm->width()) * mag() - width() * .8;
|
||
if (x < -lx)
|
||
x = -lx;
|
||
setOffset(x, yoffset());
|
||
}
|
||
else {
|
||
Page* page = score()->pages().back();
|
||
qreal x, y;
|
||
if (MScore::verticalOrientation()) {
|
||
x = 10.0;
|
||
y = yoffset() - (page->height() + 25.0) * mag();
|
||
qreal ly = 10.0 - page->pos().y() * mag();
|
||
if (y < ly)
|
||
y = ly;
|
||
}
|
||
else {
|
||
y = 10.0;
|
||
x = xoffset() - (page->width() + 25.0) * mag();
|
||
qreal lx = 10.0 - page->pos().x() * mag();
|
||
if (x < lx)
|
||
x = lx;
|
||
}
|
||
setOffset(x, y);
|
||
}
|
||
update();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// pagePrev
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::pagePrev()
|
||
{
|
||
if (score()->pages().empty())
|
||
return;
|
||
if (score()->layoutMode() == LayoutMode::LINE) {
|
||
qreal x = xoffset() + width() * .8;
|
||
if (x > 10.0)
|
||
x = 10.0;
|
||
setOffset(x, yoffset());
|
||
}
|
||
else {
|
||
Page* page = score()->pages().front();
|
||
qreal x, y;
|
||
if (MScore::verticalOrientation()) {
|
||
x = 10.0;
|
||
y = yoffset() + (page->height() + 25.0) * mag();
|
||
if (y > 10.0)
|
||
y = 10.0;
|
||
}
|
||
else {
|
||
y = 10.0;
|
||
x = xoffset() + (page->width() + 25.0) * mag();
|
||
if (x > 10.0)
|
||
x = 10.0;
|
||
}
|
||
setOffset(x, y);
|
||
}
|
||
update();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// pageTop
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::pageTop()
|
||
{
|
||
if (score()->layoutMode() == LayoutMode::LINE)
|
||
setOffset(10.0, 0.0);
|
||
else
|
||
setOffset(10.0, 10.0);
|
||
update();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// pageEnd
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::pageEnd()
|
||
{
|
||
if (score()->pages().empty())
|
||
return;
|
||
if (score()->layoutMode() == LayoutMode::LINE) {
|
||
MeasureBase* lm = score()->last();
|
||
qreal lx = (lm->canvasPos().x() + lm->width()) * mag();
|
||
lx -= width() * .8;
|
||
setOffset(-lx, yoffset());
|
||
}
|
||
else {
|
||
Page* lastPage = score()->pages().back();
|
||
QPointF p(lastPage->pos());
|
||
if (MScore::verticalOrientation()) {
|
||
setOffset(25.0, 25 - p.y() * mag());
|
||
}
|
||
else {
|
||
setOffset(25.0 - p.x() * mag(), 25.0);
|
||
}
|
||
}
|
||
update();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// adjustCanvasPosition
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::adjustCanvasPosition(const Element* el, bool playBack, int staff )
|
||
{
|
||
if (this != mscore->currentScoreView() && !_moveWhenInactive)
|
||
return;
|
||
// TODO: change icon, or add panning options
|
||
if (!mscore->panDuringPlayback())
|
||
return;
|
||
|
||
if (score()->layoutMode() == LayoutMode::LINE) {
|
||
|
||
if (!el)
|
||
return;
|
||
|
||
/* Not used, because impossible to get panel width beforehand
|
||
const MeasureBase* m = 0;
|
||
if (el->type() == ElementType::MEASURE)
|
||
m = static_cast<const MeasureBase*>(el);
|
||
else
|
||
m = static_cast<const Measure*>(el->parent()->findMeasure());
|
||
*/
|
||
|
||
qreal xo = 0.0; // new x offset
|
||
QRectF curPos = playBack ? _cursor->rect() : el->canvasBoundingRect();
|
||
// keep current note in view as well if applicable (note input mode)
|
||
Element* current = nullptr;
|
||
if (noteEntryMode()) {
|
||
current = score()->selection().cr();
|
||
if (current && current != el)
|
||
curPos |= current->canvasBoundingRect();
|
||
}
|
||
|
||
qreal curPosR = curPos.right(); // Position on the canvas
|
||
qreal curPosL = curPos.left(); // Position on the canvas
|
||
qreal curPosMagR = curPosR * mag() + xoffset(); // Position in the screen
|
||
qreal curPosMagL = curPosL * mag() + xoffset(); // Position in the screen
|
||
qreal marginLeft = width() * 0.05;
|
||
qreal marginRight = width() * 0.05; // leaves 5% margin to the right
|
||
|
||
if (_continuousPanel->active())
|
||
marginLeft += _continuousPanel->width() * mag();
|
||
|
||
// this code implements "continuous" panning
|
||
// it could potentially be enabled via more panning options
|
||
//if (playBack && _cursor) {
|
||
// // keep playback cursor pinned at 25%
|
||
// xo = -curPosL * mag() + marginLeft + width() * 0.2;
|
||
// }
|
||
//else
|
||
if (round(curPosMagR) > round(width() - marginRight)) {
|
||
// focus in or beyond right margin
|
||
// pan to left margin in playback,
|
||
// most of the way left in note entry,
|
||
// otherwise just enforce right margin
|
||
if (playBack)
|
||
xo = -curPosL * mag() + marginLeft;
|
||
else if (noteEntryMode())
|
||
xo = -curPosL * mag() + marginLeft + width() * 0.2;
|
||
else
|
||
xo = -curPosR * mag() + width() - marginRight;
|
||
}
|
||
else if (round(curPosMagL) < round(marginLeft) ) {
|
||
// focus in or beyond left margin
|
||
// enforce left margin
|
||
// (previously we moved canvas all the way right,
|
||
// but this made sense only when navigating right-to-left)
|
||
xo = -curPosL * mag() + marginLeft;
|
||
}
|
||
else {
|
||
// focus is within margins, so do nothing
|
||
return;
|
||
}
|
||
// avoid empty space on either side of "page"
|
||
qreal scoreEnd = score()->pages().front()->width() * mag() + xo;
|
||
if (xo > 10)
|
||
xo = 10;
|
||
else if (scoreEnd < width())
|
||
xo += width() - scoreEnd;
|
||
|
||
setOffset(xo, yoffset());
|
||
update();
|
||
return;
|
||
}
|
||
|
||
const MeasureBase* m;
|
||
if (!el)
|
||
return;
|
||
else if (el->type() == ElementType::NOTE)
|
||
m = static_cast<const Note*>(el)->chord()->measure();
|
||
else if (el->type() == ElementType::REST)
|
||
m = static_cast<const Rest*>(el)->measure();
|
||
else if (el->type() == ElementType::CHORD)
|
||
m = static_cast<const Chord*>(el)->measure();
|
||
else if (el->type() == ElementType::SEGMENT)
|
||
m = static_cast<const Segment*>(el)->measure();
|
||
else if (el->type() == ElementType::LYRICS)
|
||
m = static_cast<const Lyrics*>(el)->measure();
|
||
else if ( (el->type() == ElementType::HARMONY || el->type() == ElementType::FIGURED_BASS)
|
||
&& el->parent()->type() == ElementType::SEGMENT)
|
||
m = static_cast<const Segment*>(el->parent())->measure();
|
||
else if (el->type() == ElementType::HARMONY && el->parent()->type() == ElementType::FRET_DIAGRAM
|
||
&& el->parent()->parent()->type() == ElementType::SEGMENT)
|
||
m = static_cast<const Segment*>(el->parent()->parent())->measure();
|
||
else if (el->type() == ElementType::MEASURE || el->type() == ElementType::VBOX)
|
||
m = static_cast<const MeasureBase*>(el);
|
||
else if (el->isSpannerSegment()) {
|
||
Element* se = static_cast<const SpannerSegment*>(el)->spanner()->startElement();
|
||
m = static_cast<Measure*>(se->findMeasure());
|
||
}
|
||
else if (el->isSpanner()) {
|
||
Element* se = static_cast<const Spanner*>(el)->startElement();
|
||
m = static_cast<Measure*>(se->findMeasure());
|
||
}
|
||
else {
|
||
// attempt to find measure
|
||
Element* e = el->parent();
|
||
while (e && e->type() != ElementType::MEASURE)
|
||
e = e->parent();
|
||
if (e)
|
||
m = static_cast<Measure*>(e);
|
||
else
|
||
return;
|
||
}
|
||
if (!m)
|
||
return;
|
||
|
||
int staffIdx = el->staffIdx();
|
||
System* sys = m->system();
|
||
if (!sys)
|
||
return;
|
||
|
||
QPointF p(el->canvasPos());
|
||
QRectF r(imatrix.mapRect(geometry()));
|
||
QRectF mRect(m->canvasBoundingRect());
|
||
QRectF sysRect;
|
||
if (staffIdx == -1)
|
||
sysRect = sys->canvasBoundingRect();
|
||
else
|
||
sysRect = sys->staff(staffIdx)->bbox();
|
||
|
||
// only try to track measure if not during playback
|
||
if (!playBack)
|
||
sysRect = mRect;
|
||
|
||
double _spatium = score()->spatium();
|
||
const qreal border = _spatium * 3;
|
||
QRectF showRect;
|
||
if (staff == -1) {
|
||
showRect = QRectF(mRect.x(), sysRect.y(), mRect.width(), sysRect.height())
|
||
.adjusted(-border, -border, border, border);
|
||
}
|
||
else {
|
||
// find a box for the individual stave in a system
|
||
QRectF stave = QRectF(sys->canvasBoundingRect().left(),
|
||
sys->staffCanvasYpage(staff),
|
||
sys->width(),
|
||
sys->staff(staff)->bbox().height());
|
||
showRect = mRect.intersected(stave).adjusted(-border, -border, border, border);
|
||
}
|
||
|
||
/*printf("%f %f %f %f %f %f %f %f %d\n",
|
||
showRect.x(), showRect.y(), showRect.width(), showRect.height(),
|
||
r.x(), r.y(), r.width(), r.height(),
|
||
r.contains(showRect)
|
||
);
|
||
*/
|
||
// canvas is not as wide as measure, track note instead
|
||
if (r.width() < showRect.width()) {
|
||
showRect.setX(p.x());
|
||
showRect.setWidth(el->width());
|
||
}
|
||
|
||
// canvas is not as tall as system
|
||
if (r.height() < showRect.height()) {
|
||
if (sys->staves()->size() == 1 || !playBack) {
|
||
// track note if single staff
|
||
showRect.setY(p.y());
|
||
showRect.setHeight(el->height());
|
||
}
|
||
else if (sys->page()->systems().size() == 1) {
|
||
// otherwise, just keep current vertical position if possible
|
||
// see issue #7724
|
||
showRect.setY(r.y());
|
||
showRect.setHeight(r.height());
|
||
}
|
||
}
|
||
if (shadowNote->visible())
|
||
setShadowNote(p);
|
||
|
||
if (r.contains(showRect))
|
||
return;
|
||
|
||
qreal x = - xoffset() / mag();
|
||
qreal y = - yoffset() / mag();
|
||
|
||
qreal oldX = x, oldY = y;
|
||
|
||
if (showRect.left() < r.left())
|
||
x = showRect.left() - border;
|
||
else if (showRect.left() > r.right())
|
||
x = showRect.right() - width() / mag() + border;
|
||
else if (r.width() >= showRect.width() && showRect.right() > r.right())
|
||
x = showRect.left() - border;
|
||
if (showRect.top() < r.top() && showRect.bottom() < r.bottom())
|
||
y = showRect.top() - border;
|
||
else if (showRect.top() > r.bottom())
|
||
y = showRect.bottom() - height() / mag() + border;
|
||
else if (r.height() >= showRect.height() && showRect.bottom() > r.bottom())
|
||
y = showRect.top() - border;
|
||
|
||
// align to page borders if extends beyond
|
||
Page* page = sys->page();
|
||
if (x < page->x() || r.width() >= page->width())
|
||
x = page->x();
|
||
else if (r.width() < page->width() && r.width() + x > page->width() + page->x())
|
||
x = (page->width() + page->x()) - r.width();
|
||
if (y < page->y() || r.height() >= page->height())
|
||
y = page->y();
|
||
else if (r.height() < page->height() && r.height() + y > page->height() + page->y())
|
||
y = (page->height() + page->y()) - r.height();
|
||
|
||
// hack: don't update if we haven't changed the offset
|
||
if (oldX == x && oldY == y)
|
||
return;
|
||
|
||
setOffset(-x * mag(), -y * mag());
|
||
update();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmdEnterRest
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmdEnterRest()
|
||
{
|
||
const InputState& is = _score->inputState();
|
||
if (is.track() == -1 || is.segment() == 0) // invalid state
|
||
return;
|
||
cmdEnterRest(is.duration());
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmdEnterRest
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmdEnterRest(const TDuration& d)
|
||
{
|
||
if (!noteEntryMode())
|
||
cmd("note-input");
|
||
if (_score->usingNoteEntryMethod(NoteEntryMethod::RHYTHM))
|
||
_score->cmd(getAction("pad-rest"), editData);
|
||
else
|
||
_score->cmdEnterRest(d);
|
||
#if 0
|
||
expandVoice();
|
||
if (_is.cr() == 0) {
|
||
qDebug("cannot enter rest here");
|
||
return;
|
||
}
|
||
|
||
int track = _is.track;
|
||
Segment* seg = setNoteRest(_is.cr(), track, -1, d.fraction(), 0, AUTO);
|
||
ChordRest* cr = static_cast<ChordRest*>(seg->element(track));
|
||
if (cr)
|
||
nextInputPos(cr, false);
|
||
_is.rest = false; // continue with normal note entry
|
||
#endif
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// mscoreState
|
||
//---------------------------------------------------------
|
||
|
||
ScoreState ScoreView::mscoreState() const
|
||
{
|
||
if (fotoMode())
|
||
return STATE_FOTO;
|
||
if (state == ViewState::NOTE_ENTRY) {
|
||
const InputState is = _score->inputState();
|
||
Staff* staff = _score->staff(is.track() / VOICES);
|
||
switch( staff->staffType(is.tick())->group()) {
|
||
case StaffGroup::STANDARD:
|
||
return STATE_NOTE_ENTRY_STAFF_PITCHED;
|
||
case StaffGroup::TAB:
|
||
return STATE_NOTE_ENTRY_STAFF_TAB;
|
||
case StaffGroup::PERCUSSION:
|
||
return STATE_NOTE_ENTRY_STAFF_DRUM;
|
||
}
|
||
}
|
||
if (state == ViewState::EDIT || state == ViewState::DRAG_EDIT) {
|
||
if (editData.element && editData.element->isLyrics())
|
||
return STATE_LYRICS_EDIT;
|
||
else if (editData.element &&
|
||
( (editData.element->isHarmony()) || editData.element->isFiguredBass()) )
|
||
return STATE_HARMONY_FIGBASS_EDIT;
|
||
else if (editData.element && editData.element->isTextBase())
|
||
return STATE_TEXT_EDIT;
|
||
return STATE_EDIT;
|
||
}
|
||
if (state == ViewState::PLAY)
|
||
return STATE_PLAY;
|
||
return STATE_NORMAL;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// startUndoRedo
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::startUndoRedo(bool undo)
|
||
{
|
||
_score->undoRedo(undo, state == ViewState::EDIT ? &editData : 0);
|
||
|
||
if (_score->inputState().segment())
|
||
mscore->setPos(_score->inputState().tick());
|
||
if (_score->noteEntryMode() && !noteEntryMode())
|
||
cmd("note-input"); // enter note entry mode
|
||
else if (!_score->noteEntryMode() && noteEntryMode())
|
||
cmd("escape"); // leave note entry mode
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// addSlur
|
||
// command invoked, or icon double clicked
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::addSlur()
|
||
{
|
||
InputState& is = _score->inputState();
|
||
if (noteEntryMode() && is.slur()) {
|
||
const std::vector<SpannerSegment*>& el = is.slur()->spannerSegments();
|
||
if (!el.empty())
|
||
el.front()->setSelected(false);
|
||
is.setSlur(nullptr);
|
||
return;
|
||
}
|
||
ChordRest* cr1;
|
||
ChordRest* cr2;
|
||
const auto& sel = _score->selection();
|
||
auto el = sel.uniqueElements();
|
||
|
||
if (sel.isRange()) {
|
||
int startTrack = sel.staffStart() * VOICES;
|
||
int endTrack = sel.staffEnd() * VOICES;
|
||
for (int track = startTrack; track < endTrack; ++track) {
|
||
cr1 = 0;
|
||
cr2 = 0;
|
||
for (Element* e : el) {
|
||
if (e->track() != track)
|
||
continue;
|
||
if (e->isNote())
|
||
e = toNote(e)->chord();
|
||
if (!e->isChord())
|
||
continue;
|
||
ChordRest* cr = toChordRest(e);
|
||
if (!cr1 || cr1->tick() > cr->tick())
|
||
cr1 = cr;
|
||
if (!cr2 || cr2->tick() < cr->tick())
|
||
cr2 = cr;
|
||
}
|
||
if (cr1 && (cr1 != cr2))
|
||
cmdAddSlur(cr1, cr2);
|
||
}
|
||
}
|
||
else {
|
||
cr1 = 0;
|
||
cr2 = 0;
|
||
for (Element* e : el) {
|
||
if (e->isNote())
|
||
e = toNote(e)->chord();
|
||
if (!e->isChord())
|
||
continue;
|
||
ChordRest* cr = toChordRest(e);
|
||
if (!cr1 || cr->isBefore(cr1))
|
||
cr1 = cr;
|
||
if (!cr2 || cr2->isBefore(cr))
|
||
cr2 = cr;
|
||
}
|
||
if (cr1 == cr2)
|
||
cr2 = 0;
|
||
if (cr1)
|
||
cmdAddSlur(cr1, cr2);
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmdAddSlur
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmdAddSlur(ChordRest* cr1, ChordRest* cr2)
|
||
{
|
||
bool startEditMode = false;
|
||
if (cr2 == 0) {
|
||
cr2 = nextChordRest(cr1);
|
||
if (cr2 == 0)
|
||
return;
|
||
startEditMode = true; // start slur in edit mode if last chord is not given
|
||
}
|
||
|
||
_score->startCmd();
|
||
|
||
Slur* slur = new Slur(cr1->score());
|
||
slur->setTick(cr1->tick());
|
||
slur->setTick2(cr2->tick());
|
||
slur->setTrack(cr1->track());
|
||
if (cr2->staff()->part() == cr1->staff()->part() && !cr2->staff()->isLinked(cr1->staff()))
|
||
slur->setTrack2(cr2->track());
|
||
else
|
||
slur->setTrack2(cr1->track());
|
||
slur->setStartElement(cr1);
|
||
slur->setEndElement(cr2);
|
||
|
||
cr1->score()->undoAddElement(slur);
|
||
SlurSegment* ss = new SlurSegment(cr1->score());
|
||
ss->setSpannerSegmentType(SpannerSegmentType::SINGLE);
|
||
if (cr1 == cr2)
|
||
ss->setSlurOffset(Grip::END, QPointF(3.0 * cr1->score()->spatium(), 0.0));
|
||
slur->add(ss);
|
||
|
||
_score->endCmd();
|
||
|
||
if (noteEntryMode()) {
|
||
_score->inputState().setSlur(slur);
|
||
ss->setSelected(true);
|
||
}
|
||
else if (startEditMode) {
|
||
editData.element = ss;
|
||
changeState(ViewState::EDIT);
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmdAddHairpin
|
||
// '<' typed on keyboard
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmdAddHairpin(HairpinType type)
|
||
{
|
||
const Selection& selection = _score->selection();
|
||
// special case for two selected chordrests on same staff
|
||
bool twoNotesSameStaff = false;
|
||
if (selection.isList() && selection.elements().size() == 2) {
|
||
ChordRest* cr1 = selection.firstChordRest();
|
||
ChordRest* cr2 = selection.lastChordRest();
|
||
if (cr1 && cr2 && cr1 != cr2 && cr1->staffIdx() == cr2->staffIdx())
|
||
twoNotesSameStaff = true;
|
||
}
|
||
// add hairpin on each staff if possible
|
||
if (selection.isRange() && selection.staffStart() != selection.staffEnd() - 1) {
|
||
_score->startCmd();
|
||
for (int staffIdx = selection.staffStart() ; staffIdx < selection.staffEnd(); ++staffIdx) {
|
||
ChordRest* cr1 = selection.firstChordRest(staffIdx * VOICES);
|
||
ChordRest* cr2 = selection.lastChordRest(staffIdx * VOICES);
|
||
if (!cr1)
|
||
continue;
|
||
if (cr2 == 0)
|
||
cr2 = cr1;
|
||
_score->addHairpin(type, cr1->tick(), cr2->tick() + cr2->actualTicks(), cr1->track());
|
||
}
|
||
_score->endCmd();
|
||
}
|
||
else if (selection.isRange() || selection.isSingle() || twoNotesSameStaff) {
|
||
// for single staff range selection, or single selection,
|
||
// find start & end elements elements
|
||
ChordRest* cr1;
|
||
ChordRest* cr2;
|
||
_score->getSelectedChordRest2(&cr1, &cr2);
|
||
if (!cr1)
|
||
return;
|
||
if (cr2 == 0)
|
||
cr2 = cr1;
|
||
|
||
_score->startCmd();
|
||
int tick2 = twoNotesSameStaff ? cr2->tick() : cr2->tick() + cr2->actualTicks();
|
||
Hairpin* pin = _score->addHairpin(type, cr1->tick(), tick2, cr1->track());
|
||
// pin->layout();
|
||
_score->endCmd();
|
||
|
||
const std::vector<SpannerSegment*>& el = pin->spannerSegments();
|
||
if (!noteEntryMode()) {
|
||
if (!el.empty()) {
|
||
editData.element = el.front();
|
||
changeState(ViewState::EDIT);
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
// do not attempt for list selection
|
||
// or we will keep adding hairpins to the same chordrests
|
||
return;
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmdAddNoteLine
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmdAddNoteLine()
|
||
{
|
||
Note* firstNote = 0;
|
||
Note* lastNote = 0;
|
||
if (_score->selection().isRange()) {
|
||
int startTrack = _score->selection().staffStart() * VOICES;
|
||
int endTrack = _score->selection().staffEnd() * VOICES;
|
||
for (int track = startTrack; track < endTrack; ++track) {
|
||
for (Note* n : _score->selection().noteList(track)) {
|
||
if (firstNote == 0 || firstNote->chord()->tick() > n->chord()->tick())
|
||
firstNote = n;
|
||
if (lastNote == 0 || lastNote->chord()->tick() < n->chord()->tick())
|
||
lastNote = n;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
for (Note* n : _score->selection().noteList()) {
|
||
if (firstNote == 0 || firstNote->chord()->tick() > n->chord()->tick())
|
||
firstNote = n;
|
||
if (lastNote == 0 || lastNote->chord()->tick() < n->chord()->tick())
|
||
lastNote = n;
|
||
}
|
||
}
|
||
if (!firstNote || !lastNote) {
|
||
qDebug("addNoteLine: no note %p %p", firstNote, lastNote);
|
||
return;
|
||
}
|
||
if (firstNote == lastNote) {
|
||
qDebug("addNoteLine: no support for note to same note line %p", firstNote);
|
||
return;
|
||
}
|
||
TextLine* tl = new TextLine(_score);
|
||
tl->setParent(firstNote);
|
||
tl->setStartElement(firstNote);
|
||
tl->setEndElement(lastNote);
|
||
tl->setDiagonal(true);
|
||
tl->setAnchor(Spanner::Anchor::NOTE);
|
||
tl->setTick(firstNote->chord()->tick());
|
||
_score->startCmd();
|
||
_score->undoAddElement(tl);
|
||
_score->endCmd();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmdChangeEnharmonic
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmdChangeEnharmonic(bool both)
|
||
{
|
||
_score->startCmd();
|
||
QList<Note*> notes = _score->selection().uniqueNotes();
|
||
for (Note* n : notes) {
|
||
Staff* staff = n->staff();
|
||
if (staff->part()->instrument()->useDrumset())
|
||
continue;
|
||
if (staff->isTabStaff(n->tick())) {
|
||
int string = n->line() + (both ? 1 : -1);
|
||
int fret = staff->part()->instrument()->stringData()->fret(n->pitch(), string, staff, n->chord()->tick());
|
||
if (fret != -1) {
|
||
n->undoChangeProperty(Pid::FRET, fret);
|
||
n->undoChangeProperty(Pid::STRING, string);
|
||
}
|
||
}
|
||
else {
|
||
static const int tab[36] = {
|
||
26, 14, 2, // 60 B# C Dbb
|
||
21, 21, 9, // 61 C# C# Db
|
||
28, 16, 4, // 62 C## D Ebb
|
||
23, 23, 11, // 63 D# D# Eb
|
||
30, 18, 6, // 64 D## E Fb
|
||
25, 13, 1, // 65 E# F Gbb
|
||
20, 20, 8, // 66 F# F# Gb
|
||
27, 15, 3, // 67 F## G Abb
|
||
22, 22, 10, // 68 G# G# Ab
|
||
29, 17, 5, // 69 G## A Bbb
|
||
24, 24, 12, // 70 A# A# Bb
|
||
31, 19, 7, // 71 A## B Cb
|
||
};
|
||
int tpc = n->tpc();
|
||
int i;
|
||
for (i = 0; i < 36; ++i) {
|
||
if (tab[i] == tpc) {
|
||
if ((i % 3) < 2) {
|
||
if (tab[i] == tab[i + 1])
|
||
tpc = tab[i + 2];
|
||
else
|
||
tpc = tab[i + 1];
|
||
}
|
||
else {
|
||
tpc = tab[i - 2];
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
if (i == 36) {
|
||
qDebug("tpc %d not found", tpc);
|
||
}
|
||
else {
|
||
n->undoSetTpc(tpc);
|
||
if (both || staff->part()->instrument(n->chord()->tick())->transpose().isZero()) {
|
||
// change both spellings
|
||
int t = n->transposeTpc(tpc);
|
||
if (n->concertPitch())
|
||
n->undoChangeProperty(Pid::TPC2, t);
|
||
else
|
||
n->undoChangeProperty(Pid::TPC1, t);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
Selection& selection = _score->selection();
|
||
selection.clear();
|
||
for (Note* n : notes)
|
||
selection.add(n);
|
||
_score->endCmd();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cloneElement
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cloneElement(Element* e)
|
||
{
|
||
if (e->isMeasure() || e->isNote() || e->isVBox() || e->isSpacer())
|
||
return;
|
||
QDrag* drag = new QDrag(this);
|
||
QMimeData* mimeData = new QMimeData;
|
||
if (e->isSpannerSegment())
|
||
e = toSpannerSegment(e)->spanner();
|
||
mimeData->setData(mimeSymbolFormat, e->mimeData(QPointF()));
|
||
drag->setMimeData(mimeData);
|
||
drag->setPixmap(QPixmap());
|
||
drag->start(Qt::CopyAction);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// changeEditElement
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::changeEditElement(Element* e)
|
||
{
|
||
Grip grip = editData.curGrip;
|
||
endEdit();
|
||
startEdit(e, grip);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// setCursorVisible
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::setCursorVisible(bool v)
|
||
{
|
||
_cursor->setVisible(v);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmdTuplet
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmdTuplet(int n, ChordRest* cr)
|
||
{
|
||
if (cr->durationType() < TDuration(TDuration::DurationType::V_128TH) && cr->durationType() != TDuration(TDuration::DurationType::V_MEASURE)) {
|
||
mscore->noteTooShortForTupletDialog();
|
||
return;
|
||
}
|
||
Measure* measure = cr->measure();
|
||
if (measure && measure->isMMRest())
|
||
return;
|
||
|
||
Fraction f(cr->duration());
|
||
Tuplet* ot = cr->tuplet();
|
||
|
||
f.reduce(); //measure duration might not be reduced
|
||
Fraction ratio(n, f.numerator());
|
||
Fraction fr(1, f.denominator());
|
||
while (ratio.numerator() >= ratio.denominator()*2) {
|
||
ratio /= 2;
|
||
fr /= 2;
|
||
}
|
||
|
||
Tuplet* tuplet = new Tuplet(_score);
|
||
tuplet->setRatio(ratio);
|
||
|
||
//
|
||
// "fr" is the fraction value of one tuple element
|
||
//
|
||
// "tuplet time" is "normal time" / tuplet->ratio()
|
||
// Example: an 1/8 has 240 midi ticks, in an 1/8 triplet the note
|
||
// has a tick duration of 240 / (3/2) = 160 ticks
|
||
//
|
||
|
||
tuplet->setDuration(f);
|
||
TDuration baseLen(fr);
|
||
tuplet->setBaseLen(baseLen);
|
||
|
||
tuplet->setTrack(cr->track());
|
||
tuplet->setTick(cr->tick());
|
||
tuplet->setParent(measure);
|
||
|
||
if (ot)
|
||
tuplet->setTuplet(ot);
|
||
|
||
cmdCreateTuplet(cr, tuplet);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmdCreateTuplet
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmdCreateTuplet(ChordRest* cr, Tuplet* tuplet)
|
||
{
|
||
_score->cmdCreateTuplet(cr, tuplet);
|
||
|
||
const std::vector<DurationElement*>& cl = tuplet->elements();
|
||
size_t ne = cl.size();
|
||
DurationElement* el = 0;
|
||
if (ne && cl[0]->type() == ElementType::REST)
|
||
el = cl[0];
|
||
else if (ne > 1)
|
||
el = cl[1];
|
||
if (el) {
|
||
_score->select(el, SelectType::SINGLE, 0);
|
||
_score->inputState().setDuration(tuplet->baseLen());
|
||
changeState(ViewState::NOTE_ENTRY);
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// changeVoice
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::changeVoice(int voice)
|
||
{
|
||
InputState* is = &score()->inputState();
|
||
int track = (is->track() / VOICES) * VOICES + voice;
|
||
|
||
if (is->noteEntryMode()) {
|
||
is->setTrack(track);
|
||
if (is->segment()) { // can be null for eg repeatMeasure
|
||
is->setSegment(is->segment()->measure()->first(SegmentType::ChordRest));
|
||
moveCursor();
|
||
score()->setUpdateAll();
|
||
score()->update();
|
||
mscore->setPos(is->segment()->tick());
|
||
}
|
||
}
|
||
else {
|
||
// treat as command to move notes to another voice
|
||
score()->changeVoice(voice);
|
||
// modify the input state only if the command was successful
|
||
for (ChordRest* cr : score()->getSelectedChordRests())
|
||
if (cr->voice() == voice) {
|
||
is->setTrack(track);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmdTuplet
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmdTuplet(int n)
|
||
{
|
||
_score->startCmd();
|
||
if (noteEntryMode()) {
|
||
_score->expandVoice();
|
||
ChordRest* cr = _score->inputState().cr();
|
||
if (cr) {
|
||
_score->changeCRlen(cr, _score->inputState().duration());
|
||
cmdTuplet(n, cr);
|
||
}
|
||
}
|
||
else {
|
||
for (ChordRest* cr : _score->getSelectedChordRests()) {
|
||
if (!cr->isGrace()) {
|
||
cmdTuplet(n, cr);
|
||
}
|
||
}
|
||
}
|
||
_score->endCmd();
|
||
moveCursor(); // do this after endCmd to make sure segment has been laid out
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// midiNoteReceived
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::midiNoteReceived(int pitch, bool chord, int velocity)
|
||
{
|
||
qDebug("midiNoteReceived %d chord %d", pitch, chord);
|
||
|
||
MidiInputEvent ev;
|
||
ev.pitch = pitch;
|
||
ev.chord = chord;
|
||
ev.velocity = velocity;
|
||
|
||
score()->masterScore()->enqueueMidiEvent(ev);
|
||
|
||
if (!score()->undoStack()->active())
|
||
cmd((const char*)0);
|
||
|
||
if (!chord && velocity && !realtimeTimer->isActive() && score()->usingNoteEntryMethod(NoteEntryMethod::REALTIME_AUTO)) {
|
||
// First note pressed in automatic real-time mode.
|
||
extendNoteTimer->start(preferences.getInt(PREF_IO_MIDI_REALTIMEDELAY)); // set timer to trigger repeatedly
|
||
triggerCmdRealtimeAdvance(); // also trigger once immediately
|
||
}
|
||
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// extendCurrentNote
|
||
// Called after user has held down a midi key for a while.
|
||
// TODO: adapt to allow calling from StepTime mode.
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::extendCurrentNote()
|
||
{
|
||
if (!noteEntryMode() || realtimeTimer->isActive())
|
||
return;
|
||
|
||
allowRealtimeRests = false;
|
||
realtimeTimer->start(preferences.getInt(PREF_IO_MIDI_REALTIMEDELAY)); // set timer to trigger repeatedly
|
||
triggerCmdRealtimeAdvance(); // also trigger once immediately
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// realtimeAdvance
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::realtimeAdvance(bool allowRests)
|
||
{
|
||
if (!noteEntryMode())
|
||
return;
|
||
InputState& is = score()->inputState();
|
||
switch (is.noteEntryMethod()) {
|
||
case NoteEntryMethod::REALTIME_MANUAL:
|
||
allowRealtimeRests = allowRests;
|
||
triggerCmdRealtimeAdvance();
|
||
break;
|
||
case NoteEntryMethod::REALTIME_AUTO:
|
||
if (realtimeTimer->isActive())
|
||
realtimeTimer->stop();
|
||
else {
|
||
allowRealtimeRests = allowRests;
|
||
realtimeTimer->start(preferences.getInt(PREF_IO_MIDI_REALTIMEDELAY));
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// triggerCmdRealtimeAdvance
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::triggerCmdRealtimeAdvance()
|
||
{
|
||
InputState& is = score()->inputState();
|
||
bool realtime = is.usingNoteEntryMethod(NoteEntryMethod::REALTIME_AUTO) || is.usingNoteEntryMethod(NoteEntryMethod::REALTIME_MANUAL);
|
||
if (!realtime || !noteEntryMode() || (!allowRealtimeRests && score()->activeMidiPitches()->empty())) {
|
||
if (realtimeTimer->isActive())
|
||
realtimeTimer->stop();
|
||
allowRealtimeRests = true;
|
||
return;
|
||
}
|
||
// give audible feedback immediately to indicate a beat, but don’t advance just yet.
|
||
seq->playMetronomeBeat(_score->tick2beatType(is.tick()));
|
||
// The user will want to press notes "on the beat" and not before the beat, so wait a
|
||
// little in case midi input event is received just after realtime-advance was called.
|
||
QTimer::singleShot(100, Qt::PreciseTimer, this, SLOT(cmdRealtimeAdvance()));
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmdRealtimeAdvance
|
||
// move input forwards and extend current chord/rest.
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmdRealtimeAdvance()
|
||
{
|
||
InputState& is = _score->inputState();
|
||
if (!is.noteEntryMode())
|
||
return;
|
||
_score->startCmd();
|
||
int ticks2measureEnd = is.segment()->measure()->ticks() - is.segment()->rtick();
|
||
if (!is.cr() || (is.cr()->duration() != is.duration().fraction() && is.duration().ticks() < ticks2measureEnd))
|
||
_score->setNoteRest(is.segment(), is.track(), NoteVal(), is.duration().fraction(), Direction::AUTO);
|
||
ChordRest* prevCR = toChordRest(is.cr());
|
||
is.moveToNextInputPos();
|
||
if (_score->activeMidiPitches()->empty())
|
||
_score->setNoteRest(is.segment(), is.track(), NoteVal(), is.duration().fraction(), Direction::AUTO);
|
||
else {
|
||
Chord* prevChord = prevCR->isChord() ? toChord(prevCR) : 0;
|
||
bool partOfChord = false;
|
||
for (const MidiInputEvent &ev : *_score->activeMidiPitches()) {
|
||
_score->addTiedMidiPitch(ev.pitch, partOfChord, prevChord);
|
||
partOfChord = true;
|
||
}
|
||
}
|
||
if (prevCR->measure() != is.segment()->measure()) {
|
||
// just advanced across barline. Now simplify tied notes.
|
||
score()->regroupNotesAndRests(prevCR->measure()->tick(), is.segment()->measure()->tick(), is.track());
|
||
}
|
||
_score->endCmd();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmdAddChordName
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmdAddChordName()
|
||
{
|
||
if (!_score->checkHasMeasures())
|
||
return;
|
||
|
||
int track = -1;
|
||
Segment* segment = nullptr;
|
||
Element* el = _score->selection().element();
|
||
if (el && el->type() == ElementType::FRET_DIAGRAM) {
|
||
FretDiagram* fd = toFretDiagram(el);
|
||
track = fd->track();
|
||
while (el && !el->isSegment())
|
||
el = el->parent();
|
||
if (el)
|
||
segment = toSegment(el);
|
||
}
|
||
else {
|
||
ChordRest* cr = _score->getSelectedChordRest();
|
||
if (cr) {
|
||
track = cr->track();
|
||
segment = cr->segment();
|
||
}
|
||
}
|
||
if (track == -1 || !segment)
|
||
return;
|
||
|
||
_score->startCmd();
|
||
Harmony* harmony = new Harmony(_score);
|
||
harmony->setTrack(track);
|
||
harmony->setParent(segment);
|
||
_score->undoAddElement(harmony);
|
||
_score->endCmd();
|
||
|
||
_score->select(harmony, SelectType::SINGLE, 0);
|
||
startEditMode(harmony);
|
||
_score->update();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmdAddText
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmdAddText(Tid tid)
|
||
{
|
||
if (!_score->checkHasMeasures())
|
||
return;
|
||
if (noteEntryMode()) // force out of entry mode
|
||
changeState(ViewState::NORMAL);
|
||
|
||
TextBase* s = 0;
|
||
_score->startCmd();
|
||
switch (tid) {
|
||
case Tid::TITLE:
|
||
case Tid::SUBTITLE:
|
||
case Tid::COMPOSER:
|
||
case Tid::POET:
|
||
case Tid::INSTRUMENT_EXCERPT:
|
||
{
|
||
MeasureBase* measure = _score->first();
|
||
if (!measure->isVBox()) {
|
||
_score->insertMeasure(ElementType::VBOX, measure);
|
||
measure = measure->prev();
|
||
}
|
||
s = new Text(_score, tid);
|
||
s->setParent(measure);
|
||
adjustCanvasPosition(measure, false);
|
||
_score->undoAddElement(s);
|
||
}
|
||
break;
|
||
|
||
case Tid::REHEARSAL_MARK:
|
||
{
|
||
ChordRest* cr = _score->getSelectedChordRest();
|
||
if (!cr)
|
||
break;
|
||
s = new RehearsalMark(_score);
|
||
cr->undoAddAnnotation(s);
|
||
}
|
||
break;
|
||
case Tid::STAFF:
|
||
{
|
||
ChordRest* cr = _score->getSelectedChordRest();
|
||
if (!cr)
|
||
break;
|
||
s = new StaffText(_score, Tid::STAFF);
|
||
cr->undoAddAnnotation(s);
|
||
}
|
||
break;
|
||
case Tid::SYSTEM:
|
||
{
|
||
ChordRest* cr = _score->getSelectedChordRest();
|
||
if (!cr)
|
||
break;
|
||
s = new SystemText(_score, Tid::SYSTEM);
|
||
cr->undoAddAnnotation(s);
|
||
}
|
||
break;
|
||
case Tid::EXPRESSION:
|
||
{
|
||
ChordRest* cr = _score->getSelectedChordRest();
|
||
if (!cr)
|
||
break;
|
||
s = new StaffText(_score, Tid::EXPRESSION);
|
||
s->setPlacement(Placement::BELOW);
|
||
cr->undoAddAnnotation(s);
|
||
}
|
||
break;
|
||
case Tid::INSTRUMENT_CHANGE:
|
||
{
|
||
ChordRest* cr = _score->getSelectedChordRest();
|
||
if (!cr)
|
||
break;
|
||
s = new InstrumentChange(_score);
|
||
cr->undoAddAnnotation(s);
|
||
}
|
||
break;
|
||
case Tid::FINGERING:
|
||
{
|
||
Element* e = _score->getSelectedElement();
|
||
if (!e || !e->isNote())
|
||
break;
|
||
bool isTablature = e->staff()->isTabStaff(e->tick());
|
||
bool tabFingering = e->staff()->staffType(e->tick())->showTabFingering();
|
||
if (isTablature && !tabFingering)
|
||
break;
|
||
s = new Fingering(_score);
|
||
s->setTrack(e->track());
|
||
s->setParent(e);
|
||
_score->undoAddElement(s);
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
if (s) {
|
||
_score->select(s, SelectType::SINGLE, 0);
|
||
_score->endCmd();
|
||
Measure* m = s->findMeasure();
|
||
if (m && m->hasMMRest() && s->links()) {
|
||
Measure* mmRest = m->mmRest();
|
||
for (ScoreElement* se : *s->links()) {
|
||
TextBase* s1 = toTextBase(se);
|
||
if (s != s1 && s1->findMeasure() == mmRest) {
|
||
s = s1;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
startEditMode(s);
|
||
}
|
||
else
|
||
_score->endCmd();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmdAppendMeasures
|
||
/// Append \a n measures.
|
||
///
|
||
/// Keyboard callback, called from pulldown menu.
|
||
//
|
||
// - called from pulldown menu
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmdAppendMeasures(int n, ElementType type)
|
||
{
|
||
_score->startCmd();
|
||
appendMeasures(n, type);
|
||
_score->endCmd();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// appendMeasure
|
||
//---------------------------------------------------------
|
||
|
||
MeasureBase* ScoreView::appendMeasure(ElementType type)
|
||
{
|
||
_score->startCmd();
|
||
_score->insertMeasure(type, 0);
|
||
MeasureBase* mb = _score->last();
|
||
_score->endCmd();
|
||
return mb;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// appendMeasures
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::appendMeasures(int n, ElementType type)
|
||
{
|
||
if (_score->noStaves()) {
|
||
QMessageBox::warning(0, "MuseScore",
|
||
tr("No staves found:\n"
|
||
"please use the instruments dialog to\n"
|
||
"first create some staves"));
|
||
return;
|
||
}
|
||
for (int i = 0; i < n; ++i)
|
||
_score->insertMeasure(type, 0);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmdInsertMeasures
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmdInsertMeasures(int n, ElementType type)
|
||
{
|
||
MeasureBase* mb = checkSelectionStateForInsertMeasure();
|
||
if (!mb)
|
||
return;
|
||
_score->startCmd();
|
||
for (int i = 0; i < n; ++i)
|
||
_score->insertMeasure(type, mb);
|
||
_score->endCmd();
|
||
|
||
if (mb->type() == ElementType::MEASURE) {
|
||
// re-select the original measure (which may now be covered by an mmrest)
|
||
// do this after the layout so mmrests are updated
|
||
Measure* m = _score->tick2measureMM(mb->tick());
|
||
_score->select(m, SelectType::SINGLE, 0);
|
||
}
|
||
else {
|
||
// original selection was not a measure, just re-select it
|
||
_score->select(mb, SelectType::SINGLE, 0);
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmdInsertMeasure
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmdInsertMeasure(ElementType type)
|
||
{
|
||
MeasureBase* mb = checkSelectionStateForInsertMeasure();
|
||
if (!mb)
|
||
return;
|
||
_score->startCmd();
|
||
_score->insertMeasure(type, mb);
|
||
mb = mb->prev();
|
||
if (mb->type() == ElementType::TBOX) {
|
||
TBox* tbox = static_cast<TBox*>(mb);
|
||
Text* s = tbox->text();
|
||
_score->select(s, SelectType::SINGLE, 0);
|
||
_score->endCmd();
|
||
startEditMode(s);
|
||
return;
|
||
}
|
||
if (mb)
|
||
_score->select(mb, SelectType::SINGLE, 0);
|
||
_score->endCmd();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// checkSelectionStateForInsertMeasure
|
||
//---------------------------------------------------------
|
||
|
||
MeasureBase* ScoreView::checkSelectionStateForInsertMeasure()
|
||
{
|
||
MeasureBase* mb = 0;
|
||
if (_score->selection().isRange()) {
|
||
mb = _score->selection().startSegment()->measure();
|
||
return mb;
|
||
}
|
||
|
||
mb = _score->selection().findMeasure();
|
||
if (mb)
|
||
return mb;
|
||
|
||
Element* e = _score->selection().element();
|
||
if (e) {
|
||
if (e->type() == ElementType::VBOX || e->type() == ElementType::TBOX || e->type() == ElementType::HBOX)
|
||
return static_cast<MeasureBase*>(e);
|
||
}
|
||
QMessageBox::warning(0, "MuseScore",
|
||
tr("No measure selected:\n" "Please select a measure and try again"));
|
||
return 0;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmdRepeatSelection
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmdRepeatSelection()
|
||
{
|
||
const Selection& selection = _score->selection();
|
||
|
||
if (noteEntryMode() && selection.isSingle()) {
|
||
Element* el = _score->selection().element();
|
||
if (el && el->type() == ElementType::NOTE) {
|
||
if (!_score->inputState().endOfScore()) {
|
||
_score->startCmd();
|
||
bool addTo = false;
|
||
Chord* c = static_cast<Note*>(el)->chord();
|
||
for (Note* note : c->notes()) {
|
||
NoteVal nval = note->noteVal();
|
||
_score->addPitch(nval, addTo);
|
||
addTo = true;
|
||
}
|
||
_score->endCmd();
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
if (!selection.isRange()) {
|
||
qDebug("wrong selection type");
|
||
return;
|
||
}
|
||
|
||
QString mimeType = selection.mimeType();
|
||
if (mimeType.isEmpty()) {
|
||
qDebug("mime type is empty");
|
||
return;
|
||
}
|
||
QMimeData* mimeData = new QMimeData;
|
||
mimeData->setData(mimeType, selection.mimeData());
|
||
if (MScore::debugMode)
|
||
qDebug("cmdRepeatSelection: <%s>", mimeData->data(mimeType).data());
|
||
QApplication::clipboard()->setMimeData(mimeData);
|
||
|
||
QByteArray d(mimeData->data(mimeType));
|
||
XmlReader xml(d);
|
||
xml.setPasteMode(true);
|
||
|
||
int dStaff = selection.staffStart();
|
||
Segment* endSegment = selection.endSegment();
|
||
|
||
if (endSegment && endSegment->segmentType() != SegmentType::ChordRest)
|
||
endSegment = endSegment->next1(SegmentType::ChordRest);
|
||
if (endSegment && endSegment->element(dStaff * VOICES)) {
|
||
Element* e = endSegment->element(dStaff * VOICES);
|
||
if (e) {
|
||
ChordRest* cr = toChordRest(e);
|
||
_score->startCmd();
|
||
_score->pasteStaff(xml, cr->segment(), cr->staffIdx());
|
||
_score->endCmd();
|
||
}
|
||
else
|
||
qDebug("ScoreView::cmdRepeatSelection: cannot paste: %p <%s>", e, e ? e->name() : "");
|
||
}
|
||
else {
|
||
qDebug("cmdRepeatSelection: cannot paste: endSegment: %p dStaff %d", endSegment, dStaff);
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// searchPage
|
||
//---------------------------------------------------------
|
||
|
||
bool ScoreView::searchPage(int n)
|
||
{
|
||
bool result = true;
|
||
n -= score()->pageNumberOffset();
|
||
if (n <= 0) {
|
||
n = 1;
|
||
result = false;
|
||
}
|
||
n--;
|
||
if (n >= _score->npages()) {
|
||
result = false;
|
||
n = _score->npages() - 1;
|
||
}
|
||
const Page* page = _score->pages()[n];
|
||
foreach (System* s, page->systems()) {
|
||
if (s->firstMeasure()) {
|
||
gotoMeasure(s->firstMeasure());
|
||
break;
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// searchMeasure
|
||
//---------------------------------------------------------
|
||
|
||
bool ScoreView::searchMeasure(int n)
|
||
{
|
||
if (n <= 0)
|
||
return false;
|
||
bool result = true;
|
||
--n;
|
||
int i = 0;
|
||
Measure* measure;
|
||
for (measure = _score->firstMeasureMM(); measure; measure = measure->nextMeasureMM()) {
|
||
int nn = _score->styleB(Sid::createMultiMeasureRests) && measure->isMMRest()
|
||
? measure->mmRestCount() : 1;
|
||
if (n >= i && n < (i+nn))
|
||
break;
|
||
i += nn;
|
||
}
|
||
if (!measure) {
|
||
measure = score()->lastMeasureMM();
|
||
result = false;
|
||
}
|
||
gotoMeasure(measure);
|
||
return result;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// searchRehearsalMark
|
||
//---------------------------------------------------------
|
||
|
||
bool ScoreView::searchRehearsalMark(const QString& s)
|
||
{
|
||
//search rehearsal marks
|
||
QString ss = s.toLower();
|
||
bool found = false;
|
||
for (Segment* seg = score()->firstSegment(SegmentType::ChordRest); seg; seg = seg->next1(SegmentType::ChordRest)) {
|
||
for (Element* e : seg->annotations()){
|
||
if (e->type() == ElementType::REHEARSAL_MARK) {
|
||
RehearsalMark* rm = static_cast<RehearsalMark*>(e);
|
||
QString rms = rm->plainText().toLower();
|
||
if (rms.startsWith(ss)) {
|
||
gotoMeasure(seg->measure());
|
||
found = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (found)
|
||
break;
|
||
}
|
||
return found;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// gotoMeasure
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::gotoMeasure(Measure* measure)
|
||
{
|
||
adjustCanvasPosition(measure, state != ViewState::NORMAL);
|
||
int tracks = _score->nstaves() * VOICES;
|
||
for (Segment* segment = measure->first(); segment; segment = segment->next()) {
|
||
if (segment->segmentType() != SegmentType::ChordRest)
|
||
continue;
|
||
int track;
|
||
for (track = 0; track < tracks; ++track) {
|
||
ChordRest* cr = static_cast<ChordRest*>(segment->element(track));
|
||
if (cr) {
|
||
Element* e;
|
||
if (cr->type() == ElementType::CHORD)
|
||
e = static_cast<Chord*>(cr)->upNote();
|
||
else //REST
|
||
e = cr;
|
||
|
||
_score->select(e, SelectType::SINGLE, 0);
|
||
break;
|
||
}
|
||
}
|
||
if (track != tracks)
|
||
break;
|
||
}
|
||
_score->setUpdateAll();
|
||
_score->update();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// layoutChanged
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::layoutChanged()
|
||
{
|
||
if (mscore->navigator())
|
||
mscore->navigator()->layoutChanged();
|
||
_curLoopIn->move(_score->pos(POS::LEFT));
|
||
Measure* lm = _score->lastMeasure();
|
||
if (lm && _score->pos(POS::RIGHT) > lm->endTick())
|
||
_score->setPos(POS::RIGHT, lm->endTick());
|
||
_curLoopOut->move(_score->pos(POS::RIGHT));
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// elementLower
|
||
//---------------------------------------------------------
|
||
|
||
static bool elementLower(const Element* e1, const Element* e2)
|
||
{
|
||
if (!e1->selectable())
|
||
return false;
|
||
if (e1->z() == e2->z()) {
|
||
if (e1->type() == e2->type()) {
|
||
if (e1->type() == ElementType::NOTEDOT) {
|
||
const NoteDot* n1 = static_cast<const NoteDot*>(e1);
|
||
const NoteDot* n2 = static_cast<const NoteDot*>(e2);
|
||
if (n1->note() && n1->note()->hidden())
|
||
return n2;
|
||
else
|
||
return n1;
|
||
}
|
||
else if (e1->type() == ElementType::NOTE) {
|
||
const Note* n1 = static_cast<const Note*>(e1);
|
||
const Note* n2 = static_cast<const Note*>(e2);
|
||
if (n1->hidden())
|
||
return n2;
|
||
else
|
||
return n1;
|
||
}
|
||
}
|
||
}
|
||
return e1->z() < e2->z();
|
||
}
|
||
|
||
|
||
|
||
//---------------------------------------------------------
|
||
// elementNear
|
||
//---------------------------------------------------------
|
||
|
||
Element* ScoreView::elementNear(QPointF p)
|
||
{
|
||
Page* page = point2page(p);
|
||
if (!page)
|
||
return 0;
|
||
|
||
p -= page->pos();
|
||
double w = (preferences.getInt(PREF_UI_CANVAS_MISC_SELECTIONPROXIMITY) * .5) / matrix().m11();
|
||
QRectF r(p.x() - w, p.y() - w, 3.0 * w, 3.0 * w);
|
||
|
||
QList<Element*> el = page->items(r);
|
||
QList<Element*> ll;
|
||
for (Element* e : el) {
|
||
e->itemDiscovered = 0;
|
||
if (!e->selectable() || e->isPage())
|
||
continue;
|
||
if (e->contains(p))
|
||
ll.append(e);
|
||
}
|
||
int n = ll.size();
|
||
if ((n == 0) || ((n == 1) && (ll[0]->isMeasure()))) {
|
||
//
|
||
// if no relevant element hit, look nearby
|
||
//
|
||
for (Element* e : el) {
|
||
if (e->isPage() || !e->selectable())
|
||
continue;
|
||
if (e->intersects(r))
|
||
ll.append(e);
|
||
}
|
||
}
|
||
if (ll.empty()) {
|
||
// qDebug(" nothing found");
|
||
return 0;
|
||
}
|
||
qSort(ll.begin(), ll.end(), elementLower);
|
||
|
||
#if 0
|
||
qDebug("==");
|
||
for (const Element* e : ll)
|
||
qDebug(" %s selected %d z %d", e->name(), e->selected(), e->z());
|
||
#endif
|
||
Element* e = ll.at(0);
|
||
return e;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// posChanged
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::posChanged(POS pos, unsigned tick)
|
||
{
|
||
if (this != mscore->currentScoreView() && !_moveWhenInactive)
|
||
return;
|
||
switch (pos) {
|
||
case POS::CURRENT:
|
||
if (noteEntryMode())
|
||
moveCursor(); // update input cursor position
|
||
else
|
||
moveCursor(tick); // update play position
|
||
break;
|
||
case POS::LEFT:
|
||
_curLoopIn->move(_score->pos(POS::LEFT));
|
||
break;
|
||
case POS::RIGHT:
|
||
_curLoopOut->move(_score->pos(POS::RIGHT));
|
||
break;
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// loopToggled
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::loopToggled(bool val)
|
||
{
|
||
if (_score->lastMeasure() == 0)
|
||
return;
|
||
if (_score->pos(POS::LEFT) == 0 && _score->pos(POS::RIGHT) == 0)
|
||
_score->setPos(POS::RIGHT, _score->lastMeasure()->endTick());
|
||
_curLoopIn->move(_score->loopInTick());
|
||
_curLoopOut->move(_score->loopOutTick());
|
||
_curLoopIn->setVisible(val);
|
||
_curLoopOut->setVisible(val);
|
||
update();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmdMoveCR
|
||
// swap selected cr with cr to the left or right
|
||
// - not across measure boundaries
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmdMoveCR(bool left)
|
||
{
|
||
Element* e = _score->getSelectedElement();
|
||
if (e && (e->type() == ElementType::NOTE || e->type() == ElementType::REST)) {
|
||
if (e->type() == ElementType::NOTE)
|
||
e = e->parent();
|
||
QList<ChordRest*> crl;
|
||
if (e->links()) {
|
||
for (ScoreElement* cr : *e->links())
|
||
crl.append(static_cast<ChordRest*>(cr));
|
||
}
|
||
else
|
||
crl.append(static_cast<ChordRest*>(e));
|
||
|
||
bool cmdActive = false;
|
||
for (ChordRest* cr1 : crl) {
|
||
if (cr1->type() == ElementType::REST) {
|
||
Rest* r = static_cast<Rest*>(cr1);
|
||
if (r->measure() && r->measure()->isMMRest())
|
||
break;
|
||
}
|
||
ChordRest* cr2 = left ? prevChordRest(cr1) : nextChordRest(cr1);
|
||
if (cr2 && cr1->measure() == cr2->measure() && !cr1->tuplet() && !cr2->tuplet()
|
||
&& cr1->durationType() == cr2->durationType() && cr1->duration() == cr2->duration()) {
|
||
if (!cmdActive) {
|
||
_score->startCmd();
|
||
cmdActive = true;
|
||
}
|
||
_score->undo(new SwapCR(cr1, cr2));
|
||
}
|
||
}
|
||
if (cmdActive)
|
||
_score->endCmd();
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmdAddRemoveBreaks
|
||
/// add or remove line breaks within a range selection
|
||
/// or, if nothing is selected, the entire score
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmdAddRemoveBreaks()
|
||
{
|
||
bool noSelection = !_score->selection().isRange();
|
||
|
||
if (noSelection)
|
||
_score->cmdSelectAll();
|
||
else if (!_score->selection().isRange())
|
||
return;
|
||
|
||
BreaksDialog bd;
|
||
if (!bd.exec())
|
||
return;
|
||
|
||
int interval = bd.remove || bd.lock ? 0 : bd.interval;
|
||
|
||
_score->addRemoveBreaks(interval, bd.lock);
|
||
|
||
if (noSelection)
|
||
_score->deselectAll();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// cmdCopyLyricsToClipboard
|
||
/// Copy the score lyrics into clipboard
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::cmdCopyLyricsToClipboard()
|
||
{
|
||
QApplication::clipboard()->setText(_score->extractLyrics());
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// updateContinuousPanel
|
||
// slot triggered when moving around the score to keep the panel visible
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::updateContinuousPanel()
|
||
{
|
||
if (_score->layoutMode() == LayoutMode::LINE)
|
||
update();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// updateShadowNotes
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::updateShadowNotes()
|
||
{
|
||
if (shadowNote->visible())
|
||
setShadowNote(shadowNote->pos());
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// getEditElement
|
||
//---------------------------------------------------------
|
||
|
||
Element* ScoreView::getEditElement()
|
||
{
|
||
return editData.element;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// onElementDestruction
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::onElementDestruction(Element* e)
|
||
{
|
||
if (editData.element == e)
|
||
editData.element = nullptr;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// startNoteEntryMode
|
||
//---------------------------------------------------------
|
||
|
||
void ScoreView::startNoteEntryMode()
|
||
{
|
||
changeState(ViewState::NOTE_ENTRY);
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// fotoMode
|
||
//---------------------------------------------------------
|
||
|
||
bool ScoreView::fotoMode() const
|
||
{
|
||
switch (state) {
|
||
case ViewState::NORMAL:
|
||
case ViewState::DRAG:
|
||
case ViewState::DRAG_OBJECT:
|
||
case ViewState::EDIT:
|
||
case ViewState::DRAG_EDIT:
|
||
case ViewState::LASSO:
|
||
case ViewState::NOTE_ENTRY:
|
||
case ViewState::PLAY:
|
||
case ViewState::ENTRY_PLAY:
|
||
break;
|
||
|
||
case ViewState::FOTO:
|
||
case ViewState::FOTO_DRAG:
|
||
case ViewState::FOTO_DRAG_EDIT:
|
||
case ViewState::FOTO_DRAG_OBJECT:
|
||
case ViewState::FOTO_LASSO:
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
}
|