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::GroupItem<>
68{
71
72 PopupSubMenu( const Identifier &stringId,
73 const TranslatableString &caption_, PopupMenuTable &table );
74
75 ~PopupSubMenu() override;
76};
77
79 using GroupItem::GroupItem;
80};
81
82class PopupMenuHandler : public wxEvtHandler
83{
84public:
85 PopupMenuHandler() = default;
88
90
96 virtual void InitUserData(void *pUserData) = 0;
97};
98
100 explicit PopupMenuVisitor( PopupMenuTable &table ) : mTable{ table } {}
102};
103
104// Opaque structure built by PopupMenuTable::BuildMenu
105class AUDACITY_DLL_API PopupMenu
106{
107public:
108 virtual ~PopupMenu();
109 virtual void Popup( wxWindow &window, const wxPoint &pos ) = 0;
110};
111
112class AUDACITY_DLL_API PopupMenuTable : public PopupMenuHandler
113{
114public:
116
117 // Supply a nonempty caption for sub-menu tables
118 PopupMenuTable( const Identifier &id, const TranslatableString &caption = {} )
119 : mId{ id }
120 , mCaption{ caption }
121 , mRegistry{ std::make_unique<Registry::GroupItem<>>(mId) }
122 {}
123
124 // Optional pUserData gets passed to the InitUserData routines of tables.
125 // No memory management responsibility is assumed by this function.
126 static std::unique_ptr<PopupMenu> BuildMenu(
127 PopupMenuTable *pTable, void *pUserData = NULL);
128
129 const Identifier &Id() const { return mId; }
130 const TranslatableString &Caption() const { return mCaption; }
131 const Registry::GroupItemBase *GetRegistry() const { return mRegistry.get(); }
132
133 // Typically statically constructed:
136 const Registry::Placement &placement, Registry::BaseItemPtr pItem )
137 { table.RegisterItem( placement, std::move( pItem ) ); }
138 };
139
140 // menu must have been built by BuildMenu
141 // More items get added to the end of it
142 static void ExtendMenu( PopupMenu &menu, PopupMenuTable &otherTable );
143
144 const std::shared_ptr< Registry::GroupItemBase > &Get( void *pUserData )
145 {
146 if ( pUserData )
147 this->InitUserData( pUserData );
148 if (!mTop)
149 Populate();
150 return mTop;
151 }
152
153 void Clear()
154 {
155 mTop.reset();
156 }
157
158 // Forms a computed item, which may be omitted when function returns null
159 // and thus can be a conditional item
160 template< typename Table >
162 const std::function< Registry::BaseItemPtr( Table& ) > &factory )
163 {
164 using namespace Registry;
165 return std::make_unique< ComputedItem >(
166 [factory]( Visitor &baseVisitor ){
167 auto &visitor = static_cast< PopupMenuVisitor& >( baseVisitor );
168 auto &table = static_cast< Table& >( visitor.mTable );
169 return factory( table );
170 }
171 );
172 }
173
174private:
175 void RegisterItem(
176 const Registry::Placement &placement, Registry::BaseItemPtr pItem );
177
178protected:
179 // This convenience function composes a label, with the following optional
180 // part put in parentheses if useExtra is true
182 bool useExtra, const TranslatableString &extra )
183 {
184 return useExtra
185 ? XXO("%s (%s)").Format( label, extra )
186 : label;
187 }
188
189 virtual void Populate() = 0;
190
191 // To be used in implementations of Populate():
192 void Append( Registry::BaseItemPtr pItem );
193
194 void Append(
195 const Identifier &stringId, PopupMenuTableEntry::Type type, int id,
196 const TranslatableString &string, wxCommandEventFunction memFn,
197 // This callback might check or disable a menu item:
199
200 void AppendItem( const Identifier &stringId, int id,
201 const TranslatableString &string, wxCommandEventFunction memFn,
202 // This callback might check or disable a menu item:
203 const PopupMenuTableEntry::InitFunction &init = {} )
204 { Append( stringId, PopupMenuTableEntry::Item, id, string, memFn, init ); }
205
206 void AppendRadioItem( const Identifier &stringId, int id,
207 const TranslatableString &string, wxCommandEventFunction memFn,
208 // This callback might check or disable a menu item:
209 const PopupMenuTableEntry::InitFunction &init = {} )
210 { Append( stringId, PopupMenuTableEntry::RadioItem, id, string, memFn, init ); }
211
212 void AppendCheckItem( const Identifier &stringId, int id,
213 const TranslatableString &string, wxCommandEventFunction memFn,
214 const PopupMenuTableEntry::InitFunction &init = {} )
215 { Append( stringId, PopupMenuTableEntry::CheckItem, id, string, memFn, init ); }
216
217 void BeginSection( const Identifier &name );
218 void EndSection();
219
220 std::shared_ptr< Registry::GroupItemBase > mTop;
221 std::vector< Registry::GroupItemBase* > mStack;
224 std::unique_ptr<Registry::GroupItemBase> mRegistry;
225};
226
227// A "CRTP" class that injects a convenience function, which appends a menu item
228// computed lazily by a function that is passed the table (after it has stored
229// its user data)
230template< typename Derived, typename Base = PopupMenuTable >
231class ComputedPopupMenuTable : public Base
232{
233public:
234 using Base::Base;
235 using Base::Append;
236
237 // Appends a computed item, which may be omitted when function returns null
238 // and thus can be a conditional item
239 using Factory = std::function< Registry::BaseItemPtr( Derived& ) >;
241 {
242 return Base::Computed( factory );
243 }
244
245 void Append( const Factory &factory )
246 {
247 Append( Computed( factory ) );
248 }
249};
250
251/*
252The following macros make it easy to attach a popup menu to a window.
253
254Example of usage:
255
256In class MyTable (maybe in the private section),
257which inherits from PopupMenuTable,
258
259DECLARE_POPUP_MENU(MyTable);
260virtual void InitUserData(void *pUserData);
261virtual void DestroyMenu();
262
263Then in MyTable.cpp,
264
265void MyTable::InitUserData(void *pUserData)
266{
267 // Remember pData
268 auto pData = static_cast<MyData*>(pUserData);
269}
270
271void MyTable::DestroyMenu()
272{
273 // Do cleanup
274}
275
276BEGIN_POPUP_MENU(MyTable)
277 // This is inside a function and can contain arbitrary code. But usually
278 // you only need a sequence of calls:
279
280 AppendItem("Cut",
281 OnCutSelectedTextID, XO("Cu&t"), POPUP_MENU_FN( OnCutSelectedText ),
282 // optional argument:
283 [](PopupMenuHandler &handler, wxMenu &menu, int id)
284 {
285 auto data = static_cast<MyTable&>( handler ).pData;
286 // maybe enable or disable the menu item
287 }
288 );
289 // etc.
290
291END_POPUP_MENU()
292
293where OnCutSelectedText is a (maybe private) member function of MyTable.
294
295Elsewhere,
296
297MyTable myTable;
298MyData data;
299auto pMenu = PopupMenuTable::BuildMenu(pParent, &myTable, &data);
300
301// Optionally:
302OtherTable otherTable;
303PopupMenuTable::ExtendMenu( *pMenu, otherTable );
304
305pMenu->Popup( *pParent, { event.m_x, event.m_y } );
306
307That's all!
308*/
309
310#define DECLARE_POPUP_MENU(HandlerClass) \
311 void Populate() override;
312
313// begins function
314#define BEGIN_POPUP_MENU(HandlerClass) \
315void HandlerClass::Populate() { \
316 using My = HandlerClass; \
317 mTop = std::make_shared< PopupSubMenu >( \
318 Id(), Caption(), *this ); \
319 mStack.clear(); \
320 mStack.push_back( mTop.get() );
321
322#define POPUP_MENU_FN( memFn ) ( (wxCommandEventFunction) (&My::memFn) )
323
324#define POPUP_MENU_SUB_MENU(stringId, classname, pUserData ) \
325 mStack.back()->items.push_back( \
326 Registry::Indirect(classname::Instance().Get(pUserData)));
327
328// ends function
329#define END_POPUP_MENU() }
330
331#endif
const TranslatableString name
Definition: Distortion.cpp:76
XXO("&Cut/Copy/Paste Toolbar")
EndSection()
BeginSection("Basic")
TranslatableString label
Definition: TagsEditor.cpp:164
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()
std::vector< Registry::GroupItemBase * > mStack
std::unique_ptr< Registry::GroupItemBase > mRegistry
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)
const std::shared_ptr< Registry::GroupItemBase > & Get(void *pUserData)
virtual void Populate()=0
const Identifier & Id() const
std::shared_ptr< Registry::GroupItemBase > mTop
static TranslatableString MakeLabel(const TranslatableString &label, bool useExtra, const TranslatableString &extra)
void RegisterItem(const Registry::Placement &placement, Registry::BaseItemPtr pItem)
TranslatableString mCaption
const Registry::GroupItemBase * GetRegistry() const
const TranslatableString & Caption() const
void AppendCheckItem(const Identifier &stringId, int id, const TranslatableString &string, wxCommandEventFunction memFn, const PopupMenuTableEntry::InitFunction &init={})
Holds a msgid for the translation catalog; may also bind format arguments.
Definition: Menus.h:35
void RegisterItem(GroupItemBase &registry, const Placement &placement, BaseItemPtr pItem)
Definition: Registry.cpp:774
std::unique_ptr< BaseItem > BaseItemPtr
Definition: Registry.h:73
static RegisteredToolbarFactory factory
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
Common abstract base class for items that group other items.
Definition: Registry.h:130