diff options
Diffstat (limited to 'src/gui/rulers/TempoRuler.cpp')
-rw-r--r-- | src/gui/rulers/TempoRuler.cpp | 1091 |
1 files changed, 1091 insertions, 0 deletions
diff --git a/src/gui/rulers/TempoRuler.cpp b/src/gui/rulers/TempoRuler.cpp new file mode 100644 index 0000000..270b224 --- /dev/null +++ b/src/gui/rulers/TempoRuler.cpp @@ -0,0 +1,1091 @@ +/* -*- 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 "TempoRuler.h" + +#include <klocale.h> +#include <kstddirs.h> +#include "misc/Debug.h" +#include "base/Composition.h" +#include "base/NotationTypes.h" +#include "base/RealTime.h" +#include "base/RulerScale.h" +#include "base/SnapGrid.h" +#include "document/RosegardenGUIDoc.h" +#include "document/MultiViewCommandHistory.h" +#include "gui/application/RosegardenGUIApp.h" +#include "gui/dialogs/TempoDialog.h" +#include "gui/general/GUIPalette.h" +#include "gui/widgets/TextFloat.h" +#include "TempoColour.h" +#include <kaction.h> +#include <kglobal.h> +#include <kxmlguiclient.h> +#include <kxmlguifactory.h> +#include <qcolor.h> +#include <qcursor.h> +#include <qevent.h> +#include <qfont.h> +#include <qfontmetrics.h> +#include <qiconset.h> +#include <qobject.h> +#include <qpainter.h> +#include <qpixmap.h> +#include <qpoint.h> +#include <qpopupmenu.h> +#include <qrect.h> +#include <qsize.h> +#include <qstring.h> +#include <qwidget.h> + + +namespace Rosegarden +{ + +TempoRuler::TempoRuler(RulerScale *rulerScale, + RosegardenGUIDoc *doc, + KMainWindow *parentMainWindow, + double xorigin, + int height, + bool small, + QWidget *parent, + const char *name) : + QWidget(parent, name), + m_xorigin(xorigin), + m_height(height), + m_currentXOffset(0), + m_width( -1), + m_small(small), + m_illuminate( -1), + m_illuminatePoint(false), + m_illuminateTarget(false), + m_refreshLinesOnly(false), + m_dragVert(false), + m_dragTarget(false), + m_dragHoriz(false), + m_dragStartY(0), + m_dragStartX(0), + m_dragFine(false), + m_clickX(0), + m_dragStartTempo( -1), + m_dragStartTarget( -1), + m_dragOriginalTempo( -1), + m_dragOriginalTarget( -1), + m_composition(&doc->getComposition()), + m_rulerScale(rulerScale), + m_menu(0), + m_parentMainWindow(parentMainWindow), + m_fontMetrics(m_boldFont) +{ + // m_font.setPointSize(m_small ? 9 : 11); + // m_boldFont.setPointSize(m_small ? 9 : 11); + + // m_font.setPixelSize(m_height * 2 / 3); + // m_boldFont.setPixelSize(m_height * 2 / 3); + + m_font.setPixelSize(m_height / 3); + m_boldFont.setPixelSize(m_height * 2 / 5); + m_boldFont.setBold(true); + m_fontMetrics = QFontMetrics(m_boldFont); + + m_textFloat = new TextFloat(this); + m_textFloat->hide(); + + // setBackgroundColor(GUIPalette::getColour(GUIPalette::TextRulerBackground)); + setBackgroundMode(Qt::NoBackground); + + QObject::connect + (doc->getCommandHistory(), SIGNAL(commandExecuted()), + this, SLOT(update())); + + QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/"); + QIconSet icon; + + icon = QIconSet(QPixmap(pixmapDir + "/toolbar/event-insert-tempo.png")); + new KAction(i18n("Insert Tempo Change"), icon, 0, this, + SLOT(slotInsertTempoHere()), actionCollection(), + "insert_tempo_here"); + + new KAction(i18n("Insert Tempo Change at Playback Position"), 0, 0, this, + SLOT(slotInsertTempoAtPointer()), actionCollection(), + "insert_tempo_at_pointer"); + + icon = QIconSet(QPixmap(pixmapDir + "/toolbar/event-delete.png")); + new KAction(i18n("Delete Tempo Change"), icon, 0, this, + SLOT(slotDeleteTempoChange()), actionCollection(), + "delete_tempo"); + + new KAction(i18n("Ramp Tempo to Next Tempo"), 0, 0, this, + SLOT(slotRampToNext()), actionCollection(), + "ramp_to_next"); + + new KAction(i18n("Un-Ramp Tempo"), 0, 0, this, + SLOT(slotUnramp()), actionCollection(), + "unramp"); + + icon = QIconSet(QPixmap(pixmapDir + "/toolbar/event-edit.png")); + new KAction(i18n("Edit Tempo..."), icon, 0, this, + SLOT(slotEditTempo()), actionCollection(), + "edit_tempo"); + + new KAction(i18n("Edit Time Signature..."), 0, 0, this, + SLOT(slotEditTimeSignature()), actionCollection(), + "edit_time_signature"); + + new KAction(i18n("Open Tempo and Time Signature Editor"), 0, 0, this, + SLOT(slotEditTempos()), actionCollection(), + "edit_tempos"); + + setMouseTracking(false); +} + +TempoRuler::~TempoRuler() +{ + // we have to do this so that the menu is re-created properly + // when the main window is itself recreated (on a File->New for instance) + KXMLGUIFactory* factory = m_parentMainWindow->factory(); + if (factory) + factory->removeClient(this); +} + +void +TempoRuler::connectSignals() +{ + connect(this, + SIGNAL(doubleClicked(timeT)), + RosegardenGUIApp::self(), + SLOT(slotEditTempos(timeT))); + + connect(this, + SIGNAL(changeTempo(timeT, + tempoT, + tempoT, + TempoDialog::TempoDialogAction)), + RosegardenGUIApp::self(), + SLOT(slotChangeTempo(timeT, + tempoT, + tempoT, + TempoDialog::TempoDialogAction))); + + connect(this, + SIGNAL(moveTempo(timeT, + timeT)), + RosegardenGUIApp::self(), + SLOT(slotMoveTempo(timeT, + timeT))); + + connect(this, + SIGNAL(deleteTempo(timeT)), + RosegardenGUIApp::self(), + SLOT(slotDeleteTempo(timeT))); + + connect(this, + SIGNAL(editTempo(timeT)), + RosegardenGUIApp::self(), + SLOT(slotEditTempo(timeT))); + + connect(this, + SIGNAL(editTimeSignature(timeT)), + RosegardenGUIApp::self(), + SLOT(slotEditTimeSignature(timeT))); + + connect(this, + SIGNAL(editTempos(timeT)), + RosegardenGUIApp::self(), + SLOT(slotEditTempos(timeT))); +} + +void +TempoRuler::slotScrollHoriz(int x) +{ + 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); + } +} + +void +TempoRuler::mousePressEvent(QMouseEvent *e) +{ + if (e->button() == LeftButton) { + + if (e->type() == QEvent::MouseButtonDblClick) { + timeT t = m_rulerScale->getTimeForX + (e->x() - m_currentXOffset - m_xorigin); + emit doubleClicked(t); + return ; + } + + int x = e->x() + 1; + int y = e->y(); + timeT t = m_rulerScale->getTimeForX(x - m_currentXOffset - m_xorigin); + int tcn = m_composition->getTempoChangeNumberAt(t); + + if (tcn < 0 || tcn >= m_composition->getTempoChangeCount()) + return ; + + std::pair<timeT, tempoT> tc = m_composition->getTempoChange(tcn); + std::pair<bool, tempoT> tr = m_composition->getTempoRamping(tcn, true); + + m_dragStartY = y; + m_dragStartX = x; + m_dragStartTime = tc.first; + m_dragPreviousTime = m_dragStartTime; + m_dragStartTempo = tc.second; + m_dragStartTarget = tr.first ? tr.second : -1; + m_dragOriginalTempo = m_dragStartTempo; + m_dragOriginalTarget = m_dragStartTarget; + m_dragFine = ((e->state() & Qt::ShiftButton) != 0); + + int px = m_rulerScale->getXForTime(tc.first) + m_currentXOffset + m_xorigin; + if (x >= px && x < px + 5) { + m_dragHoriz = true; + m_dragVert = false; + setCursor(Qt::SplitHCursor); + } else { + timeT nt = m_composition->getEndMarker(); + if (tcn < m_composition->getTempoChangeCount() - 1) { + nt = m_composition->getTempoChange(tcn + 1).first; + } + int nx = m_rulerScale->getXForTime(nt) + m_currentXOffset + m_xorigin; + if (x > px + 5 && x > nx - 5) { + m_dragTarget = true; + setCursor(Qt::SizeVerCursor); + } else { + m_dragTarget = false; + setCursor(Qt::SplitVCursor); + } + m_dragVert = true; + m_dragHoriz = false; + } + + } else if (e->button() == RightButton) { + + m_clickX = e->x(); + if (!m_menu) + createMenu(); + if (m_menu) { + // enable 'delete' action only if cursor is actually over a tempo change + actionCollection()->action("delete_tempo")->setEnabled(m_illuminatePoint); + m_menu->exec(QCursor::pos()); + } + + } +} + +void +TempoRuler::mouseReleaseEvent(QMouseEvent *e) +{ + if (m_dragVert) { + + m_dragVert = false; + unsetCursor(); + + if (e->x() < 0 || e->x() >= width() || + e->y() < 0 || e->y() >= height()) { + leaveEvent(0); + } + + // First we make a note of the values that we just set and + // restore the tempo to whatever it was previously, so that + // the undo for any following command will work correctly. + // Then we emit so that our user can issue the right command. + + int tcn = m_composition->getTempoChangeNumberAt(m_dragStartTime); + std::pair<timeT, tempoT> tc = m_composition->getTempoChange(tcn); + std::pair<bool, tempoT> tr = m_composition->getTempoRamping(tcn, true); + + if (tc.second != m_dragOriginalTempo) { + m_composition->addTempoAtTime(m_dragStartTime, + m_dragOriginalTempo, + m_dragOriginalTarget); + emit changeTempo(m_dragStartTime, tc.second, + tr.first ? tr.second : -1, + TempoDialog::AddTempo); + } + + return ; + + } else if (m_dragHoriz) { + + m_dragHoriz = false; + unsetCursor(); + + if (e->x() < 0 || e->x() >= width() || + e->y() < 0 || e->y() >= height()) { + leaveEvent(0); + } + + if (m_dragPreviousTime != m_dragStartTime) { + + // As above, restore the original tempo and then emit a + // signal to ensure a proper command happens. + + int tcn = m_composition->getTempoChangeNumberAt(m_dragPreviousTime); + m_composition->removeTempoChange(tcn); + m_composition->addTempoAtTime(m_dragStartTime, + m_dragStartTempo, + m_dragStartTarget); + + emit moveTempo(m_dragStartTime, m_dragPreviousTime); + } + + return ; + } +} + +void +TempoRuler::mouseMoveEvent(QMouseEvent *e) +{ + bool shiftPressed = ((e->state() & Qt::ShiftButton) != 0); + + if (m_dragVert) { + + if (shiftPressed != m_dragFine) { + + m_dragFine = shiftPressed; + m_dragStartY = e->y(); + + // reset the start tempi to whatever we last updated them + // to as we switch into or out of fine mode + int tcn = m_composition->getTempoChangeNumberAt(m_dragStartTime); + std::pair<timeT, tempoT> tc = m_composition->getTempoChange(tcn); + std::pair<bool, tempoT> tr = m_composition->getTempoRamping(tcn, true); + m_dragStartTempo = tc.second; + m_dragStartTarget = tr.first ? tr.second : -1; + } + + int diff = m_dragStartY - e->y(); // +ve for upwards drag + tempoT newTempo = m_dragStartTempo; + tempoT newTarget = m_dragStartTarget; + + if (diff != 0) { + + float qpm = m_composition->getTempoQpm(newTempo); + + if (m_dragTarget && newTarget > 0) { + qpm = m_composition->getTempoQpm(newTarget); + } + + float qdiff = (m_dragFine ? diff * 0.05 : diff * 0.5); + qpm += qdiff; + if (qpm < 1) + qpm = 1; + + if (m_dragTarget) { + + newTarget = m_composition->getTempoForQpm(qpm); + + } else { + + newTempo = m_composition->getTempoForQpm(qpm); + + if (newTarget >= 0) { + qpm = m_composition->getTempoQpm(newTarget); + qpm += qdiff; + if (qpm < 1) + qpm = 1; + newTarget = m_composition->getTempoForQpm(qpm); + } + } + } + + showTextFloat(newTempo, newTarget, m_dragStartTime); + m_composition->addTempoAtTime(m_dragStartTime, newTempo, newTarget); + update(); + + } else if (m_dragHoriz) { + + int x = e->x(); + + SnapGrid grid(m_rulerScale); + if (shiftPressed) { + grid.setSnapTime(SnapGrid::NoSnap); + } else { + grid.setSnapTime(SnapGrid::SnapToUnit); + } + timeT newTime = grid.snapX(x - m_currentXOffset - m_xorigin, + SnapGrid::SnapEither); + + int tcn = m_composition->getTempoChangeNumberAt(m_dragPreviousTime); + int ncn = m_composition->getTempoChangeNumberAt(newTime); + if (ncn > tcn || ncn < tcn - 1) + return ; + if (ncn >= 0 && ncn == tcn - 1) { + std::pair<timeT, tempoT> nc = m_composition->getTempoChange(ncn); + if (nc.first == newTime) + return ; + } + + // std::cerr << " -> " << newTime << std::endl; + + m_composition->removeTempoChange(tcn); + m_composition->addTempoAtTime(newTime, + m_dragStartTempo, + m_dragStartTarget); + showTextFloat(m_dragStartTempo, m_dragStartTarget, newTime, true); + m_dragPreviousTime = newTime; + update(); + + } else { + + int x = e->x() + 1; + timeT t = m_rulerScale->getTimeForX(x - m_currentXOffset - m_xorigin); + int tcn = m_composition->getTempoChangeNumberAt(t); + + if (tcn < 0 || tcn >= m_composition->getTempoChangeCount()) + return ; + + std::pair<timeT, tempoT> tc = m_composition->getTempoChange(tcn); + std::pair<bool, tempoT> tr = m_composition->getTempoRamping(tcn, true); + + int bar, beat, fraction, remainder; + m_composition->getMusicalTimeForAbsoluteTime(tc.first, bar, beat, + fraction, remainder); + RG_DEBUG << "Tempo change: tempo " << m_composition->getTempoQpm(tc.second) << " at " << bar << ":" << beat << ":" << fraction << ":" << remainder << endl; + + m_illuminate = tcn; + m_illuminatePoint = false; + m_illuminateTarget = false; + //!!! m_refreshLinesOnly = true; + + //!!! merge this test with the one in mousePressEvent as + //isCloseToStart or equiv, and likewise for close to end + + int px = m_rulerScale->getXForTime(tc.first) + m_currentXOffset + m_xorigin; + if (x >= px && x < px + 5) { + m_illuminatePoint = true; + } else { + timeT nt = m_composition->getEndMarker(); + if (tcn < m_composition->getTempoChangeCount() - 1) { + nt = m_composition->getTempoChange(tcn + 1).first; + } + int nx = m_rulerScale->getXForTime(nt) + m_currentXOffset + m_xorigin; + if (x > px + 5 && x > nx - 5) { + m_illuminateTarget = true; + } + + // std::cerr << "nt = " << nt << ", nx = " << nx << ", x = " << x << ", m_illuminateTarget = " << m_illuminateTarget << std::endl; + } + + showTextFloat(tc.second, tr.first ? tr.second : -1, + tc.first, m_illuminatePoint); + + update(); + } +} + +void +TempoRuler::wheelEvent(QWheelEvent *e) +{} + +void +TempoRuler::enterEvent(QEvent *) +{ + setMouseTracking(true); +} + +void +TempoRuler::leaveEvent(QEvent *) +{ + if (!m_dragVert && !m_dragHoriz) { + setMouseTracking(false); + m_illuminate = -1; + m_illuminatePoint = false; + //!!! m_refreshLinesOnly = true; + m_textFloat->hide(); + update(); + } +} + +void +TempoRuler::showTextFloat(tempoT tempo, tempoT target, + timeT time, bool showTime) +{ + float qpm = m_composition->getTempoQpm(tempo); + int qi = int(qpm + 0.0001); + int q0 = int(qpm * 10 + 0.0001) % 10; + int q00 = int(qpm * 100 + 0.0001) % 10; + + bool haveSet = false; + + QString tempoText, timeText; + + if (time >= 0) { + + if (showTime) { + int bar, beat, fraction, remainder; + m_composition->getMusicalTimeForAbsoluteTime + (time, bar, beat, fraction, remainder); + RealTime rt = m_composition->getElapsedRealTime(time); + + // blargh -- duplicated with TempoView::makeTimeString + timeText = QString("%1%2%3-%4%5-%6%7-%8%9") + .arg(bar / 100) + .arg((bar % 100) / 10) + .arg(bar % 10) + .arg(beat / 10) + .arg(beat % 10) + .arg(fraction / 10) + .arg(fraction % 10) + .arg(remainder / 10) + .arg(remainder % 10); + + timeText = QString("%1\n%2") + .arg(timeText) + // .arg(rt.toString().c_str()); + .arg(rt.toText(true).c_str()); + } + + TimeSignature sig = + m_composition->getTimeSignatureAt(time); + + if (sig.getBeatDuration() != + Note(Note::Crotchet).getDuration()) { + + float bpm = + (qpm * + Note(Note::Crotchet).getDuration()) + / sig.getBeatDuration(); + int bi = int(bpm + 0.0001); + int b0 = int(bpm * 10 + 0.0001) % 10; + int b00 = int(bpm * 100 + 0.0001) % 10; + + tempoText = i18n("%1.%2%3 (%4.%5%6 bpm)") + .arg(qi).arg(q0).arg(q00) + .arg(bi).arg(b0).arg(b00); + haveSet = true; + } + } + + if (!haveSet) { + tempoText = i18n("%1.%2%3 bpm").arg(qi).arg(q0).arg(q00); + } + + if (target > 0 && target != tempo) { + float tq = m_composition->getTempoQpm(target); + int tqi = int(tq + 0.0001); + int tq0 = int(tq * 10 + 0.0001) % 10; + int tq00 = int(tq * 100 + 0.0001) % 10; + tempoText = i18n("%1 - %2.%3%4").arg(tempoText).arg(tqi).arg(tq0).arg(tq00); + } + + if (showTime && time >= 0) { + m_textFloat->setText(QString("%1\n%2").arg(timeText).arg(tempoText)); + } else { + m_textFloat->setText(tempoText); + } + + QPoint cp = mapFromGlobal(QPoint(QCursor::pos())); + // std::cerr << "cp = " << cp.x() << "," << cp.y() << ", tempo = " << qpm << std::endl; + QPoint mp = cp + pos(); + + QWidget *parent = parentWidget(); + while (parent->parentWidget() && + !parent->isTopLevel() && + !parent->isDialog()) { + mp += parent->pos(); + parent = parent->parentWidget(); + } + + int yoff = cp.y() + m_textFloat->height() + 3; + mp = QPoint(mp.x() + 10, mp.y() > yoff ? mp.y() - yoff : 0); + + m_textFloat->move(mp); + m_textFloat->show(); +} + +QSize +TempoRuler::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 +TempoRuler::minimumSizeHint() const +{ + double firstBarWidth = m_rulerScale->getBarWidth(0) + m_xorigin; + QSize res = QSize(int(firstBarWidth), m_height); + return res; +} + +int +TempoRuler::getYForTempo(tempoT tempo) +{ + int drawh = height() - 4; + int y = drawh / 2; + + tempoT minTempo = m_composition->getMinTempo(); + tempoT maxTempo = m_composition->getMaxTempo(); + + if (maxTempo > minTempo) { + y = drawh - + int((double(tempo - minTempo) / double(maxTempo - minTempo)) + * drawh + 0.5); + } + + return y; +} + +tempoT +TempoRuler::getTempoForY(int y) +{ + int drawh = height() - 4; + + tempoT minTempo = m_composition->getMinTempo(); + tempoT maxTempo = m_composition->getMaxTempo(); + + tempoT tempo = minTempo; + + if (maxTempo > minTempo) { + tempo = (maxTempo - minTempo) * + (double(drawh - y) / double(drawh)) + minTempo + 0.5; + } + + return tempo; +} + +void +TempoRuler::paintEvent(QPaintEvent* e) +{ + QRect clipRect = e->rect(); + + if (m_buffer.width() < width() || m_buffer.height() < height()) { + m_buffer = QPixmap(width(), height()); + } + + m_buffer.fill(GUIPalette::getColour + (GUIPalette::TextRulerBackground)); + + QPainter paint(&m_buffer); + paint.setPen(GUIPalette::getColour + (GUIPalette::TextRulerForeground)); + + paint.setClipRegion(e->region()); + paint.setClipRect(clipRect); + + if (m_xorigin > 0) { + paint.fillRect(0, 0, m_xorigin, height(), paletteBackgroundColor()); + } + + timeT from = m_rulerScale->getTimeForX + (clipRect.x() - m_currentXOffset - 100 - m_xorigin); + timeT to = m_rulerScale->getTimeForX + (clipRect.x() + clipRect.width() - m_currentXOffset + 100 - m_xorigin); + + QRect boundsForHeight = m_fontMetrics.boundingRect("019"); + int fontHeight = boundsForHeight.height(); + int textY = fontHeight + 2; + + double prevEndX = -1000.0; + double prevTempo = 0.0; + long prevBpm = 0; + + typedef std::map<timeT, int> TimePoints; + int tempoChangeHere = 1; + int timeSigChangeHere = 2; + TimePoints timePoints; + + for (int tempoNo = m_composition->getTempoChangeNumberAt(from); + tempoNo <= m_composition->getTempoChangeNumberAt(to) + 1; ++tempoNo) { + + if (tempoNo >= 0 && tempoNo < m_composition->getTempoChangeCount()) { + timePoints.insert + (TimePoints::value_type + (m_composition->getTempoChange(tempoNo).first, + tempoChangeHere)); + } + } + + for (int sigNo = m_composition->getTimeSignatureNumberAt(from); + sigNo <= m_composition->getTimeSignatureNumberAt(to) + 1; ++sigNo) { + + if (sigNo >= 0 && sigNo < m_composition->getTimeSignatureCount()) { + timeT time(m_composition->getTimeSignatureChange(sigNo).first); + if (timePoints.find(time) != timePoints.end()) { + timePoints[time] |= timeSigChangeHere; + } else { + timePoints.insert(TimePoints::value_type(time, timeSigChangeHere)); + } + } + } + + int lastx = 0, lasty = 0, lastx1 = 0; + bool haveSome = false; + // tempoT minTempo = m_composition->getMinTempo(); + // tempoT maxTempo = m_composition->getMaxTempo(); + bool illuminate = false; + + if (m_illuminate >= 0) { + int tcn = m_composition->getTempoChangeNumberAt(from); + illuminate = (m_illuminate == tcn); + } + + for (TimePoints::iterator i = timePoints.begin(); ; ++i) { + + timeT t0, t1; + + if (i == timePoints.begin()) { + t0 = from; + } else { + TimePoints::iterator j(i); + --j; + t0 = j->first; + } + + if (i == timePoints.end()) { + t1 = to; + } else { + t1 = i->first; + } + + if (t1 <= t0) + t1 = to; + + int tcn = m_composition->getTempoChangeNumberAt(t0); + tempoT tempo = m_composition->getTempoAtTime(t0); + + std::pair<bool, tempoT> ramping(false, tempo); + if (tcn > 0 && tcn < m_composition->getTempoChangeCount() + 1) { + ramping = m_composition->getTempoRamping(tcn - 1, true); + } + + double x0, x1; + x0 = m_rulerScale->getXForTime(t0) + m_currentXOffset + m_xorigin; + x1 = m_rulerScale->getXForTime(t1) + m_currentXOffset + m_xorigin; + /*!!! + if (x0 > e->rect().x()) { + paint.fillRect(e->rect().x(), 0, x0 - e->rect().x(), height(), + paletteBackgroundColor()); + } + */ + QColor colour = TempoColour::getColour(m_composition->getTempoQpm(tempo)); + paint.setPen(colour); + paint.setBrush(colour); + + if (!m_refreshLinesOnly) { + // RG_DEBUG << "TempoRuler: draw rect from " << x0 << " to " << x1 << endl; + paint.drawRect(int(x0), 0, int(x1 - x0) + 1, height()); + } + + int y = getYForTempo(tempo); + /*!!! + int drawh = height() - 4; + int y = drawh / 2; + if (maxTempo > minTempo) { + y = drawh - + int((double(tempo - minTempo) / double(maxTempo - minTempo)) + * drawh + 0.5); + } + */ + y += 2; + + if (haveSome) { + + int x = int(x0) + 1; + int ry = lasty; + + bool illuminateLine = (illuminate && + !m_illuminatePoint && !m_illuminateTarget); + + paint.setPen(illuminateLine ? Qt::white : Qt::black); + + if (ramping.first) { + ry = getYForTempo(ramping.second); + ry += 2; + /*!!! + ry = drawh - + int((double(ramping.second - minTempo) / + double(maxTempo - minTempo)) + * drawh + 0.5); + */ + } + + paint.drawLine(lastx + 1, lasty, x - 2, ry); + + if (!illuminateLine && illuminate && m_illuminateTarget) { + if (x > lastx) { + paint.setPen(Qt::white); + paint.drawLine(x - 6, ry - ((ry - lasty) * 6) / (x - lastx), + x - 2, ry); + } + } + + if (m_illuminate >= 0) { + illuminate = (m_illuminate == tcn); + } + + bool illuminatePoint = (illuminate && m_illuminatePoint); + + paint.setPen(illuminatePoint ? Qt::white : Qt::black); + paint.drawRect(x - 1, y - 1, 3, 3); + + paint.setPen(illuminatePoint ? Qt::black : Qt::white); + paint.drawPoint(x, y); + } + + lastx = int(x0) + 1; + lastx1 = int(x1) + 1; + lasty = y; + if (i == timePoints.end()) + break; + haveSome = true; + } + + if (lastx1 < e->rect().x() + e->rect().width()) { + /*!!! + paint.fillRect(lastx1, 0, + e->rect().x() + e->rect().width() - lastx1, height(), + paletteBackgroundColor()); + */ + } + + if (haveSome) { + bool illuminateLine = (illuminate && !m_illuminatePoint); + paint.setPen(illuminateLine ? Qt::white : Qt::black); + paint.drawLine(lastx + 1, lasty, width(), lasty); + } else if (!m_refreshLinesOnly) { + tempoT tempo = m_composition->getTempoAtTime(from); + QColor colour = TempoColour::getColour(m_composition->getTempoQpm(tempo)); + paint.setPen(colour); + paint.setBrush(colour); + paint.drawRect(e->rect()); + } + + paint.setPen(Qt::black); + paint.setBrush(Qt::black); + paint.drawLine(0, 0, width(), 0); + + for (TimePoints::iterator i = timePoints.begin(); + i != timePoints.end(); ++i) { + + timeT time = i->first; + double x = m_rulerScale->getXForTime(time) + m_currentXOffset + + m_xorigin; + + /* + paint.drawLine(static_cast<int>(x), + height() - (height()/4), + static_cast<int>(x), + height()); + */ + + if ((i->second & timeSigChangeHere) && !m_refreshLinesOnly) { + + TimeSignature sig = + m_composition->getTimeSignatureAt(time); + + QString str = QString("%1/%2") + .arg(sig.getNumerator()) + .arg(sig.getDenominator()); + + paint.setFont(m_boldFont); + paint.drawText(static_cast<int>(x) + 2, m_height - 2, str); + } + + if ((i->second & tempoChangeHere) && !m_refreshLinesOnly) { + + double tempo = m_composition->getTempoQpm(m_composition->getTempoAtTime(time)); + long bpm = long(tempo); + // long frac = long(tempo * 100 + 0.001) - 100 * bpm; + + QString tempoString = QString("%1").arg(bpm); + + if (tempo == prevTempo) { + if (m_small) + continue; + tempoString = "="; + } else if (bpm == prevBpm) { + tempoString = (tempo > prevTempo ? "+" : "-"); + } else { + if (m_small && (bpm != (bpm / 10 * 10))) { + if (bpm == prevBpm + 1) + tempoString = "+"; + else if (bpm == prevBpm - 1) + tempoString = "-"; + } + } + prevTempo = tempo; + prevBpm = bpm; + + QRect bounds = m_fontMetrics.boundingRect(tempoString); + + paint.setFont(m_font); + if (time > 0) + x -= bounds.width() / 2; + // if (x > bounds.width() / 2) x -= bounds.width() / 2; + if (prevEndX >= x - 3) + x = prevEndX + 3; + paint.drawText(static_cast<int>(x), textY, tempoString); + prevEndX = x + bounds.width(); + } + } + + paint.end(); + + QPainter dbpaint(this); + // dbpaint.drawPixmap(0, 0, m_buffer); + dbpaint.drawPixmap(clipRect.x(), clipRect.y(), + m_buffer, + clipRect.x(), clipRect.y(), + clipRect.width(), clipRect.height()); + + dbpaint.end(); + + m_refreshLinesOnly = false; +} + +void +TempoRuler::slotInsertTempoHere() +{ + SnapGrid grid(m_rulerScale); + grid.setSnapTime(SnapGrid::SnapToUnit); + timeT t = grid.snapX(m_clickX - m_currentXOffset - m_xorigin, + SnapGrid::SnapLeft); + tempoT tempo = Composition::getTempoForQpm(120.0); + + int tcn = m_composition->getTempoChangeNumberAt(t); + if (tcn >= 0 && tcn < m_composition->getTempoChangeCount()) { + std::pair<timeT, tempoT> tc = m_composition->getTempoChange(tcn); + if (tc.first == t) + return ; + tempo = tc.second; + } + + emit changeTempo(t, tempo, -1, TempoDialog::AddTempo); +} + +void +TempoRuler::slotInsertTempoAtPointer() +{ + timeT t = m_composition->getPosition(); + tempoT tempo = Composition::getTempoForQpm(120.0); + + int tcn = m_composition->getTempoChangeNumberAt(t); + if (tcn >= 0 && tcn < m_composition->getTempoChangeCount()) { + std::pair<timeT, tempoT> tc = m_composition->getTempoChange(tcn); + if (tc.first == t) + return ; + tempo = tc.second; + } + + emit changeTempo(t, tempo, -1, TempoDialog::AddTempo); +} + +void +TempoRuler::slotDeleteTempoChange() +{ + timeT t = m_rulerScale->getTimeForX(m_clickX - m_currentXOffset - m_xorigin); + emit deleteTempo(t); +} + +void +TempoRuler::slotRampToNext() +{ + timeT t = m_rulerScale->getTimeForX(m_clickX - m_currentXOffset - m_xorigin); + + int tcn = m_composition->getTempoChangeNumberAt(t); + if (tcn < 0 || tcn >= m_composition->getTempoChangeCount()) + return ; + + std::pair<timeT, tempoT> tc = m_composition->getTempoChange(tcn); + + emit changeTempo(tc.first, tc.second, 0, TempoDialog::AddTempo); +} + +void +TempoRuler::slotUnramp() +{ + timeT t = m_rulerScale->getTimeForX(m_clickX - m_currentXOffset - m_xorigin); + + int tcn = m_composition->getTempoChangeNumberAt(t); + if (tcn < 0 || tcn >= m_composition->getTempoChangeCount()) + return ; + + std::pair<timeT, tempoT> tc = m_composition->getTempoChange(tcn); + + emit changeTempo(tc.first, tc.second, -1, TempoDialog::AddTempo); +} + +void +TempoRuler::slotEditTempo() +{ + timeT t = m_rulerScale->getTimeForX(m_clickX - m_currentXOffset - m_xorigin); + emit editTempo(t); +} + +void +TempoRuler::slotEditTimeSignature() +{ + timeT t = m_rulerScale->getTimeForX(m_clickX - m_currentXOffset - m_xorigin); + emit editTimeSignature(t); +} + +void +TempoRuler::slotEditTempos() +{ + timeT t = m_rulerScale->getTimeForX(m_clickX - m_currentXOffset - m_xorigin); + emit editTempos(t); +} + +void +TempoRuler::createMenu() +{ + setXMLFile("temporuler.rc"); + + KXMLGUIFactory* factory = m_parentMainWindow->factory(); + factory->addClient(this); + + QWidget* tmp = factory->container("tempo_ruler_menu", this); + + m_menu = dynamic_cast<QPopupMenu*>(tmp); + + if (!m_menu) { + RG_DEBUG << "MarkerRuler::createMenu() failed\n"; + } +} + + +} +#include "TempoRuler.moc" |