diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 18:37:05 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 18:37:05 +0000 |
commit | 145364a8af6a1fec06556221e66d4b724a62fc9a (patch) | |
tree | 53bd71a544008c518034f208d64c932dc2883f50 /src/gui/editors/matrix | |
download | rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.tar.gz rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.zip |
Added old abandoned KDE3 version of the RoseGarden MIDI tool
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/rosegarden@1097595 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/gui/editors/matrix')
34 files changed, 9076 insertions, 0 deletions
diff --git a/src/gui/editors/matrix/MatrixCanvasView.cpp b/src/gui/editors/matrix/MatrixCanvasView.cpp new file mode 100644 index 0000000..c92b4aa --- /dev/null +++ b/src/gui/editors/matrix/MatrixCanvasView.cpp @@ -0,0 +1,302 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#include "MatrixCanvasView.h" + +#include "base/SnapGrid.h" +#include "gui/general/MidiPitchLabel.h" +#include "gui/general/RosegardenCanvasView.h" +#include "MatrixElement.h" +#include "MatrixStaff.h" +#include "QCanvasMatrixRectangle.h" +#include "QCanvasMatrixDiamond.h" +#include <qcanvas.h> +#include <qpoint.h> +#include <qwidget.h> +#include "misc/Debug.h" + + + +namespace Rosegarden +{ + +MatrixCanvasView::MatrixCanvasView(MatrixStaff& staff, + SnapGrid *snapGrid, + bool drumMode, + QCanvas *viewing, QWidget *parent, + const char *name, WFlags f) + : RosegardenCanvasView(viewing, parent, name, f), + m_staff(staff), + m_snapGrid(snapGrid), + m_drumMode(drumMode), + m_previousEvTime(0), + m_previousEvPitch(0), + m_mouseWasPressed(false), + m_ignoreClick(false), + m_smoothModifier(Qt::ShiftButton), + m_lastSnap(SnapGrid::SnapToBeat), + m_isSnapTemporary(false) +{ + viewport()->setMouseTracking(true); +} + +MatrixCanvasView::~MatrixCanvasView() +{} + +void MatrixCanvasView::contentsMousePressEvent(QMouseEvent* e) +{ + QPoint p = inverseMapPoint(e->pos()); + + updateGridSnap(e); + + MATRIX_DEBUG << "MatrixCanvasView::contentsMousePressEvent: snap time is " << m_snapGrid->getSnapTime(double(p.x())) << endl; + + timeT evTime; + + if (m_drumMode) { + evTime = m_snapGrid->snapX(p.x(), SnapGrid::SnapEither); + MATRIX_DEBUG << "MatrixCanvasView: drum mode: snapEither " << p.x() << " -> " << evTime << endl; + } else { + evTime = m_snapGrid->snapX(p.x(), SnapGrid::SnapLeft); + MATRIX_DEBUG << "MatrixCanvasView: normal mode: snapLeft " << p.x() << " -> " << evTime << endl; + } + + int evPitch = m_staff.getHeightAtCanvasCoords(p.x(), p.y()); + + timeT emTime = m_staff.getSegment().getEndMarkerTime(); + if (evTime > emTime) + evTime = emTime; + timeT esTime = m_staff.getSegment().getStartTime(); + if (evTime < esTime) + evTime = esTime; + +// std::cerr << "MatrixCanvasView::contentsMousePressEvent() at pitch " +// << evPitch << ", time " << evTime << std::endl; + + QCanvasItemList itemList = canvas()->collisions(p); + QCanvasItemList::Iterator it; + MatrixElement* mel = 0; + QCanvasItem* activeItem = 0; + + for (it = itemList.begin(); it != itemList.end(); ++it) { + + QCanvasItem *item = *it; + + QCanvasMatrixRectangle *mRect = 0; + + if (item->active()) { + activeItem = item; + break; + } + + if ((mRect = dynamic_cast<QCanvasMatrixRectangle*>(item))) { + +// std::cerr << "MatrixCanvasView: looking at element with rect " << mRect->rect().x() << "," << mRect->rect().y() << " (" << mRect->rect().width() << "x" << mRect->rect().height() << ")" << std::endl; + +// std::cerr << "MatrixCanvasView: point is " << p.x() << "," << p.y()<< std::endl; + + QRect rect = mRect->rect(); + if (dynamic_cast<QCanvasMatrixDiamond*>(mRect)) { + rect = QRect(rect.x() - rect.height()/2, + rect.y(), + rect.width(), + rect.height()); + } + +// std::cerr << "MatrixCanvasView: adjusted rect " << rect.x() << "," << rect.y() << " (" << rect.width() << "x" << rect.height() << ")" << std::endl; + + // QCanvas::collisions() can be a bit optimistic and report + // items which are close to the point but not actually under it. + // So a little sanity check helps. + if (!rect.contains(p, true)) continue; + + mel = &(mRect->getMatrixElement()); +// std::cerr << "MatrixCanvasView::contentsMousePressEvent: collision with an existing matrix element" << std::endl; + break; + } + } + + if (activeItem) { // active item takes precedence over notation elements + emit activeItemPressed(e, activeItem); + m_mouseWasPressed = true; + return ; + } + + emit mousePressed(evTime, evPitch, e, mel); + m_mouseWasPressed = true; + + // Ignore click if it was above the staff and not + // on an active item + // + if (!m_staff.containsCanvasCoords(p.x(), p.y()) && !activeItem) + m_ignoreClick = true; +} + +void MatrixCanvasView::contentsMouseMoveEvent(QMouseEvent* e) +{ + QPoint p = inverseMapPoint(e->pos()); + /* + if (m_snapGrid->getSnapTime(double(p.x()))) + m_lastSnap = m_snapGrid->getSnapTime(double(p.x())); + */ + updateGridSnap(e); + + if (m_ignoreClick) + return ; + + timeT evTime; + if (m_drumMode) { + evTime = m_snapGrid->snapX(p.x(), SnapGrid::SnapEither); + } else { + evTime = m_snapGrid->snapX(p.x(), SnapGrid::SnapLeft); + } + + int evPitch = m_staff.getHeightAtCanvasCoords(p.x(), p.y()); + + timeT emTime = m_staff.getSegment().getEndMarkerTime(); + if (evTime > emTime) + evTime = emTime; + + timeT stTime = m_staff.getSegment().getStartTime(); + if (evTime < stTime) + evTime = stTime; + + if (evTime != m_previousEvTime) { + emit hoveredOverAbsoluteTimeChanged(evTime); + m_previousEvTime = evTime; + } + + QCanvasItemList itemList = canvas()->collisions(p); + MatrixElement* mel = 0; + + for (QCanvasItemList::iterator it = itemList.begin(); + it != itemList.end(); ++it) { + + QCanvasItem *item = *it; + QCanvasMatrixRectangle *mRect = 0; + + if ((mRect = dynamic_cast<QCanvasMatrixRectangle*>(item))) { + if (!mRect->rect().contains(p, true)) + continue; + mel = &(mRect->getMatrixElement()); + MATRIX_DEBUG << "have element" << endl; + break; + } + } + + if (!m_mouseWasPressed && // if mouse pressed, leave this to the tool + (evPitch != m_previousEvPitch || mel)) { + MidiPitchLabel label(evPitch); + if (mel) { + emit hoveredOverNoteChanged(evPitch, true, + mel->event()->getAbsoluteTime()); + } else { + emit hoveredOverNoteChanged(evPitch, false, 0); + } + m_previousEvPitch = evPitch; + } + +// if (m_mouseWasPressed) + emit mouseMoved(evTime, evPitch, e); + +} + +void MatrixCanvasView::contentsMouseDoubleClickEvent (QMouseEvent* e) +{ + QPoint p = inverseMapPoint(e->pos()); + + if (!m_staff.containsCanvasCoords(p.x(), p.y())) { + m_ignoreClick = true; + return ; + } + + contentsMousePressEvent(e); +} + +void MatrixCanvasView::contentsMouseReleaseEvent(QMouseEvent* e) +{ + QPoint p = inverseMapPoint(e->pos()); + + if (m_ignoreClick) { + m_ignoreClick = false; + return ; + } + + timeT evTime; + if (m_drumMode) { + evTime = m_snapGrid->snapX(p.x(), SnapGrid::SnapEither); + } else { + evTime = m_snapGrid->snapX(p.x(), SnapGrid::SnapLeft); + } + + int evPitch = m_staff.getHeightAtCanvasCoords(p.x(), p.y()); + + timeT emTime = m_staff.getSegment().getEndMarkerTime(); + if (evTime > emTime) + evTime = emTime; + + emit mouseReleased(evTime, evPitch, e); + m_mouseWasPressed = false; +} + +void MatrixCanvasView::slotExternalWheelEvent(QWheelEvent* e) +{ + wheelEvent(e); +} + +void MatrixCanvasView::updateGridSnap(QMouseEvent *e) +{ + Qt::ButtonState bs = e->state(); + + // MATRIX_DEBUG << "MatrixCanvasView::updateGridSnap : bs = " + // << bs << " - sm = " << getSmoothModifier() << ", is temporary " << m_isSnapTemporary << ", saved is " << m_lastSnap << endl; + + if (bs & getSmoothModifier()) { + + if (!m_isSnapTemporary) { + m_lastSnap = m_snapGrid->getSnapSetting(); + } + m_snapGrid->setSnapTime(SnapGrid::NoSnap); + m_isSnapTemporary = true; + + } else if (m_isSnapTemporary) { + + m_snapGrid->setSnapTime(m_lastSnap); + m_isSnapTemporary = false; + } +} + +void MatrixCanvasView::enterEvent(QEvent *e) +{ + emit mouseEntered(); +} + +void MatrixCanvasView::leaveEvent(QEvent *e) +{ + emit mouseLeft(); +} + +} +#include "MatrixCanvasView.moc" diff --git a/src/gui/editors/matrix/MatrixCanvasView.h b/src/gui/editors/matrix/MatrixCanvasView.h new file mode 100644 index 0000000..2ec4c7e --- /dev/null +++ b/src/gui/editors/matrix/MatrixCanvasView.h @@ -0,0 +1,162 @@ + +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_MATRIXCANVASVIEW_H_ +#define _RG_MATRIXCANVASVIEW_H_ + +#include "gui/general/RosegardenCanvasView.h" +#include "base/Event.h" + + +class QWidget; +class QWheelEvent; +class QMouseEvent; +class QCanvasItem; +class QCanvas; + + +namespace Rosegarden +{ + +class SnapGrid; +class MatrixStaff; +class MatrixElement; + + +class MatrixCanvasView : public RosegardenCanvasView +{ + Q_OBJECT + +public: + MatrixCanvasView(MatrixStaff&, + SnapGrid *, + bool drumMode, + QCanvas *viewing, + QWidget *parent=0, const char *name=0, WFlags f=0); + + ~MatrixCanvasView(); + + void setSmoothModifier(Qt::ButtonState s) { m_smoothModifier = s; } + Qt::ButtonState getSmoothModifier() { return m_smoothModifier; } + +signals: + + /** + * Emitted when the user clicks on a QCanvasItem which is active + * + * @see QCanvasItem#setActive + */ + void activeItemPressed(QMouseEvent*, + QCanvasItem* item); + + /** + * Emitted when the mouse cursor moves to a different height + * on the staff. Returns the new pitch. + */ + void hoveredOverNoteChanged(int evPitch, bool haveEvent, + timeT evTime); + + /** + * Emitted when the mouse cursor moves to a note which is at a + * different time + * + * \a time is set to the absolute time of the note the cursor is + * hovering on + */ + void hoveredOverAbsoluteTimeChanged(unsigned int time); + + void mousePressed(timeT time, int pitch, + QMouseEvent*, MatrixElement*); + + void mouseMoved(timeT time, int pitch, QMouseEvent*); + + void mouseReleased(timeT time, int pitch, QMouseEvent*); + + void mouseEntered(); + void mouseLeft(); + +public slots: + void slotExternalWheelEvent(QWheelEvent*); + +protected: + /** + * Callback for a mouse button press event in the canvas + */ + virtual void contentsMousePressEvent(QMouseEvent*); + + /** + * Callback for a mouse move event in the canvas + */ + virtual void contentsMouseMoveEvent(QMouseEvent*); + + /** + * Callback for a mouse button release event in the canvas + */ + virtual void contentsMouseReleaseEvent(QMouseEvent*); + + /** + * Callback for a mouse double-click event in the canvas + * + * NOTE: a double click event is always preceded by a mouse press + * event + */ + virtual void contentsMouseDoubleClickEvent(QMouseEvent*); + + virtual void enterEvent(QEvent *); + virtual void leaveEvent(QEvent *); + + /** + * Update the value of snap grid according to the button's state + * + * If the button was pressed with the 'smooth' modifier, set the + * grid so it won't snap time. + * + * @see #setSmoothModifier + * @see #getSmoothModifier + */ + void updateGridSnap(QMouseEvent *e); + + //--------------- Data members --------------------------------- + + MatrixStaff &m_staff; + SnapGrid *m_snapGrid; + bool m_drumMode; + + timeT m_previousEvTime; + int m_previousEvPitch; + + bool m_mouseWasPressed; + bool m_ignoreClick; + + Qt::ButtonState m_smoothModifier; + timeT m_lastSnap; + bool m_isSnapTemporary; +}; + + + +} + +#endif diff --git a/src/gui/editors/matrix/MatrixElement.cpp b/src/gui/editors/matrix/MatrixElement.cpp new file mode 100644 index 0000000..1101284 --- /dev/null +++ b/src/gui/editors/matrix/MatrixElement.cpp @@ -0,0 +1,160 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#include "MatrixElement.h" +#include "misc/Debug.h" + +#include "base/Event.h" +#include "base/NotationTypes.h" +#include "base/ViewElement.h" +#include "gui/general/GUIPalette.h" +#include "QCanvasMatrixDiamond.h" +#include "QCanvasMatrixRectangle.h" +#include <qbrush.h> +#include <qcanvas.h> +#include <qcolor.h> + + +namespace Rosegarden +{ + +MatrixElement::MatrixElement(Event *event, bool drum) : + ViewElement(event), + m_canvasRect(drum ? + new QCanvasMatrixDiamond(*this, 0) : + new QCanvasMatrixRectangle(*this, 0)), + m_overlapRectangles(NULL) +{ + // MATRIX_DEBUG << "new MatrixElement " + // << this << " wrapping " << event << endl; +} + +MatrixElement::~MatrixElement() +{ + // MATRIX_DEBUG << "MatrixElement " << this << "::~MatrixElement() wrapping " + // << event() << endl; + + m_canvasRect->hide(); + delete m_canvasRect; + + removeOverlapRectangles(); +} + +void MatrixElement::setCanvas(QCanvas* c) +{ + if (!m_canvasRect->canvas()) { + + m_canvasRect->setCanvas(c); + + // We set this by velocity now (matrixstaff.cpp) + // + //m_canvasRect->setBrush(RosegardenGUIColours::MatrixElementBlock); + + m_canvasRect->setPen(GUIPalette::getColour(GUIPalette::MatrixElementBorder)); + m_canvasRect->show(); + } +} + +bool MatrixElement::isNote() const +{ + return event()->isa(Note::EventType); +} + +void MatrixElement::drawOverlapRectangles() +{ + if (m_overlapRectangles) removeOverlapRectangles(); + + QRect elRect = m_canvasRect->rect(); + QCanvasItemList + itemList = m_canvasRect->canvas()->collisions(elRect); + QCanvasItemList::Iterator it; + MatrixElement* mel = 0; + + + for (it = itemList.begin(); it != itemList.end(); ++it) { + + QCanvasMatrixRectangle *mRect = 0; + if ((mRect = dynamic_cast<QCanvasMatrixRectangle*>(*it))) { + + // Element does'nt collide with itself + if (mRect == m_canvasRect) continue; + + QRect rect = mRect->rect() & elRect; + if (!rect.isEmpty()) { + if (!m_overlapRectangles) { + m_overlapRectangles = new OverlapRectangles(); + } + + QCanvasRectangle * + overlap = new QCanvasRectangle(rect, m_canvasRect->canvas()); + overlap->setBrush(GUIPalette::getColour(GUIPalette::MatrixOverlapBlock)); + overlap->setZ(getCanvasZ() + 1); + overlap->show(); + m_overlapRectangles->push_back(overlap); + } + } + } +} + +void MatrixElement::redrawOverlaps(QRect rect) +{ + QCanvasItemList + itemList = m_canvasRect->canvas()->collisions(rect); + QCanvasItemList::Iterator it; + MatrixElement* mel = 0; + + for (it = itemList.begin(); it != itemList.end(); ++it) { + QCanvasMatrixRectangle *mRect = 0; + if ((mRect = dynamic_cast<QCanvasMatrixRectangle*>(*it))) { + mRect->getMatrixElement().drawOverlapRectangles(); + } + } +} + +void MatrixElement::removeOverlapRectangles() +{ + if (!m_overlapRectangles) return; + + OverlapRectangles::iterator it; + for (it = m_overlapRectangles->begin(); it != m_overlapRectangles->end(); ++it) { + (*it)->hide(); + delete *it; + } + + delete m_overlapRectangles; + m_overlapRectangles = NULL; +} + +bool MatrixElement::getVisibleRectangle(QRect &rectangle) +{ + if (m_canvasRect && m_canvasRect->isVisible()) { + rectangle = m_canvasRect->rect(); + return true; + } + return false; +} + + +} diff --git a/src/gui/editors/matrix/MatrixElement.h b/src/gui/editors/matrix/MatrixElement.h new file mode 100644 index 0000000..d330991 --- /dev/null +++ b/src/gui/editors/matrix/MatrixElement.h @@ -0,0 +1,138 @@ + +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_MATRIXELEMENT_H_ +#define _RG_MATRIXELEMENT_H_ + +#include "base/ViewElement.h" +#include <qbrush.h> +#include <qcanvas.h> +#include "QCanvasMatrixRectangle.h" + +class QColor; + + +namespace Rosegarden +{ + +class Event; + +class MatrixElement : public ViewElement +{ + + typedef std::vector <QCanvasRectangle *> OverlapRectangles; + + +public: + MatrixElement(Event *event, bool drum); + + virtual ~MatrixElement(); + + void setCanvas(QCanvas* c); + + /** + * Returns the actual x coordinate of the element on the canvas + */ + double getCanvasX() const { return m_canvasRect->x(); } + + /** + * Returns the actual y coordinate of the element on the canvas + */ + double getCanvasY() const { return m_canvasRect->y(); } + + double getCanvasZ() const { return m_canvasRect->z(); } + + /** + * Sets the x coordinate of the element on the canvas + */ + void setCanvasX(double x) { m_canvasRect->setX(x); } + + /** + * Sets the y coordinate of the element on the canvas + */ + void setCanvasY(double y) { m_canvasRect->setY(y); } + + void setCanvasZ(double z) { m_canvasRect->setZ(z); } + + /** + * Sets the width of the rectangle on the canvas + */ + void setWidth(int w) { m_canvasRect->setSize(w, m_canvasRect->height()); } + int getWidth() { return m_canvasRect->width(); } + + /** + * Sets the height of the rectangle on the canvas + */ + void setHeight(int h) { m_canvasRect->setSize(m_canvasRect->width(), h); } + int getHeight() { return m_canvasRect->height(); } + + /// Returns true if the wrapped event is a note + bool isNote() const; + + /* + * Set the colour of the element + */ + void setColour(const QColor &colour) + { m_canvasRect->setBrush(QBrush(colour)); } + + /** + * Draws overlap rectangles (if any) + * (should not be called in drum mode) + */ + void drawOverlapRectangles(); + + /** + * Removes overlap rectangles if any + */ + void removeOverlapRectangles(); + + /** + * If element rectangle is currently visible gets its size and returns true. + * Returns false if element rectangle is undefined or not visible. + */ + bool getVisibleRectangle(QRect &rectangle); + + /** + * Redraw overlap rectangles of all matrix elements colliding with rect + */ + void redrawOverlaps(QRect rect); + +protected: + + //--------------- Data members --------------------------------- + + QCanvasMatrixRectangle *m_canvasRect; + + OverlapRectangles *m_overlapRectangles; + +}; + + +typedef ViewElementList MatrixElementList; + + +} + +#endif diff --git a/src/gui/editors/matrix/MatrixEraser.cpp b/src/gui/editors/matrix/MatrixEraser.cpp new file mode 100644 index 0000000..6c2373e --- /dev/null +++ b/src/gui/editors/matrix/MatrixEraser.cpp @@ -0,0 +1,110 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#include "MatrixEraser.h" +#include "misc/Debug.h" + +#include <klocale.h> +#include <kstddirs.h> +#include "base/ViewElement.h" +#include "commands/matrix/MatrixEraseCommand.h" +#include "gui/general/EditTool.h" +#include "MatrixStaff.h" +#include "MatrixTool.h" +#include "MatrixView.h" +#include <kaction.h> +#include <kglobal.h> +#include <qiconset.h> +#include <qstring.h> + + +namespace Rosegarden +{ + +MatrixEraser::MatrixEraser(MatrixView* parent) + : MatrixTool("MatrixEraser", parent), + m_currentStaff(0) +{ + QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/"); + QCanvasPixmap pixmap(pixmapDir + "/toolbar/select.xpm"); + QIconSet icon = QIconSet(pixmap); + + new KAction(i18n("Switch to Select Tool"), icon, 0, this, + SLOT(slotSelectSelected()), actionCollection(), + "select"); + + new KAction(i18n("Switch to Draw Tool"), "pencil", 0, this, + SLOT(slotDrawSelected()), actionCollection(), + "draw"); + + new KAction(i18n("Switch to Move Tool"), "move", 0, this, + SLOT(slotMoveSelected()), actionCollection(), + "move"); + + pixmap.load(pixmapDir + "/toolbar/resize.xpm"); + icon = QIconSet(pixmap); + new KAction(i18n("Switch to Resize Tool"), icon, 0, this, + SLOT(slotResizeSelected()), actionCollection(), + "resize"); + + createMenu("matrixeraser.rc"); +} + +void MatrixEraser::handleLeftButtonPress(timeT, + int, + int staffNo, + QMouseEvent*, + ViewElement* el) +{ + MATRIX_DEBUG << "MatrixEraser::handleLeftButtonPress : el = " + << el << endl; + + if (!el) + return ; // nothing to erase + + m_currentStaff = m_mParentView->getStaff(staffNo); + + MatrixEraseCommand* command = + new MatrixEraseCommand(m_currentStaff->getSegment(), el->event()); + + m_mParentView->addCommandToHistory(command); + + m_mParentView->update(); +} + +void MatrixEraser::ready() +{ + m_mParentView->setCanvasCursor(Qt::pointingHandCursor); + setBasicContextHelp(); +} + +void MatrixEraser::setBasicContextHelp() +{ + setContextHelp(i18n("Click on a note to delete it")); +} + +const QString MatrixEraser::ToolName = "eraser"; + +} diff --git a/src/gui/editors/matrix/MatrixEraser.h b/src/gui/editors/matrix/MatrixEraser.h new file mode 100644 index 0000000..4e3d65f --- /dev/null +++ b/src/gui/editors/matrix/MatrixEraser.h @@ -0,0 +1,69 @@ + +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_MATRIXERASER_H_ +#define _RG_MATRIXERASER_H_ + +#include "MatrixTool.h" +#include <qstring.h> + +class QMouseEvent; + + +namespace Rosegarden +{ + +class ViewElement; +class MatrixView; +class MatrixStaff; + + +class MatrixEraser : public MatrixTool +{ + friend class MatrixToolBox; + +public: + + virtual void handleLeftButtonPress(timeT, + int height, + int staffNo, + QMouseEvent *event, + ViewElement*); + + static const QString ToolName; + + virtual void ready(); + +protected: + MatrixEraser(MatrixView*); + + void setBasicContextHelp(); + + MatrixStaff* m_currentStaff; +}; + +} + +#endif diff --git a/src/gui/editors/matrix/MatrixHLayout.cpp b/src/gui/editors/matrix/MatrixHLayout.cpp new file mode 100644 index 0000000..99b89c2 --- /dev/null +++ b/src/gui/editors/matrix/MatrixHLayout.cpp @@ -0,0 +1,220 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#include "MatrixHLayout.h" +#include "MatrixElement.h" +#include "misc/Debug.h" + +#include "base/Composition.h" +#include "base/LayoutEngine.h" +#include "base/NotationTypes.h" +#include "base/Profiler.h" +#include "base/Segment.h" +#include "base/Staff.h" +#include "MatrixStaff.h" + +#include <cmath> + + +namespace Rosegarden +{ + +MatrixHLayout::MatrixHLayout(Composition *c) : + HorizontalLayoutEngine(c), + m_totalWidth(0.0), + m_firstBar(0) +{} + +MatrixHLayout::~MatrixHLayout() +{} + +void MatrixHLayout::reset() +{} + +void MatrixHLayout::resetStaff(Staff&, timeT, timeT) +{} + +void MatrixHLayout::scanStaff(Staff &staffBase, + timeT startTime, timeT endTime) +{ + Profiler profiler("MatrixHLayout::scanStaff", true); + + // The Matrix layout is not currently designed to be able to lay + // out more than one staff, because we have no requirement to show + // more than one at once in the Matrix view. To make it work for + // multiple staffs should be straightforward; we just need to bear + // in mind that they might start and end at different times (hence + // the total width and bar list can't just be calculated from the + // last staff scanned as they are now). + + MatrixStaff &staff = static_cast<MatrixStaff &>(staffBase); + bool isFullScan = (startTime == endTime); + + MatrixElementList *notes = staff.getViewElementList(); + MatrixElementList::iterator startItr = notes->begin(); + MatrixElementList::iterator endItr = notes->end(); + + if (!isFullScan) { + startItr = notes->findNearestTime(startTime); + if (startItr == notes->end()) + startItr = notes->begin(); + endItr = notes->findTime(endTime); + } + + if (endItr == notes->end() && startItr == notes->begin()) { + isFullScan = true; + } + + // Do this in two parts: bar lines separately from elements. + // (We don't need to do all that stuff notationhlayout has to do, + // scanning the notes bar-by-bar; we can just place the bar lines + // in the theoretically-correct places and do the same with the + // notes quite independently.) + + Segment &segment = staff.getSegment(); + Composition *composition = segment.getComposition(); + m_firstBar = composition->getBarNumber(segment.getStartTime()); + timeT from = composition->getBarStart(m_firstBar), + to = composition->getBarEndForTime(segment.getEndMarkerTime()); + + double startPosition = from; + + // 1. Bar lines and time signatures. We only re-make these on + // full scans. + + if (isFullScan || m_barData.size() == 0) { + + m_barData.clear(); + int barNo = m_firstBar; + + MATRIX_DEBUG << "MatrixHLayout::scanStaff() : start time = " << startTime << ", first bar = " << m_firstBar << ", end time = " << endTime << ", end marker time = " << segment.getEndMarkerTime() << ", from = " << from << ", to = " << to << endl; + + // hack for partial bars + // + timeT adjTo = to; + + if (composition->getBarStartForTime(segment.getEndMarkerTime()) + != segment.getEndMarkerTime()) + adjTo++; + + while (from < adjTo) { + + bool isNew = false; + TimeSignature timeSig = + composition->getTimeSignatureInBar(barNo, isNew); + + if (isNew || barNo == m_firstBar) { + m_barData.push_back(BarData((from - startPosition) * + staff.getTimeScaleFactor(), + TimeSigData(true, timeSig))); + } else { + m_barData.push_back(BarData((from - startPosition) * + staff.getTimeScaleFactor(), + TimeSigData(false, timeSig))); + } + + from = composition->getBarEndForTime(from); + ++barNo; + } + + m_barData.push_back(BarData(to * staff.getTimeScaleFactor(), + TimeSigData(false, TimeSignature()))); + } + + // 2. Elements + + m_totalWidth = 0.0; + MatrixElementList::iterator i = startItr; + + while (i != endItr) { + + (*i)->setLayoutX(((*i)->getViewAbsoluteTime() - startPosition) + * staff.getTimeScaleFactor()); + + double width = (*i)->getViewDuration() * staff.getTimeScaleFactor(); + + // Make sure that very small elements can still be seen + // + if (width < 3) width = 3; + else width += 1; // fiddle factor + + static_cast<MatrixElement*>((*i))->setWidth(lrint(width)); + + if (isFullScan) { + m_totalWidth = (*i)->getLayoutX() + width; + } else { + m_totalWidth = std::max(m_totalWidth, (*i)->getLayoutX() + width); + } + + ++i; + } +} + +double MatrixHLayout::getTotalWidth() const +{ + return m_totalWidth; +} + +int MatrixHLayout::getFirstVisibleBar() const +{ + return m_firstBar; +} + +int MatrixHLayout::getLastVisibleBar() const +{ + int barNo = m_firstBar + m_barData.size() - 2; + if (barNo < m_firstBar + 1) + barNo = m_firstBar + 1; + + return barNo; +} + +double MatrixHLayout::getBarPosition(int barNo) const +{ + if (barNo < getFirstVisibleBar()) { + return getBarPosition(getFirstVisibleBar()); + } + + if (barNo > getLastVisibleBar()) { + return getBarPosition(getLastVisibleBar()); + } + + return m_barData[barNo - m_firstBar].first; +} + +bool MatrixHLayout::getTimeSignaturePosition(Staff &, + int barNo, + TimeSignature &timeSig, + double &timeSigX) +{ + timeSig = m_barData[barNo - m_firstBar].second.second; + timeSigX = m_barData[barNo - m_firstBar].first; + return m_barData[barNo - m_firstBar].second.first; +} + +void MatrixHLayout::finishLayout(timeT, timeT) +{} + +} diff --git a/src/gui/editors/matrix/MatrixHLayout.h b/src/gui/editors/matrix/MatrixHLayout.h new file mode 100644 index 0000000..76f1b31 --- /dev/null +++ b/src/gui/editors/matrix/MatrixHLayout.h @@ -0,0 +1,150 @@ + +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_MATRIXHLAYOUT_H_ +#define _RG_MATRIXHLAYOUT_H_ + +#include "base/FastVector.h" +#include "base/LayoutEngine.h" +#include <utility> +#include "base/Event.h" + +#include "gui/general/HZoomable.h" + + + +namespace Rosegarden +{ + +class TimeSignature; +class Staff; +class Composition; + + +class MatrixHLayout : public HorizontalLayoutEngine +{ +public: + MatrixHLayout(Composition *c); + virtual ~MatrixHLayout(); + + /** + * Resets internal data stores for all staffs + */ + virtual void reset(); + + /** + * Resets internal data stores for a specific staff + */ + virtual void resetStaff(Staff &staff, + timeT = 0, + timeT = 0); + + /** + * Returns the total length of all elements once layout is done. + * This is the x-coord of the end of the last element on the + * longest staff + */ + virtual double getTotalWidth() const; + + /** + * Returns the number of the first visible bar line + */ + virtual int getFirstVisibleBar() const; + + /** + * Returns the number of the first visible bar line + */ + virtual int getLastVisibleBar() const; + + /** + * Returns the x-coordinate of the given bar number + */ + virtual double getBarPosition(int barNo) const; + + /** + * Precomputes layout data for a single staff, updating any + * internal data stores associated with that staff and updating + * any layout-related properties in the events on the staff's + * segment. + */ + virtual void scanStaff(Staff&, + timeT = 0, + timeT = 0); + + /** + * Computes any layout data that may depend on the results of + * scanning more than one staff. This may mean doing most of + * the layout (likely for horizontal layout) or nothing at all + * (likely for vertical layout). + */ + virtual void finishLayout(timeT = 0, + timeT = 0); + + /** + * Returns true if there is a new time signature in the given bar, + * setting timeSignature appropriately and setting timeSigX to its + * x-coord + */ + virtual bool getTimeSignaturePosition(Staff &staff, + int barNo, + TimeSignature &timeSig, + double &timeSigX); + +protected: + + //--------------- Data members --------------------------------- + + // pair of has-time-sig and time-sig + typedef std::pair<bool, TimeSignature> TimeSigData; + // pair of layout-x and time-signature if there is one + typedef std::pair<double, TimeSigData> BarData; + typedef FastVector<BarData> BarDataList; + BarDataList m_barData; + double m_totalWidth; + int m_firstBar; +}; + +/** + * "zoomable" version of the above, used in the MatrixView + * to properly scale Tempo and Chord rulers. + * + */ +class ZoomableMatrixHLayoutRulerScale : public RulerScale, public HZoomable { +public: + ZoomableMatrixHLayoutRulerScale(MatrixHLayout& layout) : RulerScale(layout.getComposition()), m_referenceHLayout(layout) {}; + + virtual double getBarPosition(int n) const { return m_referenceHLayout.getBarPosition(n) * getHScaleFactor(); } + virtual double getXForTime(timeT time) const { return m_referenceHLayout.getXForTime(time) * getHScaleFactor(); } + virtual timeT getTimeForX(double x) const { return m_referenceHLayout.getTimeForX(x / getHScaleFactor()); } + virtual double getBarWidth(int n) const { return m_referenceHLayout.getBarWidth(n) * getHScaleFactor(); } + virtual int getLastVisibleBar() const { return m_referenceHLayout.getLastVisibleBar(); } + +protected: + MatrixHLayout& m_referenceHLayout; +}; + +} + +#endif diff --git a/src/gui/editors/matrix/MatrixMover.cpp b/src/gui/editors/matrix/MatrixMover.cpp new file mode 100644 index 0000000..d725f16 --- /dev/null +++ b/src/gui/editors/matrix/MatrixMover.cpp @@ -0,0 +1,481 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#include "MatrixMover.h" + +#include "base/BaseProperties.h" +#include <klocale.h> +#include <kstddirs.h> +#include "base/Event.h" +#include "base/Segment.h" +#include "base/Selection.h" +#include "base/SnapGrid.h" +#include "base/ViewElement.h" +#include "commands/matrix/MatrixModifyCommand.h" +#include "commands/matrix/MatrixInsertionCommand.h" +#include "commands/notation/NormalizeRestsCommand.h" +#include "gui/general/EditTool.h" +#include "gui/general/RosegardenCanvasView.h" +#include "MatrixElement.h" +#include "MatrixStaff.h" +#include "MatrixTool.h" +#include "MatrixView.h" +#include "MatrixVLayout.h" +#include <kaction.h> +#include <kglobal.h> +#include <qiconset.h> +#include <qpoint.h> +#include <qstring.h> +#include "misc/Debug.h" + + +namespace Rosegarden +{ + +MatrixMover::MatrixMover(MatrixView* parent) : + MatrixTool("MatrixMover", parent), + m_currentElement(0), + m_currentStaff(0), + m_lastPlayedPitch(-1) +{ + QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/"); + QCanvasPixmap pixmap(pixmapDir + "/toolbar/select.xpm"); + QIconSet icon = QIconSet(pixmap); + + new KAction(i18n("Switch to Select Tool"), icon, 0, this, + SLOT(slotSelectSelected()), actionCollection(), + "select"); + + new KAction(i18n("Switch to Draw Tool"), "pencil", 0, this, + SLOT(slotDrawSelected()), actionCollection(), + "draw"); + + new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this, + SLOT(slotEraseSelected()), actionCollection(), + "erase"); + + pixmap.load(pixmapDir + "/toolbar/resize.xpm"); + icon = QIconSet(pixmap); + new KAction(i18n("Switch to Resize Tool"), icon, 0, this, + SLOT(slotResizeSelected()), actionCollection(), + "resize"); + + createMenu("matrixmover.rc"); +} + +void MatrixMover::handleEventRemoved(Event *event) +{ + if (m_currentElement && m_currentElement->event() == event) { + m_currentElement = 0; + } +} + +void MatrixMover::handleLeftButtonPress(timeT time, + int pitch, + int staffNo, + QMouseEvent* e, + ViewElement* el) +{ + MATRIX_DEBUG << "MatrixMover::handleLeftButtonPress() : time = " << time << ", el = " << el << endl; + if (!el) return; + + m_quickCopy = (e->state() & Qt::ControlButton); + + if (!m_duplicateElements.empty()) { + for (size_t i = 0; i < m_duplicateElements.size(); ++i) { + delete m_duplicateElements[i]->event(); + delete m_duplicateElements[i]; + } + m_duplicateElements.clear(); + } + + m_currentElement = dynamic_cast<MatrixElement*>(el); + m_currentStaff = m_mParentView->getStaff(staffNo); + + if (m_currentElement) { + + // Add this element and allow movement + // + EventSelection* selection = m_mParentView->getCurrentSelection(); + + if (selection) { + EventSelection *newSelection; + + if ((e->state() & Qt::ShiftButton) || + selection->contains(m_currentElement->event())) + newSelection = new EventSelection(*selection); + else + newSelection = new EventSelection(m_currentStaff->getSegment()); + + // if the selection already contains the event, remove it from the + // selection if shift is pressed + if (selection->contains(m_currentElement->event())){ + if (e->state() & Qt::ShiftButton) + newSelection->removeEvent(m_currentElement->event()); + } else { + newSelection->addEvent(m_currentElement->event()); + } + m_mParentView->setCurrentSelection(newSelection, true, true); + m_mParentView->canvas()->update(); + selection = newSelection; + } else { + m_mParentView->setSingleSelectedEvent(m_currentStaff->getSegment(), + m_currentElement->event(), + true); + m_mParentView->canvas()->update(); + } + + long velocity = m_mParentView->getCurrentVelocity(); + m_currentElement->event()->get<Int>(BaseProperties::VELOCITY, velocity); + m_mParentView->playNote(m_currentStaff->getSegment(), pitch, velocity); + m_lastPlayedPitch = pitch; + + if (m_quickCopy && selection) { + for (EventSelection::eventcontainer::iterator i = + selection->getSegmentEvents().begin(); + i != selection->getSegmentEvents().end(); ++i) { + + MatrixElement *element = m_currentStaff->getElement(*i); + if (!element) continue; + + MatrixElement *duplicate = new MatrixElement + (new Event(**i), m_mParentView->isDrumMode()); + duplicate->setLayoutY(element->getLayoutY()); + duplicate->setLayoutX(element->getLayoutX()); + duplicate->setWidth(element->getWidth()); + duplicate->setHeight(element->getHeight()); + duplicate->setCanvasZ(-1); + m_currentStaff->positionElement(duplicate); + m_duplicateElements.push_back(duplicate); + } + } + } + + m_clickX = m_mParentView->inverseMapPoint(e->pos()).x(); +} + +timeT +MatrixMover::getDragTime(QMouseEvent *e, timeT candidate) +{ + int x = m_mParentView->inverseMapPoint(e->pos()).x(); + int xdiff = x - m_clickX; + + const SnapGrid &grid = getSnapGrid(); + const RulerScale &scale = *grid.getRulerScale(); + + timeT eventTime = m_currentElement->getViewAbsoluteTime(); + int eventX = scale.getXForTime(eventTime); + timeT preSnapTarget = scale.getTimeForX(eventX + xdiff); + + candidate = grid.snapTime(preSnapTarget, SnapGrid::SnapEither); + + if (xdiff == 0 || + (abs(eventTime - preSnapTarget) < abs(candidate - preSnapTarget))) { + candidate = eventTime; + } + + return candidate; +} + +int MatrixMover::handleMouseMove(timeT newTime, + int newPitch, + QMouseEvent *e) +{ + MATRIX_DEBUG << "MatrixMover::handleMouseMove() time = " + << newTime << endl; + + if (e) { + setBasicContextHelp(e->state() & Qt::ControlButton); + } + + if (!m_currentElement || !m_currentStaff) + return RosegardenCanvasView::NoFollow; + + if (getSnapGrid().getSnapSetting() != SnapGrid::NoSnap) { + setContextHelp(i18n("Hold Shift to avoid snapping to beat grid")); + } else { + clearContextHelp(); + } + + if (e) newTime = getDragTime(e, newTime); + + emit hoveredOverNoteChanged(newPitch, true, newTime); + + using BaseProperties::PITCH; + int diffPitch = 0; + if (m_currentElement->event()->has(PITCH)) { + diffPitch = newPitch - m_currentElement->event()->get<Int>(PITCH); + } + + int diffY = + int(((m_currentStaff->getLayoutYForHeight(newPitch) - + m_currentStaff->getElementHeight() / 2) - + m_currentElement->getLayoutY())); + + EventSelection* selection = m_mParentView->getCurrentSelection(); + EventSelection::eventcontainer::iterator it = + selection->getSegmentEvents().begin(); + + MatrixElement *element = 0; + int maxY = m_currentStaff->getCanvasYForHeight(0); + + for (; it != selection->getSegmentEvents().end(); it++) { + element = m_currentStaff->getElement(*it); + + if (element) { + + timeT diffTime = element->getViewAbsoluteTime() - + m_currentElement->getViewAbsoluteTime(); + + int newX = getSnapGrid().getRulerScale()-> + getXForTime(newTime + diffTime); + + if (newX < 0) newX = 0; + + int newY = int(element->getLayoutY() + diffY); + + if (newY < 0) newY = 0; + if (newY > maxY) newY = maxY; + + element->setLayoutX(newX); + element->setLayoutY(newY); + + m_currentStaff->positionElement(element); + } + } + + if (newPitch != m_lastPlayedPitch) { + long velocity = m_mParentView->getCurrentVelocity(); + m_currentElement->event()->get<Int>(BaseProperties::VELOCITY, velocity); + m_mParentView->playNote(m_currentStaff->getSegment(), newPitch, velocity); + m_lastPlayedPitch = newPitch; + } + + m_mParentView->canvas()->update(); + return RosegardenCanvasView::FollowHorizontal | + RosegardenCanvasView::FollowVertical; +} + +void MatrixMover::handleMouseRelease(timeT newTime, + int newPitch, + QMouseEvent *e) +{ + MATRIX_DEBUG << "MatrixMover::handleMouseRelease() - newPitch = " + << newPitch << endl; + + if (!m_currentElement || !m_currentStaff) + return; + + if (newPitch > MatrixVLayout::maxMIDIPitch) + newPitch = MatrixVLayout::maxMIDIPitch; + if (newPitch < 0) + newPitch = 0; + + if (e) newTime = getDragTime(e, newTime); + + using BaseProperties::PITCH; + timeT diffTime = newTime - m_currentElement->getViewAbsoluteTime(); + int diffPitch = 0; + if (m_currentElement->event()->has(PITCH)) { + diffPitch = newPitch - m_currentElement->event()->get<Int>(PITCH); + } + + EventSelection *selection = m_mParentView->getCurrentSelection(); + + if ((diffTime == 0 && diffPitch == 0) || selection->getAddedEvents() == 0) { + for (size_t i = 0; i < m_duplicateElements.size(); ++i) { + delete m_duplicateElements[i]->event(); + delete m_duplicateElements[i]; + } + m_duplicateElements.clear(); + m_mParentView->canvas()->update(); + m_currentElement = 0; + return; + } + + if (newPitch != m_lastPlayedPitch) { + long velocity = m_mParentView->getCurrentVelocity(); + m_currentElement->event()->get<Int>(BaseProperties::VELOCITY, velocity); + m_mParentView->playNote(m_currentStaff->getSegment(), newPitch, velocity); + m_lastPlayedPitch = newPitch; + } + + QString commandLabel; + if (m_quickCopy) { + if (selection->getAddedEvents() < 2) { + commandLabel = i18n("Copy and Move Event"); + } else { + commandLabel = i18n("Copy and Move Events"); + } + } else { + if (selection->getAddedEvents() < 2) { + commandLabel = i18n("Move Event"); + } else { + commandLabel = i18n("Move Events"); + } + } + + KMacroCommand *macro = new KMacroCommand(commandLabel); + + EventSelection::eventcontainer::iterator it = + selection->getSegmentEvents().begin(); + + Segment &segment = m_currentStaff->getSegment(); + + EventSelection *newSelection = new EventSelection(segment); + + timeT normalizeStart = selection->getStartTime(); + timeT normalizeEnd = selection->getEndTime(); + + if (m_quickCopy) { + for (size_t i = 0; i < m_duplicateElements.size(); ++i) { + timeT time = m_duplicateElements[i]->getViewAbsoluteTime(); + timeT endTime = time + m_duplicateElements[i]->getViewDuration(); + if (time < normalizeStart) normalizeStart = time; + if (endTime > normalizeEnd) normalizeEnd = endTime; + macro->addCommand(new MatrixInsertionCommand + (segment, time, endTime, + m_duplicateElements[i]->event())); + delete m_duplicateElements[i]->event(); + delete m_duplicateElements[i]; + } + m_duplicateElements.clear(); + m_quickCopy = false; + } + + for (; it != selection->getSegmentEvents().end(); it++) { + + timeT newTime = (*it)->getAbsoluteTime() + diffTime; + + int newPitch = 60; + if ((*it)->has(PITCH)) { + newPitch = (*it)->get<Int>(PITCH) + diffPitch; + } + + Event *newEvent = 0; + + if (newTime < segment.getStartTime()) { + newTime = segment.getStartTime(); + } + + if (newTime + (*it)->getDuration() >= segment.getEndMarkerTime()) { + timeT limit = getSnapGrid().snapTime + (segment.getEndMarkerTime() - 1, SnapGrid::SnapLeft); + if (newTime > limit) newTime = limit; + timeT newDuration = std::min + ((*it)->getDuration(), segment.getEndMarkerTime() - newTime); + newEvent = new Event(**it, newTime, newDuration); + } else { + newEvent = new Event(**it, newTime); + } + + newEvent->set<Int>(BaseProperties::PITCH, newPitch); + + macro->addCommand(new MatrixModifyCommand(segment, + (*it), + newEvent, + true, + false)); + newSelection->addEvent(newEvent); + } + + normalizeStart = std::min(normalizeStart, newSelection->getStartTime()); + normalizeEnd = std::max(normalizeEnd, newSelection->getEndTime()); + + macro->addCommand(new NormalizeRestsCommand(segment, + normalizeStart, + normalizeEnd)); + + m_mParentView->setCurrentSelection(0, false, false); + m_mParentView->addCommandToHistory(macro); + m_mParentView->setCurrentSelection(newSelection, false, false); + + m_mParentView->canvas()->update(); + m_currentElement = 0; + + setBasicContextHelp(); +} + +void MatrixMover::ready() +{ + connect(m_parentView->getCanvasView(), SIGNAL(contentsMoving (int, int)), + this, SLOT(slotMatrixScrolled(int, int))); + connect(this, SIGNAL(hoveredOverNoteChanged(int, bool, timeT)), + m_mParentView, SLOT(slotHoveredOverNoteChanged(int, bool, timeT))); + m_mParentView->setCanvasCursor(Qt::sizeAllCursor); + setBasicContextHelp(); +} + +void MatrixMover::stow() +{ + disconnect(m_parentView->getCanvasView(), SIGNAL(contentsMoving (int, int)), + this, SLOT(slotMatrixScrolled(int, int))); + disconnect(this, SIGNAL(hoveredOverNoteChanged(int, bool, timeT)), + m_mParentView, SLOT(slotHoveredOverNoteChanged(int, bool, timeT))); +} + +void MatrixMover::slotMatrixScrolled(int newX, int newY) +{ + if (!m_currentElement) + return ; + + QPoint newP1(newX, newY), oldP1(m_parentView->getCanvasView()->contentsX(), + m_parentView->getCanvasView()->contentsY()); + + QPoint offset = newP1 - oldP1; + + offset = m_mParentView->inverseMapPoint(offset); + + QPoint p(m_currentElement->getCanvasX(), m_currentElement->getCanvasY()); + p += offset; + + timeT newTime = getSnapGrid().snapX(p.x()); + int newPitch = m_currentStaff->getHeightAtCanvasCoords(p.x(), p.y()); + + handleMouseMove(newTime, newPitch, 0); +} + +void MatrixMover::setBasicContextHelp(bool ctrlPressed) +{ + EventSelection *selection = m_mParentView->getCurrentSelection(); + if (!selection || selection->getAddedEvents() < 2) { + if (!ctrlPressed) { + setContextHelp(i18n("Click and drag to move a note; hold Ctrl as well to copy it")); + } else { + setContextHelp(i18n("Click and drag to copy a note")); + } + } else { + if (!ctrlPressed) { + setContextHelp(i18n("Click and drag to move selected notes; hold Ctrl as well to copy")); + } else { + setContextHelp(i18n("Click and drag to copy selected notes")); + } + } +} + +const QString MatrixMover::ToolName = "mover"; + +} +#include "MatrixMover.moc" diff --git a/src/gui/editors/matrix/MatrixMover.h b/src/gui/editors/matrix/MatrixMover.h new file mode 100644 index 0000000..ac95c5f --- /dev/null +++ b/src/gui/editors/matrix/MatrixMover.h @@ -0,0 +1,112 @@ + +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_MATRIXMOVER_H_ +#define _RG_MATRIXMOVER_H_ + +#include "MatrixTool.h" +#include <qstring.h> +#include "base/Event.h" + + +class QMouseEvent; + + +namespace Rosegarden +{ + +class ViewElement; +class MatrixView; +class MatrixStaff; +class MatrixElement; +class Event; + + +class MatrixMover : public MatrixTool +{ + Q_OBJECT + + friend class MatrixToolBox; + +public: + virtual void handleLeftButtonPress(timeT, + int height, + int staffNo, + QMouseEvent *event, + ViewElement*); + + /** + * Set the duration of the element + */ + virtual int handleMouseMove(timeT, + int height, + QMouseEvent*); + + /** + * Actually insert the new element + */ + virtual void handleMouseRelease(timeT, + int height, + QMouseEvent*); + + static const QString ToolName; + + /** + * Respond to an event being deleted -- it may be the one the tool + * is remembering as the current event. + */ + virtual void handleEventRemoved(Event *event); + + virtual void ready(); + virtual void stow(); + +signals: + void hoveredOverNoteChanged(int evPitch, bool haveEvent, timeT evTime); + +protected slots: + void slotMatrixScrolled(int x, int y); + +protected: + MatrixMover(MatrixView*); + + void setBasicContextHelp(bool ctrlPressed = false); + + timeT getDragTime(QMouseEvent *e, timeT candidate); + + MatrixElement* m_currentElement; + MatrixStaff* m_currentStaff; + + std::vector<MatrixElement *> m_duplicateElements; + bool m_quickCopy; + + int m_lastPlayedPitch; + int m_clickX; +}; + + + +} + +#endif diff --git a/src/gui/editors/matrix/MatrixPainter.cpp b/src/gui/editors/matrix/MatrixPainter.cpp new file mode 100644 index 0000000..be63bd7 --- /dev/null +++ b/src/gui/editors/matrix/MatrixPainter.cpp @@ -0,0 +1,370 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#include "MatrixPainter.h" + +#include "base/BaseProperties.h" +#include <klocale.h> +#include <kstddirs.h> +#include "base/Event.h" +#include "base/NotationTypes.h" +#include "base/SegmentMatrixHelper.h" +#include "base/SnapGrid.h" +#include "base/ViewElement.h" +#include "commands/matrix/MatrixInsertionCommand.h" +#include "commands/matrix/MatrixEraseCommand.h" +#include "commands/matrix/MatrixPercussionInsertionCommand.h" +#include "gui/general/EditTool.h" +#include "gui/general/RosegardenCanvasView.h" +#include "MatrixElement.h" +#include "MatrixStaff.h" +#include "MatrixTool.h" +#include "MatrixView.h" +#include <kaction.h> +#include <kglobal.h> +#include <qiconset.h> +#include <qpoint.h> +#include <qstring.h> +#include "misc/Debug.h" + + +namespace Rosegarden +{ + +MatrixPainter::MatrixPainter(MatrixView* parent) + : MatrixTool("MatrixPainter", parent), + m_currentElement(0), + m_currentStaff(0) +{ + QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/"); + QCanvasPixmap pixmap(pixmapDir + "/toolbar/select.xpm"); + QIconSet icon = QIconSet(pixmap); + + new KAction(i18n("Switch to Select Tool"), icon, 0, this, + SLOT(slotSelectSelected()), actionCollection(), + "select"); + + new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this, + SLOT(slotEraseSelected()), actionCollection(), + "erase"); + + new KAction(i18n("Switch to Move Tool"), "move", 0, this, + SLOT(slotMoveSelected()), actionCollection(), + "move"); + + pixmap.load(pixmapDir + "/toolbar/resize.xpm"); + icon = QIconSet(pixmap); + new KAction(i18n("Switch to Resize Tool"), icon, 0, this, + SLOT(slotResizeSelected()), actionCollection(), + "resize"); + + createMenu("matrixpainter.rc"); +} + +MatrixPainter::MatrixPainter(QString name, MatrixView* parent) + : MatrixTool(name, parent), + m_currentElement(0), + m_currentStaff(0) +{} + +void MatrixPainter::handleEventRemoved(Event *event) +{ + if (m_currentElement && m_currentElement->event() == event) { + m_currentElement = 0; + } +} + +void MatrixPainter::handleLeftButtonPress(timeT time, + int pitch, + int staffNo, + QMouseEvent *e, + ViewElement *element) +{ + MATRIX_DEBUG << "MatrixPainter::handleLeftButtonPress : pitch = " + << pitch << ", time : " << time << endl; + + QPoint p = m_mParentView->inverseMapPoint(e->pos()); + + m_currentStaff = m_mParentView->getStaff(staffNo); + + // Don't create an overlapping event on the same note on the same channel + if (dynamic_cast<MatrixElement*>(element)) { + std::cerr << "MatrixPainter::handleLeftButtonPress : overlap with an other matrix element" << std::endl; + // In percussion matrix, we delete the existing event rather + // than just ignoring it -- this is reasonable as the event + // has no meaningful duration, so we can just toggle it on and + // off with repeated clicks + if (m_mParentView->isDrumMode()) { + if (element->event()) { + MatrixEraseCommand *command = + new MatrixEraseCommand(m_currentStaff->getSegment(), + element->event()); + m_mParentView->addCommandToHistory(command); + } + } + m_currentElement = 0; + return ; + } + + // This is needed for the event duration rounding + SnapGrid grid(getSnapGrid()); + + Event *ev = new Event(Note::EventType, time, + grid.getSnapTime(double(p.x()))); + ev->set<Int>(BaseProperties::PITCH, pitch); + ev->set<Int>(BaseProperties::VELOCITY, m_mParentView->getCurrentVelocity()); + + m_currentElement = new MatrixElement(ev, m_mParentView->isDrumMode()); + + int y = m_currentStaff->getLayoutYForHeight(pitch) - + m_currentStaff->getElementHeight() / 2; + + m_currentElement->setLayoutY(y); + m_currentElement->setLayoutX(grid.getRulerScale()->getXForTime(time)); + m_currentElement->setHeight(m_currentStaff->getElementHeight()); + + int width = grid.getRulerScale()->getXForTime(time + ev->getDuration()) + - m_currentElement->getLayoutX() + 1; + + m_currentElement->setWidth(width); + + m_currentStaff->positionElement(m_currentElement); + m_mParentView->update(); + + // preview + m_mParentView->playNote(ev); +} + +int MatrixPainter::handleMouseMove(timeT time, + int pitch, + QMouseEvent *e) +{ + // sanity check + if (!m_currentElement) + return RosegardenCanvasView::NoFollow; + + if (getSnapGrid().getSnapSetting() != SnapGrid::NoSnap) { + setContextHelp(i18n("Hold Shift to avoid snapping to beat grid")); + } else { + clearContextHelp(); + } + + // We don't want to use the time passed in, because it's snapped + // to the left and we want a more particular policy + + if (e) { + QPoint p = m_mParentView->inverseMapPoint(e->pos()); + time = getSnapGrid().snapX(p.x(), SnapGrid::SnapEither); + if (time >= m_currentElement->getViewAbsoluteTime()) { + time = getSnapGrid().snapX(p.x(), SnapGrid::SnapRight); + } else { + time = getSnapGrid().snapX(p.x(), SnapGrid::SnapLeft); + } + } + + MATRIX_DEBUG << "MatrixPainter::handleMouseMove : pitch = " + << pitch << ", time : " << time << endl; + + using BaseProperties::PITCH; + + if (time == m_currentElement->getViewAbsoluteTime()) { + time = + m_currentElement->getViewAbsoluteTime() + + m_currentElement->getViewDuration(); + } + + int width = getSnapGrid().getRulerScale()->getXForTime(time) + - getSnapGrid().getRulerScale()->getXForTime + (m_currentElement->getViewAbsoluteTime()) + 1; + + m_currentElement->setWidth(width); +// std::cerr << "current element width "<< width << std::endl; + + if (m_currentElement->event()->has(PITCH) && + pitch != m_currentElement->event()->get<Int>(PITCH)) { + + m_currentElement->event()->set<Int>(PITCH, pitch); + + int y = m_currentStaff->getLayoutYForHeight(pitch) - + m_currentStaff->getElementHeight() / 2; + + m_currentElement->setLayoutY(y); + + m_currentStaff->positionElement(m_currentElement); + + // preview + m_mParentView->playNote(m_currentElement->event()); + } + + m_mParentView->update(); + + return RosegardenCanvasView::FollowHorizontal | + RosegardenCanvasView::FollowVertical; +} + +void MatrixPainter::handleMouseRelease(timeT endTime, + int, + QMouseEvent *e) +{ + // This can happen in case of screen/window capture - + // we only get a mouse release, the window snapshot tool + // got the mouse down + if (!m_currentElement) + return ; + + // We don't want to use the time passed in, because it's snapped + // to the left and we want a more particular policy + + if (e) { + QPoint p = m_mParentView->inverseMapPoint(e->pos()); + endTime = getSnapGrid().snapX(p.x(), SnapGrid::SnapEither); + if (endTime >= m_currentElement->getViewAbsoluteTime()) { + endTime = getSnapGrid().snapX(p.x(), SnapGrid::SnapRight); + } else { + endTime = getSnapGrid().snapX(p.x(), SnapGrid::SnapLeft); + } + } + + timeT time = m_currentElement->getViewAbsoluteTime(); + timeT segmentEndTime = m_currentStaff->getSegment().getEndMarkerTime(); + + if (m_mParentView->isDrumMode()) { + + if (time > segmentEndTime) + time = segmentEndTime; + + MatrixPercussionInsertionCommand *command = + new MatrixPercussionInsertionCommand(m_currentStaff->getSegment(), + time, + m_currentElement->event()); + m_mParentView->addCommandToHistory(command); + + Event* ev = m_currentElement->event(); + delete m_currentElement; + delete ev; + + ev = command->getLastInsertedEvent(); + if (ev) + m_mParentView->setSingleSelectedEvent(m_currentStaff->getSegment(), + ev); + } else { + + // Insert element if it has a non null duration, + // discard it otherwise + // + if (time > endTime) + std::swap(time, endTime); + + if (endTime == time) + endTime = time + m_currentElement->getViewDuration(); + + if (time < segmentEndTime) { + + if (endTime > segmentEndTime) + endTime = segmentEndTime; + + SegmentMatrixHelper helper(m_currentStaff->getSegment()); + MATRIX_DEBUG << "MatrixPainter::handleMouseRelease() : helper.insertNote()" << endl; + + MatrixInsertionCommand* command = + new MatrixInsertionCommand(m_currentStaff->getSegment(), + time, + endTime, + m_currentElement->event()); + + m_mParentView->addCommandToHistory(command); + + Event* ev = m_currentElement->event(); + delete m_currentElement; + delete ev; + + ev = command->getLastInsertedEvent(); + if (ev) + m_mParentView->setSingleSelectedEvent(m_currentStaff->getSegment(), + ev); + } else { + + Event* ev = m_currentElement->event(); + delete m_currentElement; + delete ev; + } + } + + m_mParentView->update(); + m_currentElement = 0; + + setBasicContextHelp(); +} + +void MatrixPainter::ready() +{ + connect(m_parentView->getCanvasView(), SIGNAL(contentsMoving (int, int)), + this, SLOT(slotMatrixScrolled(int, int))); + + m_mParentView->setCanvasCursor(Qt::crossCursor); + + setBasicContextHelp(); +} + +void MatrixPainter::stow() +{ + disconnect(m_parentView->getCanvasView(), SIGNAL(contentsMoving (int, int)), + this, SLOT(slotMatrixScrolled(int, int))); +} + +void MatrixPainter::slotMatrixScrolled(int newX, int newY) +{ + if (!m_currentElement) + return ; + + QPoint newP1(newX, newY), oldP1(m_parentView->getCanvasView()->contentsX(), + m_parentView->getCanvasView()->contentsY()); + + QPoint offset = newP1 - oldP1; + + offset = m_mParentView->inverseMapPoint(offset); + + QPoint p(m_currentElement->getCanvasX() + m_currentElement->getWidth(), m_currentElement->getCanvasY()); + p += offset; + + timeT newTime = getSnapGrid().snapX(p.x()); + int newPitch = m_currentStaff->getHeightAtCanvasCoords(p.x(), p.y()); + + handleMouseMove(newTime, newPitch, 0); +} + +void MatrixPainter::setBasicContextHelp() +{ + if (getSnapGrid().getSnapSetting() != SnapGrid::NoSnap) { + setContextHelp(i18n("Click and drag to draw a note; Shift to avoid snapping to grid")); + } else { + setContextHelp(i18n("Click and drag to draw a note")); + } +} + +const QString MatrixPainter::ToolName = "painter"; + +} +#include "MatrixPainter.moc" diff --git a/src/gui/editors/matrix/MatrixPainter.h b/src/gui/editors/matrix/MatrixPainter.h new file mode 100644 index 0000000..570243a --- /dev/null +++ b/src/gui/editors/matrix/MatrixPainter.h @@ -0,0 +1,105 @@ + +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_MATRIXPAINTER_H_ +#define _RG_MATRIXPAINTER_H_ + +#include "MatrixTool.h" +#include <qstring.h> +#include "base/Event.h" + + +class QMouseEvent; + + +namespace Rosegarden +{ + +class ViewElement; +class MatrixView; +class MatrixStaff; +class MatrixElement; +class Event; + + +class MatrixPainter : public MatrixTool +{ + Q_OBJECT + + friend class MatrixToolBox; + +public: + + virtual void handleLeftButtonPress(timeT, + int height, + int staffNo, + QMouseEvent *event, + ViewElement*); + + /** + * Set the duration of the element + */ + virtual int handleMouseMove(timeT, + int height, + QMouseEvent*); + + /** + * Actually insert the new element + */ + virtual void handleMouseRelease(timeT, + int height, + QMouseEvent*); + + static const QString ToolName; + + /** + * Respond to an event being deleted -- it may be the one the tool + * is remembering as the current event. + */ + virtual void handleEventRemoved(Event *event); + + virtual void ready(); + virtual void stow(); + +protected slots: + + void slotMatrixScrolled(int x, int y); + +protected: + MatrixPainter(MatrixView*); + MatrixPainter(QString name, MatrixView*); + + void setBasicContextHelp(); + + MatrixElement* m_currentElement; + MatrixStaff* m_currentStaff; +}; + + + + +} + +#endif diff --git a/src/gui/editors/matrix/MatrixParameterBox.cpp b/src/gui/editors/matrix/MatrixParameterBox.cpp new file mode 100644 index 0000000..c330b94 --- /dev/null +++ b/src/gui/editors/matrix/MatrixParameterBox.cpp @@ -0,0 +1,99 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#include "MatrixParameterBox.h" + +#include "base/Instrument.h" +#include "base/BasicQuantizer.h" +#include "base/Selection.h" +#include "document/RosegardenGUIDoc.h" +#include "gui/editors/parameters/InstrumentParameterBox.h" +#include <kcombobox.h> +#include <qfont.h> +#include <qfontmetrics.h> +#include <qframe.h> +#include <qlayout.h> +#include <qwidget.h> + + +namespace Rosegarden +{ + +MatrixParameterBox::MatrixParameterBox(RosegardenGUIDoc *doc, + QWidget *parent, const char* name): + QFrame(parent, name), + m_quantizations(BasicQuantizer::getStandardQuantizations()), + m_doc(doc) +{ + setFrameStyle(NoFrame); + initBox(); +} + +MatrixParameterBox::~MatrixParameterBox() +{} + +void +MatrixParameterBox::initBox() +{ + QFont boldFont; + boldFont.setPointSize(int(boldFont.pointSize() * 9.5 / 10.0 + 0.5)); + boldFont.setBold(true); + + QFont plainFont; + plainFont.setPointSize(plainFont.pointSize() * 9 / 10); + QFont font = plainFont; + + QFontMetrics fontMetrics(font); + // magic numbers: 13 is the height of the menu pixmaps, 10 is just 10 + //int comboHeight = std::max(fontMetrics.height(), 13) + 10; + + QGridLayout *gridLayout = new QGridLayout(this, 20, 3, 8, 1); + + m_instrumentParameterBox = new InstrumentParameterBox(m_doc, this); + gridLayout->addMultiCellWidget(m_instrumentParameterBox, 0, 7, 0, 2); + +} + +void +MatrixParameterBox::setSelection(EventSelection *selection) +{ + if (!selection) + return ; + + EventSelection::eventcontainer::iterator + it = selection->getSegmentEvents().begin(); + +for (; it != selection->getSegmentEvents().end(); it++) {} + +} + +void +MatrixParameterBox::useInstrument(Instrument *instrument) +{ + m_instrumentParameterBox->useInstrument(instrument); +} + +} +#include "MatrixParameterBox.moc" diff --git a/src/gui/editors/matrix/MatrixParameterBox.h b/src/gui/editors/matrix/MatrixParameterBox.h new file mode 100644 index 0000000..d8d4a4d --- /dev/null +++ b/src/gui/editors/matrix/MatrixParameterBox.h @@ -0,0 +1,76 @@ + +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_MATRIXPARAMETERBOX_H_ +#define _RG_MATRIXPARAMETERBOX_H_ + +#include <qframe.h> +#include <vector> +#include "base/Event.h" + + +class QWidget; +class KComboBox; + + +namespace Rosegarden +{ + +class RosegardenGUIDoc; +class InstrumentParameterBox; +class Instrument; +class EventSelection; + + +class MatrixParameterBox : public QFrame +{ + Q_OBJECT + +public: + MatrixParameterBox(RosegardenGUIDoc *doc=0, QWidget *parent=0, const char* name=0); + ~MatrixParameterBox(); + + void initBox(); + void setSelection(EventSelection *); + void useInstrument(Instrument *instrument); + +protected: + + KComboBox *m_quantizeCombo; + KComboBox *m_snapGridCombo; + InstrumentParameterBox *m_instrumentParameterBox; + + std::vector<timeT> m_quantizations; + std::vector<timeT> m_snapValues; + + RosegardenGUIDoc *m_doc; + +}; + + + +} + +#endif diff --git a/src/gui/editors/matrix/MatrixResizer.cpp b/src/gui/editors/matrix/MatrixResizer.cpp new file mode 100644 index 0000000..2fab5e8 --- /dev/null +++ b/src/gui/editors/matrix/MatrixResizer.cpp @@ -0,0 +1,333 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#include "MatrixResizer.h" + +#include <klocale.h> +#include <kstddirs.h> +#include "base/Event.h" +#include "base/Segment.h" +#include "base/Selection.h" +#include "base/SnapGrid.h" +#include "base/ViewElement.h" +#include "commands/matrix/MatrixModifyCommand.h" +#include "commands/notation/NormalizeRestsCommand.h" +#include "gui/general/EditTool.h" +#include "gui/general/RosegardenCanvasView.h" +#include "MatrixElement.h" +#include "MatrixStaff.h" +#include "MatrixTool.h" +#include "MatrixView.h" +#include <kaction.h> +#include <kglobal.h> +#include <qiconset.h> +#include <qpoint.h> +#include <qstring.h> +#include "misc/Debug.h" + + +namespace Rosegarden +{ + +MatrixResizer::MatrixResizer(MatrixView* parent) + : MatrixTool("MatrixResizer", parent), + m_currentElement(0), + m_currentStaff(0) +{ + QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/"); + QCanvasPixmap pixmap(pixmapDir + "/toolbar/select.xpm"); + QIconSet icon = QIconSet(pixmap); + + new KAction(i18n("Switch to Select Tool"), icon, 0, this, + SLOT(slotSelectSelected()), actionCollection(), + "select"); + + new KAction(i18n("Switch to Draw Tool"), "pencil", 0, this, + SLOT(slotDrawSelected()), actionCollection(), + "draw"); + + new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this, + SLOT(slotEraseSelected()), actionCollection(), + "erase"); + + new KAction(i18n("Switch to Move Tool"), "move", 0, this, + SLOT(slotMoveSelected()), actionCollection(), + "move"); + + createMenu("matrixresizer.rc"); +} + +void MatrixResizer::handleEventRemoved(Event *event) +{ + if (m_currentElement && m_currentElement->event() == event) { + m_currentElement = 0; + } +} + +void MatrixResizer::handleLeftButtonPress(timeT, + int, + int staffNo, + QMouseEvent* e, + ViewElement* el) +{ + MATRIX_DEBUG << "MatrixResizer::handleLeftButtonPress() : el = " + << el << endl; + + if (!el) + return ; // nothing to erase + + m_currentElement = dynamic_cast<MatrixElement*>(el); + m_currentStaff = m_mParentView->getStaff(staffNo); + + if (m_currentElement) { + + // Add this element and allow movement + // + EventSelection* selection = m_mParentView->getCurrentSelection(); + + if (selection) { + EventSelection *newSelection; + + if ((e->state() & Qt::ShiftButton) || + selection->contains(m_currentElement->event())) + newSelection = new EventSelection(*selection); + else + newSelection = new EventSelection(m_currentStaff->getSegment()); + + newSelection->addEvent(m_currentElement->event()); + m_mParentView->setCurrentSelection(newSelection, true, true); + m_mParentView->canvas()->update(); + } else { + m_mParentView->setSingleSelectedEvent(m_currentStaff->getSegment(), + m_currentElement->event(), + true); + m_mParentView->canvas()->update(); + } + } +} + +int MatrixResizer::handleMouseMove(timeT newTime, + int, + QMouseEvent *e) +{ + setBasicContextHelp(); + + if (!m_currentElement || !m_currentStaff) + return RosegardenCanvasView::NoFollow; + + if (getSnapGrid().getSnapSetting() != SnapGrid::NoSnap) { + setContextHelp(i18n("Hold Shift to avoid snapping to beat grid")); + } else { + clearContextHelp(); + } + + // For the resizer we normally don't want to use the official + // time, because it's snapped to the left and we want to snap in + // the closest direction instead + + if (e) { + QPoint p = m_mParentView->inverseMapPoint(e->pos()); + newTime = getSnapGrid().snapX(p.x(), SnapGrid::SnapEither); + } + + timeT newDuration = newTime - m_currentElement->getViewAbsoluteTime(); + + if (newDuration == 0) { + newDuration += getSnapGrid().getSnapTime + (m_currentElement->getViewAbsoluteTime()); + } + + int width = getSnapGrid().getRulerScale()->getXForTime + (m_currentElement->getViewAbsoluteTime() + newDuration) + - m_currentElement->getLayoutX() + 1; + + int initialWidth = m_currentElement->getWidth(); + + int diffWidth = initialWidth - width; + + EventSelection* selection = m_mParentView->getCurrentSelection(); + EventSelection::eventcontainer::iterator it = + selection->getSegmentEvents().begin(); + + MatrixElement *element = 0; + for (; it != selection->getSegmentEvents().end(); it++) { + element = m_currentStaff->getElement(*it); + + if (element) { + int newWidth = element->getWidth() - diffWidth; + + MATRIX_DEBUG << "MatrixResizer::handleMouseMove - " + << "new width = " << newWidth << endl; + + element->setWidth(newWidth); + m_currentStaff->positionElement(element); + } + } + + m_mParentView->canvas()->update(); + return RosegardenCanvasView::FollowHorizontal; +} + +void MatrixResizer::handleMouseRelease(timeT newTime, + int, + QMouseEvent *e) +{ + if (!m_currentElement || !m_currentStaff) + return ; + + // For the resizer we don't want to use the time passed in, + // because it's snapped to the left and we want to snap in the + // closest direction instead + + if (e) { + QPoint p = m_mParentView->inverseMapPoint(e->pos()); + newTime = getSnapGrid().snapX(p.x(), SnapGrid::SnapEither); + } + + timeT diffDuration = + newTime - m_currentElement->getViewAbsoluteTime() - + m_currentElement->getViewDuration(); + + EventSelection *selection = m_mParentView->getCurrentSelection(); + + if (selection->getAddedEvents() == 0) + return ; + else { + QString commandLabel = i18n("Resize Event"); + + if (selection->getAddedEvents() > 1) + commandLabel = i18n("Resize Events"); + + KMacroCommand *macro = new KMacroCommand(commandLabel); + + EventSelection::eventcontainer::iterator it = + selection->getSegmentEvents().begin(); + + Segment &segment = m_currentStaff->getSegment(); + + EventSelection *newSelection = new EventSelection(segment); + + timeT normalizeStart = selection->getStartTime(); + timeT normalizeEnd = selection->getEndTime(); + + for (; it != selection->getSegmentEvents().end(); it++) { + timeT eventTime = (*it)->getAbsoluteTime(); + timeT eventDuration = (*it)->getDuration() + diffDuration; + + + MATRIX_DEBUG << "MatrixResizer::handleMouseRelease - " + << "Time = " << eventTime + << ", Duration = " << eventDuration << endl; + + + if (eventDuration < 0) { + eventTime += eventDuration; + eventDuration = -eventDuration; + } + + if (eventDuration == 0) { + eventDuration += getSnapGrid().getSnapTime(eventTime); + } + + if (eventTime + eventDuration >= segment.getEndMarkerTime()) { + eventDuration = std::min(eventDuration, + segment.getEndMarkerTime() - eventTime); + } + + Event *newEvent = + new Event(**it, + eventTime, + eventDuration); + + macro->addCommand(new MatrixModifyCommand(segment, + *it, + newEvent, + false, + false)); + + newSelection->addEvent(newEvent); + } + + normalizeStart = std::min(normalizeStart, newSelection->getStartTime()); + normalizeEnd = std::max(normalizeEnd, newSelection->getEndTime()); + + macro->addCommand(new NormalizeRestsCommand(segment, + normalizeStart, + normalizeEnd)); + + m_mParentView->setCurrentSelection(0, false, false); + m_mParentView->addCommandToHistory(macro); + m_mParentView->setCurrentSelection(newSelection, false, false); + } + + m_mParentView->update(); + m_currentElement = 0; + setBasicContextHelp(); +} + +void MatrixResizer::ready() +{ + connect(m_parentView->getCanvasView(), SIGNAL(contentsMoving (int, int)), + this, SLOT(slotMatrixScrolled(int, int))); + m_mParentView->setCanvasCursor(Qt::sizeHorCursor); + setBasicContextHelp(); +} + +void MatrixResizer::stow() +{ + disconnect(m_parentView->getCanvasView(), SIGNAL(contentsMoving (int, int)), + this, SLOT(slotMatrixScrolled(int, int))); +} + +void MatrixResizer::slotMatrixScrolled(int newX, int newY) +{ + QPoint newP1(newX, newY), oldP1(m_parentView->getCanvasView()->contentsX(), + m_parentView->getCanvasView()->contentsY()); + + QPoint p(newX, newY); + + if (newP1.x() > oldP1.x()) { + p.setX(newX + m_parentView->getCanvasView()->visibleWidth()); + } + + p = m_mParentView->inverseMapPoint(p); + int newTime = getSnapGrid().snapX(p.x()); + handleMouseMove(newTime, 0, 0); +} + +void MatrixResizer::setBasicContextHelp() +{ + EventSelection *selection = m_mParentView->getCurrentSelection(); + if (selection && selection->getAddedEvents() > 1) { + setContextHelp(i18n("Click and drag to resize selected notes")); + } else { + setContextHelp(i18n("Click and drag to resize a note")); + } +} + +const QString MatrixResizer::ToolName = "resizer"; + +} +#include "MatrixResizer.moc" diff --git a/src/gui/editors/matrix/MatrixResizer.h b/src/gui/editors/matrix/MatrixResizer.h new file mode 100644 index 0000000..e623cac --- /dev/null +++ b/src/gui/editors/matrix/MatrixResizer.h @@ -0,0 +1,102 @@ + +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_MATRIXRESIZER_H_ +#define _RG_MATRIXRESIZER_H_ + +#include "MatrixTool.h" +#include <qstring.h> +#include "base/Event.h" + + +class QMouseEvent; + + +namespace Rosegarden +{ + +class ViewElement; +class MatrixView; +class MatrixStaff; +class MatrixElement; +class Event; + + +class MatrixResizer : public MatrixTool +{ + Q_OBJECT + + friend class MatrixToolBox; + +public: + virtual void handleLeftButtonPress(timeT, + int height, + int staffNo, + QMouseEvent *event, + ViewElement*); + + /** + * Set the duration of the element + */ + virtual int handleMouseMove(timeT, + int height, + QMouseEvent*); + + /** + * Actually insert the new element + */ + virtual void handleMouseRelease(timeT, + int height, + QMouseEvent*); + + static const QString ToolName; + + /** + * Respond to an event being deleted -- it may be the one the tool + * is remembering as the current event. + */ + virtual void handleEventRemoved(Event *event); + + virtual void ready(); + virtual void stow(); + +protected slots: + + void slotMatrixScrolled(int x, int y); + +protected: + MatrixResizer(MatrixView*); + + void setBasicContextHelp(); + + MatrixElement* m_currentElement; + MatrixStaff* m_currentStaff; +}; + + + +} + +#endif diff --git a/src/gui/editors/matrix/MatrixSelector.cpp b/src/gui/editors/matrix/MatrixSelector.cpp new file mode 100644 index 0000000..fbb9689 --- /dev/null +++ b/src/gui/editors/matrix/MatrixSelector.cpp @@ -0,0 +1,629 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#include "MatrixSelector.h" + +#include "base/BaseProperties.h" +#include <klocale.h> +#include <kstddirs.h> +#include "base/Event.h" +#include "base/NotationTypes.h" +#include "base/Selection.h" +#include "base/ViewElement.h" +#include "commands/edit/EventEditCommand.h" +#include "gui/dialogs/EventEditDialog.h" +#include "gui/dialogs/SimpleEventEditDialog.h" +#include "gui/general/EditTool.h" +#include "gui/general/EditToolBox.h" +#include "gui/general/GUIPalette.h" +#include "gui/general/RosegardenCanvasView.h" +#include "MatrixElement.h" +#include "MatrixMover.h" +#include "MatrixPainter.h" +#include "MatrixResizer.h" +#include "MatrixStaff.h" +#include "MatrixTool.h" +#include "MatrixView.h" +#include <kaction.h> +#include <kglobal.h> +#include <kapplication.h> +#include <kconfig.h> +#include <qdialog.h> +#include <qiconset.h> +#include <qpoint.h> +#include <qstring.h> +#include "misc/Debug.h" + + +namespace Rosegarden +{ + +MatrixSelector::MatrixSelector(MatrixView* view) + : MatrixTool("MatrixSelector", view), + m_selectionRect(0), + m_updateRect(false), + m_currentStaff(0), + m_clickedElement(0), + m_dispatchTool(0), + m_justSelectedBar(false), + m_matrixView(view), + m_selectionToMerge(0) +{ + connect(m_parentView, SIGNAL(usedSelection()), + this, SLOT(slotHideSelection())); + + new KAction(i18n("Switch to Draw Tool"), "pencil", 0, this, + SLOT(slotDrawSelected()), actionCollection(), + "draw"); + + new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this, + SLOT(slotEraseSelected()), actionCollection(), + "erase"); + + new KAction(i18n("Switch to Move Tool"), "move", 0, this, + SLOT(slotMoveSelected()), actionCollection(), + "move"); + + QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/"); + QCanvasPixmap pixmap(pixmapDir + "/toolbar/resize.xpm"); + QIconSet icon = QIconSet(pixmap); + + new KAction(i18n("Switch to Resize Tool"), icon, 0, this, + SLOT(slotResizeSelected()), actionCollection(), + "resize"); + + createMenu("matrixselector.rc"); +} + +void MatrixSelector::handleEventRemoved(Event *event) +{ + if (m_dispatchTool) + m_dispatchTool->handleEventRemoved(event); + if (m_clickedElement && m_clickedElement->event() == event) { + m_clickedElement = 0; + } +} + +void MatrixSelector::slotClickTimeout() +{ + m_justSelectedBar = false; +} + +void MatrixSelector::handleLeftButtonPress(timeT time, + int height, + int staffNo, + QMouseEvent* e, + ViewElement *element) +{ + MATRIX_DEBUG << "MatrixSelector::handleMousePress" << endl; + + if (m_justSelectedBar) { + handleMouseTripleClick(time, height, staffNo, e, element); + m_justSelectedBar = false; + return ; + } + + QPoint p = m_mParentView->inverseMapPoint(e->pos()); + + m_currentStaff = m_mParentView->getStaff(staffNo); + + // Do the merge selection thing + // + delete m_selectionToMerge; // you can safely delete 0, you know? + const EventSelection *selectionToMerge = 0; + if (e->state() & Qt::ShiftButton) + selectionToMerge = m_mParentView->getCurrentSelection(); + + m_selectionToMerge = + (selectionToMerge ? new EventSelection(*selectionToMerge) : 0); + + // Now the rest of the element stuff + // + m_clickedElement = dynamic_cast<MatrixElement*>(element); + + if (m_clickedElement) { + int x = int(m_clickedElement->getLayoutX()); + int width = m_clickedElement->getWidth(); + int resizeStart = int(double(width) * 0.85) + x; + + // max size of 10 + if ((x + width ) - resizeStart > 10) + resizeStart = x + width - 10; + + if (p.x() > resizeStart) { + m_dispatchTool = m_parentView-> + getToolBox()->getTool(MatrixResizer::ToolName); + } else { + m_dispatchTool = m_parentView-> + getToolBox()->getTool(MatrixMover::ToolName); + } + + m_dispatchTool->ready(); + + m_dispatchTool->handleLeftButtonPress(time, + height, + staffNo, + e, + element); + return ; + + } else if (e->state() & Qt::ControlButton) { + + handleMidButtonPress(time, height, staffNo, e, element); + return; + + } else { + + // Workaround for #930420 Positional error in sweep-selection box + // boundary + int zoomValue = (int)m_matrixView->m_hZoomSlider->getCurrentSize(); + MatrixStaff *staff = m_mParentView->getStaff(staffNo); + int pitch = m_currentStaff->getHeightAtCanvasCoords(p.x(), p.y()); + int pitchCentreHeight = staff->getTotalHeight() - + pitch * staff->getLineSpacing() - 2; // 2 or ? + int pitchLineHeight = pitchCentreHeight + staff->getLineSpacing() / 2; + int drawHeight = p.y(); + if (drawHeight <= pitchLineHeight + 1 && + drawHeight >= pitchLineHeight - 1) { + if (drawHeight == pitchLineHeight) + drawHeight += 2; + else + drawHeight += 2 * (drawHeight - pitchLineHeight); + } + MATRIX_DEBUG << "#### MatrixSelector::handleLeftButtonPress() : zoom " + << zoomValue + << " pitch " << pitch + << " pitchCentreHeight " << pitchCentreHeight + << " pitchLineHeight " << pitchLineHeight + << " lineSpacing " << staff->getLineSpacing() + << " drawHeight " << drawHeight << endl; + m_selectionRect->setX(int(p.x() / 4)*4); // more workaround for #930420 + m_selectionRect->setY(drawHeight); + m_selectionRect->setSize(0, 0); + + m_selectionRect->show(); + m_updateRect = true; + + // Clear existing selection if we're not merging + // + if (!m_selectionToMerge) { + m_mParentView->setCurrentSelection(0, false, true); + m_mParentView->canvas()->update(); + } + } + + //m_parentView->setCursorPosition(p.x()); +} + +void MatrixSelector::handleMidButtonPress(timeT time, + int height, + int staffNo, + QMouseEvent* e, + ViewElement *element) +{ + m_clickedElement = 0; // should be used for left-button clicks only + + // Don't allow overlapping elements on the same channel + if (dynamic_cast<MatrixElement*>(element)) + return ; + + m_dispatchTool = m_parentView-> + getToolBox()->getTool(MatrixPainter::ToolName); + + m_dispatchTool->ready(); + + m_dispatchTool->handleLeftButtonPress(time, height, staffNo, e, element); +} + +void MatrixSelector::handleMouseDoubleClick(timeT , + int , + int staffNo, + QMouseEvent *ev, + ViewElement *element) +{ + /* + if (m_dispatchTool) + { + m_dispatchTool->handleMouseDoubleClick(time, height, staffNo, e, element); + } + */ + + m_clickedElement = dynamic_cast<MatrixElement*>(element); + + MatrixStaff *staff = m_mParentView->getStaff(staffNo); + if (!staff) + return ; + + if (m_clickedElement) { + + if (m_clickedElement->event()->isa(Note::EventType) && + m_clickedElement->event()->has(BaseProperties::TRIGGER_SEGMENT_ID)) { + + int id = m_clickedElement->event()->get + <Int> + (BaseProperties::TRIGGER_SEGMENT_ID); + emit editTriggerSegment(id); + return ; + } + + if (ev->state() & ShiftButton) { // advanced edit + + EventEditDialog dialog(m_mParentView, *m_clickedElement->event(), true); + + if (dialog.exec() == QDialog::Accepted && + dialog.isModified()) { + + EventEditCommand *command = new EventEditCommand + (staff->getSegment(), + m_clickedElement->event(), + dialog.getEvent()); + + m_mParentView->addCommandToHistory(command); + } + } else { + + SimpleEventEditDialog dialog(m_mParentView, m_mParentView->getDocument(), + *m_clickedElement->event(), false); + + if (dialog.exec() == QDialog::Accepted && + dialog.isModified()) { + + EventEditCommand *command = new EventEditCommand + (staff->getSegment(), + m_clickedElement->event(), + dialog.getEvent()); + + m_mParentView->addCommandToHistory(command); + } + } + + } /* + + #988167: Matrix:Multiclick select methods don't work in matrix editor + Postponing this, as it falls foul of world-matrix transformation + etiquette and other such niceties + + else { + + QRect rect = staff->getBarExtents(ev->x(), ev->y()); + + m_selectionRect->setX(rect.x() + 2); + m_selectionRect->setY(rect.y()); + m_selectionRect->setSize(rect.width() - 4, rect.height()); + + m_selectionRect->show(); + m_updateRect = false; + + m_justSelectedBar = true; + QTimer::singleShot(QApplication::doubleClickInterval(), this, + SLOT(slotClickTimeout())); + } */ +} + +void MatrixSelector::handleMouseTripleClick(timeT t, + int height, + int staffNo, + QMouseEvent *ev, + ViewElement *element) +{ + if (!m_justSelectedBar) + return ; + m_justSelectedBar = false; + + MatrixStaff *staff = m_mParentView->getStaff(staffNo); + if (!staff) + return ; + + if (m_clickedElement) { + + // should be safe, as we've already set m_justSelectedBar false + handleLeftButtonPress(t, height, staffNo, ev, element); + return ; + + } else { + + m_selectionRect->setX(staff->getX()); + m_selectionRect->setY(staff->getY()); + m_selectionRect->setSize(int(staff->getTotalWidth()) - 1, + staff->getTotalHeight() - 1); + + m_selectionRect->show(); + m_updateRect = false; + } +} + +int MatrixSelector::handleMouseMove(timeT time, int height, + QMouseEvent *e) +{ + QPoint p = m_mParentView->inverseMapPoint(e->pos()); + + if (m_dispatchTool) { + return m_dispatchTool->handleMouseMove(time, height, e); + } + + + if (!m_updateRect) { + setContextHelpFor(e->pos(), + getSnapGrid().getSnapSetting() == SnapGrid::NoSnap); + return RosegardenCanvasView::NoFollow; + } else { + clearContextHelp(); + } + + int w = int(p.x() - m_selectionRect->x()); + int h = int(p.y() - m_selectionRect->y()); + + // Qt rectangle dimensions appear to be 1-based + if (w > 0) + ++w; + else + --w; + if (h > 0) + ++h; + else + --h; + + // Workaround for #930420 Positional error in sweep-selection box boundary + int wFix = (w > 0) ? 3 : 0; + int hFix = (h > 0) ? 3 : 0; + int xFix = (w < 0) ? 3 : 0; + m_selectionRect->setSize(w - wFix, h - hFix); + m_selectionRect->setX(m_selectionRect->x() + xFix); + setViewCurrentSelection(); + m_selectionRect->setSize(w, h); + m_selectionRect->setX(m_selectionRect->x() - xFix); + m_mParentView->canvas()->update(); + + return RosegardenCanvasView::FollowHorizontal | RosegardenCanvasView::FollowVertical; +} + +void MatrixSelector::handleMouseRelease(timeT time, int height, QMouseEvent *e) +{ + MATRIX_DEBUG << "MatrixSelector::handleMouseRelease" << endl; + + if (m_dispatchTool) { + m_dispatchTool->handleMouseRelease(time, height, e); + + m_dispatchTool->stow(); + ready(); + + // don't delete the tool as it's still part of the toolbox + m_dispatchTool = 0; + + return ; + } + + m_updateRect = false; + + if (m_clickedElement) { + m_mParentView->setSingleSelectedEvent(m_currentStaff->getSegment(), + m_clickedElement->event(), + false, true); + m_mParentView->canvas()->update(); + m_clickedElement = 0; + + } else if (m_selectionRect) { + setViewCurrentSelection(); + m_selectionRect->hide(); + m_mParentView->canvas()->update(); + } + + // Tell anyone who's interested that the selection has changed + emit gotSelection(); + + setContextHelpFor(e->pos()); +} + +void MatrixSelector::ready() +{ + if (m_mParentView) { + m_selectionRect = new QCanvasRectangle(m_mParentView->canvas()); + m_selectionRect->hide(); + m_selectionRect->setPen(QPen(GUIPalette::getColour(GUIPalette::SelectionRectangle), 2)); + + m_mParentView->setCanvasCursor(Qt::arrowCursor); + //m_mParentView->setPositionTracking(false); + } + + connect(m_parentView->getCanvasView(), SIGNAL(contentsMoving (int, int)), + this, SLOT(slotMatrixScrolled(int, int))); + + setContextHelp(i18n("Click and drag to select; middle-click and drag to draw new note")); +} + +void MatrixSelector::stow() +{ + if (m_selectionRect) { + delete m_selectionRect; + m_selectionRect = 0; + m_mParentView->canvas()->update(); + } + + disconnect(m_parentView->getCanvasView(), SIGNAL(contentsMoving (int, int)), + this, SLOT(slotMatrixScrolled(int, int))); + +} + +void MatrixSelector::slotHideSelection() +{ + if (!m_selectionRect) + return ; + m_selectionRect->hide(); + m_selectionRect->setSize(0, 0); + m_mParentView->canvas()->update(); +} + +void MatrixSelector::slotMatrixScrolled(int newX, int newY) +{ + if (m_updateRect) { + int offsetX = newX - m_parentView->getCanvasView()->contentsX(); + int offsetY = newY - m_parentView->getCanvasView()->contentsY(); + + int w = int(m_selectionRect->width() + offsetX); + int h = int(m_selectionRect->height() + offsetY); + + // Qt rectangle dimensions appear to be 1-based + if (w > 0) + ++w; + else + --w; + if (h > 0) + ++h; + else + --h; + + m_selectionRect->setSize(w, h); + setViewCurrentSelection(); + m_mParentView->canvas()->update(); + } +} + +void MatrixSelector::setViewCurrentSelection() +{ + EventSelection* selection = getSelection(); + + if (m_selectionToMerge && selection && + m_selectionToMerge->getSegment() == selection->getSegment()) { + + selection->addFromSelection(m_selectionToMerge); + m_mParentView->setCurrentSelection(selection, true, true); + + } else if (!m_selectionToMerge) { + + m_mParentView->setCurrentSelection(selection, true, true); + + } + +} + +EventSelection* MatrixSelector::getSelection() +{ + if (!m_selectionRect->visible()) return 0; + + Segment& originalSegment = m_currentStaff->getSegment(); + EventSelection* selection = new EventSelection(originalSegment); + + // get the selections + // + QCanvasItemList l = m_selectionRect->collisions(true); + + if (l.count()) + { + for (QCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it) + { + QCanvasItem *item = *it; + QCanvasMatrixRectangle *matrixRect = 0; + + if ((matrixRect = dynamic_cast<QCanvasMatrixRectangle*>(item))) + { + MatrixElement *mE = &matrixRect->getMatrixElement(); + selection->addEvent(mE->event()); + } + } + } + + if (selection->getAddedEvents() > 0) { + return selection; + } else { + delete selection; + return 0; + } +} + +void MatrixSelector::setContextHelpFor(QPoint p, bool ctrlPressed) +{ + kapp->config()->setGroup(GeneralOptionsConfigGroup); + if (!kapp->config()->readBoolEntry("toolcontexthelp", true)) return; + + p = m_mParentView->inverseMapPoint(p); + + // same logic as in MatrixCanvasView::contentsMousePressEvent + + QCanvasItemList itemList = m_mParentView->canvas()->collisions(p); + QCanvasItemList::Iterator it; + MatrixElement* mel = 0; + QCanvasItem* activeItem = 0; + + for (it = itemList.begin(); it != itemList.end(); ++it) { + + QCanvasItem *item = *it; + QCanvasMatrixRectangle *mRect = 0; + + if (item->active()) { + break; + } + + if ((mRect = dynamic_cast<QCanvasMatrixRectangle*>(item))) { + if (! mRect->rect().contains(p, true)) continue; + mel = &(mRect->getMatrixElement()); + break; + } + } + + if (!mel) { + setContextHelp(i18n("Click and drag to select; middle-click and drag to draw new note")); + + } else { + + // same logic as in handleMouseButtonPress + + int x = int(mel->getLayoutX()); + int width = mel->getWidth(); + int resizeStart = int(double(width) * 0.85) + x; + + // max size of 10 + if ((x + width ) - resizeStart > 10) + resizeStart = x + width - 10; + + EventSelection *s = m_mParentView->getCurrentSelection(); + + if (p.x() > resizeStart) { + if (s && s->getAddedEvents() > 1) { + setContextHelp(i18n("Click and drag to resize selected notes")); + } else { + setContextHelp(i18n("Click and drag to resize note")); + } + } else { + if (s && s->getAddedEvents() > 1) { + if (!ctrlPressed) { + setContextHelp(i18n("Click and drag to move selected notes; hold Ctrl as well to copy")); + } else { + setContextHelp(i18n("Click and drag to copy selected notes")); + } + } else { + if (!ctrlPressed) { + setContextHelp(i18n("Click and drag to move note; hold Ctrl as well to copy")); + } else { + setContextHelp(i18n("Click and drag to copy note")); + } + } + } + } +} + +const QString MatrixSelector::ToolName = "selector"; + +} +#include "MatrixSelector.moc" diff --git a/src/gui/editors/matrix/MatrixSelector.h b/src/gui/editors/matrix/MatrixSelector.h new file mode 100644 index 0000000..a1d1ca4 --- /dev/null +++ b/src/gui/editors/matrix/MatrixSelector.h @@ -0,0 +1,177 @@ + +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_MATRIXSELECTOR_H_ +#define _RG_MATRIXSELECTOR_H_ + +#include "MatrixTool.h" +#include <qstring.h> +#include "base/Event.h" + + +class QMouseEvent; +class QCanvasRectangle; + + +namespace Rosegarden +{ + +class ViewElement; +class MatrixView; +class MatrixStaff; +class MatrixElement; +class EventSelection; +class Event; +class EditTool; + + +class MatrixSelector : public MatrixTool +{ + Q_OBJECT + + friend class MatrixToolBox; + +public: + + virtual void handleLeftButtonPress(timeT time, + int height, + int staffNo, + QMouseEvent *event, + ViewElement *element); + + virtual void handleMidButtonPress(timeT time, + int height, + int staffNo, + QMouseEvent *event, + ViewElement *element); + + virtual int handleMouseMove(timeT time, + int height, + QMouseEvent *event); + + virtual void handleMouseRelease(timeT, + int height, + QMouseEvent *event); + + /** + * Double-click: edit an event or make a whole-bar selection + */ + virtual void handleMouseDoubleClick(timeT time, + int height, + int staffNo, + QMouseEvent* event, + ViewElement *element); + + /** + * Triple-click: maybe make a whole-staff selection + */ + virtual void handleMouseTripleClick(timeT time, + int height, + int staffNo, + QMouseEvent* event, + ViewElement *element); + + + /** + * Create the selection rect + * + * We need this because MatrixView deletes all QCanvasItems + * along with it. This happens before the MatrixSelector is + * deleted, so we can't delete the selection rect in + * ~MatrixSelector because that leads to double deletion. + */ + virtual void ready(); + + /** + * Delete the selection rect. + */ + virtual void stow(); + + /** + * Returns the currently selected events + * + * The returned result is owned by the caller + */ + EventSelection* getSelection(); + + /** + * Respond to an event being deleted -- it may be the one the tool + * is remembering as the current event. + */ + virtual void handleEventRemoved(Event *event); + + static const QString ToolName; + +public slots: + /** + * Hide the selection rectangle + * + * Should be called after a cut or a copy has been + * performed + */ + void slotHideSelection(); + + void slotClickTimeout(); + +protected slots: + + void slotMatrixScrolled(int x, int y); + +signals: + void gotSelection(); // inform that we've got a new selection + void editTriggerSegment(int); + +protected: + MatrixSelector(MatrixView*); + + void setContextHelpFor(QPoint p, bool ctrlPressed = false); + + void setViewCurrentSelection(); + + //--------------- Data members --------------------------------- + + QCanvasRectangle* m_selectionRect; + bool m_updateRect; + + int m_clickedStaff; + MatrixStaff* m_currentStaff; + + MatrixElement* m_clickedElement; + + // tool to delegate to + EditTool* m_dispatchTool; + + bool m_justSelectedBar; + + MatrixView * m_matrixView; + + EventSelection *m_selectionToMerge; +}; + + + +} + +#endif diff --git a/src/gui/editors/matrix/MatrixStaff.cpp b/src/gui/editors/matrix/MatrixStaff.cpp new file mode 100644 index 0000000..b6be79f --- /dev/null +++ b/src/gui/editors/matrix/MatrixStaff.cpp @@ -0,0 +1,232 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#include "MatrixStaff.h" +#include "misc/Debug.h" + +#include "base/BaseProperties.h" +#include "base/Composition.h" +#include "base/Event.h" +#include "base/Instrument.h" +#include "base/MidiProgram.h" +#include "base/NotationTypes.h" +#include "base/Segment.h" +#include "base/Selection.h" +#include "base/SnapGrid.h" +#include "base/Staff.h" +#include "base/Track.h" +#include "base/ViewElement.h" +#include "base/SegmentMatrixHelper.h" +#include "document/RosegardenGUIDoc.h" +#include "gui/general/GUIPalette.h" +#include "gui/general/LinedStaff.h" +#include "gui/rulers/DefaultVelocityColour.h" +#include "MatrixElement.h" +#include "MatrixView.h" +#include "MatrixVLayout.h" +#include <qcanvas.h> + + +namespace Rosegarden +{ + +MatrixStaff::MatrixStaff(QCanvas *canvas, + Segment *segment, + SnapGrid *snapGrid, + int id, + int vResolution, + MatrixView *view) : + LinedStaff(canvas, segment, snapGrid, id, vResolution, 1), + m_scaleFactor(2.0 / + Note(Note::Shortest).getDuration()), + m_view(view) +{} + +MatrixStaff::~MatrixStaff() +{ + // nothing +} + +int MatrixStaff::getLineCount() const +{ + // MATRIX_DEBUG << "MatrixStaff::getLineCount: isDrumMode " << m_view->isDrumMode() << ", key mapping " << (getKeyMapping() ? getKeyMapping()->getName() : "<none>") << endl; + + if (m_view->isDrumMode()) { + const MidiKeyMapping *km = getKeyMapping(); + if (km) + return km->getPitchExtent() + 1; + } + return MatrixVLayout::maxMIDIPitch + 2; +} + +int MatrixStaff::getLegerLineCount() const +{ + return 0; +} + +int MatrixStaff::getBottomLineHeight() const +{ + if (m_view->isDrumMode()) { + const MidiKeyMapping *km = getKeyMapping(); + if (km) + return km->getPitchForOffset(0); + } + return 0; +} + +int MatrixStaff::getHeightPerLine() const +{ + return 1; +} + +bool MatrixStaff::elementsInSpaces() const +{ + return true; +} + +bool MatrixStaff::showBeatLines() const +{ + return true; +} + +bool MatrixStaff::wrapEvent(Event* e) +{ + // Changed from "Note or Time signature" to just "Note" because + // there should be no time signature events in any ordinary + // segments, they're only in the composition's ref segment + + return e->isa(Note::EventType) && + Staff::wrapEvent(e); +} + +void +MatrixStaff::positionElements(timeT from, timeT to) +{ + MatrixElementList *mel = getViewElementList(); + + MatrixElementList::iterator beginAt = mel->findTime(from); + if (beginAt != mel->begin()) + --beginAt; + + MatrixElementList::iterator endAt = mel->findTime(to); + + for (MatrixElementList::iterator i = beginAt; i != endAt; ++i) { + positionElement(*i); + } +} + +void MatrixStaff::positionElement(ViewElement* vel) +{ + MatrixElement* el = dynamic_cast<MatrixElement*>(vel); + + // Memorize initial rectangle position. May be some overlap rectangles + // belonging to other notes are here and should be refreshed after + // current element is moved. + QRect initialRect; + bool rectWasVisible; + if (! m_view->isDrumMode()) + rectWasVisible = el->getVisibleRectangle(initialRect); + + LinedStaffCoords coords = getCanvasCoordsForLayoutCoords + (el->getLayoutX(), int(el->getLayoutY())); + + // Get velocity for colouring + // + using BaseProperties::VELOCITY; + long velocity = 127; + if (el->event()->has(VELOCITY)) + el->event()->get + <Int>(VELOCITY, velocity); + + el->setCanvas(m_canvas); + + // Is the event currently selected? Colour accordingly. + // + EventSelection *selection = m_view->getCurrentSelection(); + + if (selection && selection->contains(el->event())) + el->setColour(GUIPalette::getColour(GUIPalette::SelectedElement)); + else if (el->event()->has(BaseProperties::TRIGGER_SEGMENT_ID)) + el->setColour(Qt::gray); + else + el->setColour(DefaultVelocityColour::getInstance()->getColour(velocity)); + + el->setCanvasX(coords.first); + el->setCanvasY((double)coords.second); + + // Display overlaps + if (m_view->isDrumMode()) { + SegmentMatrixHelper helper(m_segment); + if (helper.isDrumColliding(el->event())) + el->setColour(GUIPalette::getColour(GUIPalette::MatrixOverlapBlock)); + } else { + el->drawOverlapRectangles(); + + // Refresh other overlap rectangles + if (rectWasVisible) el->redrawOverlaps(initialRect); + } + +} + +MatrixElement* +MatrixStaff::getElement(Event *event) +{ + ViewElementList::iterator i = findEvent(event); + if (i == m_viewElementList->end()) + return 0; + return dynamic_cast<MatrixElement*>(*i); +} + +void +MatrixStaff::eventRemoved(const Segment *segment, + Event *event) +{ + LinedStaff::eventRemoved(segment, event); + m_view->handleEventRemoved(event); +} + +ViewElement* +MatrixStaff::makeViewElement(Event* e) +{ + return new MatrixElement(e, m_view->isDrumMode()); +} + +const MidiKeyMapping* +MatrixStaff::getKeyMapping() const +{ + Composition *comp = getSegment().getComposition(); + if (!comp) + return 0; + TrackId trackId = getSegment().getTrack(); + Track *track = comp->getTrackById(trackId); + Instrument *instr = m_view->getDocument()->getStudio(). + getInstrumentById(track->getInstrument()); + if (!instr) + return 0; + return m_view->getKeyMapping(); +} + + +} diff --git a/src/gui/editors/matrix/MatrixStaff.h b/src/gui/editors/matrix/MatrixStaff.h new file mode 100644 index 0000000..cd0a9dc --- /dev/null +++ b/src/gui/editors/matrix/MatrixStaff.h @@ -0,0 +1,111 @@ + +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_MATRIXSTAFF_H_ +#define _RG_MATRIXSTAFF_H_ + +#include "base/Staff.h" +#include "gui/general/LinedStaff.h" +#include "base/Event.h" + + +class QCanvas; + + +namespace Rosegarden +{ + +class ViewElement; +class SnapGrid; +class Segment; +class MidiKeyMapping; +class MatrixView; +class MatrixElement; +class Event; + + +class MatrixStaff : public LinedStaff +{ +public: + MatrixStaff(QCanvas *canvas, + Segment *segment, + SnapGrid *snapGrid, + int id, + int vResolution, + MatrixView *view); + virtual ~MatrixStaff(); + +protected: + virtual int getLineCount() const; + virtual int getLegerLineCount() const; + virtual int getBottomLineHeight() const; + virtual int getHeightPerLine() const; + virtual bool elementsInSpaces() const; + virtual bool showBeatLines() const; + + const MidiKeyMapping *getKeyMapping() const; + + /** + * Override from Staff<T> + * Wrap only notes + */ + virtual bool wrapEvent(Event*); + + /** + * Override from Staff<T> + * Let tools know if their current element has gone + */ + virtual void eventRemoved(const Segment *, Event *); + + virtual ViewElement* makeViewElement(Event*); + +public: + LinedStaff::setResolution; + +// double getTimeScaleFactor() const { return m_scaleFactor * 2; } // TODO: GROSS HACK to enhance matrix resolution (see also in matrixview.cpp) - BREAKS MATRIX VIEW, see bug 1000595 + double getTimeScaleFactor() const { return m_scaleFactor; } + void setTimeScaleFactor(double f) { m_scaleFactor = f; } + + int getElementHeight() { return m_resolution; } + + virtual void positionElements(timeT from, + timeT to); + + virtual void positionElement(ViewElement*); + + // Get an element for an Event + // + MatrixElement* getElement(Event *event); + +private: + double m_scaleFactor; + + MatrixView *m_view; +}; + + +} + +#endif diff --git a/src/gui/editors/matrix/MatrixTool.cpp b/src/gui/editors/matrix/MatrixTool.cpp new file mode 100644 index 0000000..b036559 --- /dev/null +++ b/src/gui/editors/matrix/MatrixTool.cpp @@ -0,0 +1,79 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#include "MatrixTool.h" + +#include "gui/general/EditTool.h" +#include "MatrixView.h" +#include <kaction.h> +#include <qstring.h> + + +namespace Rosegarden +{ + +MatrixTool::MatrixTool(const QString& menuName, MatrixView* parent) + : EditTool(menuName, parent), + m_mParentView(parent) +{} + +void +MatrixTool::slotSelectSelected() +{ + m_parentView->actionCollection()->action("select")->activate(); +} + +void +MatrixTool::slotMoveSelected() +{ + m_parentView->actionCollection()->action("move")->activate(); +} + +void +MatrixTool::slotEraseSelected() +{ + m_parentView->actionCollection()->action("erase")->activate(); +} + +void +MatrixTool::slotResizeSelected() +{ + m_parentView->actionCollection()->action("resize")->activate(); +} + +void +MatrixTool::slotDrawSelected() +{ + m_parentView->actionCollection()->action("draw")->activate(); +} + +const SnapGrid & +MatrixTool::getSnapGrid() const +{ + return m_mParentView->getSnapGrid(); +} + +} +#include "MatrixTool.moc" diff --git a/src/gui/editors/matrix/MatrixTool.h b/src/gui/editors/matrix/MatrixTool.h new file mode 100644 index 0000000..5127f57 --- /dev/null +++ b/src/gui/editors/matrix/MatrixTool.h @@ -0,0 +1,74 @@ + +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_MATRIXTOOL_H_ +#define _RG_MATRIXTOOL_H_ + +#include "gui/general/EditTool.h" + + +class QString; + + +namespace Rosegarden +{ + +class MatrixView; +class SnapGrid; + + +////////////////////////////////////////////////////////////////////// + +class MatrixTool : public EditTool +{ + Q_OBJECT + +public: +// virtual void ready(); + +protected slots: + + // For switching between tools on RMB + // + void slotSelectSelected(); + void slotMoveSelected(); + void slotEraseSelected(); + void slotResizeSelected(); + void slotDrawSelected(); + + const SnapGrid &getSnapGrid() const; + +protected: + MatrixTool(const QString& menuName, MatrixView*); + + //--------------- Data members --------------------------------- + + MatrixView* m_mParentView; +}; + + +} + +#endif diff --git a/src/gui/editors/matrix/MatrixToolBox.cpp b/src/gui/editors/matrix/MatrixToolBox.cpp new file mode 100644 index 0000000..466cfea --- /dev/null +++ b/src/gui/editors/matrix/MatrixToolBox.cpp @@ -0,0 +1,87 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#include "MatrixToolBox.h" + +#include "gui/general/EditToolBox.h" +#include "gui/general/EditTool.h" +#include "MatrixView.h" +#include "MatrixPainter.h" +#include "MatrixEraser.h" +#include "MatrixSelector.h" +#include "MatrixMover.h" +#include "MatrixResizer.h" + +#include <qstring.h> +#include <kmessagebox.h> + +namespace Rosegarden +{ + +MatrixToolBox::MatrixToolBox(MatrixView* parent) + : EditToolBox(parent), + m_mParentView(parent) +{} + +EditTool* MatrixToolBox::createTool(const QString& toolName) +{ + MatrixTool* tool = 0; + + QString toolNamelc = toolName.lower(); + + if (toolNamelc == MatrixPainter::ToolName) + + tool = new MatrixPainter(m_mParentView); + + else if (toolNamelc == MatrixEraser::ToolName) + + tool = new MatrixEraser(m_mParentView); + + else if (toolNamelc == MatrixSelector::ToolName) + + tool = new MatrixSelector(m_mParentView); + + else if (toolNamelc == MatrixMover::ToolName) + + tool = new MatrixMover(m_mParentView); + + else if (toolNamelc == MatrixResizer::ToolName) + + tool = new MatrixResizer(m_mParentView); + + else { + KMessageBox::error(0, QString("MatrixToolBox::createTool : unrecognised toolname %1 (%2)") + .arg(toolName).arg(toolNamelc)); + return 0; + } + + m_tools.insert(toolName, tool); + + return tool; + +} + +} +#include "MatrixToolBox.moc" diff --git a/src/gui/editors/matrix/MatrixToolBox.h b/src/gui/editors/matrix/MatrixToolBox.h new file mode 100644 index 0000000..3bf0818 --- /dev/null +++ b/src/gui/editors/matrix/MatrixToolBox.h @@ -0,0 +1,60 @@ + +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_MATRIXTOOLBOX_H_ +#define _RG_MATRIXTOOLBOX_H_ + +#include "gui/general/EditToolBox.h" + +class QString; + + +namespace Rosegarden +{ + +class EditTool; +class MatrixView; +class MatrixElement; +class MatrixStaff; + +class MatrixToolBox : public EditToolBox +{ + Q_OBJECT +public: + MatrixToolBox(MatrixView* parent); + +protected: + + virtual EditTool* createTool(const QString& toolName); + + //--------------- Data members --------------------------------- + + MatrixView* m_mParentView; +}; + + +} + +#endif diff --git a/src/gui/editors/matrix/MatrixVLayout.cpp b/src/gui/editors/matrix/MatrixVLayout.cpp new file mode 100644 index 0000000..aadcdf3 --- /dev/null +++ b/src/gui/editors/matrix/MatrixVLayout.cpp @@ -0,0 +1,100 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#include "MatrixVLayout.h" +#include "misc/Debug.h" + +#include "base/BaseProperties.h" +#include "base/LayoutEngine.h" +#include "base/Staff.h" +#include "MatrixElement.h" +#include "MatrixStaff.h" + + +namespace Rosegarden +{ + +MatrixVLayout::MatrixVLayout() +{} + +MatrixVLayout::~MatrixVLayout() +{} + +void MatrixVLayout::reset() +{} + +void MatrixVLayout::resetStaff(Staff&, timeT, timeT) +{} + +void MatrixVLayout::scanStaff(Staff& staffBase, + timeT startTime, timeT endTime) +{ + MatrixStaff& staff = dynamic_cast<MatrixStaff&>(staffBase); + + using BaseProperties::PITCH; + + MatrixElementList *notes = staff.getViewElementList(); + + MatrixElementList::iterator from = notes->begin(); + MatrixElementList::iterator to = notes->end(); + MatrixElementList::iterator i; + + if (startTime != endTime) { + from = notes->findNearestTime(startTime); + if (from == notes->end()) + from = notes->begin(); + to = notes->findTime(endTime); + } + + MATRIX_DEBUG << "MatrixVLayout::scanStaff : id = " + << staff.getId() << endl; + + + for (i = from; i != to; ++i) { + + MatrixElement *el = dynamic_cast<MatrixElement*>((*i)); + + if (!el->isNote()) + continue; // notes only + + long pitch = 60; + el->event()->get + <Int>(PITCH, pitch); + + int y = staff.getLayoutYForHeight(pitch) - staff.getElementHeight() / 2; + + el->setLayoutY(y); + el->setHeight(staff.getElementHeight()); + } + +} + +void MatrixVLayout::finishLayout(timeT, timeT) +{} + +const int MatrixVLayout::minMIDIPitch = 0; +const int MatrixVLayout::maxMIDIPitch = 127; + +} diff --git a/src/gui/editors/matrix/MatrixVLayout.h b/src/gui/editors/matrix/MatrixVLayout.h new file mode 100644 index 0000000..a33e0d1 --- /dev/null +++ b/src/gui/editors/matrix/MatrixVLayout.h @@ -0,0 +1,91 @@ + +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_MATRIXVLAYOUT_H_ +#define _RG_MATRIXVLAYOUT_H_ + +#include "base/LayoutEngine.h" +#include "base/Event.h" + + + + +namespace Rosegarden +{ + +class Staff; + + +class MatrixVLayout : public VerticalLayoutEngine +{ +public: + MatrixVLayout(); + + virtual ~MatrixVLayout(); + + /** + * Resets internal data stores for all staffs + */ + virtual void reset(); + + /** + * Resets internal data stores for a specific staff + */ + virtual void resetStaff(Staff &staff, + timeT = 0, + timeT = 0); + + /** + * Precomputes layout data for a single staff, updating any + * internal data stores associated with that staff and updating + * any layout-related properties in the events on the staff's + * segment. + */ + virtual void scanStaff(Staff &staff, + timeT = 0, + timeT = 0); + + /** + * Computes any layout data that may depend on the results of + * scanning more than one staff. This may mean doing most of + * the layout (likely for horizontal layout) or nothing at all + * (likely for vertical layout). + */ + virtual void finishLayout(timeT = 0, + timeT = 0); + + static const int minMIDIPitch; + static const int maxMIDIPitch; + +protected: + //--------------- Data members --------------------------------- + + +}; + + +} + +#endif diff --git a/src/gui/editors/matrix/MatrixView.cpp b/src/gui/editors/matrix/MatrixView.cpp new file mode 100644 index 0000000..38abe20 --- /dev/null +++ b/src/gui/editors/matrix/MatrixView.cpp @@ -0,0 +1,3076 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#include "MatrixView.h" + +#include "base/BaseProperties.h" +#include "misc/Debug.h" +#include "misc/Strings.h" +#include "base/AudioLevel.h" +#include "base/Clipboard.h" +#include "base/Composition.h" +#include "base/Event.h" +#include "base/Instrument.h" +#include "base/LayoutEngine.h" +#include "base/MidiProgram.h" +#include "base/NotationTypes.h" +#include "base/Profiler.h" +#include "base/PropertyName.h" +#include "base/BasicQuantizer.h" +#include "base/LegatoQuantizer.h" +#include "base/RealTime.h" +#include "base/RulerScale.h" +#include "base/Segment.h" +#include "base/Selection.h" +#include "base/SnapGrid.h" +#include "base/Staff.h" +#include "base/Studio.h" +#include "base/Track.h" +#include "commands/edit/ChangeVelocityCommand.h" +#include "commands/edit/ClearTriggersCommand.h" +#include "commands/edit/CollapseNotesCommand.h" +#include "commands/edit/CopyCommand.h" +#include "commands/edit/CutCommand.h" +#include "commands/edit/EraseCommand.h" +#include "commands/edit/EventQuantizeCommand.h" +#include "commands/edit/EventUnquantizeCommand.h" +#include "commands/edit/PasteEventsCommand.h" +#include "commands/edit/SelectionPropertyCommand.h" +#include "commands/edit/SetTriggerCommand.h" +#include "commands/matrix/MatrixInsertionCommand.h" +#include "document/RosegardenGUIDoc.h" +#include "document/ConfigGroups.h" +#include "gui/application/RosegardenGUIApp.h" +#include "gui/dialogs/EventFilterDialog.h" +#include "gui/dialogs/EventParameterDialog.h" +#include "gui/dialogs/QuantizeDialog.h" +#include "gui/dialogs/TriggerSegmentDialog.h" +#include "gui/editors/guitar/Chord.h" +#include "gui/editors/notation/NotationElement.h" +#include "gui/editors/notation/NotationStrings.h" +#include "gui/editors/notation/NotePixmapFactory.h" +#include "gui/editors/parameters/InstrumentParameterBox.h" +#include "gui/rulers/StandardRuler.h" +#include "gui/general/ActiveItem.h" +#include "gui/general/EditViewBase.h" +#include "gui/general/EditView.h" +#include "gui/general/GUIPalette.h" +#include "gui/general/MidiPitchLabel.h" +#include "gui/kdeext/KTmpStatusMsg.h" +#include "gui/rulers/ChordNameRuler.h" +#include "gui/rulers/LoopRuler.h" +#include "gui/rulers/PercussionPitchRuler.h" +#include "gui/rulers/PitchRuler.h" +#include "gui/rulers/PropertyBox.h" +#include "gui/rulers/PropertyViewRuler.h" +#include "gui/rulers/TempoRuler.h" +#include "gui/studio/StudioControl.h" +#include "gui/widgets/QDeferScrollView.h" +#include "MatrixCanvasView.h" +#include "MatrixElement.h" +#include "MatrixEraser.h" +#include "MatrixHLayout.h" +#include "MatrixMover.h" +#include "MatrixPainter.h" +#include "MatrixResizer.h" +#include "MatrixSelector.h" +#include "MatrixStaff.h" +#include "MatrixToolBox.h" +#include "MatrixVLayout.h" +#include "PianoKeyboard.h" +#include "sound/MappedEvent.h" +#include "sound/SequencerDataBlock.h" +#include <klocale.h> +#include <kstddirs.h> +#include <kaction.h> +#include <kcombobox.h> +#include <kconfig.h> +#include <kdockwidget.h> +#include <kglobal.h> +#include <kmessagebox.h> +#include <kstatusbar.h> +#include <ktoolbar.h> +#include <kxmlguiclient.h> +#include <qcanvas.h> +#include <qcursor.h> +#include <qdialog.h> +#include <qlayout.h> +#include <qiconset.h> +#include <qlabel.h> +#include <qpixmap.h> +#include <qpoint.h> +#include <qscrollview.h> +#include <qsize.h> +#include <qslider.h> +#include <qstring.h> +#include <qwidget.h> +#include <qwmatrix.h> + + +namespace Rosegarden +{ + +static double xorigin = 0.0; + + +MatrixView::MatrixView(RosegardenGUIDoc *doc, + std::vector<Segment *> segments, + QWidget *parent, + bool drumMode) + : EditView(doc, segments, 3, parent, "matrixview"), + m_hlayout(&doc->getComposition()), + m_referenceRuler(new ZoomableMatrixHLayoutRulerScale(m_hlayout)), + m_vlayout(), + m_snapGrid(new SnapGrid(&m_hlayout)), + m_lastEndMarkerTime(0), + m_hoveredOverAbsoluteTime(0), + m_hoveredOverNoteName(0), + m_selectionCounter(0), + m_insertModeLabel(0), + m_haveHoveredOverNote(false), + m_previousEvPitch(0), + m_dockLeft(0), + m_canvasView(0), + m_pianoView(0), + m_localMapping(0), + m_lastNote(0), + m_quantizations(BasicQuantizer::getStandardQuantizations()), + m_chordNameRuler(0), + m_tempoRuler(0), + m_playTracking(true), + m_dockVisible(true), + m_drumMode(drumMode), + m_mouseInCanvasView(false) +{ + RG_DEBUG << "MatrixView ctor: drumMode " << drumMode << "\n"; + + QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/toolbar"); + QPixmap matrixPixmap(pixmapDir + "/matrix.xpm"); + + m_dockLeft = createDockWidget("params dock", matrixPixmap, 0L, + i18n("Instrument Parameters")); + m_dockLeft->manualDock(m_mainDockWidget, // dock target + KDockWidget::DockLeft, // dock site + 20); // relation target/this (in percent) + + connect(m_dockLeft, SIGNAL(iMBeingClosed()), + this, SLOT(slotParametersClosed())); + connect(m_dockLeft, SIGNAL(hasUndocked()), + this, SLOT(slotParametersClosed())); + // Apparently, hasUndocked() is emitted when the dock widget's + // 'close' button on the dock handle is clicked. + connect(m_mainDockWidget, SIGNAL(docking(KDockWidget*, KDockWidget::DockPosition)), + this, SLOT(slotParametersDockedBack(KDockWidget*, KDockWidget::DockPosition))); + + Composition &comp = doc->getComposition(); + + m_toolBox = new MatrixToolBox(this); + + initStatusBar(); + + connect(m_toolBox, SIGNAL(showContextHelp(const QString &)), + this, SLOT(slotToolHelpChanged(const QString &))); + + QCanvas *tCanvas = new QCanvas(this); + + m_config->setGroup(MatrixViewConfigGroup); + if (m_config->readBoolEntry("backgroundtextures-1.6-plus", true)) { + QPixmap background; + QString pixmapDir = + KGlobal::dirs()->findResource("appdata", "pixmaps/"); + // We now use a lined background for the non-percussion matrix, + // suggested and supplied by Alessandro Preziosi + QString backgroundPixmap = isDrumMode() ? "bg-paper-white.xpm" : "bg-matrix-lines.xpm"; + if (background.load(QString("%1/misc/%2"). + arg(pixmapDir, backgroundPixmap))) { + tCanvas->setBackgroundPixmap(background); + } + } + + MATRIX_DEBUG << "MatrixView : creating staff\n"; + + Track *track = + comp.getTrackById(segments[0]->getTrack()); + + Instrument *instr = getDocument()->getStudio(). + getInstrumentById(track->getInstrument()); + + int resolution = 8; + + if (isDrumMode() && instr && instr->getKeyMapping()) { + resolution = 11; + } + + for (unsigned int i = 0; i < segments.size(); ++i) { + m_staffs.push_back(new MatrixStaff(tCanvas, + segments[i], + m_snapGrid, + i, + resolution, + this)); + // staff has one too many rows to avoid a half-row at the top: + m_staffs[i]->setY( -resolution / 2); + //!!! if (isDrumMode()) m_staffs[i]->setX(resolution); + if (i == 0) + m_staffs[i]->setCurrent(true); + } + + MATRIX_DEBUG << "MatrixView : creating canvas view\n"; + + const MidiKeyMapping *mapping = 0; + + if (instr) { + mapping = instr->getKeyMapping(); + if (mapping) { + RG_DEBUG << "MatrixView: Instrument has key mapping: " + << mapping->getName() << endl; + m_localMapping = new MidiKeyMapping(*mapping); + extendKeyMapping(); + } else { + RG_DEBUG << "MatrixView: Instrument has no key mapping\n"; + } + } + + m_pianoView = new QDeferScrollView(getCentralWidget()); + + QWidget* vport = m_pianoView->viewport(); + + if (isDrumMode() && mapping && + !m_localMapping->getMap().empty()) { + m_pitchRuler = new PercussionPitchRuler(vport, + m_localMapping, + resolution); // line spacing + } else { + m_pitchRuler = new PianoKeyboard(vport); + } + + m_pianoView->setVScrollBarMode(QScrollView::AlwaysOff); + m_pianoView->setHScrollBarMode(QScrollView::AlwaysOff); + m_pianoView->addChild(m_pitchRuler); + m_pianoView->setFixedWidth(m_pianoView->contentsWidth()); + + m_grid->addWidget(m_pianoView, CANVASVIEW_ROW, 1); + + m_parameterBox = new InstrumentParameterBox(getDocument(), m_dockLeft); + m_dockLeft->setWidget(m_parameterBox); + + RosegardenGUIApp *app = RosegardenGUIApp::self(); + connect(app, + SIGNAL(pluginSelected(InstrumentId, int, int)), + m_parameterBox, + SLOT(slotPluginSelected(InstrumentId, int, int))); + connect(app, + SIGNAL(pluginBypassed(InstrumentId, int, bool)), + m_parameterBox, + SLOT(slotPluginBypassed(InstrumentId, int, bool))); + connect(app, + SIGNAL(instrumentParametersChanged(InstrumentId)), + m_parameterBox, + SLOT(slotInstrumentParametersChanged(InstrumentId))); + connect(m_parameterBox, + SIGNAL(instrumentParametersChanged(InstrumentId)), + app, + SIGNAL(instrumentParametersChanged(InstrumentId))); + connect(m_parameterBox, + SIGNAL(selectPlugin(QWidget *, InstrumentId, int)), + app, + SLOT(slotShowPluginDialog(QWidget *, InstrumentId, int))); + connect(m_parameterBox, + SIGNAL(showPluginGUI(InstrumentId, int)), + app, + SLOT(slotShowPluginGUI(InstrumentId, int))); + connect(parent, // RosegardenGUIView + SIGNAL(checkTrackAssignments()), + this, + SLOT(slotCheckTrackAssignments())); + + // Assign the instrument + // + m_parameterBox->useInstrument(instr); + + if (m_drumMode) { + connect(m_parameterBox, + SIGNAL(instrumentPercussionSetChanged(Instrument *)), + this, + SLOT(slotPercussionSetChanged(Instrument *))); + } + + // Set the snap grid from the stored size in the segment + // + int snapGridSize = m_staffs[0]->getSegment().getSnapGridSize(); + + MATRIX_DEBUG << "MatrixView : Snap Grid Size = " << snapGridSize << endl; + + if (snapGridSize != -1) { + m_snapGrid->setSnapTime(snapGridSize); + } else { + m_config->setGroup(MatrixViewConfigGroup); + snapGridSize = m_config->readNumEntry + ("Snap Grid Size", SnapGrid::SnapToBeat); + m_snapGrid->setSnapTime(snapGridSize); + m_staffs[0]->getSegment().setSnapGridSize(snapGridSize); + } + + m_canvasView = new MatrixCanvasView(*m_staffs[0], + m_snapGrid, + m_drumMode, + tCanvas, + getCentralWidget()); + setCanvasView(m_canvasView); + + // do this after we have a canvas + setupActions(); + setupAddControlRulerMenu(); + + stateChanged("parametersbox_closed", KXMLGUIClient::StateReverse); + + // tool bars + initActionsToolbar(); + initZoomToolbar(); + + // Connect vertical scrollbars between matrix and piano + // + connect(m_canvasView->verticalScrollBar(), SIGNAL(valueChanged(int)), + this, SLOT(slotVerticalScrollPianoKeyboard(int))); + + connect(m_canvasView->verticalScrollBar(), SIGNAL(sliderMoved(int)), + this, SLOT(slotVerticalScrollPianoKeyboard(int))); + + connect(m_canvasView, SIGNAL(zoomIn()), this, SLOT(slotZoomIn())); + connect(m_canvasView, SIGNAL(zoomOut()), this, SLOT(slotZoomOut())); + + connect(m_pianoView, SIGNAL(gotWheelEvent(QWheelEvent*)), + m_canvasView, SLOT(slotExternalWheelEvent(QWheelEvent*))); + + // ensure the piano keyb keeps the right margins when the user toggles + // the canvas view rulers + // + connect(m_canvasView, SIGNAL(bottomWidgetHeightChanged(int)), + this, SLOT(slotCanvasBottomWidgetHeightChanged(int))); + + connect(m_canvasView, SIGNAL(mouseEntered()), + this, SLOT(slotMouseEnteredCanvasView())); + + connect(m_canvasView, SIGNAL(mouseLeft()), + this, SLOT(slotMouseLeftCanvasView())); + + /* + QObject::connect + (getCanvasView(), SIGNAL(activeItemPressed(QMouseEvent*, QCanvasItem*)), + this, SLOT (activeItemPressed(QMouseEvent*, QCanvasItem*))); + */ + + QObject::connect + (getCanvasView(), + SIGNAL(mousePressed(timeT, + int, QMouseEvent*, MatrixElement*)), + this, + SLOT(slotMousePressed(timeT, + int, QMouseEvent*, MatrixElement*))); + + QObject::connect + (getCanvasView(), + SIGNAL(mouseMoved(timeT, int, QMouseEvent*)), + this, + SLOT(slotMouseMoved(timeT, int, QMouseEvent*))); + + QObject::connect + (getCanvasView(), + SIGNAL(mouseReleased(timeT, int, QMouseEvent*)), + this, + SLOT(slotMouseReleased(timeT, int, QMouseEvent*))); + + QObject::connect + (getCanvasView(), SIGNAL(hoveredOverNoteChanged(int, bool, timeT)), + this, SLOT(slotHoveredOverNoteChanged(int, bool, timeT))); + + QObject::connect + (m_pitchRuler, SIGNAL(hoveredOverKeyChanged(unsigned int)), + this, SLOT (slotHoveredOverKeyChanged(unsigned int))); + + QObject::connect + (m_pitchRuler, SIGNAL(keyPressed(unsigned int, bool)), + this, SLOT (slotKeyPressed(unsigned int, bool))); + + QObject::connect + (m_pitchRuler, SIGNAL(keySelected(unsigned int, bool)), + this, SLOT (slotKeySelected(unsigned int, bool))); + + QObject::connect + (m_pitchRuler, SIGNAL(keyReleased(unsigned int, bool)), + this, SLOT (slotKeyReleased(unsigned int, bool))); + + QObject::connect + (getCanvasView(), SIGNAL(hoveredOverAbsoluteTimeChanged(unsigned int)), + this, SLOT (slotHoveredOverAbsoluteTimeChanged(unsigned int))); + + QObject::connect + (doc, SIGNAL(pointerPositionChanged(timeT)), + this, SLOT(slotSetPointerPosition(timeT))); + + MATRIX_DEBUG << "MatrixView : applying layout\n"; + + bool layoutApplied = applyLayout(); + if (!layoutApplied) + KMessageBox::sorry(0, i18n("Couldn't apply piano roll layout")); + else { + MATRIX_DEBUG << "MatrixView : rendering elements\n"; + for (unsigned int i = 0; i < m_staffs.size(); ++i) { + + m_staffs[i]->positionAllElements(); + m_staffs[i]->getSegment().getRefreshStatus + (m_segmentsRefreshStatusIds[i]).setNeedsRefresh(false); + } + } + + StandardRuler *topStandardRuler = new StandardRuler(getDocument(), + &m_hlayout, int(xorigin), 25, + false, getCentralWidget()); + topStandardRuler->setSnapGrid(m_snapGrid); + setTopStandardRuler(topStandardRuler); + + StandardRuler *bottomStandardRuler = new StandardRuler(getDocument(), + &m_hlayout, 0, 25, + true, getBottomWidget()); + bottomStandardRuler->setSnapGrid(m_snapGrid); + setBottomStandardRuler(bottomStandardRuler); + + topStandardRuler->connectRulerToDocPointer(doc); + bottomStandardRuler->connectRulerToDocPointer(doc); + + // Disconnect the default connections for this signal from the + // top ruler, and connect our own instead + + QObject::disconnect + (topStandardRuler->getLoopRuler(), + SIGNAL(setPointerPosition(timeT)), 0, 0); + + QObject::connect + (topStandardRuler->getLoopRuler(), + SIGNAL(setPointerPosition(timeT)), + this, SLOT(slotSetInsertCursorPosition(timeT))); + + QObject::connect + (topStandardRuler, + SIGNAL(dragPointerToPosition(timeT)), + this, SLOT(slotSetInsertCursorPosition(timeT))); + + topStandardRuler->getLoopRuler()->setBackgroundColor + (GUIPalette::getColour(GUIPalette::InsertCursorRuler)); + + connect(topStandardRuler->getLoopRuler(), SIGNAL(startMouseMove(int)), + m_canvasView, SLOT(startAutoScroll(int))); + connect(topStandardRuler->getLoopRuler(), SIGNAL(stopMouseMove()), + m_canvasView, SLOT(stopAutoScroll())); + + connect(bottomStandardRuler->getLoopRuler(), SIGNAL(startMouseMove(int)), + m_canvasView, SLOT(startAutoScroll(int))); + connect(bottomStandardRuler->getLoopRuler(), SIGNAL(stopMouseMove()), + m_canvasView, SLOT(stopAutoScroll())); + connect(m_bottomStandardRuler, SIGNAL(dragPointerToPosition(timeT)), + this, SLOT(slotSetPointerPosition(timeT))); + + // Force height for the moment + // + m_pitchRuler->setFixedHeight(canvas()->height()); + + + updateViewCaption(); + + // Add a velocity ruler + // + //!!! addPropertyViewRuler(BaseProperties::VELOCITY); + + m_chordNameRuler = new ChordNameRuler + (m_referenceRuler, doc, segments, 0, 20, getCentralWidget()); + m_chordNameRuler->setStudio(&getDocument()->getStudio()); + addRuler(m_chordNameRuler); + + m_tempoRuler = new TempoRuler + (m_referenceRuler, doc, this, 0, 24, false, getCentralWidget()); + static_cast<TempoRuler *>(m_tempoRuler)->connectSignals(); + addRuler(m_tempoRuler); + + stateChanged("have_selection", KXMLGUIClient::StateReverse); + slotTestClipboard(); + + timeT start = doc->getComposition().getLoopStart(); + timeT end = doc->getComposition().getLoopEnd(); + m_topStandardRuler->getLoopRuler()->slotSetLoopMarker(start, end); + m_bottomStandardRuler->getLoopRuler()->slotSetLoopMarker(start, end); + + setCurrentSelection(0, false); + + // Change this if the matrix view ever has its own page + // in the config dialog. + setConfigDialogPageIndex(0); + + // default zoom + m_config->setGroup(MatrixViewConfigGroup); + double zoom = m_config->readDoubleNumEntry("Zoom Level", + m_hZoomSlider->getCurrentSize()); + m_hZoomSlider->setSize(zoom); + m_referenceRuler->setHScaleFactor(zoom); + + // Scroll view to centre middle-C and warp to pointer position + // + m_canvasView->scrollBy(0, m_staffs[0]->getCanvasYForHeight(60) / 2); + + slotSetPointerPosition(comp.getPosition()); + + // All toolbars should be created before this is called + setAutoSaveSettings("MatrixView", true); + + readOptions(); + setOutOfCtor(); + + // Property and Control Rulers + // + if (getCurrentSegment()->getViewFeatures()) + slotShowVelocityControlRuler(); + setupControllerTabs(); + + setRewFFwdToAutoRepeat(); + slotCompositionStateUpdate(); +} + +MatrixView::~MatrixView() +{ + slotSaveOptions(); + + delete m_chordNameRuler; + + for (unsigned int i = 0; i < m_staffs.size(); ++i) { + delete m_staffs[i]; // this will erase all "notes" canvas items + } + + // This looks silly but the reason is that on destruction of the + // MatrixCanvasView, setCanvas() is called (this is in + // ~QCanvasView so we can't do anything about it). This calls + // QCanvasView::updateContentsSize(), which in turn updates the + // view's scrollbars, hence calling QScrollBar::setValue(), and + // sending the QSCrollbar::valueChanged() signal. But we have a + // slot connected to that signal + // (MatrixView::slotVerticalScrollPianoKeyboard), which scrolls + // the pianoView. However at this stage the pianoView has already + // been deleted, so a likely outcome is a crash. + // + // A solution is to zero out m_pianoView here, and to check if + // it's non null in slotVerticalScrollPianoKeyboard. + // + m_pianoView = 0; + + delete m_snapGrid; + + if (m_localMapping) + delete m_localMapping; +} + +void MatrixView::slotSaveOptions() +{ + m_config->setGroup(MatrixViewConfigGroup); + + m_config->writeEntry("Show Chord Name Ruler", getToggleAction("show_chords_ruler")->isChecked()); + m_config->writeEntry("Show Tempo Ruler", getToggleAction("show_tempo_ruler")->isChecked()); + m_config->writeEntry("Show Parameters", m_dockVisible); + //getToggleAction("m_dockLeft->isVisible()); + + m_config->sync(); +} + +void MatrixView::readOptions() +{ + EditView::readOptions(); + m_config->setGroup(MatrixViewConfigGroup); + + bool opt = false; + + opt = m_config->readBoolEntry("Show Chord Name Ruler", false); + getToggleAction("show_chords_ruler")->setChecked(opt); + slotToggleChordsRuler(); + + opt = m_config->readBoolEntry("Show Tempo Ruler", true); + getToggleAction("show_tempo_ruler")->setChecked(opt); + slotToggleTempoRuler(); + + opt = m_config->readBoolEntry("Show Parameters", true); + if (!opt) { + m_dockLeft->undock(); + m_dockLeft->hide(); + stateChanged("parametersbox_closed", KXMLGUIClient::StateNoReverse); + m_dockVisible = false; + } + +} + +void MatrixView::setupActions() +{ + EditViewBase::setupActions("matrix.rc"); + EditView::setupActions(); + + // + // Edition tools (eraser, selector...) + // + KRadioAction* toolAction = 0; + + QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/"); + QIconSet icon(QPixmap(pixmapDir + "/toolbar/select.xpm")); + + toolAction = new KRadioAction(i18n("&Select and Edit"), icon, Key_F2, + this, SLOT(slotSelectSelected()), + actionCollection(), "select"); + toolAction->setExclusiveGroup("tools"); + + toolAction = new KRadioAction(i18n("&Draw"), "pencil", Key_F3, + this, SLOT(slotPaintSelected()), + actionCollection(), "draw"); + toolAction->setExclusiveGroup("tools"); + + toolAction = new KRadioAction(i18n("&Erase"), "eraser", Key_F4, + this, SLOT(slotEraseSelected()), + actionCollection(), "erase"); + toolAction->setExclusiveGroup("tools"); + + toolAction = new KRadioAction(i18n("&Move"), "move", Key_F5, + this, SLOT(slotMoveSelected()), + actionCollection(), "move"); + toolAction->setExclusiveGroup("tools"); + + QCanvasPixmap pixmap(pixmapDir + "/toolbar/resize.xpm"); + icon = QIconSet(pixmap); + toolAction = new KRadioAction(i18n("Resi&ze"), icon, Key_F6, + this, SLOT(slotResizeSelected()), + actionCollection(), "resize"); + toolAction->setExclusiveGroup("tools"); + + icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("chord"))); + (new KToggleAction(i18n("C&hord Insert Mode"), icon, Key_H, + this, SLOT(slotUpdateInsertModeStatus()), + actionCollection(), "chord_mode"))-> + setChecked(false); + + pixmap.load(pixmapDir + "/toolbar/step_by_step.xpm"); + icon = QIconSet(pixmap); + new KToggleAction(i18n("Ste&p Recording"), icon, 0, this, + SLOT(slotToggleStepByStep()), actionCollection(), + "toggle_step_by_step"); + + pixmap.load(pixmapDir + "/toolbar/quantize.png"); + icon = QIconSet(pixmap); + new KAction(EventQuantizeCommand::getGlobalName(), icon, Key_Equal, this, + SLOT(slotTransformsQuantize()), actionCollection(), + "quantize"); + + new KAction(i18n("Repeat Last Quantize"), Key_Plus, this, + SLOT(slotTransformsRepeatQuantize()), actionCollection(), + "repeat_quantize"); + + new KAction(CollapseNotesCommand::getGlobalName(), Key_Equal + CTRL, this, + SLOT(slotTransformsCollapseNotes()), actionCollection(), + "collapse_notes"); + + new KAction(i18n("&Legato"), Key_Minus, this, + SLOT(slotTransformsLegato()), actionCollection(), + "legatoize"); + + new KAction(ChangeVelocityCommand::getGlobalName(10), 0, + Key_Up + SHIFT, this, + SLOT(slotVelocityUp()), actionCollection(), + "velocity_up"); + + new KAction(ChangeVelocityCommand::getGlobalName( -10), 0, + Key_Down + SHIFT, this, + SLOT(slotVelocityDown()), actionCollection(), + "velocity_down"); + + new KAction(i18n("Set to Current Velocity"), 0, this, + SLOT(slotSetVelocitiesToCurrent()), actionCollection(), + "set_to_current_velocity"); + + new KAction(i18n("Set Event &Velocities..."), 0, this, + SLOT(slotSetVelocities()), actionCollection(), + "set_velocities"); + + new KAction(i18n("Trigger Se&gment..."), 0, this, + SLOT(slotTriggerSegment()), actionCollection(), + "trigger_segment"); + + new KAction(i18n("Remove Triggers..."), 0, this, + SLOT(slotRemoveTriggers()), actionCollection(), + "remove_trigger"); + + new KAction(i18n("Select &All"), Key_A + CTRL, this, + SLOT(slotSelectAll()), actionCollection(), + "select_all"); + + new KAction(i18n("&Delete"), Key_Delete, this, + SLOT(slotEditDelete()), actionCollection(), + "delete"); + + new KAction(i18n("Cursor &Back"), 0, Key_Left, this, + SLOT(slotStepBackward()), actionCollection(), + "cursor_back"); + + new KAction(i18n("Cursor &Forward"), 0, Key_Right, this, + SLOT(slotStepForward()), actionCollection(), + "cursor_forward"); + + new KAction(i18n("Cursor Ba&ck Bar"), 0, Key_Left + CTRL, this, + SLOT(slotJumpBackward()), actionCollection(), + "cursor_back_bar"); + + new KAction(i18n("Cursor For&ward Bar"), 0, Key_Right + CTRL, this, + SLOT(slotJumpForward()), actionCollection(), + "cursor_forward_bar"); + + new KAction(i18n("Cursor Back and Se&lect"), SHIFT + Key_Left, this, + SLOT(slotExtendSelectionBackward()), actionCollection(), + "extend_selection_backward"); + + new KAction(i18n("Cursor Forward and &Select"), SHIFT + Key_Right, this, + SLOT(slotExtendSelectionForward()), actionCollection(), + "extend_selection_forward"); + + new KAction(i18n("Cursor Back Bar and Select"), SHIFT + CTRL + Key_Left, this, + SLOT(slotExtendSelectionBackwardBar()), actionCollection(), + "extend_selection_backward_bar"); + + new KAction(i18n("Cursor Forward Bar and Select"), SHIFT + CTRL + Key_Right, this, + SLOT(slotExtendSelectionForwardBar()), actionCollection(), + "extend_selection_forward_bar"); + + new KAction(i18n("Cursor to St&art"), 0, + /* #1025717: conflicting meanings for ctrl+a - dupe with Select All + Key_A + CTRL, */ this, + SLOT(slotJumpToStart()), actionCollection(), + "cursor_start"); + + new KAction(i18n("Cursor to &End"), 0, Key_E + CTRL, this, + SLOT(slotJumpToEnd()), actionCollection(), + "cursor_end"); + + icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap + ("transport-cursor-to-pointer"))); + new KAction(i18n("Cursor to &Playback Pointer"), icon, 0, this, + SLOT(slotJumpCursorToPlayback()), actionCollection(), + "cursor_to_playback_pointer"); + + icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap + ("transport-play"))); + KAction *play = new KAction(i18n("&Play"), icon, Key_Enter, this, + SIGNAL(play()), actionCollection(), "play"); + // Alternative shortcut for Play + KShortcut playShortcut = play->shortcut(); + playShortcut.append( KKey(Key_Return + CTRL) ); + play->setShortcut(playShortcut); + + icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap + ("transport-stop"))); + new KAction(i18n("&Stop"), icon, Key_Insert, this, + SIGNAL(stop()), actionCollection(), "stop"); + + icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap + ("transport-rewind"))); + new KAction(i18n("Re&wind"), icon, Key_End, this, + SIGNAL(rewindPlayback()), actionCollection(), + "playback_pointer_back_bar"); + + icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap + ("transport-ffwd"))); + new KAction(i18n("&Fast Forward"), icon, Key_PageDown, this, + SIGNAL(fastForwardPlayback()), actionCollection(), + "playback_pointer_forward_bar"); + + icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap + ("transport-rewind-end"))); + new KAction(i18n("Rewind to &Beginning"), icon, 0, this, + SIGNAL(rewindPlaybackToBeginning()), actionCollection(), + "playback_pointer_start"); + + icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap + ("transport-ffwd-end"))); + new KAction(i18n("Fast Forward to &End"), icon, 0, this, + SIGNAL(fastForwardPlaybackToEnd()), actionCollection(), + "playback_pointer_end"); + + icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap + ("transport-pointer-to-cursor"))); + new KAction(i18n("Playback Pointer to &Cursor"), icon, 0, this, + SLOT(slotJumpPlaybackToCursor()), actionCollection(), + "playback_pointer_to_cursor"); + + icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap + ("transport-solo"))); + new KToggleAction(i18n("&Solo"), icon, 0, this, + SLOT(slotToggleSolo()), actionCollection(), + "toggle_solo"); + + icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap + ("transport-tracking"))); + (new KToggleAction(i18n("Scro&ll to Follow Playback"), icon, Key_Pause, this, + SLOT(slotToggleTracking()), actionCollection(), + "toggle_tracking"))->setChecked(m_playTracking); + + icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap + ("transport-panic"))); + new KAction(i18n("Panic"), icon, Key_P + CTRL + ALT, this, + SIGNAL(panic()), actionCollection(), "panic"); + + new KAction(i18n("Set Loop to Selection"), Key_Semicolon + CTRL, this, + SLOT(slotPreviewSelection()), actionCollection(), + "preview_selection"); + + new KAction(i18n("Clear L&oop"), Key_Colon + CTRL, this, + SLOT(slotClearLoop()), actionCollection(), + "clear_loop"); + + new KAction(i18n("Clear Selection"), Key_Escape, this, + SLOT(slotClearSelection()), actionCollection(), + "clear_selection"); + + // icon = QIconSet(QCanvasPixmap(pixmapDir + "/toolbar/eventfilter.xpm")); + new KAction(i18n("&Filter Selection"), "filter", Key_F + CTRL, this, + SLOT(slotFilterSelection()), actionCollection(), + "filter_selection"); + + timeT crotchetDuration = Note(Note::Crotchet).getDuration(); + m_snapValues.push_back(SnapGrid::NoSnap); + m_snapValues.push_back(SnapGrid::SnapToUnit); + m_snapValues.push_back(crotchetDuration / 16); + m_snapValues.push_back(crotchetDuration / 12); + m_snapValues.push_back(crotchetDuration / 8); + m_snapValues.push_back(crotchetDuration / 6); + m_snapValues.push_back(crotchetDuration / 4); + m_snapValues.push_back(crotchetDuration / 3); + m_snapValues.push_back(crotchetDuration / 2); + m_snapValues.push_back(crotchetDuration); + m_snapValues.push_back((crotchetDuration * 3) / 2); + m_snapValues.push_back(crotchetDuration * 2); + m_snapValues.push_back(SnapGrid::SnapToBeat); + m_snapValues.push_back(SnapGrid::SnapToBar); + + for (unsigned int i = 0; i < m_snapValues.size(); i++) { + + timeT d = m_snapValues[i]; + + if (d == SnapGrid::NoSnap) { + new KAction(i18n("&No Snap"), 0, this, + SLOT(slotSetSnapFromAction()), + actionCollection(), "snap_none"); + } else if (d == SnapGrid::SnapToUnit) { + } else if (d == SnapGrid::SnapToBeat) { + new KAction(i18n("Snap to Bea&t"), Key_1, this, + SLOT(slotSetSnapFromAction()), + actionCollection(), "snap_beat"); + } else if (d == SnapGrid::SnapToBar) { + new KAction(i18n("Snap to &Bar"), Key_5, this, + SLOT(slotSetSnapFromAction()), + actionCollection(), "snap_bar"); + } else { + + timeT err = 0; + QString label = NotationStrings::makeNoteMenuLabel(d, true, err); + QPixmap pixmap = NotePixmapFactory::toQPixmap + (NotePixmapFactory::makeNoteMenuPixmap(d, err)); + + KShortcut cut = 0; + if (d == crotchetDuration / 16) cut = Key_0; + else if (d == crotchetDuration / 8) cut = Key_3; + else if (d == crotchetDuration / 4) cut = Key_6; + else if (d == crotchetDuration / 2) cut = Key_8; + else if (d == crotchetDuration) cut = Key_4; + else if (d == crotchetDuration * 2) cut = Key_2; + + QString actionName = QString("snap_%1").arg(int((crotchetDuration * 4) / d)); + if (d == (crotchetDuration * 3) / 2) actionName = "snap_3"; + new KAction(i18n("Snap to %1").arg(label), pixmap, cut, this, + SLOT(slotSetSnapFromAction()), actionCollection(), + actionName); + } + } + + // + // Settings menu + // + new KAction(i18n("Show Instrument Parameters"), 0, this, + SLOT(slotDockParametersBack()), + actionCollection(), + "show_inst_parameters"); + + new KToggleAction(i18n("Show Ch&ord Name Ruler"), 0, this, + SLOT(slotToggleChordsRuler()), + actionCollection(), "show_chords_ruler"); + + new KToggleAction(i18n("Show &Tempo Ruler"), 0, this, + SLOT(slotToggleTempoRuler()), + actionCollection(), "show_tempo_ruler"); + + createGUI(getRCFileName(), false); + + if (getSegmentsOnlyRestsAndClefs()) + actionCollection()->action("draw")->activate(); + else + actionCollection()->action("select")->activate(); +} + +bool +MatrixView::isInChordMode() +{ + return ((KToggleAction *)actionCollection()->action("chord_mode"))-> + isChecked(); +} + +void MatrixView::slotDockParametersBack() +{ + m_dockLeft->dockBack(); +} + +void MatrixView::slotParametersClosed() +{ + stateChanged("parametersbox_closed"); + m_dockVisible = false; +} + +void MatrixView::slotParametersDockedBack(KDockWidget* dw, KDockWidget::DockPosition) +{ + if (dw == m_dockLeft) { + stateChanged("parametersbox_closed", KXMLGUIClient::StateReverse); + m_dockVisible = true; + } +} + +void MatrixView::slotCheckTrackAssignments() +{ + Track *track = + m_staffs[0]->getSegment().getComposition()-> + getTrackById(m_staffs[0]->getSegment().getTrack()); + + Instrument *instr = getDocument()->getStudio(). + getInstrumentById(track->getInstrument()); + + m_parameterBox->useInstrument(instr); +} + +void MatrixView::initStatusBar() +{ + KStatusBar* sb = statusBar(); + + m_hoveredOverAbsoluteTime = new QLabel(sb); + m_hoveredOverNoteName = new QLabel(sb); + + m_hoveredOverAbsoluteTime->setMinimumWidth(175); + m_hoveredOverNoteName->setMinimumWidth(65); + + sb->addWidget(m_hoveredOverAbsoluteTime); + sb->addWidget(m_hoveredOverNoteName); + + m_insertModeLabel = new QLabel(sb); + m_insertModeLabel->setMinimumWidth(20); + sb->addWidget(m_insertModeLabel); + + sb->insertItem(KTmpStatusMsg::getDefaultMsg(), + KTmpStatusMsg::getDefaultId(), 1); + sb->setItemAlignment(KTmpStatusMsg::getDefaultId(), + AlignLeft | AlignVCenter); + + m_selectionCounter = new QLabel(sb); + sb->addWidget(m_selectionCounter); +} + +void MatrixView::slotToolHelpChanged(const QString &s) +{ + QString msg = " " + s; + if (m_toolContextHelp == msg) return; + m_toolContextHelp = msg; + + m_config->setGroup(GeneralOptionsConfigGroup); + if (!m_config->readBoolEntry("toolcontexthelp", true)) return; + + if (m_mouseInCanvasView) statusBar()->changeItem(m_toolContextHelp, 1); +} + +void MatrixView::slotMouseEnteredCanvasView() +{ + m_config->setGroup(GeneralOptionsConfigGroup); + if (!m_config->readBoolEntry("toolcontexthelp", true)) return; + + m_mouseInCanvasView = true; + statusBar()->changeItem(m_toolContextHelp, 1); +} + +void MatrixView::slotMouseLeftCanvasView() +{ + m_mouseInCanvasView = false; + statusBar()->changeItem(KTmpStatusMsg::getDefaultMsg(), 1); +} + +bool MatrixView::applyLayout(int staffNo, + timeT startTime, + timeT endTime) +{ + Profiler profiler("MatrixView::applyLayout", true); + + m_hlayout.reset(); + m_vlayout.reset(); + + for (unsigned int i = 0; i < m_staffs.size(); ++i) { + + if (staffNo >= 0 && (int)i != staffNo) + continue; + + m_hlayout.scanStaff(*m_staffs[i], startTime, endTime); + m_vlayout.scanStaff(*m_staffs[i], startTime, endTime); + } + + m_hlayout.finishLayout(); + m_vlayout.finishLayout(); + + if (m_staffs[0]->getSegment().getEndMarkerTime() != m_lastEndMarkerTime || + m_lastEndMarkerTime == 0 || + isCompositionModified()) { + readjustCanvasSize(); + m_lastEndMarkerTime = m_staffs[0]->getSegment().getEndMarkerTime(); + } + + return true; +} + +void MatrixView::refreshSegment(Segment *segment, + timeT startTime, timeT endTime) +{ + Profiler profiler("MatrixView::refreshSegment", true); + + MATRIX_DEBUG << "MatrixView::refreshSegment(" << startTime + << ", " << endTime << ")\n"; + + applyLayout( -1, startTime, endTime); + + if (!segment) + segment = m_segments[0]; + + if (endTime == 0) + endTime = segment->getEndTime(); + else if (startTime == endTime) { + startTime = segment->getStartTime(); + endTime = segment->getEndTime(); + } + + m_staffs[0]->positionElements(startTime, endTime); + repaintRulers(); +} + +QSize MatrixView::getViewSize() +{ + return canvas()->size(); +} + +void MatrixView::setViewSize(QSize s) +{ + MATRIX_DEBUG << "MatrixView::setViewSize() w = " << s.width() << endl; + + canvas()->resize(getXbyInverseWorldMatrix(s.width()), s.height()); + getCanvasView()->resizeContents(s.width(), s.height()); + + MATRIX_DEBUG << "MatrixView::setViewSize() contentsWidth = " << getCanvasView()->contentsWidth() << endl; +} + +void MatrixView::repaintRulers() +{ + for (unsigned int i = 0; i != m_propertyViewRulers.size(); i++) + m_propertyViewRulers[i].first->repaint(); +} + +void MatrixView::updateView() +{ + canvas()->update(); +} + +void MatrixView::setCurrentSelection(EventSelection* s, bool preview, + bool redrawNow) +{ + //!!! rather too much here shared with notationview -- could much of + // this be in editview? + + if (m_currentEventSelection == s) { + updateQuantizeCombo(); + return ; + } + + if (m_currentEventSelection) { + getStaff(0)->positionElements(m_currentEventSelection->getStartTime(), + m_currentEventSelection->getEndTime()); + } + + EventSelection *oldSelection = m_currentEventSelection; + m_currentEventSelection = s; + + timeT startA, endA, startB, endB; + + if (oldSelection) { + startA = oldSelection->getStartTime(); + endA = oldSelection->getEndTime(); + startB = s ? s->getStartTime() : startA; + endB = s ? s->getEndTime() : endA; + } else { + // we know they can't both be null -- first thing we tested above + startA = startB = s->getStartTime(); + endA = endB = s->getEndTime(); + } + + // refreshSegment takes start==end to mean refresh everything + if (startA == endA) + ++endA; + if (startB == endB) + ++endB; + + bool updateRequired = true; + + if (s) { + + bool foundNewEvent = false; + + for (EventSelection::eventcontainer::iterator i = + s->getSegmentEvents().begin(); + i != s->getSegmentEvents().end(); ++i) { + + if (oldSelection && oldSelection->getSegment() == s->getSegment() + && oldSelection->contains(*i)) + continue; + + foundNewEvent = true; + + if (preview) { + long pitch; + if ((*i)->get<Int>(BaseProperties::PITCH, pitch)) { + long velocity = -1; + (void)((*i)->get<Int>(BaseProperties::VELOCITY, velocity)); + if (!((*i)->has(BaseProperties::TIED_BACKWARD) && + (*i)->get<Bool>(BaseProperties::TIED_BACKWARD))) + playNote(s->getSegment(), pitch, velocity); + } + } + } + + if (!foundNewEvent) { + if (oldSelection && + oldSelection->getSegment() == s->getSegment() && + oldSelection->getSegmentEvents().size() == + s->getSegmentEvents().size()) + updateRequired = false; + } + } + + if (updateRequired) { + + if ((endA >= startB && endB >= startA) && + (!s || !oldSelection || + oldSelection->getSegment() == s->getSegment())) { + + Segment &segment(s ? s->getSegment() : + oldSelection->getSegment()); + + if (redrawNow) { + // recolour the events now + getStaff(segment)->positionElements(std::min(startA, startB), + std::max(endA, endB)); + } else { + // mark refresh status and then request a repaint + segment.getRefreshStatus + (m_segmentsRefreshStatusIds + [getStaff(segment)->getId()]). + push(std::min(startA, startB), std::max(endA, endB)); + } + + } else { + // do two refreshes, one for each -- here we know neither is null + + if (redrawNow) { + // recolour the events now + getStaff(oldSelection->getSegment())->positionElements(startA, + endA); + + getStaff(s->getSegment())->positionElements(startB, endB); + } else { + // mark refresh status and then request a repaint + + oldSelection->getSegment().getRefreshStatus + (m_segmentsRefreshStatusIds + [getStaff(oldSelection->getSegment())->getId()]). + push(startA, endA); + + s->getSegment().getRefreshStatus + (m_segmentsRefreshStatusIds + [getStaff(s->getSegment())->getId()]). + push(startB, endB); + } + } + } + + delete oldSelection; + + if (s) { + + int eventsSelected = s->getSegmentEvents().size(); + m_selectionCounter->setText + (i18n(" 1 event selected ", + " %n events selected ", eventsSelected)); + + } else { + m_selectionCounter->setText(i18n(" No selection ")); + } + + m_selectionCounter->update(); + + slotSetCurrentVelocityFromSelection(); + + // Clear states first, then enter only those ones that apply + // (so as to avoid ever clearing one after entering another, in + // case the two overlap at all) + stateChanged("have_selection", KXMLGUIClient::StateReverse); + stateChanged("have_notes_in_selection", KXMLGUIClient::StateReverse); + stateChanged("have_rests_in_selection", KXMLGUIClient::StateReverse); + + if (s) { + stateChanged("have_selection", KXMLGUIClient::StateNoReverse); + if (s->contains(Note::EventType)) { + stateChanged("have_notes_in_selection", + KXMLGUIClient::StateNoReverse); + } + if (s->contains(Note::EventRestType)) { + stateChanged("have_rests_in_selection", + KXMLGUIClient::StateNoReverse); + } + } + + updateQuantizeCombo(); + + if (redrawNow) + updateView(); + else + update(); +} + +void MatrixView::updateQuantizeCombo() +{ + timeT unit = 0; + + if (m_currentEventSelection) { + unit = + BasicQuantizer::getStandardQuantization + (m_currentEventSelection); + } else { + unit = + BasicQuantizer::getStandardQuantization + (&(m_staffs[0]->getSegment())); + } + + for (unsigned int i = 0; i < m_quantizations.size(); ++i) { + if (unit == m_quantizations[i]) { + m_quantizeCombo->setCurrentItem(i); + return ; + } + } + + m_quantizeCombo->setCurrentItem(m_quantizeCombo->count() - 1); // "Off" +} + +void MatrixView::slotPaintSelected() +{ + EditTool* painter = m_toolBox->getTool(MatrixPainter::ToolName); + + setTool(painter); +} + +void MatrixView::slotEraseSelected() +{ + EditTool* eraser = m_toolBox->getTool(MatrixEraser::ToolName); + + setTool(eraser); +} + +void MatrixView::slotSelectSelected() +{ + EditTool* selector = m_toolBox->getTool(MatrixSelector::ToolName); + + connect(selector, SIGNAL(gotSelection()), + this, SLOT(slotNewSelection())); + + connect(selector, SIGNAL(editTriggerSegment(int)), + this, SIGNAL(editTriggerSegment(int))); + + setTool(selector); +} + +void MatrixView::slotMoveSelected() +{ + EditTool* mover = m_toolBox->getTool(MatrixMover::ToolName); + + setTool(mover); +} + +void MatrixView::slotResizeSelected() +{ + EditTool* resizer = m_toolBox->getTool(MatrixResizer::ToolName); + + setTool(resizer); +} + +void MatrixView::slotTransformsQuantize() +{ + if (!m_currentEventSelection) + return ; + + QuantizeDialog dialog(this); + + if (dialog.exec() == QDialog::Accepted) { + KTmpStatusMsg msg(i18n("Quantizing..."), this); + addCommandToHistory(new EventQuantizeCommand + (*m_currentEventSelection, + dialog.getQuantizer())); + } +} + +void MatrixView::slotTransformsRepeatQuantize() +{ + if (!m_currentEventSelection) + return ; + + KTmpStatusMsg msg(i18n("Quantizing..."), this); + addCommandToHistory(new EventQuantizeCommand + (*m_currentEventSelection, + "Quantize Dialog Grid", false)); // no i18n (config group name) +} + +void MatrixView::slotTransformsCollapseNotes() +{ + if (!m_currentEventSelection) + return ; + KTmpStatusMsg msg(i18n("Collapsing notes..."), this); + + addCommandToHistory(new CollapseNotesCommand + (*m_currentEventSelection)); +} + +void MatrixView::slotTransformsLegato() +{ + if (!m_currentEventSelection) + return ; + + KTmpStatusMsg msg(i18n("Making legato..."), this); + addCommandToHistory(new EventQuantizeCommand + (*m_currentEventSelection, + new LegatoQuantizer(0))); // no quantization +} + +void MatrixView::slotMousePressed(timeT time, int pitch, + QMouseEvent* e, MatrixElement* el) +{ + MATRIX_DEBUG << "MatrixView::mousePressed at pitch " + << pitch << ", time " << time << endl; + + // Don't allow moving/insertion before the beginning of the + // segment + timeT curSegmentStartTime = getCurrentSegment()->getStartTime(); + if (curSegmentStartTime > time) + time = curSegmentStartTime; + + m_tool->handleMousePress(time, pitch, 0, e, el); + + if (e->button() != RightButton) { + getCanvasView()->startAutoScroll(); + } + + // play a preview + //playPreview(pitch); +} + +void MatrixView::slotMouseMoved(timeT time, int pitch, QMouseEvent* e) +{ + // Don't allow moving/insertion before the beginning of the + // segment + timeT curSegmentStartTime = getCurrentSegment()->getStartTime(); + if (curSegmentStartTime > time) + time = curSegmentStartTime; + + if (activeItem()) { + activeItem()->handleMouseMove(e); + updateView(); + } else { + int follow = m_tool->handleMouseMove(time, pitch, e); + getCanvasView()->setScrollDirectionConstraint(follow); + + // if (follow != RosegardenCanvasView::NoFollow) { + // getCanvasView()->doAutoScroll(); + // } + + // play a preview + if (pitch != m_previousEvPitch) { + //playPreview(pitch); + m_previousEvPitch = pitch; + } + } + +} + +void MatrixView::slotMouseReleased(timeT time, int pitch, QMouseEvent* e) +{ + // Don't allow moving/insertion before the beginning of the + // segment + timeT curSegmentStartTime = getCurrentSegment()->getStartTime(); + if (curSegmentStartTime > time) + time = curSegmentStartTime; + + if (activeItem()) { + activeItem()->handleMouseRelease(e); + setActiveItem(0); + updateView(); + } + + // send the real event time now (not adjusted for beginning of bar) + m_tool->handleMouseRelease(time, pitch, e); + m_previousEvPitch = 0; + getCanvasView()->stopAutoScroll(); +} + +void +MatrixView::slotHoveredOverNoteChanged(int evPitch, + bool haveEvent, + timeT evTime) +{ + MidiPitchLabel label(evPitch); + + if (haveEvent) { + + m_haveHoveredOverNote = true; + + int bar, beat, fraction, remainder; + getDocument()->getComposition().getMusicalTimeForAbsoluteTime + (evTime, bar, beat, fraction, remainder); + + RealTime rt = + getDocument()->getComposition().getElapsedRealTime(evTime); + long ms = rt.msec(); + + QString msg = i18n("Note: %1 (%2.%3s)") + .arg(QString("%1-%2-%3-%4") + .arg(QString("%1").arg(bar + 1).rightJustify(3, '0')) + .arg(QString("%1").arg(beat).rightJustify(2, '0')) + .arg(QString("%1").arg(fraction).rightJustify(2, '0')) + .arg(QString("%1").arg(remainder).rightJustify(2, '0'))) + .arg(rt.sec) + .arg(QString("%1").arg(ms).rightJustify(3, '0')); + + m_hoveredOverAbsoluteTime->setText(msg); + } + + m_haveHoveredOverNote = false; + + m_hoveredOverNoteName->setText(i18n("%1 (%2)") + .arg(label.getQString()) + .arg(evPitch)); + + m_pitchRuler->drawHoverNote(evPitch); +} + +void +MatrixView::slotHoveredOverKeyChanged(unsigned int y) +{ + MatrixStaff& staff = *(m_staffs[0]); + + int evPitch = staff.getHeightAtCanvasCoords( -1, y); + + if (evPitch != m_previousEvPitch) { + MidiPitchLabel label(evPitch); + m_hoveredOverNoteName->setText(QString("%1 (%2)"). + arg(label.getQString()).arg(evPitch)); + m_previousEvPitch = evPitch; + } +} + +void +MatrixView::slotHoveredOverAbsoluteTimeChanged(unsigned int time) +{ + if (m_haveHoveredOverNote) return; + + timeT t = time; + + int bar, beat, fraction, remainder; + getDocument()->getComposition().getMusicalTimeForAbsoluteTime + (t, bar, beat, fraction, remainder); + + RealTime rt = + getDocument()->getComposition().getElapsedRealTime(t); + long ms = rt.msec(); + + // At the advice of doc.trolltech.com/3.0/qstring.html#sprintf + // we replaced this QString format("%ld (%ld.%03lds)"); + // to support Unicode + + QString message = i18n("Time: %1 (%2.%3s)") + .arg(QString("%1-%2-%3-%4") + .arg(QString("%1").arg(bar + 1).rightJustify(3, '0')) + .arg(QString("%1").arg(beat).rightJustify(2, '0')) + .arg(QString("%1").arg(fraction).rightJustify(2, '0')) + .arg(QString("%1").arg(remainder).rightJustify(2, '0'))) + .arg(rt.sec) + .arg(QString("%1").arg(ms).rightJustify(3, '0')); + + m_hoveredOverAbsoluteTime->setText(message); +} + +void +MatrixView::slotSetPointerPosition(timeT time) +{ + slotSetPointerPosition(time, m_playTracking); +} + +void +MatrixView::slotSetPointerPosition(timeT time, bool scroll) +{ + Composition &comp = getDocument()->getComposition(); + int barNo = comp.getBarNumber(time); + + if (barNo >= m_hlayout.getLastVisibleBarOnStaff(*m_staffs[0])) { + + Segment &seg = m_staffs[0]->getSegment(); + + if (seg.isRepeating() && time < seg.getRepeatEndTime()) { + time = + seg.getStartTime() + + ((time - seg.getStartTime()) % + (seg.getEndMarkerTime() - seg.getStartTime())); + m_staffs[0]->setPointerPosition(m_hlayout, time); + } else { + m_staffs[0]->hidePointer(); + scroll = false; + } + } else if (barNo < m_hlayout.getFirstVisibleBarOnStaff(*m_staffs[0])) { + m_staffs[0]->hidePointer(); + scroll = false; + } else { + m_staffs[0]->setPointerPosition(m_hlayout, time); + } + + if (scroll && !getCanvasView()->isAutoScrolling()) + getCanvasView()->slotScrollHoriz(static_cast<int>(getXbyWorldMatrix(m_hlayout.getXForTime(time)))); + + updateView(); +} + +void +MatrixView::slotSetInsertCursorPosition(timeT time, bool scroll) +{ + //!!! For now. Probably unlike slotSetPointerPosition this one + // should snap to the nearest event or grid line. + + m_staffs[0]->setInsertCursorPosition(m_hlayout, time); + + if (scroll && !getCanvasView()->isAutoScrolling()) { + getCanvasView()->slotScrollHoriz + (static_cast<int>(getXbyWorldMatrix(m_hlayout.getXForTime(time)))); + } + + updateView(); +} + +void MatrixView::slotEditCut() +{ + MATRIX_DEBUG << "MatrixView::slotEditCut()\n"; + + if (!m_currentEventSelection) + return ; + KTmpStatusMsg msg(i18n("Cutting selection to clipboard..."), this); + + addCommandToHistory(new CutCommand(*m_currentEventSelection, + getDocument()->getClipboard())); +} + +void MatrixView::slotEditCopy() +{ + if (!m_currentEventSelection) + return ; + KTmpStatusMsg msg(i18n("Copying selection to clipboard..."), this); + + addCommandToHistory(new CopyCommand(*m_currentEventSelection, + getDocument()->getClipboard())); + + emit usedSelection(); +} + +void MatrixView::slotEditPaste() +{ + if (getDocument()->getClipboard()->isEmpty()) { + slotStatusHelpMsg(i18n("Clipboard is empty")); + return ; + } + + KTmpStatusMsg msg(i18n("Inserting clipboard contents..."), this); + + PasteEventsCommand *command = new PasteEventsCommand + (m_staffs[0]->getSegment(), getDocument()->getClipboard(), + getInsertionTime(), PasteEventsCommand::MatrixOverlay); + + if (!command->isPossible()) { + slotStatusHelpMsg(i18n("Couldn't paste at this point")); + } else { + addCommandToHistory(command); + setCurrentSelection(new EventSelection(command->getPastedEvents())); + } +} + +void MatrixView::slotEditDelete() +{ + if (!m_currentEventSelection) + return ; + KTmpStatusMsg msg(i18n("Deleting selection..."), this); + + addCommandToHistory(new EraseCommand(*m_currentEventSelection)); + + // clear and clear + setCurrentSelection(0, false); +} + +void MatrixView::slotKeyPressed(unsigned int y, bool repeating) +{ + slotHoveredOverKeyChanged(y); + + getCanvasView()->slotScrollVertSmallSteps(y); + + Composition &comp = getDocument()->getComposition(); + Studio &studio = getDocument()->getStudio(); + + MatrixStaff& staff = *(m_staffs[0]); + MidiByte evPitch = staff.getHeightAtCanvasCoords( -1, y); + + // Don't do anything if we're part of a run up the keyboard + // and the pitch hasn't changed + // + if (m_lastNote == evPitch && repeating) + return ; + + // Save value + m_lastNote = evPitch; + if (!repeating) + m_firstNote = evPitch; + + Track *track = comp.getTrackById( + staff.getSegment().getTrack()); + + Instrument *ins = + studio.getInstrumentById(track->getInstrument()); + + // check for null instrument + // + if (ins == 0) + return ; + + MappedEvent mE(ins->getId(), + MappedEvent::MidiNote, + evPitch + staff.getSegment().getTranspose(), + MidiMaxValue, + RealTime::zeroTime, + RealTime::zeroTime, + RealTime::zeroTime); + StudioControl::sendMappedEvent(mE); + +} + +void MatrixView::slotKeySelected(unsigned int y, bool repeating) +{ + slotHoveredOverKeyChanged(y); + + getCanvasView()->slotScrollVertSmallSteps(y); + + MatrixStaff& staff = *(m_staffs[0]); + Segment &segment(staff.getSegment()); + MidiByte evPitch = staff.getHeightAtCanvasCoords( -1, y); + + // Don't do anything if we're part of a run up the keyboard + // and the pitch hasn't changed + // + if (m_lastNote == evPitch && repeating) + return ; + + // Save value + m_lastNote = evPitch; + if (!repeating) + m_firstNote = evPitch; + + EventSelection *s = new EventSelection(segment); + + for (Segment::iterator i = segment.begin(); + segment.isBeforeEndMarker(i); ++i) { + + if ((*i)->isa(Note::EventType) && + (*i)->has(BaseProperties::PITCH)) { + + MidiByte p = (*i)->get + <Int> + (BaseProperties::PITCH); + if (p >= std::min(m_firstNote, evPitch) && + p <= std::max(m_firstNote, evPitch)) { + s->addEvent(*i); + } + } + } + + if (m_currentEventSelection) { + // allow addFromSelection to deal with eliminating duplicates + s->addFromSelection(m_currentEventSelection); + } + + setCurrentSelection(s, false); + + // now play the note as well + + Composition &comp = getDocument()->getComposition(); + Studio &studio = getDocument()->getStudio(); + Track *track = comp.getTrackById(segment.getTrack()); + Instrument *ins = + studio.getInstrumentById(track->getInstrument()); + + // check for null instrument + // + if (ins == 0) + return ; + + MappedEvent mE(ins->getId(), + MappedEvent::MidiNoteOneShot, + evPitch + segment.getTranspose(), + MidiMaxValue, + RealTime::zeroTime, + RealTime(0, 250000000), + RealTime::zeroTime); + StudioControl::sendMappedEvent(mE); +} + +void MatrixView::slotKeyReleased(unsigned int y, bool repeating) +{ + MatrixStaff& staff = *(m_staffs[0]); + int evPitch = staff.getHeightAtCanvasCoords(-1, y); + + if (m_lastNote == evPitch && repeating) + return; + + Rosegarden::Segment &segment(staff.getSegment()); + + // send note off (note on at zero velocity) + + Rosegarden::Composition &comp = getDocument()->getComposition(); + Rosegarden::Studio &studio = getDocument()->getStudio(); + Rosegarden::Track *track = comp.getTrackById(segment.getTrack()); + Rosegarden::Instrument *ins = + studio.getInstrumentById(track->getInstrument()); + + // check for null instrument + // + if (ins == 0) + return; + + evPitch = evPitch + segment.getTranspose(); + if (evPitch < 0 || evPitch > 127) return; + + Rosegarden::MappedEvent mE(ins->getId(), + Rosegarden::MappedEvent::MidiNote, + evPitch, + 0, + Rosegarden::RealTime::zeroTime, + Rosegarden::RealTime::zeroTime, + Rosegarden::RealTime::zeroTime); + Rosegarden::StudioControl::sendMappedEvent(mE); +} + +void MatrixView::slotVerticalScrollPianoKeyboard(int y) +{ + if (m_pianoView) // check that the piano view still exists (see dtor) + m_pianoView->setContentsPos(0, y); +} + +void MatrixView::slotInsertNoteFromAction() +{ + const QObject *s = sender(); + QString name = s->name(); + + Segment &segment = *getCurrentSegment(); + int pitch = 0; + + Accidental accidental = + Accidentals::NoAccidental; + + timeT time(getInsertionTime()); + ::Rosegarden::Key key = segment.getKeyAtTime(time); + Clef clef = segment.getClefAtTime(time); + + try { + + pitch = getPitchFromNoteInsertAction(name, accidental, clef, key); + + } catch (...) { + + KMessageBox::sorry + (this, i18n("Unknown note insert action %1").arg(name)); + return ; + } + + KTmpStatusMsg msg(i18n("Inserting note"), this); + + MATRIX_DEBUG << "Inserting note at pitch " << pitch << endl; + + Event modelEvent(Note::EventType, 0, 1); + modelEvent.set<Int>(BaseProperties::PITCH, pitch); + modelEvent.set<String>(BaseProperties::ACCIDENTAL, accidental); + timeT endTime(time + m_snapGrid->getSnapTime(time)); + + MatrixInsertionCommand* command = + new MatrixInsertionCommand(segment, time, endTime, &modelEvent); + + addCommandToHistory(command); + + if (!isInChordMode()) { + slotSetInsertCursorPosition(endTime); + } +} + +void MatrixView::closeWindow() +{ + delete this; +} + +bool MatrixView::canPreviewAnotherNote() +{ + static time_t lastCutOff = 0; + static int sinceLastCutOff = 0; + + time_t now = time(0); + ++sinceLastCutOff; + + if ((now - lastCutOff) > 0) { + sinceLastCutOff = 0; + lastCutOff = now; + } else { + if (sinceLastCutOff >= 20) { + // don't permit more than 20 notes per second, to avoid + // gungeing up the sound drivers + MATRIX_DEBUG << "Rejecting preview (too busy)" << endl; + return false; + } + } + + return true; +} + +void MatrixView::playNote(Event *event) +{ + // Only play note events + // + if (!event->isa(Note::EventType)) + return ; + + Composition &comp = getDocument()->getComposition(); + Studio &studio = getDocument()->getStudio(); + + // Get the Instrument + // + Track *track = comp.getTrackById( + m_staffs[0]->getSegment().getTrack()); + + Instrument *ins = + studio.getInstrumentById(track->getInstrument()); + + if (ins == 0) + return ; + + if (!canPreviewAnotherNote()) + return ; + + // Get a velocity + // + MidiByte velocity = MidiMaxValue / 4; // be easy on the user's ears + long eventVelocity = 0; + if (event->get + <Int>(BaseProperties::VELOCITY, eventVelocity)) + velocity = eventVelocity; + + RealTime duration = + comp.getElapsedRealTime(event->getDuration()); + + // create + MappedEvent mE(ins->getId(), + MappedEvent::MidiNoteOneShot, + (MidiByte) + event->get + <Int> + (BaseProperties::PITCH) + + m_staffs[0]->getSegment().getTranspose(), + velocity, + RealTime::zeroTime, + duration, + RealTime::zeroTime); + + StudioControl::sendMappedEvent(mE); +} + +void MatrixView::playNote(const Segment &segment, int pitch, + int velocity) +{ + Composition &comp = getDocument()->getComposition(); + Studio &studio = getDocument()->getStudio(); + + Track *track = comp.getTrackById(segment.getTrack()); + + Instrument *ins = + studio.getInstrumentById(track->getInstrument()); + + // check for null instrument + // + if (ins == 0) + return ; + + if (velocity < 0) + velocity = getCurrentVelocity(); + + MappedEvent mE(ins->getId(), + MappedEvent::MidiNoteOneShot, + pitch + segment.getTranspose(), + velocity, + RealTime::zeroTime, + RealTime(0, 250000000), + RealTime::zeroTime); + + StudioControl::sendMappedEvent(mE); +} + +MatrixStaff* +MatrixView::getStaff(const Segment &segment) +{ + for (unsigned int i = 0; i < m_staffs.size(); ++i) { + if (&(m_staffs[i]->getSegment()) == &segment) + return m_staffs[i]; + } + + return 0; +} + +void +MatrixView::setSingleSelectedEvent(int staffNo, Event *event, + bool preview, bool redrawNow) +{ + setSingleSelectedEvent(getStaff(staffNo)->getSegment(), event, + preview, redrawNow); +} + +void +MatrixView::setSingleSelectedEvent(Segment &segment, + Event *event, + bool preview, bool redrawNow) +{ + setCurrentSelection(0, false); + + EventSelection *selection = new EventSelection(segment); + selection->addEvent(event); + + //!!! + // this used to say + // setCurrentSelection(selection, true) + // since the default arg for preview is false, this changes the + // default semantics -- test what circumstance this matters in + // and choose an acceptable solution for both matrix & notation + setCurrentSelection(selection, preview, redrawNow); +} + +void +MatrixView::slotNewSelection() +{ + MATRIX_DEBUG << "MatrixView::slotNewSelection\n"; + + // m_parameterBox->setSelection(m_currentEventSelection); +} + +void +MatrixView::slotSetSnapFromIndex(int s) +{ + slotSetSnap(m_snapValues[s]); +} + +void +MatrixView::slotSetSnapFromAction() +{ + const QObject *s = sender(); + QString name = s->name(); + + if (name.left(5) == "snap_") { + int snap = name.right(name.length() - 5).toInt(); + if (snap > 0) { + slotSetSnap(Note(Note::Semibreve).getDuration() / snap); + } else if (name == "snap_none") { + slotSetSnap(SnapGrid::NoSnap); + } else if (name == "snap_beat") { + slotSetSnap(SnapGrid::SnapToBeat); + } else if (name == "snap_bar") { + slotSetSnap(SnapGrid::SnapToBar); + } else if (name == "snap_unit") { + slotSetSnap(SnapGrid::SnapToUnit); + } else { + MATRIX_DEBUG << "Warning: MatrixView::slotSetSnapFromAction: unrecognised action " << name << endl; + } + } +} + +void +MatrixView::slotSetSnap(timeT t) +{ + MATRIX_DEBUG << "MatrixView::slotSetSnap: time is " << t << endl; + m_snapGrid->setSnapTime(t); + + for (unsigned int i = 0; i < m_snapValues.size(); ++i) { + if (m_snapValues[i] == t) { + m_snapGridCombo->setCurrentItem(i); + break; + } + } + + for (unsigned int i = 0; i < m_staffs.size(); ++i) + m_staffs[i]->sizeStaff(m_hlayout); + + m_segments[0]->setSnapGridSize(t); + + m_config->setGroup(MatrixViewConfigGroup); + m_config->writeEntry("Snap Grid Size", t); + + updateView(); +} + +void +MatrixView::slotQuantizeSelection(int q) +{ + MATRIX_DEBUG << "MatrixView::slotQuantizeSelection\n"; + + timeT unit = + ((unsigned int)q < m_quantizations.size() ? m_quantizations[q] : 0); + + Quantizer *quant = + new BasicQuantizer + (unit ? unit : + Note(Note::Shortest).getDuration(), false); + + if (unit) { + KTmpStatusMsg msg(i18n("Quantizing..."), this); + if (m_currentEventSelection && + m_currentEventSelection->getAddedEvents()) { + addCommandToHistory(new EventQuantizeCommand + (*m_currentEventSelection, quant)); + } else { + Segment &s = m_staffs[0]->getSegment(); + addCommandToHistory(new EventQuantizeCommand + (s, s.getStartTime(), s.getEndMarkerTime(), + quant)); + } + } else { + KTmpStatusMsg msg(i18n("Unquantizing..."), this); + if (m_currentEventSelection && + m_currentEventSelection->getAddedEvents()) { + addCommandToHistory(new EventUnquantizeCommand + (*m_currentEventSelection, quant)); + } else { + Segment &s = m_staffs[0]->getSegment(); + addCommandToHistory(new EventUnquantizeCommand + (s, s.getStartTime(), s.getEndMarkerTime(), + quant)); + } + } +} + +void +MatrixView::initActionsToolbar() +{ + MATRIX_DEBUG << "MatrixView::initActionsToolbar" << endl; + + KToolBar *actionsToolbar = toolBar("Actions Toolbar"); + + if (!actionsToolbar) { + MATRIX_DEBUG << "MatrixView::initActionsToolbar - " + << "tool bar not found" << endl; + return ; + } + + // The SnapGrid combo and Snap To... menu items + // + QLabel *sLabel = new QLabel(i18n(" Grid: "), actionsToolbar, "kde toolbar widget"); + sLabel->setIndent(10); + + QPixmap noMap = NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("menu-no-note")); + + m_snapGridCombo = new KComboBox(actionsToolbar); + + for (unsigned int i = 0; i < m_snapValues.size(); i++) { + + timeT d = m_snapValues[i]; + + if (d == SnapGrid::NoSnap) { + m_snapGridCombo->insertItem(i18n("None")); + } else if (d == SnapGrid::SnapToUnit) { + m_snapGridCombo->insertItem(i18n("Unit")); + } else if (d == SnapGrid::SnapToBeat) { + m_snapGridCombo->insertItem(i18n("Beat")); + } else if (d == SnapGrid::SnapToBar) { + m_snapGridCombo->insertItem(i18n("Bar")); + } else { + timeT err = 0; + QString label = NotationStrings::makeNoteMenuLabel(d, true, err); + QPixmap pixmap = NotePixmapFactory::toQPixmap + (NotePixmapFactory::makeNoteMenuPixmap(d, err)); + m_snapGridCombo->insertItem((err ? noMap : pixmap), label); + } + + if (d == m_snapGrid->getSnapSetting()) { + m_snapGridCombo->setCurrentItem(m_snapGridCombo->count() - 1); + } + } + + connect(m_snapGridCombo, SIGNAL(activated(int)), + this, SLOT(slotSetSnapFromIndex(int))); + + // Velocity combo. Not a spin box, because the spin box is too + // slow to use unless we make it typeable into, and then it takes + // focus away from our more important widgets + + QLabel *vlabel = new QLabel(i18n(" Velocity: "), actionsToolbar, "kde toolbar widget"); + vlabel->setIndent(10); + + m_velocityCombo = new KComboBox(actionsToolbar); + for (int i = 0; i <= 127; ++i) { + m_velocityCombo->insertItem(QString("%1").arg(i)); + } + m_velocityCombo->setCurrentItem(100); //!!! associate with segment + + // Quantize combo + // + QLabel *qLabel = new QLabel(i18n(" Quantize: "), actionsToolbar, "kde toolbar widget"); + qLabel->setIndent(10); + + m_quantizeCombo = new KComboBox(actionsToolbar); + + for (unsigned int i = 0; i < m_quantizations.size(); ++i) { + + timeT time = m_quantizations[i]; + timeT error = 0; + QString label = NotationStrings::makeNoteMenuLabel(time, true, error); + QPixmap pmap = NotePixmapFactory::toQPixmap(NotePixmapFactory::makeNoteMenuPixmap(time, error)); + m_quantizeCombo->insertItem(error ? noMap : pmap, label); + } + + m_quantizeCombo->insertItem(noMap, i18n("Off")); + + connect(m_quantizeCombo, SIGNAL(activated(int)), + this, SLOT(slotQuantizeSelection(int))); +} + +void +MatrixView::initZoomToolbar() +{ + MATRIX_DEBUG << "MatrixView::initZoomToolbar" << endl; + + KToolBar *zoomToolbar = toolBar("Zoom Toolbar"); + + if (!zoomToolbar) { + MATRIX_DEBUG << "MatrixView::initZoomToolbar - " + << "tool bar not found" << endl; + return ; + } + + std::vector<double> zoomSizes; // in units-per-pixel + + //double defaultBarWidth44 = 100.0; + //double duration44 = TimeSignature(4,4).getBarDuration(); + + static double factors[] = { 0.025, 0.05, 0.1, 0.2, 0.5, + 1.0, 1.5, 2.5, 5.0, 10.0, 20.0 }; + // Zoom labels + // + for (unsigned int i = 0; i < sizeof(factors) / sizeof(factors[0]); ++i) { +// zoomSizes.push_back(duration44 / (defaultBarWidth44 * factors[i])); + +// zoomSizes.push_back(factors[i] / 2); // GROSS HACK - see in matrixstaff.h - BREAKS MATRIX VIEW, see bug 1000595 + zoomSizes.push_back(factors[i]); + } + + m_hZoomSlider = new ZoomSlider<double> + (zoomSizes, -1, QSlider::Horizontal, zoomToolbar, "kde toolbar widget"); + m_hZoomSlider->setTracking(true); + m_hZoomSlider->setFocusPolicy(QWidget::NoFocus); + + m_zoomLabel = new QLabel(zoomToolbar, "kde toolbar widget"); + m_zoomLabel->setIndent(10); + m_zoomLabel->setFixedWidth(80); + + connect(m_hZoomSlider, + SIGNAL(valueChanged(int)), + SLOT(slotChangeHorizontalZoom(int))); + +} + +void +MatrixView::slotChangeHorizontalZoom(int) +{ + double zoomValue = m_hZoomSlider->getCurrentSize(); + + // m_zoomLabel->setText(i18n("%1%").arg(zoomValue*100.0 * 2)); // GROSS HACK - see in matrixstaff.h - BREAKS MATRIX VIEW, see bug 1000595 + m_zoomLabel->setText(i18n("%1%").arg(zoomValue*100.0)); + + MATRIX_DEBUG << "MatrixView::slotChangeHorizontalZoom() : zoom factor = " + << zoomValue << endl; + + m_referenceRuler->setHScaleFactor(zoomValue); + + if (m_tempoRuler) + m_tempoRuler->repaint(); + if (m_chordNameRuler) + m_chordNameRuler->repaint(); + + // Set zoom matrix + // + QWMatrix zoomMatrix; + zoomMatrix.scale(zoomValue, 1.0); + m_canvasView->setWorldMatrix(zoomMatrix); + + // make control rulers zoom too + // + setControlRulersZoom(zoomMatrix); + + if (m_topStandardRuler) + m_topStandardRuler->setHScaleFactor(zoomValue); + if (m_bottomStandardRuler) + m_bottomStandardRuler->setHScaleFactor(zoomValue); + + for (unsigned int i = 0; i < m_propertyViewRulers.size(); ++i) { + m_propertyViewRulers[i].first->setHScaleFactor(zoomValue); + m_propertyViewRulers[i].first->repaint(); + } + + if (m_topStandardRuler) + m_topStandardRuler->update(); + if (m_bottomStandardRuler) + m_bottomStandardRuler->update(); + + m_config->setGroup(MatrixViewConfigGroup); + m_config->writeEntry("Zoom Level", zoomValue); + + // If you do adjust the viewsize then please remember to + // either re-center() or remember old scrollbar position + // and restore. + // + + int newWidth = computePostLayoutWidth(); + + // int newWidth = int(getXbyWorldMatrix(getCanvasView()->canvas()->width())); + + // We DO NOT resize the canvas(), only the area it's displaying on + // + getCanvasView()->resizeContents(newWidth, getViewSize().height()); + + // This forces a refresh of the h. scrollbar, even if the canvas width + // hasn't changed + // + getCanvasView()->polish(); + + getCanvasView()->slotScrollHoriz + (getXbyWorldMatrix(m_staffs[0]->getLayoutXOfInsertCursor())); +} + +void +MatrixView::slotZoomIn() +{ + m_hZoomSlider->increment(); +} + +void +MatrixView::slotZoomOut() +{ + m_hZoomSlider->decrement(); +} + +void +MatrixView::scrollToTime(timeT t) +{ + double layoutCoord = m_hlayout.getXForTime(t); + getCanvasView()->slotScrollHoriz(int(layoutCoord)); +} + +int +MatrixView::getCurrentVelocity() const +{ + return m_velocityCombo->currentItem(); +} + +void +MatrixView::slotSetCurrentVelocity(int value) +{ + m_velocityCombo->setCurrentItem(value); +} + + +void +MatrixView::slotSetCurrentVelocityFromSelection() +{ + if (!m_currentEventSelection) return; + + float totalVelocity = 0; + int count = 0; + + for (EventSelection::eventcontainer::iterator i = + m_currentEventSelection->getSegmentEvents().begin(); + i != m_currentEventSelection->getSegmentEvents().end(); ++i) { + + if ((*i)->has(BaseProperties::VELOCITY)) { + totalVelocity += (*i)->get<Int>(BaseProperties::VELOCITY); + ++count; + } + } + + if (count > 0) { + slotSetCurrentVelocity((totalVelocity / count) + 0.5); + } +} + +unsigned int +MatrixView::addPropertyViewRuler(const PropertyName &property) +{ + // Try and find this controller if it exists + // + for (unsigned int i = 0; i != m_propertyViewRulers.size(); i++) { + if (m_propertyViewRulers[i].first->getPropertyName() == property) + return i; + } + + int height = 20; + + PropertyViewRuler *newRuler = new PropertyViewRuler(&m_hlayout, + m_segments[0], + property, + xorigin, + height, + getCentralWidget()); + + addRuler(newRuler); + + PropertyBox *newControl = new PropertyBox(strtoqstr(property), + m_parameterBox->width() + m_pitchRuler->width(), + height, + getCentralWidget()); + + addPropertyBox(newControl); + + m_propertyViewRulers.push_back( + std::pair<PropertyViewRuler*, PropertyBox*>(newRuler, newControl)); + + return m_propertyViewRulers.size() - 1; +} + +bool +MatrixView::removePropertyViewRuler(unsigned int number) +{ + if (number > m_propertyViewRulers.size() - 1) + return false; + + std::vector<std::pair<PropertyViewRuler*, PropertyBox*> >::iterator it + = m_propertyViewRulers.begin(); + while (number--) + it++; + + delete it->first; + delete it->second; + m_propertyViewRulers.erase(it); + + return true; +} + +RulerScale* +MatrixView::getHLayout() +{ + return &m_hlayout; +} + +Staff* +MatrixView::getCurrentStaff() +{ + return getStaff(0); +} + +Segment * +MatrixView::getCurrentSegment() +{ + MatrixStaff *staff = getStaff(0); + return (staff ? &staff->getSegment() : 0); +} + +timeT +MatrixView::getInsertionTime() +{ + MatrixStaff *staff = m_staffs[0]; + return staff->getInsertCursorTime(m_hlayout); +} + +void +MatrixView::slotStepBackward() +{ + timeT time(getInsertionTime()); + slotSetInsertCursorPosition(SnapGrid(&m_hlayout).snapTime + (time - 1, + SnapGrid::SnapLeft)); +} + +void +MatrixView::slotStepForward() +{ + timeT time(getInsertionTime()); + slotSetInsertCursorPosition(SnapGrid(&m_hlayout).snapTime + (time + 1, + SnapGrid::SnapRight)); +} + +void +MatrixView::slotJumpCursorToPlayback() +{ + slotSetInsertCursorPosition(getDocument()->getComposition().getPosition()); +} + +void +MatrixView::slotJumpPlaybackToCursor() +{ + emit jumpPlaybackTo(getInsertionTime()); +} + +void +MatrixView::slotToggleTracking() +{ + m_playTracking = !m_playTracking; +} + +void +MatrixView::slotSelectAll() +{ + Segment *segment = m_segments[0]; + Segment::iterator it = segment->begin(); + EventSelection *selection = new EventSelection(*segment); + + for (; segment->isBeforeEndMarker(it); it++) + if ((*it)->isa(Note::EventType)) + selection->addEvent(*it); + + setCurrentSelection(selection, false); +} + +void MatrixView::slotPreviewSelection() +{ + if (!m_currentEventSelection) + return ; + + getDocument()->slotSetLoop(m_currentEventSelection->getStartTime(), + m_currentEventSelection->getEndTime()); +} + +void MatrixView::slotClearLoop() +{ + getDocument()->slotSetLoop(0, 0); +} + +void MatrixView::slotClearSelection() +{ + // Actually we don't clear the selection immediately: if we're + // using some tool other than the select tool, then the first + // press switches us back to the select tool. + + MatrixSelector *selector = dynamic_cast<MatrixSelector *>(m_tool); + + if (!selector) { + slotSelectSelected(); + } else { + setCurrentSelection(0); + } +} + +void MatrixView::slotFilterSelection() +{ + RG_DEBUG << "MatrixView::slotFilterSelection" << endl; + + Segment *segment = getCurrentSegment(); + EventSelection *existingSelection = m_currentEventSelection; + if (!segment || !existingSelection) + return ; + + EventFilterDialog dialog(this); + if (dialog.exec() == QDialog::Accepted) { + RG_DEBUG << "slotFilterSelection- accepted" << endl; + + bool haveEvent = false; + + EventSelection *newSelection = new EventSelection(*segment); + EventSelection::eventcontainer &ec = + existingSelection->getSegmentEvents(); + for (EventSelection::eventcontainer::iterator i = + ec.begin(); i != ec.end(); ++i) { + if (dialog.keepEvent(*i)) { + haveEvent = true; + newSelection->addEvent(*i); + } + } + + if (haveEvent) + setCurrentSelection(newSelection); + else + setCurrentSelection(0); + } +} + +void +MatrixView::readjustCanvasSize() +{ + int maxHeight = 0; + + for (unsigned int i = 0; i < m_staffs.size(); ++i) { + + MatrixStaff &staff = *m_staffs[i]; + + staff.sizeStaff(m_hlayout); + + // if (staff.getTotalWidth() + staff.getX() > maxWidth) { + // maxWidth = staff.getTotalWidth() + staff.getX() + 1; + // } + + if (staff.getTotalHeight() + staff.getY() > maxHeight) { + if (isDrumMode()) { + maxHeight = staff.getTotalHeight() + staff.getY() + 5; + } else { + maxHeight = staff.getTotalHeight() + staff.getY() + 1; + } + } + + } + + int newWidth = computePostLayoutWidth(); + + // now get the EditView to do the biz + readjustViewSize(QSize(newWidth, maxHeight), true); + + repaintRulers(); +} + +void MatrixView::slotVelocityUp() +{ + if (!m_currentEventSelection) + return ; + KTmpStatusMsg msg(i18n("Raising velocities..."), this); + + addCommandToHistory + (new ChangeVelocityCommand(10, *m_currentEventSelection)); + + slotSetCurrentVelocityFromSelection(); +} + +void MatrixView::slotVelocityDown() +{ + if (!m_currentEventSelection) + return ; + KTmpStatusMsg msg(i18n("Lowering velocities..."), this); + + addCommandToHistory + (new ChangeVelocityCommand( -10, *m_currentEventSelection)); + + slotSetCurrentVelocityFromSelection(); +} + +void +MatrixView::slotSetVelocities() +{ + if (!m_currentEventSelection) + return ; + + EventParameterDialog dialog(this, + i18n("Set Event Velocities"), + BaseProperties::VELOCITY, + getCurrentVelocity()); + + if (dialog.exec() == QDialog::Accepted) { + KTmpStatusMsg msg(i18n("Setting Velocities..."), this); + addCommandToHistory(new SelectionPropertyCommand + (m_currentEventSelection, + BaseProperties::VELOCITY, + dialog.getPattern(), + dialog.getValue1(), + dialog.getValue2())); + } +} + +void +MatrixView::slotSetVelocitiesToCurrent() +{ + if (!m_currentEventSelection) return; + + addCommandToHistory(new SelectionPropertyCommand + (m_currentEventSelection, + BaseProperties::VELOCITY, + FlatPattern, + getCurrentVelocity(), + getCurrentVelocity())); +} + +void +MatrixView::slotTriggerSegment() +{ + if (!m_currentEventSelection) + return ; + + TriggerSegmentDialog dialog(this, &getDocument()->getComposition()); + if (dialog.exec() != QDialog::Accepted) + return ; + + addCommandToHistory(new SetTriggerCommand(*m_currentEventSelection, + dialog.getId(), + true, + dialog.getRetune(), + dialog.getTimeAdjust(), + Marks::NoMark, + i18n("Trigger Segment"))); +} + +void +MatrixView::slotRemoveTriggers() +{ + if (!m_currentEventSelection) + return ; + + addCommandToHistory(new ClearTriggersCommand(*m_currentEventSelection, + i18n("Remove Triggers"))); +} + +void +MatrixView::slotToggleChordsRuler() +{ + toggleWidget(m_chordNameRuler, "show_chords_ruler"); +} + +void +MatrixView::slotToggleTempoRuler() +{ + toggleWidget(m_tempoRuler, "show_tempo_ruler"); +} + +void +MatrixView::paintEvent(QPaintEvent* e) +{ + //!!! There's a lot of code shared between matrix and notation for + // dealing with step recording (the insertable note event stuff). + // It should probably be factored out into a base class, but I'm + // not sure I wouldn't rather wait until the functionality is all + // sorted in both matrix and notation so we can be sure how much + // of it is actually common. + + EditView::paintEvent(e); + + // now deal with any backlog of insertable notes that appeared + // during paint (because it's not safe to modify a segment from + // within a sub-event-loop in a processEvents call from a paint) + if (!m_pendingInsertableNotes.empty()) { + std::vector<std::pair<int, int> > notes = m_pendingInsertableNotes; + m_pendingInsertableNotes.clear(); + for (unsigned int i = 0; i < notes.size(); ++i) { + slotInsertableNoteEventReceived(notes[i].first, notes[i].second, true); + } + } +} + +void +MatrixView::updateViewCaption() +{ + // Set client label + // + QString view = i18n("Matrix"); + if (isDrumMode()) + view = i18n("Percussion"); + + if (m_segments.size() == 1) { + + TrackId trackId = m_segments[0]->getTrack(); + Track *track = + m_segments[0]->getComposition()->getTrackById(trackId); + + int trackPosition = -1; + if (track) + trackPosition = track->getPosition(); + + setCaption(i18n("%1 - Segment Track #%2 - %3") + .arg(getDocument()->getTitle()) + .arg(trackPosition + 1) + .arg(view)); + + } else if (m_segments.size() == getDocument()->getComposition().getNbSegments()) { + + setCaption(i18n("%1 - All Segments - %2") + .arg(getDocument()->getTitle()) + .arg(view)); + + } else { + + setCaption(i18n("%1 - 1 Segment - %2", + "%1 - %n Segments - %2", + m_segments.size()) + .arg(getDocument()->getTitle()) + .arg(view)); + } +} + +int MatrixView::computePostLayoutWidth() +{ + Segment *segment = m_segments[0]; + Composition *composition = segment->getComposition(); + int endX = int(m_hlayout.getXForTime + (composition->getBarEndForTime + (segment->getEndMarkerTime()))); + int startX = int(m_hlayout.getXForTime + (composition->getBarStartForTime + (segment->getStartTime()))); + + int newWidth = int(getXbyWorldMatrix(endX - startX)); + + MATRIX_DEBUG << "MatrixView::readjustCanvasSize() : startX = " + << startX + << " endX = " << endX + << " newWidth = " << newWidth + << " endmarkertime : " << segment->getEndMarkerTime() + << " barEnd for time : " << composition->getBarEndForTime(segment->getEndMarkerTime()) + << endl; + + newWidth += 12; + if (isDrumMode()) + newWidth += 12; + + return newWidth; +} + +bool MatrixView::getMinMaxPitches(int& minPitch, int& maxPitch) +{ + minPitch = MatrixVLayout::maxMIDIPitch + 1; + maxPitch = MatrixVLayout::minMIDIPitch - 1; + + std::vector<MatrixStaff*>::iterator sit; + for (sit = m_staffs.begin(); sit != m_staffs.end(); ++sit) { + + MatrixElementList *mel = (*sit)->getViewElementList(); + MatrixElementList::iterator eit; + for (eit = mel->begin(); eit != mel->end(); ++eit) { + + NotationElement *el = static_cast<NotationElement*>(*eit); + if (el->isNote()) { + Event* ev = el->event(); + int pitch = ev->get + <Int> + (BaseProperties::PITCH); + if (minPitch > pitch) + minPitch = pitch; + if (maxPitch < pitch) + maxPitch = pitch; + } + } + } + + return maxPitch >= minPitch; +} + +void MatrixView::extendKeyMapping() +{ + int minStaffPitch, maxStaffPitch; + if (getMinMaxPitches(minStaffPitch, maxStaffPitch)) { + int minKMPitch = m_localMapping->getPitchForOffset(0); + int maxKMPitch = m_localMapping->getPitchForOffset(0) + + m_localMapping->getPitchExtent() - 1; + if (minStaffPitch < minKMPitch) + m_localMapping->getMap()[minStaffPitch] = std::string(""); + if (maxStaffPitch > maxKMPitch) + m_localMapping->getMap()[maxStaffPitch] = std::string(""); + } +} + +void +MatrixView::slotInsertableNoteEventReceived(int pitch, int velocity, bool noteOn) +{ + // hjj: + // The default insertion mode is implemented equivalently in + // notationviewslots.cpp: + // - proceed if notes do not overlap + // - make the chord if notes do overlap, and do not proceed + + static int numberOfNotesOn = 0; + static time_t lastInsertionTime = 0; + if (!noteOn) { + numberOfNotesOn--; + return ; + } + + KToggleAction *action = dynamic_cast<KToggleAction *> + (actionCollection()->action("toggle_step_by_step")); + if (!action) { + MATRIX_DEBUG << "WARNING: No toggle_step_by_step action" << endl; + return ; + } + if (!action->isChecked()) + return ; + + if (m_inPaintEvent) { + m_pendingInsertableNotes.push_back(std::pair<int, int>(pitch, velocity)); + return ; + } + + Segment &segment = *getCurrentSegment(); + + // If the segment is transposed, we want to take that into + // account. But the note has already been played back to the user + // at its untransposed pitch, because that's done by the MIDI THRU + // code in the sequencer which has no way to know whether a note + // was intended for step recording. So rather than adjust the + // pitch for playback according to the transpose setting, we have + // to adjust the stored pitch in the opposite direction. + + pitch -= segment.getTranspose(); + + KTmpStatusMsg msg(i18n("Inserting note"), this); + + MATRIX_DEBUG << "Inserting note at pitch " << pitch << endl; + + Event modelEvent(Note::EventType, 0, 1); + modelEvent.set<Int>(BaseProperties::PITCH, pitch); + static timeT insertionTime(getInsertionTime()); + if (insertionTime >= segment.getEndMarkerTime()) { + MATRIX_DEBUG << "WARNING: off end of segment" << endl; + return ; + } + time_t now; + time (&now); + double elapsed = difftime(now, lastInsertionTime); + time (&lastInsertionTime); + + if (numberOfNotesOn <= 0 || elapsed > 10.0 ) { + numberOfNotesOn = 0; + insertionTime = getInsertionTime(); + } + numberOfNotesOn++; + timeT endTime(insertionTime + m_snapGrid->getSnapTime(insertionTime)); + + if (endTime <= insertionTime) { + static bool showingError = false; + if (showingError) + return ; + showingError = true; + KMessageBox::sorry(this, i18n("Can't insert note: No grid duration selected")); + showingError = false; + return ; + } + + MatrixInsertionCommand* command = + new MatrixInsertionCommand(segment, insertionTime, endTime, &modelEvent); + + addCommandToHistory(command); + + if (!isInChordMode()) { + slotSetInsertCursorPosition(endTime); + } +} + +void +MatrixView::slotInsertableNoteOnReceived(int pitch, int velocity) +{ + MATRIX_DEBUG << "MatrixView::slotInsertableNoteOnReceived: " << pitch << endl; + slotInsertableNoteEventReceived(pitch, velocity, true); +} + +void +MatrixView::slotInsertableNoteOffReceived(int pitch, int velocity) +{ + MATRIX_DEBUG << "MatrixView::slotInsertableNoteOffReceived: " << pitch << endl; + slotInsertableNoteEventReceived(pitch, velocity, false); +} + +void +MatrixView::slotToggleStepByStep() +{ + KToggleAction *action = dynamic_cast<KToggleAction *> + (actionCollection()->action("toggle_step_by_step")); + if (!action) { + MATRIX_DEBUG << "WARNING: No toggle_step_by_step action" << endl; + return ; + } + if (action->isChecked()) { // after toggling, that is + emit stepByStepTargetRequested(this); + } else { + emit stepByStepTargetRequested(0); + } +} + +void +MatrixView::slotUpdateInsertModeStatus() +{ + QString message; + if (isInChordMode()) { + message = i18n(" Chord "); + } else { + message = ""; + } + m_insertModeLabel->setText(message); +} + +void +MatrixView::slotStepByStepTargetRequested(QObject *obj) +{ + KToggleAction *action = dynamic_cast<KToggleAction *> + (actionCollection()->action("toggle_step_by_step")); + if (!action) { + MATRIX_DEBUG << "WARNING: No toggle_step_by_step action" << endl; + return ; + } + action->setChecked(obj == this); +} + +void +MatrixView::slotInstrumentLevelsChanged(InstrumentId id, + const LevelInfo &info) +{ + if (!m_parameterBox) + return ; + + Composition &comp = getDocument()->getComposition(); + + Track *track = + comp.getTrackById(m_staffs[0]->getSegment().getTrack()); + if (!track || track->getInstrument() != id) + return ; + + Instrument *instr = getDocument()->getStudio(). + getInstrumentById(track->getInstrument()); + if (!instr || instr->getType() != Instrument::SoftSynth) + return ; + + float dBleft = AudioLevel::fader_to_dB + (info.level, 127, AudioLevel::LongFader); + float dBright = AudioLevel::fader_to_dB + (info.levelRight, 127, AudioLevel::LongFader); + + m_parameterBox->setAudioMeter(dBleft, dBright, + AudioLevel::DB_FLOOR, + AudioLevel::DB_FLOOR); +} + +void +MatrixView::slotPercussionSetChanged(Instrument * newInstr) +{ + // Must be called only when in drum mode + assert(m_drumMode); + + int resolution = 8; + if (newInstr && newInstr->getKeyMapping()) { + resolution = 11; + } + + const MidiKeyMapping *mapping = 0; + if (newInstr) { + mapping = newInstr->getKeyMapping(); + } + + // Construct a local new keymapping : + if (m_localMapping) + delete m_localMapping; + if (mapping) { + m_localMapping = new MidiKeyMapping(*mapping); + extendKeyMapping(); + } else { + m_localMapping = 0; + } + + m_staffs[0]->setResolution(resolution); + + delete m_pitchRuler; + + QWidget *vport = m_pianoView->viewport(); + + // Create a new pitchruler widget + PitchRuler *pitchRuler; + if (newInstr && newInstr->getKeyMapping() && + !newInstr->getKeyMapping()->getMap().empty()) { + pitchRuler = new PercussionPitchRuler(vport, + m_localMapping, + resolution); // line spacing + } else { + pitchRuler = new PianoKeyboard(vport); + } + + + QObject::connect + (pitchRuler, SIGNAL(hoveredOverKeyChanged(unsigned int)), + this, SLOT (slotHoveredOverKeyChanged(unsigned int))); + + QObject::connect + (pitchRuler, SIGNAL(keyPressed(unsigned int, bool)), + this, SLOT (slotKeyPressed(unsigned int, bool))); + + QObject::connect + (pitchRuler, SIGNAL(keySelected(unsigned int, bool)), + this, SLOT (slotKeySelected(unsigned int, bool))); + + QObject::connect + (pitchRuler, SIGNAL(keyReleased(unsigned int, bool)), + this, SLOT (slotKeyReleased(unsigned int, bool))); + + // Replace the old pitchruler widget + m_pitchRuler = pitchRuler; + m_pianoView->addChild(m_pitchRuler); + m_pitchRuler->show(); + m_pianoView->setFixedWidth(pitchRuler->sizeHint().width()); + + // Update matrix canvas + readjustCanvasSize(); + bool layoutApplied = applyLayout(); + if (!layoutApplied) + KMessageBox::sorry(0, i18n("Couldn't apply piano roll layout")); + else { + MATRIX_DEBUG << "MatrixView : rendering elements\n"; + m_staffs[0]->positionAllElements(); + m_staffs[0]->getSegment().getRefreshStatus + (m_segmentsRefreshStatusIds[0]).setNeedsRefresh(false); + update(); + } +} + +void +MatrixView::slotCanvasBottomWidgetHeightChanged(int newHeight) +{ + m_pianoView->setBottomMargin(newHeight + + m_canvasView->horizontalScrollBar()->height()); +} + +MatrixCanvasView* MatrixView::getCanvasView() +{ + return dynamic_cast<MatrixCanvasView *>(m_canvasView); +} + +} +#include "MatrixView.moc" diff --git a/src/gui/editors/matrix/MatrixView.h b/src/gui/editors/matrix/MatrixView.h new file mode 100644 index 0000000..49e0358 --- /dev/null +++ b/src/gui/editors/matrix/MatrixView.h @@ -0,0 +1,692 @@ + +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_MATRIXVIEW_H_ +#define _RG_MATRIXVIEW_H_ + +#include "base/MidiProgram.h" +#include "base/PropertyName.h" +#include "base/SnapGrid.h" +#include "gui/general/EditView.h" +#include "gui/widgets/ZoomSlider.h" +#include "MatrixHLayout.h" +#include "MatrixVLayout.h" +#include "MatrixCanvasView.h" +#include <kdockwidget.h> +#include <qpoint.h> +#include <qsize.h> +#include <vector> +#include "base/Event.h" +#include "document/ConfigGroups.h" + + +class QWidget; +class QPaintEvent; +class QObject; +class QMouseEvent; +class QLabel; +class QCursor; +class QCanvas; +class KComboBox; + + +namespace Rosegarden +{ + +class Staff; +class Segment; +class RulerScale; +class RosegardenGUIDoc; +class QDeferScrollView; +class PropertyViewRuler; +class PropertyBox; +class PitchRuler; +class MidiKeyMapping; +class MatrixStaff; +class MatrixElement; +class InstrumentParameterBox; +class Instrument; +class EventSelection; +class Event; +class ChordNameRuler; +class LevelInfo; + + +/** + * Matrix ("Piano Roll") View + * + * Note: we currently display only one staff + */ +class MatrixView : public EditView +{ + Q_OBJECT + + friend class MatrixSelector; + +public: + MatrixView(RosegardenGUIDoc *doc, + std::vector<Segment *> segments, + QWidget *parent, bool drumMode); + + virtual ~MatrixView(); + + virtual bool applyLayout(int staffNo = -1, + timeT startTime = 0, + timeT endTime = 0); + + virtual void refreshSegment(Segment *segment, + timeT startTime = 0, + timeT endTime = 0); + + QCanvas* canvas() { return getCanvasView()->canvas(); } + + void setCanvasCursor(const QCursor &cursor) { + getCanvasView()->viewport()->setCursor(cursor); + } + + MatrixStaff* getStaff(int i) + { + if (i >= 0 && unsigned(i) < m_staffs.size()) return m_staffs[i]; + else return 0; + } + + MatrixStaff *getStaff(const Segment &segment); + + virtual void updateView(); + + bool isDrumMode() { return m_drumMode; } + + /** + * Discover whether chord-mode insertions are enabled (as opposed + * to the default melody-mode) + */ + bool isInChordMode(); + + /** + * Set the current event selection. + * + * If preview is true, sound the selection as well. + * + * If redrawNow is true, recolour the elements on the canvas; + * otherwise just line up a refresh for the next paint event. + * + * (If the selection has changed as part of a modification to a + * segment, redrawNow should be unnecessary and undesirable, as a + * paint event will occur in the next event loop following the + * command invocation anyway.) + */ + virtual void setCurrentSelection(EventSelection* s, + bool preview = false, + bool redrawNow = false); + + /** + * Set the current event selection to a single event + */ + void setSingleSelectedEvent(int staffNo, + Event *event, + bool preview = false, + bool redrawNow = false); + + /** + * Set the current event selection to a single event + */ + void setSingleSelectedEvent(Segment &segment, + Event *event, + bool preview = false, + bool redrawNow = false); + + + /** + * Play a Note Event using the keyPressed() signal + */ + void playNote(Event *event); + + /** + * Play a preview (same as above but a simpler interface) + */ + void playNote(const Segment &segment, int pitch, int velocity = -1); + + /** + * Get the SnapGrid + */ + const SnapGrid &getSnapGrid() const { return *m_snapGrid; } + + /** + * Add a ruler that allows control of a single property - + * return the number of the added ruler + * + */ + unsigned int addPropertyViewRuler(const PropertyName &property); + + /** + * Remove a control ruler - return true if it's a valid ruler number + */ + bool removePropertyViewRuler(unsigned int number); + + /** + * Adjust an X coord by world matrix + */ + double getXbyWorldMatrix(double value) + { return m_canvasView->worldMatrix().m11() * value; } + + double getXbyInverseWorldMatrix(double value) + { return m_canvasView->inverseWorldMatrix().m11() * value; } + + QPoint inverseMapPoint(const QPoint& p) { return m_canvasView->inverseMapPoint(p); } + + /* + * Repaint the control rulers + * + */ + void repaintRulers(); + + /* + * Readjust the canvas size + * + */ + void readjustCanvasSize(); + + /* + * Scrolls the view such that the given time is centered + */ + void scrollToTime(timeT t); + + /** + * Get the local keyMapping (when in drum mode) + */ + MidiKeyMapping *getKeyMapping() { return m_localMapping; } + + /** + * Get the velocity currently set in the velocity menu. + */ + int getCurrentVelocity() const; + +signals: + /** + * Emitted when the selection has been cut or copied + * + * @see MatrixSelector#hideSelection + */ + void usedSelection(); + + void play(); + void stop(); + void fastForwardPlayback(); + void rewindPlayback(); + void fastForwardPlaybackToEnd(); + void rewindPlaybackToBeginning(); + void jumpPlaybackTo(timeT); + void panic(); + + void stepByStepTargetRequested(QObject *); + + void editTriggerSegment(int); + + void editTimeSignature(timeT); + +public slots: + + /** + * put the indicationed text/object into the clipboard and remove * it + * from the document + */ + virtual void slotEditCut(); + + /** + * put the indicationed text/object into the clipboard + */ + virtual void slotEditCopy(); + + /** + * paste the clipboard into the document + */ + virtual void slotEditPaste(); + + /** + * Delete the current selection + */ + void slotEditDelete(); + + virtual void slotStepBackward(); // override from EditView + virtual void slotStepForward(); // override from EditView + + void slotPreviewSelection(); + void slotClearLoop(); + void slotClearSelection(); + + /** + * Filter selection by event type + */ + void slotFilterSelection(); // dummy - not actually functional yet + + /// edition tools + void slotPaintSelected(); + void slotEraseSelected(); + void slotSelectSelected(); + void slotMoveSelected(); + void slotResizeSelected(); + + void slotToggleStepByStep(); + + /// status stuff + void slotUpdateInsertModeStatus(); + + /// transforms + void slotTransformsQuantize(); + void slotTransformsRepeatQuantize(); + void slotTransformsLegato(); + void slotVelocityUp(); + void slotVelocityDown(); + + /// settings + void slotToggleChordsRuler(); + void slotToggleTempoRuler(); + + /// cursor moves + void slotJumpCursorToPlayback(); + void slotJumpPlaybackToCursor(); + void slotToggleTracking(); + + /// Canvas actions slots + + /** + * Called when a mouse press occurred on a matrix element + * or somewhere on the staff + */ + void slotMousePressed(timeT time, int pitch, + QMouseEvent*, MatrixElement*); + + void slotMouseMoved(timeT time, int pitch, QMouseEvent*); + void slotMouseReleased(timeT time, int pitch, QMouseEvent*); + + /** + * Called when the mouse cursor moves over a different height on + * the staff + * + * @see MatrixCanvasView#hoveredOverNoteChanged() + */ + void slotHoveredOverNoteChanged(int evPitch, bool haveEvent, + timeT evTime); + + /** + * Called when the mouse cursor moves over a different key on + * the piano keyboard + * + * @see PianoKeyboard#hoveredOverKeyChanged() + */ + void slotHoveredOverKeyChanged(unsigned int); + + /** + * Called when the mouse cursor moves over a note which is at a + * different time on the staff + * + * @see MatrixCanvasView#hoveredOverNoteChange() + */ + void slotHoveredOverAbsoluteTimeChanged(unsigned int); + + /** + * Set the time pointer position during playback + */ + void slotSetPointerPosition(timeT time); + + /** + * Set the time pointer position during playback + */ + void slotSetPointerPosition(timeT time, + bool scroll); + + /** + * Set the insertion pointer position (from the bottom LoopRuler) + */ + void slotSetInsertCursorPosition(timeT position, bool scroll); + + virtual void slotSetInsertCursorPosition(timeT position) { + slotSetInsertCursorPosition(position, true); + } + + /** + * Catch the keyboard being pressed + */ + void slotKeyPressed(unsigned int y, bool repeating); + + /** + * Catch the keyboard being released + */ + void slotKeyReleased(unsigned int y, bool repeating); + + /** + * Catch the keyboard being pressed with selection modifier + */ + void slotKeySelected(unsigned int y, bool repeating); + + /** + * Handle scrolling between view and PianoKeyboard + */ + void slotVerticalScrollPianoKeyboard(int y); + + /** + * Close + */ + void closeWindow(); + + /** + * A new selection has been acquired by a tool + */ + void slotNewSelection(); + + /** + * Set the snaptime of the grid from an item in the snap combo + */ + void slotSetSnapFromIndex(int); + + /** + * Set the snaptime of the grid based on the name of the invoking action + */ + void slotSetSnapFromAction(); + + /** + * Set the snaptime of the grid + */ + void slotSetSnap(timeT); + + /** + * Quantize a selection to a given level + */ + void slotQuantizeSelection(int); + + /** + * Collapse equal pitch notes + */ + void slotTransformsCollapseNotes(); + + /** + * Pop-up the velocity modification dialog + */ + void slotSetVelocities(); + + /** + * Set selected event velocities to whatever's in the velocity widget + */ + void slotSetVelocitiesToCurrent(); + + /** + * Pop-up the select trigger segment dialog + */ + void slotTriggerSegment(); + + /** + * Clear triggers from selection + */ + void slotRemoveTriggers(); + + /** + * Change horizontal zoom + */ + void slotChangeHorizontalZoom(int); + + void slotZoomIn(); + void slotZoomOut(); + + /** + * Select all + */ + void slotSelectAll(); + + /** + * Keyboard insert + */ + void slotInsertNoteFromAction(); + + /// Note-on received asynchronously -- consider step-by-step editing + void slotInsertableNoteOnReceived(int pitch, int velocity); + + /// Note-off received asynchronously -- consider step-by-step editing + void slotInsertableNoteOffReceived(int pitch, int velocity); + + /// Note-on or note-off received asynchronously -- as above + void slotInsertableNoteEventReceived(int pitch, int velocity, bool noteOn); + + /// The given QObject has originated a step-by-step-editing request + void slotStepByStepTargetRequested(QObject *); + + void slotInstrumentLevelsChanged(InstrumentId, + const LevelInfo &); + + /// Set the velocity menu to the given value + void slotSetCurrentVelocity(int); + void slotSetCurrentVelocityFromSelection(); + +protected slots: + void slotCanvasBottomWidgetHeightChanged(int newHeight); + + /** + * A new percussion key mapping has to be displayed + */ + void slotPercussionSetChanged(Instrument *); + + /** + * Re-dock the parameters box to its initial position + */ + void slotDockParametersBack(); + + /** + * The parameters box was closed + */ + void slotParametersClosed(); + + /** + * The parameters box was docked back + */ + void slotParametersDockedBack(KDockWidget*, KDockWidget::DockPosition); + + /** + * The instrument for this track may have changed + */ + void slotCheckTrackAssignments(); + + void slotToolHelpChanged(const QString &); + void slotMouseEnteredCanvasView(); + void slotMouseLeftCanvasView(); + +protected: + virtual RulerScale* getHLayout(); + + virtual Segment *getCurrentSegment(); + virtual Staff *getCurrentStaff(); + virtual timeT getInsertionTime(); + + /** + * save general Options like all bar positions and status as well + * as the geometry and the recent file list to the configuration + * file + */ + virtual void slotSaveOptions(); + + /** + * read general Options again and initialize all variables like the recent file list + */ + virtual void readOptions(); + + /** + * create menus and toolbars + */ + virtual void setupActions(); + + /** + * setup status bar + */ + virtual void initStatusBar(); + + /** + * update the current quantize level from selection or entire segment + */ + virtual void updateQuantizeCombo(); + + /** + * Return the size of the MatrixCanvasView + */ + virtual QSize getViewSize(); + + /** + * Set the size of the MatrixCanvasView + */ + virtual void setViewSize(QSize); + + virtual MatrixCanvasView *getCanvasView(); + + /** + * Init matrix actions toolbar + */ + void initActionsToolbar(); + + /** + * Zoom toolbar + */ + void initZoomToolbar(); + + /** + * Test whether we've had too many preview notes recently + */ + bool canPreviewAnotherNote(); + + virtual void paintEvent(QPaintEvent* e); + + virtual void updateViewCaption(); + + int computePostLayoutWidth(); + + /** + * Get min and max pitches of notes on matrix. + * Return false if no notes. + */ + bool getMinMaxPitches(int& minPitch, int& maxPitch); + + /** + * If necessary, extend local keymapping to contain + * all notes currently on staff + */ + void extendKeyMapping(); + + //--------------- Data members --------------------------------- + + std::vector<MatrixStaff*> m_staffs; + + MatrixHLayout m_hlayout; + MatrixVLayout m_vlayout; + SnapGrid *m_snapGrid; + + timeT m_lastEndMarkerTime; + + // Status bar elements + QLabel* m_hoveredOverAbsoluteTime; + QLabel* m_hoveredOverNoteName; + QLabel *m_selectionCounter; + QLabel *m_insertModeLabel; + bool m_haveHoveredOverNote; + + /** + * used in slotHoveredOverKeyChanged to track moves over the piano + * keyboard + */ + int m_previousEvPitch; + + KDockWidget *m_dockLeft; + MatrixCanvasView *m_canvasView; + QDeferScrollView *m_pianoView; + PitchRuler *m_pitchRuler; + + MidiKeyMapping *m_localMapping; + + // The last note we sent in case we're swooshing up and + // down the keyboard and don't want repeat notes sending + // + MidiByte m_lastNote; + + // The first note we sent in similar case (only used for + // doing effective sweep selections + // + MidiByte m_firstNote; + + PropertyName m_selectedProperty; + + // The parameter box + // + InstrumentParameterBox *m_parameterBox; + + // Toolbar flora + // + KComboBox *m_velocityCombo; + KComboBox *m_quantizeCombo; + KComboBox *m_snapGridCombo; + ZoomSlider<double> *m_hZoomSlider; + ZoomSlider<double> *m_vZoomSlider; + QLabel *m_zoomLabel; + + // Hold our matrix quantization values and snap values + // + std::vector<timeT> m_quantizations; + std::vector<timeT> m_snapValues; + + std::vector<std::pair<PropertyViewRuler*, PropertyBox*> > m_propertyViewRulers; + + ChordNameRuler *m_chordNameRuler; + QWidget *m_tempoRuler; + + // ruler used to scale tempo and chord name ruler + ZoomableMatrixHLayoutRulerScale* m_referenceRuler; + + std::vector<std::pair<int, int> > m_pendingInsertableNotes; + + bool m_playTracking; + bool m_dockVisible; + bool m_drumMode; + + bool m_mouseInCanvasView; + QString m_toolContextHelp; +}; + +// Commented this out - was a MatrixView inner class, but we get a warning +// that Q_OBJECT can't be used in an inner class - gl +// + +// class NoteSender : public QObject +// { +// Q_OBJECT + +// public: +// NoteSender(int i, int p) : m_insid(i), m_pitch(p) { } +// virtual ~NoteSender(); + +// public slots: +// void sendNote(); + +// private: +// int m_insid, m_pitch; +// }; + + +} + +#endif diff --git a/src/gui/editors/matrix/PianoKeyboard.cpp b/src/gui/editors/matrix/PianoKeyboard.cpp new file mode 100644 index 0000000..e4641d0 --- /dev/null +++ b/src/gui/editors/matrix/PianoKeyboard.cpp @@ -0,0 +1,299 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#include "PianoKeyboard.h" +#include "misc/Debug.h" + +#include "gui/general/GUIPalette.h" +#include "gui/general/MidiPitchLabel.h" +#include "gui/rulers/PitchRuler.h" +#include "MatrixStaff.h" +#include "MatrixView.h" +#include <qcolor.h> +#include <qcursor.h> +#include <qevent.h> +#include <qfont.h> +#include <qpainter.h> +#include <qsize.h> +#include <qwidget.h> + + +namespace Rosegarden +{ + +const unsigned int _smallWhiteKeyHeight = 14; +const unsigned int _whiteKeyHeight = 18; + +PianoKeyboard::PianoKeyboard(QWidget *parent, int keys) + : PitchRuler(parent), + m_keySize(48, 18), + m_blackKeySize(24, 8), + m_nbKeys(keys), + m_mouseDown(false), + m_hoverHighlight(new QWidget(this)), + m_lastHoverHighlight(0), + m_lastKeyPressed(0) +{ + m_hoverHighlight->hide(); + m_hoverHighlight->setPaletteBackgroundColor(GUIPalette::getColour(GUIPalette::MatrixKeyboardFocus)); + + setPaletteBackgroundColor(QColor(238, 238, 224)); + + computeKeyPos(); + setMouseTracking(true); +} + +QSize PianoKeyboard::sizeHint() const +{ + return QSize(m_keySize.width(), + m_keySize.height() * m_nbKeys); +} + +QSize PianoKeyboard::minimumSizeHint() const +{ + return m_keySize; +} + +void PianoKeyboard::computeKeyPos() +{ + // int y = -9; + int y = -4; + + unsigned int posInOctave = 0, + keyHeight = _smallWhiteKeyHeight; + + for (unsigned int i = 0; i < m_nbKeys; ++i) { + posInOctave = (i + 5) % 7; + + if (y >= 0) { + m_whiteKeyPos.push_back(y); + m_allKeyPos.push_back(y); + } + + if (posInOctave == 2) + m_labelKeyPos.push_back(y + (keyHeight * 3 / 4) - 2); + + if (posInOctave == 0 || + posInOctave == 2 || + posInOctave == 6 || + posInOctave == 3) { // draw shorter white key + + + keyHeight = _smallWhiteKeyHeight; + + if (posInOctave == 2 || + posInOctave == 6) + --keyHeight; + + } else { + + keyHeight = _whiteKeyHeight; + } + + if (posInOctave != 2 && posInOctave != 6) { // draw black key + + unsigned int bY = y + keyHeight - m_blackKeySize.height() / 2; + + m_blackKeyPos.push_back(bY); + m_allKeyPos.push_back(bY); + + } + + y += keyHeight; + } +} + +void PianoKeyboard::paintEvent(QPaintEvent*) +{ + static QFont *pFont = 0; + if (!pFont) { + pFont = new QFont(); + pFont->setPixelSize(9); + } + + QPainter paint(this); + + paint.setFont(*pFont); + + for (unsigned int i = 0; i < m_whiteKeyPos.size(); ++i) + paint.drawLine(0, m_whiteKeyPos[i], + m_keySize.width(), m_whiteKeyPos[i]); + + for (unsigned int i = 0; i < m_labelKeyPos.size(); ++i) { + + int pitch = (m_labelKeyPos.size() - i) * 12; + + // for some reason I don't immediately comprehend, + // m_labelKeyPos contains two more octaves than we need + pitch -= 24; + + MidiPitchLabel label(pitch); + paint.drawText(m_blackKeySize.width(), m_labelKeyPos[i], + label.getQString()); + } + + paint.setBrush(colorGroup().foreground()); + + for (unsigned int i = 0; i < m_blackKeyPos.size(); ++i) + paint.drawRect(0, m_blackKeyPos[i], + m_blackKeySize.width(), m_blackKeySize.height()); +} + +void PianoKeyboard::enterEvent(QEvent *) +{ + //drawHoverNote(e->y()); +} + +void PianoKeyboard::leaveEvent(QEvent*) +{ + m_hoverHighlight->hide(); + + int pos = mapFromGlobal( cursor().pos() ).x(); + if ( pos > m_keySize.width() - 5 || pos < 0 ) { // bit of a hack + emit keyReleased(m_lastKeyPressed, false); + } +} + +void PianoKeyboard::drawHoverNote(int evPitch) +{ + if (m_lastHoverHighlight != evPitch) { + //MATRIX_DEBUG << "PianoKeyboard::drawHoverNote : note = " << evPitch << endl; + m_lastHoverHighlight = evPitch; + + int count = 0; + std::vector<unsigned int>::iterator it; + for (it = m_allKeyPos.begin(); it != m_allKeyPos.end(); ++it, ++count) { + if (126 - evPitch == count) { + int width = m_keySize.width() - 8; + int yPos = *it + 5; + + // check if this is a black key + // + std::vector<unsigned int>::iterator bIt; + bool isBlack = false; + for (bIt = m_blackKeyPos.begin(); bIt != m_blackKeyPos.end(); ++bIt) { + if (*bIt == *it) { + isBlack = true; + break; + } + } + + // Adjust for black note + // + if (isBlack) { + width = m_blackKeySize.width() - 8; + yPos -= 3; + } else { + // If a white note then ensure that we allow for short/tall ones + // + std::vector<unsigned int>::iterator wIt = m_whiteKeyPos.begin(), tIt; + + while (wIt != m_whiteKeyPos.end()) { + if (*wIt == *it) { + tIt = wIt; + + if (++tIt != m_whiteKeyPos.end()) { + //MATRIX_DEBUG << "WHITE KEY HEIGHT = " << *tIt - *wIt << endl; + if (*tIt - *wIt == _whiteKeyHeight) { + yPos += 2; + } + + } + } + + ++wIt; + } + + + } + + m_hoverHighlight->setFixedSize(width, 4); + m_hoverHighlight->move(3, yPos); + m_hoverHighlight->show(); + + return ; + } + } + } + + +} + +void PianoKeyboard::mouseMoveEvent(QMouseEvent* e) +{ + // The routine to work out where this should appear doesn't coincide with the note + // that we send to the sequencer - hence this is a bit pointless and crap at the moment. + // My own fault it's so crap but there you go. + // + // RWB (20040220) + // + MatrixView *matrixView = dynamic_cast<MatrixView*>(topLevelWidget()); + if (matrixView) { + MatrixStaff *staff = matrixView->getStaff(0); + + if (staff) { + drawHoverNote(staff->getHeightAtCanvasCoords(e->x(), e->y())); + } + } + + if (e->state() & Qt::LeftButton) { + if (m_selecting) + emit keySelected(e->y(), true); + else + emit keyPressed(e->y(), true); // we're swooshing + + emit keyReleased(m_lastKeyPressed, true); + m_lastKeyPressed = e->y(); + } else + emit hoveredOverKeyChanged(e->y()); +} + +void PianoKeyboard::mousePressEvent(QMouseEvent *e) +{ + Qt::ButtonState bs = e->state(); + + if (e->button() == LeftButton) { + m_mouseDown = true; + m_selecting = (bs & Qt::ShiftButton); + m_lastKeyPressed = e->y(); + + if (m_selecting) + emit keySelected(e->y(), false); + else + emit keyPressed(e->y(), false); + } +} + +void PianoKeyboard::mouseReleaseEvent(QMouseEvent *e) +{ + if (e->button() == LeftButton) { + m_mouseDown = false; + m_selecting = false; + emit keyReleased(e->y(), false); + } +} + +} +#include "PianoKeyboard.moc" diff --git a/src/gui/editors/matrix/PianoKeyboard.h b/src/gui/editors/matrix/PianoKeyboard.h new file mode 100644 index 0000000..e8b06bb --- /dev/null +++ b/src/gui/editors/matrix/PianoKeyboard.h @@ -0,0 +1,133 @@ + +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_PIANOKEYBOARD_H_ +#define _RG_PIANOKEYBOARD_H_ + +#include "gui/rulers/PitchRuler.h" +#include <qsize.h> +#include <vector> + + +class QWidget; +class QPaintEvent; +class QMouseEvent; +class QEvent; + + +namespace Rosegarden +{ + + + +class PianoKeyboard : public PitchRuler +{ + Q_OBJECT +public: + PianoKeyboard(QWidget *parent, int keys = 88); + + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; + + /* + * We want to be able to call this from the matrix view + */ + void drawHoverNote(int evPitch); + +signals: + + /** + * A key has been clicked on the keyboard. + * + * The repeating flag is there to tell the MatrixView not to send + * the same note again as we're in the middle of a swoosh. + * MatrixView does the y -> Note calculation. + */ + void keyPressed(unsigned int y, bool repeating); + + /** + * A key has been clicked with the selection modifier pressed. + * The MatrixView will probably interpret this as meaning to + * select all notes of that pitch. + * + * The repeating flag is there to tell the MatrixView not to + * clear the selection as we're in the middle of a swoosh. + * MatrixView does the y -> Note calculation. + */ + void keySelected(unsigned int y, bool repeating); + + /** + * A key has been released on the keyboard. + * + * The repeating flag is there to tell the MatrixView not to send + * the same note again as we're in the middle of a swoosh. + * MatrixView does the y -> Note calculation. + */ + void keyReleased(unsigned int y, bool repeating); + + /** + * Emitted when the mouse cursor moves to a different key when + * not clicking or selecting. + * MatrixView does the y -> Note calculation. + */ + void hoveredOverKeyChanged(unsigned int y); + +protected: + + virtual void paintEvent(QPaintEvent*); + + virtual void mouseMoveEvent(QMouseEvent*); + virtual void mousePressEvent(QMouseEvent*); + virtual void mouseReleaseEvent(QMouseEvent*); + virtual void enterEvent(QEvent *); + virtual void leaveEvent(QEvent *); + + // compute all key positions and store them + // + void computeKeyPos(); + + //--------------- Data members --------------------------------- + QSize m_keySize; + QSize m_blackKeySize; + unsigned int m_nbKeys; + + std::vector<unsigned int> m_whiteKeyPos; + std::vector<unsigned int> m_blackKeyPos; + std::vector<unsigned int> m_labelKeyPos; + std::vector<unsigned int> m_allKeyPos; + + bool m_mouseDown; + bool m_selecting; + + // highlight element on the keyboard + QWidget *m_hoverHighlight; + int m_lastHoverHighlight; + int m_lastKeyPressed; +}; + + +} + +#endif diff --git a/src/gui/editors/matrix/QCanvasMatrixDiamond.cpp b/src/gui/editors/matrix/QCanvasMatrixDiamond.cpp new file mode 100644 index 0000000..582b53a --- /dev/null +++ b/src/gui/editors/matrix/QCanvasMatrixDiamond.cpp @@ -0,0 +1,82 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#include "QCanvasMatrixDiamond.h" + +#include "MatrixElement.h" +#include "QCanvasMatrixRectangle.h" +#include <qcanvas.h> +#include <qpainter.h> +#include <qpointarray.h> +#include <qpoint.h> + + +namespace Rosegarden +{ + +QCanvasMatrixDiamond::QCanvasMatrixDiamond(MatrixElement &n, + QCanvas* canvas) : + QCanvasMatrixRectangle(n, canvas) +{} + +QCanvasMatrixDiamond::~QCanvasMatrixDiamond() +{ + hide(); +} + +QPointArray QCanvasMatrixDiamond::areaPoints() const +{ + QPointArray pa(4); + int pw = (pen().width() + 1) / 2; + if ( pw < 1 ) + pw = 1; + if ( pen() == NoPen ) + pw = 0; + pa[0] = QPoint((int)x() - height() / 2 - pw, (int)y() - pw); + pa[1] = pa[0] + QPoint(height() + pw * 2, 0); + pa[2] = pa[1] + QPoint(0, height() + pw * 2); + pa[3] = pa[0] + QPoint(0, height() + pw * 2); + return pa; +} + +void QCanvasMatrixDiamond::drawShape(QPainter & p) +{ + p.save(); + p.setWorldXForm(false); + + QPointArray pa(4); + int q = height() / 2 + 2; + QPoint mapPos = p.worldMatrix().map(QPoint(int(x()), int(y()))); + + pa[0] = QPoint(mapPos.x(), mapPos.y() - 3); + pa[1] = QPoint(mapPos.x() + q, mapPos.y() - 3 + q); + pa[2] = pa[0] + QPoint(0, q * 2); + pa[3] = pa[1] - QPoint(q * 2, 0); + p.drawConvexPolygon(pa); + + p.restore(); +} + +} diff --git a/src/gui/editors/matrix/QCanvasMatrixDiamond.h b/src/gui/editors/matrix/QCanvasMatrixDiamond.h new file mode 100644 index 0000000..5163b12 --- /dev/null +++ b/src/gui/editors/matrix/QCanvasMatrixDiamond.h @@ -0,0 +1,61 @@ + +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_QCANVASMATRIXDIAMOND_H_ +#define _RG_QCANVASMATRIXDIAMOND_H_ + +#include "QCanvasMatrixRectangle.h" +#include <qpointarray.h> + + +class QPainter; +class QCanvas; + + +namespace Rosegarden +{ + +class MatrixElement; + + +/** + * A QCanvas diamond shape referencing a MatrixElement + */ +class QCanvasMatrixDiamond : public QCanvasMatrixRectangle +{ +public: + QCanvasMatrixDiamond(MatrixElement&, QCanvas *); + ~QCanvasMatrixDiamond(); + + QPointArray areaPoints() const; + +protected: + void drawShape(QPainter &); +}; + + +} + +#endif diff --git a/src/gui/editors/matrix/QCanvasMatrixRectangle.cpp b/src/gui/editors/matrix/QCanvasMatrixRectangle.cpp new file mode 100644 index 0000000..a27b480 --- /dev/null +++ b/src/gui/editors/matrix/QCanvasMatrixRectangle.cpp @@ -0,0 +1,44 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + + +#include "QCanvasMatrixRectangle.h" + +#include "MatrixElement.h" +#include <qcanvas.h> + + +namespace Rosegarden +{ + +QCanvasMatrixRectangle::QCanvasMatrixRectangle(MatrixElement& n, + QCanvas* canvas) + : QCanvasRectangle(canvas), + m_matrixElement(n) +{} + +QCanvasMatrixRectangle::~QCanvasMatrixRectangle() +{} + +} diff --git a/src/gui/editors/matrix/QCanvasMatrixRectangle.h b/src/gui/editors/matrix/QCanvasMatrixRectangle.h new file mode 100644 index 0000000..64b6e65 --- /dev/null +++ b/src/gui/editors/matrix/QCanvasMatrixRectangle.h @@ -0,0 +1,60 @@ + +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <richard.bown@ferventsoftware.com> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_QCANVASMATRIXRECTANGLE_H_ +#define _RG_QCANVASMATRIXRECTANGLE_H_ + +#include <qcanvas.h> + + +namespace Rosegarden +{ + +class MatrixElement; + + +/** + * A QCanvasRectangle referencing a MatrixElement + */ +class QCanvasMatrixRectangle : public QCanvasRectangle +{ +public: + QCanvasMatrixRectangle(MatrixElement&, QCanvas*); + + virtual ~QCanvasMatrixRectangle(); + + MatrixElement& getMatrixElement() { return m_matrixElement; } + +protected: + //--------------- Data members --------------------------------- + + MatrixElement& m_matrixElement; + +}; + + +} + +#endif |