1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-11-21 16:37:12 +01:00

Define abstract Visitor for menu items, and concrete subclass...

... The general visitation procedure will find reuse with other specific
actions, and maybe for other non-menu trees too
This commit is contained in:
Paul Licameli
2019-01-08 12:19:51 -05:00
parent 4eee417ded
commit 356e5d545e
3 changed files with 143 additions and 53 deletions

View File

@@ -121,6 +121,12 @@ void GroupItem::AppendOne( BaseItemPtr&& ptr )
GroupItem::~GroupItem() {} GroupItem::~GroupItem() {}
TransparentGroupItem::~TransparentGroupItem() {} TransparentGroupItem::~TransparentGroupItem() {}
Visitor::~Visitor(){}
void Visitor::BeginGroup(GroupItem &, const Path &) {}
void Visitor::EndGroup(GroupItem &, const Path &) {}
void Visitor::Visit(SingleItem &, const Path &) {}
} }
namespace MenuTable { namespace MenuTable {
@@ -250,9 +256,11 @@ using Path = std::vector< Identifier >;
// forward declaration for mutually recursive functions // forward declaration for mutually recursive functions
void VisitItem( void VisitItem(
Registry::Visitor &visitor,
AudacityProject &project, CollectedItems &collection, AudacityProject &project, CollectedItems &collection,
Path &path, BaseItem *pItem ); Path &path, BaseItem *pItem );
void VisitItems( void VisitItems(
Registry::Visitor &visitor,
AudacityProject &project, CollectedItems &collection, AudacityProject &project, CollectedItems &collection,
Path &path, GroupItem *pGroup ) Path &path, GroupItem *pGroup )
{ {
@@ -265,69 +273,28 @@ void VisitItems(
// Now visit them // Now visit them
path.push_back( pGroup->name ); path.push_back( pGroup->name );
for ( const auto &pSubItem : newCollection.items ) for ( const auto &pSubItem : newCollection.items )
VisitItem( project, collection, path, pSubItem ); VisitItem( visitor, project, collection, path, pSubItem );
path.pop_back(); path.pop_back();
} }
void VisitItem( void VisitItem(
Registry::Visitor &visitor,
AudacityProject &project, CollectedItems &collection, AudacityProject &project, CollectedItems &collection,
Path &path, BaseItem *pItem ) Path &path, BaseItem *pItem )
{ {
if (!pItem) if (!pItem)
return; return;
auto &manager = CommandManager::Get( project ); if (const auto pSingle =
dynamic_cast<SingleItem*>( pItem )) {
if (const auto pCommand = visitor.Visit( *pSingle, path );
dynamic_cast<CommandItem*>( pItem )) {
manager.AddItem( project,
pCommand->name, pCommand->label_in,
pCommand->finder, pCommand->callback,
pCommand->flags, pCommand->options
);
}
else
if (const auto pCommandList =
dynamic_cast<CommandGroupItem*>( pItem ) ) {
manager.AddItemList(pCommandList->name,
pCommandList->items.data(), pCommandList->items.size(),
pCommandList->finder, pCommandList->callback,
pCommandList->flags, pCommandList->isEffect);
}
else
if (const auto pMenu =
dynamic_cast<MenuItem*>( pItem )) {
manager.BeginMenu( pMenu->title );
// recursion
VisitItems( project, collection, path, pMenu );
manager.EndMenu();
}
else
if (const auto pConditionalGroup =
dynamic_cast<ConditionalGroupItem*>( pItem )) {
const auto flag = pConditionalGroup->condition();
if (!flag)
manager.BeginOccultCommands();
// recursion
VisitItems( project, collection, path, pConditionalGroup );
if (!flag)
manager.EndOccultCommands();
} }
else else
if (const auto pGroup = if (const auto pGroup =
dynamic_cast<TransparentGroupItem*>( pItem )) { dynamic_cast<GroupItem*>( pItem )) {
visitor.BeginGroup( *pGroup, path );
// recursion // recursion
VisitItems( project, collection, path, pGroup ); VisitItems( visitor, project, collection, path, pGroup );
} visitor.EndGroup( *pGroup, path );
else
if (dynamic_cast<SeparatorItem*>( pItem )) {
manager.AddSeparator();
}
else
if (const auto pSpecial =
dynamic_cast<SpecialItem*>( pItem )) {
const auto pCurrentMenu = manager.CurrentMenu();
wxASSERT( pCurrentMenu );
pSpecial->fn( project, *pCurrentMenu );
} }
else else
wxASSERT( false ); wxASSERT( false );
@@ -337,12 +304,14 @@ void VisitItem(
namespace Registry { namespace Registry {
void VisitTopItem( AudacityProject &project, BaseItem *pTopItem ) void Visit(
Visitor &visitor, AudacityProject &project,
BaseItem *pTopItem )
{ {
std::vector< BaseItemSharedPtr > computedItems; std::vector< BaseItemSharedPtr > computedItems;
CollectedItems collection{ {}, computedItems }; CollectedItems collection{ {}, computedItems };
Path emptyPath; Path emptyPath;
VisitItem( project, collection, emptyPath, pTopItem ); VisitItem( visitor, project, collection, emptyPath, pTopItem );
} }
} }
@@ -392,6 +361,99 @@ static const auto menuTree = MenuTable::Items( MenuPathStart
, HelpMenu() , HelpMenu()
); );
namespace {
struct MenuItemVisitor : Registry::Visitor
{
MenuItemVisitor( AudacityProject &proj, CommandManager &man )
: project(proj), manager( man ) {}
void BeginGroup( GroupItem &item, const Path& ) override
{
auto pItem = &item;
if (const auto pMenu =
dynamic_cast<MenuItem*>( pItem )) {
manager.BeginMenu( pMenu->title );
}
else
if (const auto pConditionalGroup =
dynamic_cast<ConditionalGroupItem*>( pItem )) {
const auto flag = pConditionalGroup->condition();
if (!flag)
manager.BeginOccultCommands();
// to avoid repeated call of condition predicate in EndGroup():
flags.push_back(flag);
}
else
if (const auto pGroup =
dynamic_cast<TransparentGroupItem*>( pItem )) {
}
else
wxASSERT( false );
}
void EndGroup( GroupItem &item, const Path& ) override
{
auto pItem = &item;
if (const auto pMenu =
dynamic_cast<MenuItem*>( pItem )) {
manager.EndMenu();
}
else
if (const auto pConditionalGroup =
dynamic_cast<ConditionalGroupItem*>( pItem )) {
const bool flag = flags.back();
if (!flag)
manager.EndOccultCommands();
flags.pop_back();
}
else
if (const auto pGroup =
dynamic_cast<TransparentGroupItem*>( pItem )) {
}
else
wxASSERT( false );
}
void Visit( SingleItem &item, const Path& ) override
{
auto pItem = &item;
if (const auto pCommand =
dynamic_cast<CommandItem*>( pItem )) {
manager.AddItem( project,
pCommand->name, pCommand->label_in,
pCommand->finder, pCommand->callback,
pCommand->flags, pCommand->options
);
}
else
if (const auto pCommandList =
dynamic_cast<CommandGroupItem*>( pItem ) ) {
manager.AddItemList(pCommandList->name,
pCommandList->items.data(), pCommandList->items.size(),
pCommandList->finder, pCommandList->callback,
pCommandList->flags, pCommandList->isEffect);
}
else
if (dynamic_cast<SeparatorItem*>( pItem )) {
manager.AddSeparator();
}
else
if (const auto pSpecial =
dynamic_cast<SpecialItem*>( pItem )) {
const auto pCurrentMenu = manager.CurrentMenu();
wxASSERT( pCurrentMenu );
pSpecial->fn( project, *pCurrentMenu );
}
else
wxASSERT( false );
}
AudacityProject &project;
CommandManager &manager;
std::vector<bool> flags;
};
}
void MenuCreator::CreateMenusAndCommands(AudacityProject &project) void MenuCreator::CreateMenusAndCommands(AudacityProject &project)
{ {
auto &commandManager = CommandManager::Get( project ); auto &commandManager = CommandManager::Get( project );
@@ -403,7 +465,8 @@ void MenuCreator::CreateMenusAndCommands(AudacityProject &project)
auto menubar = commandManager.AddMenuBar(wxT("appmenu")); auto menubar = commandManager.AddMenuBar(wxT("appmenu"));
wxASSERT(menubar); wxASSERT(menubar);
Registry::VisitTopItem( project, menuTree.get() ); MenuItemVisitor visitor{ project, commandManager };
MenuManager::Visit( visitor, project );
GetProjectFrame( project ).SetMenuBar(menubar.release()); GetProjectFrame( project ).SetMenuBar(menubar.release());
@@ -414,6 +477,11 @@ void MenuCreator::CreateMenusAndCommands(AudacityProject &project)
#endif #endif
} }
void MenuManager::Visit( Registry::Visitor &visitor, AudacityProject &project )
{
Registry::Visit( visitor, project, menuTree.get() );
}
// TODO: This surely belongs in CommandManager? // TODO: This surely belongs in CommandManager?
void MenuManager::ModifyUndoMenuItems(AudacityProject &project) void MenuManager::ModifyUndoMenuItems(AudacityProject &project)
{ {

View File

@@ -35,6 +35,8 @@ enum EffectType : int;
typedef wxString PluginID; typedef wxString PluginID;
typedef wxArrayString PluginIDs; typedef wxArrayString PluginIDs;
namespace Registry{ class Visitor; }
class MenuCreator class MenuCreator
{ {
public: public:
@@ -68,6 +70,9 @@ public:
MenuManager &operator=( const MenuManager & ) PROHIBITED; MenuManager &operator=( const MenuManager & ) PROHIBITED;
~MenuManager(); ~MenuManager();
static void Visit(
Registry::Visitor &visitor, AudacityProject &project );
static void ModifyUndoMenuItems(AudacityProject &project); static void ModifyUndoMenuItems(AudacityProject &project);
static void ModifyToolbarMenus(AudacityProject &project); static void ModifyToolbarMenus(AudacityProject &project);
// Calls ModifyToolbarMenus() on all projects // Calls ModifyToolbarMenus() on all projects

View File

@@ -524,6 +524,23 @@ namespace Registry {
using GroupItem::GroupItem; using GroupItem::GroupItem;
~TransparentGroupItem() override; ~TransparentGroupItem() override;
}; };
// Define actions to be done in Visit.
// Default implementations do nothing
// The supplied path does not include the name of the item
class Visitor
{
public:
virtual ~Visitor();
using Path = std::vector< Identifier >;
virtual void BeginGroup( GroupItem &item, const Path &path );
virtual void EndGroup( GroupItem &item, const Path &path );
virtual void Visit( SingleItem &item, const Path &path );
};
// Top-down visitation of all items and groups in a tree
void Visit(
Visitor &visitor, AudacityProject &project, BaseItem *pTopItem );
} }
// Define items that populate tables that specifically describe menu trees // Define items that populate tables that specifically describe menu trees