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 "CommandManager.h"
30
32struct PopupMenuSection;
33class PopupMenuTable;
35struct PopupSubMenu;
40};
42
44{
45 enum Type { Item, RadioItem, CheckItem };
47 std::function< void( PopupMenuHandler &handler, wxMenu &menu, int id ) >;
48
50 int id;
52 wxCommandEventFunction func;
55
58 Type type_, int id_, const TranslatableString &caption_,
59 wxCommandEventFunction func_, PopupMenuHandler &handler_,
60 InitFunction init_ = {} )
61 : SingleItem{ stringId }
62 , type(type_)
63 , id(id_)
64 , caption(caption_)
65 , func(func_)
66 , handler( handler_ )
67 , init( init_ )
68 {
69 wxASSERT(func);
70 }
71
72 ~PopupMenuTableEntry() override;
73};
74
75struct AUDACITY_DLL_API PopupSubMenu
77{
80
81 PopupSubMenu( const Identifier &stringId,
82 const TranslatableString &caption_, PopupMenuTable &table );
83
84 ~PopupSubMenu() override;
85 Properties GetProperties() const override;
86};
87
90{
92 ~PopupMenuSection() override;
93 Properties GetProperties() const override { return Section; }
94};
95
96class PopupMenuHandler : public wxEvtHandler
97{
98public:
99 PopupMenuHandler() = default;
102
104
110 virtual void InitUserData(void *pUserData) = 0;
111};
112
113// Opaque structure built by PopupMenuTable::BuildMenu
114class AUDACITY_DLL_API PopupMenu
115{
116public:
117 virtual ~PopupMenu();
118 virtual void Popup( wxWindow &window, const wxPoint &pos ) = 0;
119};
120
121class AUDACITY_DLL_API PopupMenuTable : public PopupMenuHandler
122{
123public:
125
126 // Supply a nonempty caption for sub-menu tables
127 PopupMenuTable(const Identifier &id, const TranslatableString &caption = {})
128 : mId{ id }
129 , mCaption{ caption }
130 , mRegistry{
131 std::make_unique<PopupMenuGroupItem>(mId) }
132 {}
133
134 // Optional pUserData gets passed to the InitUserData routines of tables.
135 // No memory management responsibility is assumed by this function.
136 static std::unique_ptr<PopupMenu> BuildMenu(
137 PopupMenuTable *pTable, void *pUserData = NULL);
138
139 const Identifier &Id() const { return mId; }
140 const TranslatableString &Caption() const { return mCaption; }
141 const auto *GetRegistry() const { return mRegistry.get(); }
142
143 // Typically statically constructed:
144 template<typename Ptr> struct AttachedItem {
146 const Registry::Placement &placement, Ptr pItem )
147 { table.RegisterItem( placement, std::move( pItem ) ); }
148 };
149
150 // menu must have been built by BuildMenu
151 // More items get added to the end of it
152 static void ExtendMenu( PopupMenu &menu, PopupMenuTable &otherTable );
153
154 const auto &Get(void *pUserData)
155 {
156 if ( pUserData )
157 this->InitUserData( pUserData );
158 if (!mTop)
159 Populate();
160 return mTop;
161 }
162
163 void Clear()
164 {
165 mTop.reset();
166 }
167
168 // Adapts a factory for a table sub-type
169 template<typename Table, typename Factory>
170 static auto Adapt(const Factory &factory)
171 {
172 return [factory](PopupMenuTable &table){
173 return std::shared_ptr{ factory(static_cast<Table&>(table)) };
174 };
175 }
176
177private:
178 template<typename Ptr> void RegisterItem(
179 const Registry::Placement &placement, Ptr pItem)
180 {
181 Registry::RegisterItem(*mRegistry, placement, move(pItem));
182 }
183
184protected:
185 // This convenience function composes a label, with the following optional
186 // part put in parentheses if useExtra is true
188 bool useExtra, const TranslatableString &extra )
189 {
190 return useExtra
191 ? XXO("%s (%s)").Format( label, extra )
192 : label;
193 }
194
195 virtual void Populate() = 0;
196
197 // To be used in implementations of Populate():
198 template<typename Ptr>
199 void Append(Ptr pItem) { mStack.back()->push_back(std::move(pItem)); }
200
201 void Append(
202 const Identifier &stringId, PopupMenuTableEntry::Type type, int id,
203 const TranslatableString &string, wxCommandEventFunction memFn,
204 // This callback might check or disable a menu item:
206
207 void AppendItem( const Identifier &stringId, int id,
208 const TranslatableString &string, wxCommandEventFunction memFn,
209 // This callback might check or disable a menu item:
210 const PopupMenuTableEntry::InitFunction &init = {} )
211 { Append( stringId, PopupMenuTableEntry::Item, id, string, memFn, init ); }
212
213 void AppendRadioItem( const Identifier &stringId, int id,
214 const TranslatableString &string, wxCommandEventFunction memFn,
215 // This callback might check or disable a menu item:
216 const PopupMenuTableEntry::InitFunction &init = {} )
217 { Append( stringId, PopupMenuTableEntry::RadioItem, id, string, memFn, init ); }
218
219 void AppendCheckItem( const Identifier &stringId, int id,
220 const TranslatableString &string, wxCommandEventFunction memFn,
221 const PopupMenuTableEntry::InitFunction &init = {} )
222 { Append( stringId, PopupMenuTableEntry::CheckItem, id, string, memFn, init ); }
223
224 void BeginSection( const Identifier &name );
225 void EndSection();
226
227 std::shared_ptr<PopupSubMenu> mTop;
228 std::vector<PopupMenuGroupItem*> mStack;
231 std::unique_ptr<PopupMenuGroupItem> mRegistry;
232};
233
234/*
235The following macros make it easy to attach a popup menu to a window.
236
237Example of usage:
238
239In class MyTable (maybe in the private section),
240which inherits from PopupMenuTable,
241
242DECLARE_POPUP_MENU(MyTable);
243virtual void InitUserData(void *pUserData);
244virtual void DestroyMenu();
245
246Then in MyTable.cpp,
247
248void MyTable::InitUserData(void *pUserData)
249{
250 // Remember pData
251 auto pData = static_cast<MyData*>(pUserData);
252}
253
254void MyTable::DestroyMenu()
255{
256 // Do cleanup
257}
258
259BEGIN_POPUP_MENU(MyTable)
260 // This is inside a function and can contain arbitrary code. But usually
261 // you only need a sequence of calls:
262
263 AppendItem("Cut",
264 OnCutSelectedTextID, XO("Cu&t"), POPUP_MENU_FN( OnCutSelectedText ),
265 // optional argument:
266 [](PopupMenuHandler &handler, wxMenu &menu, int id)
267 {
268 auto data = static_cast<MyTable&>( handler ).pData;
269 // maybe enable or disable the menu item
270 }
271 );
272 // etc.
273
274END_POPUP_MENU()
275
276where OnCutSelectedText is a (maybe private) member function of MyTable.
277
278Elsewhere,
279
280MyTable myTable;
281MyData data;
282auto pMenu = PopupMenuTable::BuildMenu(pParent, &myTable, &data);
283
284// Optionally:
285OtherTable otherTable;
286PopupMenuTable::ExtendMenu( *pMenu, otherTable );
287
288pMenu->Popup( *pParent, { event.m_x, event.m_y } );
289
290That's all!
291*/
292
293#define DECLARE_POPUP_MENU(HandlerClass) \
294 void Populate() override;
295
296// begins function
297#define BEGIN_POPUP_MENU(HandlerClass) \
298void HandlerClass::Populate() { \
299 using My = HandlerClass; \
300 mTop = std::make_shared< PopupSubMenu >( \
301 Id(), Caption(), *this ); \
302 mStack.clear(); \
303 mStack.push_back( mTop.get() );
304
305#define POPUP_MENU_FN( memFn ) ( (wxCommandEventFunction) (&My::memFn) )
306
307#define POPUP_MENU_SUB_MENU(stringId, classname, pUserData ) \
308 mStack.back()->push_back( \
309 Registry::Indirect(classname::Instance().Get(pUserData)));
310
311// ends function
312#define END_POPUP_MENU() }
313
314#endif
static RegisteredToolbarFactory factory
XXO("&Cut/Copy/Paste Toolbar")
EndSection()
BeginSection("Basic")
Registry::GroupItem< PopupMenuTableTraits > PopupMenuGroupItem
wxString name
Definition: TagsEditor.cpp:166
TranslatableString label
Definition: TagsEditor.cpp:165
Append(Adapt< My >([](My &table) { return(WaveChannelSubViews::numFactories() > 1) ? 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=WaveChannelView::GetFirst(track);menu.Check(id, view.GetMultiView());}) :nullptr;}))
int id
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 Append(Ptr pItem)
void AppendRadioItem(const Identifier &stringId, int id, const TranslatableString &string, wxCommandEventFunction memFn, const PopupMenuTableEntry::InitFunction &init={})
PopupMenuTable(const Identifier &id, const TranslatableString &caption={})
std::shared_ptr< PopupSubMenu > mTop
const auto * GetRegistry() const
virtual void Populate()=0
static auto Adapt(const Factory &factory)
const Identifier & Id() const
std::unique_ptr< PopupMenuGroupItem > mRegistry
static TranslatableString MakeLabel(const TranslatableString &label, bool useExtra, const TranslatableString &extra)
void RegisterItem(const Registry::Placement &placement, Ptr pItem)
TranslatableString mCaption
const TranslatableString & Caption() const
std::vector< PopupMenuGroupItem * > mStack
void AppendCheckItem(const Identifier &stringId, int id, const TranslatableString &string, wxCommandEventFunction memFn, const PopupMenuTableEntry::InitFunction &init={})
const auto & Get(void *pUserData)
Holds a msgid for the translation catalog; may also bind format arguments.
void RegisterItem(GroupItem< RegistryTraits > &registry, const Placement &placement, std::unique_ptr< Item > pItem)
Definition: Registry.h:371
A mix-in discovered by dynamic_cast; independent of the Traits.
Definition: MenuRegistry.h:106
~PopupMenuSection() override
Properties GetProperties() const override
AttachedItem(PopupMenuTable &table, const Registry::Placement &placement, Ptr 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
PopupMenuTable & table
TranslatableString caption
Has variadic and range constructors that check types.
Definition: Registry.h:292
Common abstract base class for items that are not groups.
Definition: Registry.h:224
Primary template for a list of arbitrary types.
Definition: TypeList.h:61