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

@ -1238,6 +1238,7 @@
5ED1D0B11CDE560C00471E3C /* BackedPanel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5ED1D0AF1CDE560C00471E3C /* BackedPanel.cpp */; };
5EF17C231D1F0A690090A642 /* ScrubbingToolBar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5EF17C211D1F0A690090A642 /* ScrubbingToolBar.cpp */; };
5EF958851DEB121800191280 /* InconsistencyException.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5EF958831DEB121800191280 /* InconsistencyException.cpp */; };
5E73963B1DAFD82D00BA0A4D /* PopupMenuTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E7396391DAFD82D00BA0A4D /* PopupMenuTable.cpp */; };
8406A93812D0F2510011EA01 /* EQDefaultCurves.xml in Resources */ = {isa = PBXBuildFile; fileRef = 8406A93712D0F2510011EA01 /* EQDefaultCurves.xml */; };
8484F31413086237002DF7F0 /* DeviceManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8484F31213086237002DF7F0 /* DeviceManager.cpp */; };
AA0084191EA8C6E70070CCE3 /* TracksBehaviorsPrefs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA0084181EA8C6E70070CCE3 /* TracksBehaviorsPrefs.cpp */; };
@ -3072,6 +3073,8 @@
5EF17C221D1F0A690090A642 /* ScrubbingToolBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScrubbingToolBar.h; sourceTree = "<group>"; };
5EF958831DEB121800191280 /* InconsistencyException.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InconsistencyException.cpp; sourceTree = "<group>"; };
5EF958841DEB121800191280 /* InconsistencyException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InconsistencyException.h; sourceTree = "<group>"; };
5E7396391DAFD82D00BA0A4D /* PopupMenuTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PopupMenuTable.cpp; sourceTree = "<group>"; };
5E73963A1DAFD82D00BA0A4D /* PopupMenuTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PopupMenuTable.h; sourceTree = "<group>"; };
82FF184D13CF01A600C1B664 /* dBTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dBTable.cpp; path = sbsms/src/dBTable.cpp; sourceTree = "<group>"; };
82FF184E13CF01A600C1B664 /* dBTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dBTable.h; path = sbsms/src/dBTable.h; sourceTree = "<group>"; };
82FF184F13CF01A600C1B664 /* slide.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = slide.cpp; path = sbsms/src/slide.cpp; sourceTree = "<group>"; };
@ -4442,6 +4445,8 @@
5ED1D0A91CDE55BD00471E3C /* Overlay.cpp */,
5ED1D0AA1CDE55BD00471E3C /* Overlay.h */,
5ED1D0AB1CDE55BD00471E3C /* OverlayPanel.cpp */,
5E7396391DAFD82D00BA0A4D /* PopupMenuTable.cpp */,
5E73963A1DAFD82D00BA0A4D /* PopupMenuTable.h */,
5ED1D0AC1CDE55BD00471E3C /* OverlayPanel.h */,
28530C4A0DF2105200555C94 /* ProgressDialog.cpp */,
28530C4B0DF2105200555C94 /* ProgressDialog.h */,
@ -7606,6 +7611,7 @@
1790B18709883BFD008A330A /* QualityPrefs.cpp in Sources */,
1790B18809883BFD008A330A /* SpectrumPrefs.cpp in Sources */,
1790B18909883BFD008A330A /* Prefs.cpp in Sources */,
5E73963B1DAFD82D00BA0A4D /* PopupMenuTable.cpp in Sources */,
1790B18A09883BFD008A330A /* Printing.cpp in Sources */,
1790B18B09883BFD008A330A /* Project.cpp in Sources */,
1790B18C09883BFD008A330A /* Resample.cpp in Sources */,

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

View File

@ -256,6 +256,7 @@
<ClCompile Include="..\..\..\src\widgets\Overlay.cpp" />
<ClCompile Include="..\..\..\src\widgets\OverlayPanel.cpp" />
<ClCompile Include="..\..\..\src\widgets\wxPanelWrapper.cpp" />
<ClCompile Include="..\..\..\src\widgets\PopupMenuTable.cpp" />
<ClCompile Include="..\..\..\src\WrappedType.cpp" />
<ClCompile Include="..\..\..\src\effects\Amplify.cpp" />
<ClCompile Include="..\..\..\src\effects\AutoDuck.cpp" />
@ -511,6 +512,7 @@
<ClInclude Include="..\..\..\src\widgets\OverlayPanel.h" />
<ClInclude Include="..\..\..\src\widgets\wxPanelWrapper.h" />
<ClInclude Include="..\..\..\src\wxFileNameWrapper.h" />
<ClInclude Include="..\..\..\src\widgets\PopupMenuTable.h" />
<ClInclude Include="..\..\configwin.h" />
<ClInclude Include="..\..\..\src\Dependencies.h" />
<ClInclude Include="..\..\..\src\DeviceManager.h" />
@ -1169,4 +1171,4 @@
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\ny.targets" />
</ImportGroup>
</Project>
</Project>

View File

@ -986,6 +986,9 @@
<ClCompile Include="..\..\..\src\tracks\playabletrack\wavetrack\ui\WaveTrackVRulerControls.cpp">
<Filter>src\tracks\playabletrack\wavetrack\ui</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\widgets\PopupMenuTable.cpp">
<Filter>src\widgets</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\src\AboutDialog.h">
@ -1966,6 +1969,9 @@
<ClInclude Include="..\..\..\src\tracks\playabletrack\wavetrack\ui\WaveTrackVRulerControls.h">
<Filter>src\tracks\playabletrack\wavetrack\ui</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\widgets\PopupMenuTable.h">
<Filter>src\widgets</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="..\..\audacity.ico">