1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-12-12 07:36:24 +01:00

Define class PopupMenuTable to make it easy to attach and detach handlers...

... to the parent TrackPanel window
This commit is contained in:
Paul Licameli
2015-07-11 14:37:53 -04:00
committed by Paul Licameli
parent 14d45eda33
commit cba51e1bf8
6 changed files with 307 additions and 1 deletions

View File

@@ -622,6 +622,8 @@ audacity_SOURCES = \
widgets/Overlay.h \
widgets/OverlayPanel.cpp \
widgets/OverlayPanel.h \
widgets/PopupMenuTable.cpp \
widgets/PopupMenuTable.h \
widgets/ProgressDialog.cpp \
widgets/ProgressDialog.h \
widgets/Ruler.cpp \

View File

@@ -0,0 +1,94 @@
/**********************************************************************
Audacity: A Digital Audio Editor
PopupMenuTable.cpp
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#include "../Audacity.h"
#include "PopupMenuTable.h"
PopupMenuTable::Menu::~Menu()
{
Disconnect();
}
void PopupMenuTable::Menu::Extend(PopupMenuTable *pTable)
{
auto connect = [&]( const PopupMenuTable::Entry *pEntry ) {
this->pParent->Connect
(pEntry->id, wxEVT_COMMAND_MENU_SELECTED,
pEntry->func, NULL, pTable);
};
for (const PopupMenuTable::Entry *pEntry = &*pTable->Get().begin();
pEntry->IsValid(); ++pEntry) {
switch (pEntry->type) {
case PopupMenuTable::Entry::Item:
{
this->Append(pEntry->id, pEntry->caption);
connect( pEntry );
break;
}
case PopupMenuTable::Entry::RadioItem:
{
this->AppendRadioItem(pEntry->id, pEntry->caption);
connect( pEntry );
break;
}
case PopupMenuTable::Entry::CheckItem:
{
this->AppendCheckItem(pEntry->id, pEntry->caption);
connect( pEntry );
break;
}
case PopupMenuTable::Entry::Separator:
this->AppendSeparator();
break;
case PopupMenuTable::Entry::SubMenu:
{
const auto subTable = pEntry->subTable;
auto subMenu = BuildMenu( this->pParent, subTable, pUserData );
this->AppendSubMenu( subMenu.release(), pEntry->caption );
}
default:
break;
}
}
pTable->InitMenu(this, pUserData);
}
void PopupMenuTable::Menu::DisconnectTable(PopupMenuTable *pTable)
{
for (const PopupMenuTable::Entry *pEntry = &*pTable->Get().begin();
pEntry->IsValid(); ++pEntry) {
if ( pEntry->IsItem() )
pParent->Disconnect( pEntry->id, wxEVT_COMMAND_MENU_SELECTED,
pEntry->func, NULL, pTable );
else if ( pEntry->IsSubMenu() )
// recur
DisconnectTable(pEntry->subTable);
}
pTable->DestroyMenu();
}
void PopupMenuTable::Menu::Disconnect()
{
for ( auto pTable : tables )
DisconnectTable(pTable);
}
// static
std::unique_ptr<PopupMenuTable::Menu> PopupMenuTable::BuildMenu
( wxEvtHandler *pParent, PopupMenuTable *pTable, void *pUserData )
{
// Rebuild as needed each time. That makes it safe in case of language change.
std::unique_ptr<Menu> theMenu{ safenew Menu( pParent, pUserData ) };
theMenu->Extend(pTable);
return std::move( theMenu );
}

View File

@@ -0,0 +1,196 @@
/**********************************************************************
Audacity: A Digital Audio Editor
PopupMenuTable.h
Paul Licameli
This file defines PopupMenuTable, which inherits from wxEventHandler,
associated macros simplifying the population of tables,
and PopupMenuTable::Menu which is buildable from one or more such
tables, and automatically attaches and detaches the event handlers.
**********************************************************************/
#ifndef __AUDACITY_POPUP_MENU_TABLE__
#define __AUDACITY_POPUP_MENU_TABLE__
class wxCommandEvent;
class wxString;
#include <vector>
#include <wx/event.h>
#include <wx/menu.h>
#include "../MemoryX.h"
#include "../TranslatableStringArray.h"
class PopupMenuTable;
struct PopupMenuTableEntry
{
enum Type { Item, RadioItem, CheckItem, Separator, SubMenu, Invalid };
Type type;
int id;
wxString caption;
wxObjectEventFunction func;
PopupMenuTable *subTable;
PopupMenuTableEntry(Type type_, int id_, wxString caption_,
wxObjectEventFunction func_, PopupMenuTable *subTable_)
: type(type_)
, id(id_)
, caption(caption_)
, func(func_)
, subTable(subTable_)
{}
bool IsItem() const { return type == Item || type == RadioItem || type == CheckItem; }
bool IsSubMenu() const { return type == SubMenu; }
bool IsValid() const { return type != Invalid; }
};
class PopupMenuTable : public TranslatableArray< std::vector<PopupMenuTableEntry> >
{
public:
typedef PopupMenuTableEntry Entry;
class Menu
: public wxMenu
{
friend class PopupMenuTable;
Menu(wxEvtHandler *pParent_, void *pUserData_)
: pParent{ pParent_ }, tables{}, pUserData{ pUserData_ } {}
public:
virtual ~Menu();
void Extend(PopupMenuTable *pTable);
void DisconnectTable(PopupMenuTable *pTable);
void Disconnect();
private:
wxEvtHandler *pParent;
std::vector<PopupMenuTable*> tables;
void *pUserData;
};
// Called when the menu is about to pop up.
// Your chance to enable and disable items.
virtual void InitMenu(Menu *pMenu, void *pUserData) = 0;
// Called when menu is destroyed.
virtual void DestroyMenu() = 0;
// Optional pUserData gets passed to the InitMenu routines of tables.
// No memory management responsibility is assumed by this function.
static std::unique_ptr<Menu> BuildMenu
(wxEvtHandler *pParent, PopupMenuTable *pTable, void *pUserData = NULL);
};
/*
The following macros make it easy to attach a popup menu to a window.
Exmple of usage:
In class MyTable (maybe in the private section),
which inherits from PopupMenuTable,
DECLARE_POPUP_MENU(MyTable);
virtual void InitMenu(Menu *pMenu, void *pUserData);
virtual void DestroyMenu();
Then in MyTable.cpp,
void MyTable::InitMenu(Menu *pMenu, void *pUserData)
{
auto pData = static_cast<MyData*>(pUserData);
// Remember pData, enable or disable menu items
}
void MyTable::DestroyMenu()
{
// Do cleanup
}
BEGIN_POPUP_MENU(MyTable)
// This is inside a function and can contain arbitrary code. But usually
// you only need a sequence of macro calls:
POPUP_MENU_ITEM(OnCutSelectedTextID, _("Cu&t"), OnCutSelectedText)
// etc.
END_POPUP_MENU()
where OnCutSelectedText is a (maybe private) member function of MyTable.
Elswhere,
MyTable myTable;
MyData data;
auto pMenu = PopupMenuTable::BuildMenu(pParent, &myTable, &data);
// Optionally:
OtherTable otherTable;
pMenu->Extend(&otherTable);
pParent->PopupMenu(pMenu.get(), event.m_x, event.m_y);
That's all!
*/
#define DECLARE_POPUP_MENU(HandlerClass) \
virtual void Populate();
// begins function
#define BEGIN_POPUP_MENU(HandlerClass) \
void HandlerClass::Populate() { \
typedef HandlerClass My;
#define POPUP_MENU_APPEND(type, id, string, memFn, subTable) \
mContents.push_back( Entry { \
type, \
id, \
string, \
memFn, \
subTable \
} );
#define POPUP_MENU_APPEND_ITEM(type, id, string, memFn) \
POPUP_MENU_APPEND( \
type, \
id, \
string, \
(wxObjectEventFunction)(wxEventFunction)(wxCommandEventFunction) \
(&My::memFn), \
nullptr )
#define POPUP_MENU_ITEM(id, string, memFn) \
POPUP_MENU_APPEND_ITEM(Entry::Item, id, string, memFn);
#define POPUP_MENU_RADIO_ITEM(id, string, memFn) \
POPUP_MENU_APPEND_ITEM(Entry::RadioItem, id, string, memFn);
#define POPUP_MENU_CHECK_ITEM(id, string, memFn) \
POPUP_MENU_APPEND_ITEM(Entry::CheckItem, id, string, memFn);
// classname names a class that derives from MenuTable and defines Instance()
#define POPUP_MENU_SUB_MENU(id, string, classname) \
POPUP_MENU_APPEND( \
Entry::SubMenu, id, string, nullptr, &classname::Instance() );
#define POPUP_MENU_SEPARATOR() \
POPUP_MENU_APPEND( \
Entry::Separator, -1, wxT(""), nullptr, nullptr );
// ends function
#define END_POPUP_MENU() \
POPUP_MENU_APPEND( \
Entry::Invalid, -1, wxT(""), nullptr, nullptr ) \
}
#endif