← Back to team overview

kicad-developers team mailing list archive

Re: [PATCH] Fix (Ctrl)+(ASCII control key) hotkey handling

 

Updated patch - quite a few changes. First I noticed I was inadvertently 
trapping some keys I shouldn't (try using Ctrl+S with the original 
patch...), and then I decided to tackle the old "bug" that certain key 
combinations, like Shift+Tab, cannot be used.

To make sure hotkeys are recognized equally everywhere, I took the code 
I originally wrote for the hotkey editor widget to handle wxEVT_CHAR and 
wxEVT_CHAR_HOOK keypress events "intelligently" and factored it out into 
a separate class to be used in other places. I then pulled it into 
EDA_DRAW_PANEL and EDA_DRAW_PANEL_GAL to replace the existing keycode 
rewriting code (which originally was just "if hotkey is in WXK_CONTROL_A 
through WXK_CONTROL_Z, remap to Ctrl + A..Z").

With this patch, all 'standard' combinations of keys on a US keyboard 
should be usable, support for international keys is the same as before.
Let me know if you find any combinations that don't work.

Please test! This is platform-dependent stuff - it's the same 
platform-dependent stuff as before, but I need to make sure I haven't 
added regressions. I've tested on Linux and Win10; more thorough testing 
even on those same platforms is welcome.


On Wed, Jan 20, 2016 at 10:44:36PM -0500, Chris Pavlina wrote:
> There is an old bug that people turned up while testing my new hotkey 
> editor, attached is a patch that fixes it. This patch pokes into the 
> main hotkey handling code, so I really want as many people to test it as 
> possible - it's a relatively minor bug, I don't want to go introducing 
> twelve regressions to fix one small bug. Here's what I want to keep an 
> eye out for:
> 
> - Hotkeys (Ctrl+Tab), (Tab), and (Ctrl+I) are all independent and 
>   distinguished from each other, both in the hotkey editor and in actual 
>   use. Make sure all of them work, and make sure none of them answers 
>   for the others.
> 
> - Hotkeys that are *not* handled by the main hotkey code (for example, 
>   menu keys like Alt+F to call up the File menu) are not affected.
> 
> - Behavior on OSX, with its weird Ctrl mapping, is not broken (or, at 
>   least, is no more broken than usual ;)
> 
> I have tested on Linux and Windows 10, though more thorough testing on 
> those platforms is welcome.
> 
> Patch/bug summary:
> 
> [PATCH] Fix (Ctrl)+(ASCII control key) hotkey handling
> 
> wxWidgets has quirks with how it handles these keys. For example, in
> wxEVT_CHAR, Ctrl+Tab and Ctrl+I are indistinguishable.
> 
> - Modify the special wxEVT_CHAR_HOOK handler from WIDGET_HOTKEY_LIST to
>   handle this case as well as the other funny cases it already handles.
> 
> - Factor this handler out into a function in hotkeys_basic.h for use
>   elsewhere.
> 
> - Add this handler to the central hotkey handler, remove existing
>   (buggy) ASCII control key handling.
> 
> Thanks for testing.
> 
> -- Chris
diff --git a/common/draw_panel.cpp b/common/draw_panel.cpp
index 063c5db..5418f4c 100644
--- a/common/draw_panel.cpp
+++ b/common/draw_panel.cpp
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2009 Jean-Pierre Charras, jean-pierre.charras@xxxxxxxxxxxxxxxxx
  * Copyright (C) 2007-2011 Wayne Stambaugh <stambaughw@xxxxxxxxxxx>
- * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -77,7 +77,7 @@ BEGIN_EVENT_TABLE( EDA_DRAW_PANEL, wxScrolledWindow )
 #endif
     EVT_MOUSE_EVENTS( EDA_DRAW_PANEL::OnMouseEvent )
     EVT_CHAR( EDA_DRAW_PANEL::OnKeyEvent )
-    EVT_CHAR_HOOK( EDA_DRAW_PANEL::OnCharHook )
+    EVT_CHAR_HOOK( EDA_DRAW_PANEL::OnKeyEvent )
     EVT_PAINT( EDA_DRAW_PANEL::OnPaint )
     EVT_ERASE_BACKGROUND( EDA_DRAW_PANEL::OnEraseBackground )
     EVT_SCROLLWIN( EDA_DRAW_PANEL::OnScroll )
@@ -1381,59 +1381,27 @@ void EDA_DRAW_PANEL::OnMouseEvent( wxMouseEvent& event )
 }
 
 
