226 lines
6.1 KiB
C++
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;
|
|
}
|
|
|