1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-07-30 07:29:29 +02:00

Preliminaries before a registration system for track popup menus

This commit is contained in:
Paul Licameli 2020-02-09 23:51:09 -05:00
commit 52bc74861a
12 changed files with 447 additions and 444 deletions

View File

@ -72,9 +72,9 @@ enum
};
BEGIN_POPUP_MENU(LabelTrackMenuTable)
BEGIN_POPUP_MENU_SECTION( "Basic" )
POPUP_MENU_ITEM( "Font", OnSetFontID, XO("&Font..."), OnSetFont)
END_POPUP_MENU_SECTION()
BeginSection( "Basic" );
AppendItem( "Font", OnSetFontID, XO("&Font..."), POPUP_MENU_FN( OnSetFont ) );
EndSection();
END_POPUP_MENU()
void LabelTrackMenuTable::OnSetFont(wxCommandEvent &)

View File

@ -131,10 +131,10 @@ void NoteTrackMenuTable::OnChangeOctave(wxCommandEvent &event)
}
BEGIN_POPUP_MENU(NoteTrackMenuTable)
BEGIN_POPUP_MENU_SECTION( "Basic" )
POPUP_MENU_ITEM( "Up", OnUpOctaveID, XO("Up &Octave"), OnChangeOctave)
POPUP_MENU_ITEM( "Down", OnDownOctaveID, XO("Down Octa&ve"), OnChangeOctave)
END_POPUP_MENU_SECTION()
BeginSection( "Basic" );
AppendItem( "Up", OnUpOctaveID, XO("Up &Octave"), POPUP_MENU_FN( OnChangeOctave ) );
AppendItem( "Down", OnDownOctaveID, XO("Down Octa&ve"), POPUP_MENU_FN( OnChangeOctave ) );
EndSection();
END_POPUP_MENU()
PopupMenuTable *NoteTrackControls::GetMenuExtension(Track *)

View File

@ -256,24 +256,24 @@ void NoteTrackVRulerMenuTable::OnZoom( int iZoomCode ){
BEGIN_POPUP_MENU(NoteTrackVRulerMenuTable)
BEGIN_POPUP_MENU_SECTION( "Zoom" )
BEGIN_POPUP_MENU_SECTION( "Basic" )
POPUP_MENU_ITEM( "Reset", OnZoomResetID, XO("Zoom Reset\tShift-Right-Click"), OnZoomReset)
POPUP_MENU_ITEM( "Max", OnZoomMaxID, XO("Max Zoom"), OnZoomMax)
END_POPUP_MENU_SECTION()
BeginSection( "Zoom" );
BeginSection( "Basic" );
AppendItem( "Reset", OnZoomResetID, XO("Zoom Reset\tShift-Right-Click"), POPUP_MENU_FN( OnZoomReset ) );
AppendItem( "Max", OnZoomMaxID, XO("Max Zoom"), POPUP_MENU_FN( OnZoomMax ) );
EndSection();
BEGIN_POPUP_MENU_SECTION( "InOut" )
POPUP_MENU_ITEM( "In", OnZoomInVerticalID, XO("Zoom In\tLeft-Click/Left-Drag"), OnZoomInVertical)
POPUP_MENU_ITEM( "Out", OnZoomOutVerticalID, XO("Zoom Out\tShift-Left-Click"), OnZoomOutVertical)
END_POPUP_MENU_SECTION()
END_POPUP_MENU_SECTION()
BeginSection( "InOut" );
AppendItem( "In", OnZoomInVerticalID, XO("Zoom In\tLeft-Click/Left-Drag"), POPUP_MENU_FN( OnZoomInVertical ) );
AppendItem( "Out", OnZoomOutVerticalID, XO("Zoom Out\tShift-Left-Click"), POPUP_MENU_FN( OnZoomOutVertical ) );
EndSection();
EndSection();
BEGIN_POPUP_MENU_SECTION( "Pan" )
BEGIN_POPUP_MENU_SECTION( "Octaves" )
POPUP_MENU_ITEM( "Up", OnUpOctaveID, XO("Up &Octave"), OnUpOctave)
POPUP_MENU_ITEM( "Down", OnDownOctaveID, XO("Down Octa&ve"), OnDownOctave)
END_POPUP_MENU_SECTION()
END_POPUP_MENU_SECTION()
BeginSection( "Pan" );
BeginSection( "Octaves" );
AppendItem( "Up", OnUpOctaveID, XO("Up &Octave"), POPUP_MENU_FN( OnUpOctave) );
AppendItem( "Down", OnDownOctaveID, XO("Down Octa&ve"), POPUP_MENU_FN( OnDownOctave ) );
EndSection();
EndSection();
END_POPUP_MENU()

View File

@ -265,36 +265,40 @@ PopupMenuTable &SpectrumVRulerMenuTable::Instance()
return instance;
}
void SpectrumVRulerMenuTable::InitMenu(wxMenu *pMenu)
{
WaveTrackVRulerMenuTable::InitMenu(pMenu);
WaveTrack *const wt = mpData->pTrack;
const int id =
OnFirstSpectrumScaleID + (int)(wt->GetSpectrogramSettings().scaleType);
pMenu->Check(id, true);
}
BEGIN_POPUP_MENU(SpectrumVRulerMenuTable)
BEGIN_POPUP_MENU_SECTION( "Scales" )
BeginSection( "Scales" );
{
const auto & names = SpectrogramSettings::GetScaleNames();
for (int ii = 0, nn = names.size(); ii < nn; ++ii) {
POPUP_MENU_RADIO_ITEM( names[ii].Internal(),
AppendRadioItem( names[ii].Internal(),
OnFirstSpectrumScaleID + ii, names[ii].Msgid(),
OnSpectrumScaleType);
POPUP_MENU_FN( OnSpectrumScaleType ),
[]( PopupMenuHandler &handler, wxMenu &menu, int id ){
WaveTrack *const wt =
static_cast<SpectrumVRulerMenuTable&>( handler )
.mpData->pTrack;
if ( id ==
OnFirstSpectrumScaleID +
(int)(wt->GetSpectrogramSettings().scaleType ) )
menu.Check(id, true);
}
);
}
}
END_POPUP_MENU_SECTION()
EndSection();
BEGIN_POPUP_MENU_SECTION( "Zoom" )
POPUP_MENU_ITEM( "Reset", OnZoomResetID, XO("Zoom Reset"), OnZoomReset)
POPUP_MENU_ITEM( "Fit", OnZoomFitVerticalID, XO("Zoom to Fit\tShift-Right-Click"), OnZoomFitVertical)
POPUP_MENU_ITEM( "In", OnZoomInVerticalID, XO("Zoom In\tLeft-Click/Left-Drag"), OnZoomInVertical)
POPUP_MENU_ITEM( "Out", OnZoomOutVerticalID, XO("Zoom Out\tShift-Left-Click"), OnZoomOutVertical)
END_POPUP_MENU_SECTION()
BeginSection( "Zoom" );
AppendItem( "Reset", OnZoomResetID, XO("Zoom Reset"),
POPUP_MENU_FN( OnZoomReset ) );
AppendItem( "Fit", OnZoomFitVerticalID, XO("Zoom to Fit\tShift-Right-Click"),
POPUP_MENU_FN( OnZoomFitVertical ) );
AppendItem( "In", OnZoomInVerticalID, XO("Zoom In\tLeft-Click/Left-Drag"),
POPUP_MENU_FN( OnZoomInVertical ) );
AppendItem( "Out", OnZoomOutVerticalID, XO("Zoom Out\tShift-Left-Click"),
POPUP_MENU_FN( OnZoomOutVertical ) );
EndSection();
END_POPUP_MENU()

