1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2000 Red Hat, Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
23 #include "gtk/gtklabel.h"
24 #include "gtk/gtksignal.h"
25 #include "gtk/gtkwindow.h"
26 #include "gtkimcontextxim.h"
28 typedef struct _StatusWindow StatusWindow;
38 /* A context status window; these are kept in the status_windows list. */
43 /* Toplevel window to which the status window corresponds */
46 /* Signal connection ids; we connect to the toplevel */
47 gulong destroy_handler_id;
48 gulong configure_handler_id;
51 static void gtk_im_context_xim_class_init (GtkIMContextXIMClass *class);
52 static void gtk_im_context_xim_init (GtkIMContextXIM *im_context_xim);
53 static void gtk_im_context_xim_finalize (GObject *obj);
54 static void gtk_im_context_xim_set_client_window (GtkIMContext *context,
55 GdkWindow *client_window);
56 static gboolean gtk_im_context_xim_filter_keypress (GtkIMContext *context,
58 static void gtk_im_context_xim_reset (GtkIMContext *context);
59 static void gtk_im_context_xim_focus_in (GtkIMContext *context);
60 static void gtk_im_context_xim_focus_out (GtkIMContext *context);
61 static void gtk_im_context_xim_set_cursor_location (GtkIMContext *context,
63 static void gtk_im_context_xim_set_use_preedit (GtkIMContext *context,
64 gboolean use_preedit);
65 static void gtk_im_context_xim_get_preedit_string (GtkIMContext *context,
67 PangoAttrList **attrs,
70 static void status_window_show (GtkIMContextXIM *context_xim);
71 static void status_window_hide (GtkIMContextXIM *context_xim);
72 static void status_window_set_text (GtkIMContextXIM *context_xim,
75 static XIC gtk_im_context_xim_get_ic (GtkIMContextXIM *context_xim);
76 static GObjectClass *parent_class;
78 GType gtk_type_im_context_xim = 0;
80 GSList *open_ims = NULL;
82 /* List of status windows for different toplevels */
83 static GSList *status_windows = NULL;
86 gtk_im_context_xim_register_type (GTypeModule *type_module)
88 static const GTypeInfo im_context_xim_info =
90 sizeof (GtkIMContextXIMClass),
92 (GBaseFinalizeFunc) NULL,
93 (GClassInitFunc) gtk_im_context_xim_class_init,
94 NULL, /* class_finalize */
95 NULL, /* class_data */
96 sizeof (GtkIMContextXIM),
98 (GtkObjectInitFunc) gtk_im_context_xim_init,
101 gtk_type_im_context_xim =
102 g_type_module_register_type (type_module,
105 &im_context_xim_info, 0);
108 #define PREEDIT_MASK (XIMPreeditCallbacks | XIMPreeditPosition | \
109 XIMPreeditArea | XIMPreeditNothing | XIMPreeditNone)
110 #define STATUS_MASK (XIMStatusCallbacks | XIMStatusArea | \
111 XIMStatusNothing | XIMStatusNone)
112 #define ALLOWED_MASK (XIMPreeditCallbacks | XIMPreeditNothing | XIMPreeditNone | \
113 XIMStatusCallbacks | XIMStatusNothing | XIMStatusNone)
116 choose_better_style (XIMStyle style1, XIMStyle style2)
120 if (style1 == 0) return style2;
121 if (style2 == 0) return style1;
122 if ((style1 & (PREEDIT_MASK | STATUS_MASK))
123 == (style2 & (PREEDIT_MASK | STATUS_MASK)))
126 s1 = style1 & PREEDIT_MASK;
127 s2 = style2 & PREEDIT_MASK;
130 if (u & XIMPreeditCallbacks)
131 return (s1 == XIMPreeditCallbacks) ? style1 : style2;
132 else if (u & XIMPreeditPosition)
133 return (s1 == XIMPreeditPosition) ? style1 :style2;
134 else if (u & XIMPreeditArea)
135 return (s1 == XIMPreeditArea) ? style1 : style2;
136 else if (u & XIMPreeditNothing)
137 return (s1 == XIMPreeditNothing) ? style1 : style2;
139 s1 = style1 & STATUS_MASK;
140 s2 = style2 & STATUS_MASK;
142 if (u & XIMStatusCallbacks)
143 return (s1 == XIMStatusCallbacks) ? style1 : style2;
144 else if (u & XIMStatusArea)
145 return (s1 == XIMStatusArea) ? style1 : style2;
146 else if (u & XIMStatusNothing)
147 return (s1 == XIMStatusNothing) ? style1 : style2;
148 else if (u & XIMStatusNone)
149 return (s1 == XIMStatusNone) ? style1 : style2;
151 return 0; /* Get rid of stupid warning */
155 setup_im (GtkXIMInfo *info)
157 XIMStyles *xim_styles = NULL;
158 XIMValuesList *ic_values = NULL;
161 XGetIMValues (info->im,
162 XNQueryInputStyle, &xim_styles,
163 XNQueryICValuesList, &ic_values,
169 for (i = 0; i < xim_styles->count_styles; i++)
170 if ((xim_styles->supported_styles[i] & ALLOWED_MASK) == xim_styles->supported_styles[i])
171 info->style = choose_better_style (info->style,
172 xim_styles->supported_styles[i]);
179 for (i = 0; i < ic_values->count_values; i++)
180 g_print ("%s\n", ic_values->supported_values[i]);
181 for (i = 0; i < xim_styles->count_styles; i++)
182 g_print ("%#x\n", xim_styles->supported_styles[i]);
193 get_im (GdkDisplay *display,
203 info = tmp_list->data;
204 if (info->display == display &&
205 strcmp (info->locale, locale) == 0)
208 tmp_list = tmp_list->next;
213 if (XSupportsLocale ())
215 if (!XSetLocaleModifiers (""))
216 g_warning ("Unable to set locale modifiers with XSetLocaleModifiers()");
218 im = XOpenIM (GDK_DISPLAY_XDISPLAY (display), NULL, NULL, NULL);
221 g_warning ("Unable to open XIM input method, falling back to XLookupString()");
225 info = g_new (GtkXIMInfo, 1);
226 open_ims = g_slist_prepend (open_ims, im);
228 info->display = display;
229 info->locale = g_strdup (locale);
240 gtk_im_context_xim_class_init (GtkIMContextXIMClass *class)
242 GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
243 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
245 parent_class = g_type_class_peek_parent (class);
247 im_context_class->set_client_window = gtk_im_context_xim_set_client_window;
248 im_context_class->filter_keypress = gtk_im_context_xim_filter_keypress;
249 im_context_class->reset = gtk_im_context_xim_reset;
250 im_context_class->get_preedit_string = gtk_im_context_xim_get_preedit_string;
251 im_context_class->focus_in = gtk_im_context_xim_focus_in;
252 im_context_class->focus_out = gtk_im_context_xim_focus_out;
253 im_context_class->set_cursor_location = gtk_im_context_xim_set_cursor_location;
254 im_context_class->set_use_preedit = gtk_im_context_xim_set_use_preedit;
255 gobject_class->finalize = gtk_im_context_xim_finalize;
259 gtk_im_context_xim_init (GtkIMContextXIM *im_context_xim)
261 im_context_xim->use_preedit = TRUE;
262 im_context_xim->filter_key_release = FALSE;
266 gtk_im_context_xim_finalize (GObject *obj)
268 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (obj);
272 XDestroyIC (context_xim->ic);
273 context_xim->ic = NULL;
276 g_free (context_xim->locale);
277 g_free (context_xim->mb_charset);
281 reinitialize_ic (GtkIMContextXIM *context_xim)
285 XDestroyIC (context_xim->ic);
286 context_xim->ic = NULL;
288 if (context_xim->preedit_length)
290 context_xim->preedit_length = 0;
291 g_signal_emit_by_name (context_xim, "preedit_changed");
297 gtk_im_context_xim_set_client_window (GtkIMContext *context,
298 GdkWindow *client_window)
300 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
302 reinitialize_ic (context_xim);
303 context_xim->client_window = client_window;
305 if (context_xim->client_window)
306 context_xim->im_info = get_im (gdk_drawable_get_display (context_xim->client_window), context_xim->locale);
308 context_xim->im_info = NULL;
312 gtk_im_context_xim_new (void)
314 GtkIMContextXIM *result;
315 const gchar *charset;
317 result = GTK_IM_CONTEXT_XIM (g_object_new (GTK_TYPE_IM_CONTEXT_XIM, NULL));
319 result->locale = g_strdup (setlocale (LC_CTYPE, NULL));
321 g_get_charset (&charset);
322 result->mb_charset = g_strdup (charset);
324 return GTK_IM_CONTEXT (result);
328 mb_to_utf8 (GtkIMContextXIM *context_xim,
331 GError *error = NULL;
334 if (strcmp (context_xim->mb_charset, "UTF-8") == 0)
335 result = g_strdup (str);
338 result = g_convert (str, -1,
339 "UTF-8", context_xim->mb_charset,
343 g_warning ("Error converting text from IM to UTF-8: %s\n", error->message);
344 g_error_free (error);
352 gtk_im_context_xim_filter_keypress (GtkIMContext *context,
355 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
356 XIC ic = gtk_im_context_xim_get_ic (context_xim);
357 gchar static_buffer[256];
358 gchar *buffer = static_buffer;
359 gint buffer_size = sizeof(static_buffer) - 1;
363 gboolean result = FALSE;
364 GdkWindow *root_window = gdk_screen_get_root_window (gdk_drawable_get_screen (event->window));
366 XKeyPressedEvent xevent;
368 if (event->type == GDK_KEY_RELEASE && !context_xim->filter_key_release)
371 xevent.type = (event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease;
372 xevent.serial = 0; /* hope it doesn't matter */
373 xevent.send_event = event->send_event;
374 xevent.display = GDK_DRAWABLE_XDISPLAY (event->window);
375 xevent.window = GDK_DRAWABLE_XID (event->window);
376 xevent.root = GDK_DRAWABLE_XID (root_window);
377 xevent.subwindow = xevent.window;
378 xevent.time = event->time;
379 xevent.x = xevent.x_root = 0;
380 xevent.y = xevent.y_root = 0;
381 xevent.state = event->state;
382 xevent.keycode = event->hardware_keycode;
383 xevent.same_screen = True;
385 if (XFilterEvent ((XEvent *)&xevent, GDK_DRAWABLE_XID (context_xim->client_window)))
390 num_bytes = XmbLookupString (ic, &xevent, buffer, buffer_size, &keysym, &status);
393 num_bytes = XLookupString (&xevent, buffer, buffer_size, &keysym, NULL);
394 status = XLookupBoth;
397 if (status == XBufferOverflow)
399 buffer_size = num_bytes;
400 buffer = g_malloc (num_bytes + 1);
404 /* I don't know how we should properly handle XLookupKeysym or XLookupBoth
405 * here ... do input methods actually change the keysym? we can't really
406 * feed it back to accelerator processing at this point...
408 if (status == XLookupChars || status == XLookupBoth)
412 buffer[num_bytes] = '\0';
414 result_utf8 = mb_to_utf8 (context_xim, buffer);
417 if ((guchar)result_utf8[0] >= 0x20 &&
418 result_utf8[0] != 0x7f) /* Some IM have a nasty habit of converting
419 * control characters into strings
422 g_signal_emit_by_name (context, "commit", result_utf8);
426 g_free (result_utf8);
434 gtk_im_context_xim_focus_in (GtkIMContext *context)
436 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
437 XIC ic = gtk_im_context_xim_get_ic (context_xim);
444 status_window_show (context_xim);
450 gtk_im_context_xim_focus_out (GtkIMContext *context)
452 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
453 XIC ic = gtk_im_context_xim_get_ic (context_xim);
460 status_window_hide (context_xim);
466 gtk_im_context_xim_set_cursor_location (GtkIMContext *context,
469 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
470 XIC ic = gtk_im_context_xim_get_ic (context_xim);
472 XVaNestedList preedit_attr;
481 preedit_attr = XVaCreateNestedList (0,
482 XNSpotLocation, &spot,
485 XNPreeditAttributes, preedit_attr,
493 gtk_im_context_xim_set_use_preedit (GtkIMContext *context,
494 gboolean use_preedit)
496 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
498 use_preedit = use_preedit != FALSE;
500 if (context_xim->use_preedit != use_preedit)
502 context_xim->use_preedit = use_preedit;
503 reinitialize_ic (context_xim);
510 gtk_im_context_xim_reset (GtkIMContext *context)
512 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
513 XIC ic = gtk_im_context_xim_get_ic (context_xim);
516 /* restore conversion state after resetting ic later */
517 XIMPreeditState preedit_state = XIMPreeditUnKnown;
518 XVaNestedList preedit_attr;
519 gboolean have_preedit_state = FALSE;
525 if (context_xim->preedit_length == 0)
528 preedit_attr = XVaCreateNestedList(0,
529 XNPreeditState, &preedit_state,
531 if (!XGetICValues(ic,
532 XNPreeditAttributes, preedit_attr,
534 have_preedit_state = TRUE;
538 result = XmbResetIC (ic);
540 preedit_attr = XVaCreateNestedList(0,
541 XNPreeditState, preedit_state,
543 if (have_preedit_state)
545 XNPreeditAttributes, preedit_attr,
552 char *result_utf8 = mb_to_utf8 (context_xim, result);
555 g_signal_emit_by_name (context, "commit", result_utf8);
556 g_free (result_utf8);
560 if (context_xim->preedit_length)
562 context_xim->preedit_length = 0;
563 g_signal_emit_by_name (context, "preedit_changed");
569 /* Mask of feedback bits that we render
571 #define FEEDBACK_MASK (XIMReverse | XIMUnderline)
574 add_feedback_attr (PangoAttrList *attrs,
576 XIMFeedback feedback,
580 PangoAttribute *attr;
582 gint start_index = g_utf8_offset_to_pointer (str, start_pos) - str;
583 gint end_index = g_utf8_offset_to_pointer (str, end_pos) - str;
585 if (feedback & XIMUnderline)
587 attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
588 attr->start_index = start_index;
589 attr->end_index = end_index;
591 pango_attr_list_change (attrs, attr);
594 if (feedback & XIMReverse)
596 attr = pango_attr_foreground_new (0xffff, 0xffff, 0xffff);
597 attr->start_index = start_index;
598 attr->end_index = end_index;
600 pango_attr_list_change (attrs, attr);
602 attr = pango_attr_background_new (0, 0, 0);
603 attr->start_index = start_index;
604 attr->end_index = end_index;
606 pango_attr_list_change (attrs, attr);
609 if (feedback & ~FEEDBACK_MASK)
610 g_warning ("Unrendered feedback style: %#lx", feedback & ~FEEDBACK_MASK);
614 gtk_im_context_xim_get_preedit_string (GtkIMContext *context,
616 PangoAttrList **attrs,
619 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
620 gchar *utf8 = g_ucs4_to_utf8 (context_xim->preedit_chars, context_xim->preedit_length, NULL, NULL, NULL);
625 XIMFeedback last_feedback = 0;
628 *attrs = pango_attr_list_new ();
630 for (i = 0; i < context_xim->preedit_length; i++)
632 XIMFeedback new_feedback = context_xim->feedbacks[i] & FEEDBACK_MASK;
633 if (new_feedback != last_feedback)
636 add_feedback_attr (*attrs, utf8, last_feedback, start, i);
638 last_feedback = new_feedback;
644 add_feedback_attr (*attrs, utf8, last_feedback, start, i);
653 *cursor_pos = context_xim->preedit_cursor;
657 preedit_start_callback (XIC xic,
658 XPointer client_data,
661 GtkIMContext *context = GTK_IM_CONTEXT (client_data);
663 g_signal_emit_by_name (context, "preedit_start");
667 preedit_done_callback (XIC xic,
668 XPointer client_data,
671 GtkIMContext *context = GTK_IM_CONTEXT (client_data);
673 g_signal_emit_by_name (context, "preedit_end");
677 xim_text_to_utf8 (GtkIMContextXIM *context, XIMText *xim_text, gchar **text)
679 gint text_length = 0;
680 GError *error = NULL;
681 gchar *result = NULL;
683 if (xim_text && xim_text->string.multi_byte)
685 if (xim_text->encoding_is_wchar)
687 g_warning ("Wide character return from Xlib not currently supported");
692 if (strcmp (context->mb_charset, "UTF-8") == 0)
693 result = g_strdup (xim_text->string.multi_byte);
695 result = g_convert (xim_text->string.multi_byte,
703 text_length = g_utf8_strlen (result, -1);
705 if (text_length != xim_text->length)
707 g_warning ("Size mismatch when converting text from input method: supplied length = %d\n, result length = %d", xim_text->length, text_length);
712 g_warning ("Error converting text from IM to UCS-4: %s", error->message);
713 g_error_free (error);
730 preedit_draw_callback (XIC xic,
731 XPointer client_data,
732 XIMPreeditDrawCallbackStruct *call_data)
734 GtkIMContextXIM *context = GTK_IM_CONTEXT_XIM (client_data);
736 XIMText *new_xim_text = call_data->text;
737 gint new_text_length;
738 gunichar *new_text = NULL;
744 gint chg_first = CLAMP (call_data->chg_first, 0, context->preedit_length);
745 gint chg_length = CLAMP (call_data->chg_length, 0, context->preedit_length - chg_first);
747 context->preedit_cursor = call_data->caret;
749 if (chg_first != call_data->chg_first || chg_length != call_data->chg_length)
750 g_warning ("Invalid change to preedit string, first=%d length=%d (orig length == %d)",
751 call_data->chg_first, call_data->chg_length, context->preedit_length);
753 new_text_length = xim_text_to_utf8 (context, new_xim_text, &tmp);
756 new_text = g_utf8_to_ucs4_fast (tmp, -1, NULL);
760 diff = new_text_length - chg_length;
761 new_length = context->preedit_length + diff;
763 if (new_length > context->preedit_size)
765 context->preedit_size = new_length;
766 context->preedit_chars = g_renew (gunichar, context->preedit_chars, new_length);
767 context->feedbacks = g_renew (XIMFeedback, context->feedbacks, new_length);
772 for (i = chg_first + chg_length ; i < context->preedit_length; i++)
774 context->preedit_chars[i + diff] = context->preedit_chars[i];
775 context->feedbacks[i + diff] = context->feedbacks[i];
780 for (i = context->preedit_length - 1; i >= chg_first + chg_length ; i--)
782 context->preedit_chars[i + diff] = context->preedit_chars[i];
783 context->feedbacks[i + diff] = context->feedbacks[i];
787 for (i = 0; i < new_text_length; i++)
789 context->preedit_chars[chg_first + i] = new_text[i];
790 context->feedbacks[chg_first + i] = new_xim_text->feedback[i];
793 context->preedit_length += diff;
798 g_signal_emit_by_name (context, "preedit_changed");
803 preedit_caret_callback (XIC xic,
804 XPointer client_data,
805 XIMPreeditCaretCallbackStruct *call_data)
807 GtkIMContextXIM *context = GTK_IM_CONTEXT_XIM (client_data);
809 if (call_data->direction == XIMAbsolutePosition)
811 context->preedit_cursor = call_data->position;
812 g_signal_emit_by_name (context, "preedit_changed");
816 g_warning ("Caret movement command: %d %d %d not supported",
817 call_data->position, call_data->direction, call_data->style);
822 status_start_callback (XIC xic,
823 XPointer client_data,
830 status_done_callback (XIC xic,
831 XPointer client_data,
838 status_draw_callback (XIC xic,
839 XPointer client_data,
840 XIMStatusDrawCallbackStruct *call_data)
842 GtkIMContextXIM *context = GTK_IM_CONTEXT_XIM (client_data);
844 if (call_data->type == XIMTextType)
847 xim_text_to_utf8 (context, call_data->data.text, &text);
850 status_window_set_text (context, text);
852 status_window_set_text (context, "");
856 g_print ("Status drawn with bitmap - id = %#lx\n", call_data->data.bitmap);
861 get_ic_with_preedit (GtkIMContextXIM *context_xim)
864 const char *name1 = NULL;
865 XVaNestedList list1 = NULL;
866 const char *name2 = NULL;
867 XVaNestedList list2 = NULL;
869 if ((context_xim->im_info->style & PREEDIT_MASK) == XIMPreeditCallbacks)
871 context_xim->preedit_start_callback.client_data = (XPointer)context_xim;
872 context_xim->preedit_start_callback.callback = (XIMProc)preedit_start_callback;
873 context_xim->preedit_done_callback.client_data = (XPointer)context_xim;
874 context_xim->preedit_done_callback.callback = (XIMProc)preedit_done_callback;
875 context_xim->preedit_draw_callback.client_data = (XPointer)context_xim;
876 context_xim->preedit_draw_callback.callback = (XIMProc)preedit_draw_callback;
877 context_xim->preedit_caret_callback.client_data = (XPointer)context_xim;
878 context_xim->preedit_caret_callback.callback = (XIMProc)preedit_caret_callback;
879 name1 = XNPreeditAttributes;
880 list1 = XVaCreateNestedList (0,
881 XNPreeditStartCallback, &context_xim->preedit_start_callback,
882 XNPreeditDoneCallback, &context_xim->preedit_done_callback,
883 XNPreeditDrawCallback, &context_xim->preedit_draw_callback,
884 XNPreeditCaretCallback, &context_xim->preedit_caret_callback,
888 if ((context_xim->im_info->style & STATUS_MASK) == XIMStatusCallbacks)
890 XVaNestedList status_attrs;
892 context_xim->status_start_callback.client_data = (XPointer)context_xim;
893 context_xim->status_start_callback.callback = (XIMProc)status_start_callback;
894 context_xim->status_done_callback.client_data = (XPointer)context_xim;
895 context_xim->status_done_callback.callback = (XIMProc)status_done_callback;
896 context_xim->status_draw_callback.client_data = (XPointer)context_xim;
897 context_xim->status_draw_callback.callback = (XIMProc)status_draw_callback;
899 status_attrs = XVaCreateNestedList (0,
900 XNStatusStartCallback, &context_xim->status_start_callback,
901 XNStatusDoneCallback, &context_xim->status_done_callback,
902 XNStatusDrawCallback, &context_xim->status_draw_callback,
906 name1 = XNStatusAttributes;
907 list1 = status_attrs;
911 name2 = XNStatusAttributes;
912 list2 = status_attrs;
916 xic = XCreateIC (context_xim->im_info->im,
917 XNInputStyle, context_xim->im_info->style,
918 XNClientWindow, GDK_DRAWABLE_XID (context_xim->client_window),
931 get_ic_without_preedit (GtkIMContextXIM *context_xim)
933 return XCreateIC (context_xim->im_info->im,
934 XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
935 XNClientWindow, GDK_DRAWABLE_XID (context_xim->client_window),
940 gtk_im_context_xim_get_ic (GtkIMContextXIM *context_xim)
942 if (!context_xim->ic && context_xim->im_info)
944 if (!context_xim->use_preedit)
945 context_xim->ic = get_ic_without_preedit (context_xim);
947 context_xim->ic = get_ic_with_preedit (context_xim);
951 /* Don't filter key released events with XFilterEvents unless
952 * input methods ask for. This is a workaround for Solaris input
953 * method bug in C and European locales. It doubles each key
954 * stroke if both key pressed and released events are filtered.
958 XGetICValues (context_xim->ic,
959 XNFilterEvents, &mask,
961 context_xim->filter_key_release = (mask & KeyReleaseMask);
964 return context_xim->ic;
967 /**************************
969 * Status Window handling *
971 **************************/
974 status_window_expose_event (GtkWidget *widget,
975 GdkEventExpose *event)
977 gdk_draw_rectangle (widget->window,
978 widget->style->base_gc [GTK_STATE_NORMAL],
981 widget->allocation.width, widget->allocation.height);
982 gdk_draw_rectangle (widget->window,
983 widget->style->text_gc [GTK_STATE_NORMAL],
986 widget->allocation.width - 1, widget->allocation.height - 1);
992 status_window_style_set (GtkWidget *toplevel,
993 GtkStyle *previous_style,
998 for (i = 0; i < 5; i++)
999 gtk_widget_modify_fg (label, i, &toplevel->style->text[i]);
1002 /* Frees a status window and removes its link from the status_windows list */
1004 status_window_free (StatusWindow *status_window)
1006 status_windows = g_slist_remove (status_windows, status_window);
1008 g_signal_handler_disconnect (status_window->toplevel, status_window->destroy_handler_id);
1009 g_signal_handler_disconnect (status_window->toplevel, status_window->configure_handler_id);
1010 gtk_widget_destroy (status_window->window);
1011 g_object_set_data (G_OBJECT (status_window->toplevel), "gtk-im-xim-status-window", NULL);
1013 g_free (status_window);
1017 status_window_configure (GtkWidget *toplevel,
1018 GdkEventConfigure *event,
1019 StatusWindow *status_window)
1022 GtkRequisition requisition;
1024 gint height = gdk_screen_get_height (gtk_widget_get_screen (toplevel));
1026 gdk_window_get_frame_extents (toplevel->window, &rect);
1027 gtk_widget_size_request (status_window->window, &requisition);
1029 if (rect.y + rect.height + requisition.height < height)
1030 y = rect.y + rect.height;
1032 y = height - requisition.height;
1034 gtk_window_move (GTK_WINDOW (status_window->window), rect.x, y);
1040 status_window_get (GtkIMContextXIM *context_xim,
1043 GdkWindow *toplevel_gdk;
1044 GtkWidget *toplevel;
1046 StatusWindow *status_window;
1047 GtkWidget *status_label;
1049 if (!context_xim->client_window)
1052 toplevel_gdk = context_xim->client_window;
1055 GdkWindow *parent = gdk_window_get_parent (toplevel_gdk);
1056 if (parent == gdk_get_default_root_window ())
1059 toplevel_gdk = parent;
1062 gdk_window_get_user_data (toplevel_gdk, (gpointer *)&toplevel);
1066 status_window = g_object_get_data (G_OBJECT (toplevel), "gtk-im-xim-status-window");
1068 return status_window->window;
1072 status_window = g_new (StatusWindow, 1);
1073 status_window->window = gtk_window_new (GTK_WINDOW_POPUP);
1074 status_window->toplevel = toplevel;
1076 status_windows = g_slist_prepend (status_windows, status_window);
1078 window = status_window->window;
1080 gtk_window_set_policy (GTK_WINDOW (window), FALSE, FALSE, FALSE);
1081 gtk_widget_set_app_paintable (window, TRUE);
1083 status_label = gtk_label_new ("");
1084 gtk_misc_set_padding (GTK_MISC (status_label), 1, 1);
1085 gtk_widget_show (status_label);
1087 gtk_container_add (GTK_CONTAINER (window), status_label);
1089 status_window->destroy_handler_id = g_signal_connect_swapped (toplevel, "destroy",
1090 G_CALLBACK (status_window_free),
1092 status_window->configure_handler_id = g_signal_connect (toplevel, "configure_event",
1093 G_CALLBACK (status_window_configure),
1096 status_window_configure (toplevel, NULL, status_window);
1098 g_signal_connect (window, "style_set",
1099 G_CALLBACK (status_window_style_set), status_label);
1100 g_signal_connect (window, "expose_event",
1101 G_CALLBACK (status_window_expose_event), NULL);
1103 g_object_set_data (G_OBJECT (toplevel), "gtk-im-xim-status-window", status_window);
1109 status_window_has_text (GtkWidget *status_window)
1111 GtkWidget *label = GTK_BIN (status_window)->child;
1112 const gchar *text = gtk_label_get_text (GTK_LABEL (label));
1114 return text[0] != '\0';
1118 status_window_show (GtkIMContextXIM *context_xim)
1120 GtkWidget *status_window = status_window_get (context_xim, TRUE);
1122 context_xim->status_visible = TRUE;
1124 if (status_window && status_window_has_text (status_window))
1125 gtk_widget_show (status_window);
1129 status_window_hide (GtkIMContextXIM *context_xim)
1131 GtkWidget *status_window = status_window_get (context_xim, FALSE);
1133 context_xim->status_visible = FALSE;
1136 gtk_widget_hide (status_window);
1140 status_window_set_text (GtkIMContextXIM *context_xim,
1143 GtkWidget *status_window = status_window_get (context_xim, TRUE);
1147 GtkWidget *label = GTK_BIN (status_window)->child;
1148 gtk_label_set_text (GTK_LABEL (label), text);
1150 if (context_xim->status_visible && status_window_has_text (status_window))
1151 gtk_widget_show (status_window);
1153 gtk_widget_hide (status_window);
1158 * gtk_im_context_xim_shutdown:
1160 * Destroys all the status windows that are kept by the XIM contexts. This
1161 * function should only be called by the XIM module exit routine.
1164 gtk_im_context_xim_shutdown (void)
1166 while (status_windows)
1167 status_window_free (status_windows->data);