void CONTEXT_MENU::CMEventHandler::onEvent( wxEvent& aEvent ) { TOOL_EVENT evt; wxEventType type = aEvent.GetEventType(); // When the currently chosen item in the menu is changed, an update event is issued. // For example, the selection tool can use this to dynamically highlight the current item // from selection clarification popup. if( type == wxEVT_MENU_HIGHLIGHT ) evt = TOOL_EVENT( TC_COMMAND, TA_CONTEXT_MENU_UPDATE, aEvent.GetId() ); // One of menu entries was selected.. else if( type == wxEVT_COMMAND_MENU_SELECTED ) { // Store the selected position m_menu->m_selected = aEvent.GetId(); // Check if there is a TOOL_ACTION for the given ID if( m_menu->m_toolActions.count( aEvent.GetId() ) == 1 ) { evt = m_menu->m_toolActions[aEvent.GetId()]->MakeEvent(); } else { // Handling non-action menu entries (e.g. items in clarification list) evt = TOOL_EVENT( TC_COMMAND, TA_CONTEXT_MENU_CHOICE, aEvent.GetId() ); } } // forward the action/update event to the TOOL_MANAGER if( m_menu->m_tool ) m_menu->m_tool->GetManager()->ProcessEvent( evt ); }
void CONTEXT_MENU::onMenuEvent( wxMenuEvent& aEvent ) { OPT_TOOL_EVENT evt; wxEventType type = aEvent.GetEventType(); // When the currently chosen item in the menu is changed, an update event is issued. // For example, the selection tool can use this to dynamically highlight the current item // from selection clarification popup. if( type == wxEVT_MENU_HIGHLIGHT ) evt = TOOL_EVENT( TC_COMMAND, TA_CONTEXT_MENU_UPDATE, aEvent.GetId() ); // One of menu entries was selected.. else if( type == wxEVT_COMMAND_MENU_SELECTED ) { // Store the selected position m_selected = aEvent.GetId(); // Check if there is a TOOL_ACTION for the given ID if( m_toolActions.count( aEvent.GetId() ) == 1 ) { evt = m_toolActions[aEvent.GetId()]->MakeEvent(); } 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__ if( !evt ) { // Try to find the submenu which holds the selected item wxMenu* menu = NULL; FindItem( m_selected, &menu ); if( menu && menu != this ) { menu->ProcessEvent( aEvent ); return; } } #endif // Handling non-action menu entries (e.g. items in clarification list) if( !evt ) evt = TOOL_EVENT( TC_COMMAND, TA_CONTEXT_MENU_CHOICE, aEvent.GetId() ); } } assert( m_tool ); // without tool & tool manager we cannot handle events // forward the action/update event to the TOOL_MANAGER if( evt && m_tool ) m_tool->GetManager()->ProcessEvent( *evt ); }
bool SELECTION_TOOL::selectCursor( const VECTOR2I& aWhere, bool aOnDrag ) { BOARD_ITEM* item; GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide(); GENERAL_COLLECTOR collector; if( m_editModules ) collector.Collect( getModel<BOARD>(), GENERAL_COLLECTOR::ModuleItems, wxPoint( aWhere.x, aWhere.y ), guide ); else collector.Collect( getModel<BOARD>(), GENERAL_COLLECTOR::AllBoardItems, wxPoint( aWhere.x, aWhere.y ), guide ); bool anyCollected = collector.GetCount() != 0; // Remove unselectable items for( int i = collector.GetCount() - 1; i >= 0; --i ) { if( !selectable( collector[i] ) || ( aOnDrag && collector[i]->IsLocked() ) ) collector.Remove( i ); } switch( collector.GetCount() ) { case 0: if( !m_additive && anyCollected ) clearSelection(); return false; case 1: toggleSelection( collector[0] ); return true; default: // Apply some ugly heuristics to avoid disambiguation menus whenever possible guessSelectionCandidates( collector ); // Let's see if there is still disambiguation in selection.. if( collector.GetCount() == 1 ) { toggleSelection( collector[0] ); return true; } else if( collector.GetCount() > 1 ) { if( aOnDrag ) Wait( TOOL_EVENT( TC_ANY, TA_MOUSE_UP, BUT_LEFT ) ); item = disambiguationMenu( &collector ); if( item ) { toggleSelection( item ); return true; } } break; } return false; }
void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent ) { bool motion = false, buttonEvents = false; boost::optional<TOOL_EVENT> evt; int type = aEvent.GetEventType(); // Mouse handling if( type == wxEVT_MOTION || type == wxEVT_MOUSEWHEEL || type == wxEVT_LEFT_DOWN || type == wxEVT_LEFT_UP || type == wxEVT_MIDDLE_DOWN || type == wxEVT_MIDDLE_UP || type == wxEVT_RIGHT_DOWN || type == wxEVT_RIGHT_UP || type == wxEVT_LEFT_DCLICK || type == wxEVT_MIDDLE_DCLICK || type == wxEVT_RIGHT_DCLICK || // Event issued whem mouse retains position in screen coordinates, // but changes in world coordinates (e.g. autopanning) type == KIGFX::WX_VIEW_CONTROLS::EVT_REFRESH_MOUSE ) { wxMouseEvent* me = static_cast<wxMouseEvent*>( &aEvent ); int mods = decodeModifiers<wxMouseEvent>( me ); VECTOR2D screenPos = m_toolMgr->GetViewControls()->GetMousePosition(); VECTOR2D pos = getView()->ToWorld( screenPos ); if( pos != m_lastMousePos ) { motion = true; m_lastMousePos = pos; m_editFrame->UpdateStatusBar(); } for( unsigned int i = 0; i < m_buttons.size(); i++ ) buttonEvents |= handleMouseButton( aEvent, i, motion ); if( !buttonEvents && motion ) { evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_MOTION, mods ); evt->SetMousePosition( pos ); } } // Keyboard handling else if( type == wxEVT_CHAR ) { wxKeyEvent* ke = static_cast<wxKeyEvent*>( &aEvent ); int key = ke->GetKeyCode(); int mods = decodeModifiers<wxKeyEvent>( ke ); if( mods & MD_CTRL ) { #if !wxCHECK_VERSION( 2, 9, 0 ) // I really look forward to the day when we will use only one version of wxWidgets.. const int WXK_CONTROL_A = 1; const int WXK_CONTROL_Z = 26; #endif // wxWidgets have a quirk related to Ctrl+letter hot keys handled by CHAR_EVT // http://docs.wxwidgets.org/trunk/classwx_key_event.html: // "char events for ASCII letters in this case carry codes corresponding to the ASCII // value of Ctrl-Latter, i.e. 1 for Ctrl-A, 2 for Ctrl-B and so on until 26 for Ctrl-Z." if( key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z ) key += 'A' - 1; } if( key == WXK_ESCAPE ) // ESC is the special key for cancelling tools evt = TOOL_EVENT( TC_COMMAND, TA_CANCEL_TOOL ); else evt = TOOL_EVENT( TC_KEYBOARD, TA_KEY_PRESSED, key | mods ); } if( evt ) m_toolMgr->ProcessEvent( *evt ); // pass the event to the GUI, it might still be interested in it aEvent.Skip(); }
bool TOOL_DISPATCHER::handleMouseButton( wxEvent& aEvent, int aIndex, bool aMotion ) { BUTTON_STATE* st = m_buttons[aIndex]; wxEventType type = aEvent.GetEventType(); boost::optional<TOOL_EVENT> evt; bool isClick = false; bool up = type == st->upEvent; bool down = type == st->downEvent; bool dblClick = type == st->dblClickEvent; int mods = decodeModifiers<wxMouseEvent>( static_cast<wxMouseEvent*>( &aEvent ) ); int args = st->button | mods; if( down ) // Handle mouse button press { st->downTimestamp = wxGetLocalTimeMillis(); st->dragOrigin = m_lastMousePos; st->downPosition = m_lastMousePos; st->dragMaxDelta = 0; st->pressed = true; evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DOWN, args ); } else if( up ) // Handle mouse button release { st->pressed = false; if( st->dragging ) { wxLongLong t = wxGetLocalTimeMillis(); // Determine if it was just a single click or beginning of dragging if( t - st->downTimestamp < DragTimeThreshold && st->dragMaxDelta < DragDistanceThreshold ) isClick = true; else evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_UP, args ); } else isClick = true; if( isClick ) evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_CLICK, args ); st->dragging = false; } else if( dblClick ) { evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DBLCLICK, args ); } if( st->pressed && aMotion ) { st->dragging = true; double dragPixelDistance = getView()->ToScreen( m_lastMousePos - st->dragOrigin, false ).EuclideanNorm(); st->dragMaxDelta = std::max( st->dragMaxDelta, dragPixelDistance ); wxLongLong t = wxGetLocalTimeMillis(); if( t - st->downTimestamp > DragTimeThreshold || st->dragMaxDelta > DragDistanceThreshold ) { evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DRAG, args ); evt->SetMouseDragOrigin( st->dragOrigin ); evt->SetMouseDelta( m_lastMousePos - st->dragOrigin ); } } if( evt ) { evt->SetMousePosition( isClick ? st->downPosition : m_lastMousePos ); m_toolMgr->ProcessEvent( *evt ); return true; } return 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; } }
void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent ) { bool motion = false, buttonEvents = false; boost::optional<TOOL_EVENT> evt; int type = aEvent.GetEventType(); // Mouse handling if( type == wxEVT_MOTION || type == wxEVT_MOUSEWHEEL || #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT ) type == wxEVT_MAGNIFY || #endif type == wxEVT_LEFT_DOWN || type == wxEVT_LEFT_UP || type == wxEVT_MIDDLE_DOWN || type == wxEVT_MIDDLE_UP || type == wxEVT_RIGHT_DOWN || type == wxEVT_RIGHT_UP || type == wxEVT_LEFT_DCLICK || type == wxEVT_MIDDLE_DCLICK || type == wxEVT_RIGHT_DCLICK || // Event issued whem mouse retains position in screen coordinates, // but changes in world coordinates (e.g. autopanning) type == KIGFX::WX_VIEW_CONTROLS::EVT_REFRESH_MOUSE ) { wxMouseEvent* me = static_cast<wxMouseEvent*>( &aEvent ); int mods = decodeModifiers( me ); VECTOR2D screenPos = m_toolMgr->GetViewControls()->GetMousePosition(); VECTOR2D pos = getView()->ToWorld( screenPos ); if( pos != m_lastMousePos ) { motion = true; m_lastMousePos = pos; } for( unsigned int i = 0; i < m_buttons.size(); i++ ) buttonEvents |= handleMouseButton( aEvent, i, motion ); if( !buttonEvents && motion ) { evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_MOTION, mods ); evt->SetMousePosition( pos ); } #ifdef __APPLE__ // TODO That's a big ugly workaround, somehow DRAWPANEL_GAL loses focus // after second LMB click and currently I have no means to do better debugging if( type == wxEVT_LEFT_UP ) static_cast<PCB_BASE_FRAME*>( m_toolMgr->GetEditFrame() )->GetGalCanvas()->SetFocus(); #endif /* __APPLE__ */ } // Keyboard handling else if( type == wxEVT_CHAR ) { wxKeyEvent* ke = static_cast<wxKeyEvent*>( &aEvent ); int key = ke->GetKeyCode(); int mods = decodeModifiers( ke ); if( mods & MD_CTRL ) { #if !wxCHECK_VERSION( 2, 9, 0 ) // I really look forward to the day when we will use only one version of wxWidgets.. const int WXK_CONTROL_A = 1; const int WXK_CONTROL_Z = 26; #endif // wxWidgets have a quirk related to Ctrl+letter hot keys handled by CHAR_EVT // http://docs.wxwidgets.org/trunk/classwx_key_event.html: // "char events for ASCII letters in this case carry codes corresponding to the ASCII // value of Ctrl-Latter, i.e. 1 for Ctrl-A, 2 for Ctrl-B and so on until 26 for Ctrl-Z." if( key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z ) key += 'A' - 1; } if( key == WXK_ESCAPE ) // ESC is the special key for cancelling tools evt = TOOL_EVENT( TC_COMMAND, TA_CANCEL_TOOL ); else evt = TOOL_EVENT( TC_KEYBOARD, TA_KEY_PRESSED, key | mods ); } if( evt ) m_toolMgr->ProcessEvent( *evt ); // pass the event to the GUI, it might still be interested in it #ifdef __APPLE__ // On OS X, key events are always meant to be caught. An uncaught key event is assumed // to be a user input error by OS X (as they are pressing keys in a context where nothing // is there to catch the event). This annoyingly makes OS X beep and/or flash the screen // in pcbnew and the footprint editor any time a hotkey is used. The correct procedure is // to NOT pass key events to the GUI under OS X. if( type != wxEVT_CHAR ) aEvent.Skip(); #else aEvent.Skip(); #endif updateUI(); }