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