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::handleMouseButton( wxEvent& aEvent, int aIndex, bool aMotion )
214{
215 BUTTON_STATE* st = m_buttons[aIndex];
216 wxEventType type = aEvent.GetEventType();
217 std::optional<TOOL_EVENT> evt;
218 bool isClick = false;
219
220// bool up = type == st->upEvent;
221// bool down = type == st->downEvent;
222 bool up = false, down = false;
223 bool dblClick = type == st->dblClickEvent;
224 bool state = st->GetState();
225
226 if( !dblClick )
227 {
228 // Sometimes the dispatcher does not receive mouse button up event, so it stays
229 // in the dragging mode even if the mouse button is not held anymore
230 if( st->pressed && !state )
231 up = true;
232 // Don't apply same logic to down events as it kills touchpad tapping
233 else if( !st->pressed && type == st->downEvent )
234 down = true;
235 }
236
237 int mods = decodeModifiers( static_cast<wxMouseEvent*>( &aEvent ) );
238 int args = st->button | mods;
239
240 if( down ) // Handle mouse button press
241 {
242 st->downTimestamp = wxGetLocalTimeMillis();
243
244 if( !st->pressed ) // save the drag origin on the first click only
245 {
248 }
249
251 st->pressed = true;
252 evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DOWN, args );
253 }
254 else if( up ) // Handle mouse button release
255 {
256 st->pressed = false;
257
258 if( st->dragging )
259 evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_UP, args );
260 else
261 isClick = true;
262
263 if( isClick )
264 evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_CLICK, args );
265
266 st->dragging = false;
267 }
268 else if( dblClick )
269 {
270 evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DBLCLICK, args );
271 }
272
273 if( st->pressed && aMotion )
274 {
275 if( !st->dragging )
276 {
277#ifdef __WXMAC__
278 if( wxGetLocalTimeMillis() - st->downTimestamp > DragTimeThreshold )
279 st->dragging = true;
280#endif
282
283 if( abs( offset.x ) > m_sysDragMinX || abs( offset.y ) > m_sysDragMinY )
284 st->dragging = true;
285
286 }
287
288 if( st->dragging )
289 {
290 evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DRAG, args );
291 evt->setMouseDragOrigin( st->dragOrigin );
292 evt->setMouseDelta( m_lastMousePos - st->dragOrigin );
293 }
294 }
295
296 if( evt )
297 {
298 evt->SetMousePosition( isClick ? st->downPosition : m_lastMousePos );
299 m_toolMgr->ProcessEvent( *evt );
300
301 return true;
302 }
303
304 return false;
305}
306
307
316bool isKeySpecialCode( int aKeyCode )
317{
318 // These keys have predefined actions (like move thumbtrack cursor),
319 // and we do not want these actions executed
320 const std::vector<enum wxKeyCode> special_keys =
321 {
322 WXK_PAGEUP, WXK_PAGEDOWN,
323 WXK_NUMPAD_PAGEUP, WXK_NUMPAD_PAGEDOWN
324 };
325
326 return alg::contains( special_keys, aKeyCode );
327}
328
329
334static bool isKeyModifierOnly( int aKeyCode )
335{
336 static std::vector<enum wxKeyCode> special_keys =
337 {
338 WXK_CONTROL, WXK_RAW_CONTROL, WXK_SHIFT, WXK_ALT,
339#ifdef WXK_WINDOWS_LEFT
340 WXK_WINDOWS_LEFT, WXK_WINDOWS_RIGHT,
341#endif
342#ifdef WXK_MENU
343 WXK_MENU,
344#endif
345#ifdef WXK_COMMAND
346 WXK_COMMAND,
347#endif
348#ifdef WXK_META
349 WXK_META,
350#endif
351 };
352
353 return alg::contains( special_keys, aKeyCode );
354}
355
356
357static bool isMouseClick( wxEventType type )
358{
359 return type == wxEVT_LEFT_DOWN || type == wxEVT_LEFT_UP || type == wxEVT_LEFT_DCLICK
360 || type == wxEVT_MIDDLE_DOWN || type == wxEVT_MIDDLE_UP || type == wxEVT_MIDDLE_DCLICK
361 || type == wxEVT_RIGHT_DOWN || type == wxEVT_RIGHT_UP || type == wxEVT_RIGHT_DCLICK
362 || type == wxEVT_AUX1_DOWN || type == wxEVT_AUX1_UP || type == wxEVT_AUX1_DCLICK
363 || type == wxEVT_AUX2_DOWN || type == wxEVT_AUX2_UP || type == wxEVT_AUX2_DCLICK;
364}
365
366
381int translateSpecialCode( int aKeyCode )
382{
383 switch( aKeyCode )
384 {
385 case WXK_NUMPAD_UP: return WXK_UP;
386 case WXK_NUMPAD_DOWN: return WXK_DOWN;
387 case WXK_NUMPAD_LEFT: return WXK_LEFT;
388 case WXK_NUMPAD_RIGHT: return WXK_RIGHT;
389 case WXK_NUMPAD_PAGEUP: return WXK_PAGEUP;
390 case WXK_NUMPAD_PAGEDOWN: return WXK_PAGEDOWN;
391 default: break;
392 };
393
394 return aKeyCode;
395}
396
397
398std::optional<TOOL_EVENT> TOOL_DISPATCHER::GetToolEvent( wxKeyEvent* aKeyEvent, bool* keyIsSpecial )
399{
400 std::optional<TOOL_EVENT> evt;
401 int key = aKeyEvent->GetKeyCode();
402 int unicode_key = aKeyEvent->GetUnicodeKey();
403
404 // This wxEVT_CHAR_HOOK event can be ignored: not useful in KiCad
405 if( isKeyModifierOnly( key ) )
406 {
407 aKeyEvent->Skip();
408 return evt;
409 }
410
411 wxLogTrace( kicadTraceKeyEvent, wxS( "TOOL_DISPATCHER::GetToolEvent %s" ), dump( *aKeyEvent ) );
412
413 // if the key event must be skipped, skip it here if the event is a wxEVT_CHAR_HOOK
414 // and do nothing.
415 *keyIsSpecial = isKeySpecialCode( key );
416
417 if( aKeyEvent->GetEventType() == wxEVT_CHAR_HOOK )
418 key = translateSpecialCode( key );
419
420 int mods = decodeModifiers( aKeyEvent );
421
422 if( mods & MD_CTRL )
423 {
424 // wxWidgets maps key codes related to Ctrl+letter handled by CHAR_EVT
425 // (http://docs.wxwidgets.org/trunk/classwx_key_event.html):
426 // char events for ASCII letters in this case carry codes corresponding to the ASCII
427 // value of Ctrl-Latter, i.e. 1 for Ctrl-A, 2 for Ctrl-B and so on until 26 for Ctrl-Z.
428 // They are remapped here to be more easy to handle in code
429 // Note also on OSX wxWidgets has a different behavior and the mapping is made
430 // only for ctrl+'A' to ctlr+'Z' (unicode code return 'A' to 'Z').
431 // Others OS return WXK_CONTROL_A to WXK_CONTROL_Z, and Ctrl+'M' returns the same code as
432 // the return key, so the remapping does not use the unicode key value.
433#ifdef __APPLE__
434 if( unicode_key >= 'A' && unicode_key <= 'Z' && key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z )
435#else
436 ignore_unused( unicode_key );
437
438 if( key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z )
439#endif
440 key += 'A' - 1;
441 }
442
443#ifdef __APPLE__
444 if( mods & MD_ALT )
445 {
446 // OSX maps a bunch of commonly used extended-ASCII characters onto the keyboard
447 // using the ALT key. Since we use ALT for some of our hotkeys, we need to map back
448 // to the underlying keys. The kVK_ANSI_* values come from Apple and are said to be
449 // hardware independent.
450 switch( aKeyEvent->GetRawKeyCode() )
451 {
452 case /* kVK_ANSI_1 */ 0x12: key = '1'; break;
453 case /* kVK_ANSI_2 */ 0x13: key = '2'; break;
454 case /* kVK_ANSI_3 */ 0x14: key = '3'; break;
455 case /* kVK_ANSI_4 */ 0x15: key = '4'; break;
456 case /* kVK_ANSI_6 */ 0x16: key = '6'; break;
457 case /* kVK_ANSI_5 */ 0x17: key = '5'; break;
458 case /* kVK_ANSI_Equal */ 0x18: key = '='; break;
459 case /* kVK_ANSI_9 */ 0x19: key = '9'; break;
460 case /* kVK_ANSI_7 */ 0x1A: key = '7'; break;
461 case /* kVK_ANSI_Minus */ 0x1B: key = '-'; break;
462 case /* kVK_ANSI_8 */ 0x1C: key = '8'; break;
463 case /* kVK_ANSI_0 */ 0x1D: key = '0'; break;
464 default: ;
465 }
466 }
467#endif
468
469 if( key == WXK_ESCAPE ) // ESC is the special key for canceling tools
470 evt = TOOL_EVENT( TC_COMMAND, TA_CANCEL_TOOL, WXK_ESCAPE );
471 else
472 evt = TOOL_EVENT( TC_KEYBOARD, TA_KEY_PRESSED, key | mods );
473
474 return evt;
475}
476
477
479{
480 // When an escape key event arrives, keyboard events can be processed before mouse button
481 // events due to wxWidgets event queue ordering. If a mouse button was pressed and has since
482 // been released (detected via polling), we need to process that click before handling the
483 // escape to maintain proper event ordering.
484 for( BUTTON_STATE* st : m_buttons )
485 {
486 if( st->pressed && !st->GetState() )
487 {
488 st->pressed = false;
489
490 TOOL_EVENT clickEvt( TC_MOUSE, TA_MOUSE_CLICK, st->button );
491 clickEvt.SetMousePosition( st->downPosition );
492 m_toolMgr->ProcessEvent( clickEvt );
493
494 st->dragging = false;
495 }
496 }
497}
498
499
500bool TOOL_DISPATCHER::ShouldDropAutoRepeat( int aKeyCode, wxLongLong aNowMs, bool aKeyIsDown,
501 int& aLastKey, wxLongLong& aLastTimeMs )
502{
503 bool sameBurst = aKeyCode == aLastKey && ( aNowMs - aLastTimeMs ) < AutoRepeatWindowMs;
504
505 aLastKey = aKeyCode;
506 aLastTimeMs = aNowMs;
507
508 return sameBurst && !aKeyIsDown;
509}
510
511
512bool TOOL_DISPATCHER::isStaleAutoRepeat( const wxKeyEvent& aKeyEvent )
513{
514 int key = aKeyEvent.GetKeyCode();
515
516 // wxGetKeyState answers reliably for letters, digits and the named WXK_ codes used as
517 // hotkeys; modifier-only keys never reach here.
518 bool keyIsDown = wxGetKeyState( static_cast<wxKeyCode>( key ) );
519
520 return ShouldDropAutoRepeat( key, wxGetLocalTimeMillis(), keyIsDown, m_lastKeyCode,
522}
523
524
525void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent )
526{
527 bool motion = false;
528 bool buttonEvents = false;
529 VECTOR2D pos;
530 std::optional<TOOL_EVENT> evt;
531 bool keyIsEscape = false; // True if the keypress was the escape key
532 bool keyIsSpecial = false; // True if the key is a special key code
533 bool droppedStaleAutoRepeat = false; // True if a backlogged repeat was discarded
534 wxWindow* focus = wxWindow::FindFocus();
535
536 // Required in win32 to ensure wxTimer events get scheduled in between other events
537 // Or else we may stall them out entirely and never get them during actions like rapid
538 // mouse moves.
540
541 wxEventType type = aEvent.GetEventType();
542
543 // Sometimes there is no window that has the focus (it happens when another PCB_BASE_FRAME
544 // is opened and is iconized on Windows).
545 // In this case, give the focus to the parent frame (GAL canvas itself does not accept the
546 // focus when iconized for some obscure reason)
547 if( focus == nullptr )
548 {
549 wxWindow* holderWindow = dynamic_cast<wxWindow*>( m_toolMgr->GetToolHolder() );
550
551#if defined( _WIN32 ) || defined( __WXGTK__ )
552 // Mouse events may trigger regardless of window status (windows feature)
553 // However we need to avoid focus fighting (especially modals)
554 if( holderWindow && KIPLATFORM::UI::IsWindowActive( holderWindow ) )
555#else
556 if( holderWindow )
557#endif
558 {
559 holderWindow->SetFocus();
560 }
561 }
562
563 if( isMouseClick( type ) )
564 {
565 if( m_toolMgr->GetToolHolder() && m_toolMgr->GetToolHolder()->GetToolCanvas() &&
566 !m_toolMgr->GetToolHolder()->GetToolCanvas()->HasFocus() )
567 {
568 m_toolMgr->GetToolHolder()->GetToolCanvas()->SetFocus();
569 }
570 }
571
572 // Mouse handling
573 // Note: wxEVT_LEFT_DOWN event must always be skipped.
574 if( type == wxEVT_MOTION || type == wxEVT_MOUSEWHEEL ||
575 type == wxEVT_MAGNIFY ||
576 isMouseClick( type ) ||
577 // Event issued when mouse retains position in screen coordinates,
578 // but changes in world coordinates (e.g. autopanning)
580 {
581 wxMouseEvent* me = static_cast<wxMouseEvent*>( &aEvent );
582 int mods = decodeModifiers( me );
583
584 if( m_toolMgr->GetViewControls() )
585 {
586 pos = m_toolMgr->GetViewControls()->GetMousePosition();
587 m_lastMousePosScreen = m_toolMgr->GetViewControls()->GetMousePosition( false );
588
589 if( pos != m_lastMousePos )
590 {
591 motion = true;
592 m_lastMousePos = pos;
593 }
594 }
595
596 for( unsigned int i = 0; i < m_buttons.size(); i++ )
597 buttonEvents |= handleMouseButton( aEvent, i, motion );
598
599 if( m_toolMgr->GetViewControls() )
600 {
601 if( !buttonEvents && motion )
602 {
603 evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_MOTION, mods );
604 evt->SetMousePosition( pos );
605 }
606 }
607
608 // We only handle wheel events that aren't for the view control.
609 // Events with zero or one modifier are reserved for view control.
610 // When using WX_VIEW_CONTROLS, these will already be handled, but
611 // we still shouldn't consume such events if we get them (e.g. for
612 // when WX_VIEW_CONTROLS is not in use, like in the 3D viewer)
613 if( !evt && me->GetWheelRotation() != 0 )
614 {
615 const unsigned modBits =
616 static_cast<unsigned>( mods ) & MD_MODIFIER_MASK;
617 const bool shouldHandle = std::popcount( modBits ) > 1;
618
619 if( shouldHandle )
620 {
621 evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_WHEEL, mods );
622 evt->SetParameter<int>( me->GetWheelRotation() );
623 }
624 }
625 }
626 else if( type == wxEVT_CHAR_HOOK || type == wxEVT_CHAR )
627 {
628 wxKeyEvent* ke = static_cast<wxKeyEvent*>( &aEvent );
629
630 wxLogTrace( kicadTraceKeyEvent, wxS( "TOOL_DISPATCHER::DispatchWxEvent %s" ), dump( *ke ) );
631
632 // Do not process wxEVT_CHAR_HOOK for a shift-modified key, as ACTION_MANAGER::RunHotKey
633 // will run the un-shifted key and that's not what we want. Wait to get the translated
634 // key from wxEVT_CHAR.
635 // See https://gitlab.com/kicad/code/kicad/-/issues/1809
636 if( type == wxEVT_CHAR_HOOK && ke->GetModifiers() == wxMOD_SHIFT )
637 {
638 aEvent.Skip();
639 return;
640 }
641
642 keyIsEscape = ( ke->GetKeyCode() == WXK_ESCAPE );
643
644 // When escape is pressed shortly after a mouse click, the keyboard event can be
645 // processed before the mouse button release event. Flush any pending clicks first
646 // to ensure proper event ordering.
647 if( keyIsEscape )
649
650 if( KIUI::IsInputControlFocused( focus ) )
651 {
652 bool enabled = KIUI::IsInputControlEditable( focus );
653
654 // Never process key events for tools when a text entry has focus
655 if( enabled )
656 {
657 aEvent.Skip();
658 return;
659 }
660 // Even if not enabled, allow a copy out
661 else if( ke->GetModifiers() == wxMOD_CONTROL && ke->GetKeyCode() == 'C' )
662 {
663 aEvent.Skip();
664 return;
665 }
666 }
667
668 evt = GetToolEvent( ke, &keyIsSpecial );
669
670 // Drop backlogged OS key auto-repeat events that arrive after the key was released so a
671 // slow hotkey action (e.g. Rotate on a large selection) stops when the key is let go
672 // instead of running the whole queued burst. Only ordinary hotkey presses are filtered;
673 // cancel (escape) is always honoured.
674 if( evt && !evt->IsCancel() && !keyIsEscape && isStaleAutoRepeat( *ke ) )
675 {
676 evt.reset();
677 droppedStaleAutoRepeat = true;
678 }
679
680 if( evt && !evt->IsCancel() && m_toolMgr->GetViewControls() )
681 {
682 evt->SetMousePosition( m_lastMousePos );
683 evt->SetHasPosition( true );
684 }
685 }
686 else if( type == wxEVT_MENU_OPEN || type == wxEVT_MENU_CLOSE || type == wxEVT_MENU_HIGHLIGHT )
687 {
688 wxMenuEvent* tmp = dynamic_cast<wxMenuEvent*>( &aEvent );
689
690 // Something is amiss if the event has these types and isn't a menu event, so bail out on
691 // its processing
692 if( !tmp )
693 {
694 aEvent.Skip();
695 return;
696 }
697
698 wxMenuEvent& menuEvent = *tmp;
699
700#if wxCHECK_VERSION( 3, 2, 0 )
701 // Forward the event to the menu for processing
702 if( ACTION_MENU* currentMenu = dynamic_cast<ACTION_MENU*>( menuEvent.GetMenu() ) )
703 currentMenu->OnMenuEvent( menuEvent );
704#else
705 //
706 // wxWidgets has several issues that we have to work around:
707 //
708 // 1) wxWidgets 3.0.x Windows has a bug where wxEVT_MENU_OPEN and wxEVT_MENU_HIGHLIGHT
709 // events are not captured by the ACTON_MENU menus. So we forward them here.
710 // (FWIW, this one is fixed in wxWidgets 3.1.x.)
711 //
712 // 2) wxWidgets doesn't pass the menu pointer for wxEVT_MENU_HIGHLIGHT events. So we
713 // store the menu pointer from the wxEVT_MENU_OPEN call.
714 //
715 // 3) wxWidgets has no way to tell whether a command is from a menu selection or a
716 // hotkey. So we keep track of menu highlighting so we can differentiate.
717 //
718
719 static ACTION_MENU* currentMenu;
720
721 if( type == wxEVT_MENU_OPEN )
722 {
723 currentMenu = dynamic_cast<ACTION_MENU*>( menuEvent.GetMenu() );
724
725 if( currentMenu )
726 currentMenu->OnMenuEvent( menuEvent );
727 }
728 else if( type == wxEVT_MENU_HIGHLIGHT )
729 {
730 if( currentMenu )
731 currentMenu->OnMenuEvent( menuEvent );
732 }
733 else if( type == wxEVT_MENU_CLOSE )
734 {
735 if( currentMenu )
736 currentMenu->OnMenuEvent( menuEvent );
737
738 currentMenu = nullptr;
739 }
740#endif
741
742 aEvent.Skip();
743 }
744
745 bool handled = false;
746
747 if( evt )
748 {
749 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_DISPATCHER::DispatchWxEvent %s" ),
750 evt->Format() );
751
752 handled = m_toolMgr->ProcessEvent( *evt );
753
754 wxLogTrace( kicadTraceToolStack,
755 wxS( "TOOL_DISPATCHER::DispatchWxEvent - Handled: %s %s" ),
756 ( handled ? wxS( "true" ) : wxS( "false" ) ), evt->Format() );
757 }
758
759 // pass the event to the GUI, it might still be interested in it
760 // Note wxEVT_CHAR_HOOK event is already skipped for special keys not used by KiCad
761 // and wxEVT_LEFT_DOWN must be always Skipped.
762 //
763 // On OS X, key events are always meant to be caught. An uncaught key event is assumed
764 // to be a user input error by OS X (as they are pressing keys in a context where nothing
765 // is there to catch the event). This annoyingly makes OS X beep and/or flash the screen
766 // in Pcbnew and the footprint editor any time a hotkey is used. The correct procedure is
767 // to NOT pass wxEVT_CHAR events to the GUI under OS X.
768 //
769 // On Windows, avoid to call wxEvent::Skip for special keys because some keys
770 // (PAGE_UP, PAGE_DOWN) have predefined actions (like move thumbtrack cursor), and we do
771 // not want these actions executed (most are handled by KiCad)
772
773 // A dropped stale auto-repeat must be fully swallowed; skipping it would let wx menu
774 // accelerators re-run the same hotkey after the key was released.
775 if( ( !evt && !droppedStaleAutoRepeat ) || type == wxEVT_LEFT_DOWN )
776 aEvent.Skip();
777
778 // Not handled wxEVT_CHAR must be Skipped (sent to GUI).
779 // Otherwise accelerators and shortcuts in main menu or toolbars are not seen.
780 // Escape key presses are never skipped by the handler since they correspond to tool cancel
781 // events, and if they aren't skipped then they are propagated to other frames (which we
782 // don't want).
783 if( ( type == wxEVT_CHAR || type == wxEVT_CHAR_HOOK )
784 && !keyIsSpecial
785 && !handled
786 && !keyIsEscape
787 && !droppedStaleAutoRepeat )
788 {
789 aEvent.Skip();
790 }
791
792 wxLogTrace( kicadTraceToolStack, "TOOL_DISPATCHER::DispatchWxEvent - Wx event skipped: %s",
793 ( aEvent.GetSkipped() ? "true" : "false" ) );
794}
795
796// LocalWords: EDA CERN CHANGELOG txt Tomasz Wlostowski wxEvent WXK
797// LocalWords: MERCHANTABILITY bool upEvent downEvent touchpad Ctrl
798// LocalWords: wxEVENT isKeySpecialCode thumbtrack DispatchWxEvent
799// LocalWords: NUMPAD PAGEUP PAGEDOWN wxEVT EVT OSX ctrl ctlr kVK
800// LocalWords: unicode ESC keypress wxTimer iconized autopanning WX
801// 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.
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:148
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.