From 0256156decd6cd20a1513fa3136aa50c98acc4cd Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Wed, 1 Jan 2020 15:22:21 -0500 Subject: [PATCH] Implement Multi View checkbox menu item in wave track TCP... ... When it's off (default), Spectrum and Waveform buttons behave as before, and dragging of the sub-view separator is disabled. When it transitions to off, and the view is split, then the top sub-view takes up the whole view. When it transitions on, nothing visible happens. When it is on, and you choose Spectrum or Waveform, then the corrsponding sub-view toggles visibility. When a sub-view is turned on by the menu item, it appears lowest. --- .../wavetrack/ui/WaveTrackControls.cpp | 79 ++++++++++++++----- .../wavetrack/ui/WaveTrackView.cpp | 47 ++++++++++- .../wavetrack/ui/WaveTrackView.h | 7 ++ 3 files changed, 113 insertions(+), 20 deletions(-) diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp index aac25a633..1a2fa48dc 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp @@ -120,6 +120,7 @@ enum { On24BitID, // | OnFloatID, // <--- + OnMultiViewID, OnWaveformID, OnSpectrumID, OnSpectrogramSettingsID, @@ -567,6 +568,7 @@ protected: PlayableTrackControls::InitMenuData *mpData; + void OnMultiView(wxCommandEvent & event); void OnSetDisplay(wxCommandEvent & event); void OnSpectrogramSettings(wxCommandEvent & event); @@ -602,7 +604,11 @@ void WaveTrackMenuTable::InitMenu(Menu *pMenu, void *pUserData) std::vector checkedIds; - const auto displays = WaveTrackView::Get( *pTrack ).GetDisplays(); + const auto &view = WaveTrackView::Get( *pTrack ); + if (view.GetMultiView()) + checkedIds.push_back( OnMultiViewID ); + + const auto displays = view.GetDisplays(); for ( auto display : displays ) { checkedIds.push_back( display == WaveTrackViewConstants::Waveform @@ -687,18 +693,28 @@ void WaveTrackMenuTable::InitMenu(Menu *pMenu, void *pUserData) } BEGIN_POPUP_MENU(WaveTrackMenuTable) + + WaveTrack *const pTrack = static_cast(mpTrack); + const auto &view = WaveTrackView::Get( *pTrack ); + POPUP_MENU_SEPARATOR() - // 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, XO("Wa&veform"), OnSetDisplay) - POPUP_MENU_CHECK_ITEM(OnSpectrumID, XO("&Spectrogram"), OnSetDisplay) + POPUP_MENU_CHECK_ITEM(OnMultiViewID, XO("&Multi View"), OnMultiView) + + if ( view.GetMultiView() ) { + POPUP_MENU_CHECK_ITEM(OnWaveformID, XO("Wa&veform"), OnSetDisplay) + POPUP_MENU_CHECK_ITEM(OnSpectrumID, XO("&Spectrogram"), OnSetDisplay) + } + else { + POPUP_MENU_RADIO_ITEM(OnWaveformID, XO("Wa&veform"), OnSetDisplay) + POPUP_MENU_RADIO_ITEM(OnSpectrumID, XO("&Spectrogram"), OnSetDisplay) + } POPUP_MENU_ITEM(OnSpectrogramSettingsID, XO("S&pectrogram Settings..."), OnSpectrogramSettings) POPUP_MENU_SEPARATOR() +// If these are enabled again, choose a hot key for Mono that does not conflict +// with Multi View // POPUP_MENU_RADIO_ITEM(OnChannelMonoID, XO("&Mono"), OnChannelChange) // POPUP_MENU_RADIO_ITEM(OnChannelLeftID, XO("&Left Channel"), OnChannelChange) // POPUP_MENU_RADIO_ITEM(OnChannelRightID, XO("R&ight Channel"), OnChannelChange) @@ -711,9 +727,8 @@ BEGIN_POPUP_MENU(WaveTrackMenuTable) POPUP_MENU_ITEM(OnSplitStereoMonoID, XO("Split Stereo to Mo&no"), OnSplitStereoMono) #endif - WaveTrack *const pTrack = static_cast(mpTrack); if ( pTrack ) { - const auto displays = WaveTrackView::Get( *pTrack ).GetDisplays(); + const auto displays = view.GetDisplays(); bool hasWaveform = make_iterator_range( displays.begin(), displays.end() ) .contains( WaveTrackViewConstants::Waveform ); @@ -730,6 +745,23 @@ BEGIN_POPUP_MENU(WaveTrackMenuTable) END_POPUP_MENU() +void WaveTrackMenuTable::OnMultiView(wxCommandEvent & event) +{ + const auto pTrack = static_cast(mpData->pTrack); + const auto &view = WaveTrackView::Get( *pTrack ); + bool multi = !view.GetMultiView(); + WaveTrackView::WaveTrackDisplay display; + if ( !multi ) + display = *view.GetDisplays().begin(); + for (const auto channel : TrackList::Channels(pTrack)) { + auto &channelView = WaveTrackView::Get( *channel ); + channelView.SetMultiView( multi ); + if ( !multi ) + // Whichever sub-view was on top will take up all + channelView.SetDisplay( display ); + } +} + /// Set the Display mode based on the menu choice in the Track Menu. void WaveTrackMenuTable::OnSetDisplay(wxCommandEvent & event) { @@ -747,20 +779,28 @@ void WaveTrackMenuTable::OnSetDisplay(wxCommandEvent & event) id = Spectrum; break; } - const auto displays = WaveTrackView::Get( *pTrack ).GetDisplays(); - const bool wrongType = !(displays.size() == 1 && displays[0] == id); - if (wrongType) { + auto &view = WaveTrackView::Get( *pTrack ); + if ( view.GetMultiView() ) { for (auto channel : TrackList::Channels(pTrack)) { - channel->SetLastScaleType(); - WaveTrackView::Get( *channel ) - .SetDisplay(WaveTrackView::WaveTrackDisplay(id)); + WaveTrackView::Get( *channel ).ToggleSubView( id ); } + } + else { + const auto displays = view.GetDisplays(); + const bool wrongType = !(displays.size() == 1 && displays[0] == id); + if (wrongType) { + for (auto channel : TrackList::Channels(pTrack)) { + channel->SetLastScaleType(); + WaveTrackView::Get( *channel ) + .SetDisplay(WaveTrackView::WaveTrackDisplay(id)); + } - AudacityProject *const project = ::GetActiveProject(); - ProjectHistory::Get( *project ).ModifyState(true); + AudacityProject *const project = ::GetActiveProject(); + ProjectHistory::Get( *project ).ModifyState(true); - using namespace RefreshCode; - mpData->result = RefreshAll | UpdateVRuler; + using namespace RefreshCode; + mpData->result = RefreshAll | UpdateVRuler; + } } } @@ -896,6 +936,7 @@ void WaveTrackMenuTable::OnMergeStereo(wxCommandEvent &) partnerView.SetMinimized(bBothMinimizedp); partnerView.RestorePlacements( view.SavePlacements() ); + partnerView.SetMultiView( view.GetMultiView() ); //On Demand - join the queues together. if (ODManager::IsInstanceCreated()) diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp index 5c259894b..e7e54fb1b 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp @@ -197,6 +197,9 @@ public: WaveTrackView &view, WaveTrackSubView &subView, const TrackPanelMouseState &state ) { + if ( !view.GetMultiView() ) + return {}; + SubViewAdjuster adjuster{ view }; auto hit = adjuster.HitTest( subView, state.state.GetY(), state.rect.GetTop(), state.rect.GetHeight() ); @@ -470,8 +473,9 @@ void WaveTrackView::CopyTo( Track &track ) const auto &other = TrackView::Get( track ); if ( const auto pOther = dynamic_cast< WaveTrackView* >( &other ) ) { - // only one field is important to preserve in undo/redo history + // only these fields are important to preserve in undo/redo history pOther->RestorePlacements( SavePlacements() ); + pOther->mMultiView = mMultiView; } } @@ -542,6 +546,47 @@ void WaveTrackView::SetDisplay(WaveTrackDisplay display) DoSetDisplay( display ); } +void WaveTrackView::ToggleSubView(WaveTrackDisplay display) +{ + size_t ii = 0; + size_t found = 0; + if ( WaveTrackSubViews::FindIf( [&]( const WaveTrackSubView &subView ) { + if ( subView.SubViewType() == display ) { + found = ii; + return true; + } + ++ii; + return false; + } ) ) { + auto &foundPlacement = mPlacements[found]; + if ( foundPlacement.fraction > 0.0 ) { + auto index = foundPlacement.index; + foundPlacement = { -1, 0.0 }; + if (index >= 0) { + for ( auto &placement : mPlacements ) { + if ( placement.index > index ) + --placement.index; + } + } + } + else { + float total = 0; + int greatest = -1; + unsigned nn = 0; + for ( const auto &placement : mPlacements ) { + if ( placement.fraction >= 0.0 && placement.index >= 0 ) { + total += placement.fraction; + greatest = std::max( greatest, placement.index ); + ++nn; + } + } + // Turn on the sub-view, putting it lowest, and with average of the + // heights of the other sub-views + foundPlacement = { greatest + 1, total / nn }; + } + } +} + void WaveTrackView::DoSetDisplay(WaveTrackDisplay display) { size_t ii = 0; diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.h b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.h index bad1c05de..df8282b76 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.h +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.h @@ -91,6 +91,8 @@ public: void RestorePlacements( const WaveTrackSubViewPlacements &placements ) { mPlacements = placements; } + void ToggleSubView( WaveTrackDisplay id ); + // Get all the sub-views, in a sequence that is unspecified but in // correspondence with the result of SavePlacements std::vector< std::shared_ptr< WaveTrackSubView > > GetAllSubViews(); @@ -98,6 +100,9 @@ public: // Return cached height of rect in last call of GetSubViews wxCoord GetLastHeight() const { return mLastHeight; } + bool GetMultiView() const { return mMultiView; } + void SetMultiView( bool value ) { mMultiView = value; } + private: void BuildSubViews() const; void DoSetDisplay(WaveTrackDisplay display); @@ -121,6 +126,8 @@ protected: WaveTrackSubViewPlacements mPlacements; mutable wxCoord mLastHeight{}; + + bool mMultiView{ false }; }; // Helper for drawing routines