# HG changeset patch # User Chris Cannam # Date 1138035777 0 # Node ID 214054a0d8b84b6bed162181911b3eb07386f58a # Parent 49a95b1740504ba9afaa6f5b1c18955afefdc3ae * Hook up tool selection buttons to switch the cursor mode * Implement simple and multi-selection, snapping to the resolution of the current layer. You can't actually do anything with a selection yet diff -r 49a95b174050 -r 214054a0d8b8 base/Layer.h --- a/base/Layer.h Thu Jan 19 17:59:11 2006 +0000 +++ b/base/Layer.h Mon Jan 23 17:02:57 2006 +0000 @@ -60,12 +60,50 @@ virtual int getVerticalScaleWidth(QPainter &) const { return 0; } virtual void paintVerticalScale(QPainter &, QRect) const { } + //!!! I don't like these. The layer should return a structured + //string-based description list and the pane should render it + //itself. + virtual QRect getFeatureDescriptionRect(QPainter &, QPoint) const { return QRect(0, 0, 0, 0); } virtual void paintLocalFeatureDescription(QPainter &, QRect, QPoint) const { } + //!!! We also need a method (like the vertical scale method) for + //drawing additional scales like a colour scale. That is, unless + //all applicable layers can actually do this from + //paintVerticalScale as well? + + // Select mode: + // + //!!! Next, a method that gets the frame of the nearest feature in + //a particular snap direction. This would be used for selection + //mode, where we're selecting from the waveform based on feature + //location. Do we need multi-select on features as well? This is + //an issue; if you select a feature are you selecting that feature + //(in which case what do you do with it?) or a section of the + //underlying waveform? + + virtual int getNearestFeatureFrame(int frame, + size_t &resolution, + bool snapRight = true) const { + resolution = 1; + return frame; + } + + // Paint and edit modes: + // + // Layer needs to get actual mouse events, I guess. Paint mode is + // probably the easier. + + // Text mode: + // + // Label nearest feature. We need to get the feature coordinates + // and current label from the layer, and then the pane can pop up + // a little text entry dialog at the right location. Or we edit + // in place? Probably the dialog is easier. + /** * This should return true if the view can safely be scrolled * automatically by the widget (simply copying the existing data diff -r 49a95b174050 -r 214054a0d8b8 base/Selection.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/Selection.cpp Mon Jan 23 17:02:57 2006 +0000 @@ -0,0 +1,79 @@ +/* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */ + +/* + A waveform viewer and audio annotation editor. + Chris Cannam, Queen Mary University of London, 2005-2006 + + This is experimental software. Not for distribution. +*/ + +#include "Selection.h" + +Selection::Selection() : + m_startFrame(0), + m_endFrame(0) +{ +} + +Selection::Selection(size_t startFrame, size_t endFrame) : + m_startFrame(startFrame), + m_endFrame(endFrame) +{ + if (m_startFrame > m_endFrame) { + size_t tmp = m_endFrame; + m_endFrame = m_startFrame; + m_startFrame = tmp; + } +} + +Selection::Selection(const Selection &s) : + m_startFrame(s.m_startFrame), + m_endFrame(s.m_endFrame) +{ +} + +Selection & +Selection::operator=(const Selection &s) +{ + if (this != &s) { + m_startFrame = s.m_startFrame; + m_endFrame = s.m_endFrame; + } + return *this; +} + +Selection::~Selection() +{ +} + +bool +Selection::isEmpty() const +{ + return m_startFrame == m_endFrame; +} + +size_t +Selection::getStartFrame() const +{ + return m_startFrame; +} + +size_t +Selection::getEndFrame() const +{ + return m_endFrame; +} + +bool +Selection::operator<(const Selection &s) const +{ + return (m_startFrame < s.m_startFrame); +} + +bool +Selection::operator==(const Selection &s) const +{ + return (m_startFrame == s.m_startFrame && + m_endFrame == s.m_endFrame); +} + diff -r 49a95b174050 -r 214054a0d8b8 base/Selection.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/Selection.h Mon Jan 23 17:02:57 2006 +0000 @@ -0,0 +1,36 @@ +/* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */ + +/* + A waveform viewer and audio annotation editor. + Chris Cannam, Queen Mary University of London, 2005-2006 + + This is experimental software. Not for distribution. +*/ + +#ifndef _SELECTION_H_ +#define _SELECTION_H_ + +#include + +class Selection +{ +public: + Selection(); + Selection(size_t startFrame, size_t endFrame); + Selection(const Selection &); + Selection &operator=(const Selection &); + virtual ~Selection(); + + bool isEmpty() const; + size_t getStartFrame() const; + size_t getEndFrame() const; + + bool operator<(const Selection &) const; + bool operator==(const Selection &) const; + +protected: + size_t m_startFrame; + size_t m_endFrame; +}; + +#endif diff -r 49a95b174050 -r 214054a0d8b8 base/View.cpp --- a/base/View.cpp Thu Jan 19 17:59:11 2006 +0000 +++ b/base/View.cpp Mon Jan 23 17:02:57 2006 +0000 @@ -8,7 +8,6 @@ */ #include "base/View.h" -#include "base/ViewManager.h" #include "base/Layer.h" #include "base/Model.h" #include "base/ZoomConstraint.h" @@ -42,6 +41,7 @@ m_cacheCentreFrame(0), m_cacheZoomLevel(1024), m_deleting(false), + m_haveSelectedLayer(false), m_manager(0) { // QWidget::setAttribute(Qt::WA_PaintOnScreen); @@ -140,7 +140,13 @@ void View::propertyContainerSelected(PropertyContainer *pc) { - if (pc == this) return; + if (pc == this) { + if (m_haveSelectedLayer) { + m_haveSelectedLayer = false; + update(); + } + return; + } delete m_cache; m_cache = 0; @@ -156,11 +162,20 @@ } if (selectedLayer) { + m_haveSelectedLayer = true; m_layers.push_back(selectedLayer); update(); + } else { + m_haveSelectedLayer = false; } } +void +View::toolModeChanged() +{ + std::cerr << "View::toolModeChanged(" << m_manager->getToolMode() << ")" << std::endl; +} + long View::getStartFrame() const { @@ -280,6 +295,16 @@ emit propertyContainerRemoved(layer); } +Layer * +View::getSelectedLayer() +{ + if (m_haveSelectedLayer && !m_layers.empty()) { + return getLayer(getLayerCount() - 1); + } else { + return 0; + } +} + void View::setViewManager(ViewManager *manager) { @@ -288,6 +313,8 @@ m_manager->disconnect(this, SLOT(viewManagerZoomLevelChanged(void *, unsigned long, bool))); disconnect(m_manager, SIGNAL(centreFrameChanged(void *, unsigned long, bool))); disconnect(m_manager, SIGNAL(zoomLevelChanged(void *, unsigned long, bool))); + disconnect(m_manager, SIGNAL(toolModeChanged())); + disconnect(m_manager, SIGNAL(selectionChanged())); } m_manager = manager; @@ -300,11 +327,17 @@ this, SLOT(viewManagerPlaybackFrameChanged(unsigned long))); connect(m_manager, SIGNAL(zoomLevelChanged(void *, unsigned long, bool)), this, SLOT(viewManagerZoomLevelChanged(void *, unsigned long, bool))); + connect(m_manager, SIGNAL(toolModeChanged()), + this, SLOT(toolModeChanged())); + connect(m_manager, SIGNAL(selectionChanged()), + this, SLOT(update())); connect(this, SIGNAL(centreFrameChanged(void *, unsigned long, bool)), m_manager, SIGNAL(centreFrameChanged(void *, unsigned long, bool))); connect(this, SIGNAL(zoomLevelChanged(void *, unsigned long, bool)), m_manager, SIGNAL(zoomLevelChanged(void *, unsigned long, bool))); + + toolModeChanged(); } void @@ -886,7 +919,10 @@ paint.setBrush(Qt::NoBrush); for (LayerList::iterator i = scrollables.begin(); i != scrollables.end(); ++i) { + paint.setRenderHint(QPainter::Antialiasing, false); + paint.save(); (*i)->paint(paint, cacheRect); + paint.restore(); } paint.end(); @@ -926,6 +962,42 @@ (*i)->paint(paint, nonCacheRect); } + ViewManager::SelectionList selections; + + if (m_manager) { + selections = m_manager->getSelections(); + if (m_manager->haveInProgressSelection()) { + bool exclusive; + Selection inProgressSelection = + m_manager->getInProgressSelection(exclusive); + if (exclusive) selections.clear(); + selections.insert(inProgressSelection); + } + } + + paint.setPen(QColor(150, 150, 255)); + paint.setBrush(QColor(150, 150, 255, 80)); + + for (ViewManager::SelectionList::iterator i = selections.begin(); + i != selections.end(); ++i) { + + int p0 = -1, p1 = -1; + + if (int(i->getStartFrame()) >= getStartFrame()) { + p0 = (i->getStartFrame() - getStartFrame()) / m_zoomLevel; + } + + if (int(i->getEndFrame()) >= getStartFrame()) { + p1 = (i->getEndFrame() - getStartFrame()) / m_zoomLevel; + } + + if (p0 == -1 && p1 == -1) continue; + + if (p1 > width()) p1 = width() + 1; + + paint.drawRect(p0, -1, p1 - p0, height() + 1); + } + paint.end(); if (m_followPlay != PlaybackScrollContinuous) { diff -r 49a95b174050 -r 214054a0d8b8 base/View.h --- a/base/View.h Thu Jan 19 17:59:11 2006 +0000 +++ b/base/View.h Mon Jan 23 17:02:57 2006 +0000 @@ -15,10 +15,10 @@ #include "base/ZoomConstraint.h" #include "base/PropertyContainer.h" +#include "base/ViewManager.h" #include "base/XmlExportable.h" class Layer; -class ViewManager; #include @@ -111,6 +111,15 @@ */ virtual Layer *getLayer(int n) { return m_layers[n]; } + /** + * Return the layer last selected by the user. This is normally + * the top layer, the same as getLayer(getLayerCount()-1). + * However, if the user has selected the pane itself more recently + * than any of the layers on it, this function will return 0. It + * will also return 0 if there are no layers. + */ + virtual Layer *getSelectedLayer(); + virtual void setViewManager(ViewManager *m); virtual void setFollowGlobalPan(bool f); @@ -176,6 +185,8 @@ virtual void propertyContainerSelected(PropertyContainer *pc); + virtual void toolModeChanged(); + protected: View(QWidget *, bool showProgress); virtual void paintEvent(QPaintEvent *e); @@ -213,6 +224,7 @@ bool m_deleting; LayerList m_layers; // I don't own these, but see dtor note above + bool m_haveSelectedLayer; // caches for use in getScrollableBackLayers, getNonScrollableFrontLayers mutable LayerList m_lastScrollableBackLayers; diff -r 49a95b174050 -r 214054a0d8b8 base/ViewManager.cpp --- a/base/ViewManager.cpp Thu Jan 19 17:59:11 2006 +0000 +++ b/base/ViewManager.cpp Mon Jan 23 17:02:57 2006 +0000 @@ -21,7 +21,9 @@ m_globalCentreFrame(0), m_globalZoom(1024), m_lastLeft(0), - m_lastRight(0) + m_lastRight(0), + m_inProgressExclusive(true), + m_toolMode(NavigateMode) { connect(this, SIGNAL(centreFrameChanged(void *, unsigned long, bool)), @@ -50,6 +52,107 @@ return m_globalZoom; } +bool +ViewManager::haveInProgressSelection() const +{ + return !m_inProgressSelection.isEmpty(); +} + +const Selection & +ViewManager::getInProgressSelection(bool &exclusive) const +{ + exclusive = m_inProgressExclusive; + return m_inProgressSelection; +} + +void +ViewManager::setInProgressSelection(const Selection &selection, bool exclusive) +{ + m_inProgressExclusive = exclusive; + m_inProgressSelection = selection; + if (exclusive) clearSelections(); + emit selectionChanged(); +} + +void +ViewManager::clearInProgressSelection() +{ + m_inProgressSelection = Selection(); + emit selectionChanged(); +} + +const ViewManager::SelectionList & +ViewManager::getSelections() const +{ + return m_selections; +} + +void +ViewManager::setSelection(const Selection &selection) +{ + clearSelections(); + addSelection(selection); +} + +void +ViewManager::addSelection(const Selection &selection) +{ + m_selections.insert(selection); + + // Cope with a sitation where the new selection overlaps one or + // more existing ones. This is a terribly inefficient way to do + // this, but that probably isn't significant in real life. + + for (SelectionList::iterator i = m_selections.begin(); + i != m_selections.end(); ) { + + SelectionList::iterator j = i; + if (++j == m_selections.end()) break; + + if (i->getEndFrame() >= j->getStartFrame()) { + Selection merged(i->getStartFrame(), + std::max(i->getEndFrame(), j->getEndFrame())); + m_selections.erase(i); + m_selections.erase(j); + m_selections.insert(merged); + i = m_selections.begin(); + } else { + ++i; + } + } + + emit selectionChanged(); +} + +void +ViewManager::removeSelection(const Selection &selection) +{ + //!!! Likewise this needs to cope correctly with the situation + //where selection is not one of the original selection set but + //simply overlaps one of them (cutting down the original selection + //appropriately) + + m_selections.erase(selection); + + emit selectionChanged(); +} + +void +ViewManager::clearSelections() +{ + m_selections.clear(); + + emit selectionChanged(); +} + +void +ViewManager::setToolMode(ToolMode mode) +{ + m_toolMode = mode; + + emit toolModeChanged(); +} + void ViewManager::setAudioPlaySource(AudioPlaySource *source) { @@ -119,6 +222,12 @@ } } +bool +ViewManager::isPlaying() const +{ + return m_playSource && m_playSource->isPlaying(); +} + void ViewManager::considerSeek(void *p, unsigned long f, bool locked) { diff -r 49a95b174050 -r 214054a0d8b8 base/ViewManager.h --- a/base/ViewManager.h Thu Jan 19 17:59:11 2006 +0000 +++ b/base/ViewManager.h Mon Jan 23 17:02:57 2006 +0000 @@ -14,6 +14,9 @@ #include #include +#include + +#include "Selection.h" class AudioPlaySource; class PlayParameters; @@ -42,9 +45,34 @@ PlayParameters *getPlayParameters(const Model *model); void clearPlayParameters(); + bool isPlaying() const; + unsigned long getGlobalCentreFrame() const; unsigned long getGlobalZoom() const; + typedef std::set SelectionList; + + bool haveInProgressSelection() const; + const Selection &getInProgressSelection(bool &exclusive) const; + void setInProgressSelection(const Selection &selection, bool exclusive); + void clearInProgressSelection(); + + const SelectionList &getSelections() const; + void setSelection(const Selection &selection); + void addSelection(const Selection &selection); + void removeSelection(const Selection &selection); + void clearSelections(); + + enum ToolMode { + NavigateMode, + SelectMode, + EditMode, + DrawMode, + TextMode + }; + ToolMode getToolMode() const { return m_toolMode; } + void setToolMode(ToolMode mode); + signals: /** Emitted when a widget pans. The originator identifies the widget. */ void centreFrameChanged(void *originator, unsigned long frame, bool locked); @@ -58,6 +86,12 @@ /** Emitted when the output levels change. Values in range 0.0 -> 1.0. */ void outputLevelsChanged(float left, float right); + /** Emitted when the selection has changed. */ + void selectionChanged(); + + /** Emitted when the tool mode has been changed. */ + void toolModeChanged(); + protected slots: void checkPlayStatus(); void considerSeek(void *, unsigned long, bool); @@ -71,6 +105,12 @@ float m_lastLeft; float m_lastRight; + SelectionList m_selections; + Selection m_inProgressSelection; + bool m_inProgressExclusive; + + ToolMode m_toolMode; + std::map m_playParameters; };