Refactored CONTEXT_MENU, added handler for updating.

This commit is contained in:
Maciej Suminski 2015-04-30 10:46:05 +02:00
parent dfb0443b67
commit 06b978b829
5 changed files with 126 additions and 49 deletions

View File

@ -1,8 +1,9 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * 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 <tomasz.wlostowski@cern.ch> * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* @author Maciej Suminski <maciej.suminski@cern.ch>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -31,7 +32,9 @@
#include <cassert> #include <cassert>
CONTEXT_MENU::CONTEXT_MENU() : 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(); 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<CONTEXT_MENU*>::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 ) CONTEXT_MENU& CONTEXT_MENU::operator=( const CONTEXT_MENU& aMenu )
{ {
Clear(); Clear();
copyFrom( aMenu ); copyFrom( aMenu );
setupEvents();
return *this; 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 ) wxMenuItem* CONTEXT_MENU::Add( const wxString& aLabel, int aId, const BITMAP_OPAQUE* aIcon )
{ {
#ifdef DEBUG #ifdef DEBUG
if( FindItem( aId ) != NULL ) if( FindItem( aId ) != NULL )
wxLogWarning( wxT( "Adding more than one menu entry with the same ID may result in" wxLogWarning( wxT( "Adding more than one menu entry with the same ID may result in"
"undefined behaviour" ) ); "undefined behaviour" ) );
#endif #endif
wxMenuItem* item = new wxMenuItem( this, aId, aLabel, wxEmptyString, wxITEM_NORMAL ); wxMenuItem* item = new wxMenuItem( this, aId, aLabel, wxEmptyString, wxITEM_NORMAL );
if( aIcon ) if( aIcon )
@ -139,9 +152,7 @@ std::list<wxMenuItem*> CONTEXT_MENU::Add( CONTEXT_MENU* aMenu, const wxString& a
if( aExpand ) if( aExpand )
{ {
unsigned int i = 0; for( unsigned int i = 0; i < aMenu->GetMenuItemCount(); ++i )
for( i = 0; i < aMenu->GetMenuItemCount(); ++i )
{ {
wxMenuItem* item = aMenu->FindItemByPosition( i ); wxMenuItem* item = aMenu->FindItemByPosition( i );
items.push_back( appendCopy( item ) ); items.push_back( appendCopy( item ) );
@ -162,8 +173,8 @@ std::list<wxMenuItem*> CONTEXT_MENU::Add( CONTEXT_MENU* aMenu, const wxString& a
} }
} }
m_toolActions.insert( aMenu->m_toolActions.begin(), aMenu->m_toolActions.end() ); m_submenus.push_back( aMenu );
m_handlers.insert( m_handlers.end(), aMenu->m_handlers.begin(), aMenu->m_handlers.end() ); aMenu->m_parent = this;
return items; return items;
} }
@ -176,13 +187,22 @@ void CONTEXT_MENU::Clear()
GetMenuItems().DeleteContents( true ); GetMenuItems().DeleteContents( true );
GetMenuItems().Clear(); GetMenuItems().Clear();
m_toolActions.clear(); m_toolActions.clear();
m_handlers.clear();
GetMenuItems().DeleteContents( false ); // restore the default so destructor does not go wild GetMenuItems().DeleteContents( false ); // restore the default so destructor does not go wild
m_submenus.clear();
m_parent = NULL;
assert( GetMenuItemCount() == 0 ); assert( GetMenuItemCount() == 0 );
} }
void CONTEXT_MENU::UpdateAll()
{
m_update_handler();
runOnSubmenus( boost::bind( &CONTEXT_MENU::UpdateAll, _1 ) );
}
void CONTEXT_MENU::onMenuEvent( wxMenuEvent& aEvent ) void CONTEXT_MENU::onMenuEvent( wxMenuEvent& aEvent )
{ {
OPT_TOOL_EVENT evt; OPT_TOOL_EVENT evt;
@ -208,6 +228,8 @@ void CONTEXT_MENU::onMenuEvent( wxMenuEvent& aEvent )
} }
else else
{ {
runEventHandlers( aEvent, evt );
// Under Linux, every submenu can have a separate event handler, under // Under Linux, every submenu can have a separate event handler, under
// Windows all submenus are handled by the main menu. // Windows all submenus are handled by the main menu.
#ifdef __WINDOWS__ #ifdef __WINDOWS__
@ -224,14 +246,6 @@ void CONTEXT_MENU::onMenuEvent( wxMenuEvent& aEvent )
} }
} }
#endif #endif
for( std::list<CUSTOM_MENU_HANDLER>::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) // Handling non-action menu entries (e.g. items in clarification list)
if( !evt ) if( !evt )
@ -251,16 +265,22 @@ void CONTEXT_MENU::setTool( TOOL_INTERACTIVE* aTool )
{ {
m_tool = aTool; m_tool = aTool;
for( unsigned i = 0; i < GetMenuItemCount(); ++i ) runOnSubmenus( boost::bind( &CONTEXT_MENU::setTool, _1, aTool ) );
{ }
wxMenuItem* item = FindItemByPosition( i );
if( item->IsSubMenu() )
{ void CONTEXT_MENU::runEventHandlers( const wxMenuEvent& aMenuEvent, OPT_TOOL_EVENT& aToolEvent )
CONTEXT_MENU* menu = static_cast<CONTEXT_MENU*>( item->GetSubMenu() ); {
menu->setTool( aTool ); aToolEvent = m_menu_handler( aMenuEvent );
}
} if( !aToolEvent )
runOnSubmenus( boost::bind( &CONTEXT_MENU::runEventHandlers, _1, aMenuEvent, aToolEvent ) );
}
void CONTEXT_MENU::runOnSubmenus( boost::function<void(CONTEXT_MENU*)> 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 ); newItem->SetSubMenu( menu );
Append( newItem ); Append( newItem );
m_toolActions.insert( menu->m_toolActions.begin(), menu->m_toolActions.end() ); m_submenus.push_back( menu );
m_handlers.insert( m_handlers.end(), menu->m_handlers.begin(), menu->m_handlers.end() ); menu->m_parent = this;
} }
else else
{ {
@ -308,7 +328,9 @@ void CONTEXT_MENU::copyFrom( const CONTEXT_MENU& aMenu )
m_selected = -1; // aMenu.m_selected; m_selected = -1; // aMenu.m_selected;
m_tool = aMenu.m_tool; m_tool = aMenu.m_tool;
m_toolActions = aMenu.m_toolActions; 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 // Copy all the menu entries
for( unsigned i = 0; i < aMenu.GetMenuItemCount(); ++i ) for( unsigned i = 0; i < aMenu.GetMenuItemCount(); ++i )
@ -317,3 +339,14 @@ void CONTEXT_MENU::copyFrom( const CONTEXT_MENU& aMenu )
appendCopy( item ); appendCopy( item );
} }
} }
OPT_TOOL_EVENT CONTEXT_MENU::menuHandlerStub( const wxMenuEvent& )
{
return OPT_TOOL_EVENT();
}
void CONTEXT_MENU::updateHandlerStub()
{
}

