📄 gtkimcontextime.c
字号:
/* * gtkimcontextime.c * Copyright (C) 2003 Takuro Ashie * Copyright (C) 2003-2004 Kazuki IWAMOTO * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * *//* * Please see the following site for the detail of Windows IME API. * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/appendix/hh/appendix/imeimes2_35ph.asp */#include "gtkimcontextime.h"#include "imm-extra.h"#include "gdk/win32/gdkwin32.h"#include "gdk/gdkkeysyms.h"#include "gtk/gtkwidget.h"#include <pango/pango-utils.h>/* avoid warning */#ifdef STRICT# undef STRICT# include <pango/pangowin32.h># ifndef STRICT# define STRICT 1# endif#else /* STRICT */# include <pango/pangowin32.h>#endif /* STRICT *//* #define BUFSIZE 4096 */#define FREE_PREEDIT_BUFFER(ctx) \{ \ g_free((ctx)->priv->comp_str); \ g_free((ctx)->priv->read_str); \ (ctx)->priv->comp_str = NULL; \ (ctx)->priv->read_str = NULL; \ (ctx)->priv->comp_str_len = 0; \ (ctx)->priv->read_str_len = 0; \}struct _GtkIMContextIMEPrivate{ /* save IME context when the client window is focused out */ DWORD conversion_mode; DWORD sentence_mode; LPVOID comp_str; DWORD comp_str_len; LPVOID read_str; DWORD read_str_len;};/* GObject class methods */static void gtk_im_context_ime_class_init (GtkIMContextIMEClass *class);static void gtk_im_context_ime_init (GtkIMContextIME *context_ime);static void gtk_im_context_ime_dispose (GObject *obj);static void gtk_im_context_ime_finalize (GObject *obj);static void gtk_im_context_ime_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);static void gtk_im_context_ime_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);/* GtkIMContext's virtual functions */static void gtk_im_context_ime_set_client_window (GtkIMContext *context, GdkWindow *client_window);static gboolean gtk_im_context_ime_filter_keypress (GtkIMContext *context, GdkEventKey *event);static void gtk_im_context_ime_reset (GtkIMContext *context);static void gtk_im_context_ime_get_preedit_string (GtkIMContext *context, gchar **str, PangoAttrList **attrs, gint *cursor_pos);static void gtk_im_context_ime_focus_in (GtkIMContext *context);static void gtk_im_context_ime_focus_out (GtkIMContext *context);static void gtk_im_context_ime_set_cursor_location (GtkIMContext *context, GdkRectangle *area);static void gtk_im_context_ime_set_use_preedit (GtkIMContext *context, gboolean use_preedit);/* GtkIMContextIME's private functions */static void gtk_im_context_ime_set_preedit_font (GtkIMContext *context);static GdkFilterReturngtk_im_context_ime_message_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data);static void get_window_position (GdkWindow *win, gint *x, gint *y);static void cb_client_widget_hierarchy_changed (GtkWidget *widget, GtkWidget *widget2, GtkIMContextIME *context_ime);GType gtk_type_im_context_ime = 0;static GObjectClass *parent_class;voidgtk_im_context_ime_register_type (GTypeModule *type_module){ static const GTypeInfo im_context_ime_info = { sizeof (GtkIMContextIMEClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) gtk_im_context_ime_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GtkIMContextIME), 0, (GInstanceInitFunc) gtk_im_context_ime_init, }; gtk_type_im_context_ime = g_type_module_register_type (type_module, GTK_TYPE_IM_CONTEXT, "GtkIMContextIME", &im_context_ime_info, 0);}static voidgtk_im_context_ime_class_init (GtkIMContextIMEClass *class){ GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class); GObjectClass *gobject_class = G_OBJECT_CLASS (class); parent_class = g_type_class_peek_parent (class); gobject_class->finalize = gtk_im_context_ime_finalize; gobject_class->dispose = gtk_im_context_ime_dispose; gobject_class->set_property = gtk_im_context_ime_set_property; gobject_class->get_property = gtk_im_context_ime_get_property; im_context_class->set_client_window = gtk_im_context_ime_set_client_window; im_context_class->filter_keypress = gtk_im_context_ime_filter_keypress; im_context_class->reset = gtk_im_context_ime_reset; im_context_class->get_preedit_string = gtk_im_context_ime_get_preedit_string; im_context_class->focus_in = gtk_im_context_ime_focus_in; im_context_class->focus_out = gtk_im_context_ime_focus_out; im_context_class->set_cursor_location = gtk_im_context_ime_set_cursor_location; im_context_class->set_use_preedit = gtk_im_context_ime_set_use_preedit;}static voidgtk_im_context_ime_init (GtkIMContextIME *context_ime){ context_ime->client_window = NULL; context_ime->toplevel = NULL; context_ime->use_preedit = TRUE; context_ime->preediting = FALSE; context_ime->opened = FALSE; context_ime->focus = FALSE; context_ime->cursor_location.x = 0; context_ime->cursor_location.y = 0; context_ime->cursor_location.width = 0; context_ime->cursor_location.height = 0; context_ime->priv = g_malloc0 (sizeof (GtkIMContextIMEPrivate)); context_ime->priv->conversion_mode = 0; context_ime->priv->sentence_mode = 0; context_ime->priv->comp_str = NULL; context_ime->priv->comp_str_len = 0; context_ime->priv->read_str = NULL; context_ime->priv->read_str_len = 0;}static voidgtk_im_context_ime_dispose (GObject *obj){ GtkIMContext *context = GTK_IM_CONTEXT (obj); GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (obj); if (context_ime->client_window) gtk_im_context_ime_set_client_window (context, NULL); FREE_PREEDIT_BUFFER (context_ime); if (G_OBJECT_CLASS (parent_class)->dispose) G_OBJECT_CLASS (parent_class)->dispose (obj);}static voidgtk_im_context_ime_finalize (GObject *obj){ /* GtkIMContext *context = GTK_IM_CONTEXT (obj); */ GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (obj); g_free (context_ime->priv); context_ime->priv = NULL; if (G_OBJECT_CLASS (parent_class)->finalize) G_OBJECT_CLASS (parent_class)->finalize (obj);}static voidgtk_im_context_ime_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec){ GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (object); g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context_ime)); switch (prop_id) { default: break; }}static voidgtk_im_context_ime_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec){ GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (object); g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context_ime)); switch (prop_id) { default: break; }}GtkIMContext *gtk_im_context_ime_new (void){ return g_object_new (GTK_TYPE_IM_CONTEXT_IME, NULL);}static voidgtk_im_context_ime_set_client_window (GtkIMContext *context, GdkWindow *client_window){ GtkIMContextIME *context_ime; g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context)); context_ime = GTK_IM_CONTEXT_IME (context); if (client_window) { HIMC himc; HWND hwnd; hwnd = GDK_WINDOW_HWND (client_window); himc = ImmGetContext (hwnd); if (himc) { context_ime->opened = ImmGetOpenStatus (himc); ImmGetConversionStatus (himc, &context_ime->priv->conversion_mode, &context_ime->priv->sentence_mode); ImmReleaseContext (hwnd, himc); } } else if (context_ime->focus) { gtk_im_context_ime_focus_out (context); } context_ime->client_window = client_window;}static gbooleangtk_im_context_ime_filter_keypress (GtkIMContext *context, GdkEventKey *event){ GtkIMContextIME *context_ime; gboolean retval = FALSE; guint32 c; g_return_val_if_fail (GTK_IS_IM_CONTEXT_IME (context), FALSE); g_return_val_if_fail (event, FALSE); if (event->type == GDK_KEY_RELEASE) return FALSE; if (event->state & GDK_CONTROL_MASK) return FALSE; context_ime = GTK_IM_CONTEXT_IME (context); if (!context_ime->focus) return FALSE; if (!GDK_IS_WINDOW (context_ime->client_window)) return FALSE; c = gdk_keyval_to_unicode (event->keyval); if (c) { guchar utf8[10]; int len = g_unichar_to_utf8 (c, utf8); utf8[len] = 0; g_signal_emit_by_name (context_ime, "commit", utf8); retval = TRUE; } return retval;}static voidgtk_im_context_ime_reset (GtkIMContext *context){ GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (context); HWND hwnd; HIMC himc; hwnd = GDK_WINDOW_HWND (context_ime->client_window); himc = ImmGetContext (hwnd); if (!himc) return; if (context_ime->preediting && ImmGetOpenStatus (himc)) ImmNotifyIME (himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0); context_ime->preediting = FALSE; g_signal_emit_by_name (context, "preedit_changed"); ImmReleaseContext (hwnd, himc);}static gchar *get_utf8_preedit_string (GtkIMContextIME *context_ime, gint *pos_ret){ gchar *utf8str = NULL; HWND hwnd; HIMC himc; gint pos = 0; if (pos_ret) *pos_ret = 0; hwnd = GDK_WINDOW_HWND (context_ime->client_window); himc = ImmGetContext (hwnd); if (!himc) return g_strdup (""); if (context_ime->preediting) { glong len; len = ImmGetCompositionStringW (himc, GCS_COMPSTR, NULL, 0); if (len > 0) { GError *error = NULL; gpointer buf = g_alloca (len); ImmGetCompositionStringW (himc, GCS_COMPSTR, buf, len); len /= 2; utf8str = g_utf16_to_utf8 (buf, len, NULL, NULL, &error); if (error) { g_warning ("%s", error->message); g_error_free (error); } if (pos_ret) { pos = ImmGetCompositionStringW (himc, GCS_CURSORPOS, NULL, 0); if (pos < 0 || len < pos) { g_warning ("ImmGetCompositionString: " "Invalid cursor position!"); pos = 0; } } } } if (!utf8str) { utf8str = g_strdup (""); pos = 0; } if (pos_ret) *pos_ret = pos; ImmReleaseContext (hwnd, himc); return utf8str;}static PangoAttrList *get_pango_attr_list (GtkIMContextIME *context_ime, const gchar *utf8str){ PangoAttrList *attrs = pango_attr_list_new (); HWND hwnd; HIMC himc; hwnd = GDK_WINDOW_HWND (context_ime->client_window); himc = ImmGetContext (hwnd); if (!himc) return attrs; if (context_ime->preediting) { const gchar *schr = utf8str, *echr; guint8 *buf; guint16 f_red, f_green, f_blue, b_red, b_green, b_blue; glong len, spos = 0, epos, sidx = 0, eidx; PangoAttribute *attr; /* * get attributes list of IME. */ len = ImmGetCompositionStringW (himc, GCS_COMPATTR, NULL, 0); buf = g_alloca (len); ImmGetCompositionStringW (himc, GCS_COMPATTR, buf, len); /* * schr and echr are pointer in utf8str. */ for (echr = g_utf8_next_char (utf8str); *schr != '\0'; echr = g_utf8_next_char (echr)) { /* * spos and epos are buf(attributes list of IME) position by * bytes. * Using the wide-char API, this value is same with UTF-8 offset. */ epos = g_utf8_pointer_to_offset (utf8str, echr); /* * sidx and eidx are positions in utf8str by bytes. */ eidx = echr - utf8str; /* * convert attributes list to PangoAttriute. */ if (*echr == '\0' || buf[spos] != buf[epos]) { switch (buf[spos]) { case ATTR_TARGET_CONVERTED: attr = pango_attr_underline_new (PANGO_UNDERLINE_DOUBLE); attr->start_index = sidx; attr->end_index = eidx; pango_attr_list_change (attrs, attr); f_red = f_green = f_blue = 0; b_red = b_green = b_blue = 0xffff; break; case ATTR_TARGET_NOTCONVERTED: f_red = f_green = f_blue = 0xffff; b_red = b_green = b_blue = 0; break; case ATTR_INPUT_ERROR: f_red = f_green = f_blue = 0; b_red = b_green = b_blue = 0x7fff; break; default: /* ATTR_INPUT,ATTR_CONVERTED,ATTR_FIXEDCONVERTED */ attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE); attr->start_index = sidx; attr->end_index = eidx; pango_attr_list_change (attrs, attr); f_red = f_green = f_blue = 0; b_red = b_green = b_blue = 0xffff; } attr = pango_attr_foreground_new (f_red, f_green, f_blue); attr->start_index = sidx; attr->end_index = eidx; pango_attr_list_change (attrs, attr); attr = pango_attr_background_new (b_red, b_green, b_blue); attr->start_index = sidx; attr->end_index = eidx; pango_attr_list_change (attrs, attr); schr = echr; spos = epos; sidx = eidx; } } } ImmReleaseContext (hwnd, himc); return attrs;}static voidgtk_im_context_ime_get_preedit_string (GtkIMContext *context, gchar **str, PangoAttrList **attrs, gint *cursor_pos){ gchar *utf8str = NULL; gint pos = 0; GtkIMContextIME *context_ime; context_ime = GTK_IM_CONTEXT_IME (context); utf8str = get_utf8_preedit_string (context_ime, &pos); if (attrs) *attrs = get_pango_attr_list (context_ime, utf8str); if (str) { *str = utf8str; } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -