MuseScore/mscore/events.cpp
Anatoly-os 847bfaac35 Fixed AppVeyor's warnings
Initialized variables
Removed unused variables
Replaced char* local variable with std::string
Fixed unused variable check and adjusted Q_ASSERT usage
2018-02-22 14:31:39 +02:00

840 lines
30 KiB
C++

//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 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 "magbox.h"
#include "musescore.h"
#include "seq.h"
#include "texttools.h"
#include "fotomode.h"
#include "libmscore/score.h"
#include "libmscore/keysig.h"
#include "libmscore/segment.h"
#include "libmscore/utils.h"
#include "libmscore/text.h"
#include "libmscore/measure.h"
#include "libmscore/stafflines.h"
#include "libmscore/chord.h"
#include "libmscore/shadownote.h"
namespace Ms {
//---------------------------------------------------------
// event
//---------------------------------------------------------
bool ScoreView::event(QEvent* event)
{
if (event->type() == QEvent::KeyPress && editData.element) {
QKeyEvent* ke = static_cast<QKeyEvent*>(event);
if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
if (editData.element->isText())
return true;
bool rv = true;
if (ke->key() == Qt::Key_Tab) {
rv = editData.element->nextGrip(editData);
updateGrips();
_score->update();
if (rv)
return true;
}
else if (ke->key() == Qt::Key_Backtab)
rv = editData.element->prevGrip(editData);
updateGrips();
_score->update();
if (rv)
return true;
}
}
// else if (event->type() == CloneDrag) {
//TODO:drag Element* e = static_cast<CloneEvent*>(event)->element();
// cloneElement(e);
// }
else if (event->type() == QEvent::Gesture) {
return gestureEvent(static_cast<QGestureEvent*>(event));
}
else if (event->type() == QEvent::MouseButtonPress && qApp->focusWidget() != this) {
QMouseEvent* me = static_cast<QMouseEvent*>(event);
if (me->button() == Qt::LeftButton)
this->setFocus();
}
return QWidget::event(event);
}
//---------------------------------------------------------
// gestureEvent
// fired on touchscreen gestures as well as Mac touchpad gestures
//---------------------------------------------------------
bool ScoreView::gestureEvent(QGestureEvent *event)
{
if (QGesture *gesture = event->gesture(Qt::PinchGesture)) {
// Zoom in/out when receiving a pinch gesture
QPinchGesture *pinch = static_cast<QPinchGesture *>(gesture);
static qreal magStart = 1.0;
if (pinch->state() == Qt::GestureStarted) {
magStart = lmag();
}
if (pinch->changeFlags() & QPinchGesture::ScaleFactorChanged) {
// On Windows, totalScaleFactor() contains the net magnification.
// On OS X, totalScaleFactor() is 1, and scaleFactor() contains the net magnification.
qreal value = pinch->totalScaleFactor();
if (value == 1) {
value = pinch->scaleFactor();
}
zoom(magStart*value, pinch->centerPoint());
}
}
return true;
}
//---------------------------------------------------------
// wheelEvent
//---------------------------------------------------------
void ScoreView::wheelEvent(QWheelEvent* event)
{
#define PIXELSSTEPSFACTOR 5
QPoint pixelsScrolled = event->pixelDelta();
QPoint stepsScrolled = event->angleDelta();
int dx = 0, dy = 0, n = 0;
qreal nReal = 0.0;
if (!pixelsScrolled.isNull()) {
dx = pixelsScrolled.x();
dy = pixelsScrolled.y();
nReal = static_cast<qreal>(dy) / PIXELSSTEPSFACTOR;
}
else if (!stepsScrolled.isNull()) {
dx = static_cast<qreal>(stepsScrolled.x()) * qMax(2, width() / 10) / 120;
dy = static_cast<qreal>(stepsScrolled.y()) * qMax(2, height() / 10) / 120;
nReal = static_cast<qreal>(stepsScrolled.y()) / 120;
}
n = (int) nReal;
//this functionality seems currently blocked by the context menu
if (event->buttons() & Qt::RightButton) {
bool up = n > 0;
if (!up)
n = -n;
score()->startCmd();
for (int i = 0; i < n; ++i)
score()->upDown(up, UpDownMode::CHROMATIC);
score()->endCmd();
return;
}
if (event->modifiers() & Qt::ControlModifier) { // Windows touch pad pinches also execute this
QApplication::sendPostedEvents(this, 0);
zoomStep(nReal, event->pos());
return;
}
//make shift+scroll go horizontally
if (event->modifiers() & Qt::ShiftModifier && dx == 0) {
dx = dy;
dy = 0;
}
if (dx == 0 && dy == 0)
return;
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());
}
//---------------------------------------------------------
// resizeEvent
//---------------------------------------------------------
void ScoreView::resizeEvent(QResizeEvent* /*ev*/)
{
if (_magIdx != MagIdx::MAG_FREE)
setMag(mscore->getMag(this));
emit sizeChanged();
}
//---------------------------------------------------------
// focusInEvent
//---------------------------------------------------------
void ScoreView::focusInEvent(QFocusEvent* event)
{
if (this != mscore->currentScoreView())
mscore->setCurrentScoreView(this);
if (mscore->splitScreen()) {
if (!focusFrame) {
focusFrame = new QFocusFrame;
QPalette p(focusFrame->palette());
p.setColor(QPalette::WindowText, Qt::blue);
focusFrame->setPalette(p);
}
focusFrame->setWidget(static_cast<QWidget*>(this));
}
QWidget::focusInEvent(event);
}
//---------------------------------------------------------
// focusOutEvent
//---------------------------------------------------------
void ScoreView::focusOutEvent(QFocusEvent* event)
{
if (focusFrame)
focusFrame->setWidget(0);
QWidget::focusOutEvent(event);
}
//---------------------------------------------------------
// mouseReleaseEvent
//---------------------------------------------------------
void ScoreView::mouseReleaseEvent(QMouseEvent*)
{
editData.buttons = Qt::NoButton;
if (seq)
seq->stopNoteTimer();
switch (state) {
case ViewState::DRAG:
case ViewState::DRAG_OBJECT:
case ViewState::LASSO:
changeState(ViewState::NORMAL);
break;
case ViewState::DRAG_EDIT:
changeState(ViewState::EDIT);
break;
case ViewState::FOTO_DRAG:
case ViewState::FOTO_DRAG_EDIT:
case ViewState::FOTO_DRAG_OBJECT:
changeState(ViewState::FOTO);
break;
case ViewState::NORMAL:
case ViewState::EDIT:
case ViewState::NOTE_ENTRY:
case ViewState::PLAY:
case ViewState::ENTRY_PLAY:
case ViewState::FOTO:
case ViewState::FOTO_LASSO:
break;
}
}
//---------------------------------------------------------
// mousePressEventNormal
// handle mouse press event for NORMAL mode
//---------------------------------------------------------
void ScoreView::mousePressEventNormal(QMouseEvent* ev)
{
_score->masterScore()->cmdState().reset(); // DEBUG: should not be necessary
Qt::KeyboardModifiers keyState = ev->modifiers();
SelectType st = SelectType::SINGLE;
if (keyState == Qt::NoModifier)
st = SelectType::SINGLE;
else if (keyState & Qt::ShiftModifier)
st = SelectType::RANGE;
else if (keyState & Qt::ControlModifier)
st = SelectType::ADD;
Element* e = editData.element;
if (e) {
if (keyState == (Qt::ShiftModifier | Qt::ControlModifier)) {
cloneElement(e);
return;
}
if (e->isKeySig() && (keyState != Qt::ControlModifier) && st == SelectType::SINGLE) {
// special case: select for all staves
Segment* s = toKeySig(e)->segment();
bool first = true;
for (int staffIdx = 0; staffIdx < _score->nstaves(); ++staffIdx) {
Element* e = s->element(staffIdx * VOICES);
if (e) {
e->score()->select(e, first ? SelectType::SINGLE : SelectType::ADD);
first = false;
}
}
}
else {
if (st == SelectType::ADD && e->selected())
e->score()->deselect(e);
else
e->score()->select(e, st, -1);
}
if (e->isNote())
mscore->play(e);
if (e) {
_score = e->score();
_score->setUpdateAll();
}
}
else {
// special case: chacke if measure is selected
int staffIdx;
Measure* m = _score->pos2measure(editData.startMove, &staffIdx, 0, 0, 0);
if (m && m->staffLines(staffIdx)->canvasBoundingRect().contains(editData.startMove)) {
_score->select(m, st, staffIdx);
_score->setUpdateAll();
}
else
_score->deselectAll();
}
_score->update();
mscore->endCmd();
}
//---------------------------------------------------------
// mousePressEvent
//---------------------------------------------------------
void ScoreView::mousePressEvent(QMouseEvent* ev)
{
editData.startMovePixel = ev->pos();
editData.startMove = toLogical(ev->pos());
editData.lastPos = editData.startMove;
editData.pos = editData.startMove;
editData.buttons = ev->buttons();
editData.modifiers = qApp->keyboardModifiers();
Element* e = elementNear(editData.startMove);
qDebug("element %s", e ? e->name() : "--");
switch (state) {
case ViewState::NORMAL:
if (ev->button() == Qt::RightButton) // context menu?
break;
editData.element = e;
mousePressEventNormal(ev);
break;
case ViewState::FOTO: {
if (ev->buttons() & Qt::RightButton)
break;
editData.element = _foto;
bool gripClicked = false;
qreal a = editData.grip[0].width() * 1.0;
for (int i = 0; i < editData.grips; ++i) {
if (editData.grip[i].adjusted(-a, -a, a, a).contains(editData.startMove)) {
editData.curGrip = Grip(i);
updateGrips();
gripClicked = true;
score()->update();
break;
}
}
if (gripClicked)
changeState(ViewState::FOTO_DRAG_EDIT);
else if (_foto->canvasBoundingRect().contains(editData.startMove))
changeState(ViewState::FOTO_DRAG_OBJECT);
else
changeState(ViewState::FOTO_DRAG);
}
break;
case ViewState::NOTE_ENTRY:
_score->startCmd();
_score->putNote(editData.startMove, ev->modifiers() & Qt::ShiftModifier, ev->modifiers() & Qt::ControlModifier);
_score->endCmd();
if (_score->inputState().cr())
adjustCanvasPosition(_score->inputState().cr(), false);
break;
case ViewState::EDIT: {
if (editData.grips) {
qreal a = editData.grip[0].width() * 1.0;
bool gripFound = false;
for (int i = 0; i < editData.grips; ++i) {
if (editData.grip[i].adjusted(-a, -a, a, a).contains(editData.startMove)) {
editData.curGrip = Grip(i);
updateGrips();
score()->update();
gripFound = true;
break;
}
}
if (!gripFound) {
editData.element = e;
changeState(ViewState::NORMAL);
mousePressEventNormal(ev);
break;
}
}
else {
if (!editData.element->canvasBoundingRect().contains(editData.startMove)) {
editData.element = e;
changeState(ViewState::NORMAL);
mousePressEventNormal(ev);
}
else {
editData.element->mousePress(editData);
score()->update();
}
}
}
break;
default:
qDebug("mousePressEvent in state %d", int(state));
break;
}
}
//---------------------------------------------------------
// mouseMoveEvent
//---------------------------------------------------------
void ScoreView::mouseMoveEvent(QMouseEvent* me)
{
if (state != ViewState::NOTE_ENTRY && editData.buttons == Qt::NoButton)
return;
// start some drag operations after a minimum of movement:
bool drag = (me->pos() - editData.startMovePixel).manhattanLength() > 4;
switch (state) {
case ViewState::NORMAL:
if (!editData.element && (me->modifiers() & Qt::ShiftModifier))
changeState(ViewState::LASSO);
else if (editData.element && !(me->modifiers())) {
if (!drag)
return;
changeState(ViewState::DRAG_OBJECT);
}
else
changeState(ViewState::DRAG);
break;
case ViewState::NOTE_ENTRY: {
QPointF p = toLogical(me->pos());
QRectF r(shadowNote->canvasBoundingRect());
setShadowNote(p);
r |= shadowNote->canvasBoundingRect();
update(toPhysical(r).adjusted(-2, -2, 2, 2));
}
break;
case ViewState::DRAG:
case ViewState::FOTO_DRAG:
dragScoreView(me);
break;
case ViewState::DRAG_OBJECT:
case ViewState::FOTO_DRAG_OBJECT:
doDragElement(me);
break;
case ViewState::DRAG_EDIT:
case ViewState::FOTO_DRAG_EDIT:
doDragEdit(me);
break;
case ViewState::LASSO:
doDragLasso(me);
break;
case ViewState::EDIT:
if (!drag)
return;
score()->startCmd();
editData.element->startEditDrag(editData);
changeState(ViewState::DRAG_EDIT);
break;
default:
break;
}
update();
}
//---------------------------------------------------------
// mouseDoubleClickEvent
//---------------------------------------------------------
void ScoreView::mouseDoubleClickEvent(QMouseEvent* me)
{
if (state == ViewState::NORMAL) {
QPointF p = toLogical(me->pos());
Element* e = elementNear(p);
if (e && e->isEditable()) {
startEditMode(e);
changeState(ViewState::EDIT);
}
}
}
//---------------------------------------------------------
// CmdContext
//---------------------------------------------------------
struct CmdContext {
Score* s;
CmdContext(Score* _s) : s(_s) { s->startCmd(); }
~CmdContext() { s->endCmd(); }
};
//---------------------------------------------------------
// keyPressEvent
//---------------------------------------------------------
void ScoreView::keyPressEvent(QKeyEvent* ev)
{
if (state != ViewState::EDIT)
return;
CmdContext cmdContext(_score);
editData.key = ev->key();
editData.modifiers = ev->modifiers();
editData.s = ev->text();
if (MScore::debugMode)
qDebug("keyPressEvent key 0x%02x(%c) mod 0x%04x <%s> nativeKey 0x%02x scancode %d",
editData.key, editData.key, int(editData.modifiers), qPrintable(editData.s), ev->nativeVirtualKey(), ev->nativeScanCode());
if (editData.element->isLyrics()) {
if (editKeyLyrics(ev))
return;
}
else if (editData.element->isHarmony()) {
if (editData.key == Qt::Key_Space && !(editData.modifiers & CONTROL_MODIFIER)) {
harmonyBeatsTab(true, editData.modifiers & Qt::ShiftModifier);
return;
}
}
else if (editData.element->isFiguredBass()) {
if (editData.key == Qt::Key_Space && !(editData.modifiers & CONTROL_MODIFIER)) {
figuredBassTab(false, editData.modifiers & Qt::ShiftModifier);
return;
}
}
#ifdef Q_OS_WIN // Japenese IME on Windows needs to know when Contrl/Alt/Shift/CapsLock is pressed while in predit
if (editData.element->isText()) {
Text* text = toText(editData.element);
if (text->cursor(editData)->format()->preedit() && QGuiApplication::inputMethod()->locale().script() == QLocale::JapaneseScript &&
((editData.key == Qt::Key_Control || (editData.modifiers & Qt::ControlModifier)) ||
(editData.key == Qt::Key_Alt || (editData.modifiers & Qt::AltModifier)) ||
(editData.key == Qt::Key_Shift || (editData.modifiers & Qt::ShiftModifier)) ||
(editData.key == Qt::Key_CapsLock))) {
return; // musescore will ignore this key event so that the IME can handle it
}
}
#endif
if (!( (editData.modifiers & Qt::ShiftModifier) && (editData.key == Qt::Key_Backtab) )) {
if (editData.element->edit(editData)) {
if (editData.element->isText())
mscore->textTools()->updateTools(editData);
else
updateGrips();
return;
}
}
QPointF delta;
qreal _spatium = editData.element->spatium();
qreal xval, yval;
if (editData.element->isBeam()) {
xval = 0.25 * _spatium;
if (editData.modifiers & Qt::ControlModifier)
xval = _spatium;
else if (editData.modifiers & Qt::AltModifier)
xval = 4 * _spatium;
}
else {
xval = MScore::nudgeStep * _spatium;
if (editData.modifiers & Qt::ControlModifier)
xval = MScore::nudgeStep10 * _spatium;
else if (editData.modifiers & Qt::AltModifier)
xval = MScore::nudgeStep50 * _spatium;
}
yval = xval;
if (mscore->vRaster()) {
qreal vRaster = _spatium / MScore::vRaster();
if (yval < vRaster)
yval = vRaster;
}
if (mscore->hRaster()) {
qreal hRaster = _spatium / MScore::hRaster();
if (xval < hRaster)
xval = hRaster;
}
// TODO: if raster, then xval/yval should be multiple of raster
switch (editData.key) {
case Qt::Key_Left:
delta = QPointF(-xval, 0);
break;
case Qt::Key_Right:
delta = QPointF(xval, 0);
break;
case Qt::Key_Up:
delta = QPointF(0, -yval);
break;
case Qt::Key_Down:
delta = QPointF(0, yval);
break;
default:
ev->ignore();
return;
}
editData.delta = delta;
editData.hRaster = mscore->hRaster();
editData.vRaster = mscore->vRaster();
if (editData.curGrip != Grip::NO_GRIP && int(editData.curGrip) < editData.grips)
editData.pos = editData.grip[int(editData.curGrip)].center() + delta;
editData.element->startEditDrag(editData);
editData.element->editDrag(editData);
editData.element->endEditDrag(editData);
updateGrips();
}
//---------------------------------------------------------
// keyReleaseEvent
//---------------------------------------------------------
void ScoreView::keyReleaseEvent(QKeyEvent* ev)
{
if (state == ViewState::EDIT) {
auto modifiers = Qt::ControlModifier | Qt::ShiftModifier;
if (editData.element->isText() && ((ev->modifiers() & modifiers) == 0)) {
Text* text = toText(editData.element);
text->endHexState();
ev->accept();
update();
}
}
}
//---------------------------------------------------------
// contextPopup
//---------------------------------------------------------
void ScoreView::contextMenuEvent(QContextMenuEvent* ev)
{
if (state == ViewState::FOTO) {
fotoContextPopup(ev);
return;
}
QPoint gp = ev->globalPos();
editData.startMove = toLogical(ev->pos());
Element* e = elementNear(editData.startMove);
if (e) {
if (!e->selected()) {
// bool control = (ev->modifiers() & Qt::ControlModifier) ? true : false;
// _score->select(e, control ? SelectType::ADD : SelectType::SINGLE, 0);
// editData.element = e;
// select(ev);
}
if (seq)
seq->stopNotes(); // stop now because we dont get a mouseRelease event
objectPopup(gp, e);
}
else {
int staffIdx;
Measure* m = _score->pos2measure(editData.startMove, &staffIdx, 0, 0, 0);
if (m && m->staffLines(staffIdx)->canvasBoundingRect().contains(editData.startMove))
measurePopup(gp, m);
else {
QMenu* popup = new QMenu();
popup->addAction(getAction("edit-style"));
popup->addAction(getAction("page-settings"));
popup->addAction(getAction("load-style"));
_score->update();
popup->popup(gp);
}
}
ev->accept();
}
//---------------------------------------------------------
// escapeCmd
//---------------------------------------------------------
void ScoreView::escapeCmd()
{
switch (state) {
case ViewState::EDIT:
changeState(ViewState::NORMAL);
break;
case ViewState::FOTO:
case ViewState::NOTE_ENTRY:
case ViewState::PLAY:
changeState(ViewState::NORMAL);
break;
case ViewState::NORMAL:
_score->deselectAll();
update();
break;
default:
break;
}
}
//---------------------------------------------------------
// stateName
//---------------------------------------------------------
static const char* stateName(ViewState s)
{
std::string p;
switch (s) {
case ViewState::NORMAL: p = "NORMAL"; break;
case ViewState::DRAG: p = "DRAG"; break;
case ViewState::DRAG_OBJECT: p = "DRAG_OBJECT"; break;
case ViewState::EDIT: p = "EDIT"; break;
case ViewState::DRAG_EDIT: p = "DRAG_EDIT"; break;
case ViewState::LASSO: p = "LASSO"; break;
case ViewState::NOTE_ENTRY: p = "NOTE_ENTRY"; break;
case ViewState::PLAY: p = "PLAY"; break;
case ViewState::ENTRY_PLAY: p = "ENTRY_PLAY"; break;
case ViewState::FOTO: p = "FOTO"; break;
case ViewState::FOTO_DRAG: p = "FOTO_DRAG"; break;
case ViewState::FOTO_DRAG_EDIT: p = "FOTO_DRAG_EDIT"; break;
case ViewState::FOTO_DRAG_OBJECT: p = "FOTO_DRAG_OBJECT"; break;
case ViewState::FOTO_LASSO: p = "FOTO_LASSO"; break;
}
return p.c_str();
}
//---------------------------------------------------------
// seqStopped
//---------------------------------------------------------
void ScoreView::seqStopped()
{
changeState(ViewState::NORMAL);
}
//---------------------------------------------------------
// changeState
//---------------------------------------------------------
void ScoreView::changeState(ViewState s)
{
qDebug("changeState %s -> %s", stateName(state), stateName(s));
// if (state == ViewState::EDIT && s == ViewState::EDIT) {
// startEdit();
// return;
// }
if (s == ViewState::PLAY && !seq)
return;
if (s == state)
return;
//
// end current state
//
switch (state) {
case ViewState::NOTE_ENTRY:
endNoteEntry();
break;
case ViewState::DRAG:
break;
case ViewState::FOTO_DRAG_OBJECT:
for (Element* e : _score->selection().elements()) {
e->endDrag(editData);
e->triggerLayout();
}
setDropTarget(0); // this also resets dropAnchor
_score->endCmd();
break;
case ViewState::DRAG_OBJECT:
endDrag();
break;
case ViewState::FOTO_DRAG_EDIT:
case ViewState::DRAG_EDIT:
endDragEdit();
break;
case ViewState::FOTO:
if (s != ViewState::FOTO_DRAG && s != ViewState::FOTO_DRAG_EDIT && s != ViewState::FOTO_DRAG_OBJECT)
stopFotomode();
break;
case ViewState::LASSO:
endLasso();
break;
case ViewState::PLAY:
seq->stop();
break;
default:
break;
}
//
// start new state
//
switch (s) {
case ViewState::NORMAL:
if (state == ViewState::EDIT)
endEdit();
setCursor(QCursor(Qt::ArrowCursor));
break;
case ViewState::DRAG:
setCursor(QCursor(Qt::SizeAllCursor));
break;
case ViewState::FOTO_DRAG_OBJECT:
_score->select(_foto);
// fall through
case ViewState::DRAG_OBJECT:
setCursor(QCursor(Qt::ArrowCursor));
startDrag();
break;
case ViewState::FOTO_DRAG:
setCursor(QCursor(Qt::SizeAllCursor));
break;
case ViewState::NOTE_ENTRY:
setCursor(QCursor(Qt::ArrowCursor));
startNoteEntry();
break;
case ViewState::DRAG_EDIT:
case ViewState::FOTO_DRAG_EDIT:
setCursor(QCursor(Qt::ArrowCursor));
break;
case ViewState::FOTO:
setCursor(QCursor(Qt::ArrowCursor));
if (state != ViewState::FOTO_DRAG && state != ViewState::FOTO_DRAG_EDIT && state != ViewState::FOTO_DRAG_OBJECT)
startFotomode();
if (state == ViewState::FOTO_DRAG_OBJECT)
startEdit();
break;
case ViewState::EDIT:
if (state != ViewState::DRAG_EDIT)
startEdit();
break;
case ViewState::LASSO:
break;
case ViewState::PLAY:
seq->start();
break;
case ViewState::ENTRY_PLAY:
case ViewState::FOTO_LASSO:
break;
}
state = s;
mscore->changeState(mscoreState());
}
} // namespace Ms