-
-void EDA_DRAW_PANEL::OnCharHook( wxKeyEvent& event )
-{
-    event.Skip();
-}
-
 void EDA_DRAW_PANEL::OnKeyEvent( wxKeyEvent& event )
 {
-    int localkey;
-    wxPoint pos;
+    if( !m_hkfilter.should_process( event ) )
+    {
+        return;
+    }
 
-    localkey = event.GetKeyCode();
+    wxPoint pos;
+    EDA_KEY key = MapKeypressToKeycode( event );
 
-    switch( localkey )
+    if( key == GR_KEY_NONE )
     {
-    default:
-        break;
-
-    case WXK_ESCAPE:
+        // escape key
         m_abortRequest = true;
 
         if( IsMouseCaptured() )
             EndMouseCapture();
         else
             EndMouseCapture( ID_NO_TOOL_SELECTED, m_defaultCursor, wxEmptyString );
-        break;
     }
 
-    /* Normalize keys code to easily handle keys from Ctrl+A to Ctrl+Z
-     * They have an ascii code from 1 to 27 remapped
-     * to GR_KB_CTRL + 'A' to GR_KB_CTRL + 'Z'
-     */
-    if( event.ControlDown() && localkey >= WXK_CONTROL_A && localkey <= WXK_CONTROL_Z )
-        localkey += 'A' - 1;
-
-    /* Disallow shift for keys that have two keycodes on them (e.g. number and
-     * punctuation keys) leaving only the "letter keys" of A-Z.
-     * Then, you can have, e.g. Ctrl-5 and Ctrl-% (GB layout)
-     * and Ctrl-( and Ctrl-5 (FR layout).
-     * Otherwise, you'd have to have to say Ctrl-Shift-5 on a FR layout
-     */
-    bool keyIsLetter = ( localkey >= 'A' && localkey <= 'Z' ) ||
-                       ( localkey >= 'a' && localkey <= 'z' );
-
-    if( event.ShiftDown() && ( keyIsLetter || localkey > 256 ) )
-        localkey |= GR_KB_SHIFT;
-
-    if( event.ControlDown() )
-        localkey |= GR_KB_CTRL;
-
-    if( event.AltDown() )
-        localkey |= GR_KB_ALT;
-
     INSTALL_UNBUFFERED_DC( DC, this );
 
     // Some key commands use the current mouse position: refresh it.
@@ -1444,8 +1412,10 @@ void EDA_DRAW_PANEL::OnKeyEvent( wxKeyEvent& event )
 
     GetParent()->SetMousePosition( pos );
 
-    if( !GetParent()->GeneralControl( &DC, pos, localkey ) )
-        event.Skip();
+    if( !GetParent()->GeneralControl( &DC, pos, key ) )
+    {
+        m_hkfilter.skip( event );
+    }
 }
 
 
