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.
538 lines
15 KiB
538 lines
15 KiB
/*
|
|
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 "ControlRuler.h"
|
|
|
|
#include "base/Event.h"
|
|
#include "misc/Debug.h"
|
|
#include "base/RulerScale.h"
|
|
#include "base/Segment.h"
|
|
#include "base/Selection.h"
|
|
#include "ControlChangeCommand.h"
|
|
#include "ControlItem.h"
|
|
#include "ControlSelector.h"
|
|
#include "ControlTool.h"
|
|
#include "DefaultVelocityColour.h"
|
|
#include "ElementAdapter.h"
|
|
#include "gui/general/EditView.h"
|
|
#include "gui/general/RosegardenCanvasView.h"
|
|
#include "gui/widgets/TextFloat.h"
|
|
#include <tdemainwindow.h>
|
|
#include <tqcanvas.h>
|
|
#include <tqcolor.h>
|
|
#include <tqcursor.h>
|
|
#include <tqpoint.h>
|
|
#include <tqpopupmenu.h>
|
|
#include <tqscrollbar.h>
|
|
#include <tqscrollview.h>
|
|
#include <tqstring.h>
|
|
#include <tqwidget.h>
|
|
#include <algorithm>
|
|
|
|
|
|
namespace Rosegarden
|
|
{
|
|
|
|
const int ControlRuler::DefaultRulerHeight = 75;
|
|
const int ControlRuler::MinItemHeight = 5;
|
|
const int ControlRuler::MaxItemHeight = 64 + 5;
|
|
const int ControlRuler::ItemHeightRange = 64;
|
|
|
|
ControlRuler::ControlRuler(Segment *segment,
|
|
RulerScale* rulerScale,
|
|
EditViewBase* parentView,
|
|
TQCanvas* c, TQWidget* parent,
|
|
const char* name, WFlags f) :
|
|
RosegardenCanvasView(c, parent, name, f),
|
|
m_parentEditView(parentView),
|
|
m_mainHorizontalScrollBar(0),
|
|
m_rulerScale(rulerScale),
|
|
m_eventSelection(new EventSelection(*segment)),
|
|
m_segment(segment),
|
|
m_currentItem(0),
|
|
m_tool(0),
|
|
m_maxItemValue(127),
|
|
m_staffOffset(0),
|
|
m_currentX(0.0),
|
|
m_itemMoved(false),
|
|
m_selecting(false),
|
|
m_selector(new ControlSelector(this)),
|
|
m_selectionRect(new TQCanvasRectangle(canvas())),
|
|
m_menu(0)
|
|
{
|
|
setHScrollBarMode(TQScrollView::AlwaysOff);
|
|
|
|
m_selectionRect->setPen(TQt::red);
|
|
|
|
setFixedHeight(sizeHint().height());
|
|
|
|
connect(this, TQT_SIGNAL(stateChange(const TQString&, bool)),
|
|
m_parentEditView, TQT_SLOT(slotStateChanged(const TQString&, bool)));
|
|
|
|
m_numberFloat = new TextFloat(this);
|
|
m_numberFloat->hide();
|
|
|
|
m_segment->addObserver(this);
|
|
|
|
emit stateChange("have_controller_item_selected", false);
|
|
}
|
|
|
|
ControlRuler::~ControlRuler()
|
|
{
|
|
if (m_segment) {
|
|
m_segment->removeObserver(this);
|
|
}
|
|
}
|
|
|
|
void ControlRuler::slotUpdate()
|
|
{
|
|
RG_DEBUG << "ControlRuler::slotUpdate()\n";
|
|
|
|
canvas()->setAllChanged(); // TODO: be a bit more subtle, call setChanged(<time area>)
|
|
|
|
canvas()->update();
|
|
repaint();
|
|
}
|
|
|
|
void ControlRuler::slotUpdateElementsHPos()
|
|
{
|
|
computeStaffOffset();
|
|
|
|
TQCanvasItemList list = canvas()->allItems();
|
|
|
|
TQCanvasItemList::Iterator it = list.begin();
|
|
for (; it != list.end(); ++it) {
|
|
ControlItem* item = dynamic_cast<ControlItem*>(*it);
|
|
if (!item)
|
|
continue;
|
|
layoutItem(item);
|
|
}
|
|
|
|
canvas()->update();
|
|
}
|
|
|
|
void ControlRuler::layoutItem(ControlItem* item)
|
|
{
|
|
timeT itemTime = item->getElementAdapter()->getTime();
|
|
|
|
double x = m_rulerScale->getXForTime(itemTime);
|
|
|
|
item->setX(x + m_staffOffset);
|
|
int itemElementDuration = item->getElementAdapter()->getDuration();
|
|
|
|
int width = int(m_rulerScale->getXForTime(itemTime + itemElementDuration) - x);
|
|
|
|
item->setWidth(width);
|
|
|
|
// RG_DEBUG << "ControlRuler::layoutItem ControlItem x = " << x << " - width = " << width << endl;
|
|
}
|
|
|
|
void ControlRuler::setControlTool(ControlTool* tool)
|
|
{
|
|
if (m_tool)
|
|
delete m_tool;
|
|
m_tool = tool;
|
|
}
|
|
|
|
void
|
|
ControlRuler::segmentDeleted(const Segment *)
|
|
{
|
|
m_segment = 0;
|
|
}
|
|
|
|
void ControlRuler::contentsMousePressEvent(TQMouseEvent* e)
|
|
{
|
|
if (e->button() != TQt::LeftButton) {
|
|
m_numberFloat->hide();
|
|
m_selecting = false;
|
|
return ;
|
|
}
|
|
|
|
RG_DEBUG << "ControlRuler::contentsMousePressEvent()\n";
|
|
|
|
TQPoint p = inverseMapPoint(e->pos());
|
|
|
|
TQCanvasItemList l = canvas()->collisions(p);
|
|
|
|
if (l.count() == 0) { // de-select current item
|
|
clearSelectedItems();
|
|
m_selecting = true;
|
|
m_selector->handleMouseButtonPress(e);
|
|
RG_DEBUG << "ControlRuler::contentsMousePressEvent : entering selection mode\n";
|
|
return ;
|
|
}
|
|
|
|
// clear selection unless control was pressed, in which case
|
|
// add the event to the current selection
|
|
if (!(e->state() && ControlButton)) {
|
|
clearSelectedItems();
|
|
}
|
|
|
|
ControlItem *topItem = 0;
|
|
for (TQCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) {
|
|
|
|
if (ControlItem *item = dynamic_cast<ControlItem*>(*it)) {
|
|
|
|
if (topItem == 0)
|
|
topItem = item;
|
|
|
|
if (item->isSelected()) { // if the item which was clicked
|
|
// on is part of a selection,
|
|
// propagate mousepress on all
|
|
// selected items
|
|
|
|
item->handleMouseButtonPress(e);
|
|
|
|
for (TQCanvasItemList::Iterator it = m_selectedItems.begin();
|
|
it != m_selectedItems.end(); ++it) {
|
|
if (ControlItem *selectedItem =
|
|
dynamic_cast<ControlItem*>(*it)) {
|
|
selectedItem->handleMouseButtonPress(e);
|
|
}
|
|
}
|
|
|
|
|
|
} else { // select it
|
|
|
|
if (!(e->state() && ControlButton)) {
|
|
if (item->z() > topItem->z())
|
|
topItem = item;
|
|
|
|
} else {
|
|
m_selectedItems << item;
|
|
item->setSelected(true);
|
|
item->handleMouseButtonPress(e);
|
|
ElementAdapter* adapter = item->getElementAdapter();
|
|
m_eventSelection->addEvent(adapter->getEvent());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (topItem && !m_selectedItems.contains(topItem)) { // select the top item
|
|
m_selectedItems << topItem;
|
|
topItem->setSelected(true);
|
|
topItem->handleMouseButtonPress(e);
|
|
ElementAdapter* adapter = topItem->getElementAdapter();
|
|
m_eventSelection->addEvent(adapter->getEvent());
|
|
}
|
|
|
|
m_itemMoved = false;
|
|
m_lastEventPos = p;
|
|
}
|
|
|
|
void ControlRuler::contentsMouseReleaseEvent(TQMouseEvent* e)
|
|
{
|
|
if (e->button() != TQt::LeftButton) {
|
|
m_numberFloat->hide();
|
|
m_selecting = false;
|
|
return ;
|
|
}
|
|
|
|
if (m_selecting) {
|
|
updateSelection();
|
|
m_selector->handleMouseButtonRelease(e);
|
|
RG_DEBUG << "ControlRuler::contentsMouseReleaseEvent : leaving selection mode\n";
|
|
m_selecting = false;
|
|
return ;
|
|
}
|
|
|
|
for (TQCanvasItemList::Iterator it = m_selectedItems.begin(); it != m_selectedItems.end(); ++it) {
|
|
if (ControlItem *item = dynamic_cast<ControlItem*>(*it)) {
|
|
|
|
ElementAdapter * adapter = item->getElementAdapter();
|
|
m_eventSelection->addEvent(adapter->getEvent());
|
|
item->handleMouseButtonRelease(e);
|
|
}
|
|
}
|
|
|
|
emit stateChange("have_controller_item_selected", true);
|
|
|
|
if (m_itemMoved) {
|
|
|
|
m_lastEventPos = inverseMapPoint(e->pos());
|
|
|
|
// Add command to history
|
|
ControlChangeCommand* command = new ControlChangeCommand(m_selectedItems,
|
|
*m_segment,
|
|
m_eventSelection->getStartTime(),
|
|
m_eventSelection->getEndTime());
|
|
|
|
RG_DEBUG << "ControlRuler::contentsMouseReleaseEvent : adding command\n";
|
|
m_parentEditView->addCommandToHistory(command);
|
|
|
|
m_itemMoved = false;
|
|
}
|
|
|
|
m_numberFloat->hide();
|
|
}
|
|
|
|
void ControlRuler::contentsMouseMoveEvent(TQMouseEvent* e)
|
|
{
|
|
TQPoint p = inverseMapPoint(e->pos());
|
|
|
|
int deltaX = p.x() - m_lastEventPos.x(),
|
|
deltaY = p.y() - m_lastEventPos.y();
|
|
m_lastEventPos = p;
|
|
|
|
if (m_selecting) {
|
|
updateSelection();
|
|
m_selector->handleMouseMove(e, deltaX, deltaY);
|
|
slotScrollHorizSmallSteps(p.x());
|
|
return ;
|
|
}
|
|
|
|
m_itemMoved = true;
|
|
|
|
// Borrowed from Rotary - compute total position within window
|
|
//
|
|
TQPoint totalPos = mapTo(topLevelWidget(), TQPoint(0, 0));
|
|
|
|
int scrollX = dynamic_cast<EditView*>(m_parentEditView)->getRawCanvasView()->
|
|
horizontalScrollBar()->value();
|
|
|
|
/*
|
|
RG_DEBUG << "ControlRuler::contentsMouseMoveEvent - total pos = " << totalPos.x()
|
|
<< ",e pos = " << e->pos().x()
|
|
<< ", scroll bar = " << scrollX
|
|
<< endl;
|
|
*/
|
|
|
|
// Allow for scrollbar
|
|
//
|
|
m_numberFloat->move(totalPos.x() + e->pos().x() - scrollX + 20,
|
|
totalPos.y() + e->pos().y() - 10);
|
|
|
|
int value = 0;
|
|
|
|
for (TQCanvasItemList::Iterator it = m_selectedItems.begin(); it != m_selectedItems.end(); ++it) {
|
|
if (ControlItem *item = dynamic_cast<ControlItem*>(*it)) {
|
|
item->handleMouseMove(e, deltaX, deltaY);
|
|
// ElementAdapter* adapter = item->getElementAdapter();
|
|
|
|
// set value to highest in selection
|
|
if (item->getValue() >= value) {
|
|
value = item->getValue();
|
|
m_numberFloat->setText(TQString("%1").arg(value));
|
|
}
|
|
}
|
|
}
|
|
canvas()->update();
|
|
|
|
m_numberFloat->show();
|
|
|
|
}
|
|
|
|
void
|
|
ControlRuler::contentsWheelEvent(TQWheelEvent *e)
|
|
{
|
|
// not sure what to do yet
|
|
TQCanvasView::contentsWheelEvent(e);
|
|
}
|
|
|
|
void ControlRuler::updateSelection()
|
|
{
|
|
clearSelectedItems();
|
|
|
|
bool haveSelectedItems = false;
|
|
|
|
TQCanvasItemList l = getSelectionRectangle()->collisions(true);
|
|
|
|
for (TQCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) {
|
|
|
|
if (ControlItem *item = dynamic_cast<ControlItem*>(*it)) {
|
|
item->setSelected(true);
|
|
m_selectedItems << item;
|
|
haveSelectedItems = true;
|
|
|
|
ElementAdapter* adapter = item->getElementAdapter();
|
|
m_eventSelection->addEvent(adapter->getEvent());
|
|
}
|
|
}
|
|
|
|
emit stateChange("have_controller_item_selected", haveSelectedItems);
|
|
}
|
|
|
|
void ControlRuler::contentsContextMenuEvent(TQContextMenuEvent* e)
|
|
{
|
|
if (!m_menu && !m_menuName.isEmpty())
|
|
createMenu();
|
|
|
|
if (m_menu) {
|
|
RG_DEBUG << "ControlRuler::showMenu() - show menu with" << m_menu->count() << " items\n";
|
|
m_lastEventPos = inverseMapPoint(e->pos());
|
|
m_menu->exec(TQCursor::pos());
|
|
} else
|
|
RG_DEBUG << "ControlRuler::showMenu() : no menu to show\n";
|
|
|
|
}
|
|
|
|
void ControlRuler::createMenu()
|
|
{
|
|
RG_DEBUG << "ControlRuler::createMenu()\n";
|
|
|
|
TDEMainWindow* parentMainWindow = dynamic_cast<TDEMainWindow*>(topLevelWidget());
|
|
|
|
if (parentMainWindow && parentMainWindow->factory()) {
|
|
m_menu = static_cast<TQPopupMenu*>(parentMainWindow->factory()->container(m_menuName, parentMainWindow));
|
|
|
|
if (!m_menu) {
|
|
RG_DEBUG << "ControlRuler::createMenu() failed\n";
|
|
}
|
|
} else {
|
|
RG_DEBUG << "ControlRuler::createMenu() failed: no parent factory\n";
|
|
}
|
|
}
|
|
|
|
void
|
|
ControlRuler::clearSelectedItems()
|
|
{
|
|
for (TQCanvasItemList::Iterator it = m_selectedItems.begin(); it != m_selectedItems.end(); ++it) {
|
|
(*it)->setSelected(false);
|
|
}
|
|
m_selectedItems.clear();
|
|
|
|
delete m_eventSelection;
|
|
m_eventSelection = new EventSelection(*m_segment);
|
|
}
|
|
|
|
void ControlRuler::clear()
|
|
{
|
|
TQCanvasItemList allItems = canvas()->allItems();
|
|
|
|
for (TQCanvasItemList::Iterator it = allItems.begin(); it != allItems.end(); ++it) {
|
|
if (ControlItem *item = dynamic_cast<ControlItem*>(*it)) {
|
|
delete item;
|
|
}
|
|
}
|
|
}
|
|
|
|
int ControlRuler::valueToHeight(long val)
|
|
{
|
|
long scaleVal = val * (ItemHeightRange);
|
|
|
|
int res = -(int(scaleVal / getMaxItemValue()) + MinItemHeight);
|
|
|
|
//RG_DEBUG << "ControlRuler::valueToHeight : val = " << val << " - height = " << res
|
|
//<< " - scaleVal = " << scaleVal << endl;
|
|
|
|
return res;
|
|
}
|
|
|
|
long ControlRuler::heightToValue(int h)
|
|
{
|
|
long val = -h;
|
|
val -= MinItemHeight;
|
|
val *= getMaxItemValue();
|
|
val /= (ItemHeightRange);
|
|
val = std::min(val, long(getMaxItemValue()));
|
|
return val;
|
|
}
|
|
|
|
TQColor ControlRuler::valueToColour(int max, int val)
|
|
{
|
|
int maxDefault = DefaultVelocityColour::getInstance()->getMaxValue();
|
|
|
|
int value = val;
|
|
|
|
// Scale value accordingly
|
|
//
|
|
if (maxDefault != max)
|
|
value = int(double(maxDefault) * double(val) / double(max));
|
|
|
|
return DefaultVelocityColour::getInstance()->getColour(value);
|
|
}
|
|
|
|
int ControlRuler::applyTool(double x, int val)
|
|
{
|
|
if (m_tool)
|
|
return (*m_tool)(x, val);
|
|
return val;
|
|
}
|
|
|
|
void ControlRuler::flipForwards()
|
|
{
|
|
std::pair<int, int> minMax = getZMinMax();
|
|
|
|
TQCanvasItemList l = canvas()->allItems();
|
|
for (TQCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) {
|
|
|
|
// skip all but rectangles
|
|
if ((*it)->rtti() != TQCanvasItem::Rtti_Rectangle)
|
|
continue;
|
|
|
|
// match min
|
|
if ((*it)->z() == minMax.second)
|
|
(*it)->setZ(minMax.first);
|
|
else
|
|
(*it)->setZ((*it)->z() + 1);
|
|
}
|
|
|
|
canvas()->update();
|
|
}
|
|
|
|
void ControlRuler::flipBackwards()
|
|
{
|
|
std::pair<int, int> minMax = getZMinMax();
|
|
|
|
TQCanvasItemList l = canvas()->allItems();
|
|
for (TQCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) {
|
|
|
|
// skip all but rectangles
|
|
if ((*it)->rtti() != TQCanvasItem::Rtti_Rectangle)
|
|
continue;
|
|
|
|
// match min
|
|
if ((*it)->z() == minMax.first)
|
|
(*it)->setZ(minMax.second);
|
|
else
|
|
(*it)->setZ((*it)->z() - 1);
|
|
}
|
|
|
|
canvas()->update();
|
|
}
|
|
|
|
std::pair<int, int> ControlRuler::getZMinMax()
|
|
{
|
|
TQCanvasItemList l = canvas()->allItems();
|
|
std::vector<int> zList;
|
|
for (TQCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it) {
|
|
|
|
// skip all but rectangles
|
|
if ((*it)->rtti() != TQCanvasItem::Rtti_Rectangle) continue;
|
|
zList.push_back(int((*it)->z()));
|
|
}
|
|
|
|
std::sort(zList.begin(), zList.end());
|
|
|
|
return std::pair<int, int>(zList[0], zList[zList.size() - 1]);
|
|
}
|
|
|
|
TQScrollBar* ControlRuler::getMainHorizontalScrollBar()
|
|
{
|
|
return m_mainHorizontalScrollBar ? m_mainHorizontalScrollBar : horizontalScrollBar();
|
|
}
|
|
|
|
}
|
|
#include "ControlRuler.moc"
|