void TOOL_BASE::updateStartItem( const TOOL_EVENT& aEvent, bool aIgnorePads )
{
    int tl = getView()->GetTopLayer();
    VECTOR2I cp = controls()->GetCursorPosition( !aEvent.Modifier( MD_SHIFT ) );
    VECTOR2I p;

    controls()->ForceCursorPosition( false );
    m_gridHelper->SetUseGrid( !aEvent.Modifier( MD_ALT ) );
    m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );

    bool snapEnabled = true;

    if( aEvent.IsMotion() || aEvent.IsClick() )
    {
        snapEnabled = !aEvent.Modifier( MD_SHIFT );
        p = aEvent.Position();
    }
    else
    {
        p = cp;
    }

    m_startItem = pickSingleItem( p, -1, -1, aIgnorePads );

    if( !snapEnabled && m_startItem && !m_startItem->Layers().Overlaps( tl ) )
        m_startItem = nullptr;

    m_startSnapPoint = snapToItem( snapEnabled, m_startItem, p );

    if( checkSnap( m_startItem ) )
    {
        controls()->ForceCursorPosition( true, m_startSnapPoint );
    }
}
void TOOL_MANAGER::dispatchContextMenu( const TOOL_EVENT& aEvent )
{
    for( TOOL_ID toolId : m_activeTools )
    {
        TOOL_STATE* st = m_toolIdIndex[toolId];

        // the tool requested a context menu. The menu is activated on RMB click (CMENU_BUTTON mode)
        // or immediately (CMENU_NOW) mode. The latter is used for clarification lists.
        if( st->contextMenuTrigger != CMENU_OFF )
        {
            if( st->contextMenuTrigger == CMENU_BUTTON && !aEvent.IsClick( BUT_RIGHT ) )
                break;

            st->pendingWait = true;
            st->waitEvents = TOOL_EVENT( TC_ANY, TA_ANY );

            // Store the menu pointer in case it is changed by the TOOL when handling menu events
            CONTEXT_MENU* m = st->contextMenu;

            if( st->contextMenuTrigger == CMENU_NOW )
                st->contextMenuTrigger = CMENU_OFF;

            // Temporarily store the cursor position, so the tools could execute actions
            // using the point where the user has invoked a context menu
            bool forcedCursor = m_viewControls->IsCursorPositionForced();
            VECTOR2D cursorPos = m_viewControls->GetCursorPosition();
            m_viewControls->ForceCursorPosition( true, m_viewControls->GetCursorPosition() );

            // Run update handlers
            m->UpdateAll();

            boost::scoped_ptr<CONTEXT_MENU> menu( new CONTEXT_MENU( *m ) );
            GetEditFrame()->PopupMenu( menu.get() );

            // If nothing was chosen from the context menu, we must notify the tool as well
            if( menu->GetSelected() < 0 )
            {
                TOOL_EVENT evt( TC_COMMAND, TA_CONTEXT_MENU_CHOICE, -1 );
                evt.SetParameter( m );
                dispatchInternal( evt );
            }

            TOOL_EVENT evt( TC_COMMAND, TA_CONTEXT_MENU_CLOSED );
            evt.SetParameter( m );
            dispatchInternal( evt );

            m_viewControls->ForceCursorPosition( forcedCursor, cursorPos );

            break;
        }
    }
}
void PNS_TOOL_BASE::updateStartItem( TOOL_EVENT& aEvent )
{
    int tl = getView()->GetTopLayer();
    VECTOR2I cp = m_ctls->GetCursorPosition();
    VECTOR2I p;

    PNS_ITEM* startItem = NULL;
    bool snapEnabled = true;

    if( aEvent.IsMotion() || aEvent.IsClick() )
    {
        snapEnabled = !aEvent.Modifier( MD_SHIFT );
        p = aEvent.Position();
    }
    else
    {
        p = cp;
    }

    startItem = pickSingleItem( p );
    m_router->EnableSnapping( snapEnabled );

    if( !snapEnabled && startItem && !startItem->Layers().Overlaps( tl ) )
        startItem = NULL;

    if( startItem && startItem->Net() >= 0 )
    {
        bool dummy;
        VECTOR2I psnap = snapToItem( startItem, p, dummy );

        if( snapEnabled )
        {
            m_startSnapPoint = psnap;
            m_ctls->ForceCursorPosition( true, psnap );
        }
        else
        {
            m_startSnapPoint = cp;
            m_ctls->ForceCursorPosition( false );
        }

        m_startItem = startItem;
    }
    else
    {
        m_startItem = NULL;
        m_startSnapPoint = cp;
        m_ctls->ForceCursorPosition( false );
    }
}
void TOOL_MANAGER::dispatchContextMenu( const TOOL_EVENT& aEvent )
{
    for( TOOL_ID toolId : m_activeTools )
    {
        TOOL_STATE* st = m_toolIdIndex[toolId];

        // the tool requested a context menu. The menu is activated on RMB click (CMENU_BUTTON mode)
        // or immediately (CMENU_NOW) mode. The latter is used for clarification lists.
        if( st->contextMenuTrigger == CMENU_OFF )
            continue;

        if( st->contextMenuTrigger == CMENU_BUTTON && !aEvent.IsClick( BUT_RIGHT ) )
            break;

        st->pendingWait = true;
        st->waitEvents = TOOL_EVENT( TC_ANY, TA_ANY );

        // Store the menu pointer in case it is changed by the TOOL when handling menu events
        CONTEXT_MENU* m = st->contextMenu;

        if( st->contextMenuTrigger == CMENU_NOW )
            st->contextMenuTrigger = CMENU_OFF;

        // Store the cursor position, so the tools could execute actions
        // using the point where the user has invoked a context menu
        m_menuCursor = m_viewControls->GetCursorPosition();

        // Save all tools cursor settings, as they will be overridden
        for( auto idState : m_toolIdIndex )
        {
            TOOL_STATE* s = idState.second;
            const auto& vc = s->vcSettings;

            if( vc.m_forceCursorPosition )
                m_cursorSettings[idState.first] = vc.m_forcedPosition;
            else
                m_cursorSettings[idState.first] = NULLOPT;
        }

        m_viewControls->ForceCursorPosition( true, m_menuCursor );

        // Display a copy of menu
        std::unique_ptr<CONTEXT_MENU> menu( m->Clone() );

        // Run update handlers on the created copy
        menu->UpdateAll();
        m_menuOwner = toolId;
        m_menuActive = true;

        auto frame = dynamic_cast<wxFrame*>( m_editFrame );

        if( frame )
            frame->PopupMenu( menu.get() );

        // Warp the cursor as long as the menu wasn't clicked out of
        if( menu->GetSelected() >= 0 )
            m_viewControls->WarpCursor( m_menuCursor, true, false );
        // Otherwise notify the tool of a cancelled menu
        else
        {
            TOOL_EVENT evt( TC_COMMAND, TA_CONTEXT_MENU_CHOICE, -1 );
            evt.SetParameter( m );
            dispatchInternal( evt );
        }

        // Notify the tools that menu has been closed
        TOOL_EVENT evt( TC_COMMAND, TA_CONTEXT_MENU_CLOSED );
        evt.SetParameter( m );
        dispatchInternal( evt );

        m_menuActive = false;
        m_menuOwner = -1;

        // Restore cursor settings
        for( auto cursorSetting : m_cursorSettings )
        {
            auto it = m_toolIdIndex.find( cursorSetting.first );
            wxASSERT( it != m_toolIdIndex.end() );

            if( it == m_toolIdIndex.end() )
                continue;

            KIGFX::VC_SETTINGS& vc = it->second->vcSettings;
            vc.m_forceCursorPosition = (bool) cursorSetting.second;
            vc.m_forcedPosition = cursorSetting.second ? *cursorSetting.second : VECTOR2D( 0, 0 );
        }

        m_cursorSettings.clear();
        break;
    }
}