diff --git a/common/draw_panel_gal.cpp b/common/draw_panel_gal.cpp
index c060ab5..50bd478 100644
--- a/common/draw_panel_gal.cpp
+++ b/common/draw_panel_gal.cpp
@@ -82,7 +82,7 @@ EDA_DRAW_PANEL_GAL::EDA_DRAW_PANEL_GAL( wxWindow* aParentWindow, wxWindowID aWin
         wxEVT_LEFT_UP, wxEVT_LEFT_DOWN, wxEVT_LEFT_DCLICK,
         wxEVT_RIGHT_UP, wxEVT_RIGHT_DOWN, wxEVT_RIGHT_DCLICK,
         wxEVT_MIDDLE_UP, wxEVT_MIDDLE_DOWN, wxEVT_MIDDLE_DCLICK,
-        wxEVT_MOTION, wxEVT_MOUSEWHEEL, wxEVT_CHAR,
+        wxEVT_MOTION, wxEVT_MOUSEWHEEL, wxEVT_CHAR, wxEVT_CHAR_HOOK,
 #ifdef USE_OSX_MAGNIFY_EVENT
         wxEVT_MAGNIFY,
 #endif
diff --git a/common/hotkeys_basic.cpp b/common/hotkeys_basic.cpp
index 04dfd7f..171933c 100644
--- a/common/hotkeys_basic.cpp
+++ b/common/hotkeys_basic.cpp
@@ -509,7 +509,7 @@ void DisplayHotkeyList( EDA_BASE_FRAME* aFrame, struct EDA_HOTKEY_CONFIG* aDescL
  * @param aList = pointer to a EDA_HOTKEY list of commands
  * @return the corresponding EDA_HOTKEY pointer from the EDA_HOTKEY List
  */
-EDA_HOTKEY* GetDescriptorFromHotkey( int aKey, EDA_HOTKEY** aList )
+EDA_HOTKEY* GetDescriptorFromHotkey( EDA_KEY aKey, EDA_HOTKEY** aList )
 {
     for( ; *aList != NULL; aList++ )
     {
@@ -844,3 +844,142 @@ void AddHotkeyConfigMenu( wxMenu* aMenu )
                  _( "Hotkeys configuration and preferences" ),
                  KiBitmap( hotkeys_xpm ) );
 }
+
+
+EDA_HOTKEY_HOOK_FILTER::EDA_HOTKEY_HOOK_FILTER()
+{
+    m_skip_evtchar = false;
+}
+
+
+bool EDA_HOTKEY_HOOK_FILTER::should_process_in_hook( wxKeyEvent& aEvent )
+{
+    // On certain platforms, EVT_CHAR_HOOK is the only handler that receives
+    // certain "special" keys. However, it doesn't always receive "normal"
+    // keys correctly. For example, with a US keyboard, it sees ? as shift+/.
+    //
+    // Untangling these incorrect keys would be too much trouble, so we bind
+    // both events, and simply skip the EVT_CHAR_HOOK if it receives a
+    // "normal" key.
+
+    const enum wxKeyCode skipped_keys[] =
+    {
+        WXK_NONE,    WXK_SHIFT,  WXK_ALT, WXK_CONTROL, WXK_CAPITAL,
+        WXK_NUMLOCK, WXK_SCROLL, WXK_RAW_CONTROL
+    };
+
+    int key = aEvent.GetKeyCode();
+
+    for( size_t i = 0; i < DIM( skipped_keys ); ++i )
+    {
+        if( key == skipped_keys[i] )
+            return false;
+    }
+
+    // Some control keys map to Ctrl+letter (e.g. Tab is Ctrl+I) in OnChar,
+    // but not OnCharHook. This leads to incorrect recognition of
+    // Ctrl+controlkey (e.g. Ctrl+Tab is seen as just Ctrl+I). Detect
+    // this and handle here instead.
+
+    bool ctrl_alphakey = ( ( key >= 'A' && key <= 'Z' ) || ( key >= 'a' && key <= 'z' ) )
+            && aEvent.ControlDown();
+
+    bool ctrl_ctrlkey = ( key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z )
+            && aEvent.ControlDown();
+
+    return !( key <= 255 && isprint( key ) && !isspace( key ) && !ctrl_ctrlkey && !ctrl_alphakey );
+}
+
+
+void EDA_HOTKEY_HOOK_FILTER::allow_char_hook_propagation( wxKeyEvent& aEvent )
+{
+    // Let EVT_CHAR handle this one
+    aEvent.DoAllowNextEvent();
+
+    skip( aEvent );
+}
+
+
+bool EDA_HOTKEY_HOOK_FILTER::should_process( wxKeyEvent& aEvent )
+{
+    if( aEvent.GetEventType() == wxEVT_CHAR_HOOK )
+    {
+        m_skip_evtchar = should_process_in_hook( aEvent );
+
+        // Windows quirk: do NOT let the \t wxEVT_CHAR_HOOK produce
+        // wxEVT_CHAR. In some dialogs (particularly empty ones, see
+        // the dialog used for hotkey detection in WIDGET_HOTKEY_LIST),
+        // this will cause the application to freeze.
+        if( aEvent.GetKeyCode() != '\t' )
+            allow_char_hook_propagation( aEvent );
+        return m_skip_evtchar;
+    }
+    else
+    {
+        wxASSERT( aEvent.GetEventType() == wxEVT_CHAR );
+        if( !m_skip_evtchar )
+        {
+            return true;
+        }
+        else
+        {
+            aEvent.Skip();
+            return false;
+        }
+    }
+}
+
+
+void EDA_HOTKEY_HOOK_FILTER::skip( wxKeyEvent& aEvent )
+{
+    if( aEvent.GetEventType() == wxEVT_CHAR_HOOK )
+    {
+        // On Windows, wxEvent::Skip must NOT be called.
+        // On Linux and OSX, wxEvent::Skip MUST be called.
+        // No, I don't know why.
+#ifndef __WXMSW__
+        aEvent.Skip();
+#endif
+    }
+    else
+    {
+        aEvent.Skip();
+    }
+
+}
+
+
+EDA_KEY MapKeypressToKeycode( const wxKeyEvent& aEvent )
+{
+    EDA_KEY key = ( EDA_KEY ) aEvent.GetKeyCode();
+
+    if( key == WXK_ESCAPE )
+    {
+        return GR_KEY_NONE;
+    }
+    else
+    {
+        if( key >= 'a' && key <= 'z' )    // convert to uppercase
+            key = key + ('A' - 'a');
+
+        /* Disallow shift for keys that have two keycodes on them (e.g. number and
+         * punctuation keys) leaving only the "letter keys" of A-Z.
+         * Then, you can have, e.g. Ctrl-5 and Ctrl-% (GB layout)
+         * and Ctrl-( and Ctrl-5 (FR layout).
+         * Otherwise, you'd have to have to say Ctrl-Shift-5 on a FR layout
+         */
+        bool keyIsLetter = key >= 'A' && key <= 'Z';
+        bool keyIsControl = key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z;
+
+        if( aEvent.ShiftDown() && ( keyIsLetter || keyIsControl || key > 256 ) )
+            key |= GR_KB_SHIFT;
+
+        if( aEvent.ControlDown() )
+            key |= GR_KB_CTRL;
+
+        if( aEvent.AltDown() )
+            key |= GR_KB_ALT;
+
+        return key;
+    }
+}
diff --git a/common/tool/tool_dispatcher.cpp b/common/tool/tool_dispatcher.cpp
index 97127ce..99c6a28 100644
--- a/common/tool/tool_dispatcher.cpp
+++ b/common/tool/tool_dispatcher.cpp
@@ -2,6 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2013 CERN
+ * Copyright (C) 2016 KiCad Developers, see CHANGELOG.TXT for contributors.
  * @author Tomasz Wlostowski <tomasz.wlostowski@xxxxxxx>
  *
  * This program is free software; you can redistribute it and/or
@@ -291,32 +292,20 @@ void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent )
     }
 
     // Keyboard handling