View File

@ -85,8 +85,6 @@ public:
static PopupMenuTable &Instance();
private:
void InitMenu(wxMenu *pMenu) override;
void OnSpectrumScaleType(wxCommandEvent &evt);
};

View File

@ -43,22 +43,6 @@ Paul Licameli split from TrackPanel.cpp
#include <wx/frame.h>
#include <wx/sizer.h>
namespace
{
/// Puts a check mark at a given position in a menu.
template<typename Pred>
void SetMenuChecks(wxMenu & menu, const Pred &pred)
{
for (auto &item : menu.GetMenuItems())
{
if (item->IsCheckable()) {
auto id = item->GetId();
menu.Check(id, pred(id));
}
}
}
}
WaveTrackControls::~WaveTrackControls()
{
}
@ -148,21 +132,38 @@ enum {
};
namespace {
using ValueFinder = std::function< int( WaveTrack& ) >;
// A function that makes functions that check and enable sub-menu items,
// parametrized by how you get the relevant value from a track's settings
template< typename Table >
PopupMenuTableEntry::InitFunction initFn( const ValueFinder &findValue )
{
return [findValue]( PopupMenuHandler &handler, wxMenu &menu, int id ){
auto pData = static_cast<Table&>( handler ).mpData;
const auto pTrack = static_cast<WaveTrack*>(pData->pTrack);
auto &project = pData->project;
bool unsafe = ProjectAudioIO::Get( project ).IsAudioActive();
menu.Check( id, id == findValue( *pTrack ) );
menu.Enable( id, !unsafe );
};
};
}
//=============================================================================
// Table class for a sub-menu
class WaveColorMenuTable : public PopupMenuTable
struct WaveColorMenuTable : PopupMenuTable
{
WaveColorMenuTable()
: PopupMenuTable( "WaveColor", XO("&Wave Color") )
{}
DECLARE_POPUP_MENU(WaveColorMenuTable);
public:
static WaveColorMenuTable &Instance();
private:
void InitUserData(void *pUserData) override;
void InitMenu(wxMenu *pMenu) override;
void DestroyMenu() override
{
@ -171,7 +172,7 @@ private:
PlayableTrackControls::InitMenuData *mpData{};
int IdOfWaveColor(int WaveColor);
static int IdOfWaveColor(int WaveColor);
void OnWaveColorChange(wxCommandEvent & event);
};
@ -186,34 +187,27 @@ void WaveColorMenuTable::InitUserData(void *pUserData)
mpData = static_cast<PlayableTrackControls::InitMenuData*>(pUserData);
}
void WaveColorMenuTable::InitMenu(wxMenu *pMenu)
{
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
auto WaveColorId = IdOfWaveColor( pTrack->GetWaveColorIndex());
SetMenuChecks(*pMenu, [=](int id){ return id == WaveColorId; });
AudacityProject *const project = &mpData->project;
bool unsafe = ProjectAudioIO::Get( *project ).IsAudioActive();
for (int i = OnInstrument1ID; i <= OnInstrument4ID; i++) {
pMenu->Enable(i, !unsafe);
}
}
const TranslatableString GetWaveColorStr(int colorIndex)
{
return XO("Instrument %i").Format( colorIndex+1 );
}
BEGIN_POPUP_MENU(WaveColorMenuTable)
POPUP_MENU_RADIO_ITEM( "Instrument1", OnInstrument1ID,
GetWaveColorStr(0), OnWaveColorChange)
POPUP_MENU_RADIO_ITEM( "Instrument2", OnInstrument2ID,
GetWaveColorStr(1), OnWaveColorChange)
POPUP_MENU_RADIO_ITEM( "Instrument3", OnInstrument3ID,
GetWaveColorStr(2), OnWaveColorChange)
POPUP_MENU_RADIO_ITEM( "Instrument4", OnInstrument4ID,
GetWaveColorStr(3), OnWaveColorChange)
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.
@ -249,19 +243,16 @@ void WaveColorMenuTable::OnWaveColorChange(wxCommandEvent & event)
//=============================================================================
// Table class for a sub-menu
class FormatMenuTable : public PopupMenuTable
struct FormatMenuTable : PopupMenuTable
{
FormatMenuTable()
: PopupMenuTable{ "SampleFormat", XO("&Format") }
{}
DECLARE_POPUP_MENU(FormatMenuTable);
public:
static FormatMenuTable &Instance();
private:
void InitUserData(void *pUserData) override;
void InitMenu(wxMenu *pMenu) override;
void DestroyMenu() override
{
@ -270,7 +261,7 @@ private:
PlayableTrackControls::InitMenuData *mpData{};
int IdOfFormat(int format);
static int IdOfFormat(int format);
void OnFormatChange(wxCommandEvent & event);
};
@ -286,26 +277,21 @@ void FormatMenuTable::InitUserData(void *pUserData)
mpData = static_cast<PlayableTrackControls::InitMenuData*>(pUserData);
}
void FormatMenuTable::InitMenu(wxMenu *pMenu)
{
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
auto formatId = IdOfFormat(pTrack->GetSampleFormat());
SetMenuChecks(*pMenu, [=](int id){ return id == formatId; });
AudacityProject *const project = &mpData->project;
bool unsafe = ProjectAudioIO::Get( *project ).IsAudioActive();
for (int i = On16BitID; i <= OnFloatID; i++) {
pMenu->Enable(i, !unsafe);
}
}
BEGIN_POPUP_MENU(FormatMenuTable)
POPUP_MENU_RADIO_ITEM( "16Bit", On16BitID,
GetSampleFormatStr(int16Sample), OnFormatChange)
POPUP_MENU_RADIO_ITEM("24Bit", On24BitID,
GetSampleFormatStr( int24Sample), OnFormatChange)
POPUP_MENU_RADIO_ITEM( "Float", OnFloatID,
GetSampleFormatStr(floatSample), OnFormatChange)
static const auto fn = initFn< FormatMenuTable >(
[]( WaveTrack &track ){
return IdOfFormat( track.GetSampleFormat() );
}
);
AppendRadioItem( "16Bit", On16BitID,
GetSampleFormatStr(int16Sample), POPUP_MENU_FN( OnFormatChange ), fn );
AppendRadioItem("24Bit", On24BitID,
GetSampleFormatStr( int24Sample), POPUP_MENU_FN( OnFormatChange ), fn );
AppendRadioItem( "Float", OnFloatID,
GetSampleFormatStr(floatSample), POPUP_MENU_FN( OnFormatChange ), fn );
END_POPUP_MENU()
/// Converts a format enumeration to a wxWidgets menu item Id.
@ -372,19 +358,16 @@ void FormatMenuTable::OnFormatChange(wxCommandEvent & event)
//=============================================================================
// Table class for a sub-menu
class RateMenuTable : public PopupMenuTable
struct RateMenuTable : PopupMenuTable
{
RateMenuTable()
: PopupMenuTable{ "SampleRate", XO("Rat&e") }
{}
DECLARE_POPUP_MENU(RateMenuTable);
public:
static RateMenuTable &Instance();
private:
void InitUserData(void *pUserData) override;
void InitMenu(wxMenu *pMenu) override;
void DestroyMenu() override
{
@ -393,7 +376,7 @@ private:
PlayableTrackControls::InitMenuData *mpData{};
int IdOfRate(int rate);
static int IdOfRate(int rate);
void SetRate(WaveTrack * pTrack, double rate);
void OnRateChange(wxCommandEvent & event);
@ -411,36 +394,30 @@ void RateMenuTable::InitUserData(void *pUserData)
mpData = static_cast<PlayableTrackControls::InitMenuData*>(pUserData);
}
void RateMenuTable::InitMenu(wxMenu *pMenu)
{
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
const auto rateId = IdOfRate((int)pTrack->GetRate());
SetMenuChecks(*pMenu, [=](int id){ return id == rateId; });
AudacityProject *const project = &mpData->project;
bool unsafe = ProjectAudioIO::Get( *project ).IsAudioActive();
for (int i = OnRate8ID; i <= OnRateOtherID; i++) {
pMenu->Enable(i, !unsafe);
}
}
// Because of Bug 1780 we can't use POPUP_MENU_RADIO_ITEM
// Because of Bug 1780 we can't use AppendRadioItem
// If we did, we'd get no message when clicking on Other...
// when it is already selected.
BEGIN_POPUP_MENU(RateMenuTable)
POPUP_MENU_CHECK_ITEM( "8000", OnRate8ID, XO("8000 Hz"), OnRateChange)
POPUP_MENU_CHECK_ITEM( "11025", OnRate11ID, XO("11025 Hz"), OnRateChange)
POPUP_MENU_CHECK_ITEM( "16000", OnRate16ID, XO("16000 Hz"), OnRateChange)
POPUP_MENU_CHECK_ITEM( "22050", OnRate22ID, XO("22050 Hz"), OnRateChange)
POPUP_MENU_CHECK_ITEM( "44100", OnRate44ID, XO("44100 Hz"), OnRateChange)
POPUP_MENU_CHECK_ITEM( "48000", OnRate48ID, XO("48000 Hz"), OnRateChange)
POPUP_MENU_CHECK_ITEM( "88200", OnRate88ID, XO("88200 Hz"), OnRateChange)
POPUP_MENU_CHECK_ITEM( "96000", OnRate96ID, XO("96000 Hz"), OnRateChange)
POPUP_MENU_CHECK_ITEM( "176400", OnRate176ID, XO("176400 Hz"), OnRateChange)
POPUP_MENU_CHECK_ITEM( "192000", OnRate192ID, XO("192000 Hz"), OnRateChange)
POPUP_MENU_CHECK_ITEM( "352800", OnRate352ID, XO("352800 Hz"), OnRateChange)
POPUP_MENU_CHECK_ITEM( "384000", OnRate384ID, XO("384000 Hz"), OnRateChange)
POPUP_MENU_CHECK_ITEM( "Other", OnRateOtherID, XO("&Other..."), OnRateOther)
static const auto fn = initFn< RateMenuTable >(
[]( WaveTrack &track ){
return IdOfRate( (int)track.GetRate() );
}
);
AppendCheckItem( "8000", OnRate8ID, XO("8000 Hz"), POPUP_MENU_FN( OnRateChange ), fn );
AppendCheckItem( "11025", OnRate11ID, XO("11025 Hz"), POPUP_MENU_FN( OnRateChange ), fn );
AppendCheckItem( "16000", OnRate16ID, XO("16000 Hz"), POPUP_MENU_FN( OnRateChange ), fn );
AppendCheckItem( "22050", OnRate22ID, XO("22050 Hz"), POPUP_MENU_FN( OnRateChange ), fn );
AppendCheckItem( "44100", OnRate44ID, XO("44100 Hz"), POPUP_MENU_FN( OnRateChange ), fn );
AppendCheckItem( "48000", OnRate48ID, XO("48000 Hz"), POPUP_MENU_FN( OnRateChange ), fn );
AppendCheckItem( "88200", OnRate88ID, XO("88200 Hz"), POPUP_MENU_FN( OnRateChange ), fn );
AppendCheckItem( "96000", OnRate96ID, XO("96000 Hz"), POPUP_MENU_FN( OnRateChange ), fn );
AppendCheckItem( "176400", OnRate176ID, XO("176400 Hz"), POPUP_MENU_FN( OnRateChange ), fn );
AppendCheckItem( "192000", OnRate192ID, XO("192000 Hz"), POPUP_MENU_FN( OnRateChange ), fn );
AppendCheckItem( "352800", OnRate352ID, XO("352800 Hz"), POPUP_MENU_FN( OnRateChange ), fn );
AppendCheckItem( "384000", OnRate384ID, XO("384000 Hz"), POPUP_MENU_FN( OnRateChange ), fn );
AppendCheckItem( "Other", OnRateOtherID, XO("&Other..."), POPUP_MENU_FN( OnRateOther ), fn );
END_POPUP_MENU()
const int nRates = 12;
@ -573,19 +550,16 @@ void RateMenuTable::OnRateOther(wxCommandEvent &)
//=============================================================================
// Class defining common command handlers for mono and stereo tracks
class WaveTrackMenuTable : public PopupMenuTable
struct WaveTrackMenuTable : PopupMenuTable
{
public:
static WaveTrackMenuTable &Instance( Track * pTrack);
Track * mpTrack{};
protected:
WaveTrackMenuTable()
: PopupMenuTable{ "WaveTrack" }
{}
void InitUserData(void *pUserData) override;
void InitMenu(wxMenu *pMenu) override;
void DestroyMenu() override
{
@ -639,149 +613,84 @@ static std::vector<WaveTrackSubViewType> AllTypes()
return result;
}
void WaveTrackMenuTable::InitMenu(wxMenu *pMenu)
{
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
std::vector<int> checkedIds;
const auto &view = WaveTrackView::Get( *pTrack );
auto multiView = view.GetMultiView();
if (multiView)
checkedIds.push_back( OnMultiViewID );
bool hasSpectrum = false;
int uniqueDisplay = 0;
{
// Find the set of type ids of the shown displays, disregarding their
// top-to-bottom arrangement
auto displays = view.GetDisplays();
const auto end = displays.end();
auto iter = displays.begin();
std::sort( iter, end );
// Check the corresponding menu items, and decide which if any has
// the unique check
int displayId = OnSetDisplayId;
int nDisplays = 0;
for ( const auto &type : AllTypes() ) {
if ( iter != end && iter->id == type.id ) {
checkedIds.push_back( displayId );
uniqueDisplay = displayId;
++iter;
++nDisplays;
// Unfortunately this special knowledge of the Spectrum view type
// remains. In future, either let a registry system insert this
// menu item, or (better) move it 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.)
hasSpectrum = ( type.id == WaveTrackViewConstants::Spectrum );
}
++displayId;
}
if ( nDisplays > 1 )
uniqueDisplay = 0;
}
if ( multiView && uniqueDisplay )
// Bug2275 residual
// Disable the checking-off of the only sub-view
pMenu->Enable( uniqueDisplay, false );
// Bug 1253. Shouldn't open preferences if audio is busy.
// We can't change them on the fly yet anyway.
auto gAudioIO = AudioIOBase::Get();
if ( hasSpectrum )
pMenu->Enable(OnSpectrogramSettingsID, !gAudioIO->IsBusy());
AudacityProject *const project = &mpData->project;
auto &tracks = TrackList::Get( *project );
bool unsafe = RealtimeEffectManager::Get().RealtimeIsActive() &&
ProjectAudioIO::Get( *project ).IsAudioActive();
auto nChannels = TrackList::Channels(pTrack).size();
const bool isMono = ( nChannels == 1 );
const bool isStereo = ( nChannels == 2 );
// Maybe more than stereo tracks some time?
if ( isMono )
{
WaveTrack *const pTrack2 = static_cast<WaveTrack*>(mpData->pTrack);
auto next = * ++ tracks.Find(pTrack2);
if (isMono) {
const bool canMakeStereo =
(next &&
TrackList::Channels(next).size() == 1 &&
track_cast<WaveTrack*>(next));
pMenu->Enable(OnMergeStereoID, canMakeStereo && !unsafe);
int itemId;
switch (pTrack2->GetChannel()) {
case Track::LeftChannel:
itemId = OnChannelLeftID;
break;
case Track::RightChannel:
itemId = OnChannelRightID;
break;
default:
itemId = OnChannelMonoID;
break;
}
checkedIds.push_back(itemId);
}
}
else
{
pMenu->Enable(OnMergeStereoID, false);
}
SetMenuChecks(*pMenu, [&](int id){
auto end = checkedIds.end();
return end != std::find(checkedIds.begin(), end, id);
});
// Enable this only for properly stereo tracks:
pMenu->Enable(OnSwapChannelsID, isStereo && !unsafe);
pMenu->Enable(OnSplitStereoID, !isMono && !unsafe);
#ifndef EXPERIMENTAL_DA
// Can be achieved by split stereo and then dragging pan slider.
pMenu->Enable(OnSplitStereoMonoID, !isMono && !unsafe);
#endif
// Several menu items no longer needed....
#if 0
pMenu->Enable(OnChannelMonoID, isMono);
pMenu->Enable(OnChannelLeftID, isMono);
pMenu->Enable(OnChannelRightID, isMono);
#endif
}
BEGIN_POPUP_MENU(WaveTrackMenuTable)
// Functions usable "now" (list population time) and also "later"
// (in callbacks to check and disable items)
static const auto findTrack =
[]( PopupMenuHandler &handler ) -> WaveTrack & {
return *static_cast< WaveTrack* >(
static_cast< WaveTrackMenuTable& >( handler ).mpData->pTrack);
};
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpTrack);
static const auto isMono =
[]( PopupMenuHandler &handler ) -> bool {
return 1 == TrackList::Channels( &findTrack( handler ) ).size();
};
static const auto isUnsafe =
[]( PopupMenuHandler &handler ) -> bool {
auto &project =
static_cast< WaveTrackMenuTable& >( handler ).mpData->project;
return RealtimeEffectManager::Get().RealtimeIsActive() &&
ProjectAudioIO::Get( project ).IsAudioActive();
};
const auto pTrack = &findTrack( *this );
const auto &view = WaveTrackView::Get( *pTrack );
BEGIN_POPUP_MENU_SECTION( "SubViews" )
BeginSection( "SubViews" );
if ( WaveTrackSubViews::slots() > 1 )
POPUP_MENU_CHECK_ITEM( "MultiView", OnMultiViewID, XO("&Multi-view"), OnMultiView)
AppendCheckItem( "MultiView", OnMultiViewID, XO("&Multi-view"),
POPUP_MENU_FN( OnMultiView ),
[]( PopupMenuHandler &handler, wxMenu &menu, int id ){
auto &track = findTrack( handler );
const auto &view = WaveTrackView::Get( track );
menu.Check( id, view.GetMultiView() );
}
);
int id = OnSetDisplayId;
for ( const auto &type : AllTypes() ) {
static const auto initFn = []( bool radio ){ return
[radio]( PopupMenuHandler &handler, wxMenu &menu, int id ){
// Find all known sub-view types
const auto allTypes = AllTypes();
// How to convert a type to a menu item id
const auto IdForType =
[&allTypes]( const WaveTrackSubViewType &type ) -> int {
const auto begin = allTypes.begin();
return OnSetDisplayId +
(std::find( begin, allTypes.end(), type ) - begin);
};
auto &track = findTrack( handler );
const auto &view = WaveTrackView::Get( track );
const auto displays = view.GetDisplays();
const auto end = displays.end();
bool check = (end !=
std::find_if( displays.begin(), end,
[&]( const WaveTrackSubViewType &type ){
return id == IdForType( type ); } ) );
menu.Check( id, check );
// Bug2275 residual
// Disable the checking-off of the only sub-view
if ( !radio && displays.size() == 1 && check )
menu.Enable( id, false );
};
};
if ( view.GetMultiView() ) {
POPUP_MENU_CHECK_ITEM( type.name.Internal(), id++, type.name.Msgid(), OnSetDisplay)
AppendCheckItem( type.name.Internal(), id++, type.name.Msgid(),
POPUP_MENU_FN( OnSetDisplay ), initFn( false ) );
}
else {
POPUP_MENU_RADIO_ITEM( type.name.Internal(), id++, type.name.Msgid(), OnSetDisplay)
AppendRadioItem( type.name.Internal(), id++, type.name.Msgid(),
POPUP_MENU_FN( OnSetDisplay ), initFn( true ) );
}
}
END_POPUP_MENU_SECTION()
EndSection();
if ( pTrack ) {
@ -791,9 +700,9 @@ BEGIN_POPUP_MENU(WaveTrackMenuTable)
WaveTrackSubView::Type{ WaveTrackViewConstants::Waveform, {} }
) );
if( hasWaveform ){
BEGIN_POPUP_MENU_SECTION( "WaveColor" )
BeginSection( "WaveColor" );
POPUP_MENU_SUB_MENU( "WaveColor", WaveColorMenuTable, mpData )
END_POPUP_MENU_SECTION()
EndSection();
}
bool hasSpectrum = (displays.end() != std::find(
@ -801,36 +710,98 @@ BEGIN_POPUP_MENU(WaveTrackMenuTable)
WaveTrackSubView::Type{ WaveTrackViewConstants::Spectrum, {} }
) );
if( hasSpectrum ){
BEGIN_POPUP_MENU_SECTION( "SpectrogramSettings" )
POPUP_MENU_ITEM( "SpectrogramSettings", OnSpectrogramSettingsID, XO("S&pectrogram Settings..."), OnSpectrogramSettings)
END_POPUP_MENU_SECTION()
// 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.)
BeginSection( "SpectrogramSettings" );
AppendItem( "SpectrogramSettings", OnSpectrogramSettingsID, XO("S&pectrogram Settings..."), POPUP_MENU_FN( OnSpectrogramSettings ),
[]( 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());
}
);
EndSection();
}
}
BEGIN_POPUP_MENU_SECTION( "Channels" )
BeginSection( "Channels" );
// 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)
POPUP_MENU_ITEM( "MakeStereo", OnMergeStereoID, XO("Ma&ke Stereo Track"), OnMergeStereo)
// AppendRadioItem(OnChannelMonoID, XO("&Mono"),
// POPUP_MENU_FN( OnChannelChange ),
// []( PopupMenuHandler &handler, wxMenu &menu, int id ){
// menu.Enable( id, isMono( handler ) );
// menu.Check( id, findTrack( handler ).GetChannel() == Track::MonoChannel );
// }
// );
// AppendRadioItem(OnChannelLeftID, XO("&Left Channel"),
// POPUP_MENU_FN( OnChannelChange ),
// []( PopupMenuHandler &handler, wxMenu &menu, int id ){
// menu.Enable( id, isMono( handler ) );
// menu.Check( id, findTrack( handler ).GetChannel() == Track::LeftChannel );
// }
// );
// AppendRadioItem(OnChannelRightID, XO("R&ight Channel"),
// POPUP_MENU_FN( OnChannelChange ),
// []( PopupMenuHandler &handler, wxMenu &menu, int id ){
// menu.Enable( id, isMono( handler ) );
// menu.Check( id, findTrack( handler ).GetChannel() == Track::RightChannel );
// }
// );
AppendItem( "MakeStereo", OnMergeStereoID, XO("Ma&ke Stereo Track"),
POPUP_MENU_FN( OnMergeStereo ),
[]( PopupMenuHandler &handler, wxMenu &menu, int id ){
bool canMakeStereo = !isUnsafe( handler ) && isMono( handler );
if ( canMakeStereo ) {
AudacityProject &project =
static_cast< WaveTrackMenuTable& >( handler ).mpData->project;
auto &tracks = TrackList::Get( project );
auto &track = findTrack( handler );
auto next = * ++ tracks.Find(&track);
canMakeStereo =
(next &&
TrackList::Channels(next).size() == 1 &&
track_cast<WaveTrack*>(next));
}
menu.Enable( id, canMakeStereo );
}
);
POPUP_MENU_ITEM( "Swap", OnSwapChannelsID, XO("Swap Stereo &Channels"), OnSwapChannels)
POPUP_MENU_ITEM( "Split", OnSplitStereoID, XO("Spl&it Stereo Track"), OnSplitStereo)
AppendItem( "Swap", OnSwapChannelsID, XO("Swap Stereo &Channels"),
POPUP_MENU_FN( OnSwapChannels ),
[]( PopupMenuHandler &handler, wxMenu &menu, int id ){
bool isStereo =
2 == TrackList::Channels( &findTrack( handler ) ).size();
menu.Enable( id, isStereo && !isUnsafe( handler ) );
}
);
static const auto enableSplitStereo =
[]( PopupMenuHandler &handler, wxMenu &menu, int id ){
menu.Enable( id, !isMono( handler ) && !isUnsafe( handler ) );
};
AppendItem( "Split", OnSplitStereoID, XO("Spl&it Stereo Track"),
POPUP_MENU_FN( OnSplitStereo ), enableSplitStereo );
// DA: Uses split stereo track and then drag pan sliders for split-stereo-to-mono
#ifndef EXPERIMENTAL_DA
POPUP_MENU_ITEM( "SplitToMono", OnSplitStereoMonoID, XO("Split Stereo to Mo&no"), OnSplitStereoMono)
AppendItem( "SplitToMono", OnSplitStereoMonoID, XO("Split Stereo to Mo&no"), POPUP_MENU_FN( OnSplitStereoMono ), enableSplitStereo );
#endif
END_POPUP_MENU_SECTION()
EndSection();
BEGIN_POPUP_MENU_SECTION( "Format" )
BeginSection( "Format" );
POPUP_MENU_SUB_MENU( "Format", FormatMenuTable, mpData )
END_POPUP_MENU_SECTION()
EndSection();
BEGIN_POPUP_MENU_SECTION( "Rate" )
BeginSection( "Rate" );
POPUP_MENU_SUB_MENU( "Rate", RateMenuTable, mpData )
END_POPUP_MENU_SECTION()
EndSection();
END_POPUP_MENU()

