diff --git a/common/tool/context_menu.cpp b/common/tool/context_menu.cpp index 08bada78e7..105450e8a2 100644 --- a/common/tool/context_menu.cpp +++ b/common/tool/context_menu.cpp @@ -1,8 +1,9 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2013 CERN + * Copyright (C) 2013-2015 CERN * @author Tomasz Wlostowski + * @author Maciej Suminski * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -31,7 +32,9 @@ #include CONTEXT_MENU::CONTEXT_MENU() : - m_titleSet( false ), m_selected( -1 ), m_tool( NULL ), m_icon( NULL ) + m_titleSet( false ), m_selected( -1 ), m_tool( NULL ), m_parent( NULL ), m_icon( NULL ), + m_menu_handler( CONTEXT_MENU::menuHandlerStub ), + m_update_handler( CONTEXT_MENU::updateHandlerStub ) { setupEvents(); } @@ -44,12 +47,22 @@ CONTEXT_MENU::CONTEXT_MENU( const CONTEXT_MENU& aMenu ) } +CONTEXT_MENU::~CONTEXT_MENU() +{ + // Set parent to NULL to prevent submenus from unregistering from a notexisting object + for( std::list::iterator it = m_submenus.begin(); it != m_submenus.end(); ++it ) + (*it)->m_parent = NULL; + + if( m_parent ) + m_parent->m_submenus.remove( this ); +} + + CONTEXT_MENU& CONTEXT_MENU::operator=( const CONTEXT_MENU& aMenu ) { Clear(); copyFrom( aMenu ); - setupEvents(); return *this; } @@ -84,11 +97,11 @@ void CONTEXT_MENU::SetTitle( const wxString& aTitle ) wxMenuItem* CONTEXT_MENU::Add( const wxString& aLabel, int aId, const BITMAP_OPAQUE* aIcon ) { #ifdef DEBUG - if( FindItem( aId ) != NULL ) wxLogWarning( wxT( "Adding more than one menu entry with the same ID may result in" "undefined behaviour" ) ); #endif + wxMenuItem* item = new wxMenuItem( this, aId, aLabel, wxEmptyString, wxITEM_NORMAL ); if( aIcon ) @@ -139,9 +152,7 @@ std::list CONTEXT_MENU::Add( CONTEXT_MENU* aMenu, const wxString& a if( aExpand ) { - unsigned int i = 0; - - for( i = 0; i < aMenu->GetMenuItemCount(); ++i ) + for( unsigned int i = 0; i < aMenu->GetMenuItemCount(); ++i ) { wxMenuItem* item = aMenu->FindItemByPosition( i ); items.push_back( appendCopy( item ) ); @@ -162,8 +173,8 @@ std::list CONTEXT_MENU::Add( CONTEXT_MENU* aMenu, const wxString& a } } - m_toolActions.insert( aMenu->m_toolActions.begin(), aMenu->m_toolActions.end() ); - m_handlers.insert( m_handlers.end(), aMenu->m_handlers.begin(), aMenu->m_handlers.end() ); + m_submenus.push_back( aMenu ); + aMenu->m_parent = this; return items; } @@ -176,13 +187,22 @@ void CONTEXT_MENU::Clear() GetMenuItems().DeleteContents( true ); GetMenuItems().Clear(); m_toolActions.clear(); - m_handlers.clear(); GetMenuItems().DeleteContents( false ); // restore the default so destructor does not go wild + m_submenus.clear(); + m_parent = NULL; assert( GetMenuItemCount() == 0 ); } +void CONTEXT_MENU::UpdateAll() +{ + m_update_handler(); + + runOnSubmenus( boost::bind( &CONTEXT_MENU::UpdateAll, _1 ) ); +} + + void CONTEXT_MENU::onMenuEvent( wxMenuEvent& aEvent ) { OPT_TOOL_EVENT evt; @@ -208,8 +228,10 @@ void CONTEXT_MENU::onMenuEvent( wxMenuEvent& aEvent ) } else { - // Under Linux, every submenu can have a separate event handler, under - // Windows all submenus are handled by the main menu. + runEventHandlers( aEvent, evt ); + + // Under Linux, every submenu can have a separate event handler, under + // Windows all submenus are handled by the main menu. #ifdef __WINDOWS__ if( !evt ) { @@ -224,14 +246,6 @@ void CONTEXT_MENU::onMenuEvent( wxMenuEvent& aEvent ) } } #endif - for( std::list::iterator it = m_handlers.begin(); - it != m_handlers.end(); ++it ) - { - evt = (*it)( aEvent ); - - if( evt ) - break; - } // Handling non-action menu entries (e.g. items in clarification list) if( !evt ) @@ -251,16 +265,22 @@ void CONTEXT_MENU::setTool( TOOL_INTERACTIVE* aTool ) { m_tool = aTool; - for( unsigned i = 0; i < GetMenuItemCount(); ++i ) - { - wxMenuItem* item = FindItemByPosition( i ); + runOnSubmenus( boost::bind( &CONTEXT_MENU::setTool, _1, aTool ) ); +} - if( item->IsSubMenu() ) - { - CONTEXT_MENU* menu = static_cast( item->GetSubMenu() ); - menu->setTool( aTool ); - } - } + +void CONTEXT_MENU::runEventHandlers( const wxMenuEvent& aMenuEvent, OPT_TOOL_EVENT& aToolEvent ) +{ + aToolEvent = m_menu_handler( aMenuEvent ); + + if( !aToolEvent ) + runOnSubmenus( boost::bind( &CONTEXT_MENU::runEventHandlers, _1, aMenuEvent, aToolEvent ) ); +} + + +void CONTEXT_MENU::runOnSubmenus( boost::function aFunction ) +{ + std::for_each( m_submenus.begin(), m_submenus.end(), aFunction ); } @@ -283,8 +303,8 @@ wxMenuItem* CONTEXT_MENU::appendCopy( const wxMenuItem* aSource ) newItem->SetSubMenu( menu ); Append( newItem ); - m_toolActions.insert( menu->m_toolActions.begin(), menu->m_toolActions.end() ); - m_handlers.insert( m_handlers.end(), menu->m_handlers.begin(), menu->m_handlers.end() ); + m_submenus.push_back( menu ); + menu->m_parent = this; } else { @@ -308,7 +328,9 @@ void CONTEXT_MENU::copyFrom( const CONTEXT_MENU& aMenu ) m_selected = -1; // aMenu.m_selected; m_tool = aMenu.m_tool; m_toolActions = aMenu.m_toolActions; - m_handlers = aMenu.m_handlers; + m_parent = NULL; // aMenu.m_parent; + m_menu_handler = aMenu.m_menu_handler; + m_update_handler = aMenu.m_update_handler; // Copy all the menu entries for( unsigned i = 0; i < aMenu.GetMenuItemCount(); ++i ) @@ -317,3 +339,14 @@ void CONTEXT_MENU::copyFrom( const CONTEXT_MENU& aMenu ) appendCopy( item ); } } + + +OPT_TOOL_EVENT CONTEXT_MENU::menuHandlerStub( const wxMenuEvent& ) +{ + return OPT_TOOL_EVENT(); +} + + +void CONTEXT_MENU::updateHandlerStub() +{ +} diff --git a/common/tool/tool_manager.cpp b/common/tool/tool_manager.cpp index cabf2d5302..68b2ad8070 100644 --- a/common/tool/tool_manager.cpp +++ b/common/tool/tool_manager.cpp @@ -596,6 +596,9 @@ void TOOL_MANAGER::dispatchContextMenu( const TOOL_EVENT& aEvent ) // using the point where the user has invoked a context menu m_viewControls->ForceCursorPosition( true, m_viewControls->GetCursorPosition() ); + // Run update handlers + st->contextMenu->UpdateAll(); + boost::scoped_ptr menu( new CONTEXT_MENU( *st->contextMenu ) ); GetEditFrame()->PopupMenu( menu.get() ); diff --git a/include/tool/context_menu.h b/include/tool/context_menu.h index 5acf177cb0..23a643004d 100644 --- a/include/tool/context_menu.h +++ b/include/tool/context_menu.h @@ -1,8 +1,9 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2013 CERN + * Copyright (C) 2013-2015 CERN * @author Tomasz Wlostowski + * @author Maciej Suminski * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -47,9 +48,9 @@ public: ///> Copy constructor CONTEXT_MENU( const CONTEXT_MENU& aMenu ); - CONTEXT_MENU& operator=( const CONTEXT_MENU& aMenu ); + virtual ~CONTEXT_MENU(); - virtual ~CONTEXT_MENU() {} + CONTEXT_MENU& operator=( const CONTEXT_MENU& aMenu ); /** * Function SetTitle() @@ -64,7 +65,7 @@ public: * Assigns an icon for the entry. * @param aIcon is the icon to be assigned. NULL is used to remove icon. */ - void SetIcon( const BITMAP_OPAQUE* aIcon ) + inline void SetIcon( const BITMAP_OPAQUE* aIcon ) { m_icon = aIcon; } @@ -111,21 +112,43 @@ public: * menu was dismissed. * @return The position of selected item in the context menu. */ - int GetSelected() const + inline int GetSelected() const { return m_selected; } - ///> Function type to handle menu events in a custom way. - typedef boost::function CUSTOM_MENU_HANDLER; + /** + * Function UpdateAll() + * Runs update handlers for the menu and its submenus. + */ + void UpdateAll(); - ///> Adds an event handler to the custom menu event handlers chain. - void AppendCustomEventHandler( CUSTOM_MENU_HANDLER aHandler ) + typedef boost::function MENU_HANDLER; + typedef boost::function UPDATE_HANDLER; + + /** + * Function SetMenuHandler() + * Sets the menu event handler to another function. + */ + inline void SetMenuHandler( MENU_HANDLER aMenuHandler ) { - m_handlers.push_back( aHandler ); + m_menu_handler = aMenuHandler; + } + + /** + * Function SetUpdateHandler() + * Sets the update handler to a different function. + */ + inline void SetUpdateHandler( UPDATE_HANDLER aUpdateHandler ) + { + m_update_handler = aUpdateHandler; } private: + // Empty stubs used by the default constructor + static OPT_TOOL_EVENT menuHandlerStub(const wxMenuEvent& ); + static void updateHandlerStub(); + /** * Function appendCopy * Appends a copy of wxMenuItem. @@ -138,7 +161,7 @@ private: ///> Initializes handlers for events. void setupEvents(); - ///> Event handler. + ///> The default menu event handler. void onMenuEvent( wxMenuEvent& aEvent ); /** @@ -148,6 +171,13 @@ private: */ void setTool( TOOL_INTERACTIVE* aTool ); + ///> Traverses the submenus tree looking for a submenu capable of handling a particular menu + ///> event. In case it is handled, it is returned the aToolEvent parameter. + void runEventHandlers( const wxMenuEvent& aMenuEvent, OPT_TOOL_EVENT& aToolEvent ); + + ///> Runs a function on the menu and all its submenus. + void runOnSubmenus( boost::function aFunction ); + ///> Flag indicating that the menu title was set up. bool m_titleSet; @@ -163,12 +193,21 @@ private: ///> Associates tool actions with menu item IDs. Non-owning. std::map m_toolActions; - ///> Chain of custom menu event handlers. - std::list m_handlers; + ///> List of submenus. + std::list m_submenus; + + ///> Parent CONTEXT_MENU. + CONTEXT_MENU* m_parent; ///> Optional icon const BITMAP_OPAQUE* m_icon; + ///> Optional callback to translate wxMenuEvents to TOOL_EVENTs. + MENU_HANDLER m_menu_handler; + + ///> Optional callback to update the menu state before it is displayed. + UPDATE_HANDLER m_update_handler; + friend class TOOL_INTERACTIVE; }; diff --git a/pcbnew/router/router_tool.cpp b/pcbnew/router/router_tool.cpp index 644e6efe5a..f6e1708037 100644 --- a/pcbnew/router/router_tool.cpp +++ b/pcbnew/router/router_tool.cpp @@ -96,8 +96,7 @@ public: { m_board = NULL; SetIcon( width_track_via_xpm ); - AppendCustomEventHandler( boost::bind( &CONTEXT_TRACK_WIDTH_MENU::handleCustomEvent, - this, _1 ) ); + SetMenuHandler( boost::bind( &CONTEXT_TRACK_WIDTH_MENU::EventHandler, this, _1 ) ); } void SetBoard( BOARD* aBoard ) @@ -153,8 +152,7 @@ public: } } -protected: - OPT_TOOL_EVENT handleCustomEvent( const wxMenuEvent& aEvent ) + OPT_TOOL_EVENT EventHandler( const wxMenuEvent& aEvent ) { #if ID_POPUP_PCB_SELECT_VIASIZE1 < ID_POPUP_PCB_SELECT_WIDTH1 #error You have changed event ids order, it breaks code. Check the source code for more details. diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp index f60bd845fa..2030407283 100644 --- a/pcbnew/tools/selection_tool.cpp +++ b/pcbnew/tools/selection_tool.cpp @@ -1282,6 +1282,10 @@ bool SELECTION_TOOL::SanitizeSelection() void SELECTION_TOOL::generateMenu() { + // Menu has to be updated before its copy is created. Copying does not preserve subtypes of the + // stored menus, so updating may not work correctly. + m_menu.UpdateAll(); + // Create a copy of the master context menu m_menuCopy = m_menu;