-    else if( type == wxEVT_CHAR )
+    else if( type == wxEVT_CHAR || type == wxEVT_CHAR_HOOK )
     {
         wxKeyEvent* ke = static_cast<wxKeyEvent*>( &aEvent );
-        int key = ke->GetKeyCode();
-        int mods = decodeModifiers( ke );
 
-        if( mods & MD_CTRL )
+        if( m_hkfilter.should_process( *ke ) )
         {
-#if !wxCHECK_VERSION( 2, 9, 0 )
-            // I really look forward to the day when we will use only one version of wxWidgets..
-            const int WXK_CONTROL_A = 1;
-            const int WXK_CONTROL_Z = 26;
-#endif
+            int key = ke->GetKeyCode();
+            int mods = decodeModifiers( ke );
 
-            // wxWidgets have a quirk related to Ctrl+letter hot keys handled by CHAR_EVT
-            // http://docs.wxwidgets.org/trunk/classwx_key_event.html:
-            // "char events for ASCII letters in this case carry codes corresponding to the ASCII
-            // value of Ctrl-Latter, i.e. 1 for Ctrl-A, 2 for Ctrl-B and so on until 26 for Ctrl-Z."
-            if( key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z )
-                key += 'A' - 1;
+            if( key == WXK_ESCAPE ) // ESC is the special key for cancelling tools
+                evt = TOOL_EVENT( TC_COMMAND, TA_CANCEL_TOOL );
+            else
+                evt = TOOL_EVENT( TC_KEYBOARD, TA_KEY_PRESSED, key | mods );
         }
-
-        if( key == WXK_ESCAPE ) // ESC is the special key for cancelling tools
-            evt = TOOL_EVENT( TC_COMMAND, TA_CANCEL_TOOL );
-        else
-            evt = TOOL_EVENT( TC_KEYBOARD, TA_KEY_PRESSED, key | mods );
     }
 
     if( evt )
