/* -*- 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. Copyright 2000-2011 the Rosegarden development team. 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 "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 "document/CommandHistory.h" #include "MatrixElement.h" #include "MatrixViewSegment.h" #include "MatrixTool.h" #include "MatrixWidget.h" #include "MatrixScene.h" #include "MatrixMouseEvent.h" #include "misc/Debug.h" namespace Rosegarden { MatrixPainter::MatrixPainter(MatrixWidget *widget) : MatrixTool("matrixpainter.rc", "MatrixPainter", widget), m_clickTime(0), m_currentElement(0), m_currentViewSegment(0) { createAction("select", SLOT(slotSelectSelected())); createAction("resize", SLOT(slotResizeSelected())); createAction("erase", SLOT(slotEraseSelected())); createAction("move", SLOT(slotMoveSelected())); createMenu(); } void MatrixPainter::handleEventRemoved(Event *event) { if (m_currentElement && m_currentElement->event() == event) { delete m_currentElement; m_currentElement = 0; } } void MatrixPainter::handleMidButtonPress(const MatrixMouseEvent *e) { // note: middle button == third button (== left+right at the same time) // pass e = e; } void MatrixPainter::handleMouseDoubleClick(const MatrixMouseEvent *e){ /** left double click with PainterTool : deletes MatrixElement **/ MATRIX_DEBUG << "MatrixPainter::handleThridButtonPress : pitch = " << e->pitch << ", time : " << e->time << endl; m_currentViewSegment = e->viewSegment; if (!m_currentViewSegment) return; // Don't create an overlapping event on the same note on the same channel if (e->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_widget->isDrumMode()) { if (e->element->event()) { MatrixEraseCommand *command = new MatrixEraseCommand(m_currentViewSegment->getSegment(), e->element->event()); CommandHistory::getInstance()->addCommand(command); } //} delete m_currentElement; m_currentElement = 0; return; } /* // Grid needed for the event duration rounding int velocity = m_widget->getCurrentVelocity(); MATRIX_DEBUG << "velocity = " << velocity << endl; m_clickTime = e->snappedLeftTime; Event *ev = new Event(Note::EventType, e->snappedLeftTime, e->snapUnit); ev->set(BaseProperties::PITCH, e->pitch); ev->set(BaseProperties::VELOCITY, velocity); m_currentElement = new MatrixElement(m_scene, ev, m_widget->isDrumMode()); // preview m_scene->playNote(m_currentViewSegment->getSegment(), e->pitch, velocity); */ }// end handleMouseDoubleClick() void MatrixPainter::handleLeftButtonPress(const MatrixMouseEvent *e) { MATRIX_DEBUG << "MatrixPainter::handleLeftButtonPress : pitch = " << e->pitch << ", time : " << e->time << endl; m_currentViewSegment = e->viewSegment; if (!m_currentViewSegment) return; // Don't create an overlapping event on the same note on the same channel if (e->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_widget->isDrumMode()) { if (e->element->event()) { MatrixEraseCommand *command = new MatrixEraseCommand(m_currentViewSegment->getSegment(), e->element->event()); CommandHistory::getInstance()->addCommand(command); } } delete m_currentElement; m_currentElement = 0; return; } // Grid needed for the event duration rounding int velocity = m_widget->getCurrentVelocity(); MATRIX_DEBUG << "velocity = " << velocity << endl; m_clickTime = e->snappedLeftTime; // When entering notes, what you click on in concert pitch is what you // should see and hear, so we have to alter the event's physical pitch to // compensate. In a concert pitch view of an Eb segment in -9, if you click // on a Bb, you should get what will be represented in notation as a G. long pitchOffset = m_currentViewSegment->getSegment().getTranspose(); long adjustedPitch = e->pitch + (pitchOffset * -1); Event *ev = new Event(Note::EventType, e->snappedLeftTime, e->snapUnit); ev->set(BaseProperties::PITCH, adjustedPitch); ev->set(BaseProperties::VELOCITY, velocity); std::cout << "MATRIX PAINTER: I'm working from segment \"" << m_currentViewSegment->getSegment().getLabel() << "\"" << std::endl << " clicked pitch: " << e->pitch << " adjusted pitch: " << adjustedPitch << std::endl; m_currentElement = new MatrixElement(m_scene, ev, m_widget->isDrumMode(), pitchOffset); // preview m_scene->playNote(m_currentViewSegment->getSegment(), adjustedPitch, velocity); } MatrixPainter::FollowMode MatrixPainter::handleMouseMove(const MatrixMouseEvent *e) { // sanity check if (!m_currentElement) return NoFollow; if (getSnapGrid()->getSnapSetting() != SnapGrid::NoSnap) { setContextHelp(tr("Hold Shift to avoid snapping to beat grid")); } else { clearContextHelp(); } timeT time = m_clickTime; timeT endTime = e->snappedRightTime; if (endTime <= time && e->snappedLeftTime < time) endTime = e->snappedLeftTime; if (endTime == time) endTime = time + e->snapUnit; if (time > endTime) std::swap(time, endTime); MATRIX_DEBUG << "MatrixPainter::handleMouseMove : pitch = " << e->pitch << "time = " << time << ", end time = " << endTime << endl; using BaseProperties::PITCH; // we need the same pitch/height corrections for dragging notes as for // entering new ones m_currentViewSegment = e->viewSegment; if (!m_currentViewSegment) return NoFollow; long pitchOffset = m_currentViewSegment->getSegment().getTranspose(); long adjustedPitch = e->pitch + (pitchOffset * -1); long velocity = m_widget->getCurrentVelocity(); m_currentElement->event()->get(BaseProperties::VELOCITY, velocity); Event *ev = new Event(Note::EventType, time, endTime - time); ev->set(BaseProperties::PITCH, adjustedPitch); ev->set(BaseProperties::VELOCITY, velocity); bool preview = false; if (m_currentElement->event()->has(PITCH) && adjustedPitch != m_currentElement->event()->get(PITCH)) { preview = true; } Event *oldEv = m_currentElement->event(); delete m_currentElement; delete oldEv; m_currentElement = new MatrixElement(m_scene, ev, m_widget->isDrumMode(), pitchOffset); if (preview) { m_scene->playNote(m_currentViewSegment->getSegment(), adjustedPitch, velocity); } return FollowMode(FollowHorizontal | FollowVertical); } void MatrixPainter::handleMouseRelease(const MatrixMouseEvent *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; timeT time = m_clickTime; timeT endTime = e->snappedRightTime; if (endTime <= time && e->snappedLeftTime < time) endTime = e->snappedLeftTime; if (endTime == time) endTime = time + e->snapUnit; if (time > endTime) std::swap(time, endTime); if (m_widget->isDrumMode()) { MatrixPercussionInsertionCommand *command = new MatrixPercussionInsertionCommand(m_currentViewSegment->getSegment(), time, m_currentElement->event()); CommandHistory::getInstance()->addCommand(command); Event* ev = m_currentElement->event(); delete m_currentElement; delete ev; ev = command->getLastInsertedEvent(); if (ev) { m_scene->setSingleSelectedEvent (&m_currentViewSegment->getSegment(), ev, false); } } else { SegmentMatrixHelper helper(m_currentViewSegment->getSegment()); MatrixInsertionCommand* command = new MatrixInsertionCommand(m_currentViewSegment->getSegment(), time, endTime, m_currentElement->event()); CommandHistory::getInstance()->addCommand(command); Event* ev = m_currentElement->event(); delete m_currentElement; delete ev; ev = command->getLastInsertedEvent(); if (ev) { m_scene->setSingleSelectedEvent (&m_currentViewSegment->getSegment(), ev, false); } } m_currentElement = 0; m_currentViewSegment = 0; setBasicContextHelp(); } void MatrixPainter::ready() { // connect(m_parentView->getCanvasView(), SIGNAL(contentsMoving (int, int)), // this, SLOT(slotMatrixScrolled(int, int))); if (m_widget) m_widget->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) { newX = newY = 42; /*!!! if (!m_currentElement) return ; QPoint newP1(newX, newY), oldP1(m_parentView->getCanvasView()->contentsX(), m_parentView->getCanvasView()->contentsY()); QPoint offset = newP1 - oldP1; offset = m_widget->inverseMapPoint(offset); QPoint p(m_currentElement->getCanvasX() + m_currentElement->getWidth(), m_currentElement->getCanvasY()); p += offset; timeT newTime = getSnapGrid()->snapX(p.x()); int newPitch = m_currentViewSegment->getHeightAtCanvasCoords(p.x(), p.y()); handleMouseMove(newTime, newPitch, 0); */ } void MatrixPainter::setBasicContextHelp() { if (getSnapGrid()->getSnapSetting() != SnapGrid::NoSnap) { setContextHelp(tr("Click and drag to draw a note; Shift to avoid snapping to grid")); } else { setContextHelp(tr("Click and drag to draw a note")); } } const QString MatrixPainter::ToolName = "painter"; } #include "MatrixPainter.moc"