From 588c050bbb4b5779b69778855626f26d94c6b756 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 1 Nov 2018 11:13:13 -0400 Subject: [PATCH] Define visitation procedure skeleton for subdivision of CellularPanel --- src/CellularPanel.cpp | 116 ++++++++++++++++++++++++++++++++++++++++++ src/CellularPanel.h | 28 ++++++++++ 2 files changed, 144 insertions(+) diff --git a/src/CellularPanel.cpp b/src/CellularPanel.cpp index 31c3254d6..3614dc021 100644 --- a/src/CellularPanel.cpp +++ b/src/CellularPanel.cpp @@ -860,6 +860,122 @@ void CellularPanel::OnKillFocus(wxFocusEvent & WXUNUSED(event)) Refresh( false); } +// Empty out-of-line default functions to fill Visitor's vtable +CellularPanel::Visitor::~Visitor() {} +void CellularPanel::Visitor::VisitCell( + const wxRect &rect, TrackPanelCell &cell ) {} +void CellularPanel::Visitor::BeginGroup( + const wxRect &rect, TrackPanelGroup &group ) {} +void CellularPanel::Visitor::EndGroup( + const wxRect &rect, TrackPanelGroup &group ) {} + +// Public, top-level entry for generalized Visit +void CellularPanel::Visit( Visitor &visitor ) +{ + Visit( GetClientRect(), Root(), visitor ); +} + +// Common utility class for the functions that follow +namespace { + struct Adaptor : CellularPanel::Visitor { + using SimpleCellVisitor = CellularPanel::SimpleCellVisitor; + using SimpleNodeVisitor = CellularPanel::SimpleNodeVisitor; + + // Visit cells only + Adaptor( const SimpleCellVisitor& function_ ) + : function{ [&](const wxRect &rect, TrackPanelNode &cell) { + return function_( rect, static_cast(cell) ); + } } + {} + + // Visit cells and groups, each once only, choosing pre- or post- ordering + // for the groups + Adaptor( const SimpleNodeVisitor &function_, bool pre_ ) + : function{ function_ }, pre{ pre_ }, post{ ! pre_ } {} + + void VisitCell( const wxRect &rect, TrackPanelCell &cell ) override + { return function( rect, cell ); } + void BeginGroup( const wxRect &rect, TrackPanelGroup &group ) override + { if (pre) return function( rect, group ); } + void EndGroup( const wxRect &rect, TrackPanelGroup &group ) override + { if (post) return function( rect, group ); } + + SimpleNodeVisitor function; + const bool pre{ false }, post{ false }; + }; +} + +// Simplified entry points for visits that don't need all the generality of +// CellularPanel::Visitor +void CellularPanel::VisitCells( const SimpleCellVisitor &visitor ) +{ + Adaptor adaptor{ visitor }; + Visit( adaptor ); +} + +void CellularPanel::VisitPreorder( const SimpleNodeVisitor &visitor ) +{ + Adaptor adaptor{ visitor, true }; + Visit( adaptor ); +} + +void CellularPanel::VisitPostorder( const SimpleNodeVisitor &visitor ) +{ + Adaptor adaptor{ visitor, false }; + Visit( adaptor ); +} + +namespace { + wxRect Subdivide( + const wxRect &rect, bool divideX, + const TrackPanelGroup::Refinement &children, + const TrackPanelGroup::Refinement::const_iterator iter) + { + const auto lowerBound = (divideX ? rect.GetLeft() : rect.GetTop()); + const auto upperBound = (divideX ? rect.GetRight() : rect.GetBottom()); + const auto next = iter + 1; + const auto end = children.end(); + const auto nextCoord = ((next == end) ? upperBound : next->first - 1); + + // Some defense against bad overrides of TrackPanelGroup::Children + auto lesser = std::max(lowerBound, std::min(upperBound, iter->first)); + auto greater = std::max(lesser, std::min(upperBound, nextCoord)); + + auto result = rect; + if (divideX) + result.SetLeft(lesser), result.SetRight(greater); + else + result.SetTop(lesser), result.SetBottom(greater); + + return result; + }; +} + +// Private, recursive implementation function of Visit +void CellularPanel::Visit( + const wxRect &rect, const std::shared_ptr &node, + Visitor &visitor ) +{ + if (auto pCell = dynamic_cast(node.get())) + visitor.VisitCell( rect, *pCell ); + else if (auto pGroup = dynamic_cast(node.get())) { + visitor.BeginGroup( rect, *pGroup ); + + // Recur on children + const auto results = pGroup->Children( rect ); + const bool divideX = results.first == TrackPanelGroup::Axis::X; + const auto &children = results.second; + const auto begin = children.begin(), end = children.end(); + for (auto iter = begin; iter != end; ++iter) + Visit( + Subdivide(rect, divideX, children, iter), iter->second, visitor ); + + visitor.EndGroup( rect, *pGroup ); + } + else + return; +} + UIHandlePtr CellularPanel::Target() { auto &state = *mState; diff --git a/src/CellularPanel.h b/src/CellularPanel.h index 19b96e32f..f5c7595dc 100644 --- a/src/CellularPanel.h +++ b/src/CellularPanel.h @@ -18,6 +18,7 @@ class ViewInfo; class AudacityProject; class TrackPanelCell; +class TrackPanelGroup; class TrackPanelNode; struct TrackPanelMouseEvent; struct TrackPanelMouseState; @@ -55,6 +56,29 @@ public: virtual FoundCell FindCell(int mouseX, int mouseY) = 0; virtual wxRect FindRect(const TrackPanelCell &cell) = 0; + + // Structure and functions for generalized visitation of the subdivision + struct Visitor { + virtual ~Visitor(); + virtual void VisitCell( const wxRect &rect, TrackPanelCell &cell ); + virtual void BeginGroup( const wxRect &rect, TrackPanelGroup &group ); + virtual void EndGroup( const wxRect &rect, TrackPanelGroup &group ); + }; + + // Most general visit + void Visit( Visitor &visitor ); + + // Easier visit when you care only about cells + using SimpleCellVisitor = + std::function< void( const wxRect &rect, TrackPanelCell &cell ) >; + void VisitCells( const SimpleCellVisitor &visitor ); + + // Easier visits when you want to visit each node once only + using SimpleNodeVisitor = + std::function< void( const wxRect &rect, TrackPanelNode &node ) >; + void VisitPreorder( const SimpleNodeVisitor &visitor ); + void VisitPostorder( const SimpleNodeVisitor &visitor ); + virtual TrackPanelCell *GetFocusedCell() = 0; virtual void SetFocusedCell() = 0; @@ -86,6 +110,10 @@ protected: void ClearTargets(); private: + void Visit( + const wxRect &rect, const std::shared_ptr &node, + Visitor &visitor ); + bool HasRotation(); bool ChangeTarget(bool forward, bool cycle);