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
536const int MAX_MOUSE_BUTTON = 0x04;
537
538bool TOOL_DISPATCHER::isStaleAutoRepeat( const wxKeyEvent& aKeyEvent )
539{
540 // On Wayland, wxGetKeyState() only supports modifiers and lock keys. Keep all hotkey
541 // events when the backend cannot determine whether an ordinary key is still held.
543 return false;
544
545 int key = aKeyEvent.GetKeyCode();
546 bool keyIsDown = key > MAX_MOUSE_BUTTON && wxGetKeyState( static_cast<wxKeyCode>( key ) );
547
548 return ShouldDropAutoRepeat( key, wxGetLocalTimeMillis(), keyIsDown, m_lastKeyCode, m_lastKeyTime );
549}
550
551
552void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent )
553{
554 bool motion = false;
555 bool buttonEvents = false;
556 VECTOR2D pos;
557 std::optional<TOOL_EVENT> evt;
558 bool keyIsEscape = false; // True if the keypress was the escape key
559 bool keyIsSpecial = false; // True if the key is a special key code
560 bool droppedStaleAutoRepeat = false; // True if a backlogged repeat was discarded
561 wxWindow* focus = wxWindow::FindFocus();
562
563 // Required in win32 to ensure wxTimer events get scheduled in between other events
564 // Or else we may stall them out entirely and never get them during actions like rapid
565 // mouse moves.
567
568 wxEventType type = aEvent.GetEventType();
569
571 && !wxGetKeyState( static_cast<wxKeyCode>( m_lastKeyCode ) ) )
572 m_lastKeyCode = 0;
573
574 // Sometimes there is no window that has the focus (it happens when another PCB_BASE_FRAME
575 // is opened and is iconized on Windows).
576 // In this case, give the focus to the parent frame (GAL canvas itself does not accept the
577 // focus when iconized for some obscure reason)
578 if( focus == nullptr )
579 {
580 wxWindow* holderWindow = dynamic_cast<wxWindow*>( m_toolMgr->GetToolHolder() );
581
582#if defined( _WIN32 ) || defined( __WXGTK__ )
583 // Mouse events may trigger regardless of window status (windows feature)
584 // However we need to avoid focus fighting (especially modals)
585 if( holderWindow && KIPLATFORM::UI::IsWindowActive( holderWindow ) )
586#else
587 if( holderWindow )
588#endif
589 {
590 holderWindow->SetFocus();
591 }
592 }
593
594 if( isMouseClick( type ) )
595 {
596 if( m_toolMgr->GetToolHolder()
597 && m_toolMgr->GetToolHolder()->GetToolCanvas()
598 && !m_toolMgr->GetToolHolder()->GetToolCanvas()->HasFocus() )
599 {
600 m_toolMgr->GetToolHolder()->GetToolCanvas()->SetFocus();
601 }
602 }
603
604 // Mouse handling
605 // Note: wxEVT_LEFT_DOWN event must always be skipped.
606 if( type == wxEVT_MOTION
607 || type == wxEVT_MOUSEWHEEL
608 || type == wxEVT_MAGNIFY
609 || isMouseClick( type )
610 // Event issued when mouse retains position in screen coordinates,
611 // but changes in world coordinates (e.g. autopanning)
613 {
614 wxMouseEvent* me = static_cast<wxMouseEvent*>( &aEvent );
615 int mods = decodeModifiers( me );
616
617 if( m_toolMgr->GetViewControls() )
618 {
619 pos = m_toolMgr->GetViewControls()->GetMousePosition();
620 m_lastMousePosScreen = m_toolMgr->GetViewControls()->GetMousePosition( false );
621
622 if( pos != m_lastMousePos )
623 {
624 motion = true;
625 m_lastMousePos = pos;
626 }
627 }
628
629 for( unsigned int i = 0; i < m_buttons.size(); i++ )
630 buttonEvents |= handleMouseButton( aEvent, i, motion );
631
632 if( m_toolMgr->GetViewControls() )
633 {
634 if( !buttonEvents && motion )
635 {
636 evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_MOTION, mods );
637 evt->SetMousePosition( pos );
638 }
639 }
640
641 // We only handle wheel events that aren't for the view control.
642 // Events with zero or one modifier are reserved for view control.
643 // When using WX_VIEW_CONTROLS, these will already be handled, but
644 // we still shouldn't consume such events if we get them (e.g. for
645 // when WX_VIEW_CONTROLS is not in use, like in the 3D viewer)
646 if( !evt && me->GetWheelRotation() != 0 )
647 {
648 const unsigned modBits = static_cast<unsigned>( mods ) & MD_MODIFIER_MASK;
649 const bool shouldHandle = std::popcount( modBits ) > 1;
650
651 if( shouldHandle )
652 {
653 evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_WHEEL, mods );
654 evt->SetParameter<int>( me->GetWheelRotation() );
655 }
656 }
657 }
658 else if( type == wxEVT_CHAR_HOOK || type == wxEVT_CHAR )
659 {
660 wxKeyEvent* ke = static_cast<wxKeyEvent*>( &aEvent );
661
662 wxLogTrace( kicadTraceKeyEvent, wxS( "TOOL_DISPATCHER::DispatchWxEvent %s" ), dump( *ke ) );
663
664 // Do not process wxEVT_CHAR_HOOK for a shift-modified key, as ACTION_MANAGER::RunHotKey
665 // will run the un-shifted key and that's not what we want. Wait to get the translated
666 // key from wxEVT_CHAR.
667 // See https://gitlab.com/kicad/code/kicad/-/issues/1809
668 if( type == wxEVT_CHAR_HOOK && ke->GetModifiers() == wxMOD_SHIFT )
669 {
670 aEvent.Skip();
671 return;
672 }
673
674 keyIsEscape = ( ke->GetKeyCode() == WXK_ESCAPE );
675
676 // When escape is pressed shortly after a mouse click, the keyboard event can be
677 // processed before the mouse button release event. Flush any pending clicks first
678 // to ensure proper event ordering.
679 if( keyIsEscape )
681
682 if( KIUI::IsInputControlFocused( focus ) )
683 {
684 bool enabled = KIUI::IsInputControlEditable( focus );
685
686 // Never process key events for tools when a text entry has focus
687 if( enabled )
688 {
689 aEvent.Skip();
690 return;
691 }
692 // Even if not enabled, allow a copy out
693 else if( ke->GetModifiers() == wxMOD_CONTROL && ke->GetKeyCode() == 'C' )
694 {
695 aEvent.Skip();
696 return;
697 }
698 }
699
700 evt = GetToolEvent( ke, &keyIsSpecial );
701
702 // Drop backlogged OS key auto-repeat events that arrive after the key was released so a
703 // slow hotkey action (e.g. Rotate on a large selection) stops when the key is let go
704 // instead of running the whole queued burst. Only ordinary hotkey presses are filtered;
705 // cancel (escape) is always honoured.
706 if( evt && !evt->IsCancel() && !keyIsEscape && isStaleAutoRepeat( *ke ) )
707 {
708 evt.reset();
709 droppedStaleAutoRepeat = true;
710 }
711
712 if( evt && !evt->IsCancel() && m_toolMgr->GetViewControls() )
713 {
714 evt->SetMousePosition( m_lastMousePos );
715 evt->SetHasPosition( true );
716 }
717 }
718 else if( type == wxEVT_MENU_OPEN || type == wxEVT_MENU_CLOSE || type == wxEVT_MENU_HIGHLIGHT )
719 {
720 wxMenuEvent* tmp = dynamic_cast<wxMenuEvent*>( &aEvent );
721
722 // Something is amiss if the event has these types and isn't a menu event, so bail out on
723 // its processing
724 if( !tmp )
725 {
726 aEvent.Skip();
727 return;
728 }
729
730 wxMenuEvent& menuEvent = *tmp;
731
732#if wxCHECK_VERSION( 3, 2, 0 )
733 // Forward the event to the menu for processing
734 if( ACTION_MENU* currentMenu = dynamic_cast<ACTION_MENU*>( menuEvent.GetMenu() ) )
735 currentMenu->OnMenuEvent( menuEvent );
736#else
737 //
738 // wxWidgets has several issues that we have to work around:
739 //
740 // 1) wxWidgets 3.0.x Windows has a bug where wxEVT_MENU_OPEN and wxEVT_MENU_HIGHLIGHT
741 // events are not captured by the ACTON_MENU menus. So we forward them here.
742 // (FWIW, this one is fixed in wxWidgets 3.1.x.)
743 //
744 // 2) wxWidgets doesn't pass the menu pointer for wxEVT_MENU_HIGHLIGHT events. So we
745 // store the menu pointer from the wxEVT_MENU_OPEN call.
746 //
747 // 3) wxWidgets has no way to tell whether a command is from a menu selection or a
748 // hotkey. So we keep track of menu highlighting so we can differentiate.
749 //
750
751 static ACTION_MENU* currentMenu;
752
753 if( type == wxEVT_MENU_OPEN )
754 {
755 currentMenu = dynamic_cast<ACTION_MENU*>( menuEvent.GetMenu() );
756
757 if( currentMenu )
758 currentMenu->OnMenuEvent( menuEvent );
759 }
760 else if( type == wxEVT_MENU_HIGHLIGHT )
761 {
762 if( currentMenu )
763 currentMenu->OnMenuEvent( menuEvent );
764 }
765 else if( type == wxEVT_MENU_CLOSE )
766 {
767 if( currentMenu )
768 currentMenu->OnMenuEvent( menuEvent );
769
770 currentMenu = nullptr;
771 }
772#endif
773
774 aEvent.Skip();
775 }
776
777 bool handled = false;
778
779 if( evt )
780 {
781 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_DISPATCHER::DispatchWxEvent %s" ), evt->Format() );
782
783 handled = m_toolMgr->ProcessEvent( *evt );
784
785 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_DISPATCHER::DispatchWxEvent - Handled: %s %s" ),
786 ( handled ? wxS( "true" ) : wxS( "false" ) ), evt->Format() );
787 }
788
789 // pass the event to the GUI, it might still be interested in it
790 // Note wxEVT_CHAR_HOOK event is already skipped for special keys not used by KiCad
791 // and wxEVT_LEFT_DOWN must be always Skipped.
792 //
793 // On OS X, key events are always meant to be caught. An uncaught key event is assumed
794 // to be a user input error by OS X (as they are pressing keys in a context where nothing
795 // is there to catch the event). This annoyingly makes OS X beep and/or flash the screen
796 // in Pcbnew and the footprint editor any time a hotkey is used. The correct procedure is
797 // to NOT pass wxEVT_CHAR events to the GUI under OS X.
798 //
799 // On Windows, avoid to call wxEvent::Skip for special keys because some keys
800 // (PAGE_UP, PAGE_DOWN) have predefined actions (like move thumbtrack cursor), and we do
801 // not want these actions executed (most are handled by KiCad)
802
803 // A dropped stale auto-repeat must be fully swallowed; skipping it would let wx menu
804 // accelerators re-run the same hotkey after the key was released.
805 if( ( !evt && !droppedStaleAutoRepeat ) || type == wxEVT_LEFT_DOWN )
806 aEvent.Skip();
807
808 // Not handled wxEVT_CHAR must be Skipped (sent to GUI).
809 // Otherwise accelerators and shortcuts in main menu or toolbars are not seen.
810 // Escape key presses are never skipped by the handler since they correspond to tool cancel
811 // events, and if they aren't skipped then they are propagated to other frames (which we
812 // don't want).
813 if( ( type == wxEVT_CHAR || type == wxEVT_CHAR_HOOK )
814 && !keyIsSpecial
815 && !handled
816 && !keyIsEscape
817 && !droppedStaleAutoRepeat )
818 {
819 aEvent.Skip();
820 }
821
822 wxLogTrace( kicadTraceToolStack, "TOOL_DISPATCHER::DispatchWxEvent - Wx event skipped: %s",
823 ( aEvent.GetSkipped() ? "true" : "false" ) );
824}
825
826// LocalWords: EDA CERN CHANGELOG txt Tomasz Wlostowski wxEvent WXK
827// LocalWords: MERCHANTABILITY bool upEvent downEvent touchpad Ctrl
828// LocalWords: wxEVENT isKeySpecialCode thumbtrack DispatchWxEvent
829// LocalWords: NUMPAD PAGEUP PAGEDOWN wxEVT EVT OSX ctrl ctlr kVK
830// LocalWords: unicode ESC keypress wxTimer iconized autopanning WX
831// 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:549
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 SupportsKeyState()
Return whether wxGetKeyState() can query ordinary keys, beyond modifiers and lock keys.
Definition wxgtk/ui.cpp:391
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.
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.