KiCad PCB EDA Suite
Loading...
Searching...
No Matches
tool_dispatcher.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2013 CERN
5 * Copyright The KiCad Developers, see CHANGELOG.txt for contributors.
6 * @author Tomasz Wlostowski <[email protected]>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
23
24#include <bit>
25#include <optional>
26
27#include <wx/log.h>
28#include <wx/stc/stc.h>
29#include <wx/settings.h>
30
31#include <core/ignore.h>
32#include <macros.h>
33#include <trace_helpers.h>
34#include <tool/tool_manager.h>
35#include <tool/actions.h>
36#include <tool/action_manager.h>
37#include <tool/action_menu.h>
38#include <view/view.h>
41#include <eda_draw_frame.h>
42#include <core/kicad_algo.h>
43
44#include <kiplatform/app.h>
45#include <kiplatform/ui.h>
46
47
50{
51 BUTTON_STATE( TOOL_MOUSE_BUTTONS aButton, const wxEventType& aDownEvent,
52 const wxEventType& aUpEvent, const wxEventType& aDblClickEvent ) :
53 dragging( false ),
54 pressed( false ),
55 button( aButton ),
56 downEvent( aDownEvent ),
57 upEvent( aUpEvent ),
58 dblClickEvent( aDblClickEvent )
59 {};
60
63
65 bool pressed;
66
69
72
75
78
80 wxEventType downEvent;
81
83 wxEventType upEvent;
84
86 wxEventType dblClickEvent;
87
89 wxLongLong downTimestamp;
90
92 void Reset()
93 {
94 dragging = false;
95 pressed = false;
96 }
97
99 bool GetState() const
100 {
101 wxMouseState mouseState = wxGetMouseState();
102
103 switch( button )
104 {
105 case BUT_LEFT:
106 return mouseState.LeftIsDown();
107
108 case BUT_MIDDLE:
109 return mouseState.MiddleIsDown();
110
111 case BUT_RIGHT:
112 return mouseState.RightIsDown();
113
114 case BUT_AUX1:
115 return mouseState.Aux1IsDown();
116
117 case BUT_AUX2:
118 return mouseState.Aux2IsDown();
119
120 default:
121 wxFAIL_MSG( wxT( "unknown button" ) );
122 return false;
123 }
124 }
125};
126
127
129 m_lastKeyCode( 0 ),
130 m_lastKeyTime( 0 ),
131 m_toolMgr( aToolMgr )
132{
133 m_sysDragMinX = wxSystemSettings::GetMetric( wxSYS_DRAG_X );
134 m_sysDragMinY = wxSystemSettings::GetMetric( wxSYS_DRAG_Y );
135
138
139 m_buttons.push_back( new BUTTON_STATE( BUT_LEFT, wxEVT_LEFT_DOWN,
140 wxEVT_LEFT_UP, wxEVT_LEFT_DCLICK ) );
141 m_buttons.push_back( new BUTTON_STATE( BUT_RIGHT, wxEVT_RIGHT_DOWN,
142 wxEVT_RIGHT_UP, wxEVT_RIGHT_DCLICK ) );
143 m_buttons.push_back( new BUTTON_STATE( BUT_MIDDLE, wxEVT_MIDDLE_DOWN,
144 wxEVT_MIDDLE_UP, wxEVT_MIDDLE_DCLICK ) );
145 m_buttons.push_back( new BUTTON_STATE( BUT_AUX1, wxEVT_AUX1_DOWN,
146 wxEVT_AUX1_UP, wxEVT_AUX1_DCLICK ) );
147 m_buttons.push_back( new BUTTON_STATE( BUT_AUX2, wxEVT_AUX2_DOWN,
148 wxEVT_AUX2_UP, wxEVT_AUX2_DCLICK ) );
149
150 ResetState();
151}
152
153
155{
156 for( BUTTON_STATE* st : m_buttons )
157 delete st;
158}
159
160int TOOL_DISPATCHER::decodeModifiers( const wxKeyboardState* aState )
161{
162 int mods = 0;
163 int wxmods = aState->GetModifiers();
164
165 // Returns the state of key modifiers (Alt, Ctrl and so on). Be carefull:
166 // the flag wxMOD_ALTGR is defined in wxWidgets as wxMOD_CONTROL|wxMOD_ALT
167 // So AltGr key cannot used as modifier key because it is the same as Alt key + Ctrl key.
168#if CAN_USE_ALTGR_KEY
169 if( wxmods & wxMOD_ALTGR )
170 mods |= MD_ALTGR;
171 else
172#endif
173 {
174 if( wxmods & wxMOD_CONTROL )
175 mods |= MD_CTRL;
176
177 if( wxmods & wxMOD_ALT )
178 mods |= MD_ALT;
179 }
180
181 if( wxmods & wxMOD_SHIFT )
182 mods |= MD_SHIFT;
183
184#ifdef wxMOD_META
185 if( wxmods & wxMOD_META )
186 mods |= MD_META;
187#endif
188
189#ifdef wxMOD_WIN
190 if( wxmods & wxMOD_WIN )
191 mods |= MD_SUPER;
192#endif
193
194 return mods;
195}
196
197
199{
200 for( BUTTON_STATE* st : m_buttons )
201 st->Reset();
202
203 m_lastKeyCode = 0;
204 m_lastKeyTime = 0;
205}
206
207
209{
210 return m_toolMgr->GetView();
211}
212
213
214bool TOOL_DISPATCHER::IsPastDragThreshold( const VECTOR2D& aOffset, int aDragMinX, int aDragMinY )
215{
216 return abs( aOffset.x ) > aDragMinX || abs( aOffset.y ) > aDragMinY;
217}
218
219
220bool TOOL_DISPATCHER::handleMouseButton( wxEvent& aEvent, int aIndex, bool aMotion )
221{
222 BUTTON_STATE* st = m_buttons[aIndex];
223 wxEventType type = aEvent.GetEventType();
224 std::optional<TOOL_EVENT> evt;
225 bool isClick = false;
226
227// bool up = type == st->upEvent;
228// bool down = type == st->downEvent;
229 bool up = false, down = false;
230 bool dblClick = type == st->dblClickEvent;
231 bool state = st->GetState();
232
233 if( dblClick )
234 {
235 // If the mouse moved significantly between clicks, this is a fast click-drag, not a
236 // double-click. Demote to a regular mouse-down so drag detection works normally.
238
240 {
241 dblClick = false;
242 down = true;
243
244 // Treat this as a fresh press so a stale pressed state from a missed button-up
245 // does not keep the previous drag origin.
246 st->pressed = false;
247 st->dragging = false;
248 }
249 }
250
251 if( !dblClick )
252 {
253 // Sometimes the dispatcher does not receive mouse button up event, so it stays
254 // in the dragging mode even if the mouse button is not held anymore
255 if( st->pressed && !state )
256 up = true;
257 // Don't apply same logic to down events as it kills touchpad tapping
258 else if( !st->pressed && type == st->downEvent )
259 down = true;
260 }
261
262 int mods = decodeModifiers( static_cast<wxMouseEvent*>( &aEvent ) );
263 int args = st->button | mods;
264
265 if( down ) // Handle mouse button press
266 {
267 st->downTimestamp = wxGetLocalTimeMillis();
268
269 if( !st->pressed ) // save the drag origin on the first click only
270 {
273 }
274
276 st->pressed = true;
277 evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DOWN, args );
278 }
279 else if( up ) // Handle mouse button release
280 {
281 st->pressed = false;
282
283 if( st->dragging )
284 evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_UP, args );
285 else
286 isClick = true;
287
288 if( isClick )
289 evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_CLICK, args );
290
291 st->dragging = false;
292 }
293 else if( dblClick )
294 {
295 evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DBLCLICK, args );
296 }
297
298 if( st->pressed && aMotion )
299 {
300 if( !st->dragging )
301 {
302#ifdef __WXMAC__
303 if( wxGetLocalTimeMillis() - st->downTimestamp > DragTimeThreshold )
304 st->dragging = true;
305#endif
307
309 st->dragging = true;
310
311 }
312
313 if( st->dragging )
314 {
315 evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DRAG, args );
316 evt->setMouseDragOrigin( st->dragOrigin );
317 evt->setMouseDelta( m_lastMousePos - st->dragOrigin );
318 }
319 }
320
321 if( evt )
322 {
323 evt->SetMousePosition( isClick ? st->downPosition : m_lastMousePos );
324 m_toolMgr->ProcessEvent( *evt );
325
326 return true;
327 }
328
329 return false;
330}
331
332
341bool isKeySpecialCode( int aKeyCode )
342{
343 // These keys have predefined actions (like move thumbtrack cursor),
344 // and we do not want these actions executed
345 const std::vector<enum wxKeyCode> special_keys =
346 {
347 WXK_PAGEUP, WXK_PAGEDOWN,
348 WXK_NUMPAD_PAGEUP, WXK_NUMPAD_PAGEDOWN
349 };
350
351 return alg::contains( special_keys, aKeyCode );
352}
353
354
359static bool isKeyModifierOnly( int aKeyCode )
360{
361 static std::vector<enum wxKeyCode> special_keys =
362 {
363 WXK_CONTROL, WXK_RAW_CONTROL, WXK_SHIFT, WXK_ALT,
364#ifdef WXK_WINDOWS_LEFT
365 WXK_WINDOWS_LEFT, WXK_WINDOWS_RIGHT,
366#endif
367#ifdef WXK_MENU
368 WXK_MENU,
369#endif
370#ifdef WXK_COMMAND
371 WXK_COMMAND,
372#endif
373#ifdef WXK_META
374 WXK_META,
375#endif
376 };
377
378 return alg::contains( special_keys, aKeyCode );
379}
380
381
382static bool isMouseClick( wxEventType type )
383{
384 return type == wxEVT_LEFT_DOWN || type == wxEVT_LEFT_UP || type == wxEVT_LEFT_DCLICK
385 || type == wxEVT_MIDDLE_DOWN || type == wxEVT_MIDDLE_UP || type == wxEVT_MIDDLE_DCLICK
386 || type == wxEVT_RIGHT_DOWN || type == wxEVT_RIGHT_UP || type == wxEVT_RIGHT_DCLICK
387 || type == wxEVT_AUX1_DOWN || type == wxEVT_AUX1_UP || type == wxEVT_AUX1_DCLICK
388 || type == wxEVT_AUX2_DOWN || type == wxEVT_AUX2_UP || type == wxEVT_AUX2_DCLICK;
389}
390
391
406int translateSpecialCode( int aKeyCode )
407{
408 switch( aKeyCode )
409 {
410 case WXK_NUMPAD_UP: return WXK_UP;
411 case WXK_NUMPAD_DOWN: return WXK_DOWN;
412 case WXK_NUMPAD_LEFT: return WXK_LEFT;
413 case WXK_NUMPAD_RIGHT: return WXK_RIGHT;
414 case WXK_NUMPAD_PAGEUP: return WXK_PAGEUP;
415 case WXK_NUMPAD_PAGEDOWN: return WXK_PAGEDOWN;
416 default: break;
417 };
418
419 return aKeyCode;
420}
421
422
423std::optional<TOOL_EVENT> TOOL_DISPATCHER::GetToolEvent( wxKeyEvent* aKeyEvent, bool* keyIsSpecial )
424{
425 std::optional<TOOL_EVENT> evt;
426 int key = aKeyEvent->GetKeyCode();
427 int unicode_key = aKeyEvent->GetUnicodeKey();
428
429 // This wxEVT_CHAR_HOOK event can be ignored: not useful in KiCad
430 if( isKeyModifierOnly( key ) )
431 {
432 aKeyEvent->Skip();
433 return evt;
434 }
435
436 wxLogTrace( kicadTraceKeyEvent, wxS( "TOOL_DISPATCHER::GetToolEvent %s" ), dump( *aKeyEvent ) );
437
438 // if the key event must be skipped, skip it here if the event is a wxEVT_CHAR_HOOK
439 // and do nothing.
440 *keyIsSpecial = isKeySpecialCode( key );
441
442 if( aKeyEvent->GetEventType() == wxEVT_CHAR_HOOK )
443 key = translateSpecialCode( key );
444
445 int mods = decodeModifiers( aKeyEvent );
446
447 if( mods & MD_CTRL )
448 {
449 // wxWidgets maps key codes related to Ctrl+letter handled by CHAR_EVT
450 // (http://docs.wxwidgets.org/trunk/classwx_key_event.html):
451 // char events for ASCII letters in this case carry codes corresponding to the ASCII
452 // value of Ctrl-Latter, i.e. 1 for Ctrl-A, 2 for Ctrl-B and so on until 26 for Ctrl-Z.
453 // They are remapped here to be more easy to handle in code
454 // Note also on OSX wxWidgets has a different behavior and the mapping is made
455 // only for ctrl+'A' to ctlr+'Z' (unicode code return 'A' to 'Z').
456 // Others OS return WXK_CONTROL_A to WXK_CONTROL_Z, and Ctrl+'M' returns the same code as
457 // the return key, so the remapping does not use the unicode key value.
458#ifdef __APPLE__
459 if( unicode_key >= 'A' && unicode_key <= 'Z' && key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z )
460#else
461 ignore_unused( unicode_key );
462
463 if( key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z )
464#endif
465 key += 'A' - 1;
466 }
467
468#ifdef __APPLE__
469 if( mods & MD_ALT )
470 {
471 // OSX maps a bunch of commonly used extended-ASCII characters onto the keyboard
472 // using the ALT key. Since we use ALT for some of our hotkeys, we need to map back
473 // to the underlying keys. The kVK_ANSI_* values come from Apple and are said to be
474 // hardware independent.
475 switch( aKeyEvent->GetRawKeyCode() )
476 {
477 case /* kVK_ANSI_1 */ 0x12: key = '1'; break;
478 case /* kVK_ANSI_2 */ 0x13: key = '2'; break;
479 case /* kVK_ANSI_3 */ 0x14: key = '3'; break;
480 case /* kVK_ANSI_4 */ 0x15: key = '4'; break;
481 case /* kVK_ANSI_6 */ 0x16: key = '6'; break;
482 case /* kVK_ANSI_5 */ 0x17: key = '5'; break;
483 case /* kVK_ANSI_Equal */ 0x18: key = '='; break;
484 case /* kVK_ANSI_9 */ 0x19: key = '9'; break;
485 case /* kVK_ANSI_7 */ 0x1A: key = '7'; break;
486 case /* kVK_ANSI_Minus */ 0x1B: key = '-'; break;
487 case /* kVK_ANSI_8 */ 0x1C: key = '8'; break;
488 case /* kVK_ANSI_0 */ 0x1D: key = '0'; break;
489 default: ;
490 }
491 }
492#endif
493
494 if( key == WXK_ESCAPE ) // ESC is the special key for canceling tools
495 evt = TOOL_EVENT( TC_COMMAND, TA_CANCEL_TOOL, WXK_ESCAPE );
496 else
497 evt = TOOL_EVENT( TC_KEYBOARD, TA_KEY_PRESSED, key | mods );
498
499 return evt;
500}
501
502
504{
505 // When an escape key event arrives, keyboard events can be processed before mouse button
506 // events due to wxWidgets event queue ordering. If a mouse button was pressed and has since
507 // been released (detected via polling), we need to process that click before handling the
508 // escape to maintain proper event ordering.
509 for( BUTTON_STATE* st : m_buttons )
510 {
511 if( st->pressed && !st->GetState() )
512 {
513 st->pressed = false;
514
515 TOOL_EVENT clickEvt( TC_MOUSE, TA_MOUSE_CLICK, st->button );
516 clickEvt.SetMousePosition( st->downPosition );
517 m_toolMgr->ProcessEvent( clickEvt );
518
519 st->dragging = false;
520 }
521 }
522}
523
524
525bool TOOL_DISPATCHER::ShouldDropAutoRepeat( int aKeyCode, wxLongLong aNowMs, bool aKeyIsDown,
526 int& aLastKey, wxLongLong& aLastTimeMs )
527{
528 bool sameBurst = aKeyCode == aLastKey && ( aNowMs - aLastTimeMs ) < AutoRepeatWindowMs;
529
530 aLastKey = aKeyCode;
531 aLastTimeMs = aNowMs;
532
533 return sameBurst && !aKeyIsDown;
534}
535
536
537const int MAX_MOUSE_BUTTON = 0x04;
538
539bool TOOL_DISPATCHER::isStaleAutoRepeat( const wxKeyEvent& aKeyEvent )
540{
541 // On Wayland, wxGetKeyState() only supports modifiers and lock keys. Keep all hotkey
542 // events when the backend cannot determine whether an ordinary key is still held.
544 return false;
545
546 int key = aKeyEvent.GetKeyCode();
547 bool keyIsDown = key > MAX_MOUSE_BUTTON && wxGetKeyState( static_cast<wxKeyCode>( key ) );
548
549 return ShouldDropAutoRepeat( key, wxGetLocalTimeMillis(), keyIsDown, m_lastKeyCode, m_lastKeyTime );
550}
551
552
553void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent )
554{
555 bool motion = false;
556 bool buttonEvents = false;
557 VECTOR2D pos;
558 std::optional<TOOL_EVENT> evt;
559 bool keyIsEscape = false; // True if the keypress was the escape key
560 bool keyIsSpecial = false; // True if the key is a special key code
561 bool droppedStaleAutoRepeat = false; // True if a backlogged repeat was discarded
562 wxWindow* focus = wxWindow::FindFocus();
563
564 // Required in win32 to ensure wxTimer events get scheduled in between other events
565 // Or else we may stall them out entirely and never get them during actions like rapid
566 // mouse moves.
568
569 wxEventType type = aEvent.GetEventType();
570
572 && !wxGetKeyState( static_cast<wxKeyCode>( m_lastKeyCode ) ) )
573 m_lastKeyCode = 0;
574
575 // Sometimes there is no window that has the focus (it happens when another PCB_BASE_FRAME
576 // is opened and is iconized on Windows).
577 // In this case, give the focus to the parent frame (GAL canvas itself does not accept the
578 // focus when iconized for some obscure reason)
579 if( focus == nullptr )
580 {
581 wxWindow* holderWindow = dynamic_cast<wxWindow*>( m_toolMgr->GetToolHolder() );
582
583#if defined( _WIN32 ) || defined( __WXGTK__ )
584 // Mouse events may trigger regardless of window status (windows feature)
585 // However we need to avoid focus fighting (especially modals)
586 if( holderWindow && KIPLATFORM::UI::IsWindowActive( holderWindow ) )
587#else
588 if( holderWindow )
589#endif
590 {
591 holderWindow->SetFocus();
592 }
593 }
594
595 if( isMouseClick( type ) )
596 {
597 if( m_toolMgr->GetToolHolder()
598 && m_toolMgr->GetToolHolder()->GetToolCanvas()
599 && !m_toolMgr->GetToolHolder()->GetToolCanvas()->HasFocus() )
600 {
601 m_toolMgr->GetToolHolder()->GetToolCanvas()->SetFocus();
602 }
603 }
604
605 // Mouse handling
606 // Note: wxEVT_LEFT_DOWN event must always be skipped.
607 if( type == wxEVT_MOTION
608 || type == wxEVT_MOUSEWHEEL
609 || type == wxEVT_MAGNIFY
610 || isMouseClick( type )
611 // Event issued when mouse retains position in screen coordinates,
612 // but changes in world coordinates (e.g. autopanning)
614 {
615 wxMouseEvent* me = static_cast<wxMouseEvent*>( &aEvent );
616 int mods = decodeModifiers( me );
617
618 if( m_toolMgr->GetViewControls() )
619 {
620 if( type == wxEVT_MOTION || isMouseClick( type ) )
621 {
622 const wxPoint eventPos = me->GetPosition();
623 m_lastMousePosScreen = VECTOR2D( eventPos.x, eventPos.y );
624 pos = GetClampedCoords( m_toolMgr->GetView()->ToWorld( m_lastMousePosScreen ) );
625 }
626 else
627 {
628 pos = m_toolMgr->GetViewControls()->GetMousePosition();
629 m_lastMousePosScreen = m_toolMgr->GetViewControls()->GetMousePosition( false );
630 }
631
632 if( pos != m_lastMousePos )
633 {
634 motion = true;
635 m_lastMousePos = pos;
636 }
637 }
638
639 for( unsigned int i = 0; i < m_buttons.size(); i++ )
640 buttonEvents |= handleMouseButton( aEvent, i, motion );
641
642 if( m_toolMgr->GetViewControls() )
643 {
644 if( !buttonEvents && motion )
645 {
646 evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_MOTION, mods );
647 evt->SetMousePosition( pos );
648 }
649 }
650
651 // We only handle wheel events that aren't for the view control.
652 // Events with zero or one modifier are reserved for view control.
653 // When using WX_VIEW_CONTROLS, these will already be handled, but
654 // we still shouldn't consume such events if we get them (e.g. for
655 // when WX_VIEW_CONTROLS is not in use, like in the 3D viewer)
656 if( !evt && me->GetWheelRotation() != 0 )
657 {
658 const unsigned modBits = static_cast<unsigned>( mods ) & MD_MODIFIER_MASK;
659 const bool shouldHandle = std::popcount( modBits ) > 1;
660
661 if( shouldHandle )
662 {
663 evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_WHEEL, mods );
664 evt->SetParameter<int>( me->GetWheelRotation() );
665 }
666 }
667 }
668 else if( type == wxEVT_CHAR_HOOK || type == wxEVT_CHAR )
669 {
670 wxKeyEvent* ke = static_cast<wxKeyEvent*>( &aEvent );
671
672 wxLogTrace( kicadTraceKeyEvent, wxS( "TOOL_DISPATCHER::DispatchWxEvent %s" ), dump( *ke ) );
673
674 // Do not process wxEVT_CHAR_HOOK for a shift-modified key, as ACTION_MANAGER::RunHotKey
675 // will run the un-shifted key and that's not what we want. Wait to get the translated
676 // key from wxEVT_CHAR.
677 // See https://gitlab.com/kicad/code/kicad/-/issues/1809
678 if( type == wxEVT_CHAR_HOOK && ke->GetModifiers() == wxMOD_SHIFT )
679 {
680 aEvent.Skip();
681 return;
682 }
683
684 keyIsEscape = ( ke->GetKeyCode() == WXK_ESCAPE );
685
686 // When escape is pressed shortly after a mouse click, the keyboard event can be
687 // processed before the mouse button release event. Flush any pending clicks first
688 // to ensure proper event ordering.
689 if( keyIsEscape )
691
692 if( KIUI::IsInputControlFocused( focus ) )
693 {
694 bool enabled = KIUI::IsInputControlEditable( focus );
695
696 // Never process key events for tools when a text entry has focus
697 if( enabled )
698 {
699 aEvent.Skip();
700 return;
701 }
702 // Even if not enabled, allow a copy out
703 else if( ke->GetModifiers() == wxMOD_CONTROL && ke->GetKeyCode() == 'C' )
704 {
705 aEvent.Skip();
706 return;
707 }
708 }
709
710 evt = GetToolEvent( ke, &keyIsSpecial );
711
712 // Drop backlogged OS key auto-repeat events that arrive after the key was released so a
713 // slow hotkey action (e.g. Rotate on a large selection) stops when the key is let go
714 // instead of running the whole queued burst. Only ordinary hotkey presses are filtered;
715 // cancel (escape) is always honoured.
716 if( evt && !evt->IsCancel() && !keyIsEscape && isStaleAutoRepeat( *ke ) )
717 {
718 evt.reset();
719 droppedStaleAutoRepeat = true;
720 }
721
722 if( evt && !evt->IsCancel() && m_toolMgr->GetViewControls() )
723 {
724 evt->SetMousePosition( m_lastMousePos );
725 evt->SetHasPosition( true );
726 }
727 }
728 else if( type == wxEVT_MENU_OPEN || type == wxEVT_MENU_CLOSE || type == wxEVT_MENU_HIGHLIGHT )
729 {
730 wxMenuEvent* tmp = dynamic_cast<wxMenuEvent*>( &aEvent );
731
732 // Something is amiss if the event has these types and isn't a menu event, so bail out on
733 // its processing
734 if( !tmp )
735 {
736 aEvent.Skip();
737 return;
738 }
739
740 wxMenuEvent& menuEvent = *tmp;
741
742#if wxCHECK_VERSION( 3, 2, 0 )
743 // Forward the event to the menu for processing
744 if( ACTION_MENU* currentMenu = dynamic_cast<ACTION_MENU*>( menuEvent.GetMenu() ) )
745 currentMenu->OnMenuEvent( menuEvent );
746#else
747 //
748 // wxWidgets has several issues that we have to work around:
749 //
750 // 1) wxWidgets 3.0.x Windows has a bug where wxEVT_MENU_OPEN and wxEVT_MENU_HIGHLIGHT
751 // events are not captured by the ACTON_MENU menus. So we forward them here.
752 // (FWIW, this one is fixed in wxWidgets 3.1.x.)
753 //
754 // 2) wxWidgets doesn't pass the menu pointer for wxEVT_MENU_HIGHLIGHT events. So we
755 // store the menu pointer from the wxEVT_MENU_OPEN call.
756 //
757 // 3) wxWidgets has no way to tell whether a command is from a menu selection or a
758 // hotkey. So we keep track of menu highlighting so we can differentiate.
759 //
760
761 static ACTION_MENU* currentMenu;
762
763 if( type == wxEVT_MENU_OPEN )
764 {
765 currentMenu = dynamic_cast<ACTION_MENU*>( menuEvent.GetMenu() );
766
767 if( currentMenu )
768 currentMenu->OnMenuEvent( menuEvent );
769 }
770 else if( type == wxEVT_MENU_HIGHLIGHT )
771 {
772 if( currentMenu )
773 currentMenu->OnMenuEvent( menuEvent );
774 }
775 else if( type == wxEVT_MENU_CLOSE )
776 {
777 if( currentMenu )
778 currentMenu->OnMenuEvent( menuEvent );
779
780 currentMenu = nullptr;
781 }
782#endif
783
784 aEvent.Skip();
785 }
786
787 bool handled = false;
788
789 if( evt )
790 {
791 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_DISPATCHER::DispatchWxEvent %s" ), evt->Format() );
792
793 handled = m_toolMgr->ProcessEvent( *evt );
794
795 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_DISPATCHER::DispatchWxEvent - Handled: %s %s" ),
796 ( handled ? wxS( "true" ) : wxS( "false" ) ), evt->Format() );
797 }
799 {
800 // A zoom anchored at the cursor leaves the cursor's world position where it was, so no
801 // motion follows, yet the zoom the status bar shows has changed
803 }
804
805 // pass the event to the GUI, it might still be interested in it
806 // Note wxEVT_CHAR_HOOK event is already skipped for special keys not used by KiCad
807 // and wxEVT_LEFT_DOWN must be always Skipped.
808 //
809 // On OS X, key events are always meant to be caught. An uncaught key event is assumed
810 // to be a user input error by OS X (as they are pressing keys in a context where nothing
811 // is there to catch the event). This annoyingly makes OS X beep and/or flash the screen
812 // in Pcbnew and the footprint editor any time a hotkey is used. The correct procedure is
813 // to NOT pass wxEVT_CHAR events to the GUI under OS X.
814 //
815 // On Windows, avoid to call wxEvent::Skip for special keys because some keys
816 // (PAGE_UP, PAGE_DOWN) have predefined actions (like move thumbtrack cursor), and we do
817 // not want these actions executed (most are handled by KiCad)
818
819 // A dropped stale auto-repeat must be fully swallowed; skipping it would let wx menu
820 // accelerators re-run the same hotkey after the key was released.
821 if( ( !evt && !droppedStaleAutoRepeat ) || type == wxEVT_LEFT_DOWN )
822 aEvent.Skip();
823
824 // Not handled wxEVT_CHAR must be Skipped (sent to GUI).
825 // Otherwise accelerators and shortcuts in main menu or toolbars are not seen.
826 // Escape key presses are never skipped by the handler since they correspond to tool cancel
827 // events, and if they aren't skipped then they are propagated to other frames (which we
828 // don't want).
829 if( ( type == wxEVT_CHAR || type == wxEVT_CHAR_HOOK )
830 && !keyIsSpecial
831 && !handled
832 && !keyIsEscape
833 && !droppedStaleAutoRepeat )
834 {
835 aEvent.Skip();
836 }
837
838 wxLogTrace( kicadTraceToolStack, "TOOL_DISPATCHER::DispatchWxEvent - Wx event skipped: %s",
839 ( aEvent.GetSkipped() ? "true" : "false" ) );
840}
841
842// LocalWords: EDA CERN CHANGELOG txt Tomasz Wlostowski wxEvent WXK
843// LocalWords: MERCHANTABILITY bool upEvent downEvent touchpad Ctrl
844// LocalWords: wxEVENT isKeySpecialCode thumbtrack DispatchWxEvent
845// LocalWords: NUMPAD PAGEUP PAGEDOWN wxEVT EVT OSX ctrl ctlr kVK
846// LocalWords: unicode ESC keypress wxTimer iconized autopanning WX
847// LocalWords: un Wx
Define the structure of a menu based on ACTIONs.
Definition action_menu.h:43
void OnMenuEvent(wxMenuEvent &aEvent)
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:68
static const wxEventType EVT_REFRESH_MOUSE
Event that forces mouse move event in the dispatcher (eg.
static const int DragDistanceThreshold
The distance threshold for mouse cursor that distinguishes between a single mouse click and a beginni...
static const int DragTimeThreshold
The time threshold for a mouse button press that distinguishes between a single mouse click and a beg...
virtual void ResetState()
Bring the dispatcher to its initial state.
static bool IsPastDragThreshold(const VECTOR2D &aOffset, int aDragMinX, int aDragMinY)
Pure decision on whether a cursor offset from a drag origin has crossed the drag threshold.
virtual void DispatchWxEvent(wxEvent &aEvent)
Process wxEvents (mostly UI events), translate them to TOOL_EVENTs, and make tools handle those.
static const int AutoRepeatWindowMs
The maximum gap (ms) between two events of the same key for them to count as one OS auto-repeat burst...
KIGFX::VIEW * getView()
Returns the instance of VIEW, used by the application.
int m_sysDragMinX
Minimum distance before drag is activated in the X axis.
wxLongLong m_lastKeyTime
Local time (ms) at which m_lastKeyCode was last processed.
bool handleMouseButton(wxEvent &aEvent, int aIndex, bool aMotion)
Handles mouse related events (click, motion, dragging).
static bool ShouldDropAutoRepeat(int aKeyCode, wxLongLong aNowMs, bool aKeyIsDown, int &aLastKey, wxLongLong &aLastTimeMs)
Pure decision used to discard backlogged OS key auto-repeat events.
int m_sysDragMinY
Maximum distance before drag is activated in the Y axis.
static int decodeModifiers(const wxKeyboardState *aState)
Returns the state of key modifiers (Alt, Ctrl and so on) as OR'ed list of bits (MD_CTRL,...
void flushPendingClicks()
Processes any pending mouse clicks that have been physically completed but not yet dispatched.
VECTOR2D m_lastMousePosScreen
The last mouse cursor position (in screen coordinates).
TOOL_MANAGER * m_toolMgr
Instance of tool manager that cooperates with the dispatcher.
TOOL_DISPATCHER(TOOL_MANAGER *aToolMgr)
bool isStaleAutoRepeat(const wxKeyEvent &aKeyEvent)
Decide whether a keyboard event must be dropped because it is a backlogged OS key auto-repeat event d...
std::vector< BUTTON_STATE * > m_buttons
VECTOR2D m_lastMousePos
The last mouse cursor position (in world coordinates).
std::optional< TOOL_EVENT > GetToolEvent(wxKeyEvent *aKeyEvent, bool *aSpecialKeyFlag)
Map a wxWidgets key event to a TOOL_EVENT.
int m_lastKeyCode
Key code of the key that was last processed, or 0 when no key is armed.
Generic, UI-independent tool event.
Definition tool_event.h:167
void SetMousePosition(const VECTOR2D &aP)
Definition tool_event.h:549
Master controller class:
a few functions useful in geometry calculations.
VECTOR2< ret_type > GetClampedCoords(const VECTOR2< in_type > &aCoords, pad_type aPadding=1u)
Clamp a vector to values that can be negated, respecting numeric limits of coordinates data type with...
const wxChar *const kicadTraceKeyEvent
Flag to enable wxKeyEvent debug tracing.
const wxChar *const kicadTraceToolStack
Flag to enable tracing of the tool handling stack.
void ignore_unused(const T &)
Definition ignore.h:20
This file contains miscellaneous commonly used macros and functions.
void ForceTimerMessagesToBeCreatedIfNecessary()
Forces wxTimers to fire more promptly on Win32.
Definition unix/app.cpp:113
bool SupportsKeyState()
Return whether wxGetKeyState() can query ordinary keys, beyond modifiers and lock keys.
Definition wxgtk/ui.cpp:491
bool IsWindowActive(wxWindow *aWindow)
Check to see if the given window is the currently active window (e.g.
Definition wxgtk/ui.cpp:230
KICOMMON_API bool IsInputControlFocused(wxWindow *aFocus=nullptr)
Check if a input control has focus.
KICOMMON_API bool IsInputControlEditable(wxWindow *aControl)
Check if a input control has focus.
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:96
Store information about a mouse button state.
VECTOR2D dragOrigin
Point where dragging has started (in world coordinates).
wxEventType dblClickEvent
The type of wxEvent that determines mouse button double click.
wxEventType upEvent
The type of wxEvent that determines mouse button release.
VECTOR2D downPosition
Point where click event has occurred.
bool dragging
Flag indicating that dragging is active for the given button.
bool GetState() const
Checks the current state of the button.
bool pressed
Flag indicating that the given button is pressed.
BUTTON_STATE(TOOL_MOUSE_BUTTONS aButton, const wxEventType &aDownEvent, const wxEventType &aUpEvent, const wxEventType &aDblClickEvent)
wxLongLong downTimestamp
Time stamp for the last mouse button press event.
TOOL_MOUSE_BUTTONS button
Determines the mouse button for which information are stored.
void Reset()
Restores initial state.
wxEventType downEvent
The type of wxEvent that determines mouse button press.
VECTOR2D dragOriginScreen
Point where dragging has started (in screen coordinates).
static bool isMouseClick(wxEventType type)
bool isKeySpecialCode(int aKeyCode)
Helper to know if a special key ( see key list ) should be captured.
const int MAX_MOUSE_BUTTON
int translateSpecialCode(int aKeyCode)
Convert some special key codes to an equivalent.
static bool isKeyModifierOnly(int aKeyCode)
Helper to know if a key should be managed by DispatchWxEvent() or if the event can be ignored and ski...
@ TA_MOUSE_CLICK
Definition tool_event.h:63
@ TA_MOUSE_MOTION
Definition tool_event.h:68
@ TA_MOUSE_UP
Definition tool_event.h:65
@ TA_MOUSE_DRAG
Definition tool_event.h:67
@ TA_MOUSE_DOWN
Definition tool_event.h:66
@ TA_KEY_PRESSED
Definition tool_event.h:72
@ TA_MOUSE_DBLCLICK
Definition tool_event.h:64
@ TA_MOUSE_WHEEL
Definition tool_event.h:69
@ TA_CANCEL_TOOL
Tool cancel event.
Definition tool_event.h:86
@ MD_MODIFIER_MASK
Definition tool_event.h:145
@ MD_META
Definition tool_event.h:143
@ MD_ALT
Definition tool_event.h:141
@ MD_CTRL
Definition tool_event.h:140
@ MD_SUPER
Definition tool_event.h:142
@ MD_ALTGR
Definition tool_event.h:144
@ MD_SHIFT
Definition tool_event.h:139
@ TC_COMMAND
Definition tool_event.h:53
@ TC_MOUSE
Definition tool_event.h:51
@ TC_KEYBOARD
Definition tool_event.h:52
TOOL_MOUSE_BUTTONS
Definition tool_event.h:126
@ BUT_AUX1
Definition tool_event.h:131
@ BUT_MIDDLE
Definition tool_event.h:130
@ BUT_LEFT
Definition tool_event.h:128
@ BUT_RIGHT
Definition tool_event.h:129
@ BUT_AUX2
Definition tool_event.h:132
wxString dump(const wxArrayString &aArray)
Debug helper for printing wxArrayString contents.
wxLogTrace helper definitions.
VECTOR2< double > VECTOR2D
Definition vector2d.h:707
WX_VIEW_CONTROLS class definition.