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