Audacity 3.2.0
PopupMenuTable.h
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5PopupMenuTable.h
6
7Paul Licameli
8
9This file defines PopupMenuTable, which inherits from wxEventHandler,
10
11associated macros simplifying the population of tables,
12
13and PopupMenuTable::Menu which is buildable from one or more such
14tables, and automatically attaches and detaches the event handlers.
15
16**********************************************************************/
17
18#ifndef __AUDACITY_POPUP_MENU_TABLE__
19#define __AUDACITY_POPUP_MENU_TABLE__
20
21class wxCommandEvent;
22
23#include <functional>
24#include <vector>
25#include <wx/menu.h> // to inherit wxMenu
26#include <memory>
27
28#include "Internat.h"
29#include "../commands/CommandManager.h"
30
32class PopupMenuTable;
33
35{
36 enum Type { Item, RadioItem, CheckItem };
38 std::function< void( PopupMenuHandler &handler, wxMenu &menu, int id ) >;
39
41 int id;
43 wxCommandEventFunction func;
46
49 Type type_, int id_, const TranslatableString &caption_,
50 wxCommandEventFunction func_, PopupMenuHandler &handler_,
51 InitFunction init_ = {} )
52 : SingleItem{ stringId }
53 , type(type_)
54 , id(id_)
55 , caption(caption_)
56 , func(func_)
57 , handler( handler_ )
58 , init( init_ )
59 {
60 wxASSERT(func);
61 }
62
63 ~PopupMenuTableEntry() override;
64};
65
66struct AUDACITY_DLL_API PopupSubMenu : Registry::ConcreteGroupItem< false >
68{
71
72 PopupSubMenu( const Identifier &stringId,
73 const TranslatableString &caption_, PopupMenuTable &table );
74
75 ~PopupSubMenu() override;
76};
77
81 using ConcreteGroupItem< false >::ConcreteGroupItem;
82};
83
84class PopupMenuHandler : public wxEvtHandler
85{
86public:
87 PopupMenuHandler() = default;
90
92
98 virtual void InitUserData(void *pUserData) = 0;
99};
100
102 explicit PopupMenuVisitor( PopupMenuTable &table ) : mTable{ table } {}
104};
105
106// Opaque structure built by PopupMenuTable::BuildMenu
107class AUDACITY_DLL_API PopupMenu
108{
109public:
110 virtual ~PopupMenu();
111 virtual void Popup( wxWindow &window, const wxPoint &pos ) = 0;
112};
113
114class AUDACITY_DLL_API PopupMenuTable : public PopupMenuHandler
115{
116public:
118
119 // Supply a nonempty caption for sub-menu tables
120 PopupMenuTable( const Identifier &id, const TranslatableString &caption = {} )
121 : mId{ id }
122 , mCaption{ caption }
123 , mRegistry{ std::make_unique<Registry::TransparentGroupItem<>>( mId ) }
124 {}
125
126 // Optional pUserData gets passed to the InitUserData routines of tables.
127 // No memory management responsibility is assumed by this function.
128 static std::unique_ptr<PopupMenu> BuildMenu(
129 PopupMenuTable *pTable, void *pUserData = NULL);
130
131 const Identifier &Id() const { return mId; }
132 const TranslatableString &Caption() const { return mCaption; }
133 const Registry::GroupItem *GetRegistry() const { return mRegistry.get(); }
134
135 // Typically statically constructed:
138 const Registry::Placement &placement, Registry::BaseItemPtr pItem )
139 { table.RegisterItem( placement, std::move( pItem ) ); }
140 };
141
142 // menu must have been built by BuildMenu
143 // More items get added to the end of it
144 static void ExtendMenu( PopupMenu &menu, PopupMenuTable &otherTable );
145
146 const std::shared_ptr< Registry::GroupItem > &Get( void *pUserData )
147 {
148 if ( pUserData )
149 this->InitUserData( pUserData );
150 if (!mTop)
151 Populate();
152 return mTop;
153 }
154
155 void Clear()
156 {
157 mTop.reset();
158 }
159
160 // Forms a computed item, which may be omitted when function returns null
161 // and thus can be a conditional item
162 template< typename Table >
164 const std::function< Registry::BaseItemPtr( Table& ) > &factory )
165 {
166 using namespace Registry;
167 return std::make_unique< ComputedItem >(
168 [factory]( Visitor &baseVisitor ){
169 auto &visitor = static_cast< PopupMenuVisitor& >( baseVisitor );
170 auto &table = static_cast< Table& >( visitor.mTable );
171 return factory( table );
172 }
173 );
174 }
175
176private:
177 void RegisterItem(
178 const Registry::Placement &placement, Registry::BaseItemPtr pItem );
179
180protected:
181 // This convenience function composes a label, with the following optional
182 // part put in parentheses if useExtra is true
184 bool useExtra, const TranslatableString &extra )
185 {
186 return useExtra
187 ? XXO("%s (%s)").Format( label, extra )
188 : label;
189 }
190
191 virtual void Populate() = 0;
192
193 // To be used in implementations of Populate():
194 void Append( Registry::BaseItemPtr pItem );
195
196 void Append(
197 const Identifier &stringId, PopupMenuTableEntry::Type type, int id,
198 const TranslatableString &string, wxCommandEventFunction memFn,
199 // This callback might check or disable a menu item:
201
202 void AppendItem( const Identifier &stringId, int id,
203 const TranslatableString &string, wxCommandEventFunction memFn,
204 // This callback might check or disable a menu item:
205 const PopupMenuTableEntry::InitFunction &init = {} )
206 { Append( stringId, PopupMenuTableEntry::Item, id, string, memFn, init ); }
207
208 void AppendRadioItem( const Identifier &stringId, int id,
209 const TranslatableString &string, wxCommandEventFunction memFn,
210 // This callback might check or disable a menu item:
211 const PopupMenuTableEntry::InitFunction &init = {} )
212 { Append( stringId, PopupMenuTableEntry::RadioItem, id, string, memFn, init ); }
213
214 void AppendCheckItem( const Identifier &stringId, int id,
215 const TranslatableString &string, wxCommandEventFunction memFn,
216 const PopupMenuTableEntry::InitFunction &init = {} )
217 { Append( stringId, PopupMenuTableEntry::CheckItem, id, string, memFn, init ); }
218
219 void BeginSection( const Identifier &name );
220 void EndSection();
221
222 std::shared_ptr< Registry::GroupItem > mTop;
223 std::vector< Registry::GroupItem* > mStack;
226 std::unique_ptr<Registry::GroupItem> mRegistry;
227};
228
229// A "CRTP" class that injects a convenience function, which appends a menu item
230// computed lazily by a function that is passed the table (after it has stored
231// its user data)
232template< typename Derived, typename Base = PopupMenuTable >
233class ComputedPopupMenuTable : public Base
234{
235public:
236 using Base::Base;
237 using Base::Append;
238
239 // Appends a computed item, which may be omitted when function returns null
240 // and thus can be a conditional item
241 using Factory = std::function< Registry::BaseItemPtr( Derived& ) >;
243 {
244 return Base::Computed( factory );
245 }
246
247 void Append( const Factory &factory )
248 {
249 Append( Computed( factory ) );
250 }
251};
252
253/*
254The following macros make it easy to attach a popup menu to a window.
255
256Example of usage:
257
258In class MyTable (maybe in the private section),
259which inherits from PopupMenuTable,
260
261DECLARE_POPUP_MENU(MyTable);
262virtual void InitUserData(void *pUserData);
263virtual void DestroyMenu();
264
265Then in MyTable.cpp,
266
267void MyTable::InitUserData(void *pUserData)
268{
269 // Remember pData
270 auto pData = static_cast<MyData*>(pUserData);
271}
272
273void MyTable::DestroyMenu()
274{
275 // Do cleanup
276}
277
278BEGIN_POPUP_MENU(MyTable)
279 // This is inside a function and can contain arbitrary code. But usually
280 // you only need a sequence of calls:
281
282 AppendItem("Cut",
283 OnCutSelectedTextID, XO("Cu&t"), POPUP_MENU_FN( OnCutSelectedText ),
284 // optional argument:
285 [](PopupMenuHandler &handler, wxMenu &menu, int id)
286 {
287 auto data = static_cast<MyTable&>( handler ).pData;
288 // maybe enable or disable the menu item
289 }
290 );
291 // etc.
292
293END_POPUP_MENU()
294
295where OnCutSelectedText is a (maybe private) member function of MyTable.
296
297Elsewhere,
298
299MyTable myTable;
300MyData data;
301auto pMenu = PopupMenuTable::BuildMenu(pParent, &myTable, &data);
302
303// Optionally:
304OtherTable otherTable;
305PopupMenuTable::ExtendMenu( *pMenu, otherTable );
306
307pMenu->Popup( *pParent, { event.m_x, event.m_y } );
308
309That's all!
310*/
311
312#define DECLARE_POPUP_MENU(HandlerClass) \
313 void Populate() override;
314
315// begins function
316#define BEGIN_POPUP_MENU(HandlerClass) \
317void HandlerClass::Populate() { \
318 using My = HandlerClass; \
319 mTop = std::make_shared< PopupSubMenu >( \
320 Id(), Caption(), *this ); \
321 mStack.clear(); \
322 mStack.push_back( mTop.get() );
323
324#define POPUP_MENU_FN( memFn ) ( (wxCommandEventFunction) (&My::memFn) )
325
326#define POPUP_MENU_SUB_MENU(stringId, classname, pUserData ) \
327 mStack.back()->items.push_back( \
328 Registry::Shared( classname::Instance().Get( pUserData ) ) );
329
330// ends function
331#define END_POPUP_MENU() }
332
333#endif
static RegisteredToolbarFactory factory
const TranslatableString name
Definition: Distortion.cpp:82
#define XXO(s)
Definition: Internat.h:44
EndSection()
BeginSection("Basic")
TranslatableString label
Definition: TagsEditor.cpp:163
int id
Append([](My &table) -> Registry::BaseItemPtr { if(WaveTrackSubViews::slots() > 1) return std::make_unique< Entry >("MultiView", Entry::CheckItem, OnMultiViewID, XXO("&Multi-view"), POPUP_MENU_FN(OnMultiView), table, [](PopupMenuHandler &handler, wxMenu &menu, int id){ auto &table=static_cast< WaveTrackMenuTable & >(handler);auto &track=table.FindWaveTrack();const auto &view=WaveTrackView::Get(track);menu.Check(id, view.GetMultiView());});else return nullptr;})
void Append(const Factory &factory)
static Registry::BaseItemPtr Computed(const Factory &factory)
std::function< Registry::BaseItemPtr(Derived &) > Factory
An explicitly nonlocalized string, not meant for the user to see.
Definition: Identifier.h:22
PopupMenuHandler(const PopupMenuHandler &)=delete
PopupMenuHandler()=default
virtual void InitUserData(void *pUserData)=0
Called before the menu items are appended.
PopupMenuHandler & operator=(const PopupMenuHandler &)=delete
virtual void Popup(wxWindow &window, const wxPoint &pos)=0
virtual ~PopupMenu()
Identifier mId
void AppendItem(const Identifier &stringId, int id, const TranslatableString &string, wxCommandEventFunction memFn, const PopupMenuTableEntry::InitFunction &init={})
void AppendRadioItem(const Identifier &stringId, int id, const TranslatableString &string, wxCommandEventFunction memFn, const PopupMenuTableEntry::InitFunction &init={})
PopupMenuTable(const Identifier &id, const TranslatableString &caption={})
static Registry::BaseItemPtr Computed(const std::function< Registry::BaseItemPtr(Table &) > &factory)
virtual void Populate()=0
const Identifier & Id() const
std::vector< Registry::GroupItem * > mStack
static TranslatableString MakeLabel(const TranslatableString &label, bool useExtra, const TranslatableString &extra)
void RegisterItem(const Registry::Placement &placement, Registry::BaseItemPtr pItem)
TranslatableString mCaption
std::unique_ptr< Registry::GroupItem > mRegistry
std::shared_ptr< Registry::GroupItem > mTop
const TranslatableString & Caption() const
const std::shared_ptr< Registry::GroupItem > & Get(void *pUserData)
void AppendCheckItem(const Identifier &stringId, int id, const TranslatableString &string, wxCommandEventFunction memFn, const PopupMenuTableEntry::InitFunction &init={})
const Registry::GroupItem * GetRegistry() const
Holds a msgid for the translation catalog; may also bind format arguments.
Definition: Menus.h:36
std::unique_ptr< BaseItem > BaseItemPtr
Definition: Registry.h:71
void RegisterItem(GroupItem &registry, const Placement &placement, BaseItemPtr pItem)
Definition: Registry.cpp:750
AttachedItem(PopupMenuTable &table, const Registry::Placement &placement, Registry::BaseItemPtr pItem)
PopupMenuHandler & handler
Type type
InitFunction init
wxCommandEventFunction func
PopupMenuTableEntry(const Identifier &stringId, Type type_, int id_, const TranslatableString &caption_, wxCommandEventFunction func_, PopupMenuHandler &handler_, InitFunction init_={})
int id
Type
@ Item
@ CheckItem
@ RadioItem
TranslatableString caption
std::function< void(PopupMenuHandler &handler, wxMenu &menu, int id) > InitFunction
PopupMenuVisitor(PopupMenuTable &table)
PopupMenuTable & mTable
PopupMenuTable & table
TranslatableString caption