608 lines
19 KiB
C++
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 = ¬e->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();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|