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.
362 lines
11 KiB
362 lines
11 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 "LoopRuler.h"
|
|
|
|
#include "misc/Debug.h"
|
|
#include "base/RulerScale.h"
|
|
#include "base/SnapGrid.h"
|
|
#include "gui/general/GUIPalette.h"
|
|
#include "gui/general/HZoomable.h"
|
|
#include "gui/general/RosegardenCanvasView.h"
|
|
#include <tqpainter.h>
|
|
#include <tqrect.h>
|
|
#include <tqsize.h>
|
|
#include <tqwidget.h>
|
|
#include <tqtooltip.h>
|
|
#include <tdelocale.h>
|
|
#include <tdeaction.h>
|
|
#include <tqpainter.h>
|
|
#include <tqpointarray.h>
|
|
#include "document/RosegardenGUIDoc.h"
|
|
|
|
|
|
namespace Rosegarden
|
|
{
|
|
|
|
LoopRuler::LoopRuler(RosegardenGUIDoc *doc,
|
|
RulerScale *rulerScale,
|
|
int height,
|
|
double xorigin,
|
|
bool invert,
|
|
TQWidget *parent,
|
|
const char *name) :
|
|
TQWidget(parent, name),
|
|
m_doc(doc),
|
|
m_height(height),
|
|
m_xorigin(xorigin),
|
|
m_invert(invert),
|
|
m_currentXOffset(0),
|
|
m_width( -1),
|
|
m_activeMousePress(false),
|
|
m_rulerScale(rulerScale),
|
|
m_defaultGrid(rulerScale),
|
|
m_loopGrid(rulerScale),
|
|
m_grid(&m_defaultGrid),
|
|
m_loopingMode(false),
|
|
m_startLoop(0), m_endLoop(0),
|
|
m_quickMarkerPen(TQPen(GUIPalette::getColour(GUIPalette::QuickMarker), 4))
|
|
{
|
|
/*
|
|
* I need to understand if this ruler is being built for the main
|
|
* window (Track Editor) or for any of the segment editors. Apparently
|
|
* the name parameter is supplied (non-NULL) only for the main window.
|
|
* I can't find of any other (more decent) way to do this. Sorry.
|
|
*/
|
|
m_mainWindow = (name != 0 && std::string(name).length() != 0);
|
|
|
|
setBackgroundColor(GUIPalette::getColour(GUIPalette::LoopRulerBackground));
|
|
|
|
// Always snap loop extents to beats; by default apply no snap to
|
|
// pointer position
|
|
//
|
|
m_defaultGrid.setSnapTime(SnapGrid::NoSnap);
|
|
m_loopGrid.setSnapTime(SnapGrid::SnapToBeat);
|
|
|
|
TQToolTip::add
|
|
(this, i18n("Click and drag to move the playback pointer.\nShift-click and drag to set a range for looping or editing.\nShift-click to clear the loop or range.\nDouble-click to start playback."));
|
|
}
|
|
|
|
LoopRuler::~LoopRuler()
|
|
{}
|
|
|
|
void
|
|
LoopRuler::setSnapGrid(SnapGrid *grid)
|
|
{
|
|
if (grid == 0) m_grid = &m_defaultGrid;
|
|
else m_grid = grid;
|
|
}
|
|
|
|
void LoopRuler::scrollHoriz(int x)
|
|
{
|
|
if (getHScaleFactor() != 1.0) {
|
|
m_currentXOffset = static_cast<int>( -x / getHScaleFactor());
|
|
repaint();
|
|
return;
|
|
}
|
|
|
|
int w = width(), h = height();
|
|
int dx = x - ( -m_currentXOffset);
|
|
m_currentXOffset = -x;
|
|
|
|
if (dx > w*3 / 4 || dx < -w*3 / 4) {
|
|
update();
|
|
return ;
|
|
}
|
|
|
|
if (dx > 0) { // moving right, so the existing stuff moves left
|
|
bitBlt(this, 0, 0, this, dx, 0, w - dx, h);
|
|
repaint(w - dx, 0, dx, h);
|
|
} else { // moving left, so the existing stuff moves right
|
|
bitBlt(this, -dx, 0, this, 0, 0, w + dx, h);
|
|
repaint(0, 0, -dx, h);
|
|
}
|
|
}
|
|
|
|
TQSize LoopRuler::sizeHint() const
|
|
{
|
|
double width =
|
|
m_rulerScale->getBarPosition(m_rulerScale->getLastVisibleBar()) +
|
|
m_rulerScale->getBarWidth(m_rulerScale->getLastVisibleBar()) +
|
|
m_xorigin;
|
|
|
|
TQSize res(std::max(int(width), m_width), m_height);
|
|
|
|
return res;
|
|
}
|
|
|
|
TQSize LoopRuler::minimumSizeHint() const
|
|
{
|
|
double firstBarWidth = m_rulerScale->getBarWidth(0) + m_xorigin;
|
|
|
|
TQSize res = TQSize(int(firstBarWidth), m_height);
|
|
|
|
return res;
|
|
}
|
|
|
|
void LoopRuler::paintEvent(TQPaintEvent* e)
|
|
{
|
|
TQPainter paint(this);
|
|
|
|
if (getHScaleFactor() != 1.0)
|
|
paint.scale(getHScaleFactor(), 1.0);
|
|
|
|
paint.setClipRegion(e->region());
|
|
paint.setClipRect(e->rect().normalize());
|
|
|
|
paint.setBrush(colorGroup().foreground());
|
|
drawBarSections(&paint);
|
|
drawLoopMarker(&paint);
|
|
|
|
if (m_mainWindow) {
|
|
timeT tQM = m_doc->getQuickMarkerTime();
|
|
if (tQM >= 0) {
|
|
// draw quick marker
|
|
double xQM = m_rulerScale->getXForTime(tQM)
|
|
+ m_xorigin + m_currentXOffset;
|
|
|
|
paint.setPen(m_quickMarkerPen);
|
|
|
|
// looks necessary to compensate for shift in the CompositionView (cursor)
|
|
paint.translate(1, 0);
|
|
|
|
// draw red segment
|
|
paint.drawLine(int(xQM), 1, int(xQM), m_height-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LoopRuler::drawBarSections(TQPainter* paint)
|
|
{
|
|
TQRect clipRect = paint->clipRegion().boundingRect();
|
|
|
|
int firstBar = m_rulerScale->getBarForX(clipRect.x() -
|
|
m_currentXOffset -
|
|
m_xorigin);
|
|
int lastBar = m_rulerScale->getLastVisibleBar();
|
|
if (firstBar < m_rulerScale->getFirstVisibleBar()) {
|
|
firstBar = m_rulerScale->getFirstVisibleBar();
|
|
}
|
|
|
|
paint->setPen(GUIPalette::getColour(GUIPalette::LoopRulerForeground));
|
|
|
|
for (int i = firstBar; i <= lastBar; ++i) {
|
|
|
|
double x = m_rulerScale->getBarPosition(i) + m_currentXOffset + m_xorigin;
|
|
if ((x * getHScaleFactor()) > clipRect.x() + clipRect.width())
|
|
break;
|
|
|
|
double width = m_rulerScale->getBarWidth(i);
|
|
if (width == 0)
|
|
continue;
|
|
|
|
if (x + width < clipRect.x())
|
|
continue;
|
|
|
|
if (m_invert) {
|
|
paint->drawLine(int(x), 0, int(x), 5*m_height / 7);
|
|
} else {
|
|
paint->drawLine(int(x), 2*m_height / 7, int(x), m_height);
|
|
}
|
|
|
|
double beatAccumulator = m_rulerScale->getBeatWidth(i);
|
|
double inc = beatAccumulator;
|
|
if (inc == 0)
|
|
continue;
|
|
|
|
for (; beatAccumulator < width; beatAccumulator += inc) {
|
|
if (m_invert) {
|
|
paint->drawLine(int(x + beatAccumulator), 0,
|
|
int(x + beatAccumulator), 2 * m_height / 7);
|
|
} else {
|
|
paint->drawLine(int(x + beatAccumulator), 5*m_height / 7,
|
|
int(x + beatAccumulator), m_height);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LoopRuler::drawLoopMarker(TQPainter* paint)
|
|
{
|
|
double x1 = (int)m_rulerScale->getXForTime(m_startLoop);
|
|
double x2 = (int)m_rulerScale->getXForTime(m_endLoop);
|
|
|
|
if (x1 > x2) {
|
|
x2 = x1;
|
|
x1 = (int)m_rulerScale->getXForTime(m_endLoop);
|
|
}
|
|
|
|
x1 += m_currentXOffset + m_xorigin;
|
|
x2 += m_currentXOffset + m_xorigin;
|
|
|
|
paint->save();
|
|
paint->setBrush(GUIPalette::getColour(GUIPalette::LoopHighlight));
|
|
paint->setPen(GUIPalette::getColour(GUIPalette::LoopHighlight));
|
|
paint->drawRect(static_cast<int>(x1), 0, static_cast<int>(x2 - x1), m_height);
|
|
paint->restore();
|
|
|
|
}
|
|
|
|
void
|
|
LoopRuler::mousePressEvent(TQMouseEvent *mE)
|
|
{
|
|
RG_DEBUG << "LoopRuler::mousePressEvent: x = " << mE->x() << endl;
|
|
|
|
TQt::ButtonState bs = mE->state();
|
|
setLoopingMode((bs & TQt::ShiftButton) != 0);
|
|
|
|
if (mE->button() == TQt::LeftButton) {
|
|
double x = mE->pos().x() / getHScaleFactor() - m_currentXOffset - m_xorigin;
|
|
|
|
if (m_loopingMode) {
|
|
m_endLoop = m_startLoop = m_loopGrid.snapX(x);
|
|
} else {
|
|
// No -- now that we're emitting when the button is
|
|
// released, we _don't_ want to emit here as well --
|
|
// otherwise we get an irritating stutter when simply
|
|
// clicking on the ruler during playback
|
|
// RG_DEBUG << "emitting setPointerPosition(" << m_rulerScale->getTimeForX(x) << ")" << endl;
|
|
// emit setPointerPosition(m_rulerScale->getTimeForX(x));
|
|
}
|
|
|
|
m_activeMousePress = true;
|
|
emit startMouseMove(RosegardenCanvasView::FollowHorizontal);
|
|
}
|
|
}
|
|
|
|
void
|
|
LoopRuler::mouseReleaseEvent(TQMouseEvent *mE)
|
|
{
|
|
if (mE->button() == TQt::LeftButton) {
|
|
if (m_loopingMode) {
|
|
// Cancel the loop if there was no drag
|
|
//
|
|
if (m_endLoop == m_startLoop) {
|
|
m_endLoop = m_startLoop = 0;
|
|
|
|
// to clear any other loop rulers
|
|
emit setLoop(m_startLoop, m_endLoop);
|
|
update();
|
|
}
|
|
|
|
// emit with the args around the right way
|
|
//
|
|
if (m_endLoop < m_startLoop)
|
|
emit setLoop(m_endLoop, m_startLoop);
|
|
else
|
|
emit setLoop(m_startLoop, m_endLoop);
|
|
} else {
|
|
// we need to re-emit this signal so that when the user releases the button
|
|
// after dragging the pointer, the pointer's position is updated again in the
|
|
// other views (typically, in the seg. canvas while the user has dragged the pointer
|
|
// in an edit view)
|
|
//
|
|
double x = mE->pos().x() / getHScaleFactor() - m_currentXOffset - m_xorigin;
|
|
emit setPointerPosition(m_grid->snapX(x));
|
|
}
|
|
emit stopMouseMove();
|
|
m_activeMousePress = false;
|
|
}
|
|
}
|
|
|
|
void
|
|
LoopRuler::mouseDoubleClickEvent(TQMouseEvent *mE)
|
|
{
|
|
double x = mE->pos().x() / getHScaleFactor() - m_currentXOffset - m_xorigin;
|
|
if (x < 0)
|
|
x = 0;
|
|
|
|
RG_DEBUG << "LoopRuler::mouseDoubleClickEvent: x = " << x << ", looping = " << m_loopingMode << endl;
|
|
|
|
if (mE->button() == TQt::LeftButton && !m_loopingMode)
|
|
emit setPlayPosition(m_grid->snapX(x));
|
|
}
|
|
|
|
void
|
|
LoopRuler::mouseMoveEvent(TQMouseEvent *mE)
|
|
{
|
|
double x = mE->pos().x() / getHScaleFactor() - m_currentXOffset - m_xorigin;
|
|
if (x < 0)
|
|
x = 0;
|
|
|
|
if (m_loopingMode) {
|
|
if (m_grid->snapX(x) != m_endLoop) {
|
|
m_endLoop = m_loopGrid.snapX(x);
|
|
emit dragLoopToPosition(m_endLoop);
|
|
update();
|
|
}
|
|
} else {
|
|
emit dragPointerToPosition(m_grid->snapX(x));
|
|
}
|
|
|
|
emit mouseMove();
|
|
}
|
|
|
|
void LoopRuler::slotSetLoopMarker(timeT startLoop,
|
|
timeT endLoop)
|
|
{
|
|
m_startLoop = startLoop;
|
|
m_endLoop = endLoop;
|
|
|
|
TQPainter paint(this);
|
|
paint.setBrush(colorGroup().foreground());
|
|
drawBarSections(&paint);
|
|
drawLoopMarker(&paint);
|
|
|
|
update();
|
|
}
|
|
|
|
}
|
|
#include "LoopRuler.moc"
|