Commit 06b978b8 authored by Maciej Suminski's avatar Maciej Suminski
Browse files

Refactored CONTEXT_MENU, added handler for updating.

parent dfb0443b
Loading
Loading
Loading
Loading
+65 −32
Original line number Original line Diff line number Diff line
/*
/*
 * 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() );
    aToolEvent = m_menu_handler( aMenuEvent );
            menu->setTool( aTool );

        }
    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()
{
}
+3 −0
Original line number Original line Diff line number Diff line
@@ -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() );


+52 −13
Original line number Original line Diff line number Diff line
/*
/*
 * 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();

    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_menu_handler = aMenuHandler;
    }


    ///> Adds an event handler to the custom menu event handlers chain.
    /**
    void AppendCustomEventHandler( CUSTOM_MENU_HANDLER aHandler )
     * Function SetUpdateHandler()
     * Sets the update handler to a different function.
     */
    inline void SetUpdateHandler( UPDATE_HANDLER aUpdateHandler )
    {
    {
        m_handlers.push_back( aHandler );
        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;
};
};


+2 −4
Original line number Original line Diff line number Diff line
@@ -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.
+4 −0
Original line number Original line Diff line number Diff line
@@ -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;