From fca64ec8d388fb63f99fe1c6cd47e41316824ae0 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Mon, 20 Mar 2023 14:39:58 -0400 Subject: [PATCH] Lift wxWidgets-specific logic out of CommandManager with new virtuals --- src/MenuCreator.cpp | 97 +++++++++++++++++++ src/commands/CommandManager.cpp | 167 +++----------------------------- src/commands/CommandManager.h | 62 +++++++++--- 3 files changed, 157 insertions(+), 169 deletions(-) diff --git a/src/MenuCreator.cpp b/src/MenuCreator.cpp index 2661d887df5f..6e7439fcf7ac 100644 --- a/src/MenuCreator.cpp +++ b/src/MenuCreator.cpp @@ -91,6 +91,16 @@ struct MenuItemVisitor final : CommandManager::Populator void DoVisit(const SingleItem &item, const Path&) final; void DoSeparator() final; + +private: + void BeginMenu(const TranslatableString & tName) final; + void EndMenu() final; + void BeginMainMenu(const TranslatableString & tName); + void EndMainMenu(); + void BeginSubMenu(const TranslatableString & tName); + void EndSubMenu(); + void BeginOccultCommands() final; + void EndOccultCommands() final; }; MenuItemVisitor::CommandListEntryEx::~CommandListEntryEx() = default; @@ -292,6 +302,93 @@ MenuItemVisitor::MenuItemVisitor(AudacityProject &proj) } MenuItemVisitor::~MenuItemVisitor() = default; + +void MenuItemVisitor::BeginMenu(const TranslatableString & tName) +{ + if ( mCurrentMenu ) + return BeginSubMenu( tName ); + else + return BeginMainMenu( tName ); +} + +/// This attaches a menu, if it's main, to the menubar +// and in all cases ends the menu +void MenuItemVisitor::EndMenu() +{ + if ( mSubMenuList.empty() ) + EndMainMenu(); + else + EndSubMenu(); +} + +void MenuItemVisitor::BeginMainMenu(const TranslatableString & tName) +{ + uCurrentMenu = std::make_unique(); + mCurrentMenu = uCurrentMenu.get(); + mCurrentMenuName = tName; +} + +/// This attaches a menu to the menubar and ends the menu +void MenuItemVisitor::EndMainMenu() +{ + // Add the menu to the menubar after all menu items have been + // added to the menu to allow OSX to rearrange special menu + // items like Preferences, About, and Quit. + wxASSERT(uCurrentMenu); + CurrentMenuBar()->Append( + uCurrentMenu.release(), mCurrentMenuName.Translation()); + mCurrentMenu = nullptr; + mCurrentMenuName = CommandManager::COMMAND; +} + +/// This starts a new submenu, and names it according to +/// the function's argument. +void MenuItemVisitor::BeginSubMenu(const TranslatableString & tName) +{ + mSubMenuList.emplace_back( tName ); + mbSeparatorAllowed = false; +} + +/// This function is called after the final item of a SUBmenu is added. +/// Submenu items are added just like regular menu items; they just happen +/// after BeginSubMenu() is called but before EndSubMenu() is called. +void MenuItemVisitor::EndSubMenu() +{ + //Save the submenu's information + SubMenuListEntry tmpSubMenu{ std::move( mSubMenuList.back() ) }; + + //Pop off the NEW submenu so CurrentMenu returns the parent of the submenu + mSubMenuList.pop_back(); + + //Add the submenu to the current menu + auto name = tmpSubMenu.name.Translation(); + CurrentMenu()->Append(0, name, tmpSubMenu.menu.release(), + name /* help string */ ); + mbSeparatorAllowed = true; +} + +void MenuItemVisitor::BeginOccultCommands() +{ + // To do: perhaps allow occult item switching at lower levels of the + // menu tree. + wxASSERT( !CurrentMenu() ); + + // Make a temporary menu bar collecting items added after. + // This bar will be discarded but other side effects on the command + // manager persist. + mTempMenuBar = AddMenuBar(wxT("ext-menu")); +} + +void MenuItemVisitor::EndOccultCommands() +{ + auto iter = mMenuBarList.end(); + if ( iter != mMenuBarList.begin() ) + mMenuBarList.erase( --iter ); + else + wxASSERT( false ); + mTempMenuBar.reset(); +} + } void MenuCreator::CreateMenusAndCommands() diff --git a/src/commands/CommandManager.cpp b/src/commands/CommandManager.cpp index 101234fd54bb..6c575b82770d 100644 --- a/src/commands/CommandManager.cpp +++ b/src/commands/CommandManager.cpp @@ -45,31 +45,11 @@ CommandManager. It holds the callback for one command. *//****************************************************************//** -\class MenuBarListEntry -\brief MenuBarListEntry is a structure used by CommandManager. - -*//****************************************************************//** - -\class SubMenuListEntry -\brief SubMenuListEntry is a structure used by CommandManager. - -*//****************************************************************//** - \class CommandListEntry \brief CommandListEntry is a structure used by CommandManager. *//****************************************************************//** -\class MenuBarList -\brief List of MenuBarListEntry. - -*//****************************************************************//** - -\class SubMenuList -\brief List of SubMenuListEntry. - -*//****************************************************************//** - \class CommandList \brief List of CommandListEntry. @@ -108,25 +88,6 @@ const TranslatableString CommandManager::COMMAND = XO("Command"); CommandManager::CommandListEntry::~CommandListEntry() = default; -struct MenuBarListEntry -{ - MenuBarListEntry(const wxString &name_, wxMenuBar *menubar_); - ~MenuBarListEntry(); - - wxString name; - wxWeakRef menubar; // This structure does not assume memory ownership! -}; - -struct SubMenuListEntry -{ - SubMenuListEntry( const TranslatableString &name_ ); - SubMenuListEntry( SubMenuListEntry&& ) = default; - ~SubMenuListEntry(); - - TranslatableString name; - std::unique_ptr menu; -}; - MenuBarListEntry::MenuBarListEntry(const wxString &name_, wxMenuBar *menubar_) : name(name_), menubar(menubar_) { @@ -316,8 +277,10 @@ void CommandManager::Populator::DoBeginGroup( dynamic_cast(pItem) ) { const auto flag = (*pConditionalGroup)(); - if (!flag) + if (!flag) { + bMakingOccultCommands = true; BeginOccultCommands(); + } // to avoid repeated call of condition predicate in EndGroup(): mFlags.push_back(flag); } @@ -328,6 +291,10 @@ void CommandManager::Populator::DoBeginGroup( wxASSERT( false ); } +void CommandManager::Populator::BeginMenu(const TranslatableString &) +{ +} + void CommandManager::Populator::DoEndGroup( const Registry::GroupItemBase &item, const Path&) { @@ -341,8 +308,10 @@ void CommandManager::Populator::DoEndGroup( dynamic_cast(pItem) ) { const bool flag = mFlags.back(); - if (!flag) + if (!flag) { EndOccultCommands(); + bMakingOccultCommands = false; + } mFlags.pop_back(); } else if (const auto pGroup = dynamic_cast(pItem)) { @@ -351,6 +320,10 @@ void CommandManager::Populator::DoEndGroup( wxASSERT( false ); } +void CommandManager::Populator::EndMenu() +{ +} + void CommandManager::Populator::DoVisit( const Registry::SingleItem &item, const Path &path) { @@ -431,106 +404,6 @@ wxMenuBar * CommandManager::Populator::CurrentMenuBar() const return mMenuBarList.back().menubar; } -/// -/// Typically used to switch back and forth -/// between adding to a hidden menu bar and -/// adding to one that is visible -/// -void CommandManager::Populator::PopMenuBar() -{ - auto iter = mMenuBarList.end(); - if ( iter != mMenuBarList.begin() ) - mMenuBarList.erase( --iter ); - else - wxASSERT( false ); -} - - -/// -/// This starts a NEW menu -/// -wxMenu *CommandManager::Populator::BeginMenu(const TranslatableString & tName) -{ - if ( mCurrentMenu ) - return BeginSubMenu( tName ); - else - return BeginMainMenu( tName ); -} - - -/// -/// This attaches a menu, if it's main, to the menubar -// and in all cases ends the menu -/// -void CommandManager::Populator::EndMenu() -{ - if ( mSubMenuList.empty() ) - EndMainMenu(); - else - EndSubMenu(); -} - - -/// -/// This starts a NEW menu -/// -wxMenu *CommandManager::Populator::BeginMainMenu(const TranslatableString & tName) -{ - uCurrentMenu = std::make_unique(); - mCurrentMenu = uCurrentMenu.get(); - mCurrentMenuName = tName; - return mCurrentMenu; -} - - -/// -/// This attaches a menu to the menubar and ends the menu -/// -void CommandManager::Populator::EndMainMenu() -{ - // Add the menu to the menubar after all menu items have been - // added to the menu to allow OSX to rearrange special menu - // items like Preferences, About, and Quit. - wxASSERT(uCurrentMenu); - CurrentMenuBar()->Append( - uCurrentMenu.release(), mCurrentMenuName.Translation()); - mCurrentMenu = nullptr; - mCurrentMenuName = COMMAND; -} - - -/// -/// This starts a NEW submenu, and names it according to -/// the function's argument. -wxMenu* CommandManager::Populator::BeginSubMenu(const TranslatableString & tName) -{ - mSubMenuList.emplace_back( tName ); - mbSeparatorAllowed = false; - return mSubMenuList.back().menu.get(); -} - - -/// -/// This function is called after the final item of a SUBmenu is added. -/// Submenu items are added just like regular menu items; they just happen -/// after BeginSubMenu() is called but before EndSubMenu() is called. -void CommandManager::Populator::EndSubMenu() -{ - //Save the submenu's information - SubMenuListEntry tmpSubMenu{ std::move( mSubMenuList.back() ) }; - - //Pop off the NEW submenu so CurrentMenu returns the parent of the submenu - mSubMenuList.pop_back(); - - //Add the submenu to the current menu - auto name = tmpSubMenu.name.Translation(); - CurrentMenu()->Append(0, name, tmpSubMenu.menu.release(), - name /* help string */ ); - mbSeparatorAllowed = true; -} - - -/// /// This returns the 'Current' Submenu, which is the one at the /// end of the mSubMenuList (or NULL, if it doesn't exist). wxMenu * CommandManager::Populator::CurrentSubMenu() const @@ -1432,22 +1305,10 @@ void CommandManager::WriteXML(XMLWriter &xmlFile) const void CommandManager::Populator::BeginOccultCommands() { - // To do: perhaps allow occult item switching at lower levels of the - // menu tree. - wxASSERT( !CurrentMenu() ); - - // Make a temporary menu bar collecting items added after. - // This bar will be discarded but other side effects on the command - // manager persist. - mTempMenuBar = AddMenuBar(wxT("ext-menu")); - bMakingOccultCommands = true; } void CommandManager::Populator::EndOccultCommands() { - PopMenuBar(); - bMakingOccultCommands = false; - mTempMenuBar.reset(); } void CommandManager::SetCommandFlags(const CommandID &name, diff --git a/src/commands/CommandManager.h b/src/commands/CommandManager.h index 08a6a9e17f73..1ebba9e845ec 100644 --- a/src/commands/CommandManager.h +++ b/src/commands/CommandManager.h @@ -34,6 +34,8 @@ #include +#include + class wxEvent; class wxMenu; class wxMenuBar; @@ -41,7 +43,25 @@ class wxMenuBar; class BoolSetting; struct MenuBarListEntry; -struct SubMenuListEntry; + +struct SubMenuListEntry +{ + SubMenuListEntry( const TranslatableString &name_ ); + SubMenuListEntry( SubMenuListEntry&& ) = default; + ~SubMenuListEntry(); + + TranslatableString name; + std::unique_ptr menu; +}; + +struct MenuBarListEntry +{ + MenuBarListEntry(const wxString &name_, wxMenuBar *menubar_); + ~MenuBarListEntry(); + + wxString name; + wxWeakRef menubar; // This structure does not assume memory ownership! +}; using MenuBarList = std::vector < MenuBarListEntry >; using SubMenuList = std::vector < SubMenuListEntry >; @@ -58,10 +78,8 @@ class AUDACITY_DLL_API CommandManager /* not final */ , public Observer::Publisher , private PrefsListener { -protected: - static const TranslatableString COMMAND; - public: + static const TranslatableString COMMAND; struct CommandListEntry; struct Factory : DefaultedGlobalHook AddMenuBar(const wxString & sMenu); - wxMenu *BeginMenu(const TranslatableString & tName); - void EndMenu(); private: void AddItemList(const CommandID & name, const ComponentInterfaceSymbol items[], @@ -171,11 +204,6 @@ class AUDACITY_DLL_API CommandManager /* not final */ CommandFunctorPointer callback, CommandFlag flags, const MenuRegistry::Options &options = {}); - void PopMenuBar(); - protected: - void BeginOccultCommands(); - void EndOccultCommands(); - private: CommandListEntry *NewIdentifier(const CommandID & name, const TranslatableString & label, CommandHandlerFinder finder, @@ -189,11 +217,9 @@ class AUDACITY_DLL_API CommandManager /* not final */ CommandHandlerFinder finder, CommandFunctorPointer callback, const MenuRegistry::Options &options = {}); - wxMenu *BeginMainMenu(const TranslatableString & tName); - void EndMainMenu(); - wxMenu* BeginSubMenu(const TranslatableString & tName); - void EndSubMenu(); + protected: wxMenuBar * CurrentMenuBar() const; + private: wxMenuBar * GetMenuBar(const wxString & sMenu) const; wxMenu * CurrentSubMenu() const; protected: @@ -203,18 +229,22 @@ class AUDACITY_DLL_API CommandManager /* not final */ // mMaxList only holds shortcuts that should not be added (by default) // and is sorted. std::vector mMaxListOnly; + protected: MenuBarList mMenuBarList; SubMenuList mSubMenuList; + private: int mCurrentID{ 17000 }; protected: // false at the start of a menu and immediately after a separator. bool mbSeparatorAllowed{ false }; - private: TranslatableString mCurrentMenuName{ COMMAND }; std::unique_ptr uCurrentMenu; wxMenu *mCurrentMenu {}; + private: bool bMakingOccultCommands{ false }; + protected: std::unique_ptr< wxMenuBar > mTempMenuBar; + private: std::vector mFlags; };