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/gtkintl.h"
24 #include "gtk/gtklabel.h"
25 #include "gtk/gtksignal.h"
26 #include "gtk/gtkwindow.h"
27 #include "gtkimcontextxim.h"
29 typedef struct _StatusWindow StatusWindow;
36 XIMStyle preedit_style_setting;
37 XIMStyle status_style_setting;
39 GtkSettings *settings;
42 XIMStyles *xim_styles;
46 /* A context status window; these are kept in the status_windows list. */
51 /* Toplevel window to which the status window corresponds */
54 /* Signal connection ids; we connect to the toplevel */
55 gulong destroy_handler_id;
56 gulong configure_handler_id;
59 static void gtk_im_context_xim_class_init (GtkIMContextXIMClass *class);
60 static void gtk_im_context_xim_init (GtkIMContextXIM *im_context_xim);
61 static void gtk_im_context_xim_finalize (GObject *obj);
62 static void gtk_im_context_xim_set_client_window (GtkIMContext *context,
63 GdkWindow *client_window);
64 static gboolean gtk_im_context_xim_filter_keypress (GtkIMContext *context,
66 static void gtk_im_context_xim_reset (GtkIMContext *context);
67 static void gtk_im_context_xim_focus_in (GtkIMContext *context);
68 static void gtk_im_context_xim_focus_out (GtkIMContext *context);
69 static void gtk_im_context_xim_set_cursor_location (GtkIMContext *context,
71 static void gtk_im_context_xim_set_use_preedit (GtkIMContext *context,
72 gboolean use_preedit);
73 static void gtk_im_context_xim_get_preedit_string (GtkIMContext *context,
75 PangoAttrList **attrs,
78 static void reinitialize_ic (GtkIMContextXIM *context_xim,
79 gboolean send_signal);
80 static void set_ic_client_window (GtkIMContextXIM *context_xim,
81 GdkWindow *client_window,
82 gboolean send_signal);
84 static void setup_styles (GtkXIMInfo *info);
86 static void status_window_show (GtkIMContextXIM *context_xim);
87 static void status_window_hide (GtkIMContextXIM *context_xim);
88 static void status_window_set_text (GtkIMContextXIM *context_xim,
91 static XIC gtk_im_context_xim_get_ic (GtkIMContextXIM *context_xim);
92 static GObjectClass *parent_class;
94 GType gtk_type_im_context_xim = 0;
96 GSList *open_ims = NULL;
98 /* List of status windows for different toplevels */
99 static GSList *status_windows = NULL;
102 gtk_im_context_xim_register_type (GTypeModule *type_module)
104 static const GTypeInfo im_context_xim_info =
106 sizeof (GtkIMContextXIMClass),
107 (GBaseInitFunc) NULL,
108 (GBaseFinalizeFunc) NULL,
109 (GClassInitFunc) gtk_im_context_xim_class_init,
110 NULL, /* class_finalize */
111 NULL, /* class_data */
112 sizeof (GtkIMContextXIM),
114 (GInstanceInitFunc) gtk_im_context_xim_init,
117 gtk_type_im_context_xim =
118 g_type_module_register_type (type_module,
121 &im_context_xim_info, 0);
124 #define PREEDIT_MASK (XIMPreeditCallbacks | XIMPreeditPosition | \
125 XIMPreeditArea | XIMPreeditNothing | XIMPreeditNone)
126 #define STATUS_MASK (XIMStatusCallbacks | XIMStatusArea | \
127 XIMStatusNothing | XIMStatusNone)
128 #define ALLOWED_MASK (XIMPreeditCallbacks | XIMPreeditNothing | XIMPreeditNone | \
129 XIMStatusCallbacks | XIMStatusNothing | XIMStatusNone)
132 choose_better_style (XIMStyle style1, XIMStyle style2)
136 if (style1 == 0) return style2;
137 if (style2 == 0) return style1;
138 if ((style1 & (PREEDIT_MASK | STATUS_MASK))
139 == (style2 & (PREEDIT_MASK | STATUS_MASK)))
142 s1 = style1 & PREEDIT_MASK;
143 s2 = style2 & PREEDIT_MASK;
146 if (u & XIMPreeditCallbacks)
147 return (s1 == XIMPreeditCallbacks) ? style1 : style2;
148 else if (u & XIMPreeditPosition)
149 return (s1 == XIMPreeditPosition) ? style1 :style2;
150 else if (u & XIMPreeditArea)
151 return (s1 == XIMPreeditArea) ? style1 : style2;
152 else if (u & XIMPreeditNothing)
153 return (s1 == XIMPreeditNothing) ? style1 : style2;
154 else if (u & XIMPreeditNone)
155 return (s1 == XIMPreeditNone) ? style1 : style2;
157 s1 = style1 & STATUS_MASK;
158 s2 = style2 & STATUS_MASK;
160 if (u & XIMStatusCallbacks)
161 return (s1 == XIMStatusCallbacks) ? style1 : style2;
162 else if (u & XIMStatusArea)
163 return (s1 == XIMStatusArea) ? style1 : style2;
164 else if (u & XIMStatusNothing)
165 return (s1 == XIMStatusNothing) ? style1 : style2;
166 else if (u & XIMStatusNone)
167 return (s1 == XIMStatusNone) ? style1 : style2;
169 return 0; /* Get rid of stupid warning */
173 reinitialize_all_ics (GtkXIMInfo *info)
177 for (tmp_list = info->ics; tmp_list; tmp_list = tmp_list->next)
178 reinitialize_ic (tmp_list->data, TRUE);
182 status_style_change (GtkXIMInfo *info)
184 GtkIMStatusStyle status_style;
186 g_object_get (info->settings,
187 "gtk-im-status-style", &status_style,
189 if (status_style == GTK_IM_STATUS_CALLBACK)
190 info->status_style_setting = XIMStatusCallbacks;
191 else if (status_style == GTK_IM_STATUS_NOTHING)
192 info->status_style_setting = XIMStatusNothing;
198 reinitialize_all_ics (info);
202 preedit_style_change (GtkXIMInfo *info)
204 GtkIMPreeditStyle preedit_style;
205 g_object_get (info->settings,
206 "gtk-im-preedit-style", &preedit_style,
208 if (preedit_style == GTK_IM_PREEDIT_CALLBACK)
209 info->preedit_style_setting = XIMPreeditCallbacks;
210 else if (preedit_style == GTK_IM_PREEDIT_NOTHING)
211 info->preedit_style_setting = XIMPreeditNothing;
217 reinitialize_all_ics (info);
221 setup_styles (GtkXIMInfo *info)
224 unsigned long settings_preference;
225 XIMStyles *xim_styles = info->xim_styles;
227 settings_preference = info->status_style_setting|info->preedit_style_setting;
231 for (i = 0; i < xim_styles->count_styles; i++)
232 if ((xim_styles->supported_styles[i] & ALLOWED_MASK) == xim_styles->supported_styles[i])
234 if (settings_preference == xim_styles->supported_styles[i])
236 info->style = settings_preference;
239 info->style = choose_better_style (info->style,
240 xim_styles->supported_styles[i]);
243 if (info->style == 0)
244 info->style = XIMPreeditNothing | XIMStatusNothing;
248 setup_im (GtkXIMInfo *info)
250 XIMValuesList *ic_values = NULL;
252 if (info->im == NULL)
255 XGetIMValues (info->im,
256 XNQueryInputStyle, &info->xim_styles,
257 XNQueryICValuesList, &ic_values,
260 info->settings = gtk_settings_get_for_screen (info->screen);
262 if (!g_object_class_find_property (G_OBJECT_GET_CLASS (info->settings),
263 "gtk-im-preedit-style"))
264 gtk_settings_install_property (g_param_spec_enum ("gtk-im-preedit-style",
265 _("IM Preedit style"),
266 _("How to draw the input method preedit string"),
267 GTK_TYPE_IM_PREEDIT_STYLE,
268 GTK_IM_PREEDIT_CALLBACK,
271 if (!g_object_class_find_property (G_OBJECT_GET_CLASS (info->settings),
272 "gtk-im-status-style"))
273 gtk_settings_install_property (g_param_spec_enum ("gtk-im-status-style",
274 _("IM Status style"),
275 _("How to draw the input method statusbar"),
276 GTK_TYPE_IM_STATUS_STYLE,
277 GTK_IM_STATUS_CALLBACK,
280 info->status_set = g_signal_connect_swapped (info->settings,
281 "notify::gtk-im-status-style",
282 G_CALLBACK (status_style_change),
284 info->preedit_set = g_signal_connect_swapped (info->settings,
285 "notify::gtk-im-preedit-style",
286 G_CALLBACK (preedit_style_change),
289 status_style_change (info);
290 preedit_style_change (info);
295 for (i = 0; i < ic_values->count_values; i++)
296 g_print ("%s\n", ic_values->supported_values[i]);
297 for (i = 0; i < xim_styles->count_styles; i++)
298 g_print ("%#x\n", xim_styles->supported_styles[i]);
307 xim_info_display_closed (GdkDisplay *display,
311 GSList *ics, *tmp_list;
313 open_ims = g_slist_remove (open_ims, info);
318 for (tmp_list = ics; tmp_list; tmp_list = tmp_list->next)
319 set_ic_client_window (tmp_list->data, NULL, TRUE);
323 g_signal_handler_disconnect (info->settings, info->status_set);
324 g_signal_handler_disconnect (info->settings, info->preedit_set);
326 XFree (info->xim_styles->supported_styles);
327 XFree (info->xim_styles);
328 g_free (info->locale);
337 get_im (GdkWindow *client_window,
343 GdkScreen *screen = gdk_drawable_get_screen (client_window);
344 GdkDisplay *display = gdk_screen_get_display (screen);
349 info = tmp_list->data;
350 if (info->screen == screen &&
351 strcmp (info->locale, locale) == 0)
354 tmp_list = tmp_list->next;
359 if (XSupportsLocale ())
361 if (!XSetLocaleModifiers (""))
362 g_warning ("Unable to set locale modifiers with XSetLocaleModifiers()");
364 im = XOpenIM (GDK_DISPLAY_XDISPLAY (display), NULL, NULL, NULL);
367 g_warning ("Unable to open XIM input method, falling back to XLookupString()");
369 info = g_new (GtkXIMInfo, 1);
370 open_ims = g_slist_prepend (open_ims, info);
372 info->screen = screen;
373 info->locale = g_strdup (locale);
375 info->xim_styles = NULL;
376 info->preedit_style_setting = 0;
377 info->status_style_setting = 0;
378 info->settings = NULL;
379 info->preedit_set = 0;
380 info->status_set = 0;
385 g_signal_connect (display, "closed",
386 G_CALLBACK (xim_info_display_closed), info);
393 gtk_im_context_xim_class_init (GtkIMContextXIMClass *class)
395 GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
396 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
398 parent_class = g_type_class_peek_parent (class);
400 im_context_class->set_client_window = gtk_im_context_xim_set_client_window;
401 im_context_class->filter_keypress = gtk_im_context_xim_filter_keypress;
402 im_context_class->reset = gtk_im_context_xim_reset;
403 im_context_class->get_preedit_string = gtk_im_context_xim_get_preedit_string;
404 im_context_class->focus_in = gtk_im_context_xim_focus_in;
405 im_context_class->focus_out = gtk_im_context_xim_focus_out;
406 im_context_class->set_cursor_location = gtk_im_context_xim_set_cursor_location;
407 im_context_class->set_use_preedit = gtk_im_context_xim_set_use_preedit;
408 gobject_class->finalize = gtk_im_context_xim_finalize;
412 gtk_im_context_xim_init (GtkIMContextXIM *im_context_xim)
414 im_context_xim->use_preedit = TRUE;
415 im_context_xim->filter_key_release = FALSE;
416 im_context_xim->status_visible = FALSE;
420 gtk_im_context_xim_finalize (GObject *obj)
422 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (obj);
424 set_ic_client_window (context_xim, NULL, FALSE);
426 g_free (context_xim->locale);
427 g_free (context_xim->mb_charset);
431 reinitialize_ic (GtkIMContextXIM *context_xim,
432 gboolean send_signal)
436 XDestroyIC (context_xim->ic);
437 status_window_hide (context_xim);
438 context_xim->ic = NULL;
440 if (context_xim->preedit_length)
442 context_xim->preedit_length = 0;
444 g_signal_emit_by_name (context_xim, "preedit_changed");
450 set_ic_client_window (GtkIMContextXIM *context_xim,
451 GdkWindow *client_window,
452 gboolean send_signal)
454 reinitialize_ic (context_xim, send_signal);
455 if (context_xim->client_window)
457 context_xim->im_info->ics = g_slist_remove (context_xim->im_info->ics, context_xim);
458 context_xim->im_info = NULL;
461 context_xim->client_window = client_window;
463 if (context_xim->client_window)
465 context_xim->im_info = get_im (context_xim->client_window, context_xim->locale);
466 context_xim->im_info->ics = g_slist_prepend (context_xim->im_info->ics, context_xim);
471 gtk_im_context_xim_set_client_window (GtkIMContext *context,
472 GdkWindow *client_window)
474 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
476 set_ic_client_window (context_xim, client_window, TRUE);
480 gtk_im_context_xim_new (void)
482 GtkIMContextXIM *result;
483 const gchar *charset;
485 result = GTK_IM_CONTEXT_XIM (g_object_new (GTK_TYPE_IM_CONTEXT_XIM, NULL));
487 result->locale = g_strdup (setlocale (LC_CTYPE, NULL));
489 g_get_charset (&charset);
490 result->mb_charset = g_strdup (charset);
492 return GTK_IM_CONTEXT (result);
496 mb_to_utf8 (GtkIMContextXIM *context_xim,
499 GError *error = NULL;
502 if (strcmp (context_xim->mb_charset, "UTF-8") == 0)
503 result = g_strdup (str);
506 result = g_convert (str, -1,
507 "UTF-8", context_xim->mb_charset,
511 g_warning ("Error converting text from IM to UTF-8: %s\n", error->message);
512 g_error_free (error);
520 gtk_im_context_xim_filter_keypress (GtkIMContext *context,
523 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
524 XIC ic = gtk_im_context_xim_get_ic (context_xim);
525 gchar static_buffer[256];
526 gchar *buffer = static_buffer;
527 gint buffer_size = sizeof(static_buffer) - 1;
531 gboolean result = FALSE;
532 GdkWindow *root_window = gdk_screen_get_root_window (gdk_drawable_get_screen (event->window));
534 XKeyPressedEvent xevent;
536 if (event->type == GDK_KEY_RELEASE && !context_xim->filter_key_release)
539 xevent.type = (event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease;
540 xevent.serial = 0; /* hope it doesn't matter */
541 xevent.send_event = event->send_event;
542 xevent.display = GDK_DRAWABLE_XDISPLAY (event->window);
543 xevent.window = GDK_DRAWABLE_XID (event->window);
544 xevent.root = GDK_DRAWABLE_XID (root_window);
545 xevent.subwindow = xevent.window;
546 xevent.time = event->time;
547 xevent.x = xevent.x_root = 0;
548 xevent.y = xevent.y_root = 0;
549 xevent.state = event->state;
550 xevent.keycode = event->hardware_keycode;
551 xevent.same_screen = True;
553 if (XFilterEvent ((XEvent *)&xevent, GDK_DRAWABLE_XID (context_xim->client_window)))
558 num_bytes = XmbLookupString (ic, &xevent, buffer, buffer_size, &keysym, &status);
561 num_bytes = XLookupString (&xevent, buffer, buffer_size, &keysym, NULL);
562 status = XLookupBoth;
565 if (status == XBufferOverflow)
567 buffer_size = num_bytes;
568 if (buffer != static_buffer)
570 buffer = g_malloc (num_bytes + 1);
574 /* I don't know how we should properly handle XLookupKeysym or XLookupBoth
575 * here ... do input methods actually change the keysym? we can't really
576 * feed it back to accelerator processing at this point...
578 if (status == XLookupChars || status == XLookupBoth)
582 buffer[num_bytes] = '\0';
584 result_utf8 = mb_to_utf8 (context_xim, buffer);
587 if ((guchar)result_utf8[0] >= 0x20 &&
588 result_utf8[0] != 0x7f) /* Some IM have a nasty habit of converting
589 * control characters into strings
592 g_signal_emit_by_name (context, "commit", result_utf8);
596 g_free (result_utf8);
600 if (buffer != static_buffer)
607 gtk_im_context_xim_focus_in (GtkIMContext *context)
609 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
610 XIC ic = gtk_im_context_xim_get_ic (context_xim);
617 status_window_show (context_xim);
623 gtk_im_context_xim_focus_out (GtkIMContext *context)
625 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
626 XIC ic = gtk_im_context_xim_get_ic (context_xim);
633 status_window_hide (context_xim);
639 gtk_im_context_xim_set_cursor_location (GtkIMContext *context,
642 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
643 XIC ic = gtk_im_context_xim_get_ic (context_xim);
645 XVaNestedList preedit_attr;
654 preedit_attr = XVaCreateNestedList (0,
655 XNSpotLocation, &spot,
658 XNPreeditAttributes, preedit_attr,
666 gtk_im_context_xim_set_use_preedit (GtkIMContext *context,
667 gboolean use_preedit)
669 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
671 use_preedit = use_preedit != FALSE;
673 if (context_xim->use_preedit != use_preedit)
675 context_xim->use_preedit = use_preedit;
676 reinitialize_ic (context_xim, TRUE);
683 gtk_im_context_xim_reset (GtkIMContext *context)
685 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
686 XIC ic = gtk_im_context_xim_get_ic (context_xim);
689 /* restore conversion state after resetting ic later */
690 XIMPreeditState preedit_state = XIMPreeditUnKnown;
691 XVaNestedList preedit_attr;
692 gboolean have_preedit_state = FALSE;
698 if (context_xim->preedit_length == 0)
701 preedit_attr = XVaCreateNestedList(0,
702 XNPreeditState, &preedit_state,
704 if (!XGetICValues(ic,
705 XNPreeditAttributes, preedit_attr,
707 have_preedit_state = TRUE;
711 result = XmbResetIC (ic);
713 preedit_attr = XVaCreateNestedList(0,
714 XNPreeditState, preedit_state,
716 if (have_preedit_state)
718 XNPreeditAttributes, preedit_attr,
725 char *result_utf8 = mb_to_utf8 (context_xim, result);
728 g_signal_emit_by_name (context, "commit", result_utf8);
729 g_free (result_utf8);
733 if (context_xim->preedit_length)
735 context_xim->preedit_length = 0;
736 g_signal_emit_by_name (context, "preedit_changed");
742 /* Mask of feedback bits that we render
744 #define FEEDBACK_MASK (XIMReverse | XIMUnderline)
747 add_feedback_attr (PangoAttrList *attrs,
749 XIMFeedback feedback,
753 PangoAttribute *attr;
755 gint start_index = g_utf8_offset_to_pointer (str, start_pos) - str;
756 gint end_index = g_utf8_offset_to_pointer (str, end_pos) - str;
758 if (feedback & XIMUnderline)
760 attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
761 attr->start_index = start_index;
762 attr->end_index = end_index;
764 pango_attr_list_change (attrs, attr);
767 if (feedback & XIMReverse)
769 attr = pango_attr_foreground_new (0xffff, 0xffff, 0xffff);
770 attr->start_index = start_index;
771 attr->end_index = end_index;
773 pango_attr_list_change (attrs, attr);
775 attr = pango_attr_background_new (0, 0, 0);
776 attr->start_index = start_index;
777 attr->end_index = end_index;
779 pango_attr_list_change (attrs, attr);
782 if (feedback & ~FEEDBACK_MASK)
783 g_warning ("Unrendered feedback style: %#lx", feedback & ~FEEDBACK_MASK);
787 gtk_im_context_xim_get_preedit_string (GtkIMContext *context,
789 PangoAttrList **attrs,
792 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
793 gchar *utf8 = g_ucs4_to_utf8 (context_xim->preedit_chars, context_xim->preedit_length, NULL, NULL, NULL);
798 XIMFeedback last_feedback = 0;
801 *attrs = pango_attr_list_new ();
803 for (i = 0; i < context_xim->preedit_length; i++)
805 XIMFeedback new_feedback = context_xim->feedbacks[i] & FEEDBACK_MASK;
806 if (new_feedback != last_feedback)
809 add_feedback_attr (*attrs, utf8, last_feedback, start, i);
811 last_feedback = new_feedback;
817 add_feedback_attr (*attrs, utf8, last_feedback, start, i);
826 *cursor_pos = context_xim->preedit_cursor;
830 preedit_start_callback (XIC xic,
831 XPointer client_data,
834 GtkIMContext *context = GTK_IM_CONTEXT (client_data);
836 g_signal_emit_by_name (context, "preedit_start");
840 preedit_done_callback (XIC xic,
841 XPointer client_data,
844 GtkIMContext *context = GTK_IM_CONTEXT (client_data);
846 g_signal_emit_by_name (context, "preedit_end");
850 xim_text_to_utf8 (GtkIMContextXIM *context, XIMText *xim_text, gchar **text)
852 gint text_length = 0;
853 GError *error = NULL;
854 gchar *result = NULL;
856 if (xim_text && xim_text->string.multi_byte)
858 if (xim_text->encoding_is_wchar)
860 g_warning ("Wide character return from Xlib not currently supported");
865 if (strcmp (context->mb_charset, "UTF-8") == 0)
866 result = g_strdup (xim_text->string.multi_byte);
868 result = g_convert (xim_text->string.multi_byte,
876 text_length = g_utf8_strlen (result, -1);
878 if (text_length != xim_text->length)
880 g_warning ("Size mismatch when converting text from input method: supplied length = %d\n, result length = %d", xim_text->length, text_length);
885 g_warning ("Error converting text from IM to UCS-4: %s", error->message);
886 g_error_free (error);
903 preedit_draw_callback (XIC xic,
904 XPointer client_data,
905 XIMPreeditDrawCallbackStruct *call_data)
907 GtkIMContextXIM *context = GTK_IM_CONTEXT_XIM (client_data);
909 XIMText *new_xim_text = call_data->text;
910 gint new_text_length;
911 gunichar *new_text = NULL;
917 gint chg_first = CLAMP (call_data->chg_first, 0, context->preedit_length);
918 gint chg_length = CLAMP (call_data->chg_length, 0, context->preedit_length - chg_first);
920 context->preedit_cursor = call_data->caret;
922 if (chg_first != call_data->chg_first || chg_length != call_data->chg_length)
923 g_warning ("Invalid change to preedit string, first=%d length=%d (orig length == %d)",
924 call_data->chg_first, call_data->chg_length, context->preedit_length);
926 new_text_length = xim_text_to_utf8 (context, new_xim_text, &tmp);
929 new_text = g_utf8_to_ucs4_fast (tmp, -1, NULL);
933 diff = new_text_length - chg_length;
934 new_length = context->preedit_length + diff;
936 if (new_length > context->preedit_size)
938 context->preedit_size = new_length;
939 context->preedit_chars = g_renew (gunichar, context->preedit_chars, new_length);
940 context->feedbacks = g_renew (XIMFeedback, context->feedbacks, new_length);
945 for (i = chg_first + chg_length ; i < context->preedit_length; i++)
947 context->preedit_chars[i + diff] = context->preedit_chars[i];
948 context->feedbacks[i + diff] = context->feedbacks[i];
953 for (i = context->preedit_length - 1; i >= chg_first + chg_length ; i--)
955 context->preedit_chars[i + diff] = context->preedit_chars[i];
956 context->feedbacks[i + diff] = context->feedbacks[i];
960 for (i = 0; i < new_text_length; i++)
962 context->preedit_chars[chg_first + i] = new_text[i];
963 context->feedbacks[chg_first + i] = new_xim_text->feedback[i];
966 context->preedit_length += diff;
971 g_signal_emit_by_name (context, "preedit_changed");
976 preedit_caret_callback (XIC xic,
977 XPointer client_data,
978 XIMPreeditCaretCallbackStruct *call_data)
980 GtkIMContextXIM *context = GTK_IM_CONTEXT_XIM (client_data);
982 if (call_data->direction == XIMAbsolutePosition)
984 context->preedit_cursor = call_data->position;
985 g_signal_emit_by_name (context, "preedit_changed");
989 g_warning ("Caret movement command: %d %d %d not supported",
990 call_data->position, call_data->direction, call_data->style);
995 status_start_callback (XIC xic,
996 XPointer client_data,
1003 status_done_callback (XIC xic,
1004 XPointer client_data,
1011 status_draw_callback (XIC xic,
1012 XPointer client_data,
1013 XIMStatusDrawCallbackStruct *call_data)
1015 GtkIMContextXIM *context = GTK_IM_CONTEXT_XIM (client_data);
1017 if (context->status_visible == FALSE)
1020 if (call_data->type == XIMTextType)
1023 xim_text_to_utf8 (context, call_data->data.text, &text);
1026 status_window_set_text (context, text);
1028 status_window_set_text (context, "");
1032 g_print ("Status drawn with bitmap - id = %#lx\n", call_data->data.bitmap);
1036 static XVaNestedList
1037 set_preedit_callback (GtkIMContextXIM *context_xim)
1039 context_xim->preedit_start_callback.client_data = (XPointer)context_xim;
1040 context_xim->preedit_start_callback.callback = (XIMProc)preedit_start_callback;
1041 context_xim->preedit_done_callback.client_data = (XPointer)context_xim;
1042 context_xim->preedit_done_callback.callback = (XIMProc)preedit_done_callback;
1043 context_xim->preedit_draw_callback.client_data = (XPointer)context_xim;
1044 context_xim->preedit_draw_callback.callback = (XIMProc)preedit_draw_callback;
1045 context_xim->preedit_caret_callback.client_data = (XPointer)context_xim;
1046 context_xim->preedit_caret_callback.callback = (XIMProc)preedit_caret_callback;
1047 return XVaCreateNestedList (0,
1048 XNPreeditStartCallback, &context_xim->preedit_start_callback,
1049 XNPreeditDoneCallback, &context_xim->preedit_done_callback,
1050 XNPreeditDrawCallback, &context_xim->preedit_draw_callback,
1051 XNPreeditCaretCallback, &context_xim->preedit_caret_callback,
1055 static XVaNestedList
1056 set_status_callback (GtkIMContextXIM *context_xim)
1058 context_xim->status_start_callback.client_data = (XPointer)context_xim;
1059 context_xim->status_start_callback.callback = (XIMProc)status_start_callback;
1060 context_xim->status_done_callback.client_data = (XPointer)context_xim;
1061 context_xim->status_done_callback.callback = (XIMProc)status_done_callback;
1062 context_xim->status_draw_callback.client_data = (XPointer)context_xim;
1063 context_xim->status_draw_callback.callback = (XIMProc)status_draw_callback;
1065 return XVaCreateNestedList (0,
1066 XNStatusStartCallback, &context_xim->status_start_callback,
1067 XNStatusDoneCallback, &context_xim->status_done_callback,
1068 XNStatusDrawCallback, &context_xim->status_draw_callback,
1073 get_ic_real (GtkIMContextXIM *context_xim)
1076 const char *name1 = NULL;
1077 XVaNestedList list1 = NULL;
1078 const char *name2 = NULL;
1079 XVaNestedList list2 = NULL;
1080 XIMStyle im_style = 0;
1082 if (context_xim->im_info->im == NULL)
1085 if (context_xim->use_preedit &&
1086 (context_xim->im_info->style & PREEDIT_MASK) == XIMPreeditCallbacks)
1088 im_style |= XIMPreeditCallbacks;
1089 name1 = XNPreeditAttributes;
1090 list1 = set_preedit_callback (context_xim);
1092 else if ((context_xim->im_info->style & PREEDIT_MASK) == XIMPreeditNone)
1093 im_style |= XIMPreeditNone;
1095 im_style |= XIMPreeditNothing;
1097 if ((context_xim->im_info->style & STATUS_MASK) == XIMStatusCallbacks)
1099 im_style |= XIMStatusCallbacks;
1102 name1 = XNStatusAttributes;
1103 list1 = set_status_callback (context_xim);
1107 name2 = XNStatusAttributes;
1108 list2 = set_status_callback (context_xim);
1111 else if ((context_xim->im_info->style & STATUS_MASK) == XIMStatusNone)
1112 im_style |= XIMStatusNone;
1114 im_style |= XIMStatusNothing;
1116 xic = XCreateIC (context_xim->im_info->im,
1117 XNInputStyle, im_style,
1118 XNClientWindow, GDK_DRAWABLE_XID (context_xim->client_window),
1129 /* Don't filter key released events with XFilterEvents unless
1130 * input methods ask for. This is a workaround for Solaris input
1131 * method bug in C and European locales. It doubles each key
1132 * stroke if both key pressed and released events are filtered.
1137 XNFilterEvents, &mask,
1139 context_xim->filter_key_release = (mask & KeyReleaseMask);
1146 gtk_im_context_xim_get_ic (GtkIMContextXIM *context_xim)
1148 if (!context_xim->ic && context_xim->im_info)
1149 context_xim->ic = get_ic_real (context_xim);
1151 return context_xim->ic;
1154 /**************************
1156 * Status Window handling *
1158 **************************/
1161 status_window_expose_event (GtkWidget *widget,
1162 GdkEventExpose *event)
1164 gdk_draw_rectangle (widget->window,
1165 widget->style->base_gc [GTK_STATE_NORMAL],
1168 widget->allocation.width, widget->allocation.height);
1169 gdk_draw_rectangle (widget->window,
1170 widget->style->text_gc [GTK_STATE_NORMAL],
1173 widget->allocation.width - 1, widget->allocation.height - 1);
1179 status_window_style_set (GtkWidget *toplevel,
1180 GtkStyle *previous_style,
1185 for (i = 0; i < 5; i++)
1186 gtk_widget_modify_fg (label, i, &toplevel->style->text[i]);
1189 /* Frees a status window and removes its link from the status_windows list */
1191 status_window_free (StatusWindow *status_window)
1193 status_windows = g_slist_remove (status_windows, status_window);
1195 g_signal_handler_disconnect (status_window->toplevel, status_window->destroy_handler_id);
1196 g_signal_handler_disconnect (status_window->toplevel, status_window->configure_handler_id);
1197 gtk_widget_destroy (status_window->window);
1198 g_object_set_data (G_OBJECT (status_window->toplevel), "gtk-im-xim-status-window", NULL);
1200 g_free (status_window);
1204 status_window_configure (GtkWidget *toplevel,
1205 GdkEventConfigure *event,
1206 StatusWindow *status_window)
1209 GtkRequisition requisition;
1211 gint height = gdk_screen_get_height (gtk_widget_get_screen (toplevel));
1213 gdk_window_get_frame_extents (toplevel->window, &rect);
1214 gtk_widget_size_request (status_window->window, &requisition);
1216 if (rect.y + rect.height + requisition.height < height)
1217 y = rect.y + rect.height;
1219 y = height - requisition.height;
1221 gtk_window_move (GTK_WINDOW (status_window->window), rect.x, y);
1227 status_window_get (GtkIMContextXIM *context_xim,
1230 GdkWindow *toplevel_gdk;
1231 GtkWidget *toplevel;
1233 StatusWindow *status_window;
1234 GtkWidget *status_label;
1236 GdkWindow *root_window;
1238 if (!context_xim->client_window)
1241 toplevel_gdk = context_xim->client_window;
1242 screen = gdk_drawable_get_screen (toplevel_gdk);
1243 root_window = gdk_screen_get_root_window (screen);
1247 GdkWindow *parent = gdk_window_get_parent (toplevel_gdk);
1248 if (parent == root_window)
1251 toplevel_gdk = parent;
1254 gdk_window_get_user_data (toplevel_gdk, (gpointer *)&toplevel);
1258 status_window = g_object_get_data (G_OBJECT (toplevel), "gtk-im-xim-status-window");
1260 return status_window->window;
1264 status_window = g_new (StatusWindow, 1);
1265 status_window->window = gtk_window_new (GTK_WINDOW_POPUP);
1266 status_window->toplevel = toplevel;
1268 status_windows = g_slist_prepend (status_windows, status_window);
1270 window = status_window->window;
1272 gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
1273 gtk_widget_set_app_paintable (window, TRUE);
1275 status_label = gtk_label_new ("");
1276 gtk_misc_set_padding (GTK_MISC (status_label), 1, 1);
1277 gtk_widget_show (status_label);
1279 gtk_container_add (GTK_CONTAINER (window), status_label);
1281 status_window->destroy_handler_id = g_signal_connect_swapped (toplevel, "destroy",
1282 G_CALLBACK (status_window_free),
1284 status_window->configure_handler_id = g_signal_connect (toplevel, "configure_event",
1285 G_CALLBACK (status_window_configure),
1288 status_window_configure (toplevel, NULL, status_window);
1290 g_signal_connect (window, "style_set",
1291 G_CALLBACK (status_window_style_set), status_label);
1292 g_signal_connect (window, "expose_event",
1293 G_CALLBACK (status_window_expose_event), NULL);
1295 g_object_set_data (G_OBJECT (toplevel), "gtk-im-xim-status-window", status_window);
1301 status_window_has_text (GtkWidget *status_window)
1303 GtkWidget *label = GTK_BIN (status_window)->child;
1304 const gchar *text = gtk_label_get_text (GTK_LABEL (label));
1306 return text[0] != '\0';
1310 status_window_show (GtkIMContextXIM *context_xim)
1312 context_xim->status_visible = TRUE;
1316 status_window_hide (GtkIMContextXIM *context_xim)
1318 GtkWidget *status_window = status_window_get (context_xim, FALSE);
1320 context_xim->status_visible = FALSE;
1323 status_window_set_text (context_xim, "");
1327 status_window_set_text (GtkIMContextXIM *context_xim,
1330 GtkWidget *status_window = status_window_get (context_xim, TRUE);
1334 GtkWidget *label = GTK_BIN (status_window)->child;
1335 gtk_label_set_text (GTK_LABEL (label), text);
1337 if (context_xim->status_visible && status_window_has_text (status_window))
1338 gtk_widget_show (status_window);
1340 gtk_widget_hide (status_window);
1345 * gtk_im_context_xim_shutdown:
1347 * Destroys all the status windows that are kept by the XIM contexts. This
1348 * function should only be called by the XIM module exit routine.
1351 gtk_im_context_xim_shutdown (void)
1353 while (status_windows)
1354 status_window_free (status_windows->data);