]> Pileus Git - ~andy/gtk/blob - modules/input/gtkimcontextxim.c
Skip g_convert when to_codeset and from_codeset are both 'UTF-8' (gnome
[~andy/gtk] / modules / input / gtkimcontextxim.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2000 Red Hat, Inc.
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 #include "locale.h"
21 #include <string.h>
22
23 #include "gtk/gtksignal.h"
24 #include "gtkimcontextxim.h"
25
26 struct _GtkXIMInfo
27 {
28   XIM im;
29   char *locale;
30   XIMStyle style;
31 };
32
33 static void     gtk_im_context_xim_class_init         (GtkIMContextXIMClass  *class);
34 static void     gtk_im_context_xim_init               (GtkIMContextXIM       *im_context_xim);
35 static void     gtk_im_context_xim_finalize           (GObject               *obj);
36 static void     gtk_im_context_xim_set_client_window  (GtkIMContext          *context,
37                                                        GdkWindow             *client_window);
38 static gboolean gtk_im_context_xim_filter_keypress    (GtkIMContext          *context,
39                                                        GdkEventKey           *key);
40 static void     gtk_im_context_xim_reset              (GtkIMContext          *context);
41 static void     gtk_im_context_xim_focus_in           (GtkIMContext          *context);
42 static void     gtk_im_context_xim_focus_out          (GtkIMContext          *context);
43 static void     gtk_im_context_xim_set_cursor_location (GtkIMContext          *context,
44                                                        GdkRectangle             *area);
45 static void     gtk_im_context_xim_get_preedit_string (GtkIMContext          *context,
46                                                        gchar                **str,
47                                                        PangoAttrList        **attrs,
48                                                        gint                  *cursor_pos);
49
50 static XIC       gtk_im_context_xim_get_ic            (GtkIMContextXIM *context_xim);
51 static GObjectClass *parent_class;
52
53 GType gtk_type_im_context_xim = 0;
54
55 static GSList *open_ims = NULL;
56
57 void
58 gtk_im_context_xim_register_type (GTypeModule *type_module)
59 {
60   static const GTypeInfo im_context_xim_info =
61   {
62     sizeof (GtkIMContextXIMClass),
63     (GBaseInitFunc) NULL,
64     (GBaseFinalizeFunc) NULL,
65     (GClassInitFunc) gtk_im_context_xim_class_init,
66     NULL,           /* class_finalize */    
67     NULL,           /* class_data */
68     sizeof (GtkIMContextXIM),
69     0,
70     (GtkObjectInitFunc) gtk_im_context_xim_init,
71   };
72
73   gtk_type_im_context_xim = 
74     g_type_module_register_type (type_module,
75                                  GTK_TYPE_IM_CONTEXT,
76                                  "GtkIMContextXIM",
77                                  &im_context_xim_info, 0);
78 }
79
80 #define PREEDIT_MASK (XIMPreeditCallbacks | XIMPreeditPosition | \
81                       XIMPreeditArea | XIMPreeditNothing | XIMPreeditNone)
82 #define STATUS_MASK (XIMStatusCallbacks | XIMStatusArea | \
83                       XIMStatusNothing | XIMStatusNone)
84 #define ALLOWED_MASK (XIMPreeditCallbacks | XIMPreeditNothing | XIMPreeditNone | \
85                       XIMStatusCallbacks | XIMStatusNothing | XIMStatusNone)
86
87 static XIMStyle 
88 choose_better_style (XIMStyle style1, XIMStyle style2) 
89 {
90   XIMStyle s1, s2, u; 
91   
92   if (style1 == 0) return style2;
93   if (style2 == 0) return style1;
94   if ((style1 & (PREEDIT_MASK | STATUS_MASK))
95         == (style2 & (PREEDIT_MASK | STATUS_MASK)))
96     return style1;
97
98   s1 = style1 & PREEDIT_MASK;
99   s2 = style2 & PREEDIT_MASK;
100   u = s1 | s2;
101   if (s1 != s2) {
102     if (u & XIMPreeditCallbacks)
103       return (s1 == XIMPreeditCallbacks) ? style1 : style2;
104     else if (u & XIMPreeditPosition)
105       return (s1 == XIMPreeditPosition) ? style1 :style2;
106     else if (u & XIMPreeditArea)
107       return (s1 == XIMPreeditArea) ? style1 : style2;
108     else if (u & XIMPreeditNothing)
109       return (s1 == XIMPreeditNothing) ? style1 : style2;
110   } else {
111     s1 = style1 & STATUS_MASK;
112     s2 = style2 & STATUS_MASK;
113     u = s1 | s2;
114     if (u & XIMStatusCallbacks)
115       return (s1 == XIMStatusCallbacks) ? style1 : style2;
116     else if (u & XIMStatusArea)
117       return (s1 == XIMStatusArea) ? style1 : style2;
118     else if (u & XIMStatusNothing)
119       return (s1 == XIMStatusNothing) ? style1 : style2;
120     else if (u & XIMStatusNone)
121       return (s1 == XIMStatusNone) ? style1 : style2;
122   }
123   return 0; /* Get rid of stupid warning */
124 }
125
126 static void
127 setup_im (GtkXIMInfo *info)
128 {
129   XIMStyles *xim_styles = NULL;
130   XIMValuesList *ic_values = NULL;
131   int i;
132   
133   XGetIMValues (info->im,
134                 XNQueryInputStyle, &xim_styles,
135                 XNQueryICValuesList, &ic_values,
136                 NULL);
137
138   info->style = 0;
139   if (xim_styles)
140     {
141       for (i = 0; i < xim_styles->count_styles; i++)
142         if ((xim_styles->supported_styles[i] & ALLOWED_MASK) == xim_styles->supported_styles[i])
143           info->style = choose_better_style (info->style,
144                                              xim_styles->supported_styles[i]);
145     }
146
147
148 #if 0
149   if (ic_values)
150     {
151       for (i = 0; i < ic_values->count_values; i++)
152         g_print ("%s\n", ic_values->supported_values[i]);
153       for (i = 0; i < xim_styles->count_styles; i++)
154         g_print ("%#x\n", xim_styles->supported_styles[i]);
155     }
156 #endif
157
158   if (xim_styles)
159     XFree (xim_styles);
160   if (ic_values)
161     XFree (ic_values);
162 }
163
164 static GtkXIMInfo *
165 get_im (const char *locale)
166 {
167   GSList *tmp_list = open_ims;
168   GtkXIMInfo *info;
169   XIM im = NULL;
170
171   while (tmp_list)
172     {
173       info = tmp_list->data;
174       if (!strcmp (info->locale, locale))
175         return info;
176
177       tmp_list = tmp_list->next;
178     }
179
180   info = NULL;
181
182   if (XSupportsLocale ())
183     {
184       if (!XSetLocaleModifiers (""))
185         g_warning ("can not set locale modifiers");
186       
187       im = XOpenIM (GDK_DISPLAY(), NULL, NULL, NULL);
188       
189       if (im)
190         {
191           info = g_new (GtkXIMInfo, 1);
192           open_ims = g_slist_prepend (open_ims, im);
193           
194           info->locale = g_strdup (locale);
195           info->im = im;
196
197           setup_im (info);
198         }
199     }
200
201   return info;
202 }
203
204 static void
205 gtk_im_context_xim_class_init (GtkIMContextXIMClass *class)
206 {
207   GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
208   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
209
210   parent_class = g_type_class_peek_parent (class);
211
212   im_context_class->set_client_window = gtk_im_context_xim_set_client_window;
213   im_context_class->filter_keypress = gtk_im_context_xim_filter_keypress;
214   im_context_class->reset = gtk_im_context_xim_reset;
215   im_context_class->get_preedit_string = gtk_im_context_xim_get_preedit_string;
216   im_context_class->focus_in = gtk_im_context_xim_focus_in;
217   im_context_class->focus_out = gtk_im_context_xim_focus_out;
218   im_context_class->set_cursor_location = gtk_im_context_xim_set_cursor_location;
219   gobject_class->finalize = gtk_im_context_xim_finalize;
220 }
221
222 static void
223 gtk_im_context_xim_init (GtkIMContextXIM *im_context_xim)
224 {
225 }
226
227 static void
228 gtk_im_context_xim_finalize (GObject *obj)
229 {
230   GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (obj);
231
232   if (context_xim->ic)
233     {
234       XDestroyIC (context_xim->ic);
235       context_xim->ic = NULL;
236     }
237  
238   g_free (context_xim->mb_charset);
239 }
240
241 static void
242 gtk_im_context_xim_set_client_window (GtkIMContext          *context,
243                                       GdkWindow             *client_window)
244 {
245   GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
246
247   if (context_xim->ic)
248     {
249       XDestroyIC (context_xim->ic);
250       context_xim->ic = NULL;
251     }
252
253   context_xim->client_window = client_window;
254 }
255
256 GtkIMContext *
257 gtk_im_context_xim_new (void)
258 {
259   GtkXIMInfo *info;
260   GtkIMContextXIM *result;
261   const gchar *charset;
262
263   info = get_im (setlocale (LC_CTYPE, NULL));
264   if (!info)
265     return NULL;
266
267   result = GTK_IM_CONTEXT_XIM (gtk_type_new (GTK_TYPE_IM_CONTEXT_XIM));
268
269   result->im_info = info;
270   
271   g_get_charset (&charset);
272   result->mb_charset = g_strdup (charset);
273
274   return GTK_IM_CONTEXT (result);
275 }
276
277 static char *
278 mb_to_utf8 (GtkIMContextXIM *context_xim,
279             const char      *str)
280 {
281   GError *error = NULL;
282   gchar *result;
283
284   if (strcmp (context_xim->mb_charset, "UTF-8") == 0)
285     result = g_strdup (str);
286   else
287     {
288       result = g_convert (str, -1,
289                           "UTF-8", context_xim->mb_charset,
290                           NULL, NULL, &error);
291       if (!result)
292         {
293           g_warning ("Error converting text from IM to UTF-8: %s\n", error->message);
294           g_error_free (error);
295         }
296     }
297   
298   return result;
299 }
300
301 static gboolean
302 gtk_im_context_xim_filter_keypress (GtkIMContext *context,
303                                     GdkEventKey  *event)
304 {
305   GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
306   XIC ic = gtk_im_context_xim_get_ic (context_xim);
307   gchar static_buffer[256];
308   gchar *buffer = static_buffer;
309   gint buffer_size = sizeof(static_buffer) - 1;
310   gint num_bytes = 0;
311   KeySym keysym;
312   Status status;
313   gboolean result = FALSE;
314
315   XKeyPressedEvent xevent;
316
317   if (!ic)
318     return FALSE;
319
320   xevent.type = KeyPress;
321   xevent.serial = 0;            /* hope it doesn't matter */
322   xevent.send_event = event->send_event;
323   xevent.display = GDK_DRAWABLE_XDISPLAY (event->window);
324   xevent.window = GDK_DRAWABLE_XID (event->window);
325   xevent.root = GDK_ROOT_WINDOW();
326   xevent.subwindow = xevent.window;
327   xevent.time = event->time;
328   xevent.x = xevent.x_root = 0;
329   xevent.y = xevent.y_root = 0;
330   xevent.state = event->state;
331   xevent.keycode = event->keyval ? XKeysymToKeycode (xevent.display, event->keyval) : 0;
332   xevent.same_screen = True;
333   
334   if (XFilterEvent ((XEvent *)&xevent, GDK_DRAWABLE_XID (context_xim->client_window)))
335     return TRUE;
336   
337  again:
338   num_bytes = XmbLookupString (ic, &xevent, buffer, buffer_size, &keysym, &status);
339
340   if (status == XBufferOverflow)
341     {
342       buffer_size = num_bytes;
343       buffer = g_malloc (num_bytes + 1);
344       goto again;
345     }
346
347   /* I don't know how we should properly handle XLookupKeysym or XLookupBoth
348    * here ... do input methods actually change the keysym? we can't really
349    * feed it back to accelerator processing at this point...
350    */
351   if (status == XLookupChars || status == XLookupBoth)
352     {
353       char *result_utf8;
354
355       buffer[num_bytes] = '\0';
356
357       result_utf8 = mb_to_utf8 (context_xim, buffer);
358       if (result_utf8)
359         {
360           if ((guchar)result_utf8[0] >= 0x20) /* Some IM have a nasty habit of converting
361                                                * control characters into strings
362                                                */
363             {
364               gtk_signal_emit_by_name (GTK_OBJECT (context), "commit", result_utf8);
365               result = TRUE;
366             }
367           
368           g_free (result_utf8);
369         }
370     }
371
372   return FALSE;
373 }
374
375 static void
376 gtk_im_context_xim_focus_in (GtkIMContext *context)
377 {
378   GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
379   XIC ic = gtk_im_context_xim_get_ic (context_xim);
380
381   if (!ic)
382     return;
383
384   XSetICFocus (ic);
385   return;
386 }
387
388 static void
389 gtk_im_context_xim_focus_out (GtkIMContext *context)
390 {
391   GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
392   XIC ic = gtk_im_context_xim_get_ic (context_xim);
393
394   if (!ic)
395     return;
396
397   XUnsetICFocus (ic);
398   return;
399 }
400
401 static void
402 gtk_im_context_xim_set_cursor_location (GtkIMContext *context,
403                                         GdkRectangle *area)
404 {
405   GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
406   XIC ic = gtk_im_context_xim_get_ic (context_xim);
407
408   XVaNestedList preedit_attr;
409   XPoint          spot;
410
411   if (!ic)
412     return;
413
414   spot.x = area->x;
415   spot.y = area->y;
416
417   preedit_attr = XVaCreateNestedList (0,
418                                       XNSpotLocation, &spot,
419                                       0);
420   XSetICValues (ic,
421                 XNPreeditAttributes, preedit_attr,
422                 NULL);
423   XFree(preedit_attr);
424
425   return;
426 }
427
428 static void
429 gtk_im_context_xim_reset (GtkIMContext *context)
430 {
431   GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
432   XIC ic = gtk_im_context_xim_get_ic (context_xim);
433   gchar *result;
434
435   /* restore conversion state after resetting ic later */
436   XIMPreeditState preedit_state = XIMPreeditUnKnown;
437   XVaNestedList preedit_attr;
438   gboolean have_preedit_state = FALSE;
439
440   if (!ic)
441     return;
442   
443
444   preedit_attr = XVaCreateNestedList(0,
445                                      XNPreeditState, &preedit_state,
446                                      0);
447   if (!XGetICValues(ic,
448                     XNPreeditAttributes, preedit_attr,
449                     NULL))
450     have_preedit_state = TRUE;
451
452   XFree(preedit_attr);
453
454   result = XmbResetIC (ic);
455
456   preedit_attr = XVaCreateNestedList(0,
457                                      XNPreeditState, preedit_state,
458                                      0);
459   if (have_preedit_state)
460     XSetICValues(ic,
461                  XNPreeditAttributes, preedit_attr,
462                  NULL);
463
464   XFree(preedit_attr);
465
466   if (result)
467     {
468       char *result_utf8 = mb_to_utf8 (context_xim, result);
469       if (result_utf8)
470         {
471           gtk_signal_emit_by_name (GTK_OBJECT (context), "commit", result_utf8);
472           g_free (result_utf8);
473         }
474     }
475
476   if (context_xim->preedit_length)
477     {
478       context_xim->preedit_length = 0;
479       gtk_signal_emit_by_name (GTK_OBJECT (context), "preedit_changed");
480     }
481
482   XFree (result);
483 }
484
485 /* Mask of feedback bits that we render
486  */
487 #define FEEDBACK_MASK (XIMReverse | XIMUnderline)
488
489 static void
490 add_feedback_attr (PangoAttrList *attrs,
491                    const gchar   *str,
492                    XIMFeedback    feedback,
493                    gint           start_pos,
494                    gint           end_pos)
495 {
496   PangoAttribute *attr;
497   
498   gint start_index = g_utf8_offset_to_pointer (str, start_pos) - str;
499   gint end_index = g_utf8_offset_to_pointer (str, end_pos) - str;
500
501   if (feedback & XIMUnderline)
502     {
503       attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
504       attr->start_index = start_index;
505       attr->end_index = end_index;
506
507       pango_attr_list_change (attrs, attr);
508     }
509
510   if (feedback & XIMReverse)
511     {
512       attr = pango_attr_foreground_new (0xffff, 0xffff, 0xffff);
513       attr->start_index = start_index;
514       attr->end_index = end_index;
515
516       pango_attr_list_change (attrs, attr);
517
518       attr = pango_attr_background_new (0, 0, 0);
519       attr->start_index = start_index;
520       attr->end_index = end_index;
521
522       pango_attr_list_change (attrs, attr);
523     }
524
525   if (feedback & ~FEEDBACK_MASK)
526     g_warning ("Unrendered feedback style: %#lx", feedback & ~FEEDBACK_MASK);
527 }
528
529 static void     
530 gtk_im_context_xim_get_preedit_string (GtkIMContext   *context,
531                                        gchar         **str,
532                                        PangoAttrList **attrs,
533                                        gint           *cursor_pos)
534 {
535   GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
536   gchar *utf8 = g_ucs4_to_utf8 (context_xim->preedit_chars, context_xim->preedit_length, NULL, NULL, NULL);
537
538   if (attrs)
539     {
540       int i;
541       XIMFeedback last_feedback = 0;
542       gint start = -1;
543       
544       *attrs = pango_attr_list_new ();
545
546       for (i = 0; i < context_xim->preedit_length; i++)
547         {
548           XIMFeedback new_feedback = context_xim->feedbacks[i] & FEEDBACK_MASK;
549           if (new_feedback != last_feedback)
550             {
551               if (start >= 0)
552                 add_feedback_attr (*attrs, utf8, last_feedback, start, i);
553               
554               last_feedback = new_feedback;
555               start = i;
556             }
557         }
558
559       if (start >= 0)
560         add_feedback_attr (*attrs, utf8, last_feedback, start, i);
561     }
562
563   if (str)
564     *str = utf8;
565   else
566     g_free (utf8);
567
568   if (cursor_pos)
569     *cursor_pos = context_xim->preedit_cursor;
570 }
571
572 static void
573 preedit_start_callback (XIC      xic,
574                         XPointer client_data,
575                         XPointer call_data)
576 {
577   GtkIMContext *context = GTK_IM_CONTEXT (client_data);
578   
579   gtk_signal_emit_by_name (GTK_OBJECT (context), "preedit_start");
580   g_print ("Starting preedit!\n");
581 }                    
582
583 static void
584 preedit_done_callback (XIC      xic,
585                      XPointer client_data,
586                      XPointer call_data)
587 {
588   GtkIMContext *context = GTK_IM_CONTEXT (client_data);
589   
590   gtk_signal_emit_by_name (GTK_OBJECT (context), "preedit_end");  
591   g_print ("Ending preedit!\n");
592 }                    
593
594 static gint
595 xim_text_to_utf8 (GtkIMContextXIM *context, XIMText *xim_text, gchar **text)
596 {
597   gint text_length = 0;
598   GError *error = NULL;
599   gchar *result = NULL;
600
601   if (xim_text && xim_text->string.multi_byte)
602     {
603       if (xim_text->encoding_is_wchar)
604         {
605           g_warning ("Wide character return from Xlib not currently supported");
606           *text = NULL;
607           return 0;
608         }
609
610       if (strcmp (context->mb_charset, "UTF-8") == 0)
611         result = g_strdup (xim_text->string.multi_byte);
612       else
613         result = g_convert (xim_text->string.multi_byte,
614                             -1,
615                             "UTF-8",
616                             context->mb_charset,
617                             NULL, NULL, &error);
618       
619       if (result)
620         {
621           text_length = g_utf8_strlen (result, -1);
622           
623           if (text_length != xim_text->length)
624             {
625               g_warning ("Size mismatch when converting text from input method: supplied length = %d\n, result length = %d", xim_text->length, text_length);
626             }
627         }
628       else
629         {
630           g_warning ("Error converting text from IM to UCS-4: %s", error->message);
631           g_error_free (error);
632
633           *text = NULL;
634           return 0;
635         }
636
637       *text = result;
638       return text_length;
639     }
640   else
641     {
642       *text = NULL;
643       return 0;
644     }
645 }
646
647 static void
648 preedit_draw_callback (XIC                           xic, 
649                        XPointer                      client_data,
650                        XIMPreeditDrawCallbackStruct *call_data)
651 {
652   GtkIMContextXIM *context = GTK_IM_CONTEXT_XIM (client_data);
653
654   XIMText *new_xim_text = call_data->text;
655   gint new_text_length;
656   gunichar *new_text = NULL;
657   gint i;
658   gint diff;
659   gint new_length;
660   gchar *tmp;
661   
662   gint chg_first = CLAMP (call_data->chg_first, 0, context->preedit_length);
663   gint chg_length = CLAMP (call_data->chg_length, 0, context->preedit_length - chg_first);
664
665   context->preedit_cursor = call_data->caret;
666   
667   if (chg_first != call_data->chg_first || chg_length != call_data->chg_length)
668     g_warning ("Invalid change to preedit string, first=%d length=%d (orig length == %d)",
669                call_data->chg_first, call_data->chg_length, context->preedit_length);
670
671   new_text_length = xim_text_to_utf8 (context, new_xim_text, &tmp);
672   if (tmp)
673     {
674       new_text = g_utf8_to_ucs4_fast (tmp, -1, NULL);
675       g_free (tmp);
676     }
677   
678   diff = new_text_length - chg_length;
679   new_length = context->preedit_length + diff;
680
681   if (new_length > context->preedit_size)
682     {
683       context->preedit_size = new_length;
684       context->preedit_chars = g_renew (gunichar, context->preedit_chars, new_length);
685       context->feedbacks = g_renew (XIMFeedback, context->feedbacks, new_length);
686     }
687
688   if (diff < 0)
689     {
690       for (i = chg_first + chg_length ; i < context->preedit_length; i++)
691         {
692           context->preedit_chars[i + diff] = context->preedit_chars[i];
693           context->feedbacks[i + diff] = context->feedbacks[i];
694         }
695     }
696   else
697     {
698       for (i = context->preedit_length - 1; i >= chg_first + chg_length ; i--)
699         {
700           context->preedit_chars[i + diff] = context->preedit_chars[i];
701           context->feedbacks[i + diff] = context->feedbacks[i];
702         }
703     }
704
705   for (i = 0; i < new_text_length; i++)
706     {
707       context->preedit_chars[chg_first + i] = new_text[i];
708       context->feedbacks[chg_first + i] = new_xim_text->feedback[i];
709     }
710
711   context->preedit_length += diff;
712
713   if (new_text)
714     g_free (new_text);
715
716   gtk_signal_emit_by_name (GTK_OBJECT (context), "preedit_changed");
717 }
718     
719
720 static void
721 preedit_caret_callback (XIC                            xic,
722                         XPointer                       client_data,
723                         XIMPreeditCaretCallbackStruct *call_data)
724 {
725   GtkIMContextXIM *context = GTK_IM_CONTEXT_XIM (client_data);
726   
727   if (call_data->direction == XIMAbsolutePosition)
728     {
729       context->preedit_cursor = call_data->position;
730       gtk_signal_emit_by_name (GTK_OBJECT (context), "preedit_changed");
731     }
732   else
733     {
734       g_warning ("Caret movement command: %d %d %d not supported",
735                  call_data->position, call_data->direction, call_data->style);
736     }
737 }            
738
739 static void
740 status_start_callback (XIC      xic,
741                        XPointer client_data,
742                        XPointer call_data)
743 {
744   g_print ("Status start\n");
745
746
747 static void
748 status_done_callback (XIC      xic,
749                       XPointer client_data,
750                       XPointer call_data)
751 {
752   g_print ("Status done!\n");
753 }
754
755 static void
756 status_draw_callback (XIC      xic,
757                       XPointer client_data,
758                       XIMStatusDrawCallbackStruct *call_data)
759 {
760   GtkIMContextXIM *context = GTK_IM_CONTEXT_XIM (client_data);
761
762   g_print ("Status draw\n");
763   if (call_data->type == XIMTextType)
764     {
765       gchar *text;
766       xim_text_to_utf8 (context, call_data->data.text, &text);
767
768       if (text)
769         g_print ("  %s\n", text);
770     }
771   else                          /* bitmap */
772     {
773       g_print ("   bitmap id = %#lx\n", call_data->data.bitmap);
774     }
775 }
776
777 static XIC
778 gtk_im_context_xim_get_ic (GtkIMContextXIM *context_xim)
779 {
780   const char *name1 = NULL;
781   XVaNestedList list1 = NULL;
782   const char *name2 = NULL;
783   XVaNestedList list2 = NULL;
784
785   if (!context_xim->ic && context_xim->client_window)
786     {
787       if ((context_xim->im_info->style & PREEDIT_MASK) == XIMPreeditCallbacks)
788         {
789           context_xim->preedit_start_callback.client_data = (XPointer)context_xim;
790           context_xim->preedit_start_callback.callback = (XIMProc)preedit_start_callback;
791           context_xim->preedit_done_callback.client_data = (XPointer)context_xim;
792           context_xim->preedit_done_callback.callback = (XIMProc)preedit_done_callback;
793           context_xim->preedit_draw_callback.client_data = (XPointer)context_xim;
794           context_xim->preedit_draw_callback.callback = (XIMProc)preedit_draw_callback;
795           context_xim->preedit_caret_callback.client_data = (XPointer)context_xim;
796           context_xim->preedit_caret_callback.callback = (XIMProc)preedit_caret_callback;
797           
798           name1 = XNPreeditAttributes;
799           list1 = XVaCreateNestedList (0,
800                                        XNPreeditStartCallback, &context_xim->preedit_start_callback,
801                                        XNPreeditDoneCallback, &context_xim->preedit_done_callback,
802                                        XNPreeditDrawCallback, &context_xim->preedit_draw_callback,
803                                        XNPreeditCaretCallback, &context_xim->preedit_caret_callback,
804                                        NULL);
805         }
806
807       if ((context_xim->im_info->style & STATUS_MASK) == XIMStatusCallbacks)
808         {
809           XVaNestedList status_attrs;
810           
811           context_xim->status_start_callback.client_data = (XPointer)context_xim;
812           context_xim->status_start_callback.callback = (XIMProc)status_start_callback;
813           context_xim->status_done_callback.client_data = (XPointer)context_xim;
814           context_xim->status_done_callback.callback = (XIMProc)status_done_callback;
815           context_xim->status_draw_callback.client_data = (XPointer)context_xim;
816           context_xim->status_draw_callback.callback = (XIMProc)status_draw_callback;
817           
818           status_attrs = XVaCreateNestedList (0,
819                                               XNStatusStartCallback, &context_xim->status_start_callback,
820                                               XNStatusDoneCallback, &context_xim->status_done_callback,
821                                               XNStatusDrawCallback, &context_xim->status_draw_callback,
822                                               NULL);
823           
824           if (name1 == NULL)
825             {
826               name1 = XNStatusAttributes;
827               list1 = status_attrs;
828             }
829           else
830             {
831               name2 = XNStatusAttributes;
832               list2 = status_attrs;
833             }
834         }
835
836       context_xim->ic = XCreateIC (context_xim->im_info->im,
837                                    XNInputStyle, context_xim->im_info->style,
838                                    XNClientWindow, GDK_DRAWABLE_XID (context_xim->client_window),
839                                    name1, list1,
840                                    name2, list2,
841                                    NULL);
842       
843       if (list1)
844         XFree (list1);
845       if (list2)
846         XFree (list2);
847     }
848
849   return context_xim->ic;
850 }