/* -*- 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 , Chris Cannam , Richard Bown 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 #include #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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 tc = m_composition->getTempoChange(tcn); std::pair 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 tc = m_composition->getTempoChange(tcn); std::pair 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 tc = m_composition->getTempoChange(tcn); std::pair 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 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 tc = m_composition->getTempoChange(tcn); std::pair 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 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 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(x), height() - (height()/4), static_cast(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(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(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 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 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 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 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(tmp); if (!m_menu) { RG_DEBUG << "MarkerRuler::createMenu() failed\n"; } } } #include "TempoRuler.moc"