diff --git a/src/tracks/playabletrack/wavetrack/ui/SpectrumView.cpp b/src/tracks/playabletrack/wavetrack/ui/SpectrumView.cpp index f4e322c7d..b8e5e4c8b 100644 --- a/src/tracks/playabletrack/wavetrack/ui/SpectrumView.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/SpectrumView.cpp @@ -665,3 +665,144 @@ static const WaveTrackSubViews::RegisteredFactory key{ return std::make_shared< SpectrumView >( view ); } }; + +// The following attaches the spectrogram settings item to the wave track popup +// menu. It is appropriate only to spectrum view and so is kept in this +// source file with the rest of the spectrum view implementation. +#include "WaveTrackControls.h" +#include "../../../../AudioIOBase.h" +#include "../../../../Menus.h" +#include "../../../../ProjectHistory.h" +#include "../../../../RefreshCode.h" +#include "../../../../prefs/PrefsDialog.h" +#include "../../../../prefs/SpectrumPrefs.h" +#include "../../../../widgets/AudacityMessageBox.h" +#include "../../../../widgets/PopupMenuTable.h" + +namespace { +struct SpectrogramSettingsHandler : PopupMenuHandler { + + PlayableTrackControls::InitMenuData *mpData{}; + static SpectrogramSettingsHandler &Instance() + { + static SpectrogramSettingsHandler instance; + return instance; + } + + void OnSpectrogramSettings(wxCommandEvent &); + + void InitUserData(void *pUserData) override + { + mpData = static_cast< PlayableTrackControls::InitMenuData* >(pUserData); + } + + void DestroyMenu() override + { + mpData = nullptr; + } +}; + +void SpectrogramSettingsHandler::OnSpectrogramSettings(wxCommandEvent &) +{ + class ViewSettingsDialog final : public PrefsDialog + { + public: + ViewSettingsDialog(wxWindow *parent, AudacityProject &project, + const TranslatableString &title, PrefsDialog::Factories &factories, + int page) + : PrefsDialog(parent, &project, title, factories) + , mPage(page) + { + } + + long GetPreferredPage() override + { + return mPage; + } + + void SavePreferredPage() override + { + } + + private: + const int mPage; + }; + + auto gAudioIO = AudioIOBase::Get(); + if (gAudioIO->IsBusy()){ + AudacityMessageBox( + XO( +"To change Spectrogram Settings, stop any\n playing or recording first."), + XO("Stop the Audio First"), + wxOK | wxICON_EXCLAMATION | wxCENTRE); + return; + } + + WaveTrack *const pTrack = static_cast(mpData->pTrack); + + PrefsDialog::Factories factories; + // factories.push_back(WaveformPrefsFactory( pTrack )); + factories.push_back(SpectrumPrefsFactory( pTrack )); + const int page = + // (pTrack->GetDisplay() == WaveTrackViewConstants::Spectrum) ? 1 : + 0; + + auto title = XO("%s:").Format( pTrack->GetName() ); + ViewSettingsDialog dialog( + mpData->pParent, mpData->project, title, factories, page); + + if (0 != dialog.ShowModal()) { + // Redraw + AudacityProject *const project = &mpData->project; + ProjectHistory::Get( *project ).ModifyState(true); + //Bug 1725 Toolbar was left greyed out. + //This solution is overkill, but does fix the problem and is what the + //prefs dialog normally does. + MenuCreator::RebuildAllMenuBars(); + mpData->result = RefreshCode::RefreshAll; + } +} + +PopupMenuTable::AttachedItem sAttachment{ + GetWaveTrackMenuTable(), + { "SubViews/Extra" }, + std::make_unique( "SpectrogramSettings", + // Conditionally add menu item for settings, if showing spectrum + PopupMenuTable::Computed< WaveTrackPopupMenuTable >( + []( WaveTrackPopupMenuTable &table ) -> Registry::BaseItemPtr { + using Entry = PopupMenuTable::Entry; + static const int OnSpectrogramSettingsID = + GetWaveTrackMenuTable().ReserveId(); + + const auto pTrack = &table.FindWaveTrack(); + const auto &view = WaveTrackView::Get( *pTrack ); + const auto displays = view.GetDisplays(); + bool hasSpectrum = (displays.end() != std::find( + displays.begin(), displays.end(), + WaveTrackSubView::Type{ WaveTrackViewConstants::Spectrum, {} } + ) ); + if( hasSpectrum ) + // In future, we might move this to the context menu of the + // Spectrum vertical ruler. + // (But the latter won't be satisfactory without a means to + // open that other context menu with keystrokes only, and that + // would require some notion of a focused sub-view.) + return std::make_unique( "SpectrogramSettings", + Entry::Item, + OnSpectrogramSettingsID, + XO("S&pectrogram Settings..."), + (wxCommandEventFunction) + (&SpectrogramSettingsHandler::OnSpectrogramSettings), + SpectrogramSettingsHandler::Instance(), + []( PopupMenuHandler &handler, wxMenu &menu, int id ){ + // Bug 1253. Shouldn't open preferences if audio is busy. + // We can't change them on the fly yet anyway. + auto gAudioIO = AudioIOBase::Get(); + menu.Enable(id, !gAudioIO->IsBusy()); + } ); + else + return nullptr; + } ) ) +}; +} + diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp index eaea13faa..3950c75d6 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp @@ -17,6 +17,7 @@ Paul Licameli split from TrackPanel.cpp #include "WaveTrackSliderHandles.h" #include "WaveTrackView.h" +#include "WaveTrackViewConstants.h" #include "../../../../AudioIOBase.h" #include "../../../../CellularPanel.h" #include "../../../../Menus.h" @@ -33,9 +34,7 @@ Paul Licameli split from TrackPanel.cpp #include "../../../../effects/RealtimeEffectManager.h" #include "../../../../ondemand/ODManager.h" #include "../../../../prefs/PrefsDialog.h" -#include "../../../../prefs/SpectrumPrefs.h" #include "../../../../prefs/ThemePrefs.h" -#include "../../../../prefs/WaveformPrefs.h" #include "../../../../widgets/AudacityMessageBox.h" #include @@ -116,23 +115,20 @@ enum { OnSetDisplayId, lastDisplayId = (OnSetDisplayId + reserveDisplays - 1), - OnSpectrogramSettingsID, - OnChannelLeftID, OnChannelRightID, OnChannelMonoID, OnMergeStereoID, - OnInstrument1ID, - OnInstrument2ID, - OnInstrument3ID, - OnInstrument4ID, OnSwapChannelsID, OnSplitStereoID, OnSplitStereoMonoID, ChannelMenuID, + + // Range of ids for registered items -- keep this last! + FirstAttachedItemId, }; @@ -156,94 +152,6 @@ PopupMenuTableEntry::InitFunction initFn( const ValueFinder &findValue ) }; } -//============================================================================= -// Table class for a sub-menu -struct WaveColorMenuTable : PopupMenuTable -{ - WaveColorMenuTable() - : PopupMenuTable( "WaveColor", XO("&Wave Color") ) - {} - DECLARE_POPUP_MENU(WaveColorMenuTable); - - static WaveColorMenuTable &Instance(); - - void InitUserData(void *pUserData) override; - - void DestroyMenu() override - { - mpData = NULL; - } - - PlayableTrackControls::InitMenuData *mpData{}; - - static int IdOfWaveColor(int WaveColor); - void OnWaveColorChange(wxCommandEvent & event); -}; - -WaveColorMenuTable &WaveColorMenuTable::Instance() -{ - static WaveColorMenuTable instance; - return instance; -} - -void WaveColorMenuTable::InitUserData(void *pUserData) -{ - mpData = static_cast(pUserData); -} - -const TranslatableString GetWaveColorStr(int colorIndex) -{ - return XO("Instrument %i").Format( colorIndex+1 ); -} - -BEGIN_POPUP_MENU(WaveColorMenuTable) - static const auto fn = initFn< WaveColorMenuTable >( - []( WaveTrack &track ){ - return IdOfWaveColor( track.GetWaveColorIndex() ); - } - ); - - AppendRadioItem( "Instrument1", OnInstrument1ID, - GetWaveColorStr(0), POPUP_MENU_FN( OnWaveColorChange ), fn ); - AppendRadioItem( "Instrument2", OnInstrument2ID, - GetWaveColorStr(1), POPUP_MENU_FN( OnWaveColorChange ), fn ); - AppendRadioItem( "Instrument3", OnInstrument3ID, - GetWaveColorStr(2), POPUP_MENU_FN( OnWaveColorChange ), fn ); - AppendRadioItem( "Instrument4", OnInstrument4ID, - GetWaveColorStr(3), POPUP_MENU_FN( OnWaveColorChange ), fn ); - -END_POPUP_MENU() - -/// Converts a WaveColor enumeration to a wxWidgets menu item Id. -int WaveColorMenuTable::IdOfWaveColor(int WaveColor) -{ return OnInstrument1ID + WaveColor;} - -/// Handles the selection from the WaveColor submenu of the -/// track menu. -void WaveColorMenuTable::OnWaveColorChange(wxCommandEvent & event) -{ - int id = event.GetId(); - wxASSERT(id >= OnInstrument1ID && id <= OnInstrument4ID); - const auto pTrack = static_cast(mpData->pTrack); - - int newWaveColor = id - OnInstrument1ID; - - AudacityProject *const project = &mpData->project; - - for (auto channel : TrackList::Channels(pTrack)) - channel->SetWaveColorIndex(newWaveColor); - - ProjectHistory::Get( *project ) - .PushState(XO("Changed '%s' to %s") - .Format( pTrack->GetName(), GetWaveColorStr(newWaveColor) ), - XO("WaveColor Change")); - - using namespace RefreshCode; - mpData->result = RefreshAll | FixScrollbars; -} - - - //============================================================================= // Table class for a sub-menu @@ -552,6 +460,8 @@ void RateMenuTable::OnRateOther(wxCommandEvent &) mpData->result = RefreshAll | FixScrollbars; } +static const auto MenuPathStart = wxT("WaveTrackMenu"); + //============================================================================= // Class defining common command handlers for mono and stereo tracks struct WaveTrackMenuTable @@ -561,21 +471,22 @@ struct WaveTrackMenuTable WaveTrackMenuTable() : ComputedPopupMenuTable< WaveTrackMenuTable, WaveTrackPopupMenuTable >{ - "WaveTrack" } - {} + MenuPathStart } + { + mNextId = FirstAttachedItemId; + } void InitUserData(void *pUserData) override; void DestroyMenu() override { - mpData = nullptr; + //mpData = nullptr; } DECLARE_POPUP_MENU(WaveTrackMenuTable); void OnMultiView(wxCommandEvent & event); void OnSetDisplay(wxCommandEvent & event); - void OnSpectrogramSettings(wxCommandEvent & event); void OnChannelChange(wxCommandEvent & event); void OnMergeStereo(wxCommandEvent & event); @@ -694,55 +605,10 @@ BEGIN_POPUP_MENU(WaveTrackMenuTable) } ); ++id; } + BeginSection( "Extra" ); + EndSection(); EndSection(); - // Conditionally add sub-menu for wave color, if showing waveform - Append( []( My &table ) -> Registry::BaseItemPtr { - const auto pTrack = &table.FindWaveTrack(); - const auto &view = WaveTrackView::Get( *pTrack ); - const auto displays = view.GetDisplays(); - bool hasWaveform = (displays.end() != std::find( - displays.begin(), displays.end(), - WaveTrackSubView::Type{ WaveTrackViewConstants::Waveform, {} } - ) ); - if( hasWaveform ) - return std::make_unique( "WaveColor", - Registry::Shared( WaveColorMenuTable::Instance() - .Get( table.mpData ) ) ); - else - return nullptr; - } ); - - // Conditionally add sub-menu for spectrogram settings, if showing spectrum - Append( []( My &table ) -> Registry::BaseItemPtr { - const auto pTrack = &table.FindWaveTrack(); - const auto &view = WaveTrackView::Get( *pTrack ); - const auto displays = view.GetDisplays(); - bool hasSpectrum = (displays.end() != std::find( - displays.begin(), displays.end(), - WaveTrackSubView::Type{ WaveTrackViewConstants::Spectrum, {} } - ) ); - if( hasSpectrum ) - // In future, we might move this to the context menu of the - // Spectrum vertical ruler. - // (But the latter won't be satisfactory without a means to - // open that other context menu with keystrokes only, and that - // would require some notion of a focused sub-view.) - return std::make_unique( "SpectrogramSettings", - std::make_unique( "SpectrogramSettings", Entry::Item, - OnSpectrogramSettingsID, - XO("S&pectrogram Settings..."), - POPUP_MENU_FN( OnSpectrogramSettings ), table, - []( PopupMenuHandler &handler, wxMenu &menu, int id ){ - // Bug 1253. Shouldn't open preferences if audio is busy. - // We can't change them on the fly yet anyway. - auto gAudioIO = AudioIOBase::Get(); - menu.Enable(id, !gAudioIO->IsBusy()); - } ) ); - else - return nullptr; - } ); - BeginSection( "Channels" ); // If these are enabled again, choose a hot key for Mono that does not conflict // with Multi View @@ -885,67 +751,6 @@ void WaveTrackMenuTable::OnSetDisplay(wxCommandEvent & event) } } -void WaveTrackMenuTable::OnSpectrogramSettings(wxCommandEvent &) -{ - class ViewSettingsDialog final : public PrefsDialog - { - public: - ViewSettingsDialog(wxWindow *parent, AudacityProject &project, - const TranslatableString &title, PrefsDialog::Factories &factories, - int page) - : PrefsDialog(parent, &project, title, factories) - , mPage(page) - { - } - - long GetPreferredPage() override - { - return mPage; - } - - void SavePreferredPage() override - { - } - - private: - const int mPage; - }; - - auto gAudioIO = AudioIOBase::Get(); - if (gAudioIO->IsBusy()){ - AudacityMessageBox( - XO( -"To change Spectrogram Settings, stop any\n playing or recording first."), - XO("Stop the Audio First"), - wxOK | wxICON_EXCLAMATION | wxCENTRE); - return; - } - - WaveTrack *const pTrack = static_cast(mpData->pTrack); - - PrefsDialog::Factories factories; - // factories.push_back(WaveformPrefsFactory( pTrack )); - factories.push_back(SpectrumPrefsFactory( pTrack )); - const int page = - // (pTrack->GetDisplay() == WaveTrackViewConstants::Spectrum) ? 1 : - 0; - - auto title = XO("%s:").Format( pTrack->GetName() ); - ViewSettingsDialog dialog( - mpData->pParent, mpData->project, title, factories, page); - - if (0 != dialog.ShowModal()) { - // Redraw - AudacityProject *const project = &mpData->project; - ProjectHistory::Get( *project ).ModifyState(true); - //Bug 1725 Toolbar was left greyed out. - //This solution is overkill, but does fix the problem and is what the - //prefs dialog normally does. - MenuCreator::RebuildAllMenuBars(); - mpData->result = RefreshCode::RefreshAll; - } -} - #if 0 void WaveTrackMenuTable::OnChannelChange(wxCommandEvent & event) { @@ -1143,11 +948,22 @@ void WaveTrackMenuTable::OnSplitStereoMono(wxCommandEvent &) //============================================================================= PopupMenuTable *WaveTrackControls::GetMenuExtension(Track * pTrack) { + static Registry::OrderingPreferenceInitializer init{ + MenuPathStart, + { + {wxT("/SubViews/Extra"), wxT("WaveColor,SpectrogramSettings")}, + } + }; WaveTrackMenuTable & result = WaveTrackMenuTable::Instance(); return &result; } +WaveTrackPopupMenuTable &GetWaveTrackMenuTable() +{ + return WaveTrackMenuTable::Instance(); +} + // drawing related #include "../../../../widgets/ASlider.h" #include "../../../../TrackInfo.h" diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.h b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.h index 8fc2e43bc..bbe8fb558 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.h +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.h @@ -75,6 +75,12 @@ struct WaveTrackPopupMenuTable : public PopupMenuTable using PopupMenuTable::PopupMenuTable; PlayableTrackControls::InitMenuData *mpData{}; WaveTrack &FindWaveTrack () const; + int ReserveId() { return mNextId++; } +protected: + int mNextId = 0; }; +// Expose the wave track menu table to registration of menu items +WaveTrackPopupMenuTable &GetWaveTrackMenuTable(); + #endif diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveformView.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveformView.cpp index 341740b2f..794522ae7 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveformView.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveformView.cpp @@ -1039,3 +1039,143 @@ static const WaveTrackSubViews::RegisteredFactory key{ return std::make_shared< WaveformView >( view ); } }; + +// The following attaches the wave color sub-menu to the wave track popup +// menu. It is appropriate only to waveform view and so is kept in this +// source file with the rest of the waveform view implementation. + +#include // for std::call_once +#include "WaveTrackControls.h" +#include "../../../../widgets/PopupMenuTable.h" +#include "../../../../ProjectAudioIO.h" +#include "../../../../ProjectHistory.h" +#include "../../../../RefreshCode.h" + +//============================================================================= +// Table class for a sub-menu +struct WaveColorMenuTable : PopupMenuTable +{ + WaveColorMenuTable() : PopupMenuTable{ "WaveColor", XO("&Wave Color") } {} + DECLARE_POPUP_MENU(WaveColorMenuTable); + + static WaveColorMenuTable &Instance(); + + void InitUserData(void *pUserData) override; + + void DestroyMenu() override + { + mpData = NULL; + } + + PlayableTrackControls::InitMenuData *mpData{}; + + int IdOfWaveColor(int WaveColor); + void OnWaveColorChange(wxCommandEvent & event); + + int OnInstrument1ID, OnInstrument2ID, OnInstrument3ID, OnInstrument4ID; +}; + +WaveColorMenuTable &WaveColorMenuTable::Instance() +{ + static WaveColorMenuTable instance; + return instance; +} + +void WaveColorMenuTable::InitUserData(void *pUserData) +{ + mpData = static_cast(pUserData); +} + +namespace { +using ValueFinder = std::function< int( WaveTrack& ) >; + +const TranslatableString GetWaveColorStr(int colorIndex) +{ + return XO("Instrument %i").Format( colorIndex+1 ); +} +} + +BEGIN_POPUP_MENU(WaveColorMenuTable) + static const auto fn = []( PopupMenuHandler &handler, wxMenu &menu, int id ){ + auto &me = static_cast( handler ); + auto pData = me.mpData; + const auto &track = *static_cast(pData->pTrack); + auto &project = pData->project; + bool unsafe = ProjectAudioIO::Get( project ).IsAudioActive(); + + menu.Check( id, id == me.IdOfWaveColor( track.GetWaveColorIndex() ) ); + menu.Enable( id, !unsafe ); + }; + + static std::once_flag flag; + std::call_once( flag, [this]{ + auto &hostTable = GetWaveTrackMenuTable(); + OnInstrument1ID = hostTable.ReserveId(); + OnInstrument2ID = hostTable.ReserveId(); + OnInstrument3ID = hostTable.ReserveId(); + OnInstrument4ID = hostTable.ReserveId(); + } ); + + AppendRadioItem( "Instrument1", OnInstrument1ID, + GetWaveColorStr(0), POPUP_MENU_FN( OnWaveColorChange ), fn ); + AppendRadioItem( "Instrument2", OnInstrument2ID, + GetWaveColorStr(1), POPUP_MENU_FN( OnWaveColorChange ), fn ); + AppendRadioItem( "Instrument3", OnInstrument3ID, + GetWaveColorStr(2), POPUP_MENU_FN( OnWaveColorChange ), fn ); + AppendRadioItem( "Instrument4", OnInstrument4ID, + GetWaveColorStr(3), POPUP_MENU_FN( OnWaveColorChange ), fn ); + +END_POPUP_MENU() + +/// Converts a WaveColor enumeration to a wxWidgets menu item Id. +int WaveColorMenuTable::IdOfWaveColor(int WaveColor) +{ return OnInstrument1ID + WaveColor;} + +/// Handles the selection from the WaveColor submenu of the +/// track menu. +void WaveColorMenuTable::OnWaveColorChange(wxCommandEvent & event) +{ + int id = event.GetId(); + wxASSERT(id >= OnInstrument1ID && id <= OnInstrument4ID); + const auto pTrack = static_cast(mpData->pTrack); + + int newWaveColor = id - OnInstrument1ID; + + AudacityProject *const project = &mpData->project; + + for (auto channel : TrackList::Channels(pTrack)) + channel->SetWaveColorIndex(newWaveColor); + + ProjectHistory::Get( *project ) + .PushState(XO("Changed '%s' to %s") + .Format( pTrack->GetName(), GetWaveColorStr(newWaveColor) ), + XO("WaveColor Change")); + + using namespace RefreshCode; + mpData->result = RefreshAll | FixScrollbars; +} + +namespace { +PopupMenuTable::AttachedItem sAttachment{ + GetWaveTrackMenuTable(), + { "SubViews/Extra" }, + std::make_unique( "WaveColor", + // Conditionally add sub-menu for wave color, if showing waveform + PopupMenuTable::Computed< WaveTrackPopupMenuTable >( + []( WaveTrackPopupMenuTable &table ) -> Registry::BaseItemPtr { + const auto pTrack = &table.FindWaveTrack(); + const auto &view = WaveTrackView::Get( *pTrack ); + const auto displays = view.GetDisplays(); + bool hasWaveform = (displays.end() != std::find( + displays.begin(), displays.end(), + WaveTrackSubView::Type{ WaveTrackViewConstants::Waveform, {} } + ) ); + if( hasWaveform ) + return Registry::Shared( WaveColorMenuTable::Instance() + .Get( table.mpData ) ); + else + return nullptr; + } ) ) +}; +} +