more fixes to keypress handling in wxGTK:
1. don't eat unprocessed events originating from child widgets 2. tell IM context about focus changes 3. set wxKeyEvent modifiers information from last GdkEventKey leading to IM's commit signal git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@28033 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -2367,11 +2367,6 @@ typedef GtkWidget *WXWidget;
|
|||||||
#define GTK_CLASS_TYPE(klass) ((klass)->type)
|
#define GTK_CLASS_TYPE(klass) ((klass)->type)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __WXGTK20__
|
|
||||||
/* Input method thing */
|
|
||||||
typedef struct _GtkIMContext GtkIMContext;
|
|
||||||
#endif /* __WXGTK20__ */
|
|
||||||
|
|
||||||
#endif /* __WXGTK__ */
|
#endif /* __WXGTK__ */
|
||||||
|
|
||||||
#if defined(__WXGTK20__) || (defined(__WXX11__) && wxUSE_UNICODE)
|
#if defined(__WXGTK20__) || (defined(__WXX11__) && wxUSE_UNICODE)
|
||||||
|
@@ -15,6 +15,10 @@
|
|||||||
#pragma interface
|
#pragma interface
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// helper structure that holds class that holds GtkIMContext object and
|
||||||
|
// some additional data needed for key events processing
|
||||||
|
struct wxGtkIMData;
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// callback definition for inserting a window (internal)
|
// callback definition for inserting a window (internal)
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@@ -193,7 +197,7 @@ public:
|
|||||||
GtkWidget *m_focusWidget;
|
GtkWidget *m_focusWidget;
|
||||||
|
|
||||||
#ifdef __WXGTK20__
|
#ifdef __WXGTK20__
|
||||||
GtkIMContext *m_imContext;
|
wxGtkIMData *m_imData;
|
||||||
#else
|
#else
|
||||||
#if HAVE_XIM
|
#if HAVE_XIM
|
||||||
// XIM support for wxWidgets
|
// XIM support for wxWidgets
|
||||||
|
@@ -15,6 +15,10 @@
|
|||||||
#pragma interface
|
#pragma interface
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// helper structure that holds class that holds GtkIMContext object and
|
||||||
|
// some additional data needed for key events processing
|
||||||
|
struct wxGtkIMData;
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// callback definition for inserting a window (internal)
|
// callback definition for inserting a window (internal)
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@@ -193,7 +197,7 @@ public:
|
|||||||
GtkWidget *m_focusWidget;
|
GtkWidget *m_focusWidget;
|
||||||
|
|
||||||
#ifdef __WXGTK20__
|
#ifdef __WXGTK20__
|
||||||
GtkIMContext *m_imContext;
|
wxGtkIMData *m_imData;
|
||||||
#else
|
#else
|
||||||
#if HAVE_XIM
|
#if HAVE_XIM
|
||||||
// XIM support for wxWidgets
|
// XIM support for wxWidgets
|
||||||
|
@@ -965,6 +965,30 @@ static inline bool wxIsAsciiKeysym(KeySym ks)
|
|||||||
return ks < 256;
|
return ks < 256;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void wxFillOtherKeyEventFields(wxKeyEvent& event,
|
||||||
|
wxWindowGTK *win,
|
||||||
|
GdkEventKey *gdk_event)
|
||||||
|
{
|
||||||
|
int x = 0;
|
||||||
|
int y = 0;
|
||||||
|
GdkModifierType state;
|
||||||
|
if (gdk_event->window)
|
||||||
|
gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
|
||||||
|
|
||||||
|
event.SetTimestamp( gdk_event->time );
|
||||||
|
event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK) != 0;
|
||||||
|
event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK) != 0;
|
||||||
|
event.m_altDown = (gdk_event->state & GDK_MOD1_MASK) != 0;
|
||||||
|
event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK) != 0;
|
||||||
|
event.m_scanCode = gdk_event->keyval;
|
||||||
|
event.m_rawCode = (wxUint32) gdk_event->keyval;
|
||||||
|
event.m_rawFlags = 0;
|
||||||
|
event.m_x = x;
|
||||||
|
event.m_y = y;
|
||||||
|
event.SetEventObject( win );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
wxTranslateGTKKeyEventToWx(wxKeyEvent& event,
|
wxTranslateGTKKeyEventToWx(wxKeyEvent& event,
|
||||||
wxWindowGTK *win,
|
wxWindowGTK *win,
|
||||||
@@ -1060,29 +1084,32 @@ wxTranslateGTKKeyEventToWx(wxKeyEvent& event,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
// now fill all the other fields
|
// now fill all the other fields
|
||||||
int x = 0;
|
wxFillOtherKeyEventFields(event, win, gdk_event);
|
||||||
int y = 0;
|
|
||||||
GdkModifierType state;
|
|
||||||
if (gdk_event->window)
|
|
||||||
gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
|
|
||||||
|
|
||||||
event.SetTimestamp( gdk_event->time );
|
|
||||||
event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK) != 0;
|
|
||||||
event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK) != 0;
|
|
||||||
event.m_altDown = (gdk_event->state & GDK_MOD1_MASK) != 0;
|
|
||||||
event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK) != 0;
|
|
||||||
event.m_keyCode = key_code;
|
event.m_keyCode = key_code;
|
||||||
event.m_scanCode = gdk_event->keyval;
|
|
||||||
event.m_rawCode = (wxUint32) gdk_event->keyval;
|
|
||||||
event.m_rawFlags = 0;
|
|
||||||
event.m_x = x;
|
|
||||||
event.m_y = y;
|
|
||||||
event.SetEventObject( win );
|
|
||||||
|
|
||||||
return TRUE;
|
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,
|
static gint gtk_window_key_press_callback( GtkWidget *widget,
|
||||||
GdkEventKey *gdk_event,
|
GdkEventKey *gdk_event,
|
||||||
wxWindow *win )
|
wxWindow *win )
|
||||||
@@ -1096,23 +1123,43 @@ static gint gtk_window_key_press_callback( GtkWidget *widget,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
if (g_blockEventsOnDrag)
|
if (g_blockEventsOnDrag)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
#ifdef __WXGTK20__
|
#ifdef __WXGTK20__
|
||||||
if (win->m_imContext)
|
// 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
|
||||||
// In GTK 2.0, we need to hand over the key event to an input method
|
// ability to let other GTK+'s handlers (namely, widgets' default signal
|
||||||
// and the IM will emit a "commit" event containing the actual utf8
|
// handlers) handle the signal by returning false from this callback.
|
||||||
// character. In that case the EVT_CHAR events will be sent from
|
// Because GTK+ sends the events to parent widgets as well, we can't
|
||||||
// there.
|
// afford loosing it, otherwise native widgets inserted into wxPanel
|
||||||
if ( gtk_im_context_filter_keypress(win->m_imContext, gdk_event) )
|
// would break in subtle ways (e.g. spacebar would no longer toggle
|
||||||
return TRUE;
|
// 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
|
#endif
|
||||||
|
|
||||||
wxKeyEvent event( wxEVT_KEY_DOWN );
|
wxKeyEvent event( wxEVT_KEY_DOWN );
|
||||||
if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
|
if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
|
||||||
{
|
{
|
||||||
// unknown key pressed, ignore (the event would be useless anyhow)
|
// 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;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1143,6 +1190,26 @@ static gint gtk_window_key_press_callback( GtkWidget *widget,
|
|||||||
// will only be sent if it is not in an accelerator table.
|
// will only be sent if it is not in an accelerator table.
|
||||||
if (!ret)
|
if (!ret)
|
||||||
{
|
{
|
||||||
|
#ifdef __WXGTK20__
|
||||||
|
if (useIM)
|
||||||
|
{
|
||||||
|
// In GTK 2.0, we need to hand over the key event to an input method
|
||||||
|
// and the IM will emit a "commit" event containing the actual utf8
|
||||||
|
// character. In that case the EVT_CHAR events will be sent from
|
||||||
|
// there.
|
||||||
|
win->m_imData->lastKeyEvent = gdk_event;
|
||||||
|
if ( gtk_im_context_filter_keypress(win->m_imData->context,
|
||||||
|
gdk_event) )
|
||||||
|
{
|
||||||
|
win->m_imData->lastKeyEvent = NULL;
|
||||||
|
wxLogTrace(TRACE_KEYS, _T("Key event intercepted by IM"));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
win->m_imData->lastKeyEvent = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
long key_code;
|
long key_code;
|
||||||
KeySym keysym = gdk_event->keyval;
|
KeySym keysym = gdk_event->keyval;
|
||||||
// Find key code for EVT_CHAR and EVT_CHAR_HOOK events
|
// Find key code for EVT_CHAR and EVT_CHAR_HOOK events
|
||||||
@@ -1259,16 +1326,23 @@ static void gtk_wxwindow_commit_cb (GtkIMContext *context,
|
|||||||
const gchar *str,
|
const gchar *str,
|
||||||
wxWindow *window)
|
wxWindow *window)
|
||||||
{
|
{
|
||||||
bool ret = FALSE;
|
|
||||||
|
|
||||||
wxKeyEvent event( wxEVT_KEY_DOWN );
|
wxKeyEvent event( wxEVT_KEY_DOWN );
|
||||||
|
|
||||||
|
// take modifiers, cursor position, timestamp etc. from the last
|
||||||
|
// key_press_event that was fed into Input Method:
|
||||||
|
if (window->m_imData->lastKeyEvent)
|
||||||
|
{
|
||||||
|
wxFillOtherKeyEventFields(event,
|
||||||
|
window, window->m_imData->lastKeyEvent);
|
||||||
|
}
|
||||||
|
|
||||||
#if wxUSE_UNICODE
|
#if wxUSE_UNICODE
|
||||||
event.m_uniChar = g_utf8_get_char( str );
|
event.m_uniChar = g_utf8_get_char( str );
|
||||||
|
|
||||||
// Backward compatible for ISO-8859
|
// Backward compatible for ISO-8859
|
||||||
if (event.m_uniChar < 256)
|
if (event.m_uniChar < 256)
|
||||||
event.m_keyCode = event.m_uniChar;
|
event.m_keyCode = event.m_uniChar;
|
||||||
|
wxLogTrace(TRACE_KEYS, _T("IM sent character '%c'"), event.m_uniChar);
|
||||||
#else
|
#else
|
||||||
wchar_t unistr[2];
|
wchar_t unistr[2];
|
||||||
unistr[0] = g_utf8_get_char(str);
|
unistr[0] = g_utf8_get_char(str);
|
||||||
@@ -1276,15 +1350,13 @@ static void gtk_wxwindow_commit_cb (GtkIMContext *context,
|
|||||||
wxCharBuffer ansistr(wxConvLocal.cWC2MB(unistr));
|
wxCharBuffer ansistr(wxConvLocal.cWC2MB(unistr));
|
||||||
// We cannot handle characters that cannot be represented in
|
// We cannot handle characters that cannot be represented in
|
||||||
// current locale's charset in non-Unicode mode:
|
// current locale's charset in non-Unicode mode:
|
||||||
if (ansistr.data() == NULL) return;
|
if (ansistr.data() == NULL)
|
||||||
|
return;
|
||||||
event.m_keyCode = ansistr[0u];
|
event.m_keyCode = ansistr[0u];
|
||||||
#endif
|
wxLogTrace(TRACE_KEYS, _T("IM sent character '%c'"), event.m_keyCode);
|
||||||
|
#endif // wxUSE_UNICODE
|
||||||
|
|
||||||
// TODO: We still need to set all the extra attributes of the
|
|
||||||
// event, modifiers and such...
|
|
||||||
|
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
// Implement OnCharHook by checking ancestor top level windows
|
// Implement OnCharHook by checking ancestor top level windows
|
||||||
wxWindow *parent = window;
|
wxWindow *parent = window;
|
||||||
@@ -1901,8 +1973,8 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget,
|
|||||||
wxapp_install_idle_handler();
|
wxapp_install_idle_handler();
|
||||||
|
|
||||||
#ifdef __WXGTK20__
|
#ifdef __WXGTK20__
|
||||||
if (win->m_imContext)
|
if (win->m_imData)
|
||||||
gtk_im_context_focus_in(win->m_imContext);
|
gtk_im_context_focus_in(win->m_imData->context);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!win->m_hasVMT) return FALSE;
|
if (!win->m_hasVMT) return FALSE;
|
||||||
@@ -1994,8 +2066,8 @@ static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk
|
|||||||
wxapp_install_idle_handler();
|
wxapp_install_idle_handler();
|
||||||
|
|
||||||
#ifdef __WXGTK20__
|
#ifdef __WXGTK20__
|
||||||
if (win->m_imContext)
|
if (win->m_imData)
|
||||||
gtk_im_context_focus_out(win->m_imContext);
|
gtk_im_context_focus_out(win->m_imData->context);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!win->m_hasVMT) return FALSE;
|
if (!win->m_hasVMT) return FALSE;
|
||||||
@@ -2318,10 +2390,11 @@ gtk_window_realized_callback( GtkWidget *m_widget, wxWindow *win )
|
|||||||
wxapp_install_idle_handler();
|
wxapp_install_idle_handler();
|
||||||
|
|
||||||
#ifdef __WXGTK20__
|
#ifdef __WXGTK20__
|
||||||
if (win->m_imContext)
|
if (win->m_imData)
|
||||||
{
|
{
|
||||||
GtkPizza *pizza = GTK_PIZZA( m_widget );
|
GtkPizza *pizza = GTK_PIZZA( m_widget );
|
||||||
gtk_im_context_set_client_window( win->m_imContext, pizza->bin_window );
|
gtk_im_context_set_client_window( win->m_imData->context,
|
||||||
|
pizza->bin_window );
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -2580,7 +2653,7 @@ void wxWindowGTK::Init()
|
|||||||
m_cursor = *wxSTANDARD_CURSOR;
|
m_cursor = *wxSTANDARD_CURSOR;
|
||||||
|
|
||||||
#ifdef __WXGTK20__
|
#ifdef __WXGTK20__
|
||||||
m_imContext = NULL;
|
m_imData = NULL;
|
||||||
m_x11Context = NULL;
|
m_x11Context = NULL;
|
||||||
#else
|
#else
|
||||||
#ifdef HAVE_XIM
|
#ifdef HAVE_XIM
|
||||||
@@ -2756,6 +2829,10 @@ wxWindowGTK::~wxWindowGTK()
|
|||||||
gtk_widget_destroy( m_widget );
|
gtk_widget_destroy( m_widget );
|
||||||
m_widget = (GtkWidget*) NULL;
|
m_widget = (GtkWidget*) NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __WXGTK20__
|
||||||
|
delete m_imData;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size )
|
bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size )
|
||||||
@@ -2804,12 +2881,12 @@ void wxWindowGTK::PostCreation()
|
|||||||
|
|
||||||
#ifdef __WXGTK20__
|
#ifdef __WXGTK20__
|
||||||
// Create input method handler
|
// Create input method handler
|
||||||
m_imContext = gtk_im_multicontext_new();
|
m_imData = new wxGtkIMData;
|
||||||
|
|
||||||
// Cannot handle drawing preedited text yet
|
// Cannot handle drawing preedited text yet
|
||||||
gtk_im_context_set_use_preedit( m_imContext, FALSE );
|
gtk_im_context_set_use_preedit( m_imData->context, FALSE );
|
||||||
|
|
||||||
g_signal_connect (G_OBJECT (m_imContext), "commit",
|
g_signal_connect (G_OBJECT (m_imData->context), "commit",
|
||||||
G_CALLBACK (gtk_wxwindow_commit_cb), this);
|
G_CALLBACK (gtk_wxwindow_commit_cb), this);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -965,6 +965,30 @@ static inline bool wxIsAsciiKeysym(KeySym ks)
|
|||||||
return ks < 256;
|
return ks < 256;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void wxFillOtherKeyEventFields(wxKeyEvent& event,
|
||||||
|
wxWindowGTK *win,
|
||||||
|
GdkEventKey *gdk_event)
|
||||||
|
{
|
||||||
|
int x = 0;
|
||||||
|
int y = 0;
|
||||||
|
GdkModifierType state;
|
||||||
|
if (gdk_event->window)
|
||||||
|
gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
|
||||||
|
|
||||||
|
event.SetTimestamp( gdk_event->time );
|
||||||
|
event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK) != 0;
|
||||||
|
event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK) != 0;
|
||||||
|
event.m_altDown = (gdk_event->state & GDK_MOD1_MASK) != 0;
|
||||||
|
event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK) != 0;
|
||||||
|
event.m_scanCode = gdk_event->keyval;
|
||||||
|
event.m_rawCode = (wxUint32) gdk_event->keyval;
|
||||||
|
event.m_rawFlags = 0;
|
||||||
|
event.m_x = x;
|
||||||
|
event.m_y = y;
|
||||||
|
event.SetEventObject( win );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
wxTranslateGTKKeyEventToWx(wxKeyEvent& event,
|
wxTranslateGTKKeyEventToWx(wxKeyEvent& event,
|
||||||
wxWindowGTK *win,
|
wxWindowGTK *win,
|
||||||
@@ -1060,29 +1084,32 @@ wxTranslateGTKKeyEventToWx(wxKeyEvent& event,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
// now fill all the other fields
|
// now fill all the other fields
|
||||||
int x = 0;
|
wxFillOtherKeyEventFields(event, win, gdk_event);
|
||||||
int y = 0;
|
|
||||||
GdkModifierType state;
|
|
||||||
if (gdk_event->window)
|
|
||||||
gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
|
|
||||||
|
|
||||||
event.SetTimestamp( gdk_event->time );
|
|
||||||
event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK) != 0;
|
|
||||||
event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK) != 0;
|
|
||||||
event.m_altDown = (gdk_event->state & GDK_MOD1_MASK) != 0;
|
|
||||||
event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK) != 0;
|
|
||||||
event.m_keyCode = key_code;
|
event.m_keyCode = key_code;
|
||||||
event.m_scanCode = gdk_event->keyval;
|
|
||||||
event.m_rawCode = (wxUint32) gdk_event->keyval;
|
|
||||||
event.m_rawFlags = 0;
|
|
||||||
event.m_x = x;
|
|
||||||
event.m_y = y;
|
|
||||||
event.SetEventObject( win );
|
|
||||||
|
|
||||||
return TRUE;
|
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,
|
static gint gtk_window_key_press_callback( GtkWidget *widget,
|
||||||
GdkEventKey *gdk_event,
|
GdkEventKey *gdk_event,
|
||||||
wxWindow *win )
|
wxWindow *win )
|
||||||
@@ -1096,23 +1123,43 @@ static gint gtk_window_key_press_callback( GtkWidget *widget,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
if (g_blockEventsOnDrag)
|
if (g_blockEventsOnDrag)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
#ifdef __WXGTK20__
|
#ifdef __WXGTK20__
|
||||||
if (win->m_imContext)
|
// 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
|
||||||
// In GTK 2.0, we need to hand over the key event to an input method
|
// ability to let other GTK+'s handlers (namely, widgets' default signal
|
||||||
// and the IM will emit a "commit" event containing the actual utf8
|
// handlers) handle the signal by returning false from this callback.
|
||||||
// character. In that case the EVT_CHAR events will be sent from
|
// Because GTK+ sends the events to parent widgets as well, we can't
|
||||||
// there.
|
// afford loosing it, otherwise native widgets inserted into wxPanel
|
||||||
if ( gtk_im_context_filter_keypress(win->m_imContext, gdk_event) )
|
// would break in subtle ways (e.g. spacebar would no longer toggle
|
||||||
return TRUE;
|
// 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
|
#endif
|
||||||
|
|
||||||
wxKeyEvent event( wxEVT_KEY_DOWN );
|
wxKeyEvent event( wxEVT_KEY_DOWN );
|
||||||
if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
|
if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
|
||||||
{
|
{
|
||||||
// unknown key pressed, ignore (the event would be useless anyhow)
|
// 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;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1143,6 +1190,26 @@ static gint gtk_window_key_press_callback( GtkWidget *widget,
|
|||||||
// will only be sent if it is not in an accelerator table.
|
// will only be sent if it is not in an accelerator table.
|
||||||
if (!ret)
|
if (!ret)
|
||||||
{
|
{
|
||||||
|
#ifdef __WXGTK20__
|
||||||
|
if (useIM)
|
||||||
|
{
|
||||||
|
// In GTK 2.0, we need to hand over the key event to an input method
|
||||||
|
// and the IM will emit a "commit" event containing the actual utf8
|
||||||
|
// character. In that case the EVT_CHAR events will be sent from
|
||||||
|
// there.
|
||||||
|
win->m_imData->lastKeyEvent = gdk_event;
|
||||||
|
if ( gtk_im_context_filter_keypress(win->m_imData->context,
|
||||||
|
gdk_event) )
|
||||||
|
{
|
||||||
|
win->m_imData->lastKeyEvent = NULL;
|
||||||
|
wxLogTrace(TRACE_KEYS, _T("Key event intercepted by IM"));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
win->m_imData->lastKeyEvent = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
long key_code;
|
long key_code;
|
||||||
KeySym keysym = gdk_event->keyval;
|
KeySym keysym = gdk_event->keyval;
|
||||||
// Find key code for EVT_CHAR and EVT_CHAR_HOOK events
|
// Find key code for EVT_CHAR and EVT_CHAR_HOOK events
|
||||||
@@ -1259,16 +1326,23 @@ static void gtk_wxwindow_commit_cb (GtkIMContext *context,
|
|||||||
const gchar *str,
|
const gchar *str,
|
||||||
wxWindow *window)
|
wxWindow *window)
|
||||||
{
|
{
|
||||||
bool ret = FALSE;
|
|
||||||
|
|
||||||
wxKeyEvent event( wxEVT_KEY_DOWN );
|
wxKeyEvent event( wxEVT_KEY_DOWN );
|
||||||
|
|
||||||
|
// take modifiers, cursor position, timestamp etc. from the last
|
||||||
|
// key_press_event that was fed into Input Method:
|
||||||
|
if (window->m_imData->lastKeyEvent)
|
||||||
|
{
|
||||||
|
wxFillOtherKeyEventFields(event,
|
||||||
|
window, window->m_imData->lastKeyEvent);
|
||||||
|
}
|
||||||
|
|
||||||
#if wxUSE_UNICODE
|
#if wxUSE_UNICODE
|
||||||
event.m_uniChar = g_utf8_get_char( str );
|
event.m_uniChar = g_utf8_get_char( str );
|
||||||
|
|
||||||
// Backward compatible for ISO-8859
|
// Backward compatible for ISO-8859
|
||||||
if (event.m_uniChar < 256)
|
if (event.m_uniChar < 256)
|
||||||
event.m_keyCode = event.m_uniChar;
|
event.m_keyCode = event.m_uniChar;
|
||||||
|
wxLogTrace(TRACE_KEYS, _T("IM sent character '%c'"), event.m_uniChar);
|
||||||
#else
|
#else
|
||||||
wchar_t unistr[2];
|
wchar_t unistr[2];
|
||||||
unistr[0] = g_utf8_get_char(str);
|
unistr[0] = g_utf8_get_char(str);
|
||||||
@@ -1276,15 +1350,13 @@ static void gtk_wxwindow_commit_cb (GtkIMContext *context,
|
|||||||
wxCharBuffer ansistr(wxConvLocal.cWC2MB(unistr));
|
wxCharBuffer ansistr(wxConvLocal.cWC2MB(unistr));
|
||||||
// We cannot handle characters that cannot be represented in
|
// We cannot handle characters that cannot be represented in
|
||||||
// current locale's charset in non-Unicode mode:
|
// current locale's charset in non-Unicode mode:
|
||||||
if (ansistr.data() == NULL) return;
|
if (ansistr.data() == NULL)
|
||||||
|
return;
|
||||||
event.m_keyCode = ansistr[0u];
|
event.m_keyCode = ansistr[0u];
|
||||||
#endif
|
wxLogTrace(TRACE_KEYS, _T("IM sent character '%c'"), event.m_keyCode);
|
||||||
|
#endif // wxUSE_UNICODE
|
||||||
|
|
||||||
// TODO: We still need to set all the extra attributes of the
|
|
||||||
// event, modifiers and such...
|
|
||||||
|
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
// Implement OnCharHook by checking ancestor top level windows
|
// Implement OnCharHook by checking ancestor top level windows
|
||||||
wxWindow *parent = window;
|
wxWindow *parent = window;
|
||||||
@@ -1901,8 +1973,8 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget,
|
|||||||
wxapp_install_idle_handler();
|
wxapp_install_idle_handler();
|
||||||
|
|
||||||
#ifdef __WXGTK20__
|
#ifdef __WXGTK20__
|
||||||
if (win->m_imContext)
|
if (win->m_imData)
|
||||||
gtk_im_context_focus_in(win->m_imContext);
|
gtk_im_context_focus_in(win->m_imData->context);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!win->m_hasVMT) return FALSE;
|
if (!win->m_hasVMT) return FALSE;
|
||||||
@@ -1994,8 +2066,8 @@ static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk
|
|||||||
wxapp_install_idle_handler();
|
wxapp_install_idle_handler();
|
||||||
|
|
||||||
#ifdef __WXGTK20__
|
#ifdef __WXGTK20__
|
||||||
if (win->m_imContext)
|
if (win->m_imData)
|
||||||
gtk_im_context_focus_out(win->m_imContext);
|
gtk_im_context_focus_out(win->m_imData->context);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!win->m_hasVMT) return FALSE;
|
if (!win->m_hasVMT) return FALSE;
|
||||||
@@ -2318,10 +2390,11 @@ gtk_window_realized_callback( GtkWidget *m_widget, wxWindow *win )
|
|||||||
wxapp_install_idle_handler();
|
wxapp_install_idle_handler();
|
||||||
|
|
||||||
#ifdef __WXGTK20__
|
#ifdef __WXGTK20__
|
||||||
if (win->m_imContext)
|
if (win->m_imData)
|
||||||
{
|
{
|
||||||
GtkPizza *pizza = GTK_PIZZA( m_widget );
|
GtkPizza *pizza = GTK_PIZZA( m_widget );
|
||||||
gtk_im_context_set_client_window( win->m_imContext, pizza->bin_window );
|
gtk_im_context_set_client_window( win->m_imData->context,
|
||||||
|
pizza->bin_window );
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -2580,7 +2653,7 @@ void wxWindowGTK::Init()
|
|||||||
m_cursor = *wxSTANDARD_CURSOR;
|
m_cursor = *wxSTANDARD_CURSOR;
|
||||||
|
|
||||||
#ifdef __WXGTK20__
|
#ifdef __WXGTK20__
|
||||||
m_imContext = NULL;
|
m_imData = NULL;
|
||||||
m_x11Context = NULL;
|
m_x11Context = NULL;
|
||||||
#else
|
#else
|
||||||
#ifdef HAVE_XIM
|
#ifdef HAVE_XIM
|
||||||
@@ -2756,6 +2829,10 @@ wxWindowGTK::~wxWindowGTK()
|
|||||||
gtk_widget_destroy( m_widget );
|
gtk_widget_destroy( m_widget );
|
||||||
m_widget = (GtkWidget*) NULL;
|
m_widget = (GtkWidget*) NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __WXGTK20__
|
||||||
|
delete m_imData;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size )
|
bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size )
|
||||||
@@ -2804,12 +2881,12 @@ void wxWindowGTK::PostCreation()
|
|||||||
|
|
||||||
#ifdef __WXGTK20__
|
#ifdef __WXGTK20__
|
||||||
// Create input method handler
|
// Create input method handler
|
||||||
m_imContext = gtk_im_multicontext_new();
|
m_imData = new wxGtkIMData;
|
||||||
|
|
||||||
// Cannot handle drawing preedited text yet
|
// Cannot handle drawing preedited text yet
|
||||||
gtk_im_context_set_use_preedit( m_imContext, FALSE );
|
gtk_im_context_set_use_preedit( m_imData->context, FALSE );
|
||||||
|
|
||||||
g_signal_connect (G_OBJECT (m_imContext), "commit",
|
g_signal_connect (G_OBJECT (m_imData->context), "commit",
|
||||||
G_CALLBACK (gtk_wxwindow_commit_cb), this);
|
G_CALLBACK (gtk_wxwindow_commit_cb), this);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user