audacia/src/widgets/PopupMenuTable.cpp

226 lines
6.1 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
PopupMenuTable.cpp
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#include "PopupMenuTable.h"
PopupMenuTableEntry::~PopupMenuTableEntry()
{}
PopupSubMenu::~PopupSubMenu()
{}
PopupSubMenu::PopupSubMenu( const Identifier &stringId,
const TranslatableString &caption_, PopupMenuTable &table_ )
: ConcreteGroupItem< false >{ stringId }
, WholeMenu{ caption_.empty() }
, caption{ caption_ }
, table{ table_ }
{
}
namespace {
struct PopupMenu : public wxMenu
{
PopupMenu(wxEvtHandler *pParent_, void *pUserData_)
: pParent{ pParent_ }, tables{}, pUserData{ pUserData_ } {}
~PopupMenu() override;
void Extend(PopupMenuTable *pTable);
void DisconnectTable(PopupMenuTable *pTable);
void Disconnect();
wxEvtHandler *pParent;
std::vector<PopupMenuTable*> tables;
void *pUserData;
};
class PopupMenuBuilder : public PopupMenuVisitor {
public:
explicit
PopupMenuBuilder( PopupMenuTable &table, PopupMenu &menu, void *pUserData )
: PopupMenuVisitor{ table }
, mMenu{ &menu }
, mRoot{ mMenu }
, mpUserData{ pUserData }
{}
void DoBeginGroup( Registry::GroupItem &item, const Path &path ) override;
void DoEndGroup( Registry::GroupItem &item, const Path &path ) override;
void DoVisit( Registry::SingleItem &item, const Path &path ) override;
void DoSeparator() override;
std::vector< std::unique_ptr<PopupMenu> > mMenus;
PopupMenu *mMenu, *mRoot;
void *const mpUserData;
};
void PopupMenuBuilder::DoBeginGroup( Registry::GroupItem &item, const Path &path )
{
if ( auto pItem = dynamic_cast<PopupSubMenu*>(&item) ) {
if ( !pItem->caption.empty() ) {
auto newMenu =
std::make_unique<PopupMenu>( mMenu->pParent, mMenu->pUserData );
newMenu->tables.push_back( &pItem->table );
mMenu = newMenu.get();
mMenus.push_back( std::move( newMenu ) );
}
}
}
void PopupMenuBuilder::DoEndGroup( Registry::GroupItem &item, const Path &path )
{
if ( auto pItem = dynamic_cast<PopupSubMenu*>(&item) ) {
if ( !pItem->caption.empty() ) {
auto subMenu = std::move( mMenus.back() );
mMenus.pop_back();
mMenu = mMenus.empty() ? mRoot : mMenus.back().get();
mMenu->AppendSubMenu( subMenu.release(), pItem->caption.Translation());
}
}
}
void PopupMenuBuilder::DoVisit( Registry::SingleItem &item, const Path &path )
{
auto pEntry = static_cast<PopupMenuTableEntry*>( &item );
switch (pEntry->type) {
case PopupMenuTable::Entry::Item:
{
mMenu->Append(pEntry->id, pEntry->caption.Translation());
break;
}
case PopupMenuTable::Entry::RadioItem:
{
mMenu->AppendRadioItem(pEntry->id, pEntry->caption.Translation());
break;
}
case PopupMenuTable::Entry::CheckItem:
{
mMenu->AppendCheckItem(pEntry->id, pEntry->caption.Translation());
break;
}
default:
wxASSERT( false );
break;
}
// This call necessary for externally registered items, else harmlessly
// redundant
pEntry->handler.InitUserData( mpUserData );
if ( pEntry->init )
pEntry->init( pEntry->handler, *mMenu, pEntry->id );
mMenu->pParent->Bind(
wxEVT_COMMAND_MENU_SELECTED, pEntry->func, &pEntry->handler, pEntry->id);
}
void PopupMenuBuilder::DoSeparator()
{
mMenu->AppendSeparator();
}
PopupMenu::~PopupMenu()
{
// Event connections between the parent window and the singleton table
// object must be broken when this menu is destroyed.
Disconnect();
}
}
void PopupMenuTable::ExtendMenu( wxMenu &menu, PopupMenuTable &table )
{
auto &theMenu = dynamic_cast<PopupMenu&>(menu);
theMenu.tables.push_back( &table );
PopupMenuBuilder visitor{ table, theMenu, theMenu.pUserData };
Registry::Visit(
visitor, table.Get( theMenu.pUserData ).get(), table.GetRegistry() );
}
void PopupMenuTable::RegisterItem(
const Registry::Placement &placement, Registry::BaseItemPtr pItem )
{
Registry::RegisterItem( *mRegistry, placement, std::move( pItem ) );
}
void PopupMenuTable::Append( Registry::BaseItemPtr pItem )
{
mStack.back()->items.push_back( std::move( pItem ) );
}
void PopupMenuTable::Append(
const Identifier &stringId, PopupMenuTableEntry::Type type, int id,
const TranslatableString &string, wxCommandEventFunction memFn,
const PopupMenuTableEntry::InitFunction &init )
{
Append( std::make_unique<Entry>(
stringId, type, id, string, memFn, *this, init ) );
}
void PopupMenuTable::BeginSection( const Identifier &name )
{
auto uSection = std::make_unique< PopupMenuSection >( name );
auto section = uSection.get();
mStack.back()->items.push_back( std::move( uSection ) );
mStack.push_back( section );
}
void PopupMenuTable::EndSection()
{
mStack.pop_back();
}
namespace{
void PopupMenu::DisconnectTable(PopupMenuTable *pTable)
{
class PopupMenuDestroyer : public PopupMenuVisitor {
public:
explicit
PopupMenuDestroyer( PopupMenuTable &table, PopupMenu &menu )
: PopupMenuVisitor{ table }
, mMenu{ menu }
{}
void DoVisit( Registry::SingleItem &item, const Path &path ) override
{
auto pEntry = static_cast<PopupMenuTableEntry*>( &item );
mMenu.pParent->Unbind( wxEVT_COMMAND_MENU_SELECTED,
pEntry->func, &pEntry->handler, pEntry->id );
pEntry->handler.DestroyMenu();
}
PopupMenu &mMenu;
};
PopupMenuDestroyer visitor{ *pTable, *this };
Registry::Visit( visitor, pTable->Get( nullptr ).get(),
pTable->GetRegistry() );
}
void PopupMenu::Disconnect()
{
for ( auto pTable : tables )
DisconnectTable(pTable);
}
}
// static
std::unique_ptr<wxMenu> PopupMenuTable::BuildMenu
( wxEvtHandler *pParent, PopupMenuTable *pTable, void *pUserData )
{
// Rebuild as needed each time. That makes it safe in case of language change.
auto theMenu = std::make_unique<PopupMenu>( pParent, pUserData );
ExtendMenu( *theMenu, *pTable );
return theMenu;
}