You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
630 lines
19 KiB
630 lines
19 KiB
/* -*- 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 <tdeconfig.h>
|
|
#include <tqdialog.h>
|
|
#include <tqiconset.h>
|
|
#include <tqpoint.h>
|
|
#include <tqstring.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, TQT_SIGNAL(usedSelection()),
|
|
this, TQT_SLOT(slotHideSelection()));
|
|
|
|
new TDEAction(i18n("Switch to Draw Tool"), "pencil", 0, this,
|
|
TQT_SLOT(slotDrawSelected()), actionCollection(),
|
|
"draw");
|
|
|
|
new TDEAction(i18n("Switch to Erase Tool"), "eraser", 0, this,
|
|
TQT_SLOT(slotEraseSelected()), actionCollection(),
|
|
"erase");
|
|
|
|
new TDEAction(i18n("Switch to Move Tool"), "move", 0, this,
|
|
TQT_SLOT(slotMoveSelected()), actionCollection(),
|
|
"move");
|
|
|
|
TQString pixmapDir = TDEGlobal::dirs()->findResource("appdata", "pixmaps/");
|
|
TQCanvasPixmap pixmap(pixmapDir + "/toolbar/resize.xpm");
|
|
TQIconSet icon = TQIconSet(pixmap);
|
|
|
|
new TDEAction(i18n("Switch to Resize Tool"), icon, 0, this,
|
|
TQT_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,
|
|
TQMouseEvent* e,
|
|
ViewElement *element)
|
|
{
|
|
MATRIX_DEBUG << "MatrixSelector::handleMousePress" << endl;
|
|
|
|
if (m_justSelectedBar) {
|
|
handleMouseTripleClick(time, height, staffNo, e, element);
|
|
m_justSelectedBar = false;
|
|
return ;
|
|
}
|
|
|
|
TQPoint 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() & TQt::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() & TQt::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,
|
|
TQMouseEvent* 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,
|
|
TQMouseEvent *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() == TQDialog::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() == TQDialog::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 {
|
|
|
|
TQRect 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;
|
|
TQTimer::singleShot(TQApplication::doubleClickInterval(), this,
|
|
TQT_SLOT(slotClickTimeout()));
|
|
} */
|
|
}
|
|
|
|
void MatrixSelector::handleMouseTripleClick(timeT t,
|
|
int height,
|
|
int staffNo,
|
|
TQMouseEvent *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,
|
|
TQMouseEvent *e)
|
|
{
|
|
TQPoint 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());
|
|
|
|
// TQt 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, TQMouseEvent *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 TQCanvasRectangle(m_mParentView->canvas());
|
|
m_selectionRect->hide();
|
|
m_selectionRect->setPen(TQPen(GUIPalette::getColour(GUIPalette::SelectionRectangle), 2));
|
|
|
|
m_mParentView->setCanvasCursor(TQt::arrowCursor);
|
|
//m_mParentView->setPositionTracking(false);
|
|
}
|
|
|
|
connect(m_parentView->getCanvasView(), TQT_SIGNAL(contentsMoving (int, int)),
|
|
this, TQT_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(), TQT_SIGNAL(contentsMoving (int, int)),
|
|
this, TQT_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);
|
|
|
|
// TQt 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->isVisible()) return 0;
|
|
|
|
Segment& originalSegment = m_currentStaff->getSegment();
|
|
EventSelection* selection = new EventSelection(originalSegment);
|
|
|
|
// get the selections
|
|
//
|
|
TQCanvasItemList l = m_selectionRect->collisions(true);
|
|
|
|
if (l.count())
|
|
{
|
|
for (TQCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it)
|
|
{
|
|
TQCanvasItem *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(TQPoint 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
|
|
|
|
TQCanvasItemList itemList = m_mParentView->canvas()->collisions(p);
|
|
TQCanvasItemList::Iterator it;
|
|
MatrixElement* mel = 0;
|
|
TQCanvasItem* activeItem = 0;
|
|
|
|
for (it = itemList.begin(); it != itemList.end(); ++it) {
|
|
|
|
TQCanvasItem *item = *it;
|
|
QCanvasMatrixRectangle *mRect = 0;
|
|
|
|
if (item->isActive()) {
|
|
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 TQString MatrixSelector::ToolName = "selector";
|
|
|
|
}
|
|
#include "MatrixSelector.moc"
|