MuseScore/mscore/chordview.cpp
2013-05-13 19:43:59 +02:00

608 lines
19 KiB
C++

//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2002-2012 Werner Schweer
//
// 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 "chordview.h"
#include "piano.h"
#include "libmscore/chord.h"
#include "libmscore/note.h"
#include "libmscore/noteevent.h"
#include "preferences.h"
#include "musescore.h"
#include "libmscore/mscore.h"
#include "libmscore/score.h"
namespace Ms {
static const int CHORD_MAP_OFFSET = 50;
static const int grip = 7;
//---------------------------------------------------------
// GripItem
//---------------------------------------------------------
GripItem::GripItem(int type, ChordView* v)
: QGraphicsRectItem()
{
_gripType = type;
_view = v;
setFlags(flags() | ItemIsSelectable | ItemIsFocusable
| ItemIgnoresTransformations | ItemIsMovable);
setRect(-grip, -grip, grip*2, grip*2);
setPos(0, 0);
_event = 0;
}
//---------------------------------------------------------
// paint
//---------------------------------------------------------
void GripItem::paint(QPainter* p, const QStyleOptionGraphicsItem*, QWidget*)
{
QPen pen(MScore::defaultColor);
pen.setWidthF(2.0);
p->setPen(pen);
p->setBrush(isSelected() ? QBrush(Qt::blue) : Qt::NoBrush);
p->drawRect(-grip, -grip, grip*2, grip*2);
}
//---------------------------------------------------------
// mouseMoveEvent
//---------------------------------------------------------
void GripItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
QPointF np = mapToScene(event->pos());
if (_event) {
NoteEvent* ne = _event->event();
if (_gripType == 0) {
qreal x = ChordView::pix2pos(np.x());
int x2 = ne->ontime() + ne->len();
ne->setOntime(x);
ne->setLen(x2 - x);
_event->setPos(ChordView::pos2pix(x), _event->pos().y());
setPos(0.0, pos().y());
GripItem* rg = _view->rightGrip();
rg->setPos(x2-x, rg->pos().y());
}
else {
qreal x = np.x() - _event->pos().x();
setPos(x, pos().y());
_event->event()->setLen(x);
}
_view->setDirty(true);
_event->update();
}
}
//---------------------------------------------------------
// ChordItem
//---------------------------------------------------------
ChordItem::ChordItem(ChordView* v, Note* n, NoteEvent* e)
: QGraphicsRectItem(), _view(v), _note(n), _event(e)
{
setFlags(flags() | ItemIsSelectable | ItemIsMovable);
_current = false;
int pitch = _event->pitch() + n->pitch();
int len = _event->len();
setRect(0, 0, len, keyHeight/2);
setBrush(QBrush());
setSelected(n->selected());
setData(0, QVariant::fromValue<void*>(n));
setPos(ChordView::pos2pix(e->ontime()), ChordView::pitch2y(pitch) + keyHeight / 4);
}
//---------------------------------------------------------
// mouseMoveEvent
//---------------------------------------------------------
void ChordItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
QPointF np = mapToScene(event->pos());
int pitch = ChordView::y2pitch(np.y());
int y = ChordView::pitch2y(pitch);
setPos(pos().x(), y + keyHeight / 4);
_event->setPitch(pitch - _note->pitch());
}
//---------------------------------------------------------
// paint
//---------------------------------------------------------
void ChordItem::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*)
{
int len = _event->len();
painter->setPen(pen());
if (_view->curNote() == _note)
painter->setBrush(_current ? Qt::yellow : Qt::blue);
else
painter->setBrush(Qt::gray);
painter->drawRect(0.0, 0.0, len, keyHeight / 2);
}
//---------------------------------------------------------
// setCurrent
//---------------------------------------------------------
void ChordItem::setCurrent(bool val)
{
_current = val;
update();
}
//---------------------------------------------------------
// ChordView
//---------------------------------------------------------
ChordView::ChordView()
: QGraphicsView()
{
setScene(new QGraphicsScene);
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
setResizeAnchor(QGraphicsView::AnchorUnderMouse);
// setMouseTracking(true);
setRubberBandSelectionMode(Qt::IntersectsItemBoundingRect);
setDragMode(QGraphicsView::RubberBandDrag);
magStep = 2;
chord = 0;
_curNote = 0;
_evenGrid = true;
lg = 0;
rg = 0;
curEvent = 0;
ticks = 1000;
_dirty = false;
scale(6.0, 1.0);
QAction* a = getAction("delete");
addAction(a);
connect(a, SIGNAL(triggered()), SLOT(deleteItem()));
connect(scene(), SIGNAL(selectionChanged()), SLOT(selectionChanged()));
}
//---------------------------------------------------------
// drawBackground
//---------------------------------------------------------
void ChordView::drawBackground(QPainter* p, const QRectF& r)
{
if (chord == 0)
return;
QRectF r1(-1000000.0, 0.0, 1000000.0+CHORD_MAP_OFFSET, 1000000.0);
QRectF r2(ticks + CHORD_MAP_OFFSET, 0.0, 1000000.0, 1000000.0);
QRectF r3(-1000000.0, 127*keyHeight, 1000000.0+CHORD_MAP_OFFSET, keyHeight);
QRectF r4(ticks + CHORD_MAP_OFFSET, 127*keyHeight, 1000000.0, keyHeight);
QColor bg(0x71, 0x8d, 0xbe);
QColor bg1 = bg.darker(150);
QColor bg2 = bg.lighter(150);
QColor bg3 = bg.darker(75);
p->fillRect(r, bg);
p->fillRect(r.intersected(r1), bg1);
p->fillRect(r.intersected(r2), bg1);
foreach (const Note* n, chord->notes()) {
p->fillRect(QRect(CHORD_MAP_OFFSET, (127 - n->pitch()) * keyHeight,
1000, keyHeight), bg2);
}
p->fillRect(r.intersected(r3), bg3);
p->fillRect(r.intersected(r4), bg3);
//---------------------------------------------------
// draw horizontal grid lines
//---------------------------------------------------
qreal y1 = r.y();
qreal y2 = y1 + r.height();
qreal x1 = r.x();
qreal x2 = x1 + r.width();
int key = floor(y1 / keyHeight);
qreal y = key * keyHeight;
for (; key < 127; ++key, y += keyHeight) {
if (y < y1)
continue;
if (y > y2)
break;
p->setPen(QPen((key % 6) == 5 ? Qt::lightGray : Qt::gray));
p->drawLine(QLineF(x1, y, x2, y));
}
//---------------------------------------------------
// draw raster
//---------------------------------------------------
if (_evenGrid) {
for (int x = 0; x <= 1000; x += 50) {
if (x % 250)
p->setPen(Qt::lightGray);
else
p->setPen(Qt::black);
p->drawLine(pos2pix(x), y1, pos2pix(x), y2);
}
}
else {
double step1 = 1000.0 / 3;
double step2 = step1 / 4;
for (int i = 0; i < 3; ++i) {
p->setPen(Qt::lightGray);
for (int k = 1; k < 4; ++k) {
int x = lrint(i * step1 + k * step2);
p->drawLine(pos2pix(x), y1, pos2pix(x), y2);
}
p->setPen(Qt::black);
int x = lrint(i * step1);
p->drawLine(pos2pix(x), y1, pos2pix(x), y2);
}
}
}
//---------------------------------------------------------
// setChord
//---------------------------------------------------------
void ChordView::setChord(Chord* c)
{
chord = c;
_dirty = false;
_pos = 0;
_locator = 0;
scene()->blockSignals(true);
scene()->clear();
locatorLine = new QGraphicsLineItem(QLineF(0.0, 0.0, 0.0, keyHeight * 127.0 * 5));
QPen pen(Qt::red);
pen.setWidth(2);
locatorLine->setPen(pen);
locatorLine->setZValue(1000); // set stacking order
locatorLine->setFlag(QGraphicsItem::ItemIgnoresTransformations, true);
scene()->addItem(locatorLine);
curEvent = 0;
_curNote = 0;
foreach(Note* note, c->notes()) {
if (note->selected() && _curNote == 0)
_curNote = note;
int n = note->playEvents().size();
for (int i = 0; i < n; ++i) {
NoteEvent* e = &note->playEvents()[i];
ChordItem* item = new ChordItem(this, note, e);
if (_curNote == note && curEvent == 0)
curEvent = item;
scene()->addItem(item);
}
}
lg = new GripItem(0, this);
rg = new GripItem(1, this);
lg->setZValue(100);
rg->setZValue(101);
lg->setVisible(false);
rg->setVisible(false);
scene()->addItem(lg);
scene()->addItem(rg);
scene()->blockSignals(false);
setCurItem(curEvent);
// selectionChanged();
scene()->setSceneRect(0.0, 0.0, double(ticks + CHORD_MAP_OFFSET * 2), keyHeight * 127);
moveLocator();
//
// move to something interesting
//
QList<QGraphicsItem*> items = scene()->selectedItems();
QRectF boundingRect;
foreach(QGraphicsItem* item, items) {
Note* note = static_cast<Note*>(item->data(0).value<void*>());
if (note)
boundingRect |= item->mapToScene(item->boundingRect()).boundingRect();
}
centerOn(boundingRect.center());
scene()->update();
}
//---------------------------------------------------------
// moveLocator
//---------------------------------------------------------
void ChordView::moveLocator()
{
if (_locator >= 0) {
locatorLine->setVisible(true);
qreal x = qreal(pos2pix(_locator));
locatorLine->setPos(QPointF(x, 0.0));
}
else
locatorLine->setVisible(false);
}
//---------------------------------------------------------
// wheelEvent
//---------------------------------------------------------
void ChordView::wheelEvent(QWheelEvent* event)
{
int step = event->delta() / 120;
double xmag = transform().m11();
double ymag = transform().m22();
if (event->modifiers() == Qt::ControlModifier) {
if (step > 0) {
for (int i = 0; i < step; ++i) {
if (xmag > 10.0)
break;
scale(1.1, 1.0);
xmag *= 1.1;
}
}
else {
for (int i = 0; i < -step; ++i) {
if (xmag < 0.001)
break;
scale(.9, 1.0);
xmag *= .9;
}
}
emit magChanged(xmag, ymag);
int tpix = 1000 * xmag;
magStep = -5;
if (tpix <= 4000)
magStep = -4;
if (tpix <= 2000)
magStep = -3;
if (tpix <= 1000)
magStep = -2;
if (tpix <= 500)
magStep = -1;
if (tpix <= 128)
magStep = 0;
if (tpix <= 64)
magStep = 1;
if (tpix <= 32)
magStep = 2;
if (tpix <= 16)
magStep = 3;
if (tpix <= 8)
magStep = 4;
if (tpix <= 4)
magStep = 5;
if (tpix <= 2)
magStep = 6;
//
// if xpos <= 0, then the scene is centered
// there is no scroll bar anymore sending
// change signals, so we have to do it here:
//
double xpos = -(mapFromScene(QPointF()).x());
if (xpos <= 0)
emit xposChanged(xpos);
}
else if (event->modifiers() == Qt::ShiftModifier) {
QWheelEvent we(event->pos(), event->delta(), event->buttons(), 0, Qt::Horizontal);
QGraphicsView::wheelEvent(&we);
}
else if (event->modifiers() == 0) {
QGraphicsView::wheelEvent(event);
}
else if (event->modifiers() == (Qt::ShiftModifier | Qt::ControlModifier)) {
if (step > 0) {
for (int i = 0; i < step; ++i) {
if (ymag > 3.0)
break;
scale(1.0, 1.1);
ymag *= 1.1;
}
}
else {
for (int i = 0; i < -step; ++i) {
if (ymag < 0.4)
break;
scale(1.0, .9);
ymag *= .9;
}
}
emit magChanged(xmag, ymag);
}
}
//---------------------------------------------------------
// y2pitch
//---------------------------------------------------------
int ChordView::y2pitch(int y)
{
return 127 - (y / keyHeight);
}
//---------------------------------------------------------
// pitch2y
//---------------------------------------------------------
int ChordView::pitch2y(int pitch)
{
return keyHeight * (127 - pitch);
}
//---------------------------------------------------------
// mousePressEvent
//---------------------------------------------------------
void ChordView::mousePressEvent(QMouseEvent* event)
{
if (event->modifiers() & Qt::ControlModifier) {
QPointF p(mapToScene(event->pos()));
int pitch = y2pitch(int(p.y()));
int tick = int(p.x()) - CHORD_MAP_OFFSET;
int ticks = 1000 - tick;
foreach(const NoteEvent& e, _curNote->playEvents()) {
if (e.pitch() != pitch)
continue;
if (tick >= e.ontime() && tick < e.offtime()) {
return; // cannot place an event here
}
int nticks = e.ontime() - tick;
if (nticks > 0)
ticks = qMin(ticks, nticks);
}
NoteEvent ne;
ne.setPitch(pitch);
ne.setOntime(tick);
ne.setLen(ticks);
_curNote->playEvents().append(ne);
NoteEvent* pne = &_curNote->playEvents()[_curNote->playEvents().size()-1];
ChordItem* item = new ChordItem(this, _curNote, pne);
scene()->addItem(item);
setCurItem(item);
_dirty = true;
}
QGraphicsView::mousePressEvent(event);
}
//---------------------------------------------------------
// mouseMoveEvent
//---------------------------------------------------------
void ChordView::mouseMoveEvent(QMouseEvent* event)
{
QPointF p(mapToScene(event->pos()));
int pitch = y2pitch(int(p.y()));
emit pitchChanged(pitch);
int tick = int(p.x()) - CHORD_MAP_OFFSET;
if (tick < 0) {
tick = 0;
_pos = -1;
}
else
_pos = tick;
emit posChanged(_pos);
QGraphicsView::mouseMoveEvent(event);
}
//---------------------------------------------------------
// leaveEvent
//---------------------------------------------------------
void ChordView::leaveEvent(QEvent* event)
{
emit pitchChanged(-1);
emit posChanged(-1);
_pos = -1;
QGraphicsView::leaveEvent(event);
}
//---------------------------------------------------------
// ensureVisible
//---------------------------------------------------------
void ChordView::ensureVisible(int tick)
{
tick += CHORD_MAP_OFFSET;
QPointF pt = mapToScene(0, height() / 2);
QGraphicsView::ensureVisible(qreal(tick), pt.y(), 240.0, 1.0);
}
//---------------------------------------------------------
// pos2pix
//---------------------------------------------------------
int ChordView::pos2pix(int pos)
{
return pos + CHORD_MAP_OFFSET;
}
//---------------------------------------------------------
// pix2pos
//---------------------------------------------------------
int ChordView::pix2pos(int pix)
{
return pix - CHORD_MAP_OFFSET;
}
//---------------------------------------------------------
// selectionChanged
//---------------------------------------------------------
void ChordView::selectionChanged()
{
QList<QGraphicsItem*> items = scene()->selectedItems();
if (items.isEmpty())
setCurItem(0);
else {
QGraphicsItem* item = items[0];
if (item->type() == ChordTypeItem) {
ChordItem* ci = static_cast<ChordItem*>(item);
setCurItem(ci);
}
}
}
//---------------------------------------------------------
// deleteItem
//---------------------------------------------------------
void ChordView::deleteItem()
{
QList<QGraphicsItem*> items = scene()->selectedItems();
foreach(QGraphicsItem* item, items) {
if (item->type() == ChordTypeItem) {
ChordItem* ci = static_cast<ChordItem*>(item);
if (curEvent == ci)
setCurItem(0);
NoteEvent* event = ci->event();
ci->note()->playEvents().removeOne(*event);
scene()->removeItem(item);
delete item;
_dirty = true;
}
}
}
//---------------------------------------------------------
// setCurItem
//---------------------------------------------------------
void ChordView::setCurItem(ChordItem* item)
{
if (curEvent)
curEvent->setCurrent(false);
curEvent = item;
bool visible = item != 0;
lg->setVisible(visible);
rg->setVisible(visible);
lg->setEvent(item);
rg->setEvent(item);
lg->setParentItem(item);
rg->setParentItem(item);
if (visible) {
lg->setPos(0, keyHeight / 4);
rg->setPos(item->event()->len(), keyHeight / 4);
item->setCurrent(true);
if (item->note() != _curNote) {
_curNote = item->note();
_curNote->score()->select(_curNote);
_curNote->score()->update();
}
}
}
}