]> Pileus Git - ~andy/gtk/blob - modules/input/gtkimcontextxim.c
add set_cursor_pos() virtual method
[~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_pos     (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_pos = gtk_im_context_xim_set_cursor_pos;
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   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   result = g_convert (str, -1,
285                       "UTF-8", context_xim->mb_charset,
286                       NULL, NULL, &error);
287
288   if (!result)
289     {
290       g_warning ("Error converting text from IM to UTF-8: %s\n", error->message);
291       g_error_free (error);
292     }
293   
294   return result;
295 }
296
297 static gboolean
298 gtk_im_context_xim_filter_keypress (GtkIMContext *context,
299                                     GdkEventKey  *event)
300 {
301   GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
302   XIC ic = gtk_im_context_xim_get_ic (context_xim);
303   gchar static_buffer[256];
304   gchar *buffer = static_buffer;
305   gint buffer_size = sizeof(static_buffer) - 1;
306   gint num_bytes = 0;
307   KeySym keysym;
308   Status status;
309   gboolean result = FALSE;
310
311   XKeyPressedEvent xevent;
312
313   if (!ic)
314     return FALSE;
315
316   xevent.type = KeyPress;
317   xevent.serial = 0;            /* hope it doesn't matter */
318   xevent.send_event = event->send_event;
319   xevent.display = GDK_DRAWABLE_XDISPLAY (event->window);
320   xevent.window = GDK_DRAWABLE_XID (event->window);
321   xevent.root = GDK_ROOT_WINDOW();
322   xevent.subwindow = xevent.window;
323   xevent.time = event->time;
324   xevent.x = xevent.x_root = 0;
325   xevent.y = xevent.y_root = 0;
326   xevent.state = event->state;
327   xevent.keycode = event->keyval ? XKeysymToKeycode (xevent.display, event->keyval) : 0;
328   xevent.same_screen = True;
329   
330   if (XFilterEvent ((XEvent *)&xevent, GDK_DRAWABLE_XID (context_xim->client_window)))
331     return TRUE;
332   
333  again:
334   num_bytes = XmbLookupString (ic, &xevent, buffer, buffer_size, &keysym, &status);
335
336   if (status == XBufferOverflow)
337     {
338       buffer_size = num_bytes;
339       buffer = g_malloc (num_bytes + 1);
340       goto again;
341     }
342
343   /* I don't know how we should properly handle XLookupKeysym or XLookupBoth
344    * here ... do input methods actually change the keysym? we can't really
345    * feed it back to accelerator processing at this point...
346    */
347   if (status == XLookupChars || status == XLookupBoth)
348     {
349       char *result_utf8;
350
351       buffer[num_bytes] = '\0';
352
353       result_utf8 = mb_to_utf8 (context_xim, buffer);
354       if (result_utf8)
355         {
356           if ((guchar)result_utf8[0] >= 0x20) /* Some IM have a nasty habit of converting
357                                                * control characters into strings
358                                                */
359             {
360               gtk_signal_emit_by_name (GTK_OBJECT (context), "commit", result_utf8);
361               result = TRUE;
362             }
363           
364           g_free (result_utf8);
365         }
366     }
367
368   return FALSE;
369 }
370
371 static void
372 gtk_im_context_xim_focus_in (GtkIMContext *context)
373 {
374   GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
375   XIC ic = gtk_im_context_xim_get_ic (context_xim);
376
377   if (!ic)
378     return;
379
380   XSetICFocus (ic);
381   return;
382 }
383
384 static void
385 gtk_im_context_xim_focus_out (GtkIMContext *context)
386 {
387   GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
388   XIC ic = gtk_im_context_xim_get_ic (context_xim);
389
390   if (!ic)
391     return;
392
393   XUnsetICFocus (ic);
394   return;
395 }
396
397 static void
398 gtk_im_context_xim_set_cursor_pos (GtkIMContext *context,
399                                    GdkRectangle *area)
400 {
401   GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
402   XIC ic = gtk_im_context_xim_get_ic (context_xim);
403
404   XVaNestedList preedit_attr;
405   XPoint          spot;
406
407   if (!ic)
408     return;
409
410   spot.x = area->x;
411   spot.y = area->y;
412
413   preedit_attr = XVaCreateNestedList (0,
414                                       XNSpotLocation, &spot,
415                                       0);
416   XSetICValues (ic,
417                 XNPreeditAttributes, preedit_attr,
418                 NULL);
419   XFree(preedit_attr);
420
421   return;
422 }
423
424 static void
425 gtk_im_context_xim_reset (GtkIMContext *context)
426 {
427   GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
428   XIC ic = gtk_im_context_xim_get_ic (context_xim);
429   gchar *result;
430
431   /* restore conversion state after resetting ic later */
432   XIMPreeditState preedit_state = XIMPreeditUnKnown;
433   XVaNestedList preedit_attr;
434   gboolean have_preedit_state = FALSE;
435
436   if (!ic)
437     return;
438   
439
440   preedit_attr = XVaCreateNestedList(0,
441                                      XNPreeditState, &preedit_state,
442                                      0);
443   if (!XGetICValues(ic,
444                     XNPreeditAttributes, preedit_attr,
445                     NULL))
446     have_preedit_state = TRUE;
447
448   XFree(preedit_attr);
449
450   result = XmbResetIC (ic);
451
452   preedit_attr = XVaCreateNestedList(0,
453                                      XNPreeditState, preedit_state,
454                                      0);
455   if (have_preedit_state)
456     XSetICValues(ic,
457                  XNPreeditAttributes, preedit_attr,
458                  NULL);
459
460   XFree(preedit_attr);
461
462   if (result)
463     {
464       char *result_utf8 = mb_to_utf8 (context_xim, result);
465       if (result_utf8)
466         {
467           gtk_signal_emit_by_name (GTK_OBJECT (context), "commit", result_utf8);
468           g_free (result_utf8);
469         }
470     }
471
472   if (context_xim->preedit_length)
473     {
474       context_xim->preedit_length = 0;
475       gtk_signal_emit_by_name (GTK_OBJECT (context), "preedit_changed");
476     }
477
478   XFree (result);
479 }
480
481 /* Mask of feedback bits that we render
482  */
483 #define FEEDBACK_MASK (XIMReverse | XIMUnderline)
484
485 static void
486 add_feedback_attr (PangoAttrList *attrs,
487                    const gchar   *str,
488                    XIMFeedback    feedback,
489                    gint           start_pos,
490                    gint           end_pos)
491 {
492   PangoAttribute *attr;
493   
494   gint start_index = g_utf8_offset_to_pointer (str, start_pos) - str;
495   gint end_index = g_utf8_offset_to_pointer (str, end_pos) - str;
496
497   if (feedback & XIMUnderline)
498     {
499       attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
500       attr->start_index = start_index;
501       attr->end_index = end_index;
502
503       pango_attr_list_change (attrs, attr);
504     }
505
506   if (feedback & XIMReverse)
507     {
508       attr = pango_attr_foreground_new (0xffff, 0xffff, 0xffff);
509       attr->start_index = start_index;
510       attr->end_index = end_index;
511
512       pango_attr_list_change (attrs, attr);
513
514       attr = pango_attr_background_new (0, 0, 0);
515       attr->start_index = start_index;
516       attr->end_index = end_index;
517
518       pango_attr_list_change (attrs, attr);
519     }
520
521   if (feedback & ~FEEDBACK_MASK)
522     g_warning ("Unrendered feedback style: %#lx", feedback & ~FEEDBACK_MASK);
523 }
524
525 static void     
526 gtk_im_context_xim_get_preedit_string (GtkIMContext   *context,
527                                        gchar         **str,
528                                        PangoAttrList **attrs,
529                                        gint           *cursor_pos)
530 {
531   GtkIMContextXIM *context_xim = GTK_IM_CONTEXT_XIM (context);
532   gchar *utf8 = g_ucs4_to_utf8 (context_xim->preedit_chars, context_xim->preedit_length, NULL, NULL, NULL);
533
534   if (attrs)
535     {
536       int i;
537       XIMFeedback last_feedback = 0;
538       gint start = -1;
539       
540       *attrs = pango_attr_list_new ();
541
542       for (i = 0; i < context_xim->preedit_length; i++)
543         {
544           XIMFeedback new_feedback = context_xim->feedbacks[i] & FEEDBACK_MASK;
545           if (new_feedback != last_feedback)
546             {
547               if (start >= 0)
548                 add_feedback_attr (*attrs, utf8, last_feedback, start, i);
549               
550               last_feedback = new_feedback;
551               start = i;
552             }
553         }
554
555       if (start >= 0)
556         add_feedback_attr (*attrs, utf8, last_feedback, start, i);
557     }
558
559   if (str)
560     *str = utf8;
561   else
562     g_free (utf8);
563
564   if (cursor_pos)
565     *cursor_pos = context_xim->preedit_cursor;
566 }
567
568 static void
569 preedit_start_callback (XIC      xic,
570                         XPointer client_data,
571                         XPointer call_data)
572 {
573   GtkIMContext *context = GTK_IM_CONTEXT (client_data);
574   
575   gtk_signal_emit_by_name (GTK_OBJECT (context), "preedit_start");
576   g_print ("Starting preedit!\n");
577 }                    
578
579 static void
580 preedit_done_callback (XIC      xic,
581                      XPointer client_data,
582                      XPointer call_data)
583 {
584   GtkIMContext *context = GTK_IM_CONTEXT (client_data);
585   
586   gtk_signal_emit_by_name (GTK_OBJECT (context), "preedit_end");  
587   g_print ("Ending preedit!\n");
588 }                    
589
590 static gint
591 xim_text_to_utf8 (GtkIMContextXIM *context, XIMText *xim_text, gchar **text)
592 {
593   gint text_length = 0;
594   GError *error = NULL;
595   gchar *result = NULL;
596
597   if (xim_text && xim_text->string.multi_byte)
598     {
599       if (xim_text->encoding_is_wchar)
600         {
601           g_warning ("Wide character return from Xlib not currently supported");
602           *text = NULL;
603           return 0;
604         }
605
606       result = g_convert (xim_text->string.multi_byte,
607                           -1,
608                           "UTF-8",
609                           context->mb_charset,
610                           NULL, &text_length,  &error);
611       
612       if (result)
613         {
614           text_length = g_utf8_strlen (result, -1);
615           
616           if (text_length != xim_text->length)
617             {
618               g_warning ("Size mismatch when converting text from input method: supplied length = %d\n, result length = %d", xim_text->length, text_length);
619             }
620         }
621       else
622         {
623           g_warning ("Error converting text from IM to UCS-4: %s", error->message);
624           g_error_free (error);
625         }
626
627       *text = result;
628       return text_length;
629     }
630   else
631     {
632       *text = NULL;
633       return 0;
634     }
635 }
636
637 static void
638 preedit_draw_callback (XIC                           xic, 
639                        XPointer                      client_data,
640                        XIMPreeditDrawCallbackStruct *call_data)
641 {
642   GtkIMContextXIM *context = GTK_IM_CONTEXT_XIM (client_data);
643
644   XIMText *new_xim_text = call_data->text;
645   gint new_text_length;
646   gunichar *new_text = NULL;
647   gint i;
648   gint diff;
649   gint new_length;
650   gchar *tmp;
651   
652   gint chg_first = CLAMP (call_data->chg_first, 0, context->preedit_length);
653   gint chg_length = CLAMP (call_data->chg_length, 0, context->preedit_length - chg_first);
654
655   context->preedit_cursor = call_data->caret;
656   
657   if (chg_first != call_data->chg_first || chg_length != call_data->chg_length)
658     g_warning ("Invalid change to preedit string, first=%d length=%d (orig length == %d)",
659                call_data->chg_first, call_data->chg_length, context->preedit_length);
660
661   new_text_length = xim_text_to_utf8 (context, new_xim_text, &tmp);
662   if (tmp)
663     {
664       new_text = g_utf8_to_ucs4_fast (tmp, -1, NULL);
665       g_free (tmp);
666     }
667   
668   diff = new_text_length - chg_length;
669   new_length = context->preedit_length + diff;
670
671   if (new_length > context->preedit_size)
672     {
673       context->preedit_size = new_length;
674       context->preedit_chars = g_renew (gunichar, context->preedit_chars, new_length);
675       context->feedbacks = g_renew (XIMFeedback, context->feedbacks, new_length);
676     }
677
678   if (diff < 0)
679     {
680       for (i = chg_first + chg_length ; i < context->preedit_length; i++)
681         {
682           context->preedit_chars[i + diff] = context->preedit_chars[i];
683           context->feedbacks[i + diff] = context->feedbacks[i];
684         }
685     }
686   else
687     {
688       for (i = context->preedit_length - 1; i >= chg_first + chg_length ; i--)
689         {
690           context->preedit_chars[i + diff] = context->preedit_chars[i];
691           context->feedbacks[i + diff] = context->feedbacks[i];
692         }
693     }
694
695   for (i = 0; i < new_text_length; i++)
696     {
697       context->preedit_chars[chg_first + i] = new_text[i];
698       context->feedbacks[chg_first + i] = new_xim_text->feedback[i];
699     }
700
701   context->preedit_length += diff;
702
703   if (new_text)
704     g_free (new_text);
705
706   gtk_signal_emit_by_name (GTK_OBJECT (context), "preedit_changed");
707 }
708     
709
710 static void
711 preedit_caret_callback (XIC                            xic,
712                         XPointer                       client_data,
713                         XIMPreeditCaretCallbackStruct *call_data)
714 {
715   GtkIMContextXIM *context = GTK_IM_CONTEXT_XIM (client_data);
716   
717   if (call_data->direction == XIMAbsolutePosition)
718     {
719       context->preedit_cursor = call_data->position;
720       gtk_signal_emit_by_name (GTK_OBJECT (context), "preedit_changed");
721     }
722   else
723     {
724       g_warning ("Caret movement command: %d %d %d not supported",
725                  call_data->position, call_data->direction, call_data->style);
726     }
727 }            
728
729 static void
730 status_start_callback (XIC      xic,
731                        XPointer client_data,
732                        XPointer call_data)
733 {
734   g_print ("Status start\n");
735
736
737 static void
738 status_done_callback (XIC      xic,
739                       XPointer client_data,
740                       XPointer call_data)
741 {
742   g_print ("Status done!\n");
743 }
744
745 static void
746 status_draw_callback (XIC      xic,
747                       XPointer client_data,
748                       XIMStatusDrawCallbackStruct *call_data)
749 {
750   GtkIMContextXIM *context = GTK_IM_CONTEXT_XIM (client_data);
751
752   g_print ("Status draw\n");
753   if (call_data->type == XIMTextType)
754     {
755       gchar *text;
756       xim_text_to_utf8 (context, call_data->data.text, &text);
757
758       if (text)
759         g_print ("  %s\n", text);
760     }
761   else                          /* bitmap */
762     {
763       g_print ("   bitmap id = %#lx\n", call_data->data.bitmap);
764     }
765 }
766
767 static XIC
768 gtk_im_context_xim_get_ic (GtkIMContextXIM *context_xim)
769 {
770   const char *name1 = NULL;
771   XVaNestedList list1 = NULL;
772   const char *name2 = NULL;
773   XVaNestedList list2 = NULL;
774
775   if (!context_xim->ic && context_xim->client_window)
776     {
777       if ((context_xim->im_info->style & PREEDIT_MASK) == XIMPreeditCallbacks)
778         {
779           context_xim->preedit_start_callback.client_data = (XPointer)context_xim;
780           context_xim->preedit_start_callback.callback = (XIMProc)preedit_start_callback;
781           context_xim->preedit_done_callback.client_data = (XPointer)context_xim;
782           context_xim->preedit_done_callback.callback = (XIMProc)preedit_done_callback;
783           context_xim->preedit_draw_callback.client_data = (XPointer)context_xim;
784           context_xim->preedit_draw_callback.callback = (XIMProc)preedit_draw_callback;
785           context_xim->preedit_caret_callback.client_data = (XPointer)context_xim;
786           context_xim->preedit_caret_callback.callback = (XIMProc)preedit_caret_callback;
787           
788           name1 = XNPreeditAttributes;
789           list1 = XVaCreateNestedList (0,
790                                        XNPreeditStartCallback, &context_xim->preedit_start_callback,
791                                        XNPreeditDoneCallback, &context_xim->preedit_done_callback,
792                                        XNPreeditDrawCallback, &context_xim->preedit_draw_callback,
793                                        XNPreeditCaretCallback, &context_xim->preedit_caret_callback,
794                                        NULL);
795         }
796
797       if ((context_xim->im_info->style & STATUS_MASK) == XIMStatusCallbacks)
798         {
799           XVaNestedList status_attrs;
800           
801           context_xim->status_start_callback.client_data = (XPointer)context_xim;
802           context_xim->status_start_callback.callback = (XIMProc)status_start_callback;
803           context_xim->status_done_callback.client_data = (XPointer)context_xim;
804           context_xim->status_done_callback.callback = (XIMProc)status_done_callback;
805           context_xim->status_draw_callback.client_data = (XPointer)context_xim;
806           context_xim->status_draw_callback.callback = (XIMProc)status_draw_callback;
807           
808           status_attrs = XVaCreateNestedList (0,
809                                               XNStatusStartCallback, &context_xim->status_start_callback,
810                                               XNStatusDoneCallback, &context_xim->status_done_callback,
811                                               XNStatusDrawCallback, &context_xim->status_draw_callback,
812                                               NULL);
813           
814           if (name1 == NULL)
815             {
816               name1 = XNStatusAttributes;
817               list1 = status_attrs;
818             }
819           else
820             {
821               name2 = XNStatusAttributes;
822               list2 = status_attrs;
823             }
824         }
825
826       context_xim->ic = XCreateIC (context_xim->im_info->im,
827                                    XNInputStyle, context_xim->im_info->style,
828                                    XNClientWindow, GDK_DRAWABLE_XID (context_xim->client_window),
829                                    name1, list1,
830                                    name2, list2,
831                                    NULL);
832       
833       if (list1)
834         XFree (list1);
835       if (list2)
836         XFree (list2);
837     }
838
839   return context_xim->ic;
840 }