From b928095652a00bf579d8d724262b856f82a57a0f Mon Sep 17 00:00:00 2001 From: John-Mark Bell Date: Mon, 17 Feb 2014 00:21:49 +0000 Subject: GTK: add some support for input methods. We do not currently support pre-edit texts, or retrieving/modifying text surrounding the caret. This does, at least, finally allow the use of dead keys during text input, but probably is insufficient for useful input of CJKV &c. --- gtk/treeview.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++------- gtk/treeview.h | 10 --------- gtk/window.c | 50 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 109 insertions(+), 19 deletions(-) diff --git a/gtk/treeview.c b/gtk/treeview.c index 2a4732a87..c99e9fd43 100644 --- a/gtk/treeview.c +++ b/gtk/treeview.c @@ -35,12 +35,14 @@ #include "gtk/plotters.h" #include "gtk/treeview.h" #include "utils/log.h" +#include "utils/utf8.h" #include "utils/utils.h" struct nsgtk_treeview { GtkWindow *window; GtkScrolledWindow *scrolled; GtkDrawingArea *drawing_area; + GtkIMContext *input_method; bool mouse_pressed; int mouse_pressed_x; int mouse_pressed_y; @@ -52,6 +54,7 @@ struct nsgtk_treeview { void nsgtk_treeview_destroy(struct nsgtk_treeview *tv) { tree_delete(tv->tree); + g_object_unref(tv->input_method); gtk_widget_destroy(GTK_WIDGET(tv->window)); free(tv); } @@ -206,12 +209,14 @@ void nsgtk_tree_window_hide(GtkWidget *widget, gpointer g) { } -gboolean nsgtk_tree_window_button_press_event(GtkWidget *widget, +static gboolean +nsgtk_tree_window_button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer g) { struct nsgtk_treeview *tw = g; struct tree *tree = tw->tree; + gtk_im_context_reset(tw->input_method); gtk_widget_grab_focus(GTK_WIDGET(tw->drawing_area)); tw->mouse_pressed = true; @@ -243,7 +248,8 @@ gboolean nsgtk_tree_window_button_press_event(GtkWidget *widget, return TRUE; } -gboolean nsgtk_tree_window_button_release_event(GtkWidget *widget, +static gboolean +nsgtk_tree_window_button_release_event(GtkWidget *widget, GdkEventButton *event, gpointer g) { bool shift = event->state & GDK_SHIFT_MASK; @@ -307,7 +313,8 @@ gboolean nsgtk_tree_window_button_release_event(GtkWidget *widget, return TRUE; } -gboolean nsgtk_tree_window_motion_notify_event(GtkWidget *widget, +static gboolean +nsgtk_tree_window_motion_notify_event(GtkWidget *widget, GdkEventMotion *event, gpointer g) { bool shift = event->state & GDK_SHIFT_MASK; @@ -369,7 +376,8 @@ gboolean nsgtk_tree_window_motion_notify_event(GtkWidget *widget, } -gboolean nsgtk_tree_window_keypress_event(GtkWidget *widget, GdkEventKey *event, +static gboolean +nsgtk_tree_window_keypress_event(GtkWidget *widget, GdkEventKey *event, gpointer g) { struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g; @@ -381,6 +389,9 @@ gboolean nsgtk_tree_window_keypress_event(GtkWidget *widget, GdkEventKey *event, GtkAdjustment *scroll = NULL; gdouble hpage, vpage; + if (gtk_im_context_filter_keypress(tw->input_method, event)) + return TRUE; + nskey = gtk_gui_gdkkey_to_nskey(event); if (tree_keypress(tree, nskey) == true) @@ -473,6 +484,32 @@ gboolean nsgtk_tree_window_keypress_event(GtkWidget *widget, GdkEventKey *event, return TRUE; } +static gboolean +nsgtk_tree_window_keyrelease_event(GtkWidget *widget, GdkEventKey *event, + gpointer g) +{ + struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g; + + return gtk_im_context_filter_keypress(tw->input_method, event); +} + +static void +nsgtk_tree_window_input_method_commit(GtkIMContext *ctx, + const gchar *str, gpointer data) +{ + struct nsgtk_treeview *tw = (struct nsgtk_treeview *) data; + size_t len = strlen(str), offset = 0; + + while (offset < len) { + uint32_t nskey = utf8_to_ucs4(str + offset, len - offset); + + tree_keypress(tw->tree, nskey); + + offset = utf8_next(str, len, offset); + } +} + + static const struct treeview_table nsgtk_tree_callbacks = { .redraw_request = nsgtk_tree_redraw_request, .resized = nsgtk_tree_resized, @@ -498,6 +535,7 @@ struct nsgtk_treeview *nsgtk_treeview_create(unsigned int flags, tv->window = window; tv->scrolled = scrolled; tv->drawing_area = drawing_area; + tv->input_method = gtk_im_multicontext_new(); tv->tree = tree_create(flags, &nsgtk_tree_callbacks, tv); tv->mouse_state = 0; tv->mouse_pressed = false; @@ -510,17 +548,31 @@ struct nsgtk_treeview *nsgtk_treeview_create(unsigned int flags, #define CONNECT(obj, sig, callback, ptr) \ g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr)) - CONNECT(drawing_area, "button_press_event", + CONNECT(drawing_area, "button-press-event", nsgtk_tree_window_button_press_event, tv); - CONNECT(drawing_area, "button_release_event", + CONNECT(drawing_area, "button-release-event", nsgtk_tree_window_button_release_event, tv); - CONNECT(drawing_area, "motion_notify_event", + CONNECT(drawing_area, "motion-notify-event", nsgtk_tree_window_motion_notify_event, tv); - CONNECT(drawing_area, "key_press_event", + CONNECT(drawing_area, "key-press-event", nsgtk_tree_window_keypress_event, tv); + CONNECT(drawing_area, "key-release-event", + nsgtk_tree_window_keyrelease_event, + tv); + + + /* input method */ + gtk_im_context_set_client_window(tv->input_method, + nsgtk_widget_get_window(GTK_WIDGET(tv->window))); + gtk_im_context_set_use_preedit(tv->input_method, FALSE); + /* input method signals */ + CONNECT(tv->input_method, "commit", + nsgtk_tree_window_input_method_commit, + tv); + return tv; } diff --git a/gtk/treeview.h b/gtk/treeview.h index a0acbd49c..ad8180f33 100644 --- a/gtk/treeview.h +++ b/gtk/treeview.h @@ -33,16 +33,6 @@ void nsgtk_treeview_destroy(struct nsgtk_treeview *tv); struct tree *nsgtk_treeview_get_tree(struct nsgtk_treeview *tv); -gboolean nsgtk_tree_window_expose_event(GtkWidget *, GdkEventExpose *, - gpointer g); void nsgtk_tree_window_hide(GtkWidget *widget, gpointer g); -gboolean nsgtk_tree_window_button_press_event(GtkWidget *widget, - GdkEventButton *event, gpointer g); -gboolean nsgtk_tree_window_button_release_event(GtkWidget *widget, - GdkEventButton *event, gpointer g); -gboolean nsgtk_tree_window_motion_notify_event(GtkWidget *widget, - GdkEventMotion *event, gpointer g); -gboolean nsgtk_tree_window_keypress_event(GtkWidget *widget, GdkEventKey *event, - gpointer g); #endif /*__NSGTK_TREEVIEW_H__*/ diff --git a/gtk/window.c b/gtk/window.c index 038622d19..2da346b33 100644 --- a/gtk/window.c +++ b/gtk/window.c @@ -27,6 +27,7 @@ #include #include "utils/log.h" +#include "utils/utf8.h" #include "utils/utils.h" #include "utils/nsoption.h" #include "content/hlcache.h" @@ -105,6 +106,9 @@ struct gui_window { /** The icon this window should have */ GdkPixbuf *icon; + /** The input method to use with this window */ + GtkIMContext *input_method; + /** list for cleanup */ struct gui_window *next, *prev; }; @@ -315,6 +319,7 @@ static gboolean nsgtk_window_button_press_event(GtkWidget *widget, { struct gui_window *g = data; + gtk_im_context_reset(g->input_method); gtk_widget_grab_focus(GTK_WIDGET(g->layout)); gtk_widget_hide(GTK_WIDGET(nsgtk_scaffolding_history_window( g->scaffold)->window)); @@ -490,7 +495,12 @@ static gboolean nsgtk_window_keypress_event(GtkWidget *widget, GdkEventKey *event, gpointer data) { struct gui_window *g = data; - uint32_t nskey = gtk_gui_gdkkey_to_nskey(event); + uint32_t nskey; + + if (gtk_im_context_filter_keypress(g->input_method, event)) + return TRUE; + + nskey = gtk_gui_gdkkey_to_nskey(event); if (browser_window_key_press(g->bw, nskey)) return TRUE; @@ -598,6 +608,31 @@ static gboolean nsgtk_window_keypress_event(GtkWidget *widget, return TRUE; } +static gboolean nsgtk_window_keyrelease_event(GtkWidget *widget, + GdkEventKey *event, gpointer data) +{ + struct gui_window *g = data; + + return gtk_im_context_filter_keypress(g->input_method, event); +} + + +static void nsgtk_window_input_method_commit(GtkIMContext *ctx, + const gchar *str, gpointer data) +{ + struct gui_window *g = data; + size_t len = strlen(str), offset = 0; + + while (offset < len) { + uint32_t nskey = utf8_to_ucs4(str + offset, len - offset); + + browser_window_key_press(g->bw, nskey); + + offset = utf8_next(str, len, offset); + } +} + + static gboolean nsgtk_window_size_allocate_event(GtkWidget *widget, GtkAllocation *allocation, gpointer data) { @@ -658,6 +693,8 @@ static void window_destroy(GtkWidget *widget, gpointer data) struct gui_window *gw = data; browser_window_destroy(gw->bw); + + g_object_unref(gw->input_method); } /* Core interface documented in desktop/gui.h to create a gui_window */ @@ -720,6 +757,7 @@ gui_window_create(struct browser_window *bw, g->layout = GTK_LAYOUT(gtk_builder_get_object(xml, "layout")); g->status_bar = GTK_LABEL(gtk_builder_get_object(xml, "status_bar")); g->paned = GTK_PANED(gtk_builder_get_object(xml, "hpaned1")); + g->input_method = gtk_im_multicontext_new(); /* add new gui window to global list (push_top) */ @@ -763,6 +801,8 @@ gui_window_create(struct browser_window *bw, nsgtk_window_button_release_event, g); CONNECT(g->layout, "key-press-event", nsgtk_window_keypress_event, g); + CONNECT(g->layout, "key-release-event", + nsgtk_window_keyrelease_event, g); CONNECT(g->layout, "size-allocate", nsgtk_window_size_allocate_event, g); CONNECT(g->layout, "scroll-event", @@ -779,6 +819,14 @@ gui_window_create(struct browser_window *bw, CONNECT(g->container, "destroy", window_destroy, g); + /* input method */ + gtk_im_context_set_client_window(g->input_method, + nsgtk_layout_get_bin_window(g->layout)); + gtk_im_context_set_use_preedit(g->input_method, FALSE); + /* input method signals */ + CONNECT(g->input_method, "commit", + nsgtk_window_input_method_commit, g); + /* add the tab container to the scaffold notebook */ switch (temp_open_background) { case -1: -- cgit v1.2.3