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.
*
* Copyright (C) 2013 CERN
* Copyright (C) 2013-2015 CERN
* @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
* modify it under the terms of the GNU General Public License
@ -31,7 +32,9 @@
#include <cassert>
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<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 )
{
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<wxMenuItem*> 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<wxMenuItem*> 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,6 +228,8 @@ void CONTEXT_MENU::onMenuEvent( wxMenuEvent& aEvent )
}
else
{
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__
@ -224,14 +246,6 @@ void CONTEXT_MENU::onMenuEvent( wxMenuEvent& aEvent )
}
}
#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)
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<CONTEXT_MENU*>( 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<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 );
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()
{
}

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
m_viewControls->ForceCursorPosition( true, m_viewControls->GetCursorPosition() );
// Run update handlers
st->contextMenu->UpdateAll();
boost::scoped_ptr<CONTEXT_MENU> menu( new CONTEXT_MENU( *st->contextMenu ) );
GetEditFrame()->PopupMenu( menu.get() );

View File

@ -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 <tomasz.wlostowski@cern.ch>
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* 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<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.
void AppendCustomEventHandler( CUSTOM_MENU_HANDLER aHandler )
typedef boost::function<OPT_TOOL_EVENT(const wxMenuEvent&)> MENU_HANDLER;
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:
// 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<void(CONTEXT_MENU*)> 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<int, const TOOL_ACTION*> m_toolActions;
///> Chain of custom menu event handlers.
std::list<CUSTOM_MENU_HANDLER> m_handlers;
///> List of submenus.
std::list<CONTEXT_MENU*> 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;
};

View File

@ -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.

View File

@ -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;