summaryrefslogtreecommitdiffstats
path: root/src/gui/rulers
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
commit145364a8af6a1fec06556221e66d4b724a62fc9a (patch)
tree53bd71a544008c518034f208d64c932dc2883f50 /src/gui/rulers
downloadrosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.tar.gz
rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.zip
Added old abandoned KDE3 version of the RoseGarden MIDI tool
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/rosegarden@1097595 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/gui/rulers')
-rw-r--r--src/gui/rulers/ChordNameRuler.cpp523
-rw-r--r--src/gui/rulers/ChordNameRuler.h146
-rw-r--r--src/gui/rulers/ControlChangeCommand.cpp50
-rw-r--r--src/gui/rulers/ControlChangeCommand.h55
-rw-r--r--src/gui/rulers/ControlItem.cpp195
-rw-r--r--src/gui/rulers/ControlItem.h79
-rw-r--r--src/gui/rulers/ControlRuler.cpp539
-rw-r--r--src/gui/rulers/ControlRuler.h182
-rw-r--r--src/gui/rulers/ControlRulerEventEraseCommand.cpp58
-rw-r--r--src/gui/rulers/ControlRulerEventEraseCommand.h54
-rw-r--r--src/gui/rulers/ControlRulerEventInsertCommand.cpp67
-rw-r--r--src/gui/rulers/ControlRulerEventInsertCommand.h56
-rw-r--r--src/gui/rulers/ControlSelector.cpp72
-rw-r--r--src/gui/rulers/ControlSelector.h60
-rw-r--r--src/gui/rulers/ControlTool.h39
-rw-r--r--src/gui/rulers/ControllerEventAdapter.cpp83
-rw-r--r--src/gui/rulers/ControllerEventAdapter.h53
-rw-r--r--src/gui/rulers/ControllerEventsRuler.cpp499
-rw-r--r--src/gui/rulers/ControllerEventsRuler.h118
-rw-r--r--src/gui/rulers/DefaultVelocityColour.cpp55
-rw-r--r--src/gui/rulers/DefaultVelocityColour.h54
-rw-r--r--src/gui/rulers/ElementAdapter.h46
-rw-r--r--src/gui/rulers/LoopRuler.cpp363
-rw-r--r--src/gui/rulers/LoopRuler.h148
-rw-r--r--src/gui/rulers/MarkerRuler.cpp490
-rw-r--r--src/gui/rulers/MarkerRuler.h121
-rw-r--r--src/gui/rulers/PercussionPitchRuler.cpp204
-rw-r--r--src/gui/rulers/PercussionPitchRuler.h91
-rw-r--r--src/gui/rulers/PitchRuler.cpp55
-rw-r--r--src/gui/rulers/PitchRuler.h78
-rw-r--r--src/gui/rulers/PropertyBox.cpp77
-rw-r--r--src/gui/rulers/PropertyBox.h74
-rw-r--r--src/gui/rulers/PropertyControlRuler.cpp441
-rw-r--r--src/gui/rulers/PropertyControlRuler.h120
-rw-r--r--src/gui/rulers/PropertyViewRuler.cpp175
-rw-r--r--src/gui/rulers/PropertyViewRuler.h102
-rw-r--r--src/gui/rulers/RawNoteRuler.cpp573
-rw-r--r--src/gui/rulers/RawNoteRuler.h128
-rw-r--r--src/gui/rulers/StandardRuler.cpp172
-rw-r--r--src/gui/rulers/StandardRuler.h108
-rw-r--r--src/gui/rulers/TempoColour.cpp55
-rw-r--r--src/gui/rulers/TempoColour.h60
-rw-r--r--src/gui/rulers/TempoRuler.cpp1091
-rw-r--r--src/gui/rulers/TempoRuler.h180
-rw-r--r--src/gui/rulers/TextRuler.cpp157
-rw-r--r--src/gui/rulers/TextRuler.h112
-rw-r--r--src/gui/rulers/VelocityColour.cpp120
-rw-r--r--src/gui/rulers/VelocityColour.h106
-rw-r--r--src/gui/rulers/ViewElementAdapter.cpp56
-rw-r--r--src/gui/rulers/ViewElementAdapter.h59
50 files changed, 8599 insertions, 0 deletions
diff --git a/src/gui/rulers/ChordNameRuler.cpp b/src/gui/rulers/ChordNameRuler.cpp
new file mode 100644
index 0000000..2fc98f2
--- /dev/null
+++ b/src/gui/rulers/ChordNameRuler.cpp
@@ -0,0 +1,523 @@
+/* -*- 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 <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 "ChordNameRuler.h"
+#include "misc/Debug.h"
+
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "base/AnalysisTypes.h"
+#include "base/Composition.h"
+#include "base/CompositionTimeSliceAdapter.h"
+#include "base/Instrument.h"
+#include "base/NotationTypes.h"
+#include "base/Profiler.h"
+#include "base/PropertyName.h"
+#include "base/NotationQuantizer.h"
+#include "base/RefreshStatus.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/Studio.h"
+#include "base/Track.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/MultiViewCommandHistory.h"
+#include "gui/general/GUIPalette.h"
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qobject.h>
+#include <qpainter.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qtooltip.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+ChordNameRuler::ChordNameRuler(RulerScale *rulerScale,
+ RosegardenGUIDoc *doc,
+ double xorigin,
+ int height,
+ QWidget *parent,
+ const char *name) :
+ QWidget(parent, name),
+ m_xorigin(xorigin),
+ m_height(height),
+ m_currentXOffset(0),
+ m_width( -1),
+ m_ready(false),
+ m_rulerScale(rulerScale),
+ m_composition(&doc->getComposition()),
+ m_regetSegmentsOnChange(true),
+ m_currentSegment(0),
+ m_studio(0),
+ m_chordSegment(0),
+ m_fontMetrics(m_boldFont),
+ TEXT_FORMAL_X("TextFormalX"),
+ TEXT_ACTUAL_X("TextActualX")
+{
+ m_font.setPointSize(11);
+ m_font.setPixelSize(12);
+ m_boldFont.setPointSize(11);
+ m_boldFont.setPixelSize(12);
+ m_boldFont.setBold(true);
+ m_fontMetrics = QFontMetrics(m_boldFont);
+ setBackgroundColor(GUIPalette::getColour(GUIPalette::ChordNameRulerBackground));
+
+ m_compositionRefreshStatusId = m_composition->getNewRefreshStatusId();
+
+ QObject::connect(doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(update()));
+
+ QToolTip::add
+ (this, i18n("Chord name ruler.\nTurn it on and off from the Settings->Rulers menu."));
+
+}
+
+ChordNameRuler::ChordNameRuler(RulerScale *rulerScale,
+ RosegardenGUIDoc *doc,
+ std::vector<Segment *> &segments,
+ double xorigin,
+ int height,
+ QWidget *parent,
+ const char *name) :
+ QWidget(parent, name),
+ m_xorigin(xorigin),
+ m_height(height),
+ m_currentXOffset(0),
+ m_width( -1),
+ m_ready(false),
+ m_rulerScale(rulerScale),
+ m_composition(&doc->getComposition()),
+ m_regetSegmentsOnChange(false),
+ m_currentSegment(0),
+ m_studio(0),
+ m_chordSegment(0),
+ m_fontMetrics(m_boldFont),
+ TEXT_FORMAL_X("TextFormalX"),
+ TEXT_ACTUAL_X("TextActualX")
+{
+ m_font.setPointSize(11);
+ m_font.setPixelSize(12);
+ m_boldFont.setPointSize(11);
+ m_boldFont.setPixelSize(12);
+ m_boldFont.setBold(true);
+ m_fontMetrics = QFontMetrics(m_boldFont);
+ setBackgroundColor(GUIPalette::getColour(GUIPalette::ChordNameRulerBackground));
+
+ m_compositionRefreshStatusId = m_composition->getNewRefreshStatusId();
+
+ QObject::connect(doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(update()));
+
+ for (std::vector<Segment *>::iterator i = segments.begin();
+ i != segments.end(); ++i) {
+ m_segments.insert(SegmentRefreshMap::value_type
+ (*i, (*i)->getNewRefreshStatusId()));
+ }
+}
+
+ChordNameRuler::~ChordNameRuler()
+{
+ delete m_chordSegment;
+}
+
+void
+ChordNameRuler::setReady()
+{
+ m_ready = true;
+ update();
+}
+
+void
+ChordNameRuler::setCurrentSegment(Segment *segment)
+{
+ m_currentSegment = segment;
+}
+
+void
+ChordNameRuler::setStudio(Studio *studio)
+{
+ m_studio = studio;
+}
+
+void
+ChordNameRuler::slotScrollHoriz(int x)
+{
+ int w = width(), h = height();
+ int dx = x - ( -m_currentXOffset);
+ m_currentXOffset = -x;
+
+ if (dx == 0)
+ return ;
+
+ if (dx > w*7 / 8 || dx < -w*7 / 8) {
+ 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
+ChordNameRuler::sizeHint() const
+{
+ double width =
+ m_rulerScale->getBarPosition(m_rulerScale->getLastVisibleBar()) +
+ m_rulerScale->getBarWidth(m_rulerScale->getLastVisibleBar());
+
+ NOTATION_DEBUG << "Returning chord-label ruler width as " << width << endl;
+
+ QSize res(std::max(int(width), m_width), m_height);
+
+ return res;
+}
+
+QSize
+ChordNameRuler::minimumSizeHint() const
+{
+ double firstBarWidth = m_rulerScale->getBarWidth(0);
+ QSize res = QSize(int(firstBarWidth), m_height);
+ return res;
+}
+
+void
+ChordNameRuler::recalculate(timeT from, timeT to)
+{
+ if (!m_ready)
+ return ;
+
+ Profiler profiler("ChordNameRuler::recalculate");
+ NOTATION_DEBUG << "ChordNameRuler[" << this << "]::recalculate" << endl;
+
+ bool regetSegments = false;
+
+ enum RecalcLevel { RecalcNone, RecalcVisible, RecalcWhole };
+ RecalcLevel level = RecalcNone;
+
+ if (m_segments.empty()) {
+
+ regetSegments = true;
+
+ } else if (m_regetSegmentsOnChange) {
+
+ RefreshStatus &rs =
+ m_composition->getRefreshStatus(m_compositionRefreshStatusId);
+
+ if (rs.needsRefresh()) {
+ rs.setNeedsRefresh(false);
+ regetSegments = true;
+ }
+ }
+
+ if (regetSegments) {
+
+ SegmentSelection ss;
+
+ for (Composition::iterator ci = m_composition->begin();
+ ci != m_composition->end(); ++ci) {
+
+ if (m_studio) {
+
+ TrackId ti = (*ci)->getTrack();
+
+ Instrument *instr = m_studio->getInstrumentById
+ (m_composition->getTrackById(ti)->getInstrument());
+
+ if (instr &&
+ instr->getInstrumentType() == Instrument::Midi &&
+ instr->isPercussion()) {
+ continue;
+ }
+ }
+
+ ss.insert(*ci);
+ }
+
+ std::vector<SegmentRefreshMap::iterator> eraseThese;
+
+ for (SegmentRefreshMap::iterator si = m_segments.begin();
+ si != m_segments.end(); ++si) {
+ if (ss.find(si->first) == ss.end()) {
+ eraseThese.push_back(si);
+ level = RecalcWhole;
+ NOTATION_DEBUG << "Segment deleted, updating (now have " << m_segments.size() << " segments)" << endl;
+ }
+ }
+
+ for (std::vector<SegmentRefreshMap::iterator>::iterator ei = eraseThese.begin();
+ ei != eraseThese.end(); ++ei) {
+ m_segments.erase(*ei);
+ }
+
+
+ for (SegmentSelection::iterator si = ss.begin();
+ si != ss.end(); ++si) {
+
+ if (m_segments.find(*si) == m_segments.end()) {
+ m_segments.insert(SegmentRefreshMap::value_type
+ (*si, (*si)->getNewRefreshStatusId()));
+ level = RecalcWhole;
+ NOTATION_DEBUG << "Segment created, adding (now have " << m_segments.size() << " segments)" << endl;
+ }
+ }
+
+ if (m_currentSegment &&
+ ss.find(m_currentSegment) == ss.end()) {
+ m_currentSegment = 0;
+ level = RecalcWhole;
+ }
+ }
+
+ if (!m_chordSegment)
+ m_chordSegment = new Segment();
+ if (m_segments.empty())
+ return ;
+
+ SegmentRefreshStatus overallStatus;
+ overallStatus.setNeedsRefresh(false);
+
+ for (SegmentRefreshMap::iterator i = m_segments.begin();
+ i != m_segments.end(); ++i) {
+ SegmentRefreshStatus &status =
+ i->first->getRefreshStatus(i->second);
+ if (status.needsRefresh()) {
+ overallStatus.push(status.from(), status.to());
+ }
+ }
+
+ // We now have the overall area affected by these changes, across
+ // all segments. If it's entirely within our displayed area, just
+ // recalculate the displayed area; if it overlaps, calculate the
+ // union of the two areas; if it's entirely without, calculate
+ // nothing.
+
+ if (level == RecalcNone) {
+ if (from == to) {
+ NOTATION_DEBUG << "ChordNameRuler::recalculate: from==to, recalculating all" << endl;
+ level = RecalcWhole;
+ } else if (overallStatus.from() == overallStatus.to()) {
+ NOTATION_DEBUG << "ChordNameRuler::recalculate: overallStatus.from==overallStatus.to, ignoring" << endl;
+ level = RecalcNone;
+ } else if (overallStatus.from() >= from && overallStatus.to() <= to) {
+ NOTATION_DEBUG << "ChordNameRuler::recalculate: change is " << overallStatus.from() << "->" << overallStatus.to() << ", I show " << from << "->" << to << ", recalculating visible area" << endl;
+ level = RecalcVisible;
+ } else if (overallStatus.from() >= to || overallStatus.to() <= from) {
+ NOTATION_DEBUG << "ChordNameRuler::recalculate: change is " << overallStatus.from() << "->" << overallStatus.to() << ", I show " << from << "->" << to << ", ignoring" << endl;
+ level = RecalcNone;
+ } else {
+ NOTATION_DEBUG << "ChordNameRuler::recalculate: change is " << overallStatus.from() << "->" << overallStatus.to() << ", I show " << from << "->" << to << ", recalculating whole" << endl;
+ level = RecalcWhole;
+ }
+ }
+
+ if (level == RecalcNone)
+ return ;
+
+ for (SegmentRefreshMap::iterator i = m_segments.begin();
+ i != m_segments.end(); ++i) {
+ i->first->getRefreshStatus(i->second).setNeedsRefresh(false);
+ }
+
+ if (!m_currentSegment) { //!!! arbitrary, must do better
+ //!!! need a segment starting at zero or so with a clef and key in it!
+ m_currentSegment = m_segments.begin()->first;
+ }
+
+ /*!!!
+
+ for (Composition::iterator ci = m_composition->begin();
+ ci != m_composition->end(); ++ci) {
+
+ if ((*ci)->getEndMarkerTime() >= from &&
+ ((*ci)->getStartTime() <= from ||
+ (clefKeySegment &&
+ (*ci)->getStartTime() < clefKeySegment->getStartTime()))) {
+
+ clefKeySegment = *ci;
+ }
+ }
+
+ if (!clefKeySegment) return;
+ }
+ */
+
+ if (level == RecalcWhole) {
+
+ m_chordSegment->clear();
+
+ timeT clefKeyTime = m_currentSegment->getStartTime();
+ //(from < m_currentSegment->getStartTime() ?
+ // m_currentSegment->getStartTime() : from);
+
+ Clef clef = m_currentSegment->getClefAtTime(clefKeyTime);
+ m_chordSegment->insert(clef.getAsEvent( -1));
+
+ ::Rosegarden::Key key = m_currentSegment->getKeyAtTime(clefKeyTime);
+ m_chordSegment->insert(key.getAsEvent( -1));
+
+ from = 0;
+ to = 0;
+
+ } else {
+ Segment::iterator i = m_chordSegment->findTime(from);
+ Segment::iterator j = m_chordSegment->findTime(to);
+ m_chordSegment->erase(i, j);
+ }
+
+ SegmentSelection selection;
+ for (SegmentRefreshMap::iterator si = m_segments.begin(); si != m_segments.end();
+ ++si) {
+ selection.insert(si->first);
+ }
+
+ CompositionTimeSliceAdapter adapter(m_composition, &selection, from, to);
+ AnalysisHelper helper;
+ helper.labelChords(adapter, *m_chordSegment, m_composition->getNotationQuantizer());
+}
+
+void
+ChordNameRuler::paintEvent(QPaintEvent* e)
+{
+ if (!m_composition || !m_ready)
+ return ;
+
+ NOTATION_DEBUG << "*** Chord Name Ruler: paintEvent" << endl;
+
+ Profiler profiler1("ChordNameRuler::paintEvent (whole)");
+
+ QPainter paint(this);
+ paint.setPen(GUIPalette::getColour(GUIPalette::ChordNameRulerForeground));
+
+ paint.setClipRegion(e->region());
+ paint.setClipRect(e->rect().normalize());
+
+ QRect clipRect = paint.clipRegion().boundingRect();
+
+ timeT from = m_rulerScale->getTimeForX
+ (clipRect.x() - m_currentXOffset - m_xorigin - 50);
+ timeT to = m_rulerScale->getTimeForX
+ (clipRect.x() + clipRect.width() - m_currentXOffset - m_xorigin + 50);
+
+ recalculate(from, to);
+
+ if (!m_chordSegment)
+ return ;
+
+ Profiler profiler2("ChordNameRuler::paintEvent (paint)");
+
+ QRect boundsForHeight = m_fontMetrics.boundingRect("^j|lM");
+ int fontHeight = boundsForHeight.height();
+ int textY = (height() - 6) / 2 + fontHeight / 2;
+
+ double prevX = 0;
+ timeT keyAt = from - 1;
+ std::string keyText;
+
+ NOTATION_DEBUG << "*** Chord Name Ruler: paint " << from << " -> " << to << endl;
+
+ for (Segment::iterator i = m_chordSegment->findTime(from);
+ i != m_chordSegment->findTime(to); ++i) {
+
+ NOTATION_DEBUG << "type " << (*i)->getType() << " at " << (*i)->getAbsoluteTime()
+ << endl;
+
+ if (!(*i)->isa(Text::EventType) ||
+ !(*i)->has(Text::TextPropertyName) ||
+ !(*i)->has(Text::TextTypePropertyName))
+ continue;
+
+ std::string text((*i)->get
+ <String>(Text::TextPropertyName));
+
+ if ((*i)->get
+ <String>(Text::TextTypePropertyName) == Text::KeyName) {
+ timeT myTime = (*i)->getAbsoluteTime();
+ if (myTime == keyAt && text == keyText)
+ continue;
+ else {
+ keyAt = myTime;
+ keyText = text;
+ }
+ }
+
+ double x = m_rulerScale->getXForTime((*i)->getAbsoluteTime());
+ (*i)->set
+ <Int>(TEXT_FORMAL_X, (long)x);
+
+ QRect textBounds = m_fontMetrics.boundingRect(strtoqstr(text));
+ int width = textBounds.width();
+
+ x -= width / 2;
+ if (prevX >= x - 3)
+ x = prevX + 3;
+ (*i)->set
+ <Int>(TEXT_ACTUAL_X, long(x));
+ prevX = x + width;
+ }
+
+ for (Segment::iterator i = m_chordSegment->findTime(from);
+ i != m_chordSegment->findTime(to); ++i) {
+
+ if (!(*i)->isa(Text::EventType))
+ continue;
+ std::string text((*i)->get
+ <String>(Text::TextPropertyName));
+ std::string type((*i)->get
+ <String>(Text::TextTypePropertyName));
+
+ if (!(*i)->has(TEXT_FORMAL_X))
+ continue;
+
+ long formalX = (*i)->get
+ <Int>(TEXT_FORMAL_X);
+ long actualX = (*i)->get
+ <Int>(TEXT_ACTUAL_X);
+
+ formalX += m_currentXOffset + long(m_xorigin);
+ actualX += m_currentXOffset + long(m_xorigin);
+
+ paint.drawLine(formalX, height() - 4, formalX, height());
+
+ if (type == Text::KeyName) {
+ paint.setFont(m_boldFont);
+ } else {
+ paint.setFont(m_font);
+ }
+
+ paint.drawText(actualX, textY, strtoqstr(text));
+ }
+}
+
+}
+#include "ChordNameRuler.moc"
diff --git a/src/gui/rulers/ChordNameRuler.h b/src/gui/rulers/ChordNameRuler.h
new file mode 100644
index 0000000..70cdc12
--- /dev/null
+++ b/src/gui/rulers/ChordNameRuler.h
@@ -0,0 +1,146 @@
+
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_CHORDNAMERULER_H_
+#define _RG_CHORDNAMERULER_H_
+
+#include "base/PropertyName.h"
+#include <map>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qsize.h>
+#include <qwidget.h>
+#include <vector>
+#include "base/Event.h"
+
+
+class QPaintEvent;
+
+
+namespace Rosegarden
+{
+
+class Studio;
+class Segment;
+class RulerScale;
+class RosegardenGUIDoc;
+class Composition;
+
+
+/**
+ * ChordNameRuler is a widget that shows a strip of text strings
+ * describing the chords in a composition.
+ */
+
+class ChordNameRuler : public QWidget
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Construct a ChordNameRuler that displays the chords in the
+ * given Composition at positions calculated by the given
+ * RulerScale. Be aware that it will not be refreshed until
+ * setReady is called (because the first refresh is expensive).
+ */
+ ChordNameRuler(RulerScale *rulerScale,
+ RosegardenGUIDoc *doc,
+ double xorigin = 0.0,
+ int height = 0,
+ QWidget* parent = 0,
+ const char *name = 0);
+
+ /**
+ * Construct a ChordNameRuler that displays the chords in the
+ * given Segments at positions calculated by the given
+ * RulerScale. Be aware that it will not be refreshed until
+ * setReady is called (because the first refresh is expensive).
+ */
+ ChordNameRuler(RulerScale *rulerScale,
+ RosegardenGUIDoc *doc,
+ std::vector<Segment *> &segments,
+ double xorigin = 0.0,
+ int height = 0,
+ QWidget* parent = 0,
+ const char *name = 0);
+
+ ~ChordNameRuler();
+
+ /// Indicate that the chord-name ruler should make itself ready and refresh
+ void setReady();
+
+ // may have one of these; can be changed at any time (to any in given composition):
+ void setCurrentSegment(Segment *segment);
+
+ // may have one of these (to avoid using percussion tracks in chords):
+ void setStudio(Studio *studio);
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ void setMinimumWidth(int width) { m_width = width; }
+
+public slots:
+ void slotScrollHoriz(int x);
+
+protected:
+ virtual void paintEvent(QPaintEvent *);
+
+private:
+ void recalculate(timeT from = 0,
+ timeT to = 0);
+
+ double m_xorigin;
+ int m_height;
+ int m_currentXOffset;
+ int m_width;
+ bool m_ready;
+
+ RulerScale *m_rulerScale;
+
+ Composition *m_composition;
+ unsigned int m_compositionRefreshStatusId;
+
+ typedef std::map<Segment *, int> SegmentRefreshMap;
+ SegmentRefreshMap m_segments; // map to refresh status id
+ bool m_regetSegmentsOnChange;
+
+ Segment *m_currentSegment;
+ Studio *m_studio;
+
+ Segment *m_chordSegment;
+
+ QFont m_font;
+ QFont m_boldFont;
+ QFontMetrics m_fontMetrics;
+
+ const PropertyName TEXT_FORMAL_X;
+ const PropertyName TEXT_ACTUAL_X;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/ControlChangeCommand.cpp b/src/gui/rulers/ControlChangeCommand.cpp
new file mode 100644
index 0000000..f6f5d0e
--- /dev/null
+++ b/src/gui/rulers/ControlChangeCommand.cpp
@@ -0,0 +1,50 @@
+/* -*- 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 <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 "ControlChangeCommand.h"
+#include "ControlItem.h"
+#include "misc/Debug.h"
+#include <klocale.h>
+
+namespace Rosegarden {
+
+ControlChangeCommand::ControlChangeCommand(QCanvasItemList selectedItems,
+ Segment &segment,
+ Rosegarden::timeT start, Rosegarden::timeT end)
+ : BasicCommand(i18n("Control Change"), segment, start, end, true),
+ m_selectedItems(selectedItems)
+{
+ RG_DEBUG << "ControlChangeCommand : from " << start << " to " << end << endl;
+}
+
+
+void ControlChangeCommand::modifySegment()
+{
+ for (QCanvasItemList::Iterator it=m_selectedItems.begin(); it!=m_selectedItems.end(); ++it) {
+ if (ControlItem *item = dynamic_cast<ControlItem*>(*it))
+ item->updateValue();
+ }
+}
+
+}
diff --git a/src/gui/rulers/ControlChangeCommand.h b/src/gui/rulers/ControlChangeCommand.h
new file mode 100644
index 0000000..cc334a4
--- /dev/null
+++ b/src/gui/rulers/ControlChangeCommand.h
@@ -0,0 +1,55 @@
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_CONTROLCHANGECOMMAND_H_
+#define _RG_CONTROLCHANGECOMMAND_H_
+
+#include "document/BasicCommand.h"
+#include <qcanvas.h>
+
+namespace Rosegarden {
+
+/**
+ * Command defining a change (property change or similar) from the control ruler
+ */
+class ControlChangeCommand : public BasicCommand
+{
+public:
+
+ ControlChangeCommand(QCanvasItemList selectedItems,
+ Segment &segment,
+ Rosegarden::timeT start, Rosegarden::timeT end);
+ virtual ~ControlChangeCommand() {;}
+
+
+protected:
+
+ virtual void modifySegment();
+
+ QCanvasItemList m_selectedItems;
+};
+
+}
+
+#endif /*CONTROLCHANGECOMMAND_H_*/
diff --git a/src/gui/rulers/ControlItem.cpp b/src/gui/rulers/ControlItem.cpp
new file mode 100644
index 0000000..623fbf3
--- /dev/null
+++ b/src/gui/rulers/ControlItem.cpp
@@ -0,0 +1,195 @@
+/* -*- 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 <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 "ControlItem.h"
+#include "ControlRuler.h"
+#include "ElementAdapter.h"
+#include "misc/Debug.h"
+
+namespace Rosegarden {
+
+const unsigned int ControlItem::BorderThickness = 1;
+const unsigned int ControlItem::DefaultWidth = 20;
+static int _canvasItemZ = 30;
+
+ControlItem::ControlItem(ControlRuler* ruler, ElementAdapter* elementAdapter,
+ int xx, int width)
+ : QCanvasRectangle(ruler->canvas()),
+ m_value(0),
+ m_controlRuler(ruler),
+ m_elementAdapter(elementAdapter)
+{
+ if (width < DefaultWidth/4) {
+ width = DefaultWidth/4; // avoid invisible zero-duration items
+ }
+ setWidth(width);
+ setPen(QPen(Qt::black, BorderThickness));
+ setBrush(Qt::blue);
+
+ setX(xx);
+ setY(canvas()->height());
+ setZ(_canvasItemZ++); // we should make this work against controlruler
+
+ updateFromValue();
+ setEnabled(false);
+ //RG_DEBUG << "ControlItem x = " << x() << " - y = " << y() << " - width = " << width << endl;
+ show();
+}
+
+ControlItem::~ControlItem()
+{
+ delete m_elementAdapter;
+}
+
+
+void ControlItem::setValue(long v)
+{
+// RG_DEBUG << "ControlItem::setValue(" << v << ") x = " << x() << endl;
+
+ m_value = v;
+}
+
+void ControlItem::updateValue()
+{
+ m_elementAdapter->setValue(m_value);
+}
+
+void ControlItem::updateFromValue()
+{
+// RG_DEBUG << "ControlItem::updateFromValue() : " << this << endl;
+
+ if (m_elementAdapter->getValue(m_value)) {
+// RG_DEBUG << "ControlItem::updateFromValue() : value = " << m_value << endl;
+ setHeight(m_controlRuler->valueToHeight(m_value));
+ }
+}
+
+typedef std::pair<int, QCanvasItem*> ItemPair;
+struct ItemCmp
+{
+ bool operator()(const ItemPair &i1, const ItemPair &i2)
+ {
+ return i1.first > i2.first;
+ }
+};
+
+void ControlItem::draw(QPainter &painter)
+{
+ if (!isEnabled())
+ updateFromValue();
+
+ setBrush(m_controlRuler->valueToColour(m_controlRuler->getMaxItemValue(), m_value));
+
+ QCanvasRectangle::draw(painter);
+
+
+ /*
+
+ // Attempt to get overlapping rectangles ordered automatically -
+ // probably best not to do this here - rwb
+
+ // calculate collisions and assign Z values accordingly
+ //
+ QCanvasItemList l = collisions(false);
+
+ std::vector<ItemPair> sortList;
+
+ for (QCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it) {
+
+ // skip all but rectangles
+ if ((*it)->rtti() != QCanvasItem::Rtti_Rectangle) continue;
+
+ if (QCanvasRectangle *rect = dynamic_cast<QCanvasRectangle*>(*it))
+ sortList.push_back(ItemPair(rect->height(), *it));
+ }
+
+ // sort the list by height
+ std::sort(sortList.begin(), sortList.end(), ItemCmp());
+
+ int z = 20;
+
+ for (std::vector<ItemPair>::iterator it = sortList.begin();
+ it != sortList.end(); ++it)
+ {
+ RG_DEBUG << "HEIGHT = " << (*it).first << endl;
+ (*it).second->setZ(z++);
+ }
+
+ RG_DEBUG << endl << endl;
+
+ canvas()->update();
+
+ */
+
+}
+
+void ControlItem::handleMouseButtonPress(QMouseEvent*)
+{
+// RG_DEBUG << "ControlItem::handleMouseButtonPress()" << this << endl;
+ setEnabled(true);
+}
+
+void ControlItem::handleMouseButtonRelease(QMouseEvent*)
+{
+// RG_DEBUG << "ControlItem::handleMouseButtonRelease()" << this << endl;
+ setEnabled(false);
+}
+
+void ControlItem::handleMouseMove(QMouseEvent*, int /*deltaX*/, int deltaY)
+{
+// RG_DEBUG << "ControlItem::handleMouseMove()" << this << endl;
+
+ // height is always negative
+ //
+
+ m_controlRuler->applyTool(x(), deltaY);
+
+ int absNewHeight = -(getHeight() + deltaY);
+
+ // Make sure height is within bounds
+ if (absNewHeight > ControlRuler::MaxItemHeight)
+ absNewHeight = ControlRuler::MaxItemHeight;
+ else if (absNewHeight < ControlRuler::MinItemHeight)
+ absNewHeight = ControlRuler::MinItemHeight;
+
+ setHeight(-absNewHeight);
+ setValue(m_controlRuler->heightToValue(getHeight()));
+}
+
+void ControlItem::handleMouseWheel(QWheelEvent *)
+{
+// RG_DEBUG << "ControlItem::handleMouseWheel - got wheel event" << endl;
+}
+
+void ControlItem::setSelected(bool s)
+{
+ QCanvasItem::setSelected(s);
+
+ if (s) setPen(QPen(Qt::red, BorderThickness));
+ else setPen(QPen(Qt::black, BorderThickness));
+
+ canvas()->update();
+}
+
+}
diff --git a/src/gui/rulers/ControlItem.h b/src/gui/rulers/ControlItem.h
new file mode 100644
index 0000000..44f9e22
--- /dev/null
+++ b/src/gui/rulers/ControlItem.h
@@ -0,0 +1,79 @@
+/* -*- 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 <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 <qcanvas.h>
+
+namespace Rosegarden {
+
+class ControlRuler;
+class ElementAdapter;
+
+class ControlItem : public QCanvasRectangle
+{
+public:
+ ControlItem(ControlRuler* controlRuler,
+ ElementAdapter* adapter,
+ int x, int width = DefaultWidth);
+
+ ~ControlItem();
+
+ virtual void setValue(long);
+ int getValue() const { return m_value; }
+
+ void setWidth(int w) { setSize(w, height()); }
+ void setHeight(int h) { setSize(width(), h); }
+ int getHeight() { return size().height(); }
+
+ virtual void draw(QPainter &painter);
+
+ virtual void handleMouseButtonPress(QMouseEvent *e);
+ virtual void handleMouseButtonRelease(QMouseEvent *e);
+ virtual void handleMouseMove(QMouseEvent *e, int deltaX, int deltaY);
+ virtual void handleMouseWheel(QWheelEvent *e);
+
+ virtual void setSelected(bool yes);
+
+ /// recompute height according to represented value prior to a canvas repaint
+ virtual void updateFromValue();
+
+ /// update value according to height after a user edit
+ virtual void updateValue();
+
+ ElementAdapter* getElementAdapter() { return m_elementAdapter; }
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ long m_value;
+ bool m_handlingMouseMove;
+
+ ControlRuler* m_controlRuler;
+ ElementAdapter* m_elementAdapter;
+
+ static const unsigned int BorderThickness;
+ static const unsigned int DefaultWidth;
+};
+
+}
diff --git a/src/gui/rulers/ControlRuler.cpp b/src/gui/rulers/ControlRuler.cpp
new file mode 100644
index 0000000..12064f5
--- /dev/null
+++ b/src/gui/rulers/ControlRuler.cpp
@@ -0,0 +1,539 @@
+/* -*- 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 <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 <kmainwindow.h>
+#include <qcanvas.h>
+#include <qcolor.h>
+#include <qcursor.h>
+#include <qpoint.h>
+#include <qpopupmenu.h>
+#include <qscrollbar.h>
+#include <qscrollview.h>
+#include <qstring.h>
+#include <qwidget.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,
+ QCanvas* c, QWidget* 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 QCanvasRectangle(canvas())),
+ m_menu(0)
+{
+ setHScrollBarMode(QScrollView::AlwaysOff);
+
+ m_selectionRect->setPen(Qt::red);
+
+ setFixedHeight(sizeHint().height());
+
+ connect(this, SIGNAL(stateChange(const QString&, bool)),
+ m_parentEditView, SLOT(slotStateChanged(const QString&, 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();
+
+ QCanvasItemList list = canvas()->allItems();
+
+ QCanvasItemList::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(QMouseEvent* e)
+{
+ if (e->button() != Qt::LeftButton) {
+ m_numberFloat->hide();
+ m_selecting = false;
+ return ;
+ }
+
+ RG_DEBUG << "ControlRuler::contentsMousePressEvent()\n";
+
+ QPoint p = inverseMapPoint(e->pos());
+
+ QCanvasItemList 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() && QMouseEvent::ControlButton)) {
+ clearSelectedItems();
+ }
+
+ ControlItem *topItem = 0;
+ for (QCanvasItemList::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 (QCanvasItemList::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() && QMouseEvent::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(QMouseEvent* e)
+{
+ if (e->button() != Qt::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 (QCanvasItemList::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(QMouseEvent* e)
+{
+ QPoint 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
+ //
+ QPoint totalPos = mapTo(topLevelWidget(), QPoint(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 (QCanvasItemList::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(QString("%1").arg(value));
+ }
+ }
+ }
+ canvas()->update();
+
+ m_numberFloat->show();
+
+}
+
+void
+ControlRuler::contentsWheelEvent(QWheelEvent *e)
+{
+ // not sure what to do yet
+ QCanvasView::contentsWheelEvent(e);
+}
+
+void ControlRuler::updateSelection()
+{
+ clearSelectedItems();
+
+ bool haveSelectedItems = false;
+
+ QCanvasItemList l = getSelectionRectangle()->collisions(true);
+
+ for (QCanvasItemList::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(QContextMenuEvent* 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(QCursor::pos());
+ } else
+ RG_DEBUG << "ControlRuler::showMenu() : no menu to show\n";
+
+}
+
+void ControlRuler::createMenu()
+{
+ RG_DEBUG << "ControlRuler::createMenu()\n";
+
+ KMainWindow* parentMainWindow = dynamic_cast<KMainWindow*>(topLevelWidget());
+
+ if (parentMainWindow && parentMainWindow->factory()) {
+ m_menu = static_cast<QPopupMenu*>(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 (QCanvasItemList::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()
+{
+ QCanvasItemList allItems = canvas()->allItems();
+
+ for (QCanvasItemList::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;
+}
+
+QColor 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();
+
+ QCanvasItemList l = canvas()->allItems();
+ for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) {
+
+ // skip all but rectangles
+ if ((*it)->rtti() != QCanvasItem::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();
+
+ QCanvasItemList l = canvas()->allItems();
+ for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) {
+
+ // skip all but rectangles
+ if ((*it)->rtti() != QCanvasItem::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()
+{
+ QCanvasItemList l = canvas()->allItems();
+ std::vector<int> zList;
+ for (QCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it) {
+
+ // skip all but rectangles
+ if ((*it)->rtti() != QCanvasItem::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]);
+}
+
+QScrollBar* ControlRuler::getMainHorizontalScrollBar()
+{
+ return m_mainHorizontalScrollBar ? m_mainHorizontalScrollBar : horizontalScrollBar();
+}
+
+}
+#include "ControlRuler.moc"
diff --git a/src/gui/rulers/ControlRuler.h b/src/gui/rulers/ControlRuler.h
new file mode 100644
index 0000000..ac6eba4
--- /dev/null
+++ b/src/gui/rulers/ControlRuler.h
@@ -0,0 +1,182 @@
+
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_CONTROLRULER_H_
+#define _RG_CONTROLRULER_H_
+
+#include "base/Segment.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include <qcolor.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include <utility>
+
+
+class QWidget;
+class QWheelEvent;
+class QScrollBar;
+class QPopupMenu;
+class QMouseEvent;
+class QContextMenuEvent;
+class QCanvasRectangle;
+class QCanvas;
+
+
+namespace Rosegarden
+{
+
+class ControlTool;
+class ControlSelector;
+class ControlItem;
+class TextFloat;
+class Segment;
+class RulerScale;
+class EventSelection;
+class EditViewBase;
+
+
+/**
+ * ControlRuler : base class for Control Rulers
+ */
+class ControlRuler : public RosegardenCanvasView, public SegmentObserver
+{
+ Q_OBJECT
+
+ friend class ControlItem;
+
+public:
+ ControlRuler(Segment*,
+ RulerScale*,
+ EditViewBase* parentView,
+ QCanvas*,
+ QWidget* parent=0, const char* name=0, WFlags f=0);
+ virtual ~ControlRuler();
+
+ virtual QString getName() = 0;
+
+ int getMaxItemValue() { return m_maxItemValue; }
+ void setMaxItemValue(int val) { m_maxItemValue = val; }
+
+ void clear();
+
+ void setControlTool(ControlTool*);
+
+ int applyTool(double x, int val);
+
+ QCanvasRectangle* getSelectionRectangle() { return m_selectionRect; }
+
+ RulerScale* getRulerScale() { return m_rulerScale; }
+
+ // SegmentObserver interface
+ virtual void segmentDeleted(const Segment *);
+
+ static const int DefaultRulerHeight;
+ static const int MinItemHeight;
+ static const int MaxItemHeight;
+ static const int ItemHeightRange;
+
+ void flipForwards();
+ void flipBackwards();
+
+ void setMainHorizontalScrollBar(QScrollBar* s ) { m_mainHorizontalScrollBar = s; }
+
+signals:
+ void stateChange(const QString&, bool);
+
+public slots:
+ /// override RosegardenCanvasView - we don't want to change the main hscrollbar
+ virtual void slotUpdate();
+ virtual void slotUpdateElementsHPos();
+
+protected:
+ virtual void contentsMousePressEvent(QMouseEvent*);
+ virtual void contentsMouseReleaseEvent(QMouseEvent*);
+ virtual void contentsMouseMoveEvent(QMouseEvent*);
+ virtual void contentsWheelEvent(QWheelEvent*);
+ virtual void contentsContextMenuEvent(QContextMenuEvent*);
+
+ virtual QScrollBar* getMainHorizontalScrollBar();
+
+ virtual void computeStaffOffset() {};
+
+ virtual void layoutItem(ControlItem*);
+
+
+
+ // Stacking of the SegmentItems on the canvas
+ //
+ std::pair<int, int> getZMinMax();
+
+ virtual void init() = 0;
+ virtual void drawBackground() = 0;
+
+ int valueToHeight(long val);
+ long heightToValue(int height);
+ QColor valueToColour(int max, int val);
+
+ void clearSelectedItems();
+ void updateSelection();
+
+ void setMenuName(QString menuName) { m_menuName = menuName; }
+ void createMenu();
+
+ //--------------- Data members ---------------------------------
+
+ EditViewBase* m_parentEditView;
+ QScrollBar* m_mainHorizontalScrollBar;
+ RulerScale* m_rulerScale;
+ EventSelection* m_eventSelection;
+ Segment* m_segment;
+
+ ControlItem* m_currentItem;
+ QCanvasItemList m_selectedItems;
+
+ ControlTool *m_tool;
+
+ int m_maxItemValue;
+
+ double m_staffOffset;
+
+ double m_currentX;
+
+ QPoint m_lastEventPos;
+ bool m_itemMoved;
+
+ bool m_selecting;
+ ControlSelector* m_selector;
+ QCanvasRectangle* m_selectionRect;
+
+ QString m_menuName;
+ QPopupMenu* m_menu;
+
+ TextFloat *m_numberFloat;
+
+ bool m_hposUpdatePending;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/ControlRulerEventEraseCommand.cpp b/src/gui/rulers/ControlRulerEventEraseCommand.cpp
new file mode 100644
index 0000000..7a1e493
--- /dev/null
+++ b/src/gui/rulers/ControlRulerEventEraseCommand.cpp
@@ -0,0 +1,58 @@
+/* -*- 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 <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 "ControlRulerEventEraseCommand.h"
+#include "ControlItem.h"
+#include "ElementAdapter.h"
+#include "misc/Debug.h"
+#include <klocale.h>
+
+namespace Rosegarden
+{
+
+ControlRulerEventEraseCommand::ControlRulerEventEraseCommand(QCanvasItemList selectedItems,
+ Segment &segment,
+ Rosegarden::timeT start, Rosegarden::timeT end)
+ : BasicCommand(i18n("Erase Controller Event(s)"),
+ segment,
+ start,
+ (start == end) ? start + 10 : end,
+ true),
+ m_selectedItems(selectedItems)
+{
+ RG_DEBUG << "ControllerEventEraseCommand : from " << start << " to " << end << endl;
+}
+
+
+void ControlRulerEventEraseCommand::modifySegment()
+{
+ Segment &segment(getSegment());
+
+ for (QCanvasItemList::Iterator it=m_selectedItems.begin(); it!=m_selectedItems.end(); ++it) {
+ if (ControlItem *item = dynamic_cast<ControlItem*>(*it))
+ segment.eraseSingle(item->getElementAdapter()->getEvent());
+ }
+}
+
+}
diff --git a/src/gui/rulers/ControlRulerEventEraseCommand.h b/src/gui/rulers/ControlRulerEventEraseCommand.h
new file mode 100644
index 0000000..15e213b
--- /dev/null
+++ b/src/gui/rulers/ControlRulerEventEraseCommand.h
@@ -0,0 +1,54 @@
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_CONTROLLEREVENTERASECOMMAND_H_
+#define _RG_CONTROLLEREVENTERASECOMMAND_H_
+
+#include "base/Event.h"
+#include "document/BasicCommand.h"
+#include <qcanvas.h>
+
+namespace Rosegarden
+{
+
+class ControlRulerEventEraseCommand : public BasicCommand
+{
+public:
+
+ ControlRulerEventEraseCommand(QCanvasItemList selectedItems,
+ Segment &segment,
+ Rosegarden::timeT start, Rosegarden::timeT end);
+ virtual ~ControlRulerEventEraseCommand() {;}
+
+
+protected:
+
+ virtual void modifySegment();
+
+ QCanvasItemList m_selectedItems;
+};
+
+}
+
+#endif /*CONTROLLEREVENTERASECOMMAND_H_*/
diff --git a/src/gui/rulers/ControlRulerEventInsertCommand.cpp b/src/gui/rulers/ControlRulerEventInsertCommand.cpp
new file mode 100644
index 0000000..ab6a73c
--- /dev/null
+++ b/src/gui/rulers/ControlRulerEventInsertCommand.cpp
@@ -0,0 +1,67 @@
+/* -*- 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 <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 "ControlRulerEventInsertCommand.h"
+#include "base/MidiTypes.h"
+#include <klocale.h>
+
+namespace Rosegarden
+{
+
+ControlRulerEventInsertCommand::ControlRulerEventInsertCommand(const std::string &type,
+ timeT insertTime,
+ long number, long initialValue,
+ Segment &segment)
+ : BasicCommand(i18n("Insert Controller Event"),
+ segment,
+ insertTime,
+ (insertTime + Rosegarden::Note(Rosegarden::Note::Quaver).getDuration())), // must have a duration other undo doesn't work
+ m_type(type),
+ m_number(number),
+ m_initialValue(initialValue)
+{
+}
+
+void ControlRulerEventInsertCommand::modifySegment()
+{
+ Event* controllerEvent = new Event(m_type, getStartTime());
+
+ if (m_type == Rosegarden::Controller::EventType)
+ {
+ controllerEvent->set<Rosegarden::Int>(Rosegarden::Controller::VALUE, m_initialValue);
+ controllerEvent->set<Rosegarden::Int>(Rosegarden::Controller::NUMBER, m_number);
+ }
+ else if (m_type == Rosegarden::PitchBend::EventType)
+ {
+ // Convert to PitchBend MSB/LSB
+ int lsb = m_initialValue & 0x7f;
+ int msb = (m_initialValue >> 7) & 0x7f;
+ controllerEvent->set<Rosegarden::Int>(Rosegarden::PitchBend::MSB, msb);
+ controllerEvent->set<Rosegarden::Int>(Rosegarden::PitchBend::LSB, lsb);
+ }
+
+ getSegment().insert(controllerEvent);
+}
+
+}
diff --git a/src/gui/rulers/ControlRulerEventInsertCommand.h b/src/gui/rulers/ControlRulerEventInsertCommand.h
new file mode 100644
index 0000000..a180006
--- /dev/null
+++ b/src/gui/rulers/ControlRulerEventInsertCommand.h
@@ -0,0 +1,56 @@
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_CONTROLRULEREVENTINSERTCOMMAND_H_
+#define _RG_CONTROLRULEREVENTINSERTCOMMAND_H_
+
+#include "document/BasicCommand.h"
+#include <string>
+
+namespace Rosegarden
+{
+
+class ControlRulerEventInsertCommand : public BasicCommand
+{
+public:
+ ControlRulerEventInsertCommand(const std::string &type,
+ timeT insertTime,
+ long number,
+ long initialValue,
+ Segment &segment);
+
+ virtual ~ControlRulerEventInsertCommand() {;}
+
+protected:
+
+ virtual void modifySegment();
+
+ std::string m_type;
+ long m_number;
+ long m_initialValue;
+};
+
+}
+
+#endif /*CONTROLRULEREVENTINSERTCOMMAND_H_*/
diff --git a/src/gui/rulers/ControlSelector.cpp b/src/gui/rulers/ControlSelector.cpp
new file mode 100644
index 0000000..fb4abbb
--- /dev/null
+++ b/src/gui/rulers/ControlSelector.cpp
@@ -0,0 +1,72 @@
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_CONTROLSELECTOR_CPP_
+#define _RG_CONTROLSELECTOR_CPP_
+
+#include "ControlSelector.h"
+
+namespace Rosegarden {
+
+ControlSelector::ControlSelector(ControlRuler* parent)
+ : QObject(parent),
+ m_ruler(parent)
+{
+}
+
+void ControlSelector::handleMouseButtonPress(QMouseEvent *e)
+{
+ QPoint p = m_ruler->inverseMapPoint(e->pos());
+
+ getSelectionRectangle()->setX(p.x());
+ getSelectionRectangle()->setY(p.y());
+ getSelectionRectangle()->setSize(0,0);
+
+ getSelectionRectangle()->show();
+ m_ruler->canvas()->update();
+}
+
+void ControlSelector::handleMouseButtonRelease(QMouseEvent*)
+{
+ getSelectionRectangle()->hide();
+ m_ruler->canvas()->update();
+}
+
+void ControlSelector::handleMouseMove(QMouseEvent *e, int, int)
+{
+ QPoint p = m_ruler->inverseMapPoint(e->pos());
+
+ int w = int(p.x() - getSelectionRectangle()->x());
+ int h = int(p.y() - getSelectionRectangle()->y());
+ if (w > 0) ++w; else --w;
+ if (h > 0) ++h; else --h;
+
+ getSelectionRectangle()->setSize(w, h);
+
+ m_ruler->canvas()->update();
+}
+
+}
+
+#endif /*CONTROLSELECTOR_CPP_*/
diff --git a/src/gui/rulers/ControlSelector.h b/src/gui/rulers/ControlSelector.h
new file mode 100644
index 0000000..99ec773
--- /dev/null
+++ b/src/gui/rulers/ControlSelector.h
@@ -0,0 +1,60 @@
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_CONTROLSELECTOR_H_
+#define _RG_CONTROLSELECTOR_H_
+
+#include "ControlRuler.h"
+
+class QCanvasRectangle;
+
+namespace Rosegarden {
+
+
+/**
+ * Selector tool for the ControlRuler
+ *
+ * Allow the user to select several ControlItems so he can change them
+ * all at the same time
+ */
+class ControlSelector : public QObject
+{
+public:
+ ControlSelector(ControlRuler* parent);
+ virtual ~ControlSelector() {};
+
+ virtual void handleMouseButtonPress(QMouseEvent *e);
+ virtual void handleMouseButtonRelease(QMouseEvent *e);
+ virtual void handleMouseMove(QMouseEvent *e, int deltaX, int deltaY);
+
+ QCanvasRectangle* getSelectionRectangle() { return m_ruler->getSelectionRectangle(); }
+protected:
+ //--------------- Data members ---------------------------------
+
+ ControlRuler* m_ruler;
+};
+
+}
+
+#endif /*CONTROLSELECTOR_H_*/
diff --git a/src/gui/rulers/ControlTool.h b/src/gui/rulers/ControlTool.h
new file mode 100644
index 0000000..fcc528a
--- /dev/null
+++ b/src/gui/rulers/ControlTool.h
@@ -0,0 +1,39 @@
+/* -*- 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 <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.
+*/
+
+#ifndef CONTROLTOOL_H_
+#define CONTROLTOOL_H_
+
+namespace Rosegarden {
+
+class ControlTool
+{
+public:
+ virtual ~ControlTool() {};
+ virtual int operator()(double x, int val) = 0;
+};
+
+}
+
+#endif /*CONTROLTOOL_H_*/
diff --git a/src/gui/rulers/ControllerEventAdapter.cpp b/src/gui/rulers/ControllerEventAdapter.cpp
new file mode 100644
index 0000000..55abd50
--- /dev/null
+++ b/src/gui/rulers/ControllerEventAdapter.cpp
@@ -0,0 +1,83 @@
+/* -*- 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 <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 "ControllerEventAdapter.h"
+#include "base/MidiTypes.h"
+#include "misc/Debug.h"
+
+namespace Rosegarden {
+
+bool ControllerEventAdapter::getValue(long& val)
+{
+ if (m_event->getType() == Rosegarden::Controller::EventType)
+ {
+ return m_event->get<Rosegarden::Int>(Rosegarden::Controller::VALUE, val);
+ }
+ else if (m_event->getType() == Rosegarden::PitchBend::EventType)
+ {
+ long msb = 0, lsb = 0;
+ m_event->get<Rosegarden::Int>(Rosegarden::PitchBend::MSB, msb);
+ m_event->get<Rosegarden::Int>(Rosegarden::PitchBend::LSB, lsb);
+
+ long value = msb;
+ value <<= 7;
+ value |= lsb;
+
+ //RG_DEBUG << "PitchBend Get Value = " << value << endl;
+
+ val = value;
+ return true;
+ }
+
+ return false;
+}
+
+void ControllerEventAdapter::setValue(long val)
+{
+ if (m_event->getType() == Rosegarden::Controller::EventType)
+ {
+ m_event->set<Rosegarden::Int>(Rosegarden::Controller::VALUE, val);
+ }
+ else if (m_event->getType() == Rosegarden::PitchBend::EventType)
+ {
+ RG_DEBUG << "PitchBend Set Value = " << val << endl;
+
+ int lsb = val & 0x7f;
+ int msb = (val >> 7) & 0x7f;
+ m_event->set<Rosegarden::Int>(Rosegarden::PitchBend::MSB, msb);
+ m_event->set<Rosegarden::Int>(Rosegarden::PitchBend::LSB, lsb);
+ }
+}
+
+timeT ControllerEventAdapter::getTime()
+{
+ return m_event->getAbsoluteTime();
+}
+
+timeT ControllerEventAdapter::getDuration()
+{
+ return m_event->getDuration();
+}
+
+}
diff --git a/src/gui/rulers/ControllerEventAdapter.h b/src/gui/rulers/ControllerEventAdapter.h
new file mode 100644
index 0000000..4f71884
--- /dev/null
+++ b/src/gui/rulers/ControllerEventAdapter.h
@@ -0,0 +1,53 @@
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_CONTROLLEREVENTADAPTER_H_
+#define _RG_CONTROLLEREVENTADAPTER_H_
+
+#include "ElementAdapter.h"
+
+namespace Rosegarden {
+
+class ControllerEventAdapter : public Rosegarden::ElementAdapter
+{
+public:
+ ControllerEventAdapter(Event* e) : m_event(e) {}
+
+ virtual bool getValue(long&);
+ virtual void setValue(long);
+ virtual timeT getTime();
+ virtual timeT getDuration();
+
+ virtual Event* getEvent() { return m_event; }
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ Event* m_event;
+};
+
+}
+
+#endif /*CONTROLLEREVENTADAPTER_H_*/
diff --git a/src/gui/rulers/ControllerEventsRuler.cpp b/src/gui/rulers/ControllerEventsRuler.cpp
new file mode 100644
index 0000000..265a701
--- /dev/null
+++ b/src/gui/rulers/ControllerEventsRuler.cpp
@@ -0,0 +1,499 @@
+/* -*- 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 <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 "ControllerEventsRuler.h"
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/ControlParameter.h"
+#include "base/Event.h"
+#include "base/MidiTypes.h"
+#include "base/NotationTypes.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "commands/edit/EraseCommand.h"
+#include "ControlRuler.h"
+#include "ControlItem.h"
+#include "ControllerEventAdapter.h"
+#include "ControlRulerEventInsertCommand.h"
+#include "ControlRulerEventEraseCommand.h"
+#include "gui/general/EditViewBase.h"
+#include "gui/widgets/TextFloat.h"
+#include <klineeditdlg.h>
+#include <qcanvas.h>
+#include <qcolor.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include <qvalidator.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+ControllerEventsRuler::ControllerEventsRuler(Segment *segment,
+ RulerScale* rulerScale,
+ EditViewBase* parentView,
+ QCanvas* c,
+ QWidget* parent,
+ const ControlParameter *controller,
+ const char* name, WFlags f)
+ : ControlRuler(segment, rulerScale, parentView, c, parent, name, f),
+ m_defaultItemWidth(20),
+ m_controlLine(new QCanvasLine(canvas())),
+ m_controlLineShowing(false),
+ m_controlLineX(0),
+ m_controlLineY(0)
+{
+ // Make a copy of the ControlParameter if we have one
+ //
+ if (controller)
+ m_controller = new ControlParameter(*controller);
+ else
+ m_controller = 0;
+
+ setMenuName("controller_events_ruler_menu");
+ drawBackground();
+ init();
+}
+
+void
+ControllerEventsRuler::setSegment(Segment *segment)
+{
+ RG_DEBUG << "ControllerEventsRuler::setSegment(" << segment << ")" << endl;
+
+ m_segment->removeObserver(this);
+ m_segment = segment;
+ m_segment->addObserver(this);
+
+ while (child(NULL))
+ delete (child(NULL));
+
+ drawBackground();
+ init();
+}
+
+void
+ControllerEventsRuler::init()
+{
+ // Reset range information for this controller type (for the moment
+ // this assumes min is always 0.
+ //
+ setMaxItemValue(m_controller->getMax());
+
+ for (Segment::iterator i = m_segment->begin();
+ i != m_segment->end(); ++i) {
+
+ // skip if not the same type of event that we're expecting
+ //
+ if (m_controller->getType() != (*i)->getType())
+ continue;
+
+ int width = getDefaultItemWidth();
+
+ // Check for specific controller value if we need to
+ //
+ if (m_controller->getType() == Controller::EventType) {
+ try {
+ if ((*i)->get
+ <Int>(Controller::NUMBER)
+ != m_controller->getControllerValue())
+ continue;
+ } catch (...) {
+ continue;
+ }
+ } else if (m_controller->getType() == PitchBend::EventType)
+ width /= 4;
+
+ //RG_DEBUG << "ControllerEventsRuler: adding element\n";
+
+ double x = m_rulerScale->getXForTime((*i)->getAbsoluteTime());
+ new ControlItem(this, new ControllerEventAdapter(*i),
+ int(x + m_staffOffset), width);
+ }
+}
+
+void
+ControllerEventsRuler::drawBackground()
+{
+ // Draw some minimum and maximum controller value guide lines
+ //
+ QCanvasLine *topLine = new QCanvasLine(canvas());
+ QCanvasLine *topQLine = new QCanvasLine(canvas());
+ QCanvasLine *midLine = new QCanvasLine(canvas());
+ QCanvasLine *botQLine = new QCanvasLine(canvas());
+ QCanvasLine *bottomLine = new QCanvasLine(canvas());
+ //m_controlLine->setPoints(m_controlLineX, m_controlLineY, m_controlLineX, m_controlLineY);
+ int cHeight = canvas()->height();
+ int cWidth = canvas()->width();
+
+ topLine->setPen(QColor(127, 127, 127));
+ topLine->setPoints(0, 0, cWidth, 0);
+ topLine->setZ( -10);
+ topLine->show();
+
+ topQLine->setPen(QColor(192, 192, 192));
+ topQLine->setPoints(0, cHeight / 4, cWidth, cHeight / 4);
+ topQLine->setZ( -10);
+ topQLine->show();
+
+ midLine->setPen(QColor(127, 127, 127));
+ midLine->setPoints(0, cHeight / 2, cWidth, cHeight / 2);
+ midLine->setZ( -10);
+ midLine->show();
+
+ botQLine->setPen(QColor(192, 192, 192));
+ botQLine->setPoints(0, 3*cHeight / 4, cWidth, 3*cHeight / 4);
+ botQLine->setZ( -10);
+ botQLine->show();
+
+ bottomLine->setPen(QColor(127, 127, 127));
+ bottomLine->setPoints(0, cHeight - 1, cWidth, cHeight - 1);
+ bottomLine->setZ( -10);
+ bottomLine->show();
+
+ canvas()->update();
+}
+
+ControllerEventsRuler::~ControllerEventsRuler()
+{}
+
+QString ControllerEventsRuler::getName()
+{
+ if (m_controller) {
+ QString name = i18n("Unsupported Event Type");
+
+ if (m_controller->getType() == Controller::EventType) {
+ QString hexValue;
+ hexValue.sprintf("0x%x", m_controller->getControllerValue());
+
+ name = QString("%1 (%2 / %3)").arg(strtoqstr(m_controller->getName()))
+ .arg(int(m_controller->getControllerValue()))
+ .arg(hexValue);
+ } else if (m_controller->getType() == PitchBend::EventType) {
+ name = i18n("Pitch Bend");
+ }
+
+ return name;
+ } else
+ return i18n("Controller Events");
+}
+
+void ControllerEventsRuler::eventAdded(const Segment*, Event *e)
+{
+ if (e->getType() != m_controller->getType())
+ return ;
+
+ // Check for specific controller value if we need to
+ //
+ if (e->getType() == Controller::EventType) {
+ try {
+ if (e->get
+ <Int>(Controller::NUMBER) !=
+ m_controller->getControllerValue())
+ return ;
+ } catch (...) {
+ return ;
+ }
+ }
+
+ RG_DEBUG << "ControllerEventsRuler::elementAdded()\n";
+
+ double x = m_rulerScale->getXForTime(e->getAbsoluteTime());
+
+ int width = getDefaultItemWidth();
+
+ if (m_controller->getType() == PitchBend::EventType)
+ width /= 4;
+
+ new ControlItem(this, new ControllerEventAdapter(e), int(x + m_staffOffset), width);
+}
+
+void ControllerEventsRuler::eventRemoved(const Segment*, Event *e)
+{
+ if (e->getType() != m_controller->getType())
+ return ;
+
+ clearSelectedItems();
+
+ QCanvasItemList allItems = canvas()->allItems();
+
+ for (QCanvasItemList::Iterator it = allItems.begin(); it != allItems.end(); ++it) {
+ if (ControlItem *item = dynamic_cast<ControlItem*>(*it)) {
+ ControllerEventAdapter * adapter = dynamic_cast<ControllerEventAdapter*>(item->getElementAdapter());
+ if (adapter->getEvent() == e) {
+ delete item;
+ break;
+ }
+ }
+ }
+}
+
+void ControllerEventsRuler::insertControllerEvent()
+{
+ timeT insertTime = m_rulerScale->getTimeForX(m_lastEventPos.x());
+
+
+ // compute initial value from cursor height
+ //
+ long initialValue = heightToValue(m_lastEventPos.y() - canvas()->height());
+
+ RG_DEBUG << "ControllerEventsRuler::insertControllerEvent() : inserting event at "
+ << insertTime
+ << " - initial value = " << initialValue
+ << endl;
+
+ // ask controller number to user
+ long number = 0;
+
+ if (m_controller) {
+ number = m_controller->getControllerValue();
+ } else {
+ bool ok = false;
+ QIntValidator intValidator(0, 128, this);
+ QString res = KLineEditDlg::getText(i18n("Controller Event Number"), "0",
+ &ok, this, &intValidator);
+ if (ok)
+ number = res.toULong();
+ }
+
+ ControlRulerEventInsertCommand* command =
+ new ControlRulerEventInsertCommand(m_controller->getType(),
+ insertTime, number,
+ initialValue, *m_segment);
+
+ m_parentEditView->addCommandToHistory(command);
+}
+
+void ControllerEventsRuler::eraseControllerEvent()
+{
+ RG_DEBUG << "ControllerEventsRuler::eraseControllerEvent() : deleting selected events\n";
+
+ ControlRulerEventEraseCommand* command =
+ new ControlRulerEventEraseCommand(m_selectedItems,
+ *m_segment,
+ m_eventSelection->getStartTime(),
+ m_eventSelection->getEndTime());
+ m_parentEditView->addCommandToHistory(command);
+ updateSelection();
+}
+
+void ControllerEventsRuler::clearControllerEvents()
+{
+ EventSelection *es = new EventSelection(*m_segment);
+
+ for (Segment::iterator it = m_segment->begin(); it != m_segment->end(); ++it) {
+ if (!(*it)->isa(Controller::EventType))
+ continue;
+ {
+ if (m_controller) // ensure we have only the controller events we want for this ruler
+ {
+ try
+ {
+ if ((*it)->get
+ <Int>(Controller::NUMBER)
+ != m_controller->getControllerValue())
+ continue;
+ } catch (...)
+ {
+ continue;
+ }
+
+ es->addEvent(*it);
+ }
+ }
+ }
+
+ EraseCommand *command = new EraseCommand(*es);
+ m_parentEditView->addCommandToHistory(command);
+
+}
+
+void ControllerEventsRuler::startControlLine()
+{
+ m_controlLineShowing = true;
+ this->setCursor(Qt::pointingHandCursor);
+}
+
+void ControllerEventsRuler::contentsMousePressEvent(QMouseEvent *e)
+{
+ if (!m_controlLineShowing) {
+ if (e->button() == MidButton)
+ m_lastEventPos = inverseMapPoint(e->pos());
+
+ ControlRuler::contentsMousePressEvent(e); // send super
+
+ return ;
+ }
+
+ // cancel control line mode
+ if (e->button() == RightButton) {
+ m_controlLineShowing = false;
+ m_controlLine->hide();
+
+ this->setCursor(Qt::arrowCursor);
+ return ;
+ }
+
+ if (e->button() == LeftButton) {
+ QPoint p = inverseMapPoint(e->pos());
+
+ m_controlLine->show();
+ m_controlLineX = p.x();
+ m_controlLineY = p.y();
+ m_controlLine->setPoints(m_controlLineX, m_controlLineY, m_controlLineX, m_controlLineY);
+ canvas()->update();
+ }
+}
+
+void ControllerEventsRuler::contentsMouseReleaseEvent(QMouseEvent *e)
+{
+ if (!m_controlLineShowing) {
+ if (e->button() == MidButton)
+ insertControllerEvent();
+
+ ControlRuler::contentsMouseReleaseEvent(e); // send super
+
+ return ;
+ } else {
+ QPoint p = inverseMapPoint(e->pos());
+
+ timeT startTime = m_rulerScale->getTimeForX(m_controlLineX);
+ timeT endTime = m_rulerScale->getTimeForX(p.x());
+
+ long startValue = heightToValue(m_controlLineY - canvas()->height());
+ long endValue = heightToValue(p.y() - canvas()->height());
+
+ RG_DEBUG << "ControllerEventsRuler::contentsMouseReleaseEvent - "
+ << "starttime = " << startTime
+ << ", endtime = " << endTime
+ << ", startValue = " << startValue
+ << ", endValue = " << endValue
+ << endl;
+
+ drawControlLine(startTime, endTime, startValue, endValue);
+
+ m_controlLineShowing = false;
+ m_controlLine->hide();
+ this->setCursor(Qt::arrowCursor);
+ canvas()->update();
+ }
+}
+
+void ControllerEventsRuler::contentsMouseMoveEvent(QMouseEvent *e)
+{
+ if (!m_controlLineShowing) {
+ // Don't send super if we're using the middle button
+ //
+ if (e->button() == MidButton) {
+ m_lastEventPos = inverseMapPoint(e->pos());
+ return ;
+ }
+
+ ControlRuler::contentsMouseMoveEvent(e); // send super
+ return ;
+ }
+
+ QPoint p = inverseMapPoint(e->pos());
+
+ m_controlLine->setPoints(m_controlLineX, m_controlLineY, p.x(), p.y());
+ canvas()->update();
+
+}
+
+void ControllerEventsRuler::layoutItem(ControlItem* item)
+{
+ timeT itemTime = item->getElementAdapter()->getTime();
+
+ double x = m_rulerScale->getXForTime(itemTime) + m_staffOffset;
+
+ item->setX(x);
+
+ int width = getDefaultItemWidth(); // TODO: how to scale that ??
+
+ if (m_controller->getType() == PitchBend::EventType)
+ width /= 4;
+
+ item->setWidth(width);
+
+ //RG_DEBUG << "ControllerEventsRuler::layoutItem ControlItem x = " << x
+ //<< " - width = " << width << endl;
+}
+
+void
+ControllerEventsRuler::drawControlLine(timeT startTime,
+ timeT endTime,
+ int startValue,
+ int endValue)
+{
+ if (m_controller == 0)
+ return ;
+ if (startTime > endTime) {
+ std::swap(startTime, endTime);
+ std::swap(startValue, endValue);
+ }
+
+ timeT quantDur = Note(Note::Quaver).getDuration();
+
+ // If inserting a line of PitchBends then we want a smoother curve
+ //
+ if (m_controller->getType() == PitchBend::EventType)
+ quantDur = Note(Note::Demisemiquaver).getDuration();
+
+ // for the moment enter a quantized set of events
+ timeT time = startTime, newTime = 0;
+ double step = double(endValue - startValue) / double(endTime - startTime);
+
+ KMacroCommand *macro = new KMacroCommand(i18n("Add line of controllers"));
+
+ while (time < endTime) {
+ int value = startValue + int(step * double(time - startTime));
+
+ // hit the buffers
+ if (value < m_controller->getMin())
+ value = m_controller->getMin();
+ else if (value > m_controller->getMax())
+ value = m_controller->getMax();
+
+ ControlRulerEventInsertCommand* command =
+ new ControlRulerEventInsertCommand(m_controller->getType(),
+ time, m_controller->getControllerValue(), value, *m_segment);
+
+ macro->addCommand(command);
+
+ // get new time - do it by quantized distances
+ newTime = (time / quantDur) * quantDur;
+ if (newTime > time)
+ time = newTime;
+ else
+ time += quantDur;
+ }
+
+ m_parentEditView->addCommandToHistory(macro);
+}
+
+}
diff --git a/src/gui/rulers/ControllerEventsRuler.h b/src/gui/rulers/ControllerEventsRuler.h
new file mode 100644
index 0000000..2b42274
--- /dev/null
+++ b/src/gui/rulers/ControllerEventsRuler.h
@@ -0,0 +1,118 @@
+
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_CONTROLLEREVENTSRULER_H_
+#define _RG_CONTROLLEREVENTSRULER_H_
+
+#include "ControlRuler.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QWidget;
+class QMouseEvent;
+class QCanvasLine;
+class QCanvas;
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class RulerScale;
+class Event;
+class EditViewBase;
+class ControlParameter;
+class ControlItem;
+
+
+/**
+ * ControllerEventsRuler : edit Controller events
+ */
+class ControllerEventsRuler : public ControlRuler
+{
+public:
+ ControllerEventsRuler(Segment*,
+ RulerScale*,
+ EditViewBase* parentView,
+ QCanvas*,
+ QWidget* parent=0,
+ const ControlParameter *controller = 0,
+ const char* name=0, WFlags f=0);
+
+ virtual ~ControllerEventsRuler();
+
+ virtual QString getName();
+ int getDefaultItemWidth() { return m_defaultItemWidth; }
+
+ // Allow something external to reset the selection of Events
+ // that this ruler is displaying
+ //
+ void setSegment(Segment *);
+
+ /// SegmentObserver interface
+ virtual void eventAdded(const Segment *, Event *);
+ virtual void eventRemoved(const Segment *, Event *);
+
+ virtual void insertControllerEvent();
+ virtual void eraseControllerEvent();
+ virtual void clearControllerEvents();
+ virtual void startControlLine();
+
+ ControlParameter* getControlParameter() { return m_controller; }
+
+protected:
+
+ virtual void init();
+ virtual void drawBackground();
+
+ // Let's override these again here
+ //
+ virtual void contentsMousePressEvent(QMouseEvent*);
+ virtual void contentsMouseReleaseEvent(QMouseEvent*);
+ virtual void contentsMouseMoveEvent(QMouseEvent*);
+
+ virtual void layoutItem(ControlItem*);
+
+ void drawControlLine(timeT startTime,
+ timeT endTime,
+ int startValue,
+ int endValue);
+
+ //--------------- Data members ---------------------------------
+ int m_defaultItemWidth;
+
+ ControlParameter *m_controller;
+ QCanvasLine *m_controlLine;
+
+ bool m_controlLineShowing;
+ int m_controlLineX;
+ int m_controlLineY;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/DefaultVelocityColour.cpp b/src/gui/rulers/DefaultVelocityColour.cpp
new file mode 100644
index 0000000..21cf75e
--- /dev/null
+++ b/src/gui/rulers/DefaultVelocityColour.cpp
@@ -0,0 +1,55 @@
+/* -*- 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 <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 "DefaultVelocityColour.h"
+
+#include "gui/general/GUIPalette.h"
+#include <qcolor.h>
+#include "VelocityColour.h"
+
+
+namespace Rosegarden
+{
+
+DefaultVelocityColour::DefaultVelocityColour()
+ : VelocityColour(GUIPalette::getColour(GUIPalette::LevelMeterRed),
+ GUIPalette::getColour(GUIPalette::LevelMeterOrange),
+ GUIPalette::getColour(GUIPalette::LevelMeterGreen),
+ 127, // max knee
+ 115, // red knee
+ 75, // orange knee
+ 25) // green knee
+{}
+
+DefaultVelocityColour* DefaultVelocityColour::getInstance()
+{
+ if (!m_instance) m_instance = new DefaultVelocityColour;
+
+ return m_instance;
+}
+
+DefaultVelocityColour* DefaultVelocityColour::m_instance = 0;
+
+}
diff --git a/src/gui/rulers/DefaultVelocityColour.h b/src/gui/rulers/DefaultVelocityColour.h
new file mode 100644
index 0000000..09430f3
--- /dev/null
+++ b/src/gui/rulers/DefaultVelocityColour.h
@@ -0,0 +1,54 @@
+
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_DEFAULTVELOCITYCOLOUR_H_
+#define _RG_DEFAULTVELOCITYCOLOUR_H_
+
+#include "VelocityColour.h"
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class DefaultVelocityColour : public VelocityColour
+{
+public:
+ static DefaultVelocityColour* getInstance();
+
+protected:
+ DefaultVelocityColour();
+
+ static DefaultVelocityColour* m_instance;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/ElementAdapter.h b/src/gui/rulers/ElementAdapter.h
new file mode 100644
index 0000000..e14ee63
--- /dev/null
+++ b/src/gui/rulers/ElementAdapter.h
@@ -0,0 +1,46 @@
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_ELEMENTADAPTER_H_
+#define _RG_ELEMENTADAPTER_H_
+
+#include "base/Event.h"
+
+namespace Rosegarden {
+
+class ElementAdapter
+{
+public:
+ virtual ~ElementAdapter() {};
+
+ virtual bool getValue(long&) = 0;
+ virtual void setValue(long) = 0;
+ virtual timeT getTime() = 0;
+ virtual timeT getDuration() = 0;
+ virtual Event* getEvent() = 0;
+};
+
+}
+
+#endif /*ELEMENTADAPTER_H_*/
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 <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 <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"
diff --git a/src/gui/rulers/LoopRuler.h b/src/gui/rulers/LoopRuler.h
new file mode 100644
index 0000000..7c30cc8
--- /dev/null
+++ b/src/gui/rulers/LoopRuler.h
@@ -0,0 +1,148 @@
+
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_LOOPRULER_H_
+#define _RG_LOOPRULER_H_
+
+#include "base/SnapGrid.h"
+#include "gui/general/HZoomable.h"
+#include <qsize.h>
+#include <qwidget.h>
+#include <qpen.h>
+#include "base/Event.h"
+
+
+class QPaintEvent;
+class QPainter;
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class RulerScale;
+class RosegardenGUIDoc;
+
+
+/**
+ * LoopRuler is a widget that shows bar and beat durations on a
+ * ruler-like scale, and reacts to mouse clicks by sending relevant
+ * signals to modify position pointer and playback/looping states.
+*/
+class LoopRuler : public QWidget, public HZoomable
+{
+ Q_OBJECT
+
+public:
+ LoopRuler(RosegardenGUIDoc *doc,
+ RulerScale *rulerScale,
+ int height = 0,
+ double xorigin = 0.0,
+ bool invert = false,
+ QWidget* parent = 0,
+ const char *name = 0);
+
+ ~LoopRuler();
+
+ void setSnapGrid(SnapGrid *grid);
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ void scrollHoriz(int x);
+
+ void setMinimumWidth(int width) { m_width = width; }
+
+ void setHorizScaleFactor(double dy) { m_hScaleFactor = dy; }
+
+ bool hasActiveMousePress() { return m_activeMousePress; }
+
+ bool getLoopingMode() { return m_loopingMode; }
+
+public slots:
+ void slotSetLoopMarker(timeT startLoop,
+ timeT endLoop);
+
+protected:
+ // ActiveItem interface
+ virtual void mousePressEvent (QMouseEvent*);
+ virtual void mouseReleaseEvent (QMouseEvent*);
+ virtual void mouseDoubleClickEvent (QMouseEvent*);
+ virtual void mouseMoveEvent (QMouseEvent*);
+
+ virtual void paintEvent(QPaintEvent*);
+
+ void setLoopingMode(bool value) { m_loopingMode = value; }
+ void drawBarSections(QPainter*);
+ void drawLoopMarker(QPainter*); // between loop positions
+
+signals:
+ // The three main functions that this class performs
+ //
+ /// Set the pointer position on mouse single click
+ void setPointerPosition(timeT);
+
+ /// Set the pointer position on mouse drag
+ void dragPointerToPosition(timeT);
+
+ /// Set pointer position and start playing on double click
+ void setPlayPosition(timeT);
+
+ /// Set a playing loop
+ void setLoop(timeT, timeT);
+
+ /// Set the loop end position on mouse drag
+ void dragLoopToPosition(timeT);
+
+ void startMouseMove(int directionConstraint);
+ void stopMouseMove();
+ void mouseMove();
+
+protected:
+
+ //--------------- Data members ---------------------------------
+ int m_height;
+ double m_xorigin;
+ bool m_invert;
+ int m_currentXOffset;
+ int m_width;
+ bool m_activeMousePress;
+
+ RosegardenGUIDoc *m_doc;
+ bool m_mainWindow;
+ RulerScale *m_rulerScale;
+ SnapGrid m_defaultGrid;
+ SnapGrid m_loopGrid;
+ SnapGrid *m_grid;
+ QPen m_quickMarkerPen;
+ bool m_loopingMode;
+ timeT m_startLoop;
+ timeT m_endLoop;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/MarkerRuler.cpp b/src/gui/rulers/MarkerRuler.cpp
new file mode 100644
index 0000000..7dcb67a
--- /dev/null
+++ b/src/gui/rulers/MarkerRuler.cpp
@@ -0,0 +1,490 @@
+/* -*- 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 <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 "MarkerRuler.h"
+
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Composition.h"
+#include "base/RulerScale.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/HZoomable.h"
+#include "gui/dialogs/MarkerModifyDialog.h"
+#include "commands/edit/ModifyMarkerCommand.h"
+#include "document/MultiViewCommandHistory.h"
+#include <kxmlguifactory.h>
+#include <qbrush.h>
+#include <qcursor.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qpainter.h>
+#include <qpen.h>
+#include <qpoint.h>
+#include <qpopupmenu.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <qwidget.h>
+#include <klocale.h>
+#include <kaction.h>
+#include <kstddirs.h>
+#include <qtooltip.h>
+
+
+namespace Rosegarden
+{
+
+MarkerRuler::MarkerRuler(RosegardenGUIDoc *doc,
+ RulerScale *rulerScale,
+ int barHeight,
+ double xorigin,
+ QWidget* parent,
+ const char* name,
+ WFlags f)
+ : QWidget(parent, name, f),
+ m_barHeight(barHeight),
+ m_xorigin(xorigin),
+ m_currentXOffset(0),
+ m_width(-1),
+ m_clickX(0),
+ m_menu(0),
+ m_doc(doc),
+ m_rulerScale(rulerScale),
+ m_parentMainWindow(dynamic_cast<KMainWindow*>(doc->parent()))
+{
+ // If the parent window has a main window above it, we need to use
+ // that as the parent main window, not the document's parent.
+ // Otherwise we'll end up adding all actions to the same
+ // (document-level) action collection regardless of which window
+ // we're in.
+ QObject *probe = parent;
+ while (probe && !dynamic_cast<KMainWindow *>(probe)) probe = probe->parent();
+ if (probe) m_parentMainWindow = dynamic_cast<KMainWindow *>(probe);
+
+ // m_barFont = new QFont("helvetica", 12);
+ // m_barFont->setPixelSize(12);
+ m_barFont = new QFont();
+ m_barFont->setPointSize(10);
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QIconSet icon;
+
+ // Use the event insert, delete, edit icons because they are
+ // actually generic enough to serve for anything. Let's hope they
+ // don't become more event-specific in future...
+
+ icon = QIconSet(QPixmap(pixmapDir + "/toolbar/event-insert.png"));
+ new KAction(i18n("Insert Marker"), icon, 0, this,
+ SLOT(slotInsertMarkerHere()), actionCollection(),
+ "insert_marker_here");
+
+ new KAction(i18n("Insert Marker at Playback Position"), 0, this,
+ SLOT(slotInsertMarkerAtPointer()), actionCollection(),
+ "insert_marker_at_pointer");
+
+ icon = QIconSet(QPixmap(pixmapDir + "/toolbar/event-delete.png"));
+ new KAction(i18n("Delete Marker"), icon, 0, this,
+ SLOT(slotDeleteMarker()), actionCollection(),
+ "delete_marker");
+
+ icon = QIconSet(QPixmap(pixmapDir + "/toolbar/event-edit.png"));
+ new KAction(i18n("Edit Marker..."), icon, 0, this,
+ SLOT(slotEditMarker()), actionCollection(),
+ "edit_marker");
+
+ QToolTip::add
+ (this, i18n("Click on a marker to move the playback pointer.\nShift-click to set a range between markers.\nDouble-click to open the marker editor."));
+}
+
+MarkerRuler::~MarkerRuler()
+{
+ delete m_barFont;
+ // 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
+MarkerRuler::createMenu()
+{
+ setXMLFile("markerruler.rc");
+
+ KXMLGUIFactory* factory = m_parentMainWindow->factory();
+ factory->addClient(this);
+
+ QWidget* tmp = factory->container("marker_ruler_menu", this);
+
+// if (!tmp) {
+// RG_DEBUG << "MarkerRuler::createMenu() menu not found\n"
+// << domDocument().toString(4) << endl;
+// }
+
+ m_menu = dynamic_cast<QPopupMenu*>(tmp);
+
+ if (!m_menu) {
+ RG_DEBUG << "MarkerRuler::createMenu() failed\n";
+ }
+}
+
+
+void
+MarkerRuler::scrollHoriz(int x)
+{
+ m_currentXOffset = static_cast<int>( -x / getHScaleFactor());
+ repaint();
+}
+
+QSize
+MarkerRuler::sizeHint() const
+{
+ int lastBar =
+ m_rulerScale->getLastVisibleBar();
+ double width =
+ m_rulerScale->getBarPosition(lastBar) +
+ m_rulerScale->getBarWidth(lastBar) + m_xorigin;
+
+ return QSize(std::max(int(width), m_width), m_barHeight);
+}
+
+QSize
+MarkerRuler::minimumSizeHint() const
+{
+ double firstBarWidth = m_rulerScale->getBarWidth(0) + m_xorigin;
+
+ return QSize(static_cast<int>(firstBarWidth), m_barHeight);
+}
+
+void
+MarkerRuler::slotInsertMarkerHere()
+{
+ emit addMarker(getClickPosition());
+}
+
+void
+MarkerRuler::slotInsertMarkerAtPointer()
+{
+ emit addMarker(m_doc->getComposition().getPosition());
+}
+
+void
+MarkerRuler::slotDeleteMarker()
+{
+ RG_DEBUG << "MarkerRuler::slotDeleteMarker()\n";
+
+ Rosegarden::Marker* marker = getMarkerAtClickPosition();
+
+ if (marker)
+ emit deleteMarker(marker->getID(),
+ marker->getTime(),
+ marker->getName(),
+ marker->getDescription());
+}
+
+void
+MarkerRuler::slotEditMarker()
+{
+ Rosegarden::Marker* marker = getMarkerAtClickPosition();
+
+ if (!marker) return;
+
+ // I think the ruler should be doing all this stuff itself, or
+ // emitting signals connected to a dedicated marker model object,
+ // not just relying on the app object. Same goes for practically
+ // everything else we do. Hey ho. Having this here is
+ // inconsistent with the other methods, so if anyone wants to move
+ // it, be my guest.
+
+ MarkerModifyDialog dialog(this, &m_doc->getComposition(), marker);
+ if (dialog.exec() == QDialog::Accepted) {
+ ModifyMarkerCommand *command =
+ new ModifyMarkerCommand(&m_doc->getComposition(),
+ marker->getID(),
+ dialog.getOriginalTime(),
+ dialog.getTime(),
+ qstrtostr(dialog.getName()),
+ qstrtostr(dialog.getDescription()));
+ m_doc->getCommandHistory()->addCommand(command);
+ }
+}
+
+timeT
+MarkerRuler::getClickPosition()
+{
+ timeT t = m_rulerScale->getTimeForX
+ (m_clickX - m_xorigin - m_currentXOffset);
+
+ return t;
+}
+
+Rosegarden::Marker*
+MarkerRuler::getMarkerAtClickPosition()
+{
+ QRect clipRect = visibleRect();
+
+ 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();
+ }
+
+ Composition &comp = m_doc->getComposition();
+ Composition::markercontainer markers = comp.getMarkers();
+
+ timeT start = comp.getBarStart(firstBar);
+ timeT end = comp.getBarEnd(lastBar);
+
+ // need these to calculate the visible extents of a marker tag
+ QPainter painter(this);
+ painter.setFont(*m_barFont);
+ QFontMetrics metrics = painter.fontMetrics();
+
+ for (Composition::markerconstiterator i = markers.begin();
+ i != markers.end(); ++i) {
+
+ if ((*i)->getTime() >= start && (*i)->getTime() < end) {
+
+ QString name(strtoqstr((*i)->getName()));
+
+ int x = m_rulerScale->getXForTime((*i)->getTime())
+ + m_xorigin + m_currentXOffset;
+
+ int width = metrics.width(name) + 5;
+
+ int nextX = -1;
+ Composition::markerconstiterator j = i;
+ ++j;
+ if (j != markers.end()) {
+ nextX = m_rulerScale->getXForTime((*j)->getTime())
+ + m_xorigin + m_currentXOffset;
+ }
+
+ if (m_clickX >= x && m_clickX <= x + width) {
+
+ if (nextX < x || m_clickX <= nextX) {
+
+ return *i;
+ }
+ }
+ }
+ }
+
+ return 0L;
+}
+
+void
+MarkerRuler::paintEvent(QPaintEvent*)
+{
+ QPainter painter(this);
+ painter.setFont(*m_barFont);
+
+ if (getHScaleFactor() != 1.0)
+ painter.scale(getHScaleFactor(), 1.0);
+
+ QRect clipRect = visibleRect();
+
+ 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();
+ }
+
+ painter.drawLine(m_currentXOffset, 0, static_cast<int>(visibleRect().width() / getHScaleFactor()), 0);
+
+ float minimumWidth = 25.0;
+ float testSize = ((float)(m_rulerScale->getBarPosition(firstBar + 1) -
+ m_rulerScale->getBarPosition(firstBar)))
+ / minimumWidth;
+
+ int every = 0;
+ int count = 0;
+
+ if (testSize < 1.0) {
+ every = (int(1.0 / testSize));
+
+ if (every % 2 == 0)
+ every++;
+ }
+
+ for (int i = firstBar; i <= lastBar; ++i) {
+
+ double x = m_rulerScale->getBarPosition(i) + m_xorigin + m_currentXOffset;
+
+ if ((x * getHScaleFactor()) > clipRect.x() + clipRect.width())
+ break;
+
+ // always the first bar number
+ if (every && i != firstBar) {
+ if (count < every) {
+ count++;
+ continue;
+ }
+
+ // reset count if we passed
+ count = 0;
+ }
+
+ // adjust count for first bar line
+ if (every == firstBar)
+ count++;
+
+ if (i != lastBar) {
+ painter.drawLine(static_cast<int>(x), 0, static_cast<int>(x), m_barHeight);
+
+ // disable worldXForm for text
+ QPoint textDrawPoint = painter.xForm(QPoint(static_cast<int>(x + 4), 12));
+
+ bool enableXForm = painter.hasWorldXForm();
+ painter.setWorldXForm(false);
+
+ if (i >= 0)
+ painter.drawText(textDrawPoint, QString("%1").arg(i + 1));
+
+ painter.setWorldXForm(enableXForm);
+ } else {
+ const QPen normalPen = painter.pen();
+ ;
+ QPen endPen(black, 2);
+ painter.setPen(endPen);
+ painter.drawLine(static_cast<int>(x), 0, static_cast<int>(x), m_barHeight);
+ painter.setPen(normalPen);
+ }
+ }
+
+ if (m_doc) {
+ Composition &comp = m_doc->getComposition();
+ Composition::markercontainer markers = comp.getMarkers();
+ Composition::markerconstiterator it;
+
+ timeT start = comp.getBarStart(firstBar);
+ timeT end = comp.getBarEnd(lastBar);
+
+ QFontMetrics metrics = painter.fontMetrics();
+
+ for (it = markers.begin(); it != markers.end(); ++it) {
+ if ((*it)->getTime() >= start && (*it)->getTime() < end) {
+ QString name(strtoqstr((*it)->getName()));
+
+ double x = m_rulerScale->getXForTime((*it)->getTime())
+ + m_xorigin + m_currentXOffset;
+
+ painter.fillRect(static_cast<int>(x), 1,
+ static_cast<int>(metrics.width(name) + 5),
+ m_barHeight - 2,
+ QBrush(GUIPalette::getColour(GUIPalette::MarkerBackground)));
+
+ painter.drawLine(int(x), 1, int(x), m_barHeight - 2);
+ painter.drawLine(int(x) + 1, 1, int(x) + 1, m_barHeight - 2);
+
+ QPoint textDrawPoint = painter.xForm
+ (QPoint(static_cast<int>(x + 3), m_barHeight - 4));
+
+ // disable worldXForm for text
+ bool enableXForm = painter.hasWorldXForm();
+ painter.setWorldXForm(false);
+
+ painter.drawText(textDrawPoint, name);
+
+ painter.setWorldXForm(enableXForm);
+ }
+ }
+ }
+}
+
+void
+MarkerRuler::mousePressEvent(QMouseEvent *e)
+{
+ RG_DEBUG << "MarkerRuler::mousePressEvent: x = " << e->x() << endl;
+
+ if (!m_doc || !e)
+ return;
+
+ m_clickX = e->x();
+ Rosegarden::Marker* clickedMarker = getMarkerAtClickPosition();
+
+ // if right-click, show popup menu
+ //
+ if (e->button() == RightButton) {
+ if (!m_menu)
+ createMenu();
+ if (m_menu) {
+ actionCollection()->action("delete_marker")->setEnabled(clickedMarker != 0);
+ actionCollection()->action("edit_marker")->setEnabled(clickedMarker != 0);
+ m_menu->exec(QCursor::pos());
+ }
+ return;
+ }
+
+ bool shiftPressed = ((e->state() & Qt::ShiftButton) != 0);
+
+ Composition &comp = m_doc->getComposition();
+ Composition::markercontainer markers = comp.getMarkers();
+
+ if (shiftPressed) { // set loop
+
+ timeT t = m_rulerScale->getTimeForX
+ (e->x() - m_xorigin - m_currentXOffset);
+
+ timeT prev = 0;
+
+ for (Composition::markerconstiterator i = markers.begin();
+ i != markers.end(); ++i) {
+
+ timeT cur = (*i)->getTime();
+
+ if (cur >= t) {
+ emit setLoop(prev, cur);
+ return ;
+ }
+
+ prev = cur;
+ }
+
+ if (prev > 0)
+ emit setLoop(prev, comp.getEndMarker());
+
+ } else { // set pointer to clicked marker
+
+ if (clickedMarker)
+ emit setPointerPosition(clickedMarker->getTime());
+ }
+}
+
+void
+MarkerRuler::mouseDoubleClickEvent(QMouseEvent *)
+{
+ RG_DEBUG << "MarkerRuler::mouseDoubleClickEvent" << endl;
+
+ emit editMarkers();
+}
+
+}
+#include "MarkerRuler.moc"
diff --git a/src/gui/rulers/MarkerRuler.h b/src/gui/rulers/MarkerRuler.h
new file mode 100644
index 0000000..c77e6a9
--- /dev/null
+++ b/src/gui/rulers/MarkerRuler.h
@@ -0,0 +1,121 @@
+
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_MARKERRULER_H_
+#define _RG_MARKERRULER_H_
+
+#include "gui/general/HZoomable.h"
+#include <qsize.h>
+#include <qwidget.h>
+#include <kxmlguiclient.h>
+#include "base/Event.h"
+
+
+class QPaintEvent;
+class QMouseEvent;
+class QFont;
+class QPopupMenu;
+class KMainWindow;
+
+namespace Rosegarden
+{
+
+class Marker;
+class RulerScale;
+class RosegardenGUIDoc;
+
+
+class MarkerRuler : public QWidget, public HZoomable, public KXMLGUIClient
+{
+ Q_OBJECT
+
+public:
+ MarkerRuler(RosegardenGUIDoc *doc,
+ RulerScale *rulerScale,
+ int buttonHeight,
+ double xorigin = 0.0,
+ QWidget* parent = 0,
+ const char* name = 0,
+ WFlags f=0);
+
+ virtual ~MarkerRuler();
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ void scrollHoriz(int x);
+
+ void setWidth(int width) { m_width = width; }
+
+signals:
+ /// Set the pointer position on mouse single click
+ void setPointerPosition(timeT);
+
+ /// Open the marker editor window on double click
+ void editMarkers();
+
+ /// add a marker
+ void addMarker(timeT);
+
+ void deleteMarker(int, timeT, QString name, QString description);
+
+ /// Set a loop range
+ void setLoop(timeT, timeT);
+
+protected slots:
+ void slotInsertMarkerHere();
+ void slotInsertMarkerAtPointer();
+ void slotDeleteMarker();
+ void slotEditMarker();
+
+protected:
+ virtual void paintEvent(QPaintEvent*);
+ virtual void mousePressEvent(QMouseEvent *e);
+ virtual void mouseDoubleClickEvent(QMouseEvent *e);
+
+ void createMenu();
+ timeT getClickPosition();
+ Rosegarden::Marker* getMarkerAtClickPosition();
+
+ //--------------- Data members ---------------------------------
+ int m_barHeight;
+ double m_xorigin;
+ int m_currentXOffset;
+ int m_width;
+ int m_clickX;
+
+ QFont *m_barFont;
+ QPopupMenu *m_menu;
+
+ RosegardenGUIDoc *m_doc;
+ RulerScale *m_rulerScale;
+ KMainWindow* m_parentMainWindow;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/PercussionPitchRuler.cpp b/src/gui/rulers/PercussionPitchRuler.cpp
new file mode 100644
index 0000000..a89ae89
--- /dev/null
+++ b/src/gui/rulers/PercussionPitchRuler.cpp
@@ -0,0 +1,204 @@
+/* -*- 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 <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 "PercussionPitchRuler.h"
+
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/MidiProgram.h"
+#include "gui/editors/matrix/MatrixStaff.h"
+#include "gui/editors/matrix/MatrixView.h"
+#include "gui/general/MidiPitchLabel.h"
+#include "PitchRuler.h"
+#include <qcolor.h>
+#include <qevent.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qpainter.h>
+#include <qsize.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+PercussionPitchRuler::PercussionPitchRuler(QWidget *parent,
+ const MidiKeyMapping *mapping,
+ int lineSpacing) :
+ PitchRuler(parent),
+ m_mapping(mapping),
+ m_lineSpacing(lineSpacing),
+ m_mouseDown(false),
+ m_lastHoverHighlight( -1)
+{
+ m_font = new QFont();
+ m_font->setPixelSize(9);
+ m_fontMetrics = new QFontMetrics(*m_font);
+ m_width = m_fontMetrics->width(" A#2 Acoustic Bass Drum ");
+
+ setPaletteBackgroundColor(QColor(238, 238, 224));
+
+ setMouseTracking(true);
+}
+
+QSize PercussionPitchRuler::sizeHint() const
+{
+ return QSize(m_width,
+ (m_lineSpacing + 1) * m_mapping->getPitchExtent());
+}
+
+QSize PercussionPitchRuler::minimumSizeHint() const
+{
+ return QSize(m_width, m_lineSpacing + 1);
+}
+
+void PercussionPitchRuler::paintEvent(QPaintEvent*)
+{
+ QPainter paint(this);
+
+ paint.setFont(*m_font);
+
+ int minPitch = m_mapping->getPitchForOffset(0);
+ int extent = m_mapping->getPitchExtent();
+
+ for (int i = 0; i < extent; ++i) {
+ paint.drawLine(0, i * (m_lineSpacing + 1),
+ m_width, i * (m_lineSpacing + 1));
+ }
+
+ int lw = m_fontMetrics->width("A#2");
+
+ for (int i = 0; i < extent; ++i) {
+
+ MidiPitchLabel label(minPitch + i);
+ std::string key = m_mapping->getMapForKeyName(minPitch + i);
+
+ RG_DEBUG << i << ": " << label.getQString() << ": " << key << endl;
+
+ paint.drawText
+ (2, (extent - i - 1) * (m_lineSpacing + 1) +
+ m_fontMetrics->ascent() + 1,
+ label.getQString());
+
+ paint.drawText
+ (9 + lw, (extent - i - 1) * (m_lineSpacing + 1) +
+ m_fontMetrics->ascent() + 1,
+ strtoqstr(key));
+ }
+}
+
+void PercussionPitchRuler::enterEvent(QEvent *)
+{}
+
+void PercussionPitchRuler::leaveEvent(QEvent*)
+{
+ // m_hoverHighlight->hide();
+}
+
+void PercussionPitchRuler::drawHoverNote(int evPitch)
+{
+ QPainter paint(this);
+ paint.setFont(*m_font);
+
+ if (m_lastHoverHighlight != evPitch) {
+
+ int minPitch = m_mapping->getPitchForOffset(0);
+ int extent = m_mapping->getPitchExtent();
+
+ int lw = m_fontMetrics->width("A#2");
+
+ if (m_lastHoverHighlight >= 0) {
+
+ int y = (extent - (m_lastHoverHighlight - minPitch) - 1) * (m_lineSpacing + 1);
+ paint.setBrush(QColor(238, 238, 224));
+ paint.setPen(QColor(238, 238, 224));
+ paint.drawRect(lw + 7, y + 1, m_width - lw, m_lineSpacing);
+ std::string key = m_mapping->getMapForKeyName(m_lastHoverHighlight);
+ paint.setPen(Qt::black);
+ paint.drawText
+ (9 + lw, y + m_fontMetrics->ascent() + 1,
+ strtoqstr(key));
+ }
+
+ int y = (extent - (evPitch - minPitch) - 1) * (m_lineSpacing + 1);
+ m_lastHoverHighlight = evPitch;
+ paint.setBrush(paint.pen().color());
+ paint.drawRect(lw + 7, y, m_width - lw, m_lineSpacing + 1);
+ paint.setPen(QColor(238, 238, 224));
+
+ std::string key = m_mapping->getMapForKeyName(evPitch);
+ paint.drawText
+ (9 + lw, y + m_fontMetrics->ascent() + 1,
+ strtoqstr(key));
+ }
+}
+
+void PercussionPitchRuler::mouseMoveEvent(QMouseEvent* e)
+{
+ // ugh
+
+ MatrixView *matrixView = dynamic_cast<MatrixView*>(topLevelWidget());
+ if (matrixView) {
+ MatrixStaff *staff = matrixView->getStaff(0);
+ if (staff) {
+ drawHoverNote(staff->getHeightAtCanvasCoords(e->x(), e->y()));
+ }
+ }
+
+ if (m_mouseDown)
+ if (m_selecting)
+ emit keySelected(e->y(), true);
+ else
+ emit keyPressed(e->y(), true); // we're swooshing
+ else
+ emit hoveredOverKeyChanged(e->y());
+}
+
+void PercussionPitchRuler::mousePressEvent(QMouseEvent *e)
+{
+ Qt::ButtonState bs = e->state();
+
+ if (e->button() == LeftButton) {
+
+ m_mouseDown = true;
+ m_selecting = (bs & Qt::ShiftButton);
+
+ if (m_selecting)
+ emit keySelected(e->y(), false);
+ else
+ emit keyPressed(e->y(), false);
+ }
+}
+
+void PercussionPitchRuler::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (e->button() == LeftButton) {
+ m_mouseDown = false;
+ m_selecting = false;
+ }
+}
+
+}
+#include "PercussionPitchRuler.moc"
diff --git a/src/gui/rulers/PercussionPitchRuler.h b/src/gui/rulers/PercussionPitchRuler.h
new file mode 100644
index 0000000..cae61ec
--- /dev/null
+++ b/src/gui/rulers/PercussionPitchRuler.h
@@ -0,0 +1,91 @@
+
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_PERCUSSIONPITCHRULER_H_
+#define _RG_PERCUSSIONPITCHRULER_H_
+
+#include "PitchRuler.h"
+#include <qsize.h>
+
+
+class QWidget;
+class QPaintEvent;
+class QMouseEvent;
+class QFontMetrics;
+class QFont;
+class QEvent;
+
+
+namespace Rosegarden
+{
+
+class MidiKeyMapping;
+
+
+class PercussionPitchRuler : public PitchRuler
+{
+ Q_OBJECT
+public:
+ PercussionPitchRuler(QWidget *parent,
+ const MidiKeyMapping *mapping,
+ int lineSpacing);
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ void drawHoverNote(int evPitch);
+
+signals:
+ void keyPressed(unsigned int y, bool repeating);
+ void keySelected(unsigned int y, bool repeating);
+ void hoveredOverKeyChanged(unsigned int y);
+
+protected:
+ virtual void paintEvent(QPaintEvent*);
+ virtual void mouseMoveEvent(QMouseEvent*);
+ virtual void mousePressEvent(QMouseEvent*);
+ virtual void mouseReleaseEvent(QMouseEvent*);
+ virtual void enterEvent(QEvent *);
+ virtual void leaveEvent(QEvent *);
+
+ const MidiKeyMapping *m_mapping;
+
+ int m_width;
+ int m_lineSpacing;
+
+ bool m_mouseDown;
+ bool m_selecting;
+
+ int m_lastHoverHighlight;
+
+ QFont *m_font;
+ QFontMetrics *m_fontMetrics;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/PitchRuler.cpp b/src/gui/rulers/PitchRuler.cpp
new file mode 100644
index 0000000..55f4b00
--- /dev/null
+++ b/src/gui/rulers/PitchRuler.cpp
@@ -0,0 +1,55 @@
+/* -*- 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 <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 "PitchRuler.h"
+
+#include <qsize.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+PitchRuler::
+PitchRuler(QWidget *parent) :
+ QWidget(parent)
+{
+ // nothing else
+}
+
+QSize
+PitchRuler::sizeHint() const
+{
+ return QWidget::sizeHint();
+}
+
+QSize
+PitchRuler::minimumSizeHint() const
+{
+ return QWidget::minimumSizeHint();
+}
+
+}
+#include "PitchRuler.moc"
diff --git a/src/gui/rulers/PitchRuler.h b/src/gui/rulers/PitchRuler.h
new file mode 100644
index 0000000..3c47709
--- /dev/null
+++ b/src/gui/rulers/PitchRuler.h
@@ -0,0 +1,78 @@
+
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_PITCHRULER_H_
+#define _RG_PITCHRULER_H_
+
+#include <qsize.h>
+#include <qwidget.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class PitchRuler : public QWidget
+{
+ Q_OBJECT
+public:
+ PitchRuler(QWidget *parent);
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ virtual void drawHoverNote(int evPitch) = 0;
+
+signals:
+
+ /**
+ * A pitch has been clicked.
+ * y is the simple event y-coordinate.
+ * If the user is in the middle of dragging, repeating will be set.
+ */
+ void keyPressed(unsigned int y, bool repeating);
+
+ /**
+ * A pitch has been clicked with the selection modifier pressed.
+ * y is the simple event y-coordinate.
+ * If the user is in the middle of dragging, repeating will be set.
+ */
+ void keySelected(unsigned int y, bool repeating);
+
+ /**
+ * Emitted when the mouse cursor moves to a different pitch when
+ * not clicking or selecting.
+ * y is the simple event y-coordinate.
+ */
+ void hoveredOverKeyChanged(unsigned int y);
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/PropertyBox.cpp b/src/gui/rulers/PropertyBox.cpp
new file mode 100644
index 0000000..38d67ef
--- /dev/null
+++ b/src/gui/rulers/PropertyBox.cpp
@@ -0,0 +1,77 @@
+/* -*- 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 <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 "PropertyBox.h"
+
+#include "gui/general/GUIPalette.h"
+#include <qpainter.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+PropertyBox::PropertyBox(QString label,
+ int width,
+ int height,
+ QWidget *parent,
+ const char *name):
+ QWidget(parent, name),
+ m_label(label),
+ m_width(width),
+ m_height(height)
+{}
+
+QSize
+PropertyBox::sizeHint() const
+{
+ return QSize(m_width, m_height);
+}
+
+QSize
+PropertyBox::minimumSizeHint() const
+{
+ return QSize(m_width, m_height);
+}
+
+void
+PropertyBox::paintEvent(QPaintEvent *e)
+{
+ QPainter paint(this);
+
+ paint.setPen(GUIPalette::getColour(GUIPalette::MatrixElementBorder));
+ //paint.setBrush(GUIPalette::getColour(GUIPalette::MatrixElementBlock));
+
+ paint.setClipRegion(e->region());
+ paint.setClipRect(e->rect().normalize());
+
+ paint.drawRect(2, 2, m_width - 3, m_height - 3);
+ paint.drawText(10, 2 * m_height / 3, m_label);
+}
+
+}
+#include "PropertyBox.moc"
diff --git a/src/gui/rulers/PropertyBox.h b/src/gui/rulers/PropertyBox.h
new file mode 100644
index 0000000..1b36f0b
--- /dev/null
+++ b/src/gui/rulers/PropertyBox.h
@@ -0,0 +1,74 @@
+
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_PROPERTYBOX_H_
+#define _RG_PROPERTYBOX_H_
+
+#include <qsize.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+class QPaintEvent;
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * We use a ControlBox to help modify events on the ruler - set tools etc.
+ * and provide extra information or options.
+ *
+ */
+class PropertyBox : public QWidget
+{
+ Q_OBJECT
+
+public:
+ PropertyBox(QString label,
+ int width,
+ int height,
+ QWidget *parent=0,
+ const char *name = 0);
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+protected:
+ virtual void paintEvent(QPaintEvent *);
+
+ //--------------- Data members ---------------------------------
+
+ QString m_label;
+ int m_width;
+ int m_height;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/PropertyControlRuler.cpp b/src/gui/rulers/PropertyControlRuler.cpp
new file mode 100644
index 0000000..7dc1258
--- /dev/null
+++ b/src/gui/rulers/PropertyControlRuler.cpp
@@ -0,0 +1,441 @@
+/* -*- 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 <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 "PropertyControlRuler.h"
+
+#include "ControlRuler.h"
+#include "ControlItem.h"
+#include "ViewElementAdapter.h"
+#include "misc/Debug.h"
+#include "base/BaseProperties.h"
+#include "base/NotationTypes.h"
+#include "base/PropertyName.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/Staff.h"
+#include "base/ViewElement.h"
+#include "commands/edit/SelectionPropertyCommand.h"
+#include "gui/general/EditViewBase.h"
+#include "gui/widgets/TextFloat.h"
+#include "gui/general/LinedStaff.h"
+#include <qcanvas.h>
+#include <qcolor.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+PropertyControlRuler::PropertyControlRuler(PropertyName propertyName,
+ Staff* staff,
+ RulerScale* rulerScale,
+ EditViewBase* parentView,
+ QCanvas* c, QWidget* parent,
+ const char* name, WFlags f) :
+ ControlRuler(&(staff->getSegment()), rulerScale,
+ parentView, c, parent, name, f),
+ m_propertyName(propertyName),
+ m_staff(staff),
+ m_propertyLine(new QCanvasLine(canvas())),
+ m_propertyLineShowing(false),
+ m_propertyLineX(0),
+ m_propertyLineY(0)
+{
+ m_staff->addObserver(this);
+ m_propertyLine->setZ(1000); // bring to front
+
+ setMenuName("property_ruler_menu");
+ drawBackground();
+ init();
+}
+
+void
+PropertyControlRuler::setStaff(Staff *staff)
+{
+ RG_DEBUG << "PropertyControlRuler::setStaff(" << staff << ")" << endl;
+
+ m_staff->removeObserver(this);
+ m_segment->removeObserver(this);
+ m_staff = staff;
+ m_segment = &m_staff->getSegment();
+ m_staff->addObserver(this);
+ m_segment->addObserver(this);
+
+ //!!! need to delete the control items here
+
+ drawBackground();
+ init();
+}
+
+void
+PropertyControlRuler::drawBackground()
+{
+ // Draw some minimum and maximum controller value guide lines
+ //
+ QCanvasLine *topLine = new QCanvasLine(canvas());
+ QCanvasLine *topQLine = new QCanvasLine(canvas());
+ QCanvasLine *midLine = new QCanvasLine(canvas());
+ QCanvasLine *botQLine = new QCanvasLine(canvas());
+ QCanvasLine *bottomLine = new QCanvasLine(canvas());
+ //m_controlLine->setPoints(m_controlLineX, m_controlLineY, m_controlLineX, m_controlLineY);
+ int cHeight = canvas()->height();
+ int cWidth = canvas()->width();
+
+ topLine->setPen(QColor(127, 127, 127));
+ topLine->setPoints(0, 0, cWidth, 0);
+ topLine->setZ( -10);
+ topLine->show();
+
+ topQLine->setPen(QColor(192, 192, 192));
+ topQLine->setPoints(0, cHeight / 4, cWidth, cHeight / 4);
+ topQLine->setZ( -10);
+ topQLine->show();
+
+ midLine->setPen(QColor(127, 127, 127));
+ midLine->setPoints(0, cHeight / 2, cWidth, cHeight / 2);
+ midLine->setZ( -10);
+ midLine->show();
+
+ botQLine->setPen(QColor(192, 192, 192));
+ botQLine->setPoints(0, 3*cHeight / 4, cWidth, 3*cHeight / 4);
+ botQLine->setZ( -10);
+ botQLine->show();
+
+ bottomLine->setPen(QColor(127, 127, 127));
+ bottomLine->setPoints(0, cHeight - 1, cWidth, cHeight - 1);
+ bottomLine->setZ( -10);
+ bottomLine->show();
+}
+
+PropertyControlRuler::~PropertyControlRuler()
+{
+ if (m_staff) {
+ m_staff->removeObserver(this);
+ }
+}
+
+QString PropertyControlRuler::getName()
+{
+ return getPropertyName().c_str();
+}
+
+void PropertyControlRuler::init()
+{
+ ViewElementList* viewElementList = m_staff->getViewElementList();
+
+ LinedStaff* lStaff = dynamic_cast<LinedStaff*>(m_staff);
+
+ if (lStaff)
+ m_staffOffset = lStaff->getX();
+
+ for (ViewElementList::iterator i = viewElementList->begin();
+ i != viewElementList->end(); ++i) {
+
+ if ((*i)->event()->isa(Note::EventRestType))
+ continue;
+
+ double x = m_rulerScale->getXForTime((*i)->getViewAbsoluteTime());
+ new ControlItem(this, new ViewElementAdapter(*i, getPropertyName()), int(x + m_staffOffset),
+ int(m_rulerScale->getXForTime((*i)->getViewAbsoluteTime() +
+ (*i)->getViewDuration()) - x));
+
+ }
+}
+
+void PropertyControlRuler::elementAdded(const Staff *, ViewElement *el)
+{
+ RG_DEBUG << "PropertyControlRuler::elementAdded()\n";
+
+ if (el->event()->isa(Note::EventRestType))
+ return ;
+
+ double x = m_rulerScale->getXForTime(el->getViewAbsoluteTime());
+
+ new ControlItem(this, new ViewElementAdapter(el, getPropertyName()), int(x + m_staffOffset),
+ int(m_rulerScale->getXForTime(el->getViewAbsoluteTime() +
+ el->getViewDuration()) - x));
+}
+
+void PropertyControlRuler::elementRemoved(const Staff *, ViewElement *el)
+{
+ RG_DEBUG << "PropertyControlRuler::elementRemoved(\n";
+
+ clearSelectedItems();
+
+ QCanvasItemList allItems = canvas()->allItems();
+
+ for (QCanvasItemList::Iterator it = allItems.begin(); it != allItems.end(); ++it) {
+ if (ControlItem *item = dynamic_cast<ControlItem*>(*it)) {
+ ViewElementAdapter * adapter = dynamic_cast<ViewElementAdapter*>(item->getElementAdapter());
+ if (adapter->getViewElement() == el) {
+ delete item;
+ break;
+ }
+ }
+ }
+}
+
+void PropertyControlRuler::staffDeleted(const Staff *)
+{
+ m_staff = 0;
+}
+
+void
+PropertyControlRuler::endMarkerTimeChanged(const Segment *s, bool)
+{
+ timeT endMarkerTime = s->getEndMarkerTime();
+
+ RG_DEBUG << "PropertyControlRuler::endMarkerTimeChanged() " << endMarkerTime << endl;
+
+ clearSelectedItems();
+
+ clear();
+ init();
+}
+
+void PropertyControlRuler::computeStaffOffset()
+{
+ LinedStaff* lStaff = dynamic_cast<LinedStaff*>(m_staff);
+ if (lStaff)
+ m_staffOffset = lStaff->getX();
+}
+
+void PropertyControlRuler::startPropertyLine()
+{
+ RG_DEBUG << "PropertyControlRuler::startPropertyLine\n";
+ m_propertyLineShowing = true;
+ this->setCursor(Qt::pointingHandCursor);
+}
+
+void
+PropertyControlRuler::contentsMousePressEvent(QMouseEvent *e)
+{
+ RG_DEBUG << "PropertyControlRuler::contentsMousePressEvent\n";
+
+ if (!m_propertyLineShowing) {
+ if (e->button() == MidButton)
+ m_lastEventPos = inverseMapPoint(e->pos());
+
+ ControlRuler::contentsMousePressEvent(e); // send super
+
+ return ;
+ }
+
+ // cancel control line mode
+ if (e->button() == RightButton) {
+ m_propertyLineShowing = false;
+ m_propertyLine->hide();
+
+ this->setCursor(Qt::arrowCursor);
+ return ;
+ }
+
+ if (e->button() == LeftButton) {
+ QPoint p = inverseMapPoint(e->pos());
+
+ m_propertyLine->show();
+ m_propertyLineX = p.x();
+ m_propertyLineY = p.y();
+ m_propertyLine->setPoints(m_propertyLineX, m_propertyLineY, m_propertyLineX, m_propertyLineY);
+ canvas()->update();
+ }
+}
+
+void
+PropertyControlRuler::contentsMouseReleaseEvent(QMouseEvent *e)
+{
+ RG_DEBUG << "PropertyControlRuler::contentsMouseReleaseEvent\n";
+
+ /*
+ if (m_propertyLineShowing)
+ {
+ this->setCursor(Qt::arrowCursor);
+ m_propertyLineShowing = false;
+ canvas()->update();
+ }
+ */
+
+ if (!m_propertyLineShowing) {
+ /*
+ if (e->button() == MidButton)
+ insertControllerEvent();
+ */
+
+ ControlRuler::contentsMouseReleaseEvent(e); // send super
+ return ;
+ } else {
+ QPoint p = inverseMapPoint(e->pos());
+
+ timeT startTime = m_rulerScale->getTimeForX(m_propertyLineX);
+ timeT endTime = m_rulerScale->getTimeForX(p.x());
+
+ long startValue = heightToValue(m_propertyLineY - canvas()->height());
+ long endValue = heightToValue(p.y() - canvas()->height());
+
+ RG_DEBUG << "PropertyControlRuler::contentsMouseReleaseEvent - "
+ << "starttime = " << startTime
+ << ", endtime = " << endTime
+ << ", startValue = " << startValue
+ << ", endValue = " << endValue
+ << endl;
+
+ drawPropertyLine(startTime, endTime, startValue, endValue);
+
+ m_propertyLineShowing = false;
+ m_propertyLine->hide();
+ this->setCursor(Qt::arrowCursor);
+ canvas()->update();
+ }
+}
+
+void
+PropertyControlRuler::contentsMouseMoveEvent(QMouseEvent *e)
+{
+ RG_DEBUG << "PropertyControlRuler::contentsMouseMoveEvent\n";
+
+ if (!m_propertyLineShowing) {
+ // Don't send super if we're using the middle button
+ //
+ if (e->button() == MidButton) {
+ m_lastEventPos = inverseMapPoint(e->pos());
+ return ;
+ }
+
+ ControlRuler::contentsMouseMoveEvent(e); // send super
+ return ;
+ }
+
+ QPoint p = inverseMapPoint(e->pos());
+
+ m_propertyLine->setPoints(m_propertyLineX, m_propertyLineY, p.x(), p.y());
+ canvas()->update();
+}
+
+void PropertyControlRuler::contentsContextMenuEvent(QContextMenuEvent* e)
+{
+ RG_DEBUG << "PropertyControlRuler::contentsContextMenuEvent\n";
+
+ // check if we actually have some control items
+ QCanvasItemList list = canvas()->allItems();
+ bool haveItems = false;
+
+ QCanvasItemList::Iterator it = list.begin();
+ for (; it != list.end(); ++it) {
+ if (dynamic_cast<ControlItem*>(*it)) {
+ haveItems = true;
+ break;
+ }
+ }
+
+ RG_DEBUG << "PropertyControlRuler::contentsContextMenuEvent : haveItems = "
+ << haveItems << endl;
+
+ emit stateChange("have_note_events_in_segment", haveItems);
+
+ ControlRuler::contentsContextMenuEvent(e);
+}
+
+void
+PropertyControlRuler::drawPropertyLine(timeT startTime,
+ timeT endTime,
+ int startValue,
+ int endValue)
+{
+ if (startTime > endTime) {
+ std::swap(startTime, endTime);
+ std::swap(startValue, endValue);
+ }
+
+ RG_DEBUG << "PropertyControlRuler::drawPropertyLine - set velocity from "
+ << startTime
+ << " to " << endTime << endl;
+
+ // Add the "true" to catch Events overlapping this line
+ //
+ EventSelection selection(*m_segment, startTime, endTime, true);
+ PropertyPattern pattern = DecrescendoPattern;
+
+ bool haveNotes = selection.contains(Note::EventType);
+
+ if (haveNotes) {
+
+ SelectionPropertyCommand *command =
+ new SelectionPropertyCommand(&selection,
+ BaseProperties::VELOCITY,
+ pattern,
+ startValue,
+ endValue);
+
+ m_parentEditView->addCommandToHistory(command);
+
+ } else {
+
+ RG_DEBUG << "PropertyControlRuler::drawPropertyLine - no notes in selection\n";
+
+ }
+}
+
+void
+PropertyControlRuler::selectAllProperties()
+{
+ RG_DEBUG << "PropertyControlRuler::selectAllProperties" << endl;
+
+ /*
+ for(Segment::iterator i = m_segment.begin();
+ i != m_segment.end(); ++i)
+ if (!m_eventSelection->contains(*i)) m_eventSelection->addEvent(*i);
+ */
+
+ clearSelectedItems();
+
+ QCanvasItemList l = canvas()->allItems();
+ for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) {
+ if (ControlItem *item = dynamic_cast<ControlItem*>(*it)) {
+ m_selectedItems << item;
+ (*it)->setSelected(true);
+ ElementAdapter* adapter = item->getElementAdapter();
+ m_eventSelection->addEvent(adapter->getEvent());
+ }
+ }
+
+ /*
+ m_eventSelection->addFromSelection(&selection);
+ for (QCanvasItemList::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);
+}
+
+}
diff --git a/src/gui/rulers/PropertyControlRuler.h b/src/gui/rulers/PropertyControlRuler.h
new file mode 100644
index 0000000..f94f3e1
--- /dev/null
+++ b/src/gui/rulers/PropertyControlRuler.h
@@ -0,0 +1,120 @@
+
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_PROPERTYCONTROLRULER_H_
+#define _RG_PROPERTYCONTROLRULER_H_
+
+#include "base/PropertyName.h"
+#include "base/Staff.h"
+#include "ControlRuler.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QWidget;
+class QMouseEvent;
+class QContextMenuEvent;
+class QCanvasLine;
+class QCanvas;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class Staff;
+class Segment;
+class RulerScale;
+class EditViewBase;
+
+
+/**
+ * PropertyControlRuler : edit a property on events on a staff (only
+ * events with a ViewElement attached, mostly notes)
+ */
+class PropertyControlRuler : public ControlRuler, public StaffObserver
+{
+public:
+ PropertyControlRuler(PropertyName propertyName,
+ Staff*,
+ RulerScale*,
+ EditViewBase* parentView,
+ QCanvas*,
+ QWidget* parent=0, const char* name=0, WFlags f=0);
+
+ virtual ~PropertyControlRuler();
+
+ virtual QString getName();
+
+ const PropertyName& getPropertyName() { return m_propertyName; }
+
+ // Allow something external to reset the selection of Events
+ // that this ruler is displaying
+ //
+ void setStaff(Staff *);
+
+ // StaffObserver interface
+ virtual void elementAdded(const Staff *, ViewElement*);
+ virtual void elementRemoved(const Staff *, ViewElement*);
+ virtual void staffDeleted(const Staff *);
+ virtual void startPropertyLine();
+ virtual void selectAllProperties();
+
+ /// SegmentObserver interface
+ virtual void endMarkerTimeChanged(const Segment *, bool shorten);
+
+protected:
+
+ virtual void contentsMousePressEvent(QMouseEvent*);
+ virtual void contentsMouseReleaseEvent(QMouseEvent*);
+ virtual void contentsMouseMoveEvent(QMouseEvent*);
+ virtual void contentsContextMenuEvent(QContextMenuEvent*);
+
+ void drawPropertyLine(timeT startTime,
+ timeT endTime,
+ int startValue,
+ int endValue);
+
+ virtual void init();
+ virtual void drawBackground();
+ virtual void computeStaffOffset();
+
+ //--------------- Data members ---------------------------------
+
+ PropertyName m_propertyName;
+ Staff* m_staff;
+
+ QCanvasLine *m_propertyLine;
+
+ bool m_propertyLineShowing;
+ int m_propertyLineX;
+ int m_propertyLineY;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/PropertyViewRuler.cpp b/src/gui/rulers/PropertyViewRuler.cpp
new file mode 100644
index 0000000..cf5d89d
--- /dev/null
+++ b/src/gui/rulers/PropertyViewRuler.cpp
@@ -0,0 +1,175 @@
+/* -*- 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 <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 "PropertyViewRuler.h"
+
+#include "base/Event.h"
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "base/PropertyName.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "DefaultVelocityColour.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/HZoomable.h"
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qpainter.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+PropertyViewRuler::PropertyViewRuler(RulerScale *rulerScale,
+ Segment *segment,
+ const PropertyName &property,
+ double xorigin,
+ int height,
+ QWidget *parent,
+ const char *name) :
+ QWidget(parent, name),
+ m_propertyName(property),
+ m_xorigin(xorigin),
+ m_height(height),
+ m_currentXOffset(0),
+ m_width( -1),
+ m_segment(segment),
+ m_rulerScale(rulerScale),
+ m_fontMetrics(m_boldFont)
+{
+ m_boldFont.setBold(true);
+ m_fontMetrics = QFontMetrics(m_boldFont);
+
+ setBackgroundColor(GUIPalette::getColour(GUIPalette::SegmentCanvas));
+
+ QString tip = i18n("%1 controller").arg(strtoqstr(property));
+ QToolTip::add
+ (this, tip);
+}
+
+PropertyViewRuler::~PropertyViewRuler()
+{
+ // nothing
+}
+
+void
+PropertyViewRuler::slotScrollHoriz(int x)
+{
+ int w = width(), h = height();
+ x = int(double(x) / getHScaleFactor());
+ 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
+PropertyViewRuler::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
+PropertyViewRuler::minimumSizeHint() const
+{
+ double firstBarWidth = m_rulerScale->getBarWidth(0) + m_xorigin;
+ QSize res = QSize(int(firstBarWidth), m_height);
+ return res;
+}
+
+void
+PropertyViewRuler::paintEvent(QPaintEvent* e)
+{
+ QPainter paint(this);
+
+ if (getHScaleFactor() != 1.0)
+ paint.scale(getHScaleFactor(), 1.0);
+
+ paint.setPen(GUIPalette::getColour(GUIPalette::MatrixElementBorder));
+
+ QRect clipRect = e->rect().normalize();
+
+ timeT from = m_rulerScale->getTimeForX
+ (clipRect.x() - m_currentXOffset - m_xorigin);
+
+ Segment::iterator it = m_segment->findNearestTime(from);
+
+ for (; m_segment->isBeforeEndMarker(it); it++) {
+ long value = 0;
+
+ if (!(*it)->get
+ <Int>(m_propertyName, value))
+ continue;
+
+ int x = int(m_rulerScale->getXForTime((*it)->getAbsoluteTime()))
+ + m_currentXOffset + int(m_xorigin);
+
+ int xPos = x * int(getHScaleFactor());
+
+ if (xPos < clipRect.x())
+ continue;
+
+ if (xPos > (clipRect.x() + clipRect.width()))
+ break;
+
+ // include fiddle factor (+2)
+ int width =
+ int(m_rulerScale->getXForTime((*it)->getAbsoluteTime() +
+ (*it)->getDuration()) + 2)
+ + m_currentXOffset + int(m_xorigin) - x;
+
+ int blockHeight = int(double(height()) * (value / 127.0));
+
+ paint.setBrush(DefaultVelocityColour::getInstance()->getColour(value));
+
+ paint.drawRect(x, height() - blockHeight, width, blockHeight);
+ }
+}
+
+}
+#include "PropertyViewRuler.moc"
diff --git a/src/gui/rulers/PropertyViewRuler.h b/src/gui/rulers/PropertyViewRuler.h
new file mode 100644
index 0000000..b7d479c
--- /dev/null
+++ b/src/gui/rulers/PropertyViewRuler.h
@@ -0,0 +1,102 @@
+
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_PROPERTYVIEWRULER_H_
+#define _RG_PROPERTYVIEWRULER_H_
+
+#include "base/PropertyName.h"
+#include "gui/general/HZoomable.h"
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qsize.h>
+#include <qwidget.h>
+
+
+class QPaintEvent;
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class RulerScale;
+
+
+/**
+ * PropertyViewRuler is a widget that shows a range of Property
+ * (velocity, typically) values for a set of Rosegarden Events.
+ */
+class PropertyViewRuler : public QWidget, public HZoomable
+{
+ Q_OBJECT
+
+public:
+ PropertyViewRuler(RulerScale *rulerScale,
+ Segment *segment,
+ const PropertyName &property,
+ double xorigin = 0.0,
+ int height = 0,
+ QWidget* parent = 0,
+ const char *name = 0);
+
+ ~PropertyViewRuler();
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ void setMinimumWidth(int width) { m_width = width; }
+
+ /**
+ * Get the property name
+ */
+ PropertyName getPropertyName() const { return m_propertyName; }
+
+public slots:
+ void slotScrollHoriz(int x);
+
+protected:
+ virtual void paintEvent(QPaintEvent *);
+
+ //--------------- Data members ---------------------------------
+
+ PropertyName m_propertyName;
+
+ double m_xorigin;
+ int m_height;
+ int m_currentXOffset;
+ int m_width;
+
+ Segment *m_segment;
+ RulerScale *m_rulerScale;
+
+ QFont m_font;
+ QFont m_boldFont;
+ QFontMetrics m_fontMetrics;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/RawNoteRuler.cpp b/src/gui/rulers/RawNoteRuler.cpp
new file mode 100644
index 0000000..cc7d6e4
--- /dev/null
+++ b/src/gui/rulers/RawNoteRuler.cpp
@@ -0,0 +1,573 @@
+/* -*- 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 <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 "RawNoteRuler.h"
+
+#include "misc/Debug.h"
+#include "base/BaseProperties.h"
+#include "base/Composition.h"
+#include "base/NotationTypes.h"
+#include "base/NotationQuantizer.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "DefaultVelocityColour.h"
+#include "gui/general/GUIPalette.h"
+#include <klocale.h>
+#include <qcolor.h>
+#include <qpainter.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qtooltip.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+RawNoteRuler::RawNoteRuler(RulerScale *rulerScale,
+ Segment *segment,
+ double xorigin,
+ int height,
+ QWidget *parent,
+ const char *name) :
+ QWidget(parent, name),
+ m_xorigin(xorigin),
+ m_height(height),
+ m_currentXOffset(0),
+ m_width( -1),
+ m_segment(segment),
+ m_rulerScale(rulerScale)
+{
+ setBackgroundColor(GUIPalette::getColour(GUIPalette::RawNoteRulerBackground));
+ QToolTip::add(this,"");
+}
+
+RawNoteRuler::~RawNoteRuler()
+{
+ QToolTip::remove(this);
+ // nothing else
+}
+
+void
+RawNoteRuler::slotScrollHoriz(int x)
+{
+ int w = width(), h = height();
+ int dx = x - ( -m_currentXOffset);
+ if (dx == 0)
+ return ;
+ 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
+RawNoteRuler::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
+RawNoteRuler::minimumSizeHint() const
+{
+ double firstBarWidth = m_rulerScale->getBarWidth(0) + m_xorigin;
+ QSize res = QSize(int(firstBarWidth), m_height);
+ return res;
+}
+
+std::pair<timeT, timeT>
+RawNoteRuler::getExtents(Segment::iterator i)
+{
+ const Quantizer *q =
+ m_segment->getComposition()->getNotationQuantizer();
+
+ timeT u0 = (*i)->getAbsoluteTime();
+ timeT u1 = u0 + (*i)->getDuration();
+
+ timeT q0 = q->getQuantizedAbsoluteTime(*i);
+ timeT q1 = q0 + q->getQuantizedDuration(*i);
+
+ timeT t0 = std::min(u0, q0);
+ timeT t1 = std::max(u1, q1);
+
+ return std::pair<timeT, timeT>(t0, t1);
+}
+
+Segment::iterator
+RawNoteRuler::addChildren(Segment *s,
+ Segment::iterator to,
+ timeT rightBound,
+ EventTreeNode *node)
+{
+ Segment::iterator i = node->node;
+
+ std::pair<timeT, timeT> iex = getExtents(i);
+ Segment::iterator j = i;
+ Segment::iterator rightmost = to;
+
+#ifdef DEBUG_RAW_NOTE_RULER
+
+ RG_DEBUG << "addChildren called for extents " << iex.first << "->" << iex.second << ", rightBound " << rightBound << endl;
+#endif
+
+ for (++j; j != to && s->isBeforeEndMarker(j); ) {
+
+ if (!(*j)->isa(Note::EventType)) {
+ ++j;
+ continue;
+ }
+ std::pair<timeT, timeT> jex = getExtents(j);
+
+#ifdef DEBUG_RAW_NOTE_RULER
+
+ RG_DEBUG << "addChildren: event at " << (*j)->getAbsoluteTime() << ", extents " << jex.first << "->" << jex.second << endl;
+#endif
+
+ if (jex.first == jex.second) {
+ ++j;
+ continue;
+ }
+ if (jex.first >= iex.second || jex.first >= rightBound)
+ break;
+
+#ifdef DEBUG_RAW_NOTE_RULER
+
+ RG_DEBUG << "addChildren: adding" << endl;
+#endif
+
+ EventTreeNode *subnode = new EventTreeNode(j);
+
+ Segment::iterator subRightmost = addChildren(s, to, rightBound, subnode);
+ if (subRightmost != to)
+ rightmost = subRightmost;
+ else
+ rightmost = j;
+
+ node->children.push_back(subnode);
+ j = s->findTime(jex.second);
+ }
+
+ return rightmost;
+}
+
+void
+RawNoteRuler::buildForest(Segment *s,
+ Segment::iterator from,
+ Segment::iterator to)
+{
+ for (EventTreeNode::NodeList::iterator i = m_forest.begin();
+ i != m_forest.end(); ++i) {
+ delete *i;
+ }
+ m_forest.clear();
+
+ timeT endTime = (s->isBeforeEndMarker(to) ? (*to)->getAbsoluteTime() :
+ s->getEndMarkerTime());
+
+ for (Segment::iterator i = from; i != to && s->isBeforeEndMarker(i); ) {
+
+ if (!(*i)->isa(Note::EventType)) {
+ ++i;
+ continue;
+ }
+
+ std::pair<timeT, timeT> iex = getExtents(i);
+
+#ifdef DEBUG_RAW_NOTE_RULER
+
+ RG_DEBUG << "buildForest: event at " << (*i)->getAbsoluteTime() << ", extents " << iex.first << "->" << iex.second << endl;
+#endif
+
+ if (iex.first == iex.second) {
+ ++i;
+ continue;
+ }
+ if (iex.first >= endTime)
+ break;
+
+ EventTreeNode *node = new EventTreeNode(i);
+ Segment::iterator rightmost = addChildren(s, to, iex.second, node);
+ m_forest.push_back(node);
+
+ if (rightmost != to) {
+ i = rightmost;
+ ++i;
+ } else {
+ i = s->findTime(iex.second);
+ }
+
+#ifdef DEBUG_RAW_NOTE_RULER
+ RG_DEBUG << "findTime " << iex.second << " returned iterator at " << (i == s->end() ? -1 : (*i)->getAbsoluteTime()) << endl;
+#endif
+
+ }
+}
+
+void
+RawNoteRuler::dumpSubtree(EventTreeNode *node, int depth)
+{
+ if (!node)
+ return ;
+#ifdef DEBUG_RAW_NOTE_RULER
+
+ for (int i = 0; i < depth; ++i)
+ std::cerr << " ";
+ if (depth > 0)
+ std::cerr << "->";
+ std::cerr << (*node->node)->getAbsoluteTime() << ","
+ << (*node->node)->getDuration() << " [";
+ long pitch = 0;
+ if ((*node->node)->get
+ <Int>(PITCH, pitch)) {
+ std::cerr << pitch << "]" << std::endl;
+ }
+ else {
+ std::cerr << "no-pitch]" << std::endl;
+ }
+ for (EventTreeNode::NodeList::iterator i = node->children.begin();
+ i != node->children.end(); ++i) {
+ dumpSubtree(*i, depth + 1);
+ }
+#endif
+ (void)depth; // avoid warnings
+}
+
+void
+RawNoteRuler::dumpForest(EventTreeNode::NodeList *forest)
+{
+#ifdef DEBUG_RAW_NOTE_RULER
+ std::cerr << "\nFOREST:\n" << std::endl;
+
+ for (unsigned int i = 0; i < forest->size(); ++i) {
+
+ std::cerr << "\nTREE " << i << ":\n" << std::endl;
+ dumpSubtree((*forest)[i], 0);
+ }
+
+ std::cerr << std::endl;
+#endif
+
+ (void)forest; // avoid warnings
+}
+
+int
+RawNoteRuler::EventTreeNode::getDepth()
+{
+ int subchildrenDepth = 0;
+ for (NodeList::iterator i = children.begin();
+ i != children.end(); ++i) {
+ int subchildDepth = (*i)->getDepth();
+ if (subchildDepth > subchildrenDepth)
+ subchildrenDepth = subchildDepth;
+ }
+ return subchildrenDepth + 1;
+}
+
+int
+RawNoteRuler::EventTreeNode::getChildrenAboveOrBelow(bool below, int p)
+{
+ long pitch(p);
+ if (pitch < 0)
+ (*node)->get
+ <Int>(BaseProperties::PITCH, pitch);
+
+ int max = 0;
+
+ for (NodeList::iterator i = children.begin();
+ i != children.end(); ++i) {
+ int forThisChild = (*i)->getChildrenAboveOrBelow(below, pitch);
+ long thisChildPitch = pitch;
+ (*(*i)->node)->get
+ <Int>(BaseProperties::PITCH, thisChildPitch);
+ if (below ? (thisChildPitch < pitch) : (thisChildPitch > pitch)) {
+ ++forThisChild;
+ }
+ if (forThisChild > max)
+ max = forThisChild;
+ }
+
+ return max;
+}
+
+void
+RawNoteRuler::drawNode(QPainter &paint, DefaultVelocityColour &vc,
+ EventTreeNode *node, double height, double yorigin)
+{
+ int depth = node->getDepth();
+ int above = node->getChildrenAboveOrBelow(false);
+
+#ifdef DEBUG_RAW_NOTE_RULER
+
+ int below = node->getChildrenAboveOrBelow(true);
+
+ NOTATION_DEBUG << "RawNoteRuler::drawNode: children above: "
+ << above << ", below: " << below << endl;
+#endif
+
+ int toFit = depth;
+
+ double heightPer = double(height) / toFit;
+ if (heightPer > m_height / 4)
+ heightPer = m_height / 4;
+ if (heightPer < 2)
+ heightPer = 2;
+
+ double myOrigin = yorigin + (heightPer * above);
+ long myPitch = 60;
+ (*node->node)->get
+ <Int>(BaseProperties::PITCH, myPitch);
+
+ long velocity = 100;
+ (*node->node)->get
+ <Int>(BaseProperties::VELOCITY, velocity);
+ QColor colour = vc.getColour(velocity);
+
+ timeT start = (*node->node)->getAbsoluteTime();
+ timeT end = (*node->node)->getDuration() + start;
+
+ double u0 = m_rulerScale->getXForTime(start);
+ double u1 = m_rulerScale->getXForTime(end);
+
+ u0 += m_currentXOffset + m_xorigin;
+ u1 += m_currentXOffset + m_xorigin;
+
+ start = m_segment->getComposition()->getNotationQuantizer()->
+ getQuantizedAbsoluteTime(*node->node);
+ end = start + m_segment->getComposition()->getNotationQuantizer()->
+ getQuantizedDuration(*node->node);
+
+ double q0 = m_rulerScale->getXForTime(start);
+ double q1 = m_rulerScale->getXForTime(end);
+
+ q0 += m_currentXOffset + m_xorigin;
+ q1 += m_currentXOffset + m_xorigin;
+
+#ifdef DEBUG_RAW_NOTE_RULER
+
+ NOTATION_DEBUG << "RawNoteRuler: (" << int(start) << "," << myOrigin
+ << ") -> (" << int(end) << "," << myOrigin << ")" << endl;
+#endif
+
+ int qi0 = int(q0);
+ int ui0 = int(u0);
+ int qi1 = int(q1);
+ int ui1 = int(u1);
+ // int qiw = int(q1-q0) - 1;
+ int uiw = int(u1 - u0) - 1;
+ // int iy = int(myOrigin + (height - heightPer) / 2);
+ int iy = int(myOrigin);
+ int ih = int(heightPer);
+
+#ifdef DEBUG_RAW_NOTE_RULER
+
+ NOTATION_DEBUG << "RawNoteRuler: height " << height << ", heightPer "
+ << heightPer << ", iy " << iy << endl;
+#endif
+
+ paint.setPen(colour);
+ paint.setBrush(colour);
+ paint.drawRect(ui0 + 1, iy + 1, uiw, ih - 1);
+
+ paint.setPen(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground));
+ paint.setBrush(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground));
+ paint.drawLine(qi0, iy, qi1 - 1, iy);
+ paint.drawLine(qi0, iy + ih, qi1 - 1, iy + ih);
+ paint.drawLine(ui0, iy + 1, ui0, iy + ih - 1);
+ paint.drawLine(ui1 - 1, iy + 1, ui1 - 1, iy + ih - 1);
+
+ for (EventTreeNode::NodeList::iterator i = node->children.begin();
+ i != node->children.end(); ++i) {
+
+ long nodePitch = myPitch;
+ (*(*i)->node)->get
+ <Int>(BaseProperties::PITCH, nodePitch);
+
+ if (nodePitch < myPitch) {
+
+ drawNode(paint, vc, *i,
+ height - heightPer - myOrigin, myOrigin + heightPer);
+
+ } else {
+
+ drawNode(paint, vc, *i,
+ myOrigin - yorigin, yorigin);
+ }
+ }
+}
+
+void
+RawNoteRuler::paintEvent(QPaintEvent* e)
+{
+ if (!m_segment || !m_segment->getComposition())
+ return ;
+
+ // Tooltips
+ {
+ QToolTip::remove(this);
+ TrackId trackId = m_segment->getTrack();
+ Track *track =
+ m_segment->getComposition()->getTrackById(trackId);
+ int trackPosition = -1;
+ if (track)
+ trackPosition = track->getPosition();
+
+ QToolTip::add(this,i18n("Track #%1, Segment \"%2\" (runtime id %3)")
+ .arg(trackPosition + 1)
+ .arg(m_segment->getLabel())
+ .arg(m_segment->getRuntimeId()));
+ }
+
+ // START_TIMING;
+
+ QPainter paint(this);
+ paint.setClipRegion(e->region());
+ paint.setClipRect(e->rect().normalize());
+
+ QRect clipRect = paint.clipRegion().boundingRect();
+
+ 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);
+
+ paint.setPen(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground));
+ paint.setBrush(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground));
+ paint.drawLine(0, 0, width(), 0);
+
+ // draw the extent of the segment using its color
+
+ QColor brushColor = GUIPalette::convertColour(m_segment->getComposition()->
+ getSegmentColourMap().getColourByIndex(m_segment->getColourIndex()));
+ paint.setPen(brushColor);
+ paint.setBrush(brushColor);
+ int x0 = int(m_rulerScale->getXForTime(m_segment->getStartTime()) +
+ m_currentXOffset + m_xorigin);
+ int x1 = int(m_rulerScale->getXForTime(m_segment->getEndTime()) +
+ m_currentXOffset + m_xorigin);
+ paint.drawRect(x0, 1, x1-x0+1, height()-1);
+
+ // draw the bar divisions
+
+ int firstBar = m_segment->getComposition()->getBarNumber(from);
+ int lastBar = m_segment->getComposition()->getBarNumber(to);
+ std::vector<int> divisions;
+
+ for (int barNo = firstBar; barNo <= lastBar; ++barNo) {
+
+ bool isNew = false;
+ TimeSignature timeSig =
+ m_segment->getComposition()->getTimeSignatureInBar(barNo, isNew);
+ if (isNew || barNo == firstBar) {
+ timeSig.getDivisions(3, divisions);
+ if (timeSig == TimeSignature()) // special case for 4/4
+ divisions[0] = 2;
+ }
+
+ timeT barStart = m_segment->getComposition()->getBarStart(barNo);
+ timeT base = timeSig.getBarDuration();
+ timeT barEnd = barStart + base;
+
+ paint.setPen(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground));
+ paint.setBrush(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground));
+
+ int x = int(m_rulerScale->getXForTime(barStart) +
+ m_currentXOffset + m_xorigin);
+ paint.drawLine(x, 1, x, m_height);
+
+ for (int depth = 0; depth < 3; ++depth) {
+
+ int grey = depth * 60 + 60;
+ paint.setPen(QColor(grey, grey, grey));
+ paint.setBrush(QColor(grey, grey, grey));
+
+ base /= divisions[depth];
+ timeT t(barStart + base);
+ while (t < barEnd) {
+ if ((t - barStart) % (base * divisions[depth]) != 0) {
+ int x = int(m_rulerScale->getXForTime(t) +
+ m_currentXOffset + m_xorigin);
+ paint.drawLine(x, 1, x, m_height);
+ }
+ t += base;
+ }
+ }
+ }
+
+ // PRINT_ELAPSED("RawNoteRuler::paintEvent: drawing bar lines and divisions");
+
+#ifdef DEBUG_RAW_NOTE_RULER
+ NOTATION_DEBUG << "RawNoteRuler: from is " << from << ", to is " << to << endl;
+#endif
+
+ Segment::iterator i = m_segment->findNearestTime(from);
+ if (i == m_segment->end())
+ i = m_segment->begin();
+
+ // somewhat experimental, as is this whole class
+ Segment::iterator j = m_segment->findTime(to);
+ buildForest(m_segment, i, j);
+
+ // PRINT_ELAPSED("RawNoteRuler::paintEvent: buildForest");
+
+ dumpForest(&m_forest);
+
+ // PRINT_ELAPSED("RawNoteRuler::paintEvent: dumpForest");
+
+ for (EventTreeNode::NodeList::iterator fi = m_forest.begin();
+ fi != m_forest.end(); ++fi) {
+
+ // Each tree in the forest should represent a note that starts
+ // at a time when no other notes are playing (at least of
+ // those that started no earlier than the paint start time).
+ // Each node in that tree represents a note that starts
+ // playing during its parent node's note, or at the same time
+ // as it.
+
+ drawNode(paint, *DefaultVelocityColour::getInstance(), *fi, m_height - 3, 2);
+
+ }
+
+ // PRINT_ELAPSED("RawNoteRuler::paintEvent: complete");
+}
+
+}
+#include "RawNoteRuler.moc"
diff --git a/src/gui/rulers/RawNoteRuler.h b/src/gui/rulers/RawNoteRuler.h
new file mode 100644
index 0000000..f194062
--- /dev/null
+++ b/src/gui/rulers/RawNoteRuler.h
@@ -0,0 +1,128 @@
+
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_RAWNOTERULER_H_
+#define _RG_RAWNOTERULER_H_
+
+#include "base/Segment.h"
+#include <qsize.h>
+#include <qwidget.h>
+#include <utility>
+#include <vector>
+#include "base/Event.h"
+
+
+class QPaintEvent;
+class QPainter;
+
+
+namespace Rosegarden
+{
+
+class RulerScale;
+class DefaultVelocityColour;
+
+
+/**
+ * RawNoteRuler is a ruler that shows in a vaguely matrix-like fashion
+ * when notes start and end, for use with a notation view that can't
+ * otherwise show this relatively precise unquantized information.
+ * It has no editing function (yet?)
+ */
+
+class RawNoteRuler : public QWidget
+{
+ Q_OBJECT
+
+public:
+ RawNoteRuler(RulerScale *rulerScale,
+ Segment *segment,
+ double xorigin = 0.0,
+ int height = 0,
+ QWidget* parent = 0,
+ const char *name = 0);
+
+ ~RawNoteRuler();
+
+ void setCurrentSegment(Segment *segment) {
+ m_segment = segment;
+ }
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ void setMinimumWidth(int width) { m_width = width; }
+
+public slots:
+ void slotScrollHoriz(int x);
+
+protected:
+ virtual void paintEvent(QPaintEvent*);
+
+private:
+ double m_xorigin;
+ int m_height;
+ int m_currentXOffset;
+ int m_width;
+
+ Segment *m_segment;
+ RulerScale *m_rulerScale;
+
+ struct EventTreeNode
+ {
+ typedef std::vector<EventTreeNode *> NodeList;
+
+ EventTreeNode(Segment::iterator n) : node(n) { }
+ ~EventTreeNode() {
+ for (NodeList::iterator i = children.begin();
+ i != children.end(); ++i) {
+ delete *i;
+ }
+ }
+
+ int getDepth();
+ int getChildrenAboveOrBelow(bool below = false, int pitch = -1);
+
+ Segment::iterator node;
+ NodeList children;
+ };
+
+ std::pair<timeT, timeT> getExtents(Segment::iterator);
+ Segment::iterator addChildren(Segment *, Segment::iterator, timeT, EventTreeNode *);
+ void dumpSubtree(EventTreeNode *, int);
+ void dumpForest(std::vector<EventTreeNode *> *);
+ void buildForest(Segment *, Segment::iterator, Segment::iterator);
+
+ void drawNode(QPainter &, DefaultVelocityColour &, EventTreeNode *,
+ double height, double yorigin);
+
+ // needs to be class with dtor &c and containing above methods
+ EventTreeNode::NodeList m_forest;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/StandardRuler.cpp b/src/gui/rulers/StandardRuler.cpp
new file mode 100644
index 0000000..611c991
--- /dev/null
+++ b/src/gui/rulers/StandardRuler.cpp
@@ -0,0 +1,172 @@
+/* -*- 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 <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 "StandardRuler.h"
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "MarkerRuler.h"
+#include "base/RulerScale.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/MultiViewCommandHistory.h"
+#include "gui/application/RosegardenGUIApp.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/rulers/LoopRuler.h"
+#include "document/RosegardenGUIDoc.h"
+#include <qobject.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+StandardRuler::StandardRuler(RosegardenGUIDoc *doc,
+ RulerScale *rulerScale,
+ double xorigin,
+ int barHeight,
+ bool invert,
+ QWidget* parent,
+ const char* name,
+ WFlags f):
+ QVBox(parent, name, f),
+ m_invert(invert),
+ m_loopRulerHeight(10),
+ m_currentXOffset(0),
+ m_doc(doc),
+ m_rulerScale(rulerScale),
+ m_hButtonBar(0)
+{
+ setSpacing(0);
+
+ if (!m_invert) {
+ m_hButtonBar = new MarkerRuler
+ (m_doc, m_rulerScale, barHeight - m_loopRulerHeight, xorigin, this);
+ }
+
+ m_loopRuler = new LoopRuler
+ (m_doc, m_rulerScale, m_loopRulerHeight, xorigin, m_invert, this, name);
+
+ if (m_invert) {
+ m_hButtonBar = new MarkerRuler
+ (m_doc, m_rulerScale, barHeight - m_loopRulerHeight, xorigin, this);
+ }
+
+ QObject::connect
+ (doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(update()));
+
+}
+
+void StandardRuler::setSnapGrid(SnapGrid *grid)
+{
+ m_loopRuler->setSnapGrid(grid);
+}
+
+void StandardRuler::connectRulerToDocPointer(RosegardenGUIDoc *doc)
+{
+
+ RG_DEBUG << "StandardRuler::connectRulerToDocPointer" << endl;
+
+ // use the document as a hub for pointer and loop set related signals
+ // pointer and loop drag signals are specific to the current view,
+ // so they are re-emitted from the loop ruler by this widget
+ //
+ QObject::connect
+ (m_loopRuler, SIGNAL(setPointerPosition(timeT)),
+ doc, SLOT(slotSetPointerPosition(timeT)));
+
+ QObject::connect
+ (m_hButtonBar, SIGNAL(setPointerPosition(timeT)),
+ doc, SLOT(slotSetPointerPosition(timeT)));
+
+ QObject::connect
+ (m_hButtonBar, SIGNAL(editMarkers()),
+ RosegardenGUIApp::self(), SLOT(slotEditMarkers()));
+
+ QObject::connect
+ (m_hButtonBar, SIGNAL(addMarker(timeT)),
+ RosegardenGUIApp::self(), SLOT(slotAddMarker(timeT)));
+
+ QObject::connect
+ (m_hButtonBar, SIGNAL(deleteMarker(int, timeT, QString, QString)),
+ RosegardenGUIApp::self(), SLOT(slotDeleteMarker(int, timeT, QString, QString)));
+
+ QObject::connect
+ (m_loopRuler, SIGNAL(dragPointerToPosition(timeT)),
+ this, SIGNAL(dragPointerToPosition(timeT)));
+
+ QObject::connect
+ (m_loopRuler, SIGNAL(dragLoopToPosition(timeT)),
+ this, SIGNAL(dragLoopToPosition(timeT)));
+
+ QObject::connect
+ (m_loopRuler, SIGNAL(setPlayPosition(timeT)),
+ RosegardenGUIApp::self(), SLOT(slotSetPlayPosition(timeT)));
+
+ QObject::connect
+ (m_hButtonBar, SIGNAL(setLoop(timeT, timeT)),
+ doc, SLOT(slotSetLoop(timeT, timeT)));
+
+ QObject::connect
+ (m_loopRuler, SIGNAL(setLoop(timeT, timeT)),
+ doc, SLOT(slotSetLoop(timeT, timeT)));
+
+ QObject::connect
+ (doc, SIGNAL(loopChanged(timeT, timeT)),
+ m_loopRuler,
+ SLOT(slotSetLoopMarker(timeT, timeT)));
+
+ m_loopRuler->setBackgroundColor(GUIPalette::getColour(GUIPalette::PointerRuler));
+}
+
+void StandardRuler::slotScrollHoriz(int x)
+{
+ m_loopRuler->scrollHoriz(x);
+ m_hButtonBar->scrollHoriz(x);
+}
+
+void StandardRuler::setMinimumWidth(int width)
+{
+ m_hButtonBar->setMinimumWidth(width);
+ m_loopRuler->setMinimumWidth(width);
+}
+
+void StandardRuler::setHScaleFactor(double dy)
+{
+ m_hButtonBar->setHScaleFactor(dy);
+ m_loopRuler->setHScaleFactor(dy);
+}
+
+void StandardRuler::paintEvent(QPaintEvent *e)
+{
+ m_hButtonBar->update();
+ m_loopRuler->update();
+ QWidget::paintEvent(e);
+}
+
+}
+#include "StandardRuler.moc"
diff --git a/src/gui/rulers/StandardRuler.h b/src/gui/rulers/StandardRuler.h
new file mode 100644
index 0000000..de9804d
--- /dev/null
+++ b/src/gui/rulers/StandardRuler.h
@@ -0,0 +1,108 @@
+
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_STANDARDRULER_H_
+#define _RG_STANDARDRULER_H_
+
+#include <qvbox.h>
+#include "base/Event.h"
+
+
+class QWidget;
+class QPaintEvent;
+
+
+namespace Rosegarden
+{
+
+class RulerScale;
+class RosegardenGUIDoc;
+class LoopRuler;
+class MarkerRuler;
+class SnapGrid;
+
+
+class StandardRuler : public QVBox
+{
+ Q_OBJECT
+
+public:
+ StandardRuler(RosegardenGUIDoc *doc,
+ RulerScale *rulerScale,
+ double xorigin,
+ int buttonHeight,
+ bool invert = false, // draw upside-down
+ QWidget* parent = 0,
+ const char* name = 0,
+ WFlags f=0);
+
+ void setSnapGrid(SnapGrid *grid);
+
+ LoopRuler* getLoopRuler() { return m_loopRuler; }
+
+ /**
+ * Make connections from the LoopRuler to the document's
+ * position pointer -- the standard use for a LoopRuler.
+ * If you don't call this, you'll have to connect the
+ * LoopRuler's signals up to something yourself.
+ */
+ void connectRulerToDocPointer(RosegardenGUIDoc *doc);
+
+ void setMinimumWidth(int width);
+
+ void setHScaleFactor(double dy);
+
+public slots:
+ void slotScrollHoriz(int x);
+
+signals:
+ /// reflected from the loop ruler
+ void dragPointerToPosition(timeT);
+
+ /// reflected from the loop ruler
+ void dragLoopToPosition(timeT);
+
+
+protected:
+ virtual void paintEvent(QPaintEvent *);
+
+private:
+ //--------------- Data members ---------------------------------
+ bool m_invert;
+ int m_loopRulerHeight;
+ int m_currentXOffset;
+
+ RosegardenGUIDoc *m_doc;
+ RulerScale *m_rulerScale;
+
+ MarkerRuler *m_hButtonBar;
+ LoopRuler *m_loopRuler;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/TempoColour.cpp b/src/gui/rulers/TempoColour.cpp
new file mode 100644
index 0000000..5ab396a
--- /dev/null
+++ b/src/gui/rulers/TempoColour.cpp
@@ -0,0 +1,55 @@
+/* -*- 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 <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 "TempoColour.h"
+
+#include "gui/general/GUIPalette.h"
+#include <qcolor.h>
+
+
+namespace Rosegarden
+{
+
+QColor
+TempoColour::getColour(double tempo)
+{
+ int h, s, v;
+ QColor c = GUIPalette::getColour(GUIPalette::TempoBase);
+ c.hsv(&h, &s, &v);
+ v += 20;
+ if (v > 255)
+ v = 255;
+
+ h = (90 + int(tempo));
+
+ while (h < 0)
+ h += 360;
+ while (h >= 360)
+ h -= 360;
+
+ return QColor(h, s, v, QColor::Hsv);
+}
+
+}
diff --git a/src/gui/rulers/TempoColour.h b/src/gui/rulers/TempoColour.h
new file mode 100644
index 0000000..be5e3fa
--- /dev/null
+++ b/src/gui/rulers/TempoColour.h
@@ -0,0 +1,60 @@
+
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_TEMPOCOLOUR_H_
+#define _RG_TEMPOCOLOUR_H_
+
+#include <qcolor.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class TempoColour
+{
+
+public:
+ TempoColour():m_tempo(0) {;}
+ TempoColour(double tempo):m_tempo(tempo) {;}
+
+ // Get the colour for a tempo
+ //
+ QColor getColour() { return getColour(m_tempo); }
+ static QColor getColour(double tempo);
+
+private:
+
+ double m_tempo;
+
+};
+
+
+}
+
+#endif
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 <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 "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"
diff --git a/src/gui/rulers/TempoRuler.h b/src/gui/rulers/TempoRuler.h
new file mode 100644
index 0000000..1d54e9d
--- /dev/null
+++ b/src/gui/rulers/TempoRuler.h
@@ -0,0 +1,180 @@
+
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_TEMPORULER_H_
+#define _RG_TEMPORULER_H_
+
+#include "gui/dialogs/TempoDialog.h"
+#include <kxmlguiclient.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qpixmap.h>
+#include <qsize.h>
+#include <qwidget.h>
+#include "base/Event.h"
+
+
+class QWheelEvent;
+class QPopupMenu;
+class QPaintEvent;
+class QMouseEvent;
+class QEvent;
+class KMainWindow;
+
+
+namespace Rosegarden
+{
+
+class TextFloat;
+class RulerScale;
+class RosegardenGUIDoc;
+class Composition;
+
+
+/**
+ * TempoRuler is a widget that shows a strip of tempo values at
+ * x-coordinates corresponding to tempo changes in a Composition.
+ */
+
+class TempoRuler : public QWidget, public KXMLGUIClient
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Construct a TempoRuler that displays and allows editing of the
+ * tempo changes found in the given Composition, with positions
+ * calculated by the given RulerScale.
+ *
+ * The RulerScale will not be destroyed along with the TempoRuler.
+ */
+ TempoRuler(RulerScale *rulerScale,
+ RosegardenGUIDoc *doc,
+ KMainWindow *parentMainWindow,
+ double xorigin = 0.0,
+ int height = 0,
+ bool small = false,
+ QWidget* parent = 0,
+ const char *name = 0);
+
+ ~TempoRuler();
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ void setMinimumWidth(int width) { m_width = width; }
+
+ void connectSignals();
+
+signals:
+ void doubleClicked(timeT);
+
+ void changeTempo(timeT, // tempo change time
+ tempoT, // tempo value
+ tempoT, // tempo target
+ TempoDialog::TempoDialogAction); // tempo action
+
+ void moveTempo(timeT, // old time
+ timeT); // new time
+
+ void deleteTempo(timeT);
+
+ void editTempo(timeT);
+ void editTimeSignature(timeT);
+ void editTempos(timeT);
+
+public slots:
+ void slotScrollHoriz(int x);
+
+protected slots:
+ void slotInsertTempoHere();
+ void slotInsertTempoAtPointer();
+ void slotDeleteTempoChange();
+ void slotRampToNext();
+ void slotUnramp();
+ void slotEditTempo();
+ void slotEditTimeSignature();
+ void slotEditTempos();
+
+protected:
+ virtual void paintEvent(QPaintEvent *);
+ virtual void enterEvent(QEvent *);
+ virtual void leaveEvent(QEvent *);
+ virtual void mousePressEvent(QMouseEvent *);
+ virtual void mouseReleaseEvent(QMouseEvent *);
+ virtual void mouseMoveEvent(QMouseEvent *);
+ virtual void wheelEvent(QWheelEvent *);
+
+ void createMenu();
+
+private:
+ double m_xorigin;
+ int m_height;
+ int m_currentXOffset;
+ int m_width;
+ bool m_small;
+ int m_illuminate;
+ bool m_illuminatePoint;
+ bool m_illuminateTarget;
+ bool m_refreshLinesOnly;
+
+ bool m_dragVert;
+ bool m_dragTarget;
+ bool m_dragHoriz;
+ int m_dragStartY;
+ int m_dragStartX;
+ bool m_dragFine;
+ int m_clickX;
+
+ timeT m_dragStartTime;
+ timeT m_dragPreviousTime;
+ tempoT m_dragStartTempo;
+ tempoT m_dragStartTarget;
+ tempoT m_dragOriginalTempo;
+ tempoT m_dragOriginalTarget;
+
+ int getYForTempo(tempoT tempo);
+ tempoT getTempoForY(int y);
+ void showTextFloat(tempoT tempo,
+ tempoT target = -1,
+ timeT time = -1,
+ bool showTime = false);
+
+ Composition *m_composition;
+ RulerScale *m_rulerScale;
+ TextFloat *m_textFloat;
+ QPopupMenu *m_menu;
+ KMainWindow *m_parentMainWindow;
+
+ QFont m_font;
+ QFont m_boldFont;
+ QFontMetrics m_fontMetrics;
+ QPixmap m_buffer;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/TextRuler.cpp b/src/gui/rulers/TextRuler.cpp
new file mode 100644
index 0000000..0acb3ea
--- /dev/null
+++ b/src/gui/rulers/TextRuler.cpp
@@ -0,0 +1,157 @@
+/* -*- 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 <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 "TextRuler.h"
+
+#include "base/Event.h"
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/NotationTypes.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "gui/general/GUIPalette.h"
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qpainter.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+TextRuler::TextRuler(RulerScale *rulerScale,
+ Segment *segment,
+ int height,
+ QWidget *parent,
+ const char *name)
+ : QWidget(parent, name),
+ m_height(height),
+ m_currentXOffset(0),
+ m_width( -1),
+ m_segment(segment),
+ m_rulerScale(rulerScale),
+ m_font("helvetica", 12),
+ m_fontMetrics(m_font)
+{
+ m_mySegmentMaybe = (m_segment->getComposition() != 0);
+ setBackgroundColor(GUIPalette::getColour(GUIPalette::TextRulerBackground));
+
+ m_font.setPixelSize(10);
+}
+
+TextRuler::~TextRuler()
+{
+ if (m_mySegmentMaybe && !m_segment->getComposition()) {
+ delete m_segment;
+ }
+}
+
+void
+TextRuler::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);
+ }
+}
+
+QSize
+TextRuler::sizeHint() const
+{
+ double width =
+ m_rulerScale->getBarPosition(m_rulerScale->getLastVisibleBar()) +
+ m_rulerScale->getBarWidth(m_rulerScale->getLastVisibleBar());
+
+ QSize res(std::max(int(width), m_width), m_height);
+
+ return res;
+}
+
+QSize
+TextRuler::minimumSizeHint() const
+{
+ double firstBarWidth = m_rulerScale->getBarWidth(0);
+ QSize res = QSize(int(firstBarWidth), m_height);
+ return res;
+}
+
+void
+TextRuler::paintEvent(QPaintEvent* e)
+{
+ QPainter paint(this);
+ paint.setPen(GUIPalette::getColour(GUIPalette::TextRulerForeground));
+
+ paint.setClipRegion(e->region());
+ paint.setClipRect(e->rect().normalize());
+
+ QRect clipRect = paint.clipRegion().boundingRect();
+
+ timeT from = m_rulerScale->getTimeForX
+ (clipRect.x() - m_currentXOffset - 100);
+ timeT to = m_rulerScale->getTimeForX
+ (clipRect.x() + clipRect.width() - m_currentXOffset + 100);
+
+ for (Segment::iterator i = m_segment->findTime(from);
+ i != m_segment->findTime(to) && i != m_segment->end(); ++i) {
+
+ if (!(*i)->isa(Text::EventType))
+ continue;
+
+ std::string text;
+ if (!(*i)->get
+ <String>(Text::TextPropertyName, text)) {
+ RG_DEBUG
+ << "Warning: TextRuler::paintEvent: No text in text event"
+ << endl;
+ continue;
+ }
+
+ QRect bounds = m_fontMetrics.boundingRect(strtoqstr(text));
+
+ double x = m_rulerScale->getXForTime((*i)->getAbsoluteTime()) +
+ m_currentXOffset - bounds.width() / 2;
+
+ int y = height() / 2 + bounds.height() / 2;
+
+ paint.drawText(static_cast<int>(x), y, strtoqstr(text));
+ }
+}
+
+}
+#include "TextRuler.moc"
diff --git a/src/gui/rulers/TextRuler.h b/src/gui/rulers/TextRuler.h
new file mode 100644
index 0000000..7d554cb
--- /dev/null
+++ b/src/gui/rulers/TextRuler.h
@@ -0,0 +1,112 @@
+
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_TEXTRULER_H_
+#define _RG_TEXTRULER_H_
+
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qsize.h>
+#include <qwidget.h>
+
+
+class QPaintEvent;
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class RulerScale;
+
+
+/**
+ * TextRuler is a widget that shows a strip of text strings at
+ * x-coordinates corresponding to specified times. The strings
+ * are obtained from a Segment, in which they are stored as
+ * text events. (The Segment does not have to be part of a
+ * Composition.)
+ *
+ * By design, this is more suitable for the display of single-purpose
+ * read-only data such as calculated chord names or (at a pinch)
+ * lyrics; it's not really suitable for displaying text data
+ * associated with a staff.
+ */
+
+class TextRuler : public QWidget
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Construct a TextRuler that displays the text events found
+ * in the given Segment at positions calculated by the given
+ * RulerScale.
+ *
+ * The Segment will be destroyed along with the TextRuler, if
+ * it was not in a Composition when the TextRuler was created
+ * and still is not when it's destroyed. If the Segment was
+ * found to be in a Composition on either occasion, it will be
+ * assumed to be someone else's responsibility.
+ *
+ * The RulerScale will not be destroyed along with the TextRuler.
+ */
+ TextRuler(RulerScale *rulerScale,
+ Segment *segment,
+ int height = 0,
+ QWidget* parent = 0,
+ const char *name = 0);
+
+ ~TextRuler();
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ void setMinimumWidth(int width) { m_width = width; }
+
+public slots:
+ void slotScrollHoriz(int x);
+
+protected:
+ virtual void paintEvent(QPaintEvent *);
+
+private:
+ int m_height;
+ int m_currentXOffset;
+ int m_width;
+
+ bool m_mySegmentMaybe;
+
+ Segment *m_segment;
+ RulerScale *m_rulerScale;
+
+ QFont m_font;
+ QFontMetrics m_fontMetrics;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/VelocityColour.cpp b/src/gui/rulers/VelocityColour.cpp
new file mode 100644
index 0000000..02a2f90
--- /dev/null
+++ b/src/gui/rulers/VelocityColour.cpp
@@ -0,0 +1,120 @@
+/* -*- 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 <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 "VelocityColour.h"
+
+#include <qcolor.h>
+
+// #include <cassert>
+
+
+namespace Rosegarden
+{
+
+VelocityColour::VelocityColour(const QColor &loud,
+ const QColor &medium,
+ const QColor &quiet,
+ int maxValue,
+ int loudKnee,
+ int mediumKnee,
+ int quietKnee):
+ m_loudColour(loud),
+ m_mediumColour(medium),
+ m_quietColour(quiet),
+ m_loudKnee(loudKnee),
+ m_mediumKnee(mediumKnee),
+ m_quietKnee(quietKnee),
+ m_maxValue(maxValue),
+ m_mixedColour(QColor(0, 0, 0)), // black as default
+ m_multiplyFactor(1000)
+{
+
+// assert(maxValue > loudKnee);
+// assert(loudKnee > mediumKnee);
+// assert(mediumKnee > quietKnee);
+
+ // These are the colours for the first band from Quiet to Medium.
+ // We use a multiplication factor to keep ourselves in the realms
+ // of integer arithmetic as we can potentially be doing a lot of
+ // these calculations when playing.
+ //
+ //
+ m_loStartRed = m_quietColour.red() * m_multiplyFactor;
+ m_loStartGreen = m_quietColour.green() * m_multiplyFactor;
+ m_loStartBlue = m_quietColour.blue() * m_multiplyFactor;
+
+ m_loStepRed = ( m_mediumColour.red() * m_multiplyFactor
+ - m_loStartRed ) / m_mediumKnee;
+ m_loStepGreen = ( m_mediumColour.green() * m_multiplyFactor
+ - m_loStartGreen ) / m_mediumKnee;
+ m_loStepBlue = ( m_mediumColour.blue() * m_multiplyFactor
+ - m_loStartBlue ) / m_mediumKnee;
+
+ m_hiStartRed = m_mediumColour.red() * m_multiplyFactor;
+ m_hiStartGreen = m_mediumColour.green() * m_multiplyFactor;
+ m_hiStartBlue = m_mediumColour.blue() * m_multiplyFactor;
+
+ m_hiStepRed = ( m_loudColour.red() * m_multiplyFactor
+ - m_hiStartRed ) / m_mediumKnee;
+ m_hiStepGreen = ( m_loudColour.green() * m_multiplyFactor
+ - m_hiStartGreen ) / m_mediumKnee;
+ m_hiStepBlue = ( m_loudColour.blue() * m_multiplyFactor
+ - m_hiStartBlue ) / m_mediumKnee;
+
+}
+
+VelocityColour::~VelocityColour()
+{}
+
+const QColor&
+VelocityColour::getColour(int value)
+{
+ if (value > m_maxValue)
+ value = m_maxValue;
+
+ if (value < m_quietKnee) {
+ return m_quietColour;
+ } else if (value < m_mediumKnee) {
+ m_mixedColour.setRgb(
+ ( m_loStartRed + m_loStepRed * value ) / m_multiplyFactor,
+ ( m_loStartGreen + m_loStepGreen * value ) / m_multiplyFactor,
+ ( m_loStartBlue + m_loStepBlue * value ) / m_multiplyFactor);
+
+ } else if (value >= m_mediumKnee < m_loudKnee) {
+ int mixFactor = value - m_mediumKnee;
+
+ m_mixedColour.setRgb(
+ ( m_hiStartRed + m_hiStepRed * mixFactor ) / m_multiplyFactor,
+ ( m_hiStartGreen + m_hiStepGreen * mixFactor ) / m_multiplyFactor,
+ ( m_hiStartBlue + m_hiStepBlue * mixFactor ) / m_multiplyFactor);
+ } else {
+ return m_loudColour;
+ return m_loudColour;
+ }
+
+ return m_mixedColour;
+}
+
+}
diff --git a/src/gui/rulers/VelocityColour.h b/src/gui/rulers/VelocityColour.h
new file mode 100644
index 0000000..0e555e1
--- /dev/null
+++ b/src/gui/rulers/VelocityColour.h
@@ -0,0 +1,106 @@
+
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_VELOCITYCOLOUR_H_
+#define _RG_VELOCITYCOLOUR_H_
+
+#include <qcolor.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * Returns a QColour according to a formula. We provide three colours
+ * to mix, a maximum value and three knees at which points the
+ * intermediate colours max out. Play around to your satisfaction.
+ */
+class VelocityColour
+{
+
+public:
+ VelocityColour(const QColor &loud,
+ const QColor &medium,
+ const QColor &quiet,
+ int maxValue,
+ int loudKnee,
+ int mediumKnee,
+ int quietKnee);
+ ~VelocityColour();
+
+ const QColor& getColour(int value);
+
+ int getLoudKnee() const { return m_loudKnee; }
+ int getMediumKnee() const { return m_mediumKnee; }
+ int getQuietKnee() const { return m_quietKnee; }
+
+ QColor getLoudColour() const { return m_loudColour; }
+ QColor getMediumColour() const { return m_mediumColour; }
+ QColor getQuietColour() const { return m_quietColour; }
+
+ int getMaxValue() const { return m_maxValue; }
+
+private:
+
+ QColor m_loudColour;
+ QColor m_mediumColour;
+ QColor m_quietColour;
+ int m_loudKnee;
+ int m_mediumKnee;
+ int m_quietKnee;
+ int m_maxValue;
+
+ // the mixed colour that we can return
+ QColor m_mixedColour;
+
+
+ int m_loStartRed;
+ int m_loStartGreen;
+ int m_loStartBlue;
+
+ int m_loStepRed;
+ int m_loStepGreen;
+ int m_loStepBlue;
+
+ int m_hiStartRed;
+ int m_hiStartGreen;
+ int m_hiStartBlue;
+
+ int m_hiStepRed;
+ int m_hiStepGreen;
+ int m_hiStepBlue;
+
+
+ int m_multiplyFactor;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/ViewElementAdapter.cpp b/src/gui/rulers/ViewElementAdapter.cpp
new file mode 100644
index 0000000..550ddcf
--- /dev/null
+++ b/src/gui/rulers/ViewElementAdapter.cpp
@@ -0,0 +1,56 @@
+/* -*- 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 <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 "ViewElementAdapter.h"
+
+namespace Rosegarden
+{
+
+ViewElementAdapter::ViewElementAdapter(ViewElement* el, const PropertyName& p)
+ : m_viewElement(el),
+ m_propertyName(p)
+{
+}
+
+bool ViewElementAdapter::getValue(long& val)
+{
+ return m_viewElement->event()->get<Rosegarden::Int>(m_propertyName, val);
+}
+
+void ViewElementAdapter::setValue(long val)
+{
+ m_viewElement->event()->set<Rosegarden::Int>(m_propertyName, val);
+}
+
+timeT ViewElementAdapter::getTime()
+{
+ return m_viewElement->getViewAbsoluteTime();
+}
+
+timeT ViewElementAdapter::getDuration()
+{
+ return m_viewElement->getViewDuration();
+}
+
+}
diff --git a/src/gui/rulers/ViewElementAdapter.h b/src/gui/rulers/ViewElementAdapter.h
new file mode 100644
index 0000000..1207522
--- /dev/null
+++ b/src/gui/rulers/ViewElementAdapter.h
@@ -0,0 +1,59 @@
+/* -*- 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 <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.
+*/
+
+#ifndef _RG_VIEWELEMENTADAPTER_H_
+#define _RG_VIEWELEMENTADAPTER_H_
+
+#include "ElementAdapter.h"
+#include "base/ViewElement.h"
+
+namespace Rosegarden
+{
+
+class Event;
+
+class ViewElementAdapter : public ElementAdapter
+{
+public:
+ ViewElementAdapter(ViewElement*, const PropertyName&);
+
+ virtual bool getValue(long&);
+ virtual void setValue(long);
+ virtual timeT getTime();
+ virtual timeT getDuration();
+
+ virtual Event* getEvent() { return m_viewElement->event(); }
+ ViewElement* getViewElement() { return m_viewElement; }
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ ViewElement* m_viewElement;
+ const PropertyName& m_propertyName;
+};
+
+}
+
+#endif /*VIEWELEMENTADAPTER_H_*/