diff options
Diffstat (limited to 'src/gui/editors/matrix/MatrixMover.cpp')
-rw-r--r-- | src/gui/editors/matrix/MatrixMover.cpp | 481 |
1 files changed, 481 insertions, 0 deletions
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 <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + 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" |