# HG changeset patch # User Chris Cannam # Date 1138119658 0 # Node ID 73d85d19919f378b7c4313f9aa32dac7d619cd19 # Parent 214054a0d8b84b6bed162181911b3eb07386f58a * 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. diff -r 214054a0d8b8 -r 73d85d19919f base/Selection.cpp --- 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); } diff -r 214054a0d8b8 -r 73d85d19919f base/Selection.h --- 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; diff -r 214054a0d8b8 -r 73d85d19919f base/View.cpp --- 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 { diff -r 214054a0d8b8 -r 73d85d19919f base/View.h --- 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 LayerList; @@ -220,6 +222,7 @@ QPixmap *m_cache; size_t m_cacheCentreFrame; int m_cacheZoomLevel; + bool m_selectionCached; bool m_deleting; diff -r 214054a0d8b8 -r 73d85d19919f base/ViewManager.cpp --- 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) { diff -r 214054a0d8b8 -r 73d85d19919f base/ViewManager.h --- 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 m_playParameters; };