+
+ // we want to always get the same key code when the same key is
+ // pressed regardless of the state of the modifies, i.e. on a
+ // standard US keyboard pressing '5' or '%' ('5' key with
+ // Shift) should result in the same key code in OnKeyDown():
+ // '5' (although OnChar() will get either '5' or '%').
+ //
+ // to do it we first translate keysym to keycode (== scan code)
+ // and then back but always using the lower register
+ Display *dpy = (Display *)wxGetDisplay();
+ KeyCode keycode = XKeysymToKeycode(dpy, keysym);
+
+ wxLogTrace(TRACE_KEYS, _T("\t-> keycode %d"), keycode);
+
+ KeySym keysymNormalized = XKeycodeToKeysym(dpy, keycode, 0);
+
+ // use the normalized, i.e. lower register, keysym if we've
+ // got one
+ key_code = keysymNormalized ? keysymNormalized : keysym;
+
+ // as explained above, we want to have lower register key codes
+ // normally but for the letter keys we want to have the upper ones
+ //
+ // NB: don't use XConvertCase() here, we want to do it for letters
+ // only
+ key_code = toupper(key_code);
+ }
+ else // non ASCII key, what to do?
+ {
+ // by default, ignore it
+ key_code = 0;
+
+ // but if we have cached information from the last KEY_PRESS
+ if ( gdk_event->type == GDK_KEY_RELEASE )
+ {
+ // then reuse it
+ if ( keysym == s_lastKeyPress.keysym )
+ {
+ key_code = s_lastKeyPress.keycode;
+ }
+ }
+ }
+
+ if ( gdk_event->type == GDK_KEY_PRESS )
+ {
+ // remember it to be reused for KEY_UP event later
+ s_lastKeyPress.keysym = keysym;
+ s_lastKeyPress.keycode = key_code;
+ }
+ }
+
+ wxLogTrace(TRACE_KEYS, _T("\t-> wxKeyCode %ld"), key_code);
+
+ // sending unknown key events doesn't really make sense
+ if ( !key_code )
+ return FALSE;
+
+ // now fill all the other fields
+ wxFillOtherKeyEventFields(event, win, gdk_event);
+
+ event.m_keyCode = key_code;
+
+ return TRUE;
+}
+
+
+#ifdef __WXGTK20__
+struct wxGtkIMData
+{
+ GtkIMContext *context;
+ GdkEventKey *lastKeyEvent;
+
+ wxGtkIMData()
+ {
+ context = gtk_im_multicontext_new();
+ lastKeyEvent = NULL;
+ }
+ ~wxGtkIMData()
+ {
+ g_object_unref(context);
+ }
+};
+#endif
+
+static gint gtk_window_key_press_callback( GtkWidget *widget,
+ GdkEventKey *gdk_event,
+ wxWindow *win )
+{
+ DEBUG_MAIN_THREAD
+
+ if (g_isIdle)
+ wxapp_install_idle_handler();
+
+ if (!win->m_hasVMT)
+ return FALSE;
+ if (g_blockEventsOnDrag)
+ return FALSE;
+
+#ifdef __WXGTK20__
+ // We have to pass key press events through GTK+'s Input Method context
+ // object in order to get correct characters. By doing so, we loose the
+ // ability to let other GTK+'s handlers (namely, widgets' default signal
+ // handlers) handle the signal by returning false from this callback.
+ // Because GTK+ sends the events to parent widgets as well, we can't
+ // afford loosing it, otherwise native widgets inserted into wxPanel
+ // would break in subtle ways (e.g. spacebar would no longer toggle
+ // wxCheckButton's state). Therefore, we only pass the event to IM if it
+ // originated in this window's widget, which we detect by checking if we've
+ // seen the same event before (no events from children are lost this way,
+ // because gtk_window_key_press_callback is installed for native controls
+ // as well and the wxKeyEvent it creates propagates upwards).
+ static GdkEventKey s_lastEvent;
+
+ bool useIM = (win->m_imData != NULL) &&
+ memcmp(gdk_event, &s_lastEvent, sizeof(GdkEventKey)) != 0;
+
+ s_lastEvent = *gdk_event;
+#endif
+
+ wxKeyEvent event( wxEVT_KEY_DOWN );
+ if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
+ {
+ // unknown key pressed, ignore (the event would be useless anyhow)
+#ifdef __WXGTK20__
+ if ( useIM )
+ {
+ // it may be useful for the input method, though:
+ win->m_imData->lastKeyEvent = gdk_event;
+ bool ret = gtk_im_context_filter_keypress(win->m_imData->context,
+ gdk_event);
+ win->m_imData->lastKeyEvent = NULL;
+ return ret;
+ }
+#endif
+ return FALSE;
+ }
+
+ // Emit KEY_DOWN event
+ bool ret = win->GetEventHandler()->ProcessEvent( event );
+
+#if wxUSE_ACCEL
+ if (!ret)
+ {
+ wxWindowGTK *ancestor = win;
+ while (ancestor)
+ {
+ int command = ancestor->GetAcceleratorTable()->GetCommand( event );
+ if (command != -1)
+ {
+ wxCommandEvent command_event( wxEVT_COMMAND_MENU_SELECTED, command );
+ ret = ancestor->GetEventHandler()->ProcessEvent( command_event );
+ break;
+ }
+ if (ancestor->IsTopLevel())
+ break;