diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 18:37:05 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 18:37:05 +0000 |
commit | 145364a8af6a1fec06556221e66d4b724a62fc9a (patch) | |
tree | 53bd71a544008c518034f208d64c932dc2883f50 /src/gui/rulers | |
download | rosegarden-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')
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_*/ |