View File

@ -266,46 +266,45 @@ PopupMenuTable &WaveformVRulerMenuTable::Instance()
return instance;
}
void WaveformVRulerMenuTable::InitMenu(wxMenu *pMenu)
{
WaveTrackVRulerMenuTable::InitMenu(pMenu);
// DB setting is already on track drop down.
WaveTrack *const wt = mpData->pTrack;
const int id =
OnFirstWaveformScaleID + (int)(wt->GetWaveformSettings().scaleType);
pMenu->Check(id, true);
}
BEGIN_POPUP_MENU(WaveformVRulerMenuTable)
BEGIN_POPUP_MENU_SECTION( "Scales" )
BeginSection( "Scales" );
{
const auto & names = WaveformSettings::GetScaleNames();
for (int ii = 0, nn = names.size(); ii < nn; ++ii) {
POPUP_MENU_RADIO_ITEM( names[ii].Internal(),
AppendRadioItem( names[ii].Internal(),
OnFirstWaveformScaleID + ii, names[ii].Msgid(),
OnWaveformScaleType);
POPUP_MENU_FN( OnWaveformScaleType ),
[]( PopupMenuHandler &handler, wxMenu &menu, int id ){
const auto pData =
static_cast< WaveformVRulerMenuTable& >( handler ).mpData;
WaveTrack *const wt = pData->pTrack;
if ( id ==
OnFirstWaveformScaleID +
(int)(wt->GetWaveformSettings().scaleType) )
menu.Check(id, true);
}
);
}
}
END_POPUP_MENU_SECTION()
EndSection();
BEGIN_POPUP_MENU_SECTION( "Zoom" )
BEGIN_POPUP_MENU_SECTION( "Basic" )
POPUP_MENU_ITEM( "Reset", OnZoomFitVerticalID, XO("Zoom Reset\tShift-Right-Click"), OnZoomReset)
POPUP_MENU_ITEM( "TimesHalf", OnZoomDiv2ID, XO("Zoom x1/2"), OnZoomDiv2Vertical)
POPUP_MENU_ITEM( "TimesTwo", OnZoomTimes2ID, XO("Zoom x2"), OnZoomTimes2Vertical)
BeginSection( "Zoom" );
BeginSection( "Basic" );
AppendItem( "Reset", OnZoomFitVerticalID, XO("Zoom Reset\tShift-Right-Click"), POPUP_MENU_FN( OnZoomReset ) );
AppendItem( "TimesHalf", OnZoomDiv2ID, XO("Zoom x1/2"), POPUP_MENU_FN( OnZoomDiv2Vertical ) );
AppendItem( "TimesTwo", OnZoomTimes2ID, XO("Zoom x2"), POPUP_MENU_FN( OnZoomTimes2Vertical ) );
#ifdef EXPERIMENTAL_HALF_WAVE
POPUP_MENU_ITEM( "HalfWave", OnZoomHalfWaveID, XO("Half Wave"), OnZoomHalfWave)
AppendItem( "HalfWave", OnZoomHalfWaveID, XO("Half Wave"), POPUP_MENU_FN( OnZoomHalfWave ) );
#endif
END_POPUP_MENU_SECTION()
EndSection();
BEGIN_POPUP_MENU_SECTION( "InOut" )
POPUP_MENU_ITEM( "In", OnZoomInVerticalID, XO("Zoom In\tLeft-Click/Left-Drag"), OnZoomInVertical)
POPUP_MENU_ITEM( "Out", OnZoomOutVerticalID, XO("Zoom Out\tShift-Left-Click"), OnZoomOutVertical)
END_POPUP_MENU_SECTION()
END_POPUP_MENU_SECTION()
BeginSection( "InOut" );
AppendItem( "In", OnZoomInVerticalID, XO("Zoom In\tLeft-Click/Left-Drag"), POPUP_MENU_FN( OnZoomInVertical ) );
AppendItem( "Out", OnZoomOutVerticalID, XO("Zoom Out\tShift-Left-Click"), POPUP_MENU_FN( OnZoomOutVertical ) );
EndSection();
EndSection();
END_POPUP_MENU()