diff --git a/common/widgets/widget_hotkey_list.cpp b/common/widgets/widget_hotkey_list.cpp
index 3813670..ecc3e4b 100644
--- a/common/widgets/widget_hotkey_list.cpp
+++ b/common/widgets/widget_hotkey_list.cpp
@@ -84,6 +84,7 @@ public:
 class HK_PROMPT_DIALOG : public DIALOG_SHIM
 {
     wxKeyEvent m_event;
+    EDA_HOTKEY_HOOK_FILTER m_hkfilter;
 
 public:
     HK_PROMPT_DIALOG( wxWindow* aParent, wxWindowID aId, const wxString& aTitle,
@@ -145,61 +146,21 @@ public:
         // Binding both EVT_CHAR and EVT_CHAR_HOOK ensures that all key events,
         // including specials like Tab and Return, are received, particularly
         // on MSW.
-        panel->Bind( wxEVT_CHAR, &HK_PROMPT_DIALOG::OnChar, this );
-        panel->Bind( wxEVT_CHAR_HOOK, &HK_PROMPT_DIALOG::OnCharHook, this );
+        panel->Bind( wxEVT_CHAR, &HK_PROMPT_DIALOG::OnKeyEvent, this );
+        panel->Bind( wxEVT_CHAR_HOOK, &HK_PROMPT_DIALOG::OnKeyEvent, this );
     }
 
 
-    void OnCharHook( wxKeyEvent& aEvent )
+    void OnKeyEvent( wxKeyEvent& aEvent )
     {
-        // On certain platforms, EVT_CHAR_HOOK is the only handler that receives
-        // certain "special" keys. However, it doesn't always receive "normal"
-        // keys correctly. For example, with a US keyboard, it sees ? as shift+/.
-        //
-        // Untangling these incorrect keys would be too much trouble, so we bind
-        // both events, and simply skip the EVT_CHAR_HOOK if it receives a
-        // "normal" key.
-
-        const enum wxKeyCode skipped_keys[] =
+        if( m_hkfilter.should_process( aEvent ) )
         {
-            WXK_NONE,    WXK_SHIFT,  WXK_ALT, WXK_CONTROL, WXK_CAPITAL,
-            WXK_NUMLOCK, WXK_SCROLL, WXK_RAW_CONTROL
-        };
-
-        int key = aEvent.GetKeyCode();
-
-        for( size_t i = 0; i < sizeof( skipped_keys ) / sizeof( skipped_keys[0] ); ++i )
-        {
-            if( key == skipped_keys[i] )
-                return;
-        }
-
-        if( key <= 255 && isprint( key ) && !isspace( key ) )
-        {
-            // Let EVT_CHAR handle this one
-            aEvent.DoAllowNextEvent();
-
-            // On Windows, wxEvent::Skip must NOT be called.
-            // On Linux and OSX, wxEvent::Skip MUST be called.
-            // No, I don't know why.
-#ifndef __WXMSW__
-            aEvent.Skip();
-#endif
-        }
-        else
-        {
-            OnChar( aEvent );
+            m_event = aEvent;
+            EndFlexible( wxID_OK );
         }
     }
 
 
-    void OnChar( wxKeyEvent& aEvent )
-    {
-        m_event = aEvent;
-        EndFlexible( wxID_OK );
-    }
-
-
     /**
      * End the dialog whether modal or quasimodal
      */
@@ -302,7 +263,7 @@ void WIDGET_HOTKEY_LIST::EditItem( wxTreeListItem aItem )
     wxString    current_key = GetItemText( aItem, 1 );
 
     wxKeyEvent key_event = HK_PROMPT_DIALOG::PromptForKey( GetParent(), name, current_key );
-    long key = MapKeypressToKeycode( key_event );
+    EDA_KEY key = MapKeypressToKeycode( key_event );
 
     if( hkdata && key )
     {
@@ -438,7 +399,7 @@ void WIDGET_HOTKEY_LIST::OnSize( wxSizeEvent& aEvent )
 }
 
 
-bool WIDGET_HOTKEY_LIST::CheckKeyConflicts( long aKey, const wxString& aSectionTag,
+bool WIDGET_HOTKEY_LIST::CheckKeyConflicts( EDA_KEY aKey, const wxString& aSectionTag,
         EDA_HOTKEY** aConfKey, EDA_HOTKEY_CONFIG** aConfSect )
 {
     EDA_HOTKEY* conflicting_key = NULL;
@@ -632,43 +593,3 @@ bool WIDGET_HOTKEY_LIST::TransferDataFromControl()
 
     return true;
 }
-
-
-long WIDGET_HOTKEY_LIST::MapKeypressToKeycode( const wxKeyEvent& aEvent )
-{
-    long key = aEvent.GetKeyCode();
-
-    if( key == WXK_ESCAPE )
-    {
-        return 0;
-    }
-    else
-    {
-        if( key >= 'a' && key <= 'z' )    // convert to uppercase
-            key = key + ('A' - 'a');
-
-        // Remap Ctrl A (=1+GR_KB_CTRL) to Ctrl Z(=26+GR_KB_CTRL)
-        // to GR_KB_CTRL+'A' .. GR_KB_CTRL+'Z'
-        if( aEvent.ControlDown() && key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z )
-            key += 'A' - 1;
-
-        /* Disallow shift for keys that have two keycodes on them (e.g. number and
-         * punctuation keys) leaving only the "letter keys" of A-Z.
-         * Then, you can have, e.g. Ctrl-5 and Ctrl-% (GB layout)
-         * and Ctrl-( and Ctrl-5 (FR layout).
-         * Otherwise, you'd have to have to say Ctrl-Shift-5 on a FR layout
-         */
-        bool keyIsLetter = key >= 'A' && key <= 'Z';
-
-        if( aEvent.ShiftDown() && ( keyIsLetter || key > 256 ) )
-            key |= GR_KB_SHIFT;
-
-        if( aEvent.ControlDown() )
-            key |= GR_KB_CTRL;
-
-        if( aEvent.AltDown() )
-            key |= GR_KB_ALT;
-
-        return key;
-    }
-}
diff --git a/include/class_drawpanel.h b/include/class_drawpanel.h
index 8c386f0..571346b 100644
--- a/include/class_drawpanel.h
+++ b/include/class_drawpanel.h
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@xxxxxxxxxxxxxxxxxx
  * Copyright (C) 2011 Wayne Stambaugh <stambaughw@xxxxxxxxxxx>
- * Copyright (C) 1992-2011 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -34,7 +34,7 @@
 #include <colors.h>
 #include <base_struct.h>
 #include <gr_basic.h>
-
+#include <hotkeys_basic.h>
 
 class BASE_SCREEN;
 class PCB_SCREEN;
@@ -116,6 +116,9 @@ private:
     /// >= 0 (or >= n) if a block can start
     int     m_canStartBlock;
 
+    /// Hotkey event filter, for handling key events
+    EDA_HOTKEY_HOOK_FILTER m_hkfilter;
+
 public:
 
     EDA_DRAW_PANEL( EDA_DRAW_FRAME* parent, int id, const wxPoint& pos, const wxSize& size );
@@ -270,7 +273,6 @@ public:
     void OnMouseEntering( wxMouseEvent& aEvent );
     void OnMouseLeaving( wxMouseEvent& event );
     void OnKeyEvent( wxKeyEvent& event );
-    void OnCharHook( wxKeyEvent& event );
 
     void OnPan( wxCommandEvent& event );
 
diff --git a/include/hotkeys_basic.h b/include/hotkeys_basic.h
index 089fcce..abd62ba 100644
--- a/include/hotkeys_basic.h
+++ b/include/hotkeys_basic.h
@@ -57,7 +57,7 @@ extern wxString g_CommonSectionTag;
 class EDA_HOTKEY
 {
 public:
-    int      m_KeyCode;      // Key code (ascii value for ascii keys or wxWidgets code for function key
+    EDA_KEY  m_KeyCode;      // Key code (ascii value for ascii keys or wxWidgets code for function key
     wxString m_InfoMsg;      // info message.
     int      m_Idcommand;    // internal id for the corresponding command (see hotkey_id_commnand list)
     int      m_IdMenuEvent;  // id to call the corresponding event (if any) (see id.h)
@@ -203,7 +203,7 @@ void DisplayHotkeyList( EDA_BASE_FRAME* aFrame, struct EDA_HOTKEY_CONFIG* aList
  * @param aList = pointer to a EDA_HOTKEY list of commands
  * @return the corresponding EDA_HOTKEY pointer from the EDA_HOTKEY List
  */
-EDA_HOTKEY* GetDescriptorFromHotkey( int aKey, EDA_HOTKEY** aList );
+EDA_HOTKEY* GetDescriptorFromHotkey( EDA_KEY aKey, EDA_HOTKEY** aList );
 
 /**
  * Function GetDescriptorFromCommand
@@ -244,4 +244,57 @@ enum common_hotkey_id_commnand {
     HK_COMMON_END
 };
 
+/**
+ * Class EDA_HOTKEY_HOOK_FILTER
+ *
+ * Check if a key received by wxEVT_CHAR_HOOK is a "special" key that has to be
+ * handled at wxEVT_CHAR_HOOK rather than wxEVT_CHAR. This is the case for
+ * combinations like Ctrl+Tab, which are indistinguishable from Ctrl+I in
+ * wxEVT_CHAR. Other events must be handled in wxEVT_CHAR instead, as
+ * wxEVT_CHAR_HOOK handled them before input method filters have been applied.
+ * An example implementation is:
+ *
+ *      void OnKeyEvent( wxKeyEvent& aEvent )   // reponds to wxEVT_CHAR and wxEVT_CHAR_HOOK
+ *      {
+ *          static EDA_HOTKEY_HOOK_FILTER filter;
+ *
+ *          if( !filter.should_process( aEvent ) )
+ *          {
+ *              return;
+ *          }
+ *
+ *          ......
+ *
+ *          if( something )
+ *          {
+ *              filter.skip( aEvent );
+ *          }
+ *      }
+ *
+ */
+class EDA_HOTKEY_HOOK_FILTER
+{
+    bool m_skip_evtchar;
+
+    static bool should_process_in_hook( wxKeyEvent &aEvent );
+
+    static void allow_char_hook_propagation( wxKeyEvent &aEvent );
+
+public:
+    EDA_HOTKEY_HOOK_FILTER();
+
+    /**
+     * Analyze an event and determine whether it should be processed.
+     */
+    bool should_process( wxKeyEvent& aEvent );
+
+    /**
+     * Skip event. Use this instead of wxEvent::Skip, as the behavior
+     * of that for wxEVT_CHAR_HOOK is platform-dependent.
+     */
+    static void skip( wxKeyEvent& aEvent );
+};
+
+EDA_KEY MapKeypressToKeycode( const wxKeyEvent& aEvent );
+
 #endif // HOTKEYS_BASIC_H
diff --git a/include/tool/tool_dispatcher.h b/include/tool/tool_dispatcher.h
index cf6e852..ff1e0b5 100644
--- a/include/tool/tool_dispatcher.h
+++ b/include/tool/tool_dispatcher.h
@@ -2,6 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2013 CERN
+ * Copyright (C) 2016 KiCad Developers, see CHANGELOG.TXT for contributors.
  * @author Tomasz Wlostowski <tomasz.wlostowski@xxxxxxx>
  *
  * This program is free software; you can redistribute it and/or
@@ -26,6 +27,7 @@
 #define __TOOL_DISPATCHER_H
 
 #include <vector>
+#include <hotkeys_basic.h>
 #include <wx/event.h>
 #include <tool/tool_event.h>
 
@@ -130,6 +132,9 @@ private:
 
     ///> Instance of tool manager that cooperates with the dispatcher.
     TOOL_MANAGER* m_toolMgr;
+
+    ///> Hotkey filter instance for handling keypress events
+    EDA_HOTKEY_HOOK_FILTER m_hkfilter;
 };
 
 #endif
diff --git a/include/widgets/widget_hotkey_list.h b/include/widgets/widget_hotkey_list.h
index bfaf010..a29264d 100644
--- a/include/widgets/widget_hotkey_list.h
+++ b/include/widgets/widget_hotkey_list.h
@@ -130,7 +130,7 @@ protected:
      * @param aConfKey - if not NULL, outparam getting the key this one conflicts with
      * @param aConfSect - if not NULL, outparam getting the section this one conflicts with
      */
-    bool CheckKeyConflicts( long aKey, const wxString& aSectionTag,
+    bool CheckKeyConflicts( EDA_KEY aKey, const wxString& aSectionTag,
             EDA_HOTKEY** aConfKey, EDA_HOTKEY_CONFIG** aConfSect );
 
     /**
@@ -191,12 +191,6 @@ public:
      * @return true iff the operation was successful
      */
     bool TransferDataFromControl();
-
-    /**
-     * Static method MapKeypressToKeycode
-     * Map a keypress event to the correct key code for use as a hotkey.
-     */
-    static long MapKeypressToKeycode( const wxKeyEvent& aEvent );
 };
 
 #endif // __widget_hotkey_list__

Follow ups

References