summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2004-08-26 14:29:25 +0000
committerMatthias Clasen <matthiasc@src.gnome.org>2004-08-26 14:29:25 +0000
commit24dd1030f08ae6eec6328cc6407391b3e5ca13d6 (patch)
treea1b914b629922b2b0ffdf127e104cfa3f44519fa
parent3124567f18e3d2d59995eabd933cc4359215f530 (diff)
downloadgtk+-24dd1030f08ae6eec6328cc6407391b3e5ca13d6.tar.gz
Build imime.la on Windows.
2004-08-26 Matthias Clasen <mclasen@redhat.com> * modules/input/Makefile.am: Build imime.la on Windows. * modules/input/gtkimcontextime.h: * modules/input/gtkimcontextime.c: * modules/input/imime.c: * modules/input/imm-extra.h: Add the IME input method for Win32 written by Takuro Ashie and Kazuko IWAMOTO. The code was previously hosted at http://sourceforge.jp/projects/imime. (#135195)
-rw-r--r--ChangeLog11
-rw-r--r--ChangeLog.pre-2-1011
-rw-r--r--ChangeLog.pre-2-611
-rw-r--r--ChangeLog.pre-2-811
-rw-r--r--modules/input/Makefile.am15
-rw-r--r--modules/input/gtkimcontextime.c1103
-rw-r--r--modules/input/gtkimcontextime.h61
-rw-r--r--modules/input/imime.c67
-rw-r--r--modules/input/imm-extra.h49
9 files changed, 1338 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index 3d05a76c2f..5bedc380dd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2004-08-26 Matthias Clasen <mclasen@redhat.com>
+
+ * modules/input/Makefile.am: Build imime.la on Windows.
+
+ * modules/input/gtkimcontextime.h:
+ * modules/input/gtkimcontextime.c:
+ * modules/input/imime.c:
+ * modules/input/imm-extra.h: Add the IME input method for Win32
+ written by Takuro Ashie and Kazuko IWAMOTO. The code was previously
+ hosted at http://sourceforge.jp/projects/imime. (#135195)
+
2004-08-26 Bill Haneman <billh@gnome.org>
* gtk/gtktreeview:
diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10
index 3d05a76c2f..5bedc380dd 100644
--- a/ChangeLog.pre-2-10
+++ b/ChangeLog.pre-2-10
@@ -1,3 +1,14 @@
+2004-08-26 Matthias Clasen <mclasen@redhat.com>
+
+ * modules/input/Makefile.am: Build imime.la on Windows.
+
+ * modules/input/gtkimcontextime.h:
+ * modules/input/gtkimcontextime.c:
+ * modules/input/imime.c:
+ * modules/input/imm-extra.h: Add the IME input method for Win32
+ written by Takuro Ashie and Kazuko IWAMOTO. The code was previously
+ hosted at http://sourceforge.jp/projects/imime. (#135195)
+
2004-08-26 Bill Haneman <billh@gnome.org>
* gtk/gtktreeview:
diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6
index 3d05a76c2f..5bedc380dd 100644
--- a/ChangeLog.pre-2-6
+++ b/ChangeLog.pre-2-6
@@ -1,3 +1,14 @@
+2004-08-26 Matthias Clasen <mclasen@redhat.com>
+
+ * modules/input/Makefile.am: Build imime.la on Windows.
+
+ * modules/input/gtkimcontextime.h:
+ * modules/input/gtkimcontextime.c:
+ * modules/input/imime.c:
+ * modules/input/imm-extra.h: Add the IME input method for Win32
+ written by Takuro Ashie and Kazuko IWAMOTO. The code was previously
+ hosted at http://sourceforge.jp/projects/imime. (#135195)
+
2004-08-26 Bill Haneman <billh@gnome.org>
* gtk/gtktreeview:
diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8
index 3d05a76c2f..5bedc380dd 100644
--- a/ChangeLog.pre-2-8
+++ b/ChangeLog.pre-2-8
@@ -1,3 +1,14 @@
+2004-08-26 Matthias Clasen <mclasen@redhat.com>
+
+ * modules/input/Makefile.am: Build imime.la on Windows.
+
+ * modules/input/gtkimcontextime.h:
+ * modules/input/gtkimcontextime.c:
+ * modules/input/imime.c:
+ * modules/input/imm-extra.h: Add the IME input method for Win32
+ written by Takuro Ashie and Kazuko IWAMOTO. The code was previously
+ hosted at http://sourceforge.jp/projects/imime. (#135195)
+
2004-08-26 Bill Haneman <billh@gnome.org>
* gtk/gtktreeview:
diff --git a/modules/input/Makefile.am b/modules/input/Makefile.am
index 70fcab66dd..b234f0b2e6 100644
--- a/modules/input/Makefile.am
+++ b/modules/input/Makefile.am
@@ -75,6 +75,18 @@ im_ipa_la_LDFLAGS = -rpath $(moduledir) -avoid-version -module $(no_undefined)
im_ipa_la_SOURCES = imipa.c
im_ipa_la_LIBADD = $(LDADDS)
+
+im_ime_la_LDFLAGS = -rpath $(moduledir) -avoid-version -module $(no_undefined)
+im_ime_la_SOURCES = \
+ gtkimcontextime.c \
+ gtkimcontextime.h \
+ imime.c \
+ imm-extra.h
+im_ime_la_LIBADD = $(LDADDS)
+if PLATFORM_WIN32
+IM_IME_MODULE=im-ime.la
+endif
+
if CROSS_COMPILING
RUN_QUERY_IMMODULES_TEST=false
else
@@ -115,7 +127,8 @@ module_LTLIBRARIES = \
im-thai-broken.la \
im-ti-er.la \
im-ti-et.la \
- im-viqr.la
+ im-viqr.la \
+ $(IM_IME_MODULE)
gtk.immodules: Makefile.am $(module_LTLIBRARIES)
$(top_builddir)/gtk/gtk-query-immodules-2.0 $(module_LTLIBRARIES) > gtk.immodules
diff --git a/modules/input/gtkimcontextime.c b/modules/input/gtkimcontextime.c
new file mode 100644
index 0000000000..b441d53c57
--- /dev/null
+++ b/modules/input/gtkimcontextime.c
@@ -0,0 +1,1103 @@
+/*
+ * gtkimmoduleime
+ * 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.
+ *
+ * $Id$
+ */
+
+/*
+ * 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/gdkwin32.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtkwidget.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,
+ PangoFont *font);
+static GdkFilterReturn
+ gtk_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;
+
+
+void
+gtk_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 void
+gtk_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 void
+gtk_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 void
+gtk_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 void
+gtk_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 void
+gtk_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 void
+gtk_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 void
+gtk_im_context_ime_set_client_window (GtkIMContext *context,
+ GdkWindow *client_window)
+{
+ GtkIMContextIME *context_ime;
+ HWND hwnd = NULL;
+
+ g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context));
+ context_ime = GTK_IM_CONTEXT_IME (context);
+
+ if (client_window)
+ {
+ HIMC himc;
+
+ hwnd = GDK_WINDOW_HWND (client_window);
+
+ /* get default ime context */
+ himc = ImmGetContext (hwnd);
+ 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 gboolean
+gtk_im_context_ime_filter_keypress (GtkIMContext *context,
+ GdkEventKey *event)
+{
+ GtkIMContextIME *context_ime;
+ HWND hwnd;
+ HIMC himc;
+ gboolean retval = FALSE;
+
+ 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 retval;
+
+ context_ime = GTK_IM_CONTEXT_IME (context);
+ if (!context_ime->focus)
+ return FALSE;
+ if (!GDK_IS_WINDOW (context_ime->client_window))
+ return FALSE;
+
+ hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+ himc = ImmGetContext (hwnd);
+
+ /* FIXME!! event->string is deprecated */
+ if (event->string && *event->string
+ && !g_unichar_iscntrl (g_utf8_get_char_validated (event->string,
+ strlen (event->string))))
+ {
+ g_signal_emit_by_name (G_OBJECT (context_ime), "commit", event->string);
+ retval = TRUE;
+ }
+
+ ImmReleaseContext (hwnd, himc);
+
+ return retval;
+}
+
+
+static void
+gtk_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;
+
+ /*
+ g_return_val_if_fail (GTK_IS_IM_CONTEXT_IME (context_ime),
+ g_strdup (""));
+ */
+
+ hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+ himc = ImmGetContext (hwnd);
+
+ /* shouldn't return NULL */
+ g_return_val_if_fail (himc, g_strdup (""));
+
+ if (context_ime->preediting)
+ {
+ gpointer buf;
+ glong len;
+ GError *error = NULL;
+
+#ifdef UNICODE
+ len = ImmGetCompositionString (himc, GCS_COMPSTR, NULL, 0);
+ buf = g_malloc (len);
+ if (len > 0 && buf)
+ {
+ ImmGetCompositionString (himc, GCS_COMPSTR, buf, len);
+ len /= sizeof (gunichar2);
+ 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 = ImmGetCompositionString (himc, GCS_CURSORPOS, NULL, 0);
+ if (pos < 0 || len < pos)
+ {
+ g_warning ("ImmGetCompositionString: "
+ "Invalid cursor position!");
+ pos = 0;
+ }
+ }
+ g_free (buf);
+ }
+#else /* not UNICODE */
+ len = ImmGetCompositionString (himc, GCS_COMPSTR, NULL, 0);
+ buf = g_malloc (len);
+ if (len > 0 && buf)
+ {
+ ImmGetCompositionString (himc, GCS_COMPSTR, buf, len);
+ utf8str = g_locale_to_utf8 (buf, len, NULL, NULL, &error);
+ if (error)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ if (pos_ret)
+ {
+ pos = ImmGetCompositionString (himc, GCS_CURSORPOS, NULL, 0);
+ /* get cursor position by offset */
+ if (pos < len && utf8str)
+ {
+ gchar *tmpstr;
+
+ tmpstr = g_locale_to_utf8 (buf, pos, NULL, NULL, &error);
+ if (error)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+ if (tmpstr)
+ {
+ pos = g_utf8_strlen (tmpstr, -1);
+ }
+ else
+ {
+ pos = 0;
+ }
+ g_free (tmpstr);
+ }
+ else if (pos == len && utf8str)
+ {
+ pos = g_utf8_strlen (utf8str, -1);
+ }
+ else
+ {
+ g_warning ("ImmGetCompositionString: "
+ "Invalid cursor position!");
+ pos = 0;
+ }
+ }
+ g_free (buf);
+ }
+#endif /* not UNICODE */
+ }
+
+ 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;
+
+ /* g_return_val_if_fail (GTK_IS_IM_CONTEXT_IME (context_ime), attr); */
+
+ hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+ himc = ImmGetContext (hwnd);
+
+ g_return_val_if_fail (himc, 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 = ImmGetCompositionString (himc, GCS_COMPATTR, NULL, 0);
+ buf = g_malloc (len);
+ ImmGetCompositionString (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.
+ * If UNICODE is defined, this value is same with UTF-8 offset.
+ * If it's not defined, this value is same with bytes position
+ * of locale encoded preedit string.
+ *
+ */
+#ifdef UNICODE
+ epos = g_utf8_pointer_to_offset (utf8str, echr);
+#else /* not UNICODE */
+ gchar *tmpstr;
+ GError *error = NULL;
+
+ epos = spos;
+ tmpstr = g_locale_from_utf8 (schr, echr - schr, NULL, NULL, &error);
+ if (error)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+ if (tmpstr)
+ {
+ epos += strlen (tmpstr);
+ g_free (tmpstr);
+ }
+#endif /* not UNICODE */
+ /*
+ * 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;
+ }
+ }
+ g_free (buf);
+ }
+
+ ImmReleaseContext (hwnd, himc);
+
+ return attrs;
+}
+
+
+static void
+gtk_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
+ {
+ g_free (utf8str);
+ utf8str = NULL;
+ }
+
+ if (cursor_pos)
+ *cursor_pos = pos;
+}
+
+
+static void
+gtk_im_context_ime_focus_in (GtkIMContext *context)
+{
+ GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (context);
+ GdkWindow *toplevel;
+ GtkWidget *widget = NULL;
+ HWND hwnd, top_hwnd;
+ HIMC himc;
+
+ if (!GDK_IS_WINDOW (context_ime->client_window))
+ return;
+
+ hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+ himc = ImmGetContext (hwnd);
+ if (!himc)
+ return;
+
+ toplevel = gdk_window_get_toplevel (context_ime->client_window);
+ if (GDK_IS_WINDOW (toplevel))
+ {
+ gdk_window_add_filter (toplevel,
+ gtk_im_context_ime_message_filter, context_ime);
+ top_hwnd = GDK_WINDOW_HWND (toplevel);
+
+ context_ime->toplevel = toplevel;
+ }
+ else
+ {
+ g_warning ("gtk_im_context_ime_focus_in(): "
+ "cannot find toplevel window.");
+ return;
+ }
+
+ /* trace reparenting (probably no need) */
+ gdk_window_get_user_data (context_ime->client_window, (gpointer) & widget);
+ if (GTK_IS_WIDGET (widget))
+ {
+ g_signal_connect (G_OBJECT (widget), "hierarchy-changed",
+ G_CALLBACK (cb_client_widget_hierarchy_changed),
+ context_ime);
+ }
+ else
+ {
+ /* warning? */
+ }
+
+ /* swtich current context */
+ context_ime->focus = TRUE;
+
+ /* restore preedit context */
+ ImmSetConversionStatus (himc,
+ context_ime->priv->conversion_mode,
+ context_ime->priv->sentence_mode);
+
+ if (context_ime->opened)
+ {
+ if (!ImmGetOpenStatus (himc))
+ ImmSetOpenStatus (himc, TRUE);
+ if (context_ime->preediting)
+ {
+ ImmSetCompositionString (himc,
+ SCS_SETSTR,
+ context_ime->priv->comp_str,
+ context_ime->priv->comp_str_len, NULL, 0);
+ FREE_PREEDIT_BUFFER (context_ime);
+ }
+ }
+
+ /* clean */
+ ImmReleaseContext (hwnd, himc);
+}
+
+
+static void
+gtk_im_context_ime_focus_out (GtkIMContext *context)
+{
+ GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (context);
+ GdkWindow *toplevel;
+ GtkWidget *widget = NULL;
+ HWND hwnd, top_hwnd;
+ HIMC himc;
+
+ if (!GDK_IS_WINDOW (context_ime->client_window))
+ return;
+
+ hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+ himc = ImmGetContext (hwnd);
+ if (!himc)
+ return;
+
+ /* save preedit context */
+ ImmGetConversionStatus (himc,
+ &context_ime->priv->conversion_mode,
+ &context_ime->priv->sentence_mode);
+
+ if (ImmGetOpenStatus (himc))
+ {
+ gboolean preediting = context_ime->preediting;
+
+ if (preediting)
+ {
+ FREE_PREEDIT_BUFFER (context_ime);
+
+ context_ime->priv->comp_str_len
+ = ImmGetCompositionString (himc, GCS_COMPSTR, NULL, 0);
+ context_ime->priv->comp_str
+ = g_malloc (context_ime->priv->comp_str_len);
+ ImmGetCompositionString (himc, GCS_COMPSTR,
+ context_ime->priv->comp_str,
+ context_ime->priv->comp_str_len);
+
+ context_ime->priv->read_str_len
+ = ImmGetCompositionString (himc, GCS_COMPREADSTR, NULL, 0);
+ context_ime->priv->read_str
+ = g_malloc (context_ime->priv->read_str_len);
+ ImmGetCompositionString (himc, GCS_COMPREADSTR,
+ context_ime->priv->read_str,
+ context_ime->priv->read_str_len);
+ }
+
+ ImmSetOpenStatus (himc, FALSE);
+
+ context_ime->opened = TRUE;
+ context_ime->preediting = preediting;
+ }
+ else
+ {
+ context_ime->opened = FALSE;
+ context_ime->preediting = FALSE;
+ }
+
+ /* remove signal handler */
+ gdk_window_get_user_data (context_ime->client_window, (gpointer) & widget);
+ if (GTK_IS_WIDGET (widget))
+ {
+ g_signal_handlers_disconnect_by_func
+ (G_OBJECT (widget),
+ G_CALLBACK (cb_client_widget_hierarchy_changed), context_ime);
+ }
+
+ /* remove event fileter */
+ toplevel = gdk_window_get_toplevel (context_ime->client_window);
+ if (GDK_IS_WINDOW (toplevel))
+ {
+ gdk_window_remove_filter (toplevel,
+ gtk_im_context_ime_message_filter,
+ context_ime);
+ top_hwnd = GDK_WINDOW_HWND (toplevel);
+
+ context_ime->toplevel = NULL;
+ }
+ else
+ {
+ g_warning ("gtk_im_context_ime_focus_out(): "
+ "cannot find toplevel window.");
+ }
+
+ /* swtich current context */
+ context_ime->focus = FALSE;
+
+ /* clean */
+ ImmReleaseContext (hwnd, himc);
+}
+
+
+static void
+gtk_im_context_ime_set_cursor_location (GtkIMContext *context,
+ GdkRectangle *area)
+{
+ gint wx = 0, wy = 0;
+ GtkIMContextIME *context_ime;
+ COMPOSITIONFORM cf;
+ HWND hwnd;
+ HIMC himc;
+
+ g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context));
+
+ context_ime = GTK_IM_CONTEXT_IME (context);
+ if (area)
+ context_ime->cursor_location = *area;
+
+ if (!context_ime->client_window)
+ return;
+
+ hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+ himc = ImmGetContext (hwnd);
+ if (!himc)
+ return;
+
+ get_window_position (context_ime->client_window, &wx, &wy);
+ cf.dwStyle = CFS_POINT;
+ cf.ptCurrentPos.x = wx + context_ime->cursor_location.x;
+ cf.ptCurrentPos.y = wy + context_ime->cursor_location.y;
+ ImmSetCompositionWindow (himc, &cf);
+
+ ImmReleaseContext (hwnd, himc);
+}
+
+
+static void
+gtk_im_context_ime_set_use_preedit (GtkIMContext *context,
+ gboolean use_preedit)
+{
+ GtkIMContextIME *context_ime;
+
+ g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context));
+ context_ime = GTK_IM_CONTEXT_IME (context);
+
+ context_ime->use_preedit = use_preedit;
+ if (context_ime->preediting)
+ {
+ /* FIXME */
+ HWND hwnd;
+ HIMC himc;
+
+ hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+ himc = ImmGetContext (hwnd);
+ if (!himc)
+ return;
+ }
+}
+
+
+static void
+gtk_im_context_ime_set_preedit_font (GtkIMContext *context, PangoFont *font)
+{
+ GtkIMContextIME *context_ime;
+ GtkWidget *widget = NULL;
+ HWND hwnd;
+ HIMC himc;
+ PangoContext *pango_context;
+ LOGFONT *logfont;
+
+ g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context));
+
+ context_ime = GTK_IM_CONTEXT_IME (context);
+ if (!context_ime->client_window)
+ return;
+
+ gdk_window_get_user_data (context_ime->client_window, (gpointer) &widget);
+ if (!GTK_IS_WIDGET (widget))
+ return;
+
+ hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+ himc = ImmGetContext (hwnd);
+ if (!himc)
+ return;
+
+ /* set font */
+ pango_context = gtk_widget_get_pango_context (widget);
+ if (!pango_context)
+ goto ERROR_OUT;
+
+ if (!font)
+ font = pango_context_load_font (pango_context, widget->style->font_desc);
+ if (!font)
+ goto ERROR_OUT;
+
+ logfont = pango_win32_font_logfont (font);
+ if (logfont)
+ ImmSetCompositionFont (himc, logfont);
+
+ERROR_OUT:
+ /* clean */
+ ImmReleaseContext (hwnd, himc);
+}
+
+
+static GdkFilterReturn
+gtk_im_context_ime_message_filter (GdkXEvent *xevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ GtkIMContext *context;
+ GtkIMContextIME *context_ime;
+ HWND hwnd;
+ HIMC himc;
+ MSG *msg = (MSG *) xevent;
+ GdkFilterReturn retval = GDK_FILTER_CONTINUE;
+
+ g_return_val_if_fail (GTK_IS_IM_CONTEXT_IME (data), retval);
+
+ context = GTK_IM_CONTEXT (data);
+ context_ime = GTK_IM_CONTEXT_IME (data);
+ if (!context_ime->focus)
+ return retval;
+
+ hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+ himc = ImmGetContext (hwnd);
+ if (!himc)
+ return retval;
+
+ switch (msg->message)
+ {
+ case WM_IME_COMPOSITION:
+ {
+ gint wx = 0, wy = 0;
+ CANDIDATEFORM cf;
+
+ get_window_position (context_ime->client_window, &wx, &wy);
+ /* FIXME! */
+ {
+ HWND hwnd_top;
+ POINT pt;
+ RECT rc;
+
+ hwnd_top =
+ GDK_WINDOW_HWND (gdk_window_get_toplevel
+ (context_ime->client_window));
+ GetWindowRect (hwnd_top, &rc);
+ pt.x = wx;
+ pt.y = wy;
+ ClientToScreen (hwnd_top, &pt);
+ wx = pt.x - rc.left;
+ wy = pt.y - rc.top;
+ }
+ cf.dwIndex = 0;
+ cf.dwStyle = CFS_CANDIDATEPOS;
+ cf.ptCurrentPos.x = wx + context_ime->cursor_location.x;
+ cf.ptCurrentPos.y = wy + context_ime->cursor_location.y
+ + context_ime->cursor_location.height;
+ ImmSetCandidateWindow (himc, &cf);
+
+ if ((msg->lParam & GCS_COMPSTR))
+ g_signal_emit_by_name (context, "preedit_changed");
+
+ if (msg->lParam & GCS_RESULTSTR)
+ {
+ gsize len;
+ gpointer buf;
+ gchar *utf8str = NULL;
+ GError *error = NULL;
+
+ len = ImmGetCompositionString (himc, GCS_RESULTSTR, NULL, 0);
+ buf = g_alloca (len);
+ if (len > 0 && buf)
+ {
+ ImmGetCompositionString (himc, GCS_RESULTSTR, buf, len);
+#ifdef UNICODE
+ len /= sizeof (gunichar2);
+ utf8str = g_utf16_to_utf8 (buf, len, NULL, NULL, &error);
+#else /* not UNICODE */
+ utf8str = g_locale_to_utf8 (buf, len, NULL, NULL, &error);
+#endif /* not UNICODE */
+ if (error)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+ }
+
+ if (utf8str)
+ {
+ g_signal_emit_by_name (G_OBJECT (context), "commit", utf8str);
+ g_free (utf8str);
+ }
+ }
+
+ if (context_ime->use_preedit)
+ retval = TRUE;
+ break;
+ }
+
+ case WM_IME_STARTCOMPOSITION:
+ context_ime->preediting = TRUE;
+ gtk_im_context_ime_set_cursor_location (context, NULL);
+ g_signal_emit_by_name (context, "preedit_start");
+ if (context_ime->use_preedit)
+ retval = TRUE;
+ break;
+
+ case WM_IME_ENDCOMPOSITION:
+ context_ime->preediting = FALSE;
+ g_signal_emit_by_name (context, "preedit_changed");
+ g_signal_emit_by_name (context, "preedit_end");
+ if (context_ime->use_preedit)
+ retval = TRUE;
+ break;
+
+ case WM_IME_NOTIFY:
+ switch (msg->wParam)
+ {
+ case IMN_SETOPENSTATUS:
+ context_ime->opened = ImmGetOpenStatus (himc);
+ gtk_im_context_ime_set_preedit_font (context, NULL);
+ break;
+
+ default:
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ ImmReleaseContext (hwnd, himc);
+ return retval;
+}
+
+
+/*
+ * x and y must be initialized to 0.
+ */
+static void
+get_window_position (GdkWindow *win, gint *x, gint *y)
+{
+ GdkWindow *parent, *toplevel;
+ gint wx, wy;
+
+ g_return_if_fail (GDK_IS_WINDOW (win));
+ g_return_if_fail (x && y);
+
+ gdk_window_get_position (win, &wx, &wy);
+ *x += wx;
+ *y += wy;
+ parent = gdk_window_get_parent (win);
+ toplevel = gdk_window_get_toplevel (win);
+
+ if (parent && parent != toplevel)
+ get_window_position (parent, x, y);
+}
+
+
+/*
+ * probably, this handler isn't needed.
+ */
+static void
+cb_client_widget_hierarchy_changed (GtkWidget *widget,
+ GtkWidget *widget2,
+ GtkIMContextIME *context_ime)
+{
+ GdkWindow *new_toplevel;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context_ime));
+
+ if (!context_ime->client_window)
+ return;
+ if (!context_ime->focus)
+ return;
+
+ new_toplevel = gdk_window_get_toplevel (context_ime->client_window);
+ if (context_ime->toplevel == new_toplevel)
+ return;
+
+ /* remove filter from old toplevel */
+ if (GDK_IS_WINDOW (context_ime->toplevel))
+ {
+ gdk_window_remove_filter (context_ime->toplevel,
+ gtk_im_context_ime_message_filter,
+ context_ime);
+ }
+ else
+ {
+ }
+
+ /* add filter to new toplevel */
+ if (GDK_IS_WINDOW (new_toplevel))
+ {
+ gdk_window_add_filter (new_toplevel,
+ gtk_im_context_ime_message_filter, context_ime);
+ }
+ else
+ {
+ }
+
+ context_ime->toplevel = new_toplevel;
+}
diff --git a/modules/input/gtkimcontextime.h b/modules/input/gtkimcontextime.h
new file mode 100644
index 0000000000..e4d97d9265
--- /dev/null
+++ b/modules/input/gtkimcontextime.h
@@ -0,0 +1,61 @@
+/*
+ * gtkimmoduleime
+ * Copyright (C) 2003 Takuro Ashie
+ * Copyright (C) 2003 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.
+ *
+ * $Id$
+ */
+
+#include <gtk/gtkimcontext.h>
+
+extern GType gtk_type_im_context_ime;
+
+#define GTK_TYPE_IM_CONTEXT_IME gtk_type_im_context_ime
+#define GTK_IM_CONTEXT_IME(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_IM_CONTEXT_IME, GtkIMContextIME))
+#define GTK_IM_CONTEXT_IME_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_IM_CONTEXT_IME, GtkIMContextIMEClass))
+#define GTK_IS_IM_CONTEXT_IME(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_IM_CONTEXT_IME))
+#define GTK_IS_IM_CONTEXT_IME_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_IM_CONTEXT_IME))
+#define GTK_IM_CONTEXT_IME_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_IM_CONTEXT_IME, GtkIMContextIMEClass))
+
+typedef struct _GtkIMContextIME GtkIMContextIME;
+typedef struct _GtkIMContextIMEPrivate GtkIMContextIMEPrivate;
+typedef struct _GtkIMContextIMEClass GtkIMContextIMEClass;
+
+struct _GtkIMContextIME
+{
+ GtkIMContext object;
+
+ GdkWindow *client_window;
+ GdkWindow *toplevel;
+ guint use_preedit : 1;
+ guint preediting : 1;
+ guint opened : 1;
+ guint focus : 1;
+ GdkRectangle cursor_location;
+
+ GtkIMContextIMEPrivate *priv;
+};
+
+struct _GtkIMContextIMEClass
+{
+ GtkIMContextClass parent_class;
+};
+
+
+void gtk_im_context_ime_register_type (GTypeModule * type_module);
+GtkIMContext *gtk_im_context_ime_new (void);
diff --git a/modules/input/imime.c b/modules/input/imime.c
new file mode 100644
index 0000000000..6100876e5e
--- /dev/null
+++ b/modules/input/imime.c
@@ -0,0 +1,67 @@
+/*
+ * gtkimmoduleime
+ * Copyright (C) 2003 Takuro Ashie
+ *
+ * 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.
+ *
+ * $Id$
+ */
+
+#include <gtk/gtkimmodule.h>
+#include "gtkimcontextime.h"
+
+#include <string.h>
+
+static const GtkIMContextInfo ime_info = {
+ "win32ime",
+ "Windows IME",
+ "gtk+",
+ "",
+ "*",
+};
+
+static const GtkIMContextInfo *info_list[] = {
+ &ime_info,
+};
+
+void
+im_module_init (GTypeModule * module)
+{
+ gtk_im_context_ime_register_type (module);
+}
+
+void
+im_module_exit (void)
+{
+}
+
+void
+im_module_list (const GtkIMContextInfo *** contexts, int *n_contexts)
+{
+ *contexts = info_list;
+ *n_contexts = G_N_ELEMENTS (info_list);
+}
+
+GtkIMContext *
+im_module_create (const gchar * context_id)
+{
+ g_return_val_if_fail (context_id, NULL);
+
+ if (!strcmp (context_id, "win32ime"))
+ return g_object_new (GTK_TYPE_IM_CONTEXT_IME, NULL);
+ else
+ return NULL;
+}
diff --git a/modules/input/imm-extra.h b/modules/input/imm-extra.h
new file mode 100644
index 0000000000..7bed406f0c
--- /dev/null
+++ b/modules/input/imm-extra.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* $Id$ */
+
+
+#include <windows.h>
+#include <imm.h>
+
+
+/* these aren't defined in Cygwin's imm.h */
+
+#ifndef WM_IME_REQUEST
+# define WM_IME_REQUEST 0x0288
+#endif /* WM_IME_REQUEST */
+
+#ifndef IMR_COMPOSITIONWINDOW
+# define IMR_COMPOSITIONWINDOW 0x0001
+#endif /* IMR_COMPOSITIONWINDOW */
+
+#ifndef IMR_CANDIDATEWINDOW
+# define IMR_CANDIDATEWINDOW 0x0002
+#endif /* IMR_CANDIDATEWINDOW */
+
+#ifndef IMR_COMPOSITIONFONT
+# define IMR_COMPOSITIONFONT 0x0003
+#endif /* IMR_COMPOSITIONFONT */
+
+#ifndef IMR_RECONVERTSTRING
+# define IMR_RECONVERTSTRING 0x0004
+#endif /* IMR_RECONVERTSTRING */
+
+#ifndef IMR_CONFIRMRECONVERTSTRING
+# define IMR_CONFIRMRECONVERTSTRING 0x0005
+#endif /* IMR_CONFIRMRECONVERTSTRING */
+
+#ifndef IMR_QUERYCHARPOSITION
+# define IMR_QUERYCHARPOSITION 0x0006
+typedef struct tagIMECHARPOSITION {
+ DWORD dwSize;
+ DWORD dwCharPos;
+ POINT pt;
+ UINT cLineHeight;
+ RECT rcDocument;
+} IMECHARPOSITION, *PIMECHARPOSITION;
+#endif /* IMR_QUERYCHARPOSITION */
+
+#ifndef IMR_DOCUMENTFEED
+# define IMR_DOCUMENTFEED 0x0007
+#endif /* IMR_DOCUMENTFEED */