View File

@ -85,8 +85,6 @@ public:
static PopupMenuTable &Instance();
private:
virtual void InitMenu(wxMenu *pMenu) override;
void OnWaveformScaleType(wxCommandEvent &evt);
};

View File

@ -54,17 +54,6 @@ private:
mpData = static_cast<CommonTrackControls::InitMenuData*>(pUserData);
}
void InitMenu(wxMenu *pMenu) override
{
TimeTrack *const pTrack = static_cast<TimeTrack*>(mpData->pTrack);
pMenu->Check(OnTimeTrackLogIntID, pTrack->GetInterpolateLog());
auto isLog = pTrack->GetDisplayLog();
pMenu->Check(OnTimeTrackLinID, !isLog);
pMenu->Check(OnTimeTrackLogID, isLog);
}
void DestroyMenu() override
{
mpData = nullptr;
@ -162,15 +151,34 @@ void TimeTrackMenuTable::OnTimeTrackLogInt(wxCommandEvent & /*event*/)
}
BEGIN_POPUP_MENU(TimeTrackMenuTable)
BEGIN_POPUP_MENU_SECTION( "Scales" )
POPUP_MENU_RADIO_ITEM( "Linear", OnTimeTrackLinID, XO("&Linear scale"), OnTimeTrackLin)
POPUP_MENU_RADIO_ITEM( "Log", OnTimeTrackLogID, XO("L&ogarithmic scale"), OnTimeTrackLog)
END_POPUP_MENU_SECTION()
static const auto findTrack = []( PopupMenuHandler &handler ){
return static_cast<TimeTrack*>(
static_cast<TimeTrackMenuTable&>( handler ).mpData->pTrack );
};
BeginSection( "Scales" );
AppendRadioItem( "Linear", OnTimeTrackLinID, XO("&Linear scale"),
POPUP_MENU_FN( OnTimeTrackLin ),
[]( PopupMenuHandler &handler, wxMenu &menu, int id ){
menu.Check( id, !findTrack(handler)->GetDisplayLog() );
} );
AppendRadioItem( "Log", OnTimeTrackLogID, XO("L&ogarithmic scale"),
POPUP_MENU_FN( OnTimeTrackLog ),
[]( PopupMenuHandler &handler, wxMenu &menu, int id ){
menu.Check( id, findTrack(handler)->GetDisplayLog() );
} );
EndSection();
BeginSection( "Other" );
AppendItem( "Range", OnSetTimeTrackRangeID, XO("&Range..."),
POPUP_MENU_FN( OnSetTimeTrackRange ) );
AppendCheckItem( "LogInterp", OnTimeTrackLogIntID,
XO("Logarithmic &Interpolation"), POPUP_MENU_FN( OnTimeTrackLogInt),
[]( PopupMenuHandler &handler, wxMenu &menu, int id ){
menu.Check( id, findTrack(handler)->GetInterpolateLog() );
} );
EndSection();
BEGIN_POPUP_MENU_SECTION( "Other" )
POPUP_MENU_ITEM( "Range", OnSetTimeTrackRangeID, XO("&Range..."), OnSetTimeTrackRange)
POPUP_MENU_CHECK_ITEM( "LogInterp", OnTimeTrackLogIntID, XO("Logarithmic &Interpolation"), OnTimeTrackLogInt)
END_POPUP_MENU_SECTION()
END_POPUP_MENU()
PopupMenuTable *TimeTrackControls::GetMenuExtension(Track *)

