diff --git a/src/ClientData.h b/src/ClientData.h index 6f50d8bc3..56277d958 100644 --- a/src/ClientData.h +++ b/src/ClientData.h @@ -344,6 +344,40 @@ protected: } } + // \brief Invoke predicate on the ClientData objects that have been created in + // this, but do not cause the creation of any. Stop at the first for which + // the predicate returns true, and return a pointer to the corresponding + // object, or return nullptr if no return values were true. + // Beware that the sequence of visitation is not specified. + template< typename Function > + ClientData *FindIf( const Function &function ) + { + auto data = GetData(); + for( auto &pObject : data.mObject ) { + const auto &ptr = Dereferenceable(pObject); + if ( ptr && function ( *ptr ) ) + return &*ptr; + } + return nullptr; + } + + // const counterpart of previous, only compiles with a function that takes + // a value or const reference argument + template< typename Function > + const ClientData *FindIf( const Function &function ) const + { + auto data = GetData(); + for( auto &pObject : data.mObject ) { + const auto &ptr = Dereferenceable(pObject); + if ( ptr ) { + const auto &c_ref = *ptr; + if ( function( c_ref ) ); + return &*c_ref; + } + } + return nullptr; + } + // \brief For each registered factory, if the corresponding object in this // is absent, then invoke the factory and store the result. void BuildAll() diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index 1e125e63b..1304259ac 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -923,6 +923,7 @@ void TrackPanel::UpdateTrackVRuler(Track *t) for (auto channel : TrackList::Channels(t)) { auto &view = TrackView::Get( *channel ); const auto height = view.GetHeight() - (kTopMargin + kBottomMargin); + rect.SetHeight( height ); const auto subViews = view.GetSubViews( rect ); if (subViews.empty()) continue; @@ -1142,7 +1143,7 @@ struct ChannelGroup final : TrackPanelGroup { auto &view = TrackView::Get( *channel ); auto height = view.GetHeight(); rect.SetTop( yy ); - rect.SetHeight( height ); + rect.SetHeight( height - kSeparatorThickness ); const auto subViews = TrackView::Get( *channel ).GetSubViews( rect ); auto y1 = yy; for ( const auto &subView : subViews ) { diff --git a/src/effects/nyquist/Nyquist.cpp b/src/effects/nyquist/Nyquist.cpp index 7a3690bd6..2405cd382 100644 --- a/src/effects/nyquist/Nyquist.cpp +++ b/src/effects/nyquist/Nyquist.cpp @@ -1073,19 +1073,26 @@ bool NyquistEffect::ProcessOne() type = wxT("wave"); spectralEditp = mCurTrack[0]->GetSpectrogramSettings().SpectralSelectionEnabled()? wxT("T") : wxT("NIL"); // To do: accommodate split views - auto viewType = WaveTrackViewConstants::NoDisplay; auto displays = WaveTrackView::Get( *wt ).GetDisplays(); - if (!displays.empty()) - viewType = displays[0]; - switch ( viewType ) - { - case Waveform: - view = (mCurTrack[0]->GetWaveformSettings().scaleType == 0) ? wxT("\"Waveform\"") : wxT("\"Waveform (dB)\""); - break; - case Spectrum: - view = wxT("\"Spectrogram\""); - break; - default: view = wxT("NIL"); break; + auto format = [&]( decltype(displays[0]) display ){ + switch ( display ) + { + case Waveform: + return (mCurTrack[0]->GetWaveformSettings().scaleType == 0) ? wxT("\"Waveform\"") : wxT("\"Waveform (dB)\""); + case Spectrum: + return wxT("\"Spectrogram\""); + default: return wxT("NIL"); + } + }; + if (displays.empty()) + view = wxT("NIL"); + else if (displays.size() == 1) + view = format( displays[0] ); + else { + view = wxT("(list"); + for ( auto display : displays ) + view += wxString(wxT(" ")) + format( display ); + view += wxT(")"); } }, #if defined(USE_MIDI) diff --git a/src/tracks/playabletrack/wavetrack/ui/SpectrumView.cpp b/src/tracks/playabletrack/wavetrack/ui/SpectrumView.cpp index 43449dda8..56491c76e 100644 --- a/src/tracks/playabletrack/wavetrack/ui/SpectrumView.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/SpectrumView.cpp @@ -649,6 +649,6 @@ void SpectrumView::Draw( static const WaveTrackSubViews::RegisteredFactory key{ []( WaveTrackView &view ){ - return std::make_shared< SpectrumView >( view.FindTrack() ); + return std::make_shared< SpectrumView >( view ); } }; diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp index 4c2e9e3f7..ebd597d6d 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp @@ -690,10 +690,13 @@ void WaveTrackMenuTable::InitMenu(Menu *pMenu, void *pUserData) BEGIN_POPUP_MENU(WaveTrackMenuTable) POPUP_MENU_SEPARATOR() - // These radio items may become non-exclusive check items - POPUP_MENU_RADIO_ITEM(OnWaveformID, _("Wa&veform"), OnSetDisplay) - POPUP_MENU_RADIO_ITEM(OnWaveformDBID, _("&Waveform (dB)"), OnSetDisplay) - POPUP_MENU_RADIO_ITEM(OnSpectrumID, _("&Spectrogram"), OnSetDisplay) + // View types are now a non-exclusive choice. The first two are mutually + // exclusive, but the view may be in a state with either of those, and also + // spectrogram, after a mouse drag. Clicking any of these three makes that + // view take up all the height. + POPUP_MENU_CHECK_ITEM(OnWaveformID, _("Wa&veform"), OnSetDisplay) + POPUP_MENU_CHECK_ITEM(OnWaveformDBID, _("&Waveform (dB)"), OnSetDisplay) + POPUP_MENU_CHECK_ITEM(OnSpectrumID, _("&Spectrogram"), OnSetDisplay) POPUP_MENU_ITEM(OnSpectrogramSettingsID, _("S&pectrogram Settings..."), OnSpectrogramSettings) POPUP_MENU_SEPARATOR() diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp index 25949300a..69039b1be 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp @@ -12,11 +12,15 @@ Paul Licameli split from TrackPanel.cpp #include "../../../../Experimental.h" +#include <numeric> #include <wx/graphics.h> #include "../../../../WaveClip.h" #include "../../../../WaveTrack.h" +#include "../../../../HitTestResult.h" +#include "../../../../ProjectHistory.h" +#include "../../../../RefreshCode.h" #include "../../../../TrackArtist.h" #include "../../../../TrackPanelDrawingContext.h" #include "../../../../TrackPanelMouseEvent.h" @@ -27,6 +31,335 @@ Paul Licameli split from TrackPanel.cpp #include "../../../ui/TimeShiftHandle.h" +namespace { + +using WaveTrackSubViewPtrs = std::vector< std::shared_ptr< WaveTrackSubView > >; + +// Structure that collects and modifies information on sub-view positions +// Written with great generality, allowing any number of sub-views +struct SubViewAdjuster +{ + enum { HotZoneSize = 5 }; // so many pixels at top and bottom of each subview + + SubViewAdjuster( WaveTrackView &view ) + : mwView{ + std::static_pointer_cast<WaveTrackView>( view.shared_from_this() ) } + { + mSubViews = view.GetAllSubViews(); + mOrigPlacements = mNewPlacements = view.SavePlacements(); + FindPermutation(); + } + + void FindPermutation() + { + // Find a certain sort of the sub-views + auto size = mOrigPlacements.size(); + wxASSERT( mSubViews.size() == size ); + mPermutation.resize( size ); + const auto begin = mPermutation.begin(), end = mPermutation.end(); + std::iota( begin, end, 0 ); + static auto invisible = []( const WaveTrackSubViewPlacement &placement ){ + return placement.index < 0 || placement.fraction <= 0; + }; + const auto comp = [this]( size_t ii, size_t jj ){ + auto &pi = mOrigPlacements[ii]; + bool iInvisible = invisible( pi ); + + auto &pj = mOrigPlacements[jj]; + bool jInvisible = invisible( pj ); + + // Sort the invisibles to the front, rest by index + if ( iInvisible != jInvisible ) + return iInvisible; + else if ( !iInvisible ) + return pi.index < pj.index; + else + // Minor sort among the invisible views by their type + return mSubViews[ii]->SubViewType() < mSubViews[jj]->SubViewType(); + }; + std::sort( begin, end, comp ); + // Find the start of visible sub-views + auto first = std::find_if( begin, end, [this](size_t ii){ + return !invisible( mOrigPlacements[ii] ); + } ); + mFirstSubView = first - begin; + } + + bool ModifyPermutation( bool top ) + { + bool rotated = false; + const auto pBegin = mPermutation.begin(), pEnd = mPermutation.end(); + auto pFirst = pBegin + mFirstSubView; + if ( mFirstSubView > 0 ) { + // In case of dragging the top edge of the topmost view, or the + // bottom edge of the bottommost, decide which of the invisible + // views can become visible, and reassign the sequence. + // For definiteness, that choice depends on the subview type numbers; + // see the sorting criteria above. + --mFirstSubView; + --pFirst; + if ( top ) { + // If you drag down the top, the greatest-numbered invisible + // subview type will appear there. + mNewPlacements[ *pFirst ].fraction = 0; + } + else { + // If you drag up the bottom, let the least-numbered invisible + // subview type appear there. + mNewPlacements[ *pBegin ].fraction = 0; + std::rotate( pBegin, pBegin + 1, pEnd ); + rotated = true; + } + } + // Reassign index numbers to all sub-views and 0 fraction to invisibles + for ( auto pIter = pBegin; pIter != pFirst; ++pIter ) { + auto &placement = mNewPlacements[ *pIter ]; + placement.index = -1; + placement.fraction = 0; + } + size_t index = 0; + for ( auto pIter = pFirst; pIter != pEnd; ++pIter ) + mNewPlacements[ *pIter ].index = index++; + return rotated; + } + + std::pair< size_t, bool > + HitTest( WaveTrackSubView &subView, + wxCoord yy, wxCoord top, wxCoord height ) + { + const auto begin = mPermutation.begin(), end = mPermutation.end(); + auto iter = std::find_if( begin, end, [&](size_t ii){ + return mSubViews[ ii ].get() == &subView; + } ); + auto index = iter - begin; + auto size = mPermutation.size(); + if ( index < size ) { + yy -= top; + if ( yy >= 0 && yy < HotZoneSize && index > 0 ) + return { index, true }; // top hit + if ( yy < height && yy >= height - HotZoneSize && + // Have not yet called ModifyPermutation; dragging bottom of + // bottommost view allowed only if at least one view is invisible + ( index < size - 1 || mFirstSubView > 0 ) ) + return { index, false }; // bottom hit + } + return { size, false }; // not hit + } + + void UpdateViews( bool rollback ) + { + auto pView = mwView.lock(); + if ( pView ) { + auto pTrack = static_cast< WaveTrack* >( pView->FindTrack().get() ); + for ( auto pChannel : TrackList::Channels<WaveTrack>( pTrack ) ) + WaveTrackView::Get( *pChannel ).RestorePlacements( + rollback ? mOrigPlacements : mNewPlacements ); + } + } + + std::weak_ptr< WaveTrackView > mwView; + WaveTrackSubViewPtrs mSubViews; + WaveTrackSubViewPlacements mOrigPlacements, mNewPlacements; + // Array mapping ordinal into the placement and subview arrays + std::vector< size_t > mPermutation; + // index into mPermutation + size_t mFirstSubView{}; +}; + +class SubViewAdjustHandle : public UIHandle +{ +public: + enum { MinHeight = SubViewAdjuster::HotZoneSize }; + + static UIHandlePtr HitTest( + WaveTrackView &view, WaveTrackSubView &subView, + const TrackPanelMouseState &state ) + { + SubViewAdjuster adjuster{ view }; + auto hit = adjuster.HitTest( subView, + state.state.GetY(), state.rect.GetTop(), state.rect.GetHeight() ); + auto index = hit.first; + + if ( index < adjuster.mPermutation.size() ) + return std::make_shared< SubViewAdjustHandle >( + std::move( adjuster ), index, hit.second + ); + else + return {}; + } + + SubViewAdjustHandle( + SubViewAdjuster &&adjuster, size_t subViewIndex, bool top ) + : mAdjuster{ std::move( adjuster ) } + , mMySubView{ subViewIndex } + , mTop{ top } + { + if ( mAdjuster.ModifyPermutation( top ) ) + --mMySubView; + } + + Result Click( + const TrackPanelMouseEvent &event, AudacityProject * ) override + { + using namespace RefreshCode; + const auto &permutation = mAdjuster.mPermutation; + const auto size = permutation.size(); + if ( mMySubView >= size ) + return Cancelled; + + const auto &rect = event.rect; + const auto height = rect.GetHeight(); + mOrigHeight = height; + + wxASSERT( height == + mAdjuster.mOrigPlacements[ mAdjuster.mPermutation[ mMySubView ] ] + .fraction + ); + + // Find the total height of the sub-views that may resize + // Note that this depends on the redenomination of fractions that + // happened in the last call to GetSubViews + mTotalHeight = 0; + const auto begin = permutation.begin(); + auto iter = begin + ( mTop ? mAdjuster.mFirstSubView : mMySubView ); + const auto end = ( mTop ? begin + mMySubView + 1 : permutation.end() ); + for (; iter != end; ++iter) { + const auto &placement = mAdjuster.mOrigPlacements[ *iter ]; + mTotalHeight += placement.fraction; + } + + // Compute the maximum and minimum Y coordinates for drag effect + if ( mTop ) { + mOrigY = rect.GetTop(); + mYMax = rect.GetBottom(); + mYMin = mYMax - mTotalHeight + 1; + } + else { + mOrigY = rect.GetBottom(); + mYMin = rect.GetTop(); + mYMax = mYMin + mTotalHeight - 1; + } + + return RefreshNone; + } + + Result Drag( const TrackPanelMouseEvent &event, AudacityProject * ) override + { + using namespace RefreshCode; + auto pView = mAdjuster.mwView.lock(); + if ( !pView ) + return Cancelled; + + // Find new height for the dragged sub-view + auto newY = std::max( mYMin, std::min( mYMax, event.event.GetY() ) ); + const auto delta = newY - mOrigY; + wxCoord newHeight = mTop + ? mOrigHeight - delta + : mOrigHeight + delta + ; + wxASSERT( newHeight >= 0 && newHeight <= mTotalHeight ); + if ( newHeight < MinHeight ) + // Snap the dragged sub-view to nothing + newHeight = 0; + + // Reassign height for the dragged sub-view + auto &myPlacement = + mAdjuster.mNewPlacements[ mAdjuster.mPermutation[ mMySubView ] ]; + myPlacement.fraction = newHeight; + + // Grow or shrink other sub-views + auto excess = newHeight - mOrigHeight; // maybe negative + const auto adjustHeight = [&](size_t ii) { + if (excess == 0) + return true; + + auto index = mAdjuster.mPermutation[ ii ]; + + const auto &origPlacement = mAdjuster.mOrigPlacements[ index ]; + const auto oldFraction = origPlacement.fraction; + + auto &placement = mAdjuster.mNewPlacements[ index ]; + auto &fraction = placement.fraction; + + if (excess > oldFraction) { + excess -= oldFraction, fraction = 0; + return false; + } + else { + auto newFraction = oldFraction - excess; + if ( newFraction < MinHeight ) { + // This snaps very short sub-views to nothing + myPlacement.fraction += newFraction; + fraction = 0; + } + else + fraction = newFraction; + return true; + } + }; + if ( mTop ) { + for ( size_t ii = mMySubView; ii > 0; ) { + --ii; + if ( adjustHeight( ii ) ) + break; + } + } + else { + for ( size_t ii = mMySubView + 1, size = mAdjuster.mPermutation.size(); + ii < size; ++ii + ) { + if ( adjustHeight( ii ) ) + break; + } + } + + // Save adjustment to the track and request a redraw + mAdjuster.UpdateViews( false ); + return RefreshAll; + } + + HitTestPreview Preview( + const TrackPanelMouseState &state, const AudacityProject * ) override + { + static wxCursor resizeCursor{ wxCURSOR_SIZENS }; + return { + _("Click and drag to adjust sizes of sub-views."), + &resizeCursor + }; + } + + Result Release( + const TrackPanelMouseEvent &event, AudacityProject *pProject, + wxWindow *pParent) override + { + ProjectHistory::Get( *pProject ).ModifyState( false ); + return RefreshCode::RefreshNone; + } + + Result Cancel( AudacityProject * ) override + { + mAdjuster.UpdateViews( true ); + return RefreshCode::RefreshAll; + } + +private: + + SubViewAdjuster mAdjuster; + + // An index into mAdjuster.mPermutation + size_t mMySubView{}; + + wxCoord mYMin{}, mYMax{}; + wxCoord mTotalHeight{}; + wxCoord mOrigHeight{}; + wxCoord mOrigY{}; + + // Whether we drag the top or the bottom of the sub-view + bool mTop{}; +}; + +} + std::pair< bool, // if true, hit-testing is finished std::vector<UIHandlePtr> @@ -36,8 +369,20 @@ std::pair< const std::shared_ptr<WaveTrack> &wt, CommonTrackView &view) { - return WaveTrackView::DoDetailedHitTest( - state, pProject, currentTool, bMultiTool, wt, view); + auto results = WaveTrackView::DoDetailedHitTest( + state, pProject, currentTool, bMultiTool, wt, view ); + if ( results.first ) + return results; + + auto pWaveTrackView = mwWaveTrackView.lock(); + if ( pWaveTrackView && !state.state.HasModifiers() ) { + auto pHandle = SubViewAdjustHandle::HitTest( + *pWaveTrackView, *this, state ); + if (pHandle) + results.second.push_back( pHandle ); + } + + return results; } WaveTrackView &WaveTrackView::Get( WaveTrack &track ) @@ -53,22 +398,13 @@ const WaveTrackView &WaveTrackView::Get( const WaveTrack &track ) WaveTrackView::WaveTrackView( const std::shared_ptr<Track> &pTrack ) : CommonTrackView{ pTrack } { - WaveTrackSubViews::BuildAll(); +} - auto display = TracksPrefs::ViewModeChoice(); - - // Force creation always: - WaveformSettings &settings = static_cast< WaveTrack* >( pTrack.get() ) - ->GetIndependentWaveformSettings(); - - if (display == WaveTrackViewConstants::obsoleteWaveformDBDisplay) { - display = WaveTrackViewConstants::Waveform; - settings.scaleType = WaveformSettings::stLogarithmic; - } - - mPlacements.resize( WaveTrackSubViews::size() ); - - SetDisplay( display ); +WaveTrackSubView::WaveTrackSubView( WaveTrackView &waveTrackView ) + : CommonTrackView( waveTrackView.FindTrack() ) +{ + mwWaveTrackView = std::static_pointer_cast<WaveTrackView>( + waveTrackView.shared_from_this() ); } WaveTrackView::~WaveTrackView() @@ -128,6 +464,8 @@ WaveTrackView::DoDetailedHitTest auto WaveTrackView::GetDisplays() const -> std::vector<WaveTrackDisplay> { + BuildSubViews(); + // Collect the display types of visible views and sort them by position using Pair = std::pair< int, WaveTrackDisplay >; std::vector< Pair > pairs; @@ -146,6 +484,12 @@ auto WaveTrackView::GetDisplays() const -> std::vector<WaveTrackDisplay> } void WaveTrackView::SetDisplay(WaveTrackDisplay display) +{ + BuildSubViews(); + DoSetDisplay( display ); +} + +void WaveTrackView::DoSetDisplay(WaveTrackDisplay display) { size_t ii = 0; WaveTrackSubViews::ForEach( [&,display]( WaveTrackSubView &subView ){ @@ -159,18 +503,22 @@ void WaveTrackView::SetDisplay(WaveTrackDisplay display) auto WaveTrackView::GetSubViews( const wxRect &rect ) -> Refinement { - // Collect the visible views - using Pair = std::pair< float, std::shared_ptr< TrackView > >; + BuildSubViews(); + + Refinement results; + + // Collect the visible views in the right sequence + using Pair = std::pair< float*, std::shared_ptr< TrackView > >; std::vector< Pair > pairs( mPlacements.size() ); size_t ii = 0; float total = 0; WaveTrackSubViews::ForEach( [&]( WaveTrackSubView &subView ){ - const auto &placement = mPlacements[ii]; + auto &placement = mPlacements[ii]; auto index = placement.index; - auto fraction = placement.fraction; + auto &fraction = placement.fraction; if ( index >= 0 && fraction > 0.0 ) total += fraction, - pairs[ index ] = { fraction, subView.shared_from_this() }; + pairs[ index ] = { &fraction, subView.shared_from_this() }; ++ii; } ); @@ -179,17 +527,27 @@ auto WaveTrackView::GetSubViews( const wxRect &rect ) -> Refinement newEnd = std::remove_if( begin, end, []( const Pair &item ){ return !item.second; } ); pairs.erase( newEnd, end ); + results.reserve( pairs.size() ); // Assign coordinates - Refinement results; - results.reserve( pairs.size() ); - float partial = 0; + // Also update the stored placements, redenominating to the total height, + // storing integer values const auto top = rect.GetTop(); const auto height = rect.GetHeight(); + float partial = 0; + wxCoord lastCoord = 0; + float *lastFraction = nullptr; for ( const auto &pair : pairs ) { - results.emplace_back( top + (partial / total) * height, pair.second ); - partial += pair.first; + wxCoord newCoord = top + (partial / total) * height; + results.emplace_back( newCoord, pair.second ); + partial += *pair.first; + if (lastFraction) + *lastFraction = newCoord - lastCoord; + lastFraction = pair.first; + lastCoord = newCoord; } + if ( lastFraction ) + *lastFraction = top + height - lastCoord; return results; } @@ -197,6 +555,8 @@ auto WaveTrackView::GetSubViews( const wxRect &rect ) -> Refinement std::vector< std::shared_ptr< WaveTrackSubView > > WaveTrackView::GetAllSubViews() { + BuildSubViews(); + std::vector< std::shared_ptr< WaveTrackSubView > > results; WaveTrackSubViews::ForEach( [&]( WaveTrackSubView &subView ){ results.push_back( std::static_pointer_cast<WaveTrackSubView>( @@ -207,6 +567,8 @@ WaveTrackView::GetAllSubViews() void WaveTrackView::DoSetMinimized( bool minimized ) { + BuildSubViews(); + // May come here. Invoke also on sub-views. TrackView::DoSetMinimized( minimized ); WaveTrackSubViews::ForEach( [minimized](WaveTrackSubView &subView){ @@ -416,12 +778,41 @@ ClipParameters::ClipParameters void WaveTrackView::Reparent( const std::shared_ptr<Track> &parent ) { + // BuildSubViews(); // not really needed CommonTrackView::Reparent( parent ); WaveTrackSubViews::ForEach( [&parent](WaveTrackSubView &subView){ subView.Reparent( parent ); } ); } +void WaveTrackView::BuildSubViews() const +{ + if ( WaveTrackSubViews::size() == 0) { + // On-demand steps that can't happen in the constructor + auto pThis = const_cast<WaveTrackView*>( this ); + pThis->BuildAll(); + pThis->mPlacements.resize( WaveTrackSubViews::size() ); + bool minimized = GetMinimized(); + pThis->WaveTrackSubViews::ForEach( [&]( WaveTrackSubView &subView ){ + subView.DoSetMinimized( minimized ); + } ); + + auto pTrack = pThis->FindTrack(); + auto display = TracksPrefs::ViewModeChoice(); + + // Force creation always: + WaveformSettings &settings = static_cast< WaveTrack* >( pTrack.get() ) + ->GetIndependentWaveformSettings(); + + if (display == WaveTrackViewConstants::obsoleteWaveformDBDisplay) { + display = WaveTrackViewConstants::Waveform; + settings.scaleType = WaveformSettings::stLogarithmic; + } + + pThis->DoSetDisplay( display ); + } +} + void WaveTrackView::Draw( TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass ) diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.h b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.h index 00dd86c45..65b0bbf42 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.h +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.h @@ -16,10 +16,13 @@ Paul Licameli split from class WaveTrack namespace WaveTrackViewConstants{ enum Display : int; } class WaveTrack; +class WaveTrackView; + class WaveTrackSubView : public CommonTrackView { public: - using CommonTrackView::CommonTrackView; + explicit + WaveTrackSubView( WaveTrackView &waveTrackView ); virtual WaveTrackViewConstants::Display SubViewType() const = 0; @@ -31,7 +34,9 @@ public: const AudacityProject *pProject, int currentTool, bool bMultiTool, const std::shared_ptr<WaveTrack> &wt, CommonTrackView &view); -}; +private: + std::weak_ptr<WaveTrackView> mwWaveTrackView; + }; struct WaveTrackSubViewPlacement { int index; @@ -91,6 +96,9 @@ public: std::vector< std::shared_ptr< WaveTrackSubView > > GetAllSubViews(); private: + void BuildSubViews() const; + void DoSetDisplay(WaveTrackDisplay display); + // TrackPanelDrawable implementation void Draw( TrackPanelDrawingContext &context, diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveformView.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveformView.cpp index 0d02853ce..45eb16792 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveformView.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveformView.cpp @@ -1084,6 +1084,6 @@ void WaveformView::Draw( static const WaveTrackSubViews::RegisteredFactory key{ []( WaveTrackView &view ){ - return std::make_shared< WaveformView >( view.FindTrack() ); + return std::make_shared< WaveformView >( view ); } };