changeset 9:73d85d19919f

* Add play-selection and looping modes. Looping seems to work OK, but the plain play-selection is miscalculating current frame number to feed back to the GUI. * Cache selection rectanges wherever possible in View::paintEvent.
author Chris Cannam
date Tue, 24 Jan 2006 16:20:58 +0000
parents 214054a0d8b8
children ec6886f0e673
files base/Selection.cpp base/Selection.h base/View.cpp base/View.h base/ViewManager.cpp base/ViewManager.h
diffstat 6 files changed, 174 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/base/Selection.cpp	Mon Jan 23 17:02:57 2006 +0000
+++ b/base/Selection.cpp	Tue Jan 24 16:20:58 2006 +0000
@@ -65,14 +65,28 @@
 }
 
 bool
+Selection::contains(size_t frame) const
+{
+    return (frame >= m_startFrame) && (frame < m_endFrame);
+}
+
+bool
 Selection::operator<(const Selection &s) const
 {
-    return (m_startFrame < s.m_startFrame);
+    if (isEmpty()) {
+	if (s.isEmpty()) return false;
+	else return true;
+    } else {
+	if (s.isEmpty()) return false;
+	else return (m_startFrame < s.m_startFrame);
+    }
 }
 
 bool
 Selection::operator==(const Selection &s) const
 {
+    if (isEmpty()) return s.isEmpty();
+
     return (m_startFrame == s.m_startFrame &&
 	    m_endFrame == s.m_endFrame);
 }
--- a/base/Selection.h	Mon Jan 23 17:02:57 2006 +0000
+++ b/base/Selection.h	Tue Jan 24 16:20:58 2006 +0000
@@ -24,6 +24,7 @@
     bool isEmpty() const;
     size_t getStartFrame() const;
     size_t getEndFrame() const;
+    bool contains(size_t frame) const;
 
     bool operator<(const Selection &) const;
     bool operator==(const Selection &) const;
--- a/base/View.cpp	Mon Jan 23 17:02:57 2006 +0000
+++ b/base/View.cpp	Tue Jan 24 16:20:58 2006 +0000
@@ -40,6 +40,7 @@
     m_cache(0),
     m_cacheCentreFrame(0),
     m_cacheZoomLevel(1024),
+    m_selectionCached(false),
     m_deleting(false),
     m_haveSelectedLayer(false),
     m_manager(0)
@@ -315,6 +316,7 @@
 	disconnect(m_manager, SIGNAL(zoomLevelChanged(void *, unsigned long, bool)));
 	disconnect(m_manager, SIGNAL(toolModeChanged()));
 	disconnect(m_manager, SIGNAL(selectionChanged()));
+	disconnect(m_manager, SIGNAL(inProgressSelectionChanged()));
     }
 
     m_manager = manager;
@@ -330,7 +332,9 @@
     connect(m_manager, SIGNAL(toolModeChanged()),
 	    this, SLOT(toolModeChanged()));
     connect(m_manager, SIGNAL(selectionChanged()),
-	    this, SLOT(update()));
+	    this, SLOT(selectionChanged()));
+    connect(m_manager, SIGNAL(inProgressSelectionChanged()),
+	    this, SLOT(selectionChanged()));
 
     connect(this, SIGNAL(centreFrameChanged(void *, unsigned long, bool)),
 	    m_manager, SIGNAL(centreFrameChanged(void *, unsigned long, bool)));
@@ -529,6 +533,17 @@
     }
 }
 
