#include "locale.h"
#include <string.h>
+#include "gtk/gtklabel.h"
#include "gtk/gtksignal.h"
+#include "gtk/gtkwindow.h"
#include "gtkimcontextxim.h"
struct _GtkXIMInfo
static void gtk_im_context_xim_focus_out (GtkIMContext *context);
static void gtk_im_context_xim_set_cursor_location (GtkIMContext *context,
GdkRectangle *area);
+static void gtk_im_context_xim_set_use_preedit (GtkIMContext *context,
+ gboolean use_preedit);
static void gtk_im_context_xim_get_preedit_string (GtkIMContext *context,
gchar **str,
PangoAttrList **attrs,
gint *cursor_pos);
+static void status_window_show (GtkIMContextXIM *context_xim);
+static void status_window_hide (GtkIMContextXIM *context_xim);
+static void status_window_set_text (GtkIMContextXIM *context_xim,
+ const gchar *text);
+
static XIC gtk_im_context_xim_get_ic (GtkIMContextXIM *context_xim);
static GObjectClass *parent_class;
im_context_class->focus_in = gtk_im_context_xim_focus_in;
im_context_class->focus_out = gtk_im_context_xim_focus_out;
im_context_class->set_cursor_location = gtk_im_context_xim_set_cursor_location;
+ im_context_class->set_use_preedit = gtk_im_context_xim_set_use_preedit;
gobject_class->finalize = gtk_im_context_xim_finalize;
}
static void
gtk_im_context_xim_init (GtkIMContextXIM *im_context_xim)
{
+ im_context_xim->use_preedit = TRUE;
}
static void
}
static void
-gtk_im_context_xim_set_client_window (GtkIMContext *context,
- GdkWindow *client_window)
+reinitialize_ic (GtkIMContextXIM *context_xim)
{
- GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
-
if (context_xim->ic)
{
XDestroyIC (context_xim->ic);
context_xim->ic = NULL;
+
+ if (context_xim->preedit_length)
+ {
+ context_xim->preedit_length = 0;
+ g_signal_emit_by_name (context_xim, "preedit_changed");
+ }
}
+}
+
+static void
+gtk_im_context_xim_set_client_window (GtkIMContext *context,
+ GdkWindow *client_window)
+{
+ GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
+ reinitialize_ic (context_xim);
context_xim->client_window = client_window;
}
{
GtkXIMInfo *info;
GtkIMContextXIM *result;
- gchar *charset;
+ const gchar *charset;
info = get_im (setlocale (LC_CTYPE, NULL));
if (!info)
return NULL;
- result = GTK_IM_CONTEXT_XIM (gtk_type_new (GTK_TYPE_IM_CONTEXT_XIM));
+ result = GTK_IM_CONTEXT_XIM (g_object_new (GTK_TYPE_IM_CONTEXT_XIM, NULL));
result->im_info = info;
GError *error = NULL;
gchar *result;
- result = g_convert (str, -1,
- "UTF-8", context_xim->mb_charset,
- NULL, NULL, &error);
-
- if (!result)
+ if (strcmp (context_xim->mb_charset, "UTF-8") == 0)
+ result = g_strdup (str);
+ else
{
- g_warning ("Error converting text from IM to UTF-8: %s\n", error->message);
- g_error_free (error);
+ result = g_convert (str, -1,
+ "UTF-8", context_xim->mb_charset,
+ NULL, NULL, &error);
+ if (!result)
+ {
+ g_warning ("Error converting text from IM to UTF-8: %s\n", error->message);
+ g_error_free (error);
+ }
}
return result;
if (!ic)
return FALSE;
- xevent.type = KeyPress;
+ xevent.type = (event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease;
xevent.serial = 0; /* hope it doesn't matter */
xevent.send_event = event->send_event;
xevent.display = GDK_DRAWABLE_XDISPLAY (event->window);
result_utf8 = mb_to_utf8 (context_xim, buffer);
if (result_utf8)
{
- if ((guchar)result_utf8[0] >= 0x20) /* Some IM have a nasty habit of converting
- * control characters into strings
- */
+ if ((guchar)result_utf8[0] >= 0x20 &&
+ result_utf8[0] != 0x7f) /* Some IM have a nasty habit of converting
+ * control characters into strings
+ */
{
- gtk_signal_emit_by_name (GTK_OBJECT (context), "commit", result_utf8);
+ g_signal_emit_by_name (context, "commit", result_utf8);
result = TRUE;
}
}
}
- return FALSE;
+ return result;
}
static void
return;
XSetICFocus (ic);
+
+ status_window_show (context_xim);
+
return;
}
return;
XUnsetICFocus (ic);
+
+ status_window_hide (context_xim);
+
return;
}
return;
}
+static void
+gtk_im_context_xim_set_use_preedit (GtkIMContext *context,
+ gboolean use_preedit)
+{
+ GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
+
+ use_preedit = use_preedit != FALSE;
+
+ if (context_xim->use_preedit != use_preedit)
+ {
+ context_xim->use_preedit = use_preedit;
+ reinitialize_ic (context_xim);
+ }
+
+ return;
+}
+
static void
gtk_im_context_xim_reset (GtkIMContext *context)
{
return;
+ if (context_xim->preedit_length == 0)
+ return;
+
preedit_attr = XVaCreateNestedList(0,
XNPreeditState, &preedit_state,
0);
char *result_utf8 = mb_to_utf8 (context_xim, result);
if (result_utf8)
{
- gtk_signal_emit_by_name (GTK_OBJECT (context), "commit", result_utf8);
+ g_signal_emit_by_name (context, "commit", result_utf8);
g_free (result_utf8);
}
}
if (context_xim->preedit_length)
{
context_xim->preedit_length = 0;
- gtk_signal_emit_by_name (GTK_OBJECT (context), "preedit_changed");
+ g_signal_emit_by_name (context, "preedit_changed");
}
XFree (result);
{
GtkIMContext *context = GTK_IM_CONTEXT (client_data);
- gtk_signal_emit_by_name (GTK_OBJECT (context), "preedit_start");
- g_print ("Starting preedit!\n");
+ g_signal_emit_by_name (context, "preedit_start");
}
static void
{
GtkIMContext *context = GTK_IM_CONTEXT (client_data);
- gtk_signal_emit_by_name (GTK_OBJECT (context), "preedit_end");
- g_print ("Ending preedit!\n");
+ g_signal_emit_by_name (context, "preedit_end");
}
static gint
return 0;
}
- result = g_convert (xim_text->string.multi_byte,
- -1,
- "UTF-8",
- context->mb_charset,
- NULL, &text_length, &error);
+ if (strcmp (context->mb_charset, "UTF-8") == 0)
+ result = g_strdup (xim_text->string.multi_byte);
+ else
+ result = g_convert (xim_text->string.multi_byte,
+ -1,
+ "UTF-8",
+ context->mb_charset,
+ NULL, NULL, &error);
if (result)
{
{
g_warning ("Error converting text from IM to UCS-4: %s", error->message);
g_error_free (error);
+
+ *text = NULL;
+ return 0;
}
*text = result;
if (new_text)
g_free (new_text);
- gtk_signal_emit_by_name (GTK_OBJECT (context), "preedit_changed");
+ g_signal_emit_by_name (context, "preedit_changed");
}
if (call_data->direction == XIMAbsolutePosition)
{
context->preedit_cursor = call_data->position;
- gtk_signal_emit_by_name (GTK_OBJECT (context), "preedit_changed");
+ g_signal_emit_by_name (context, "preedit_changed");
}
else
{
XPointer client_data,
XPointer call_data)
{
- g_print ("Status start\n");
+ return;
}
static void
XPointer client_data,
XPointer call_data)
{
- g_print ("Status done!\n");
+ return;
}
static void
{
GtkIMContextXIM *context = GTK_IM_CONTEXT_XIM (client_data);
- g_print ("Status draw\n");
if (call_data->type == XIMTextType)
{
gchar *text;
xim_text_to_utf8 (context, call_data->data.text, &text);
if (text)
- g_print (" %s\n", text);
+ status_window_set_text (context, text);
+ else
+ status_window_set_text (context, "");
}
else /* bitmap */
{
- g_print (" bitmap id = %#lx\n", call_data->data.bitmap);
+ g_print ("Status drawn with bitmap - id = %#lx\n", call_data->data.bitmap);
}
}
if (!context_xim->ic && context_xim->client_window)
{
+ if (!context_xim->use_preedit)
+ {
+ context_xim->ic = XCreateIC (context_xim->im_info->im,
+ XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
+ XNClientWindow, GDK_DRAWABLE_XID (context_xim->client_window),
+ NULL);
+ return context_xim->ic;
+ }
+
if ((context_xim->im_info->style & PREEDIT_MASK) == XIMPreeditCallbacks)
{
context_xim->preedit_start_callback.client_data = (XPointer)context_xim;
return context_xim->ic;
}
+
+/**************************
+ * *
+ * Status Window handling *
+ * *
+ **************************/
+
+static gboolean
+status_window_expose_event (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ gdk_draw_rectangle (widget->window,
+ widget->style->base_gc [GTK_STATE_NORMAL],
+ TRUE,
+ 0, 0,
+ widget->allocation.width, widget->allocation.height);
+ gdk_draw_rectangle (widget->window,
+ widget->style->text_gc [GTK_STATE_NORMAL],
+ FALSE,
+ 0, 0,
+ widget->allocation.width - 1, widget->allocation.height - 1);
+
+ return FALSE;
+}
+
+static void
+status_window_style_set (GtkWidget *toplevel,
+ GtkStyle *previous_style,
+ GtkWidget *label)
+{
+ gint i;
+
+ for (i = 0; i < 5; i++)
+ gtk_widget_modify_fg (label, i, &toplevel->style->text[i]);
+}
+
+static void
+status_window_destroy (GtkWidget *toplevel,
+ GtkWidget *status_window)
+{
+ gtk_widget_destroy (status_window);
+ g_object_set_data (G_OBJECT (toplevel), "gtk-im-xim-status-window", NULL);
+}
+
+static gboolean
+status_window_configure (GtkWidget *toplevel,
+ GdkEventConfigure *event,
+ GtkWidget *status_window)
+{
+ GdkRectangle rect;
+ GtkRequisition requisition;
+ gint y;
+
+ gdk_window_get_frame_extents (toplevel->window, &rect);
+ gtk_widget_size_request (status_window, &requisition);
+
+ if (rect.y + rect.height + requisition.height < gdk_screen_height ())
+ y = rect.y + rect.height;
+ else
+ y = gdk_screen_height () - requisition.height;
+
+ gtk_window_move (GTK_WINDOW (status_window), rect.x, y);
+
+ return FALSE;
+}
+
+static GtkWidget *
+status_window_get (GtkIMContextXIM *context_xim,
+ gboolean create)
+{
+ GdkWindow *toplevel_gdk;
+ GtkWidget *toplevel;
+ GtkWidget *status_window;
+ GtkWidget *status_label;
+
+ if (!context_xim->client_window)
+ return NULL;
+
+ toplevel_gdk = context_xim->client_window;
+ while (TRUE)
+ {
+ GdkWindow *parent = gdk_window_get_parent (toplevel_gdk);
+ if (parent == gdk_get_default_root_window ())
+ break;
+ else
+ toplevel_gdk = parent;
+ }
+
+ gdk_window_get_user_data (toplevel_gdk, (gpointer *)&toplevel);
+ if (!toplevel)
+ return NULL;
+
+ status_window = g_object_get_data (G_OBJECT (toplevel), "gtk-im-xim-status-window");
+ if (status_window || !create)
+ return status_window;
+
+ status_window = gtk_window_new (GTK_WINDOW_POPUP);
+
+ gtk_window_set_policy (GTK_WINDOW (status_window), FALSE, FALSE, FALSE);
+ gtk_widget_set_app_paintable (status_window, TRUE);
+
+ status_label = gtk_label_new ("");
+ gtk_misc_set_padding (GTK_MISC (status_label), 1, 1);
+ gtk_widget_show (status_label);
+
+ gtk_container_add (GTK_CONTAINER (status_window), status_label);
+
+ g_signal_connect (toplevel, "destroy",
+ G_CALLBACK (status_window_destroy), status_window);
+ g_signal_connect (toplevel, "configure_event",
+ G_CALLBACK (status_window_configure), status_window);
+
+ status_window_configure (toplevel, NULL, status_window);
+
+ g_signal_connect (status_window, "style_set",
+ G_CALLBACK (status_window_style_set), status_label);
+ g_signal_connect (status_window, "expose_event",
+ G_CALLBACK (status_window_expose_event), NULL);
+
+ g_object_set_data (G_OBJECT (toplevel), "gtk-im-xim-status-window", status_window);
+
+ return status_window;
+}
+
+static gboolean
+status_window_has_text (GtkWidget *status_window)
+{
+ GtkWidget *label = GTK_BIN (status_window)->child;
+ const gchar *text = gtk_label_get_text (GTK_LABEL (label));
+
+ return text[0] != '\0';
+}
+
+static void
+status_window_show (GtkIMContextXIM *context_xim)
+{
+ GtkWidget *status_window = status_window_get (context_xim, TRUE);
+
+ context_xim->status_visible = TRUE;
+
+ if (status_window && status_window_has_text (status_window))
+ gtk_widget_show (status_window);
+}
+
+static void
+status_window_hide (GtkIMContextXIM *context_xim)
+{
+ GtkWidget *status_window = status_window_get (context_xim, FALSE);
+
+ context_xim->status_visible = FALSE;
+
+ if (status_window)
+ gtk_widget_hide (status_window);
+}
+
+static void
+status_window_set_text (GtkIMContextXIM *context_xim,
+ const gchar *text)
+{
+ GtkWidget *status_window = status_window_get (context_xim, TRUE);
+
+ if (status_window)
+ {
+ GtkWidget *label = GTK_BIN (status_window)->child;
+ gtk_label_set_text (GTK_LABEL (label), text);
+
+ if (context_xim->status_visible && status_window_has_text (status_window))
+ gtk_widget_show (status_window);
+ else
+ gtk_widget_hide (status_window);
+ }
+}