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