+void
+View::selectionChanged()
+{
+    if (m_selectionCached) {
+	delete m_cache;
+	m_cache = 0;
+	m_selectionCached = false;
+    }
+    update();
+}
+
 size_t
 View::getModelsStartFrame() const
 {
@@ -799,16 +814,23 @@
     bool layersChanged = false;
     LayerList scrollables = getScrollableBackLayers(layersChanged);
     LayerList nonScrollables = getNonScrollableFrontLayers(layersChanged);
+    bool selectionCacheable = nonScrollables.empty();
+    bool haveSelections = m_manager && !m_manager->getSelections().empty();
+    bool selectionDrawn = false;
 
 #ifdef DEBUG_VIEW_WIDGET_PAINT
     std::cerr << "View(" << this << ")::paintEvent: have " << scrollables.size()
 	      << " scrollable back layers and " << nonScrollables.size()
 	      << " non-scrollable front layers" << std::endl;
+    std::cerr << "haveSelections " << haveSelections << ", selectionCacheable "
+	      << selectionCacheable << ", m_selectionCached " << m_selectionCached << std::endl;
 #endif
 
-    if (layersChanged || scrollables.empty()) {
+    if (layersChanged || scrollables.empty() ||
+	(haveSelections && (selectionCacheable != m_selectionCached))) {
 	delete m_cache;
 	m_cache = 0;
+	m_selectionCached = false;
     }
 
     if (!scrollables.empty()) {
@@ -924,6 +946,12 @@
 	    (*i)->paint(paint, cacheRect);
 	    paint.restore();
 	}
+
+	if (haveSelections && selectionCacheable) {
+	    drawSelections(paint);
+	    m_selectionCached = repaintCache;
+	    selectionDrawn = true;
+	}
 	
 	paint.end();
 
@@ -962,42 +990,13 @@
 	(*i)->paint(paint, nonCacheRect);
     }
 	
-    ViewManager::SelectionList selections;
+    paint.end();
 
-    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.begin(this);
+    if (e) paint.setClipRect(e->rect());
+    if (!m_selectionCached) {
+	drawSelections(paint);
     }
-
-    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) {
@@ -1025,6 +1024,53 @@
     QFrame::paintEvent(e);
 }
 