View File

@ -596,6 +596,9 @@ void TOOL_MANAGER::dispatchContextMenu( const TOOL_EVENT& aEvent )
// using the point where the user has invoked a context menu // using the point where the user has invoked a context menu
m_viewControls->ForceCursorPosition( true, m_viewControls->GetCursorPosition() ); m_viewControls->ForceCursorPosition( true, m_viewControls->GetCursorPosition() );
// Run update handlers
st->contextMenu->UpdateAll();
boost::scoped_ptr<CONTEXT_MENU> menu( new CONTEXT_MENU( *st->contextMenu ) ); boost::scoped_ptr<CONTEXT_MENU> menu( new CONTEXT_MENU( *st->contextMenu ) );
GetEditFrame()->PopupMenu( menu.get() ); GetEditFrame()->PopupMenu( menu.get() );

View File

@ -1,8 +1,9 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * 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 <tomasz.wlostowski@cern.ch> * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* @author Maciej Suminski <maciej.suminski@cern.ch>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -47,9 +48,9 @@ public:
///> Copy constructor ///> Copy constructor
CONTEXT_MENU( const CONTEXT_MENU& aMenu ); 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() * Function SetTitle()
@ -64,7 +65,7 @@ public:
* Assigns an icon for the entry. * Assigns an icon for the entry.
* @param aIcon is the icon to be assigned. NULL is used to remove icon. * @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; m_icon = aIcon;
} }
@ -111,21 +112,43 @@ public:
* menu was dismissed. * menu was dismissed.
* @return The position of selected item in the context menu. * @return The position of selected item in the context menu.
*/ */
int GetSelected() const inline int GetSelected() const
{ {
return m_selected; return m_selected;
} }
///> Function type to handle menu events in a custom way. /**
typedef boost::function<OPT_TOOL_EVENT(const wxMenuEvent&)> 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. typedef boost::function<OPT_TOOL_EVENT(const wxMenuEvent&)> MENU_HANDLER;
void AppendCustomEventHandler( CUSTOM_MENU_HANDLER aHandler ) typedef boost::function<void()> 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: private:
// Empty stubs used by the default constructor
static OPT_TOOL_EVENT menuHandlerStub(const wxMenuEvent& );
static void updateHandlerStub();
/** /**
* Function appendCopy * Function appendCopy
* Appends a copy of wxMenuItem. * Appends a copy of wxMenuItem.
@ -138,7 +161,7 @@ private:
///> Initializes handlers for events. ///> Initializes handlers for events.
void setupEvents(); void setupEvents();
///> Event handler. ///> The default menu event handler.
void onMenuEvent( wxMenuEvent& aEvent ); void onMenuEvent( wxMenuEvent& aEvent );
/** /**
@ -148,6 +171,13 @@ private:
*/ */
void setTool( TOOL_INTERACTIVE* aTool ); 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<void(CONTEXT_MENU*)> aFunction );
///> Flag indicating that the menu title was set up. ///> Flag indicating that the menu title was set up.
bool m_titleSet; bool m_titleSet;
@ -163,12 +193,21 @@ private:
///> Associates tool actions with menu item IDs. Non-owning. ///> Associates tool actions with menu item IDs. Non-owning.
std::map<int, const TOOL_ACTION*> m_toolActions; std::map<int, const TOOL_ACTION*> m_toolActions;
///> Chain of custom menu event handlers. ///> List of submenus.
std::list<CUSTOM_MENU_HANDLER> m_handlers; std::list<CONTEXT_MENU*> m_submenus;
///> Parent CONTEXT_MENU.
CONTEXT_MENU* m_parent;
///> Optional icon ///> Optional icon
const BITMAP_OPAQUE* m_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; friend class TOOL_INTERACTIVE;
}; };