View File

@ -93,7 +93,6 @@ private:
void OnMoveTrack(wxCommandEvent &event);
void InitUserData(void *pUserData) override;
void InitMenu(wxMenu *pMenu) override;
void DestroyMenu() override
{
@ -114,24 +113,22 @@ void TrackMenuTable::InitUserData(void *pUserData)
mpData = static_cast<CommonTrackControls::InitMenuData*>(pUserData);
}
void TrackMenuTable::InitMenu(wxMenu *pMenu)
{
Track *const pTrack = mpData->pTrack;
const auto &tracks = TrackList::Get( mpData->project );
pMenu->Enable(OnMoveUpID, tracks.CanMoveUp(pTrack));
pMenu->Enable(OnMoveDownID, tracks.CanMoveDown(pTrack));
pMenu->Enable(OnMoveTopID, tracks.CanMoveUp(pTrack));
pMenu->Enable(OnMoveBottomID, tracks.CanMoveDown(pTrack));
}
BEGIN_POPUP_MENU(TrackMenuTable)
BEGIN_POPUP_MENU_SECTION( "Basic" )
POPUP_MENU_ITEM( "Name", OnSetNameID, XO("&Name..."), OnSetName)
END_POPUP_MENU_SECTION()
BEGIN_POPUP_MENU_SECTION( "Move" )
POPUP_MENU_ITEM( "Up",
static const auto enableIfCanMove = [](bool up){ return
[up]( PopupMenuHandler &handler, wxMenu &menu, int id ){
auto pData = static_cast<TrackMenuTable&>( handler ).mpData;
const auto &tracks = TrackList::Get( pData->project );
Track *const pTrack = pData->pTrack;
menu.Enable( id,
up ? tracks.CanMoveUp(pTrack) : tracks.CanMoveDown(pTrack) );
};
};
BeginSection( "Basic" );
AppendItem( "Name", OnSetNameID, XO("&Name..."), POPUP_MENU_FN( OnSetName ) );
EndSection();
BeginSection( "Move" );
AppendItem( "Up",
// It is not correct to use NormalizedKeyString::Display here --
// wxWidgets will apply its equivalent to the key names passed to menu
// functions.
@ -143,8 +140,8 @@ BEGIN_POPUP_MENU(TrackMenuTable)
GetKeyFromName(wxT("TrackMoveUp")).GET() ),
wxT("\t")
),
OnMoveTrack)
POPUP_MENU_ITEM( "Down",
POPUP_MENU_FN( OnMoveTrack ), enableIfCanMove(true) );
AppendItem( "Down",
OnMoveDownID,
XO("Move Track &Down").Join(
Verbatim(
@ -153,8 +150,8 @@ BEGIN_POPUP_MENU(TrackMenuTable)
GetKeyFromName(wxT("TrackMoveDown")).GET() ),
wxT("\t")
),
OnMoveTrack)
POPUP_MENU_ITEM( "Top",
POPUP_MENU_FN( OnMoveTrack ), enableIfCanMove(false) );
AppendItem( "Top",
OnMoveTopID,
XO("Move Track to &Top").Join(
Verbatim(
@ -163,8 +160,8 @@ BEGIN_POPUP_MENU(TrackMenuTable)
GetKeyFromName(wxT("TrackMoveTop")).GET() ),
wxT("\t")
),
OnMoveTrack)
POPUP_MENU_ITEM( "Bottom",
POPUP_MENU_FN( OnMoveTrack ), enableIfCanMove(true) );
AppendItem( "Bottom",
OnMoveBottomID,
XO("Move Track to &Bottom").Join(
Verbatim(
@ -173,8 +170,8 @@ BEGIN_POPUP_MENU(TrackMenuTable)
GetKeyFromName(wxT("TrackMoveBottom")).GET() ),
wxT("\t")
),
OnMoveTrack)
END_POPUP_MENU_SECTION()
POPUP_MENU_FN( OnMoveTrack ), enableIfCanMove(false) );
EndSection();
END_POPUP_MENU()

