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"
35 static void gtk_im_context_xim_class_init (GtkIMContextXIMClass *class);
36 static void gtk_im_context_xim_init (GtkIMContextXIM *im_context_xim);
37 static void gtk_im_context_xim_finalize (GObject *obj);
38 static void gtk_im_context_xim_set_client_window (GtkIMContext *context,
39 GdkWindow *client_window);
40 static gboolean gtk_im_context_xim_filter_keypress (GtkIMContext *context,
42 static void gtk_im_context_xim_reset (GtkIMContext *context);
43 static void gtk_im_context_xim_focus_in (GtkIMContext *context);
44 static void gtk_im_context_xim_focus_out (GtkIMContext *context);
45 static void gtk_im_context_xim_set_cursor_location (GtkIMContext *context,
47 static void gtk_im_context_xim_set_use_preedit (GtkIMContext *context,
48 gboolean use_preedit);
49 static void gtk_im_context_xim_get_preedit_string (GtkIMContext *context,
51 PangoAttrList **attrs,
54 static void status_window_show (GtkIMContextXIM *context_xim);
55 static void status_window_hide (GtkIMContextXIM *context_xim);
56 static void status_window_set_text (GtkIMContextXIM *context_xim,
59 static XIC gtk_im_context_xim_get_ic (GtkIMContextXIM *context_xim);
60 static GObjectClass *parent_class;
62 GType gtk_type_im_context_xim = 0;
64 static GSList *open_ims = NULL;
67 gtk_im_context_xim_register_type (GTypeModule *type_module)
69 static const GTypeInfo im_context_xim_info =
71 sizeof (GtkIMContextXIMClass),
73 (GBaseFinalizeFunc) NULL,
74 (GClassInitFunc) gtk_im_context_xim_class_init,
75 NULL, /* class_finalize */
76 NULL, /* class_data */
77 sizeof (GtkIMContextXIM),
79 (GtkObjectInitFunc) gtk_im_context_xim_init,
82 gtk_type_im_context_xim =
83 g_type_module_register_type (type_module,
86 &im_context_xim_info, 0);
89 #define PREEDIT_MASK (XIMPreeditCallbacks | XIMPreeditPosition | \
90 XIMPreeditArea | XIMPreeditNothing | XIMPreeditNone)
91 #define STATUS_MASK (XIMStatusCallbacks | XIMStatusArea | \
92 XIMStatusNothing | XIMStatusNone)
93 #define ALLOWED_MASK (XIMPreeditCallbacks | XIMPreeditNothing | XIMPreeditNone | \
94 XIMStatusCallbacks | XIMStatusNothing | XIMStatusNone)
97 choose_better_style (XIMStyle style1, XIMStyle style2)
101 if (style1 == 0) return style2;
102 if (style2 == 0) return style1;
103 if ((style1 & (PREEDIT_MASK | STATUS_MASK))
104 == (style2 & (PREEDIT_MASK | STATUS_MASK)))
107 s1 = style1 & PREEDIT_MASK;
108 s2 = style2 & PREEDIT_MASK;
111 if (u & XIMPreeditCallbacks)
112 return (s1 == XIMPreeditCallbacks) ? style1 : style2;
113 else if (u & XIMPreeditPosition)
114 return (s1 == XIMPreeditPosition) ? style1 :style2;
115 else if (u & XIMPreeditArea)
116 return (s1 == XIMPreeditArea) ? style1 : style2;
117 else if (u & XIMPreeditNothing)
118 return (s1 == XIMPreeditNothing) ? style1 : style2;
120 s1 = style1 & STATUS_MASK;
121 s2 = style2 & STATUS_MASK;
123 if (u & XIMStatusCallbacks)
124 return (s1 == XIMStatusCallbacks) ? style1 : style2;
125 else if (u & XIMStatusArea)
126 return (s1 == XIMStatusArea) ? style1 : style2;
127 else if (u & XIMStatusNothing)
128 return (s1 == XIMStatusNothing) ? style1 : style2;
129 else if (u & XIMStatusNone)
130 return (s1 == XIMStatusNone) ? style1 : style2;
132 return 0; /* Get rid of stupid warning */
136 setup_im (GtkXIMInfo *info)
138 XIMStyles *xim_styles = NULL;
139 XIMValuesList *ic_values = NULL;
142 XGetIMValues (info->im,
143 XNQueryInputStyle, &xim_styles,
144 XNQueryICValuesList, &ic_values,
150 for (i = 0; i < xim_styles->count_styles; i++)
151 if ((xim_styles->supported_styles[i] & ALLOWED_MASK) == xim_styles->supported_styles[i])
152 info->style = choose_better_style (info->style,
153 xim_styles->supported_styles[i]);
160 for (i = 0; i < ic_values->count_values; i++)
161 g_print ("%s\n", ic_values->supported_values[i]);
162 for (i = 0; i < xim_styles->count_styles; i++)
163 g_print ("%#x\n", xim_styles->supported_styles[i]);
174 get_im (const char *locale)
176 GSList *tmp_list = open_ims;
182 info = tmp_list->data;
183 if (!strcmp (info->locale, locale))
186 tmp_list = tmp_list->next;
191 if (XSupportsLocale ())
193 if (!XSetLocaleModifiers (""))
194 g_warning ("can not set locale modifiers");
196 im = XOpenIM (GDK_DISPLAY(), NULL, NULL, NULL);
200 info = g_new (GtkXIMInfo, 1);
201 open_ims = g_slist_prepend (open_ims, im);
203 info->locale = g_strdup (locale);
214 gtk_im_context_xim_class_init (GtkIMContextXIMClass *class)
216 GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
217 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
219 parent_class = g_type_class_peek_parent (class);
221 im_context_class->set_client_window = gtk_im_context_xim_set_client_window;
222 im_context_class->filter_keypress = gtk_im_context_xim_filter_keypress;
223 im_context_class->reset = gtk_im_context_xim_reset;
224 im_context_class->get_preedit_string = gtk_im_context_xim_get_preedit_string;
225 im_context_class->focus_in = gtk_im_context_xim_focus_in;
226 im_context_class->focus_out = gtk_im_context_xim_focus_out;
227 im_context_class->set_cursor_location = gtk_im_context_xim_set_cursor_location;
228 im_context_class->set_use_preedit = gtk_im_context_xim_set_use_preedit;
229 gobject_class->finalize = gtk_im_context_xim_finalize;
233 gtk_im_context_xim_init (GtkIMContextXIM *im_context_xim)
235 im_context_xim->use_preedit = TRUE;
239 gtk_im_context_xim_finalize (GObject *obj)
241 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (obj);
245 XDestroyIC (context_xim->ic);
246 context_xim->ic = NULL;
249 g_free (context_xim->mb_charset);
253 reinitialize_ic (GtkIMContextXIM *context_xim)
257 XDestroyIC (context_xim->ic);
258 context_xim->ic = NULL;
260 if (context_xim->preedit_length)
262 context_xim->preedit_length = 0;
263 g_signal_emit_by_name (context_xim, "preedit_changed");
269 gtk_im_context_xim_set_client_window (GtkIMContext *context,
270 GdkWindow *client_window)
272 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
274 reinitialize_ic (context_xim);
275 context_xim->client_window = client_window;
279 gtk_im_context_xim_new (void)
282 GtkIMContextXIM *result;
283 const gchar *charset;
285 info = get_im (setlocale (LC_CTYPE, NULL));
289 result = GTK_IM_CONTEXT_XIM (g_object_new (GTK_TYPE_IM_CONTEXT_XIM, NULL));
291 result->im_info = info;
293 g_get_charset (&charset);
294 result->mb_charset = g_strdup (charset);
296 return GTK_IM_CONTEXT (result);
300 mb_to_utf8 (GtkIMContextXIM *context_xim,
303 GError *error = NULL;
306 if (strcmp (context_xim->mb_charset, "UTF-8") == 0)
307 result = g_strdup (str);
310 result = g_convert (str, -1,
311 "UTF-8", context_xim->mb_charset,
315 g_warning ("Error converting text from IM to UTF-8: %s\n", error->message);
316 g_error_free (error);
324 gtk_im_context_xim_filter_keypress (GtkIMContext *context,
327 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
328 XIC ic = gtk_im_context_xim_get_ic (context_xim);
329 gchar static_buffer[256];
330 gchar *buffer = static_buffer;
331 gint buffer_size = sizeof(static_buffer) - 1;
335 gboolean result = FALSE;
337 XKeyPressedEvent xevent;
342 xevent.type = (event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease;
343 xevent.serial = 0; /* hope it doesn't matter */
344 xevent.send_event = event->send_event;
345 xevent.display = GDK_DRAWABLE_XDISPLAY (event->window);
346 xevent.window = GDK_DRAWABLE_XID (event->window);
347 xevent.root = GDK_ROOT_WINDOW();
348 xevent.subwindow = xevent.window;
349 xevent.time = event->time;
350 xevent.x = xevent.x_root = 0;
351 xevent.y = xevent.y_root = 0;
352 xevent.state = event->state;
353 xevent.keycode = event->keyval ? XKeysymToKeycode (xevent.display, event->keyval) : 0;
354 xevent.same_screen = True;
356 if (XFilterEvent ((XEvent *)&xevent, GDK_DRAWABLE_XID (context_xim->client_window)))
360 num_bytes = XmbLookupString (ic, &xevent, buffer, buffer_size, &keysym, &status);
362 if (status == XBufferOverflow)
364 buffer_size = num_bytes;
365 buffer = g_malloc (num_bytes + 1);
369 /* I don't know how we should properly handle XLookupKeysym or XLookupBoth
370 * here ... do input methods actually change the keysym? we can't really
371 * feed it back to accelerator processing at this point...
373 if (status == XLookupChars || status == XLookupBoth)
377 buffer[num_bytes] = '\0';
379 result_utf8 = mb_to_utf8 (context_xim, buffer);
382 if ((guchar)result_utf8[0] >= 0x20) /* Some IM have a nasty habit of converting
383 * control characters into strings
386 g_signal_emit_by_name (context, "commit", result_utf8);
390 g_free (result_utf8);
398 gtk_im_context_xim_focus_in (GtkIMContext *context)
400 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
401 XIC ic = gtk_im_context_xim_get_ic (context_xim);
408 status_window_show (context_xim);
414 gtk_im_context_xim_focus_out (GtkIMContext *context)
416 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
417 XIC ic = gtk_im_context_xim_get_ic (context_xim);
424 status_window_hide (context_xim);
430 gtk_im_context_xim_set_cursor_location (GtkIMContext *context,
433 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
434 XIC ic = gtk_im_context_xim_get_ic (context_xim);
436 XVaNestedList preedit_attr;
445 preedit_attr = XVaCreateNestedList (0,
446 XNSpotLocation, &spot,
449 XNPreeditAttributes, preedit_attr,
457 gtk_im_context_xim_set_use_preedit (GtkIMContext *context,
458 gboolean use_preedit)
460 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
462 use_preedit = use_preedit != FALSE;
464 if (context_xim->use_preedit != use_preedit)
466 context_xim->use_preedit = use_preedit;
467 reinitialize_ic (context_xim);
474 gtk_im_context_xim_reset (GtkIMContext *context)
476 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
477 XIC ic = gtk_im_context_xim_get_ic (context_xim);
480 /* restore conversion state after resetting ic later */
481 XIMPreeditState preedit_state = XIMPreeditUnKnown;
482 XVaNestedList preedit_attr;
483 gboolean have_preedit_state = FALSE;
489 if (context_xim->preedit_length == 0)
492 preedit_attr = XVaCreateNestedList(0,
493 XNPreeditState, &preedit_state,
495 if (!XGetICValues(ic,
496 XNPreeditAttributes, preedit_attr,
498 have_preedit_state = TRUE;
502 result = XmbResetIC (ic);
504 preedit_attr = XVaCreateNestedList(0,
505 XNPreeditState, preedit_state,
507 if (have_preedit_state)
509 XNPreeditAttributes, preedit_attr,
516 char *result_utf8 = mb_to_utf8 (context_xim, result);
519 g_signal_emit_by_name (context, "commit", result_utf8);
520 g_free (result_utf8);
524 if (context_xim->preedit_length)
526 context_xim->preedit_length = 0;
527 g_signal_emit_by_name (context, "preedit_changed");
533 /* Mask of feedback bits that we render
535 #define FEEDBACK_MASK (XIMReverse | XIMUnderline)
538 add_feedback_attr (PangoAttrList *attrs,
540 XIMFeedback feedback,
544 PangoAttribute *attr;
546 gint start_index = g_utf8_offset_to_pointer (str, start_pos) - str;
547 gint end_index = g_utf8_offset_to_pointer (str, end_pos) - str;
549 if (feedback & XIMUnderline)
551 attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
552 attr->start_index = start_index;
553 attr->end_index = end_index;
555 pango_attr_list_change (attrs, attr);
558 if (feedback & XIMReverse)
560 attr = pango_attr_foreground_new (0xffff, 0xffff, 0xffff);
561 attr->start_index = start_index;
562 attr->end_index = end_index;
564 pango_attr_list_change (attrs, attr);
566 attr = pango_attr_background_new (0, 0, 0);
567 attr->start_index = start_index;
568 attr->end_index = end_index;
570 pango_attr_list_change (attrs, attr);
573 if (feedback & ~FEEDBACK_MASK)
574 g_warning ("Unrendered feedback style: %#lx", feedback & ~FEEDBACK_MASK);
578 gtk_im_context_xim_get_preedit_string (GtkIMContext *context,
580 PangoAttrList **attrs,
583 GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
584 gchar *utf8 = g_ucs4_to_utf8 (context_xim->preedit_chars, context_xim->preedit_length, NULL, NULL, NULL);
589 XIMFeedback last_feedback = 0;
592 *attrs = pango_attr_list_new ();
594 for (i = 0; i < context_xim->preedit_length; i++)
596 XIMFeedback new_feedback = context_xim->feedbacks[i] & FEEDBACK_MASK;
597 if (new_feedback != last_feedback)
600 add_feedback_attr (*attrs, utf8, last_feedback, start, i);
602 last_feedback = new_feedback;
608 add_feedback_attr (*attrs, utf8, last_feedback, start, i);
617 *cursor_pos = context_xim->preedit_cursor;
621 preedit_start_callback (XIC xic,
622 XPointer client_data,
625 GtkIMContext *context = GTK_IM_CONTEXT (client_data);
627 g_signal_emit_by_name (context, "preedit_start");
631 preedit_done_callback (XIC xic,
632 XPointer client_data,
635 GtkIMContext *context = GTK_IM_CONTEXT (client_data);
637 g_signal_emit_by_name (context, "preedit_end");
641 xim_text_to_utf8 (GtkIMContextXIM *context, XIMText *xim_text, gchar **text)
643 gint text_length = 0;
644 GError *error = NULL;
645 gchar *result = NULL;
647 if (xim_text && xim_text->string.multi_byte)
649 if (xim_text->encoding_is_wchar)
651 g_warning ("Wide character return from Xlib not currently supported");
656 if (strcmp (context->mb_charset, "UTF-8") == 0)
657 result = g_strdup (xim_text->string.multi_byte);
659 result = g_convert (xim_text->string.multi_byte,
667 text_length = g_utf8_strlen (result, -1);
669 if (text_length != xim_text->length)
671 g_warning ("Size mismatch when converting text from input method: supplied length = %d\n, result length = %d", xim_text->length, text_length);
676 g_warning ("Error converting text from IM to UCS-4: %s", error->message);
677 g_error_free (error);
694 preedit_draw_callback (XIC xic,
695 XPointer client_data,
696 XIMPreeditDrawCallbackStruct *call_data)
698 GtkIMContextXIM *context = GTK_IM_CONTEXT_XIM (client_data);
700 XIMText *new_xim_text = call_data->text;
701 gint new_text_length;
702 gunichar *new_text = NULL;
708 gint chg_first = CLAMP (call_data->chg_first, 0, context->preedit_length);
709 gint chg_length = CLAMP (call_data->chg_length, 0, context->preedit_length - chg_first);
711 context->preedit_cursor = call_data->caret;
713 if (chg_first != call_data->chg_first || chg_length != call_data->chg_length)
714 g_warning ("Invalid change to preedit string, first=%d length=%d (orig length == %d)",
715 call_data->chg_first, call_data->chg_length, context->preedit_length);
717 new_text_length = xim_text_to_utf8 (context, new_xim_text, &tmp);
720 new_text = g_utf8_to_ucs4_fast (tmp, -1, NULL);
724 diff = new_text_length - chg_length;
725 new_length = context->preedit_length + diff;
727 if (new_length > context->preedit_size)
729 context->preedit_size = new_length;
730 context->preedit_chars = g_renew (gunichar, context->preedit_chars, new_length);
731 context->feedbacks = g_renew (XIMFeedback, context->feedbacks, new_length);
736 for (i = chg_first + chg_length ; i < context->preedit_length; i++)
738 context->preedit_chars[i + diff] = context->preedit_chars[i];
739 context->feedbacks[i + diff] = context->feedbacks[i];
744 for (i = context->preedit_length - 1; i >= chg_first + chg_length ; i--)
746 context->preedit_chars[i + diff] = context->preedit_chars[i];
747 context->feedbacks[i + diff] = context->feedbacks[i];
751 for (i = 0; i < new_text_length; i++)
753 context->preedit_chars[chg_first + i] = new_text[i];
754 context->feedbacks[chg_first + i] = new_xim_text->feedback[i];
757 context->preedit_length += diff;
762 g_signal_emit_by_name (context, "preedit_changed");
767 preedit_caret_callback (XIC xic,
768 XPointer client_data,
769 XIMPreeditCaretCallbackStruct *call_data)
771 GtkIMContextXIM *context = GTK_IM_CONTEXT_XIM (client_data);
773 if (call_data->direction == XIMAbsolutePosition)
775 context->preedit_cursor = call_data->position;
776 g_signal_emit_by_name (context, "preedit_changed");
780 g_warning ("Caret movement command: %d %d %d not supported",
781 call_data->position, call_data->direction, call_data->style);
786 status_start_callback (XIC xic,
787 XPointer client_data,
794 status_done_callback (XIC xic,
795 XPointer client_data,
802 status_draw_callback (XIC xic,
803 XPointer client_data,
804 XIMStatusDrawCallbackStruct *call_data)
806 GtkIMContextXIM *context = GTK_IM_CONTEXT_XIM (client_data);
808 if (call_data->type == XIMTextType)
811 xim_text_to_utf8 (context, call_data->data.text, &text);
814 status_window_set_text (context, text);
816 status_window_set_text (context, "");
820 g_print ("Status drawn with bitmap - id = %#lx\n", call_data->data.bitmap);
825 gtk_im_context_xim_get_ic (GtkIMContextXIM *context_xim)
827 const char *name1 = NULL;
828 XVaNestedList list1 = NULL;
829 const char *name2 = NULL;
830 XVaNestedList list2 = NULL;
832 if (!context_xim->ic && context_xim->client_window)
834 if (!context_xim->use_preedit)
836 context_xim->ic = XCreateIC (context_xim->im_info->im,
837 XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
838 XNClientWindow, GDK_DRAWABLE_XID (context_xim->client_window),
840 return context_xim->ic;
843 if ((context_xim->im_info->style & PREEDIT_MASK) == XIMPreeditCallbacks)
845 context_xim->preedit_start_callback.client_data = (XPointer)context_xim;
846 context_xim->preedit_start_callback.callback = (XIMProc)preedit_start_callback;
847 context_xim->preedit_done_callback.client_data = (XPointer)context_xim;
848 context_xim->preedit_done_callback.callback = (XIMProc)preedit_done_callback;
849 context_xim->preedit_draw_callback.client_data = (XPointer)context_xim;
850 context_xim->preedit_draw_callback.callback = (XIMProc)preedit_draw_callback;
851 context_xim->preedit_caret_callback.client_data = (XPointer)context_xim;
852 context_xim->preedit_caret_callback.callback = (XIMProc)preedit_caret_callback;
854 name1 = XNPreeditAttributes;
855 list1 = XVaCreateNestedList (0,
856 XNPreeditStartCallback, &context_xim->preedit_start_callback,
857 XNPreeditDoneCallback, &context_xim->preedit_done_callback,
858 XNPreeditDrawCallback, &context_xim->preedit_draw_callback,
859 XNPreeditCaretCallback, &context_xim->preedit_caret_callback,
863 if ((context_xim->im_info->style & STATUS_MASK) == XIMStatusCallbacks)
865 XVaNestedList status_attrs;
867 context_xim->status_start_callback.client_data = (XPointer)context_xim;
868 context_xim->status_start_callback.callback = (XIMProc)status_start_callback;
869 context_xim->status_done_callback.client_data = (XPointer)context_xim;
870 context_xim->status_done_callback.callback = (XIMProc)status_done_callback;
871 context_xim->status_draw_callback.client_data = (XPointer)context_xim;
872 context_xim->status_draw_callback.callback = (XIMProc)status_draw_callback;
874 status_attrs = XVaCreateNestedList (0,
875 XNStatusStartCallback, &context_xim->status_start_callback,
876 XNStatusDoneCallback, &context_xim->status_done_callback,
877 XNStatusDrawCallback, &context_xim->status_draw_callback,
882 name1 = XNStatusAttributes;
883 list1 = status_attrs;
887 name2 = XNStatusAttributes;
888 list2 = status_attrs;
892 context_xim->ic = XCreateIC (context_xim->im_info->im,
893 XNInputStyle, context_xim->im_info->style,
894 XNClientWindow, GDK_DRAWABLE_XID (context_xim->client_window),
905 return context_xim->ic;
908 /**************************
910 * Status Window handling *
912 **************************/
915 status_window_expose_event (GtkWidget *widget,
916 GdkEventExpose *event)
918 gdk_draw_rectangle (widget->window,
919 widget->style->base_gc [GTK_STATE_NORMAL],
922 widget->allocation.width, widget->allocation.height);
923 gdk_draw_rectangle (widget->window,
924 widget->style->text_gc [GTK_STATE_NORMAL],
927 widget->allocation.width - 1, widget->allocation.height - 1);
933 status_window_style_set (GtkWidget *toplevel,
934 GtkStyle *previous_style,
939 for (i = 0; i < 5; i++)
940 gtk_widget_modify_fg (label, i, &toplevel->style->text[i]);
944 status_window_destroy (GtkWidget *toplevel,
945 GtkWidget *status_window)
947 gtk_widget_destroy (status_window);
948 g_object_set_data (G_OBJECT (toplevel), "gtk-im-xim-status-window", NULL);
952 status_window_configure (GtkWidget *toplevel,
953 GdkEventConfigure *event,
954 GtkWidget *status_window)
957 GtkRequisition requisition;
960 gdk_window_get_frame_extents (toplevel->window, &rect);
961 gtk_widget_size_request (status_window, &requisition);
963 if (rect.y + rect.height + requisition.height < gdk_screen_height ())
964 y = rect.y + rect.height;
966 y = gdk_screen_height () - requisition.height;
968 gtk_window_move (GTK_WINDOW (status_window), rect.x, y);
974 status_window_get (GtkIMContextXIM *context_xim,
977 GdkWindow *toplevel_gdk;
979 GtkWidget *status_window;
980 GtkWidget *status_label;
982 if (!context_xim->client_window)
985 toplevel_gdk = context_xim->client_window;
988 GdkWindow *parent = gdk_window_get_parent (toplevel_gdk);
989 if (parent == gdk_get_default_root_window ())
992 toplevel_gdk = parent;
995 gdk_window_get_user_data (toplevel_gdk, (gpointer *)&toplevel);
999 status_window = g_object_get_data (G_OBJECT (toplevel), "gtk-im-xim-status-window");
1000 if (status_window || !create)
1001 return status_window;
1003 status_window = gtk_window_new (GTK_WINDOW_POPUP);
1005 gtk_window_set_policy (GTK_WINDOW (status_window), FALSE, FALSE, FALSE);
1006 gtk_widget_set_app_paintable (status_window, TRUE);
1008 status_label = gtk_label_new ("");
1009 gtk_misc_set_padding (GTK_MISC (status_label), 1, 1);
1010 gtk_widget_show (status_label);
1012 gtk_container_add (GTK_CONTAINER (status_window), status_label);
1014 g_signal_connect (toplevel, "destroy",
1015 G_CALLBACK (status_window_destroy), status_window);
1016 g_signal_connect (toplevel, "configure_event",
1017 G_CALLBACK (status_window_configure), status_window);
1019 status_window_configure (toplevel, NULL, status_window);
1021 g_signal_connect (status_window, "style_set",
1022 G_CALLBACK (status_window_style_set), status_label);
1023 g_signal_connect (status_window, "expose_event",
1024 G_CALLBACK (status_window_expose_event), NULL);
1026 g_object_set_data (G_OBJECT (toplevel), "gtk-im-xim-status-window", status_window);
1028 return status_window;
1032 status_window_has_text (GtkWidget *status_window)
1034 GtkWidget *label = GTK_BIN (status_window)->child;
1035 const gchar *text = gtk_label_get_text (GTK_LABEL (label));
1037 return text[0] != '\0';
1041 status_window_show (GtkIMContextXIM *context_xim)
1043 GtkWidget *status_window = status_window_get (context_xim, TRUE);
1045 context_xim->status_visible = TRUE;
1047 if (status_window && status_window_has_text (status_window))
1048 gtk_widget_show (status_window);
1052 status_window_hide (GtkIMContextXIM *context_xim)
1054 GtkWidget *status_window = status_window_get (context_xim, FALSE);
1056 context_xim->status_visible = FALSE;
1059 gtk_widget_hide (status_window);
1063 status_window_set_text (GtkIMContextXIM *context_xim,
1066 GtkWidget *status_window = status_window_get (context_xim, TRUE);
1070 GtkWidget *label = GTK_BIN (status_window)->child;
1071 gtk_label_set_text (GTK_LABEL (label), text);
1073 if (context_xim->status_visible && status_window_has_text (status_window))
1074 gtk_widget_show (status_window);
1076 gtk_widget_hide (status_window);