View File

@ -96,8 +96,7 @@ public:
{ {
m_board = NULL; m_board = NULL;
SetIcon( width_track_via_xpm ); SetIcon( width_track_via_xpm );
AppendCustomEventHandler( boost::bind( &CONTEXT_TRACK_WIDTH_MENU::handleCustomEvent, SetMenuHandler( boost::bind( &CONTEXT_TRACK_WIDTH_MENU::EventHandler, this, _1 ) );
this, _1 ) );
} }
void SetBoard( BOARD* aBoard ) void SetBoard( BOARD* aBoard )
@ -153,8 +152,7 @@ public:
} }
} }
protected: OPT_TOOL_EVENT EventHandler( const wxMenuEvent& aEvent )
OPT_TOOL_EVENT handleCustomEvent( const wxMenuEvent& aEvent )
{ {
#if ID_POPUP_PCB_SELECT_VIASIZE1 < ID_POPUP_PCB_SELECT_WIDTH1 #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. #error You have changed event ids order, it breaks code. Check the source code for more details.

View File

@ -1282,6 +1282,10 @@ bool SELECTION_TOOL::SanitizeSelection()
void SELECTION_TOOL::generateMenu() 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 // Create a copy of the master context menu
m_menuCopy = m_menu; m_menuCopy = m_menu;