diff --git a/src/WaveTrack.cpp b/src/WaveTrack.cpp index 8cc4b1d04..83508ea4a 100644 --- a/src/WaveTrack.cpp +++ b/src/WaveTrack.cpp @@ -97,15 +97,6 @@ WaveTrack::WaveTrack(const std::shared_ptr &projDirManager, sampleFo } } - // Force creation always: - WaveformSettings &settings = GetIndependentWaveformSettings(); - - mDisplay = TracksPrefs::ViewModeChoice(); - if (mDisplay == WaveTrackViewConstants::obsoleteWaveformDBDisplay) { - mDisplay = WaveTrackViewConstants::Waveform; - settings.scaleType = WaveformSettings::stLogarithmic; - } - mLegacyProjectFileOffset = 0; mFormat = format; @@ -161,7 +152,6 @@ void WaveTrack::Init(const WaveTrack &orig) mOldGain[1] = 0.0; SetDefaultName(orig.GetDefaultName()); SetName(orig.GetName()); - mDisplay = orig.mDisplay; mDisplayMin = orig.mDisplayMin; mDisplayMax = orig.mDisplayMax; mSpectrumMin = orig.mSpectrumMin; @@ -196,7 +186,6 @@ void WaveTrack::Merge(const Track &orig) { orig.TypeSwitch( [&](const WaveTrack *pwt) { const WaveTrack &wt = *pwt; - mDisplay = wt.mDisplay; mGain = wt.mGain; mPan = wt.mPan; mDisplayMin = wt.mDisplayMin; diff --git a/src/WaveTrack.h b/src/WaveTrack.h index c131fd422..fa8a838f5 100644 --- a/src/WaveTrack.h +++ b/src/WaveTrack.h @@ -17,7 +17,6 @@ #include #include "WaveTrackLocation.h" -#include "tracks/playabletrack/wavetrack/ui/WaveTrackViewConstants.h" class ProgressDialog; @@ -523,17 +522,12 @@ private: // Set the unique autosave ID void SetAutoSaveIdent(int id); - using WaveTrackDisplay = WaveTrackViewConstants::Display; - int GetLastScaleType() const { return mLastScaleType; } void SetLastScaleType() const; int GetLastdBRange() const { return mLastdBRange; } void SetLastdBRange() const; - WaveTrackDisplay GetDisplay() const { return mDisplay; } - void SetDisplay(WaveTrackDisplay display) { mDisplay = display; } - void GetDisplayBounds(float *min, float *max) const; void SetDisplayBounds(float min, float max) const; void GetSpectrumBounds(float *min, float *max) const; @@ -568,7 +562,6 @@ private: mutable float mSpectrumMin; mutable float mSpectrumMax; - WaveTrackDisplay mDisplay; mutable int mLastScaleType; // last scale type choice mutable int mLastdBRange; mutable std::vector mDisplayLocationsCache; diff --git a/src/commands/SetTrackInfoCommand.cpp b/src/commands/SetTrackInfoCommand.cpp index 57fd75c5b..2f7c8a3e3 100644 --- a/src/commands/SetTrackInfoCommand.cpp +++ b/src/commands/SetTrackInfoCommand.cpp @@ -44,7 +44,8 @@ SetTrackAudioCommand and SetTrackVisualsCommand. #include "../prefs/SpectrogramSettings.h" #include "../Shuttle.h" #include "../ShuttleGui.h" -#include "../tracks/ui/TrackView.h" +#include "../tracks/playabletrack/wavetrack/ui/WaveTrackView.h" +#include "../tracks/playabletrack/wavetrack/ui/WaveTrackViewConstants.h" #include "CommandContext.h" SetTrackBase::SetTrackBase(){ @@ -353,7 +354,7 @@ bool SetTrackVisualsCommand::ApplyInner(const CommandContext & context, Track * TrackView::Get( *t ).SetHeight( mHeight ); if( wt && bHasDisplayType ) - wt->SetDisplay( + WaveTrackView::Get( *wt ).SetDisplay( (mDisplayType == kWaveform) ? WaveTrackViewConstants::Waveform : WaveTrackViewConstants::Spectrum diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index 470280465..b53a81fdd 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -68,6 +68,8 @@ greater use in future. #include "../widgets/ProgressDialog.h" #include "../ondemand/ODManager.h" #include "TimeWarper.h" +#include "../tracks/playabletrack/wavetrack/ui/WaveTrackView.h" +#include "../tracks/playabletrack/wavetrack/ui/WaveTrackViewConstants.h" #include "../widgets/HelpSystem.h" #include "../widgets/NumericTextCtrl.h" #include "../widgets/AudacityMessageBox.h" @@ -2360,7 +2362,8 @@ void Effect::Preview(bool dryOnly) mixLeft->Offset(-mixLeft->GetStartTime()); mixLeft->SetSelected(true); - mixLeft->SetDisplay(WaveTrackViewConstants::NoDisplay); + WaveTrackView::Get( *mixLeft ) + .SetDisplay(WaveTrackViewConstants::NoDisplay); auto pLeft = mTracks->Add( mixLeft ); Track *pRight{}; if (mixRight) { @@ -2375,8 +2378,8 @@ void Effect::Preview(bool dryOnly) if (src->GetSelected() || mPreviewWithNotSelected) { auto dest = src->Copy(mT0, t1); dest->SetSelected(src->GetSelected()); - static_cast(dest.get()) - ->SetDisplay(WaveTrackViewConstants::NoDisplay); + WaveTrackView::Get( *static_cast(dest.get()) ) + .SetDisplay(WaveTrackViewConstants::NoDisplay); mTracks->Add( dest ); } } diff --git a/src/effects/nyquist/Nyquist.cpp b/src/effects/nyquist/Nyquist.cpp index 612b142f2..32efc085f 100644 --- a/src/effects/nyquist/Nyquist.cpp +++ b/src/effects/nyquist/Nyquist.cpp @@ -73,6 +73,8 @@ effects from this one class. #include "../../wxFileNameWrapper.h" #include "../../prefs/GUIPrefs.h" #include "../../prefs/WaveformSettings.h" +#include "../../tracks/playabletrack/wavetrack/ui/WaveTrackView.h" +#include "../../tracks/playabletrack/wavetrack/ui/WaveTrackViewConstants.h" #include "../../widgets/NumericTextCtrl.h" #include "../../widgets/ProgressDialog.h" @@ -542,7 +544,11 @@ bool NyquistEffect::Init() for ( auto t : TrackList::Get( *project ).Selected< const WaveTrack >() ) { - if (t->GetDisplay() != WaveTrackViewConstants::Spectrum || + const auto displays = WaveTrackView::Get(*t).GetDisplays(); + bool hasSpectral = + make_iterator_range( displays.begin(), displays.end()) + .contains( WaveTrackViewConstants::Spectrum ); + if ( !hasSpectral || !(t->GetSpectrogramSettings().SpectralSelectionEnabled())) { bAllowSpectralEditing = false; break; @@ -1066,7 +1072,12 @@ bool NyquistEffect::ProcessOne() [&](const WaveTrack *wt) { type = wxT("wave"); spectralEditp = mCurTrack[0]->GetSpectrogramSettings().SpectralSelectionEnabled()? wxT("T") : wxT("NIL"); - switch (wt->GetDisplay()) + // 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)\""); diff --git a/src/menus/SelectMenus.cpp b/src/menus/SelectMenus.cpp index 6501670b8..65d2f6edd 100644 --- a/src/menus/SelectMenus.cpp +++ b/src/menus/SelectMenus.cpp @@ -21,6 +21,8 @@ #include "../commands/CommandManager.h" #include "../toolbars/ControlToolBar.h" #include "../tracks/ui/SelectHandle.h" +#include "../tracks/playabletrack/wavetrack/ui/WaveTrackView.h" +#include "../tracks/playabletrack/wavetrack/ui/WaveTrackViewConstants.h" // private helper classes and functions namespace { @@ -33,8 +35,9 @@ void DoNextPeakFrequency(AudacityProject &project, bool up) // Find the first selected wave track that is in a spectrogram view. const WaveTrack *pTrack {}; for ( auto wt : tracks.Selected< const WaveTrack >() ) { - const int display = wt->GetDisplay(); - if (display == WaveTrackViewConstants::Spectrum) { + const auto displays = WaveTrackView::Get( *wt ).GetDisplays(); + if ( make_iterator_range( displays.begin(), displays.end() ) + .contains( WaveTrackViewConstants::Spectrum) ) { pTrack = wt; break; } diff --git a/src/prefs/SpectrumPrefs.cpp b/src/prefs/SpectrumPrefs.cpp index 2f4ac3422..51dae7154 100644 --- a/src/prefs/SpectrumPrefs.cpp +++ b/src/prefs/SpectrumPrefs.cpp @@ -31,6 +31,7 @@ #include "../TrackPanel.h" #include "../WaveTrack.h" +#include "../tracks/playabletrack/wavetrack/ui/WaveTrackView.h" #include @@ -48,7 +49,7 @@ SpectrumPrefs::SpectrumPrefs(wxWindow * parent, wxWindowID winid, WaveTrack *wt) wt->GetSpectrumBounds(&mOrigMin, &mOrigMax); mTempSettings.maxFreq = mOrigMax; mTempSettings.minFreq = mOrigMin; - mOrigDisplay = mWt->GetDisplay(); + mOrigPlacements = WaveTrackView::Get( *mWt ).SavePlacements(); } else { mTempSettings = mOrigSettings = SpectrogramSettings::defaults(); @@ -423,7 +424,7 @@ void SpectrumPrefs::Rollback() if (mWt && isOpenPage) { auto channels = TrackList::Channels(mWt); for (auto channel : channels) - channel->SetDisplay(mOrigDisplay); + WaveTrackView::Get( *channel ).RestorePlacements( mOrigPlacements ); } if (isOpenPage) { @@ -470,7 +471,8 @@ void SpectrumPrefs::Preview() if (mWt && isOpenPage) { for (auto channel : TrackList::Channels(mWt)) - channel->SetDisplay(WaveTrackViewConstants::Spectrum); + WaveTrackView::Get( *channel ) + .SetDisplay( WaveTrackViewConstants::Spectrum ); } if (isOpenPage) { diff --git a/src/prefs/SpectrumPrefs.h b/src/prefs/SpectrumPrefs.h index 4158e0f42..f22e1e7e1 100644 --- a/src/prefs/SpectrumPrefs.h +++ b/src/prefs/SpectrumPrefs.h @@ -23,6 +23,7 @@ #include "../Experimental.h" +#include #include #include "../tracks/playabletrack/wavetrack/ui/WaveTrackViewConstants.h" @@ -38,6 +39,7 @@ struct FFTParam; class ShuttleGui; class SpectrogramSettings; class WaveTrack; +struct WaveTrackSubViewPlacement; #define SPECTRUM_PREFS_PLUGIN_SYMBOL ComponentInterfaceSymbol{ XO("Spectrum") } @@ -98,7 +100,7 @@ class SpectrumPrefs final : public PrefsPanel SpectrogramSettings mTempSettings, mOrigSettings; - WaveTrackViewConstants::Display mOrigDisplay; + std::vector mOrigPlacements; float mOrigMin, mOrigMax; bool mPopulating; diff --git a/src/prefs/WaveformPrefs.cpp b/src/prefs/WaveformPrefs.cpp index 7fb3f62fe..56b295b1c 100644 --- a/src/prefs/WaveformPrefs.cpp +++ b/src/prefs/WaveformPrefs.cpp @@ -27,6 +27,8 @@ Paul Licameli #include "../TrackPanel.h" #include "../ShuttleGui.h" #include "../WaveTrack.h" +#include "../tracks/playabletrack/wavetrack/ui/WaveTrackView.h" +#include "../tracks/playabletrack/wavetrack/ui/WaveTrackViewConstants.h" WaveformPrefs::WaveformPrefs(wxWindow * parent, wxWindowID winid, WaveTrack *wt) /* i18n-hint: A waveform is a visual representation of vibration */ @@ -184,7 +186,8 @@ bool WaveformPrefs::Commit() if (mWt && isOpenPage) { for (auto channel : TrackList::Channels(mWt)) - channel->SetDisplay(WaveTrackViewConstants::Waveform); + WaveTrackView::Get( *channel ) + .SetDisplay( WaveTrackViewConstants::Waveform ); } if (isOpenPage) { diff --git a/src/tracks/playabletrack/wavetrack/ui/SampleHandle.cpp b/src/tracks/playabletrack/wavetrack/ui/SampleHandle.cpp index 6f8771da6..d28191371 100644 --- a/src/tracks/playabletrack/wavetrack/ui/SampleHandle.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/SampleHandle.cpp @@ -120,10 +120,6 @@ UIHandlePtr SampleHandle::HitTest /// editable sample const auto wavetrack = pTrack.get(); - const int displayType = wavetrack->GetDisplay(); - if (WaveTrackViewConstants::Waveform != displayType) - return {}; // Not a wave, so return. - const double tt = adjustTime(wavetrack, viewInfo.PositionToTime(state.m_x, rect.x)); if (!SampleResolutionTest(viewInfo, wavetrack, tt, rect.width)) @@ -178,17 +174,6 @@ namespace { (const wxMouseEvent &event, const wxRect &rect, const ViewInfo &viewInfo, WaveTrack *wt, int width) { - //Get out of here if we shouldn't be drawing right now: - //If we aren't displaying the waveform, Display a message dialog - const int display = wt->GetDisplay(); - if (WaveTrackViewConstants::Waveform != display) - { - AudacityMessageBox(_( -"To use Draw, choose 'Waveform' or 'Waveform (dB)' in the Track Dropdown Menu."), - _("Draw Tool")); - return false; - } - //If we aren't zoomed in far enough, show a message dialog. const double time = adjustTime(wt, viewInfo.PositionToTime(event.m_x, rect.x)); if (!SampleResolutionTest(viewInfo, wt, time, width)) diff --git a/src/tracks/playabletrack/wavetrack/ui/SpectrumView.cpp b/src/tracks/playabletrack/wavetrack/ui/SpectrumView.cpp index 6447255df..8cb0f5668 100644 --- a/src/tracks/playabletrack/wavetrack/ui/SpectrumView.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/SpectrumView.cpp @@ -15,6 +15,7 @@ Paul Licameli split from WaveTrackView.cpp #include "SpectrumVRulerControls.h" #include "WaveTrackView.h" +#include "WaveTrackViewConstants.h" #include "../../../../AColor.h" #include "../../../../Prefs.h" @@ -31,6 +32,11 @@ Paul Licameli split from WaveTrackView.cpp SpectrumView::~SpectrumView() = default; +bool SpectrumView::IsSpectral() const +{ + return true; +} + std::vector SpectrumView::DetailedHitTest( const TrackPanelMouseState &state, const AudacityProject *pProject, int currentTool, bool bMultiTool ) diff --git a/src/tracks/playabletrack/wavetrack/ui/SpectrumView.h b/src/tracks/playabletrack/wavetrack/ui/SpectrumView.h index 06f98131b..5fc9c49d1 100644 --- a/src/tracks/playabletrack/wavetrack/ui/SpectrumView.h +++ b/src/tracks/playabletrack/wavetrack/ui/SpectrumView.h @@ -28,6 +28,7 @@ public: std::shared_ptr DoGetVRulerControls() override; + bool IsSpectral() const override; private: // TrackPanelDrawable implementation diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp index 4e9eac80f..000f4318e 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp @@ -16,7 +16,7 @@ Paul Licameli split from TrackPanel.cpp #include "../../ui/PlayableTrackButtonHandles.h" #include "WaveTrackSliderHandles.h" -#include "../../../ui/TrackView.h" +#include "WaveTrackView.h" #include "../../../../AudioIOBase.h" #include "../../../../CellularPanel.h" #include "../../../../Menus.h" @@ -599,19 +599,23 @@ void WaveTrackMenuTable::InitMenu(Menu *pMenu, void *pUserData) std::vector checkedIds; - const int display = pTrack->GetDisplay(); - checkedIds.push_back( - display == WaveTrackViewConstants::Waveform - ? (pTrack->GetWaveformSettings().isLinear() - ? OnWaveformID : OnWaveformDBID) - : OnSpectrumID); + const auto displays = WaveTrackView::Get( *pTrack ).GetDisplays(); + for ( auto display : displays ) { + checkedIds.push_back( + display == WaveTrackViewConstants::Waveform + ? (pTrack->GetWaveformSettings().isLinear() + ? OnWaveformID : OnWaveformDBID) + : OnSpectrumID); + } // Bug 1253. Shouldn't open preferences if audio is busy. // We can't change them on the fly yet anyway. auto gAudioIO = AudioIOBase::Get(); const bool bAudioBusy = gAudioIO->IsBusy(); - pMenu->Enable(OnSpectrogramSettingsID, - (display == WaveTrackViewConstants::Spectrum) && !bAudioBusy); + bool hasSpectrum = + make_iterator_range( displays.begin(), displays.end() ) + .contains( WaveTrackViewConstants::Spectrum ); + pMenu->Enable(OnSpectrogramSettingsID, hasSpectrum && !bAudioBusy); AudacityProject *const project = ::GetActiveProject(); auto &tracks = TrackList::Get( *project ); @@ -683,9 +687,9 @@ void WaveTrackMenuTable::InitMenu(Menu *pMenu, void *pUserData) BEGIN_POPUP_MENU(WaveTrackMenuTable) POPUP_MENU_SEPARATOR() - POPUP_MENU_RADIO_ITEM(OnWaveformID, _("Wa&veform"), OnSetDisplay) - POPUP_MENU_RADIO_ITEM(OnWaveformDBID, _("&Waveform (dB)"), OnSetDisplay) - POPUP_MENU_RADIO_ITEM(OnSpectrumID, _("&Spectrogram"), OnSetDisplay) + 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() @@ -702,9 +706,15 @@ BEGIN_POPUP_MENU(WaveTrackMenuTable) #endif WaveTrack *const pTrack = static_cast(mpTrack); - if( pTrack && pTrack->GetDisplay() != WaveTrackViewConstants::Spectrum ){ - POPUP_MENU_SEPARATOR() - POPUP_MENU_SUB_MENU(OnWaveColorID, _("&Wave Color"), WaveColorMenuTable) + if ( pTrack ) { + const auto displays = WaveTrackView::Get( *pTrack ).GetDisplays(); + bool hasWaveform = + make_iterator_range( displays.begin(), displays.end() ) + .contains( WaveTrackViewConstants::Waveform ); + if( hasWaveform ){ + POPUP_MENU_SEPARATOR() + POPUP_MENU_SUB_MENU(OnWaveColorID, _("&Wave Color"), WaveColorMenuTable) + } } POPUP_MENU_SEPARATOR() @@ -723,7 +733,7 @@ void WaveTrackMenuTable::OnSetDisplay(wxCommandEvent & event) const auto pTrack = static_cast(mpData->pTrack); bool linear = false; - WaveTrack::WaveTrackDisplay id; + WaveTrackView::WaveTrackDisplay id; switch (idInt) { default: case OnWaveformID: @@ -734,14 +744,16 @@ void WaveTrackMenuTable::OnSetDisplay(wxCommandEvent & event) id = Spectrum; break; } - const bool wrongType = pTrack->GetDisplay() != id; + const auto displays = WaveTrackView::Get( *pTrack ).GetDisplays(); + const bool wrongType = !(displays.size() == 1 && displays[0] == id); const bool wrongScale = (id == Waveform && pTrack->GetWaveformSettings().isLinear() != linear); if (wrongType || wrongScale) { for (auto channel : TrackList::Channels(pTrack)) { channel->SetLastScaleType(); - channel->SetDisplay(WaveTrack::WaveTrackDisplay(id)); + WaveTrackView::Get( *channel ) + .SetDisplay(WaveTrackView::WaveTrackDisplay(id)); if (wrongScale) channel->GetIndependentWaveformSettings().scaleType = linear ? WaveformSettings::stLinear @@ -876,8 +888,8 @@ void WaveTrackMenuTable::OnMergeStereo(wxCommandEvent &) // Set NEW track heights and minimized state auto - &view = TrackView::Get( *pTrack ), - &partnerView = TrackView::Get( *partner ); + &view = WaveTrackView::Get( *pTrack ), + &partnerView = WaveTrackView::Get( *partner ); view.SetMinimized(false); partnerView.SetMinimized(false); int AverageHeight = (view.GetHeight() + partnerView.GetHeight()) / 2; @@ -886,6 +898,8 @@ void WaveTrackMenuTable::OnMergeStereo(wxCommandEvent &) view.SetMinimized(bBothMinimizedp); partnerView.SetMinimized(bBothMinimizedp); + partnerView.RestorePlacements( view.SavePlacements() ); + //On Demand - join the queues together. if (ODManager::IsInstanceCreated()) if (!ODManager::Instance() diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp index 8c47bcb34..bd9931763 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp @@ -22,19 +22,57 @@ Paul Licameli split from TrackPanel.cpp #include "../../../../TrackPanelMouseEvent.h" #include "../../../../ViewInfo.h" #include "../../../../prefs/SpectrogramSettings.h" +#include "../../../../prefs/WaveformSettings.h" +#include "../../../../prefs/TracksPrefs.h" #include "../../../ui/TimeShiftHandle.h" +WaveTrackView &WaveTrackView::Get( WaveTrack &track ) +{ + return static_cast< WaveTrackView& >( TrackView::Get( track ) ); +} + +const WaveTrackView &WaveTrackView::Get( const WaveTrack &track ) +{ + return Get( const_cast( track ) ); +} + WaveTrackView::WaveTrackView( const std::shared_ptr &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 ); } WaveTrackView::~WaveTrackView() { } +void WaveTrackView::CopyTo( Track &track ) const +{ + TrackView::CopyTo( track ); + auto &other = TrackView::Get( track ); + + if ( const auto pOther = dynamic_cast< WaveTrackView* >( &other ) ) { + // only one field is important to preserve in undo/redo history + pOther->RestorePlacements( SavePlacements() ); + } +} + std::vector WaveTrackView::DetailedHitTest (const TrackPanelMouseState &st, const AudacityProject *pProject, int currentTool, bool bMultiTool) @@ -75,23 +113,72 @@ WaveTrackView::DoDetailedHitTest return { false, results }; } -auto WaveTrackView::GetSubViews( const wxRect &rect ) -> Refinement +auto WaveTrackView::GetDisplays() const -> std::vector { - auto wt = static_cast( FindTrack().get() ); - auto display = wt->GetDisplay(); - std::shared_ptr pSubView; + // Collect the display types of visible views and sort them by position + using Pair = std::pair< int, WaveTrackDisplay >; + std::vector< Pair > pairs; + size_t ii = 0; + WaveTrackSubViews::ForEach( [&]( const WaveTrackSubView &subView ){ + auto &placement = mPlacements[ii]; + if ( placement.fraction > 0 ) + pairs.emplace_back( placement.index, subView.SubViewType() ); + ++ii; + } ); + std::sort( pairs.begin(), pairs.end() ); + std::vector results; + for ( const auto &pair : pairs ) + results.push_back( pair.second ); + return results; +} + +void WaveTrackView::SetDisplay(WaveTrackDisplay display) +{ + size_t ii = 0; WaveTrackSubViews::ForEach( [&,display]( WaveTrackSubView &subView ){ if ( subView.SubViewType() == display ) - pSubView = subView.shared_from_this(); + mPlacements[ii] = { 0, 1.0 }; + else + mPlacements[ii] = { -1, 0.0 }; + ++ii; } ); - if ( !pSubView ) - return {}; - return { - { - rect.GetTop(), - pSubView - } - }; +} + +auto WaveTrackView::GetSubViews( const wxRect &rect ) -> Refinement +{ + // Collect the visible views + 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 index = placement.index; + auto fraction = placement.fraction; + if ( index >= 0 && fraction > 0.0 ) + total += fraction, + pairs[ index ] = { fraction, subView.shared_from_this() }; + ++ii; + } ); + + // Remove views we don't need + auto begin = pairs.begin(), end = pairs.end(), + newEnd = std::remove_if( begin, end, + []( const Pair &item ){ return !item.second; } ); + pairs.erase( newEnd, end ); + + // Assign coordinates + Refinement results; + results.reserve( pairs.size() ); + float partial = 0; + const auto top = rect.GetTop(); + const auto height = rect.GetHeight(); + for ( const auto &pair : pairs ) { + results.emplace_back( top + (partial / total) * height, pair.second ); + partial += pair.first; + } + + return results; } void WaveTrackView::DoSetMinimized( bool minimized ) diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.h b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.h index ce25fb991..396e24b7a 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.h +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.h @@ -24,6 +24,12 @@ public: virtual WaveTrackViewConstants::Display SubViewType() const = 0; }; +struct WaveTrackSubViewPlacement { + int index; + float fraction; +}; +using WaveTrackSubViewPlacements = std::vector< WaveTrackSubViewPlacement >; + class WaveTrackView; using WaveTrackSubViews = ClientData::Site< WaveTrackView, WaveTrackSubView, ClientData::SkipCopying, std::shared_ptr @@ -37,10 +43,16 @@ class WaveTrackView final WaveTrackView &operator=( const WaveTrackView& ) = delete; public: + static WaveTrackView &Get( WaveTrack &track ); + static const WaveTrackView &Get( const WaveTrack &track ); + explicit WaveTrackView( const std::shared_ptr &pTrack ); ~WaveTrackView() override; + // Preserve some view state too for undo/redo purposes + void CopyTo( Track &track ) const override; + std::shared_ptr DoGetVRulerControls() override; // CommonTrackView implementation @@ -55,6 +67,16 @@ public: const std::shared_ptr &wt, CommonTrackView &view); + using WaveTrackDisplay = WaveTrackViewConstants::Display; + + std::vector GetDisplays() const; + void SetDisplay(WaveTrackDisplay display); + + const WaveTrackSubViewPlacements &SavePlacements() const + { return mPlacements; } + void RestorePlacements( const WaveTrackSubViewPlacements &placements ) + { mPlacements = placements; } + private: // TrackPanelDrawable implementation void Draw( @@ -71,6 +93,8 @@ private: protected: void DoSetMinimized( bool minimized ) override; + + WaveTrackSubViewPlacements mPlacements; }; // Helper for drawing routines diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveformView.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveformView.cpp index 115593418..a238d018a 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveformView.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveformView.cpp @@ -15,6 +15,7 @@ Paul Licameli split from WaveTrackView.cpp #include "WaveformVRulerControls.h" #include "WaveTrackView.h" +#include "WaveTrackViewConstants.h" #include "CutlineHandle.h" #include "SampleHandle.h" diff --git a/src/tracks/ui/CommonTrackView.cpp b/src/tracks/ui/CommonTrackView.cpp index e998e21a4..a8fd75d0f 100644 --- a/src/tracks/ui/CommonTrackView.cpp +++ b/src/tracks/ui/CommonTrackView.cpp @@ -73,7 +73,7 @@ std::vector CommonTrackView::HitTest // Finally, default of all is adjustment of the selection box. if ( isMultiTool || currentTool == selectTool ) { result = SelectHandle::HitTest( - mSelectHandle, st, pProject, FindTrack() ); + mSelectHandle, st, pProject, shared_from_this() ); if (result) results.push_back(result); } diff --git a/src/tracks/ui/EnvelopeHandle.cpp b/src/tracks/ui/EnvelopeHandle.cpp index fb6dd0352..7b3f040ac 100644 --- a/src/tracks/ui/EnvelopeHandle.cpp +++ b/src/tracks/ui/EnvelopeHandle.cpp @@ -98,12 +98,6 @@ UIHandlePtr EnvelopeHandle::WaveTrackHitTest if (!envelope) return {}; - const int displayType = wt->GetDisplay(); - // Not an envelope hit, unless we're using a type of wavetrack display - // suitable for envelopes operations, ie one of the Wave displays. - if (displayType != WaveTrackViewConstants::Waveform) - return {}; // No envelope, not a hit, so return. - // Get envelope point, range 0.0 to 1.0 const bool dB = !wt->GetWaveformSettings().isLinear(); @@ -188,9 +182,6 @@ UIHandle::Result EnvelopeHandle::Click if (pTrack) result = pTrack->TypeSwitch< decltype(RefreshNone) >( [&](WaveTrack *wt) { - if (wt->GetDisplay() != WaveTrackViewConstants::Waveform) - return Cancelled; - if (!mEnvelope) return Cancelled; diff --git a/src/tracks/ui/SelectHandle.cpp b/src/tracks/ui/SelectHandle.cpp index 45799485b..69ef49370 100644 --- a/src/tracks/ui/SelectHandle.cpp +++ b/src/tracks/ui/SelectHandle.cpp @@ -128,13 +128,16 @@ namespace } // This returns true if we're a spectral editing track. - inline bool isSpectralSelectionTrack(const Track *pTrack) { - return pTrack && pTrack->TypeSwitch< bool >( [&](const WaveTrack *wt) { - const SpectrogramSettings &settings = wt->GetSpectrogramSettings(); - const int display = wt->GetDisplay(); - return (display == WaveTrackViewConstants::Spectrum) && - settings.SpectralSelectionEnabled(); - }); + inline bool isSpectralSelectionView(const TrackView *pTrackView) { + return + pTrackView && + pTrackView->IsSpectral() && + pTrackView->FindTrack() && + pTrackView->FindTrack()->TypeSwitch< bool >( + [&](const WaveTrack *wt) { + const SpectrogramSettings &settings = wt->GetSpectrogramSettings(); + return settings.SpectralSelectionEnabled(); + }); } enum SelectionBoundary { @@ -187,7 +190,7 @@ namespace SelectionBoundary ChooseBoundary (const ViewInfo &viewInfo, - wxCoord xx, wxCoord yy, const Track *pTrack, const wxRect &rect, + wxCoord xx, wxCoord yy, const TrackView *pTrackView, const wxRect &rect, bool mayDragWidth, bool onlyWithinSnapDistance, double *pPinValue = NULL) { @@ -220,9 +223,11 @@ namespace // within the time boundaries if (!viewInfo.selectedRegion.isPoint() && t0 <= selend && selend < t1 && - isSpectralSelectionTrack(pTrack)) { + isSpectralSelectionView(pTrackView)) { // Spectral selection track is always wave - const WaveTrack *const wt = static_cast(pTrack); + auto pTrack = pTrackView->FindTrack(); + const WaveTrack *const wt = + static_cast(pTrack.get()); const wxInt64 bottomSel = (f0 >= 0) ? FrequencyToPosition(wt, f0, rect.y, rect.height) : rect.y + rect.height; @@ -374,7 +379,7 @@ namespace UIHandlePtr SelectHandle::HitTest (std::weak_ptr &holder, const TrackPanelMouseState &st, const AudacityProject *pProject, - const std::shared_ptr &pTrack) + const std::shared_ptr &pTrackView) { // This handle is a little special because there may be some state to // preserve during movement before the click. @@ -393,13 +398,14 @@ UIHandlePtr SelectHandle::HitTest const auto &viewInfo = ViewInfo::Get( *pProject ); auto result = std::make_shared( - pTrack, oldUseSnap, TrackList::Get( *pProject ), st, viewInfo ); + pTrackView, oldUseSnap, TrackList::Get( *pProject ), st, viewInfo ); result = AssignUIHandlePtr(holder, result); //Make sure we are within the selected track // Adjusting the selection edges can be turned off in // the preferences... + auto pTrack = pTrackView->FindTrack(); if (!pTrack->GetSelected() || !viewInfo.bAdjustSelectionEdges) { return result; @@ -438,16 +444,17 @@ UIHandle::Result SelectHandle::NeedChangeHighlight } SelectHandle::SelectHandle -( const std::shared_ptr &pTrack, bool useSnap, +( const std::shared_ptr &pTrackView, bool useSnap, const TrackList &trackList, const TrackPanelMouseState &st, const ViewInfo &viewInfo ) - : mpTrack{ pTrack } + : mpView{ pTrackView } , mSnapManager{ std::make_shared(&trackList, &viewInfo) } { const wxMouseState &state = st.state; mRect = st.rect; auto time = std::max(0.0, viewInfo.PositionToTime(state.m_x, mRect.x)); + auto pTrack = pTrackView->FindTrack(); mSnapStart = mSnapManager->Snap(pTrack.get(), time, false); if (mSnapStart.snappedPoint) mSnapStart.outCoord += mRect.x; @@ -531,8 +538,12 @@ UIHandle::Result SelectHandle::Click using namespace RefreshCode; + const auto pView = mpView.lock(); + if ( !pView ) + return Cancelled; + wxMouseEvent &event = evt.event; - const auto sTrack = TrackList::Get( *pProject ).Lock(mpTrack); + const auto sTrack = TrackList::Get( *pProject ).Lock( FindTrack() ); const auto pTrack = sTrack.get(); auto &trackPanel = TrackPanel::Get( *pProject ); auto &viewInfo = ViewInfo::Get( *pProject ); @@ -620,7 +631,8 @@ UIHandle::Result SelectHandle::Click double value; // Shift-click, choose closest boundary SelectionBoundary boundary = - ChooseBoundary(viewInfo, xx, event.m_y, pTrack, mRect, false, false, &value); + ChooseBoundary(viewInfo, xx, event.m_y, + pView.get(), mRect, false, false, &value); mSelectionBoundary = boundary; switch (boundary) { case SBLeft: @@ -686,7 +698,7 @@ UIHandle::Result SelectHandle::Click if (viewInfo.bAdjustSelectionEdges) { #ifdef EXPERIMENTAL_SPECTRAL_EDITING if (mFreqSelMode == FREQ_SEL_SNAPPING_CENTER && - isSpectralSelectionTrack(pTrack)) { + isSpectralSelectionView(pView.get())) { // This code is no longer reachable, but it had a place in the // spectral selection prototype. It used to be that you could be // in a center-frequency-snapping mode that was not a mouse drag @@ -714,7 +726,8 @@ UIHandle::Result SelectHandle::Click // Not shift-down, choose boundary only within snapping double value; SelectionBoundary boundary = - ChooseBoundary(viewInfo, xx, event.m_y, pTrack, mRect, true, true, &value); + ChooseBoundary(viewInfo, xx, event.m_y, + pView.get(), mRect, true, true, &value); mSelectionBoundary = boundary; switch (boundary) { case SBNone: @@ -766,7 +779,8 @@ UIHandle::Result SelectHandle::Click // If we didn't move a selection boundary, start a NEW selection selectionState.SelectNone( trackList ); #ifdef EXPERIMENTAL_SPECTRAL_EDITING - StartFreqSelection (viewInfo, event.m_y, mRect.y, mRect.height, pTrack); + StartFreqSelection (viewInfo, event.m_y, mRect.y, mRect.height, + pView.get()); #endif StartSelection(pProject); selectionState.SelectTrack( *pTrack, true, true ); @@ -791,6 +805,10 @@ UIHandle::Result SelectHandle::Drag { using namespace RefreshCode; + const auto pView = mpView.lock(); + if ( !pView ) + return Cancelled; + auto &viewInfo = ViewInfo::Get( *pProject ); const wxMouseEvent &event = evt.event; @@ -814,7 +832,7 @@ UIHandle::Result SelectHandle::Drag } // Also fuhggeddaboudit if not in a track. - auto pTrack = TrackList::Get( *pProject ).Lock(mpTrack); + auto pTrack = TrackList::Get( *pProject ).Lock( FindTrack() ); if (!pTrack) return RefreshNone; @@ -850,7 +868,7 @@ UIHandle::Result SelectHandle::Drag if (mFreqSelMode == FREQ_SEL_SNAPPING_CENTER && !viewInfo.selectedRegion.isPoint()) MoveSnappingFreqSelection - (pProject, viewInfo, y, mRect.y, mRect.height, pTrack.get()); + (pProject, viewInfo, y, mRect.y, mRect.height, pView.get()); else #endif if ( TrackList::Get( *pProject ).Lock(mFreqSelTrack) == pTrack ) @@ -883,7 +901,11 @@ HitTestPreview SelectHandle::Preview // Moved out of snapping; revert to un-escaped state mUseSnap = true; - auto pTrack = mpTrack.lock(); + const auto pView = mpView.lock(); + if ( !pView ) + return {}; + + auto pTrack = FindTrack().lock(); if (!pTrack) return {}; @@ -937,7 +959,8 @@ HitTestPreview SelectHandle::Preview // choose boundaries only in snapping tolerance, // and may choose center. SelectionBoundary boundary = - ChooseBoundary(viewInfo, xx, state.m_y, pTrack.get(), rect, !bModifierDown, !bModifierDown); + ChooseBoundary(viewInfo, xx, state.m_y, + pView.get(), rect, !bModifierDown, !bModifierDown); SetTipAndCursorForBoundary(boundary, !bShiftDown, tip, pCursor); } @@ -949,7 +972,7 @@ HitTestPreview SelectHandle::Preview // and drag width. #ifdef EXPERIMENTAL_SPECTRAL_EDITING if ((mFreqSelMode == FREQ_SEL_SNAPPING_CENTER) && - isSpectralSelectionTrack(pTrack)) { + isSpectralSelectionView(pView)) { // Not shift-down, but center frequency snapping toggle is on tip = _("Click and drag to set frequency bandwidth."); pCursor = &*envelopeCursor; @@ -966,7 +989,8 @@ HitTestPreview SelectHandle::Preview const bool bCtrlDown = state.ControlDown(); const bool bModifierDown = bShiftDown || bCtrlDown; SelectionBoundary boundary = ChooseBoundary( - viewInfo, xx, state.m_y, pTrack.get(), rect, !bModifierDown, !bModifierDown); + viewInfo, xx, state.m_y, + pView.get(), rect, !bModifierDown, !bModifierDown); SetTipAndCursorForBoundary(boundary, !bShiftDown, tip, pCursor); } @@ -1034,6 +1058,15 @@ wxRect SelectHandle::DrawingArea( return rect; } +std::weak_ptr SelectHandle::FindTrack() +{ + auto pView = mpView.lock(); + if (!pView) + return {}; + else + return pView->FindTrack(); +} + void SelectHandle::Connect(AudacityProject *pProject) { mTimerHandler = std::make_shared( this, pProject ); @@ -1113,7 +1146,7 @@ void SelectHandle::TimerHandler::OnTimer(wxCommandEvent &event) } } - auto pTrack = mParent->mpTrack.lock(); // TrackList::Lock() ? + auto pTrack = mParent->FindTrack().lock(); // TrackList::Lock() ? if (mParent->mAutoScrolling && pTrack) { // AS: To keep the selection working properly as we scroll, // we fake a mouse event (remember, this method is called @@ -1164,7 +1197,7 @@ void SelectHandle::AdjustSelection auto pTrack = Track::SharedPointer( track ); if (!pTrack) - pTrack = TrackList::Get( *pProject ).Lock(mpTrack); + pTrack = TrackList::Get( *pProject ).Lock( FindTrack() ); if (pTrack && mSnapManager.get()) { bool rightEdge = (selend > mSelStart); @@ -1216,15 +1249,15 @@ void SelectHandle::AssignSelection void SelectHandle::StartFreqSelection(ViewInfo &viewInfo, int mouseYCoordinate, int trackTopEdge, - int trackHeight, Track *pTrack) + int trackHeight, TrackView *pTrackView) { mFreqSelTrack.reset(); mFreqSelMode = FREQ_SEL_INVALID; mFreqSelPin = SelectedRegion::UndefinedFrequency; - if (isSpectralSelectionTrack(pTrack)) { + if (isSpectralSelectionView(pTrackView)) { // Spectral selection track is always wave - auto shTrack = pTrack->SharedPointer(); + auto shTrack = pTrackView->FindTrack()->SharedPointer(); mFreqSelTrack = shTrack; mFreqSelMode = FREQ_SEL_FREE; mFreqSelPin = @@ -1386,11 +1419,12 @@ void SelectHandle::StartSnappingFreqSelection void SelectHandle::MoveSnappingFreqSelection (AudacityProject *pProject, ViewInfo &viewInfo, int mouseYCoordinate, int trackTopEdge, - int trackHeight, Track *pTrack) + int trackHeight, TrackView *pTrackView) { + auto pTrack = pTrackView->FindTrack().get(); if (pTrack && pTrack->GetSelected() && - isSpectralSelectionTrack(pTrack)) { + isSpectralSelectionView(pTrackView)) { // Spectral selection track is always wave WaveTrack *const wt = static_cast(pTrack); // PRL: diff --git a/src/tracks/ui/SelectHandle.h b/src/tracks/ui/SelectHandle.h index 39ef26c81..0ef6a82be 100644 --- a/src/tracks/ui/SelectHandle.h +++ b/src/tracks/ui/SelectHandle.h @@ -21,6 +21,7 @@ class SelectionStateChanger; class SnapManager; class SpectrumAnalyst; class Track; +class TrackView; class TrackList; class ViewInfo; class WaveTrack; @@ -32,7 +33,7 @@ class SelectHandle : public UIHandle public: explicit SelectHandle - (const std::shared_ptr &pTrack, bool useSnap, + (const std::shared_ptr &pTrackView, bool useSnap, const TrackList &trackList, const TrackPanelMouseState &st, const ViewInfo &viewInfo); @@ -41,7 +42,7 @@ public: static UIHandlePtr HitTest (std::weak_ptr &holder, const TrackPanelMouseState &state, const AudacityProject *pProject, - const std::shared_ptr &pTrack); + const std::shared_ptr &pTrackView); SelectHandle &operator=(const SelectHandle&) = default; @@ -78,6 +79,8 @@ public: const SelectHandle &newState); private: + std::weak_ptr FindTrack(); + void Connect(AudacityProject *pProject); void StartSelection(AudacityProject *pProject); @@ -89,7 +92,7 @@ private: void StartFreqSelection (ViewInfo &viewInfo, int mouseYCoordinate, int trackTopEdge, - int trackHeight, Track *pTrack); + int trackHeight, TrackView *pTrackView); void AdjustFreqSelection (const WaveTrack *wt, ViewInfo &viewInfo, int mouseYCoordinate, int trackTopEdge, @@ -104,7 +107,7 @@ private: void MoveSnappingFreqSelection (AudacityProject *pProject, ViewInfo &viewInfo, int mouseYCoordinate, int trackTopEdge, - int trackHeight, Track *pTrack); + int trackHeight, TrackView *pTrackView); public: // This is needed to implement a command assignable to keystrokes static void SnapCenterOnce @@ -124,7 +127,7 @@ private: // (const ViewInfo &viewInfo, double hintFrequency, bool logF); - std::weak_ptr mpTrack; + std::weak_ptr mpView; wxRect mRect{}; SelectedRegion mInitialSelection{}; diff --git a/src/tracks/ui/TrackView.cpp b/src/tracks/ui/TrackView.cpp index e0feffa05..08e47c718 100644 --- a/src/tracks/ui/TrackView.cpp +++ b/src/tracks/ui/TrackView.cpp @@ -114,6 +114,11 @@ auto TrackView::GetSubViews( const wxRect &rect ) -> Refinement return { { rect.GetTop(), shared_from_this() } }; } +bool TrackView::IsSpectral() const +{ + return false; +} + void TrackView::DoSetMinimized(bool isMinimized) { mMinimized = isMinimized; diff --git a/src/tracks/ui/TrackView.h b/src/tracks/ui/TrackView.h index 04ca9c86c..4037b5705 100644 --- a/src/tracks/ui/TrackView.h +++ b/src/tracks/ui/TrackView.h @@ -73,6 +73,9 @@ public: > >; virtual Refinement GetSubViews( const wxRect &rect ); + // default is false + virtual bool IsSpectral() const; + virtual void DoSetMinimized( bool isMinimized ); protected: