1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-23 15:50:05 +02:00

Register Wave Color, Spectrogram Settings popup menu items...

... and so WaveTrackControls knows how to make multi views or switch among
 single views, and can change track contents such as display format and rate,
 but knows nothing about view-specific display details
This commit is contained in:
Paul Licameli 2020-02-15 15:57:17 -05:00
commit 06d4c0bdd7
6 changed files with 393 additions and 238 deletions

View File

@ -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<WaveTrack*>(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<PopupMenuSection>( "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<Entry>( "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;
} ) )
};
}

View File

@ -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"
@ -30,13 +31,10 @@ Paul Licameli split from TrackPanel.cpp
#include "../../../../TrackPanelAx.h"
#include "../../../../TrackPanelMouseEvent.h"
#include "../../../../WaveTrack.h"
#include "../../../../widgets/PopupMenuTable.h"
#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 <wx/combobox.h>
@ -87,6 +85,11 @@ std::vector<UIHandlePtr> WaveTrackControls::HitTest
return PlayableTrackControls::HitTest(st, pProject);
}
WaveTrack &WaveTrackPopupMenuTable::FindWaveTrack() const
{
return *static_cast< WaveTrack* >( mpData->pTrack );
};
enum {
reserveDisplays = 100,
@ -112,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,
};
@ -152,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<PlayableTrackControls::InitMenuData*>(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<WaveTrack*>(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
@ -548,30 +460,33 @@ 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 : ComputedPopupMenuTable< WaveTrackMenuTable >
struct WaveTrackMenuTable
: ComputedPopupMenuTable< WaveTrackMenuTable, WaveTrackPopupMenuTable >
{
static WaveTrackMenuTable &Instance();
WaveTrackMenuTable()
: ComputedPopupMenuTable< WaveTrackMenuTable >{ "WaveTrack" }
{}
: ComputedPopupMenuTable< WaveTrackMenuTable, WaveTrackPopupMenuTable >{
MenuPathStart }
{
mNextId = FirstAttachedItemId;
}
void InitUserData(void *pUserData) override;
void DestroyMenu() override
{
mpData = nullptr;
//mpData = nullptr;
}
DECLARE_POPUP_MENU(WaveTrackMenuTable);
PlayableTrackControls::InitMenuData *mpData{};
void OnMultiView(wxCommandEvent & event);
void OnSetDisplay(wxCommandEvent & event);
void OnSpectrogramSettings(wxCommandEvent & event);
void OnChannelChange(wxCommandEvent & event);
void OnMergeStereo(wxCommandEvent & event);
@ -608,15 +523,11 @@ static std::vector<WaveTrackSubViewType> AllTypes()
BEGIN_POPUP_MENU(WaveTrackMenuTable)
// Functions usable in callbacks to check and disable items
static const auto findTrack =
[]( PopupMenuHandler &handler ) -> WaveTrack & {
return *static_cast< WaveTrack* >(
static_cast< WaveTrackMenuTable& >( handler ).mpData->pTrack);
};
static const auto isMono =
[]( PopupMenuHandler &handler ) -> bool {
return 1 == TrackList::Channels( &findTrack( handler ) ).size();
auto &track =
static_cast< WaveTrackMenuTable& >( handler ).FindWaveTrack();
return 1 == TrackList::Channels( &track ).size();
};
static const auto isUnsafe =
@ -637,7 +548,8 @@ BEGIN_POPUP_MENU(WaveTrackMenuTable)
POPUP_MENU_FN( OnMultiView ),
table,
[]( PopupMenuHandler &handler, wxMenu &menu, int id ){
auto &track = findTrack( handler );
auto &table = static_cast< WaveTrackMenuTable& >( handler );
auto &track = table.FindWaveTrack();
const auto &view = WaveTrackView::Get( track );
menu.Check( id, view.GetMultiView() );
} );
@ -662,7 +574,9 @@ BEGIN_POPUP_MENU(WaveTrackMenuTable)
(std::find( begin, allTypes.end(), type ) - begin);
};
auto &track = findTrack( handler );
auto &table = static_cast< WaveTrackMenuTable& >( handler );
auto &track = table.FindWaveTrack();
const auto &view = WaveTrackView::Get( track );
const auto displays = view.GetDisplays();
@ -680,7 +594,7 @@ BEGIN_POPUP_MENU(WaveTrackMenuTable)
};
};
Append( [type, id]( My &table ) -> Registry::BaseItemPtr {
const auto pTrack = &findTrack( table );
const auto pTrack = &table.FindWaveTrack();
const auto &view = WaveTrackView::Get( *pTrack );
const auto itemType =
view.GetMultiView() ? Entry::CheckItem : Entry::RadioItem;
@ -691,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 = &findTrack( table );
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<PopupMenuSection>( "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 = &findTrack( table );
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<PopupMenuSection>( "SpectrogramSettings",
std::make_unique<Entry>( "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
@ -772,7 +641,8 @@ BEGIN_POPUP_MENU(WaveTrackMenuTable)
AudacityProject &project =
static_cast< WaveTrackMenuTable& >( handler ).mpData->project;
auto &tracks = TrackList::Get( project );
auto &track = findTrack( handler );
auto &table = static_cast< WaveTrackMenuTable& >( handler );
auto &track = table.FindWaveTrack();
auto next = * ++ tracks.Find(&track);
canMakeStereo =
(next &&
@ -786,8 +656,10 @@ BEGIN_POPUP_MENU(WaveTrackMenuTable)
AppendItem( "Swap", OnSwapChannelsID, XO("Swap Stereo &Channels"),
POPUP_MENU_FN( OnSwapChannels ),
[]( PopupMenuHandler &handler, wxMenu &menu, int id ){
auto &track =
static_cast< WaveTrackMenuTable& >( handler ).FindWaveTrack();
bool isStereo =
2 == TrackList::Channels( &findTrack( handler ) ).size();
2 == TrackList::Channels( &track ).size();
menu.Enable( id, isStereo && !isUnsafe( handler ) );
}
);
@ -879,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<WaveTrack*>(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)
{
@ -1137,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"
@ -1435,3 +1257,4 @@ auto GetDefaultWaveTrackHeight::Implementation() -> Function {
};
}
static GetDefaultWaveTrackHeight registerGetDefaultWaveTrackHeight;

View File

@ -68,4 +68,19 @@ private:
std::weak_ptr<PanSliderHandle> mPanHandle;
};
#include "../../../../widgets/PopupMenuTable.h"
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

View File

@ -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 <mutex> // 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<PlayableTrackControls::InitMenuData*>(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<WaveColorMenuTable&>( handler );
auto pData = me.mpData;
const auto &track = *static_cast<WaveTrack*>(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<WaveTrack*>(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<PopupMenuSection>( "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;
} ) )
};
}

View File

@ -142,7 +142,14 @@ void PopupMenuTable::ExtendMenu( wxMenu &menu, PopupMenuTable &table )
theMenu.tables.push_back( &table );
PopupMenuBuilder visitor{ table, theMenu, theMenu.pUserData };
Registry::Visit( visitor, table.Get( theMenu.pUserData ).get() );
Registry::Visit(
visitor, table.Get( theMenu.pUserData ).get(), table.GetRegistry() );
}
void PopupMenuTable::RegisterItem(
const Registry::Placement &placement, Registry::BaseItemPtr pItem )
{
Registry::RegisterItem( *mRegistry, placement, std::move( pItem ) );
}
void PopupMenuTable::Append( Registry::BaseItemPtr pItem )
@ -195,7 +202,8 @@ void PopupMenu::DisconnectTable(PopupMenuTable *pTable)
};
PopupMenuDestroyer visitor{ *pTable, *this };
Registry::Visit( visitor, pTable->Get( nullptr ).get() );
Registry::Visit( visitor, pTable->Get( nullptr ).get(),
pTable->GetRegistry() );
}
void PopupMenu::Disconnect()

View File

@ -109,6 +109,7 @@ public:
PopupMenuTable( const Identifier &id, const TranslatableString &caption = {} )
: mId{ id }
, mCaption{ caption }
, mRegistry{ std::make_unique<Registry::TransparentGroupItem<>>( mId ) }
{}
// Optional pUserData gets passed to the InitUserData routines of tables.
@ -118,6 +119,14 @@ public:
const Identifier &Id() const { return mId; }
const TranslatableString &Caption() const { return mCaption; }
const Registry::GroupItem *GetRegistry() const { return mRegistry.get(); }
// Typically statically constructed:
struct AttachedItem {
AttachedItem( PopupMenuTable &table,
const Registry::Placement &placement, Registry::BaseItemPtr pItem )
{ table.RegisterItem( placement, std::move( pItem ) ); }
};
// menu must have been built by BuildMenu
// More items get added to the end of it
@ -132,6 +141,26 @@ public:
return mTop;
}
// Forms a computed item, which may be omitted when function returns null
// and thus can be a conditional item
template< typename Table >
static Registry::BaseItemPtr Computed(
const std::function< Registry::BaseItemPtr( Table& ) > &factory )
{
using namespace Registry;
return std::make_unique< ComputedItem >(
[factory]( Visitor &baseVisitor ){
auto &visitor = static_cast< PopupMenuVisitor& >( baseVisitor );
auto &table = static_cast< Table& >( visitor.mTable );
return factory( table );
}
);
}
private:
void RegisterItem(
const Registry::Placement &placement, Registry::BaseItemPtr pItem );
protected:
virtual void Populate() = 0;
@ -168,31 +197,30 @@ protected:
std::vector< Registry::GroupItem* > mStack;
Identifier mId;
TranslatableString mCaption;
std::unique_ptr<Registry::GroupItem> mRegistry;
};
// A "CRTP" class that injects a convenience function, which appends a menu item
// computed lazily by a function that is passed the table (after it has stored
// its user data)
template< typename Derived >
class ComputedPopupMenuTable : public PopupMenuTable
template< typename Derived, typename Base = PopupMenuTable >
class ComputedPopupMenuTable : public Base
{
public:
using PopupMenuTable::PopupMenuTable;
using PopupMenuTable::Append;
using Base::Base;
using Base::Append;
// Appends a computed item, which may be omitted when function returns null
// and thus can be a conditional item
using Factory = std::function< Registry::BaseItemPtr( Derived& ) >;
static Registry::BaseItemPtr Computed( const Factory &factory )
{
return Base::Computed( factory );
}
void Append( const Factory &factory )
{
using namespace Registry;
Append( std::make_unique< ComputedItem >(
[factory]( Visitor &baseVisitor ){
auto &visitor = static_cast< PopupMenuVisitor& >( baseVisitor );
auto &table = static_cast< Derived& >( visitor.mTable );
return factory( table );
}
) );
Append( Computed( factory ) );
}
};