+void
+View::drawSelections(QPainter &paint)
+{
+    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.save();
+    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;
+
+#ifdef DEBUG_VIEW_WIDGET_PAINT
+	std::cerr << "View::drawSelections: " << p0 << ",-1 [" << (p1-p0) << "x" << (height()+1) << "]" << std::endl;
+#endif
+
+	paint.drawRect(p0, -1, p1 - p0, height() + 1);
+    }
+
+    paint.restore();
+}
+
 QString
 View::toXmlString(QString indent, QString extraAttributes) const
 {
--- a/base/View.h	Mon Jan 23 17:02:57 2006 +0000
+++ b/base/View.h	Tue Jan 24 16:20:58 2006 +0000
@@ -185,11 +185,13 @@
 
     virtual void propertyContainerSelected(PropertyContainer *pc);
 
+    virtual void selectionChanged();
     virtual void toolModeChanged();
 
 protected:
     View(QWidget *, bool showProgress);
     virtual void paintEvent(QPaintEvent *e);
+    virtual void drawSelections(QPainter &);
 
     typedef std::vector<Layer *> LayerList;
 
@@ -220,6 +222,7 @@
     QPixmap            *m_cache;
     size_t              m_cacheCentreFrame;
     int                 m_cacheZoomLevel;
+    bool                m_selectionCached;
 
     bool                m_deleting;
 
--- a/base/ViewManager.cpp	Mon Jan 23 17:02:57 2006 +0000
+++ b/base/ViewManager.cpp	Tue Jan 24 16:20:58 2006 +0000
@@ -23,7 +23,9 @@
     m_lastLeft(0), 
     m_lastRight(0),
     m_inProgressExclusive(true),
-    m_toolMode(NavigateMode)
+    m_toolMode(NavigateMode),
+    m_playLoopMode(false),
+    m_playSelectionMode(true)
 {
     connect(this, 
 	    SIGNAL(centreFrameChanged(void *, unsigned long, bool)),
@@ -71,14 +73,14 @@
     m_inProgressExclusive = exclusive;
     m_inProgressSelection = selection;
     if (exclusive) clearSelections();
-    emit selectionChanged();
+    emit inProgressSelectionChanged();
 }
 
 void
 ViewManager::clearInProgressSelection()
 {
     m_inProgressSelection = Selection();
-    emit selectionChanged();
+    emit inProgressSelectionChanged();
 }
 
 const ViewManager::SelectionList &
@@ -103,7 +105,11 @@
     // 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();
+    // It's essential for the correct operation of
+    // getContainingSelection that the selections do not overlap, so
+    // this is not just a frill.
+
+    for (SelectionList::iterator i = m_selections.begin();
 	 i != m_selections.end(); ) {
 	
 	SelectionList::iterator j = i;
@@ -145,6 +151,27 @@
     emit selectionChanged();
 }
 
+Selection
+ViewManager::getContainingSelection(size_t frame, bool defaultToFollowing)
+{
+    // This scales very badly with the number of selections, but it's
+    // more efficient for very small numbers of selections than a more
+    // scalable method, and I think that may be what we need
+
+    for (SelectionList::const_iterator i = m_selections.begin();
+	 i != m_selections.end(); ++i) {
+
+	if (i->contains(frame)) return *i;
+
+	if (i->getStartFrame() > frame) {
+	    if (defaultToFollowing) return *i;
+	    else return Selection();
+	}
+    }
+
+    return Selection();
+}
+
 void
 ViewManager::setToolMode(ToolMode mode)
 {
@@ -154,6 +181,22 @@
 }
 
 void
+ViewManager::setPlayLoopMode(bool mode)
+{
+    m_playLoopMode = mode;
+
+    emit playLoopModeChanged();
+}
+
+void
+ViewManager::setPlaySelectionMode(bool mode)
+{
+    m_playSelectionMode = mode;
+
+    emit playSelectionModeChanged();
+}
+
+void
 ViewManager::setAudioPlaySource(AudioPlaySource *source)
 {
     if (!m_playSource) {
--- a/base/ViewManager.h	Mon Jan 23 17:02:57 2006 +0000
+++ b/base/ViewManager.h	Tue Jan 24 16:20:58 2006 +0000
@@ -63,6 +63,14 @@
     void removeSelection(const Selection &selection);
     void clearSelections();
 
+    /**
+     * Return the selection that contains a given frame.
+     * If defaultToFollowing is true, and if the frame is not in a
+     * selected area, return the next selection after the given frame.
+     * Return the empty selection if no appropriate selection is found.
+     */
+    Selection getContainingSelection(size_t frame, bool defaultToFollowing);
+
     enum ToolMode {
 	NavigateMode,
 	SelectMode,
@@ -73,6 +81,12 @@
     ToolMode getToolMode() const { return m_toolMode; }
     void setToolMode(ToolMode mode);
 
+    bool getPlayLoopMode() const { return m_playLoopMode; }
+    void setPlayLoopMode(bool on);
+
+    bool getPlaySelectionMode() const { return m_playSelectionMode; }
+    void setPlaySelectionMode(bool on);
+
 signals:
     /** Emitted when a widget pans.  The originator identifies the widget. */
     void centreFrameChanged(void *originator, unsigned long frame, bool locked);
@@ -89,9 +103,18 @@
     /** Emitted when the selection has changed. */
     void selectionChanged();
 
+    /** Emitted when the in-progress (rubberbanding) selection has changed. */
+    void inProgressSelectionChanged();
+
     /** Emitted when the tool mode has been changed. */
     void toolModeChanged();
 
+    /** Emitted when the play loop mode has been changed. */
+    void playLoopModeChanged();
+
+    /** Emitted when the play selection mode has been changed. */
+    void playSelectionModeChanged();
+
 protected slots:
     void checkPlayStatus();
     void considerSeek(void *, unsigned long, bool);
@@ -111,6 +134,9 @@
 
     ToolMode m_toolMode;
 
+    bool m_playLoopMode;
+    bool m_playSelectionMode;
+
     std::map<const Model *, PlayParameters *> m_playParameters;
 };