summaryrefslogtreecommitdiffstats
path: root/src/gui/rulers/LoopRuler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/rulers/LoopRuler.cpp')
-rw-r--r--src/gui/rulers/LoopRuler.cpp363
1 files changed, 363 insertions, 0 deletions
diff --git a/src/gui/rulers/LoopRuler.cpp b/src/gui/rulers/LoopRuler.cpp
new file mode 100644
index 0000000..bdf6c5e
--- /dev/null
+++ b/src/gui/rulers/LoopRuler.cpp
@@ -0,0 +1,363 @@
+/* -*- 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 "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 <qpainter.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qwidget.h>
+#include <qtooltip.h>
+#include <klocale.h>
+#include <kaction.h>
+#include <qpainter.h>
+#include <qpointarray.h>
+#include "document/RosegardenGUIDoc.h"
+
+
+namespace Rosegarden
+{
+
+LoopRuler::LoopRuler(RosegardenGUIDoc *doc,
+ RulerScale *rulerScale,
+ int height,
+ double xorigin,
+ bool invert,
+ QWidget *parent,
+ const char *name) :
+ QWidget(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(QPen(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);
+
+ QToolTip::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);
+ }
+}
+
+QSize LoopRuler::sizeHint() const
+{
+ double width =
+ m_rulerScale->getBarPosition(m_rulerScale->getLastVisibleBar()) +
+ m_rulerScale->getBarWidth(m_rulerScale->getLastVisibleBar()) +
+ m_xorigin;
+
+ QSize res(std::max(int(width), m_width), m_height);
+
+ return res;
+}
+
+QSize LoopRuler::minimumSizeHint() const
+{
+ double firstBarWidth = m_rulerScale->getBarWidth(0) + m_xorigin;
+
+ QSize res = QSize(int(firstBarWidth), m_height);
+
+ return res;
+}
+
+void LoopRuler::paintEvent(QPaintEvent* e)
+{
+ QPainter 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(QPainter* paint)
+{
+ QRect 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(QPainter* 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(QMouseEvent *mE)
+{
+ RG_DEBUG << "LoopRuler::mousePressEvent: x = " << mE->x() << endl;
+
+ Qt::ButtonState bs = mE->state();
+ setLoopingMode((bs & Qt::ShiftButton) != 0);
+
+ if (mE->button() == 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(QMouseEvent *mE)
+{
+ if (mE->button() == 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(QMouseEvent *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() == LeftButton && !m_loopingMode)
+ emit setPlayPosition(m_grid->snapX(x));
+}
+
+void
+LoopRuler::mouseMoveEvent(QMouseEvent *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;
+
+ QPainter paint(this);
+ paint.setBrush(colorGroup().foreground());
+ drawBarSections(&paint);
+ drawLoopMarker(&paint);
+
+ update();
+}
+
+}
+#include "LoopRuler.moc"