View File

@ -80,7 +80,6 @@ void PopupMenuBuilder::DoBeginGroup( Registry::GroupItem &item, const Path &path
void PopupMenuBuilder::DoEndGroup( Registry::GroupItem &item, const Path &path )
{
if ( auto pItem = dynamic_cast<PopupSubMenu*>(&item) ) {
pItem->table.InitMenu( mMenu );
if ( !pItem->caption.empty() ) {
auto subMenu = std::move( mMenus.back() );
mMenus.pop_back();
@ -92,35 +91,37 @@ void PopupMenuBuilder::DoEndGroup( Registry::GroupItem &item, const Path &path )
void PopupMenuBuilder::DoVisit( Registry::SingleItem &item, const Path &path )
{
auto connect = [this]( const PopupMenuTable::Entry *pEntry ) {
mMenu->pParent->Bind
(wxEVT_COMMAND_MENU_SELECTED,
pEntry->func, mMenu->tables.back(), pEntry->id);
};
auto pEntry = static_cast<PopupMenuTableEntry*>( &item );
switch (pEntry->type) {
case PopupMenuTable::Entry::Item:
{
mMenu->Append(pEntry->id, pEntry->caption.Translation());
connect( pEntry );
break;
}
case PopupMenuTable::Entry::RadioItem:
{
mMenu->AppendRadioItem(pEntry->id, pEntry->caption.Translation());
connect( pEntry );
break;
}
case PopupMenuTable::Entry::CheckItem:
{
mMenu->AppendCheckItem(pEntry->id, pEntry->caption.Translation());
connect( pEntry );
break;
}
default:
wxASSERT( false );
break;
}
// This call necessary for externally registered items, else harmlessly
// redundant
pEntry->handler.InitUserData( mpUserData );
if ( pEntry->init )
pEntry->init( pEntry->handler, *mMenu, pEntry->id );
mMenu->pParent->Bind(
wxEVT_COMMAND_MENU_SELECTED, pEntry->func, &pEntry->handler, pEntry->id);
}
void PopupMenuBuilder::DoSeparator()
@ -145,6 +146,29 @@ void PopupMenuTable::ExtendMenu( wxMenu &menu, PopupMenuTable &table )
Registry::Visit( visitor, table.Get( theMenu.pUserData ).get() );
}
void PopupMenuTable::Append(
const Identifier &stringId, PopupMenuTableEntry::Type type, int id,
const TranslatableString &string, wxCommandEventFunction memFn,
const PopupMenuTableEntry::InitFunction &init )
{
mStack.back()->items.push_back( std::make_unique<Entry>(
stringId, type, id, string, memFn, *this, init
) );
}
void PopupMenuTable::BeginSection( const Identifier &name )
{
auto uSection = std::make_unique< PopupMenuSection >( name );
auto section = uSection.get();
mStack.back()->items.push_back( std::move( uSection ) );
mStack.push_back( section );
}
void PopupMenuTable::EndSection()
{
mStack.pop_back();
}
namespace{
void PopupMenu::DisconnectTable(PopupMenuTable *pTable)
{
@ -160,7 +184,8 @@ void PopupMenu::DisconnectTable(PopupMenuTable *pTable)
{
auto pEntry = static_cast<PopupMenuTableEntry*>( &item );
mMenu.pParent->Unbind( wxEVT_COMMAND_MENU_SELECTED,
pEntry->func, &mTable, pEntry->id );
pEntry->func, &pEntry->handler, pEntry->id );
pEntry->handler.DestroyMenu();
}
PopupMenuTable &mTable;
@ -169,8 +194,6 @@ void PopupMenu::DisconnectTable(PopupMenuTable *pTable)
PopupMenuDestroyer visitor{ *pTable, *this };
Registry::Visit( visitor, pTable->Get( nullptr ).get() );
pTable->DestroyMenu();
}
void PopupMenu::Disconnect()
@ -180,10 +203,6 @@ void PopupMenu::Disconnect()
}
}
void PopupMenuTable::InitMenu(wxMenu *)
{
}
// static
std::unique_ptr<wxMenu> PopupMenuTable::BuildMenu
( wxEvtHandler *pParent, PopupMenuTable *pTable, void *pUserData )

View File

@ -20,6 +20,7 @@ tables, and automatically attaches and detaches the event handlers.
class wxCommandEvent;
class wxString;
#include <functional>
#include <vector>
#include <wx/menu.h> // to inherit wxMenu
#include "../MemoryX.h"
@ -27,26 +28,33 @@ class wxString;
#include "../Internat.h"
#include "../commands/CommandManager.h"
class PopupMenuHandler;
class PopupMenuTable;
struct PopupMenuTableEntry : Registry::SingleItem
{
enum Type { Item, RadioItem, CheckItem };
using InitFunction =
std::function< void( PopupMenuHandler &handler, wxMenu &menu, int id ) >;
Type type;
int id;
TranslatableString caption;
wxCommandEventFunction func;
PopupMenuTable *subTable;
PopupMenuHandler &handler;
InitFunction init;
PopupMenuTableEntry( const Identifier &stringId,
Type type_, int id_, const TranslatableString &caption_,
wxCommandEventFunction func_)
wxCommandEventFunction func_, PopupMenuHandler &handler_,
InitFunction init_ )
: SingleItem{ stringId }
, type(type_)
, id(id_)
, caption(caption_)
, func(func_)
, handler( handler_ )
, init( init_ )
{}
~PopupMenuTableEntry() override;
@ -70,7 +78,24 @@ struct PopupMenuSection
using ConcreteGroupItem< false >::ConcreteGroupItem;
};
class PopupMenuTable : public wxEvtHandler
class PopupMenuHandler : public wxEvtHandler
{
public:
PopupMenuHandler() = default;
PopupMenuHandler( const PopupMenuHandler& ) = delete;
PopupMenuHandler& operator=( const PopupMenuHandler& ) = delete;
// Called before the menu items are appended.
// Store context data, if needed.
// May be called more than once before the menu opens.
virtual void InitUserData(void *pUserData) = 0;
// Called when menu is destroyed.
// May be called more than once.
virtual void DestroyMenu() = 0;
};
class PopupMenuTable : public PopupMenuHandler
{
public:
using Entry = PopupMenuTableEntry;
@ -81,18 +106,6 @@ public:
, mCaption{ caption }
{}
// Called before the menu items are appended.
// Store user data, if needed.
virtual void InitUserData(void *pUserData) = 0;
// Called when the menu is about to pop up.
// Your chance to enable and disable items.
// Default implementation does nothing.
virtual void InitMenu(wxMenu *pMenu);
// Called when menu is destroyed.
virtual void DestroyMenu() = 0;
// Optional pUserData gets passed to the InitUserData routines of tables.
// No memory management responsibility is assumed by this function.
static std::unique_ptr<wxMenu> BuildMenu
@ -115,6 +128,34 @@ public:
protected:
virtual void Populate() = 0;
// To be used in implementations of Populate():
void Append(
const Identifier &stringId, PopupMenuTableEntry::Type type, int id,
const TranslatableString &string, wxCommandEventFunction memFn,
// This callback might check or disable a menu item:
const PopupMenuTableEntry::InitFunction &init );
void AppendItem( const Identifier &stringId, int id,
const TranslatableString &string, wxCommandEventFunction memFn,
// This callback might check or disable a menu item:
const PopupMenuTableEntry::InitFunction &init = {} )
{ Append( stringId, PopupMenuTableEntry::Item, id, string, memFn, init ); }
void AppendRadioItem( const Identifier &stringId, int id,
const TranslatableString &string, wxCommandEventFunction memFn,
// This callback might check or disable a menu item:
const PopupMenuTableEntry::InitFunction &init = {} )
{ Append( stringId, PopupMenuTableEntry::RadioItem, id, string, memFn, init ); }
void AppendCheckItem( const Identifier &stringId, int id,
const TranslatableString &string, wxCommandEventFunction memFn,
const PopupMenuTableEntry::InitFunction &init = {} )
{ Append( stringId, PopupMenuTableEntry::CheckItem, id, string, memFn, init ); }
void BeginSection( const Identifier &name );
void EndSection();
void Clear() { mTop.reset(); }
std::shared_ptr< Registry::GroupItem > mTop;
@ -133,7 +174,6 @@ which inherits from PopupMenuTable,
DECLARE_POPUP_MENU(MyTable);
virtual void InitUserData(void *pUserData);
virtual void InitMenu(wxMenu *pMenu);
virtual void DestroyMenu();
Then in MyTable.cpp,
@ -144,11 +184,6 @@ void MyTable::InitUserData(void *pUserData)
auto pData = static_cast<MyData*>(pUserData);
}
void MyTable::InitMenu(wxMenu *pMenu)
{
// enable or disable menu items
}
void MyTable::DestroyMenu()
{
// Do cleanup
@ -156,9 +191,17 @@ void MyTable::DestroyMenu()
BEGIN_POPUP_MENU(MyTable)
// This is inside a function and can contain arbitrary code. But usually
// you only need a sequence of macro calls:
// you only need a sequence of calls:
POPUP_MENU_ITEM("Cut", OnCutSelectedTextID, XO("Cu&t"), OnCutSelectedText)
AppendItem("Cut",
OnCutSelectedTextID, XO("Cu&t"), POPUP_MENU_FN( OnCutSelectedText ),
// optional argument:
[](PopupMenuHandler &handler, wxMenu &menu, int id)
{
auto data = static_cast<MyTable&>( handler ).pData;
// maybe enable or disable the menu item
}
);
// etc.
END_POPUP_MENU()
@ -192,46 +235,12 @@ void HandlerClass::Populate() { \
mStack.clear(); \
mStack.push_back( mTop.get() );
#define POPUP_MENU_APPEND(stringId, type, id, string, memFn) \
mStack.back()->items.push_back( std::make_unique<Entry>( \
Identifier{ stringId }, \
type, \
id, \
string, \
memFn \
) );
#define POPUP_MENU_FN( memFn ) ( (wxCommandEventFunction) (&My::memFn) )
#define POPUP_MENU_APPEND_ITEM(stringId, type, id, string, memFn) \
POPUP_MENU_APPEND( stringId, \
type, \
id, \
string, \
(wxCommandEventFunction) (&My::memFn) )
#define POPUP_MENU_ITEM(stringId, id, string, memFn) \
POPUP_MENU_APPEND_ITEM(stringId, Entry::Item, id, string, memFn);
#define POPUP_MENU_RADIO_ITEM(stringId, id, string, memFn) \
POPUP_MENU_APPEND_ITEM(stringId, Entry::RadioItem, id, string, memFn);
#define POPUP_MENU_CHECK_ITEM(stringId, id, string, memFn) \
POPUP_MENU_APPEND_ITEM(stringId, Entry::CheckItem, id, string, memFn);
// classname names a class that derives from MenuTable and defines Instance()
#define POPUP_MENU_SUB_MENU(stringId, classname, pUserData ) \
mStack.back()->items.push_back( \
Registry::Shared( classname::Instance().Get( pUserData ) ) );
#define BEGIN_POPUP_MENU_SECTION( name ) \
{ auto uSection = std::make_unique< PopupMenuSection >( \
Identifier{ name } ); \
auto section = uSection.get(); \
mStack.back()->items.push_back( std::move( uSection ) ); \
mStack.push_back( section ); \
}
#define END_POPUP_MENU_SECTION() mStack.pop_back();
// ends function
#define END_POPUP_MENU() }