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 #include "locale.h"
20 #include <string.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 
24 //#include "gtkintl.h"
25 #include <gtk/gtk.h>
26 #if !GTK_CHECK_VERSION(3,0,0)
27 #include <gtk/gtklabel.h>
28 #include <gtk/gtksignal.h>
29 #include <gtk/gtkwindow.h>
30 #include <gdk/gdk.h>
31 #endif
32 #include "gtkimcontextgcin.h"
33 // #include "gcin.h"  // for debug only
34 #include "gcin-im-client.h"
35 #include <X11/keysym.h>
36 
37 #define DBG 0
38 
39 
40 #if !GTK_CHECK_VERSION(3,0,0)
41 #define gdk_window_get_screen gdk_drawable_get_screen
42 #endif
43 
44 
45 typedef struct _GtkGCINInfo GtkGCINInfo;
46 
47 #define FLAG_CHROME 1
48 #define FLAG_FIREFOX 2
49 
50 struct _GtkIMContextGCIN
51 {
52   GtkIMContext object;
53 
54   GdkWindow *client_window;
55 #if 0
56   GtkWidget *client_widget;
57 #endif
58   GCIN_client_handle *gcin_ch;
59   int timeout_handle;
60   char *pe_str;
61   int old_sub_comp_len;
62   gboolean pe_started;
63   GCIN_PREEDIT_ATTR *pe_att;
64   int pe_attN;
65   int pe_cursor;
66   int flags;
67 };
68 
69 
70 static void     gtk_im_context_gcin_class_init         (GtkIMContextGCINClass  *class);
71 static void     gtk_im_context_gcin_init               (GtkIMContextGCIN       *im_context_gcin);
72 static void     gtk_im_context_gcin_finalize           (GObject               *obj);
73 static void     gtk_im_context_gcin_set_client_window  (GtkIMContext          *context,
74 						       GdkWindow             *client_window);
75 static gboolean gtk_im_context_gcin_filter_keypress    (GtkIMContext          *context,
76 						       GdkEventKey           *key);
77 static void     gtk_im_context_gcin_reset              (GtkIMContext          *context);
78 static void     gtk_im_context_gcin_focus_in           (GtkIMContext          *context);
79 static void     gtk_im_context_gcin_focus_out          (GtkIMContext          *context);
80 static void     gtk_im_context_gcin_set_cursor_location (GtkIMContext          *context,
81                                                        GdkRectangle             *area);
82 static void     gtk_im_context_gcin_set_use_preedit    (GtkIMContext          *context,
83                                                        gboolean               use_preedit);
84 static void     gtk_im_context_gcin_get_preedit_string (GtkIMContext          *context,
85                                                        gchar                **str,
86                                                        PangoAttrList        **attrs,
87                                                        gint                  *cursor_pos);
88 
89 // static GObjectClass *parent_class;
90 
91 GType gtk_type_im_context_gcin = 0;
92 
93 void
gtk_im_context_gcin_register_type(GTypeModule * type_module)94 gtk_im_context_gcin_register_type (GTypeModule *type_module)
95 {
96   static const GTypeInfo im_context_gcin_info =
97   {
98     sizeof (GtkIMContextGCINClass),
99     (GBaseInitFunc) NULL,
100     (GBaseFinalizeFunc) NULL,
101     (GClassInitFunc) gtk_im_context_gcin_class_init,
102     NULL,           /* class_finalize */
103     NULL,           /* class_data */
104     sizeof (GtkIMContextGCIN),
105     0,
106     (GInstanceInitFunc) gtk_im_context_gcin_init,
107   };
108 
109   gtk_type_im_context_gcin =
110     g_type_module_register_type (type_module,
111                                  GTK_TYPE_IM_CONTEXT,
112                                  "GtkIMContextGCIN",
113                                  &im_context_gcin_info, 0);
114 }
115 
116 static void
reinitialize_all_ics(GtkGCINInfo * info)117 reinitialize_all_ics (GtkGCINInfo *info)
118 {
119 #if DBG
120   puts("reinitialize_all_ics");
121 #endif
122 }
123 
124 #if 0
125 static void gcin_display_closed (GdkDisplay *display,
126                          gboolean    is_error,
127                          GtkIMContextGCIN *context_xim)
128 {
129 #if DBG
130   puts("gcin_display_closed");
131 #endif
132   if (!context_xim->gcin_ch)
133     return;
134 
135   gcin_im_client_close(context_xim->gcin_ch);
136   context_xim->gcin_ch = NULL;
137 }
138 #endif
139 
140 GdkScreen *gdk_window_get_screen (GdkWindow *window);
141 
142 static void
get_im(GtkIMContextGCIN * context_xim)143 get_im (GtkIMContextGCIN *context_xim)
144 {
145   GdkWindow *client_window = context_xim->client_window;
146   if (!client_window)
147     return;
148   GdkScreen *screen = gdk_window_get_screen (client_window);
149   if (!screen)
150     return;
151 
152   GdkDisplay *display = gdk_screen_get_display (screen);
153   if (!display)
154     return;
155 
156 #if DBG
157 	setbuf(stdout, NULL);
158 #endif
159 
160   if (!context_xim->gcin_ch) {
161     if (!(context_xim->gcin_ch = gcin_im_client_open(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()))))
162       perror("cannot open gcin_ch");
163 #if 1
164     context_xim->timeout_handle = 0;
165     context_xim->pe_attN = 0;
166     context_xim->pe_att = NULL;
167     context_xim->pe_str = NULL;
168     context_xim->pe_cursor = 0;
169     context_xim->pe_started = FALSE;
170 #endif
171 
172 	char tt[128];
173 	int len = readlink("/proc/self/exe", tt, sizeof(tt));
174 	if (len>0) {
175 		tt[len]=0;
176 		if (strstr(tt, "chrome"))
177 			context_xim->flags = FLAG_CHROME;
178 		else if (strstr(tt, "firefox"))
179 			context_xim->flags = FLAG_FIREFOX;
180 	}
181 
182 #if 0
183     // coredump
184     g_signal_connect (display, "closed",
185                       G_CALLBACK (gcin_display_closed), context_xim);
186 #endif
187   }
188 }
189 
190 ///
191 static void
gtk_im_context_gcin_class_init(GtkIMContextGCINClass * class)192 gtk_im_context_gcin_class_init (GtkIMContextGCINClass *class)
193 {
194   GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
195   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
196 
197 //  parent_class = g_type_class_peek_parent (class);
198   im_context_class->set_client_window = gtk_im_context_gcin_set_client_window;
199   im_context_class->filter_keypress = gtk_im_context_gcin_filter_keypress;
200   im_context_class->reset = gtk_im_context_gcin_reset;
201   im_context_class->get_preedit_string = gtk_im_context_gcin_get_preedit_string;
202   im_context_class->focus_in = gtk_im_context_gcin_focus_in;
203   im_context_class->focus_out = gtk_im_context_gcin_focus_out;
204   im_context_class->set_cursor_location = gtk_im_context_gcin_set_cursor_location;
205   im_context_class->set_use_preedit = gtk_im_context_gcin_set_use_preedit;
206   gobject_class->finalize = gtk_im_context_gcin_finalize;
207 }
208 
209 
210 static void
gtk_im_context_gcin_init(GtkIMContextGCIN * im_context_gcin)211 gtk_im_context_gcin_init (GtkIMContextGCIN *im_context_gcin)
212 {
213   im_context_gcin->timeout_handle = 0;
214   im_context_gcin->pe_attN = 0;
215   im_context_gcin->pe_att = NULL;
216   im_context_gcin->pe_str = NULL;
217   im_context_gcin->pe_cursor = 0;
218   im_context_gcin->gcin_ch = NULL;
219 //  test_gdm();
220 
221 #if DBG
222   printf("gtk_im_context_gcin_init %p\n", im_context_gcin);
223 #endif
224 // dirty hack for mozilla...
225 }
226 
227 
clear_preedit(GtkIMContextGCIN * context_gcin)228 void clear_preedit(GtkIMContextGCIN *context_gcin)
229 {
230   if (!context_gcin)
231     return;
232 
233   if (context_gcin->pe_str) {
234     free(context_gcin->pe_str);
235     context_gcin->pe_str = NULL;
236   }
237 
238   if (context_gcin->pe_att) {
239     free(context_gcin->pe_att);
240     context_gcin->pe_att = NULL;
241     context_gcin->pe_attN = 0;
242   }
243 
244   context_gcin->pe_cursor = 0;
245 }
246 
247 
248 static void
gtk_im_context_gcin_finalize(GObject * obj)249 gtk_im_context_gcin_finalize (GObject *obj)
250 {
251 #if DBG
252   printf("gtk_im_context_gcin_finalize %p\n", obj);
253 #endif
254   GtkIMContextGCIN *context_xim = GTK_IM_CONTEXT_GCIN (obj);
255   clear_preedit(context_xim);
256 
257   if (context_xim->gcin_ch) {
258     gcin_im_client_close(context_xim->gcin_ch);
259     context_xim->gcin_ch = NULL;
260   }
261 }
262 
263 
set_ic_client_window(GtkIMContextGCIN * context_xim,GdkWindow * client_window)264 static void set_ic_client_window (GtkIMContextGCIN *context_xim,
265                       GdkWindow       *client_window)
266 {
267 #if DBG
268   printf("set_ic_client_window %p %p\n", context_xim, client_window);
269 #endif
270   if (!client_window)
271     return;
272 
273   context_xim->client_window = client_window;
274 
275   if (context_xim->client_window) {
276     get_im (context_xim);
277     if (context_xim->gcin_ch) {
278       gcin_im_client_set_window(context_xim->gcin_ch, GDK_WINDOW_XID(client_window));
279     }
280   }
281 }
282 
283 
284 ///
285 static void
gtk_im_context_gcin_set_client_window(GtkIMContext * context,GdkWindow * client_window)286 gtk_im_context_gcin_set_client_window (GtkIMContext          *context,
287                                       GdkWindow             *client_window)
288 {
289 #if DBG
290   printf("gtk_im_context_gcin_set_client_window\n");
291 #endif
292   GtkIMContextGCIN *context_xim = GTK_IM_CONTEXT_GCIN (context);
293   set_ic_client_window (context_xim, client_window);
294 }
295 
296 ///
297 GtkIMContext *
gtk_im_context_gcin_new(void)298 gtk_im_context_gcin_new (void)
299 {
300 #if DBG
301   printf("gtk_im_context_gcin_new\n");
302 #endif
303   GtkIMContextGCIN *result;
304 
305   result = GTK_IM_CONTEXT_GCIN (g_object_new (GTK_TYPE_IM_CONTEXT_GCIN, NULL));
306 
307   return GTK_IM_CONTEXT (result);
308 }
309 
310 #include <gdk/gdkkeysyms.h>
311 
312 #if 0
313 static gboolean open_gcin_win(int sub_comp_len, GtkIMContextGCIN *context_xim)
314 {
315     return !(context_xim->old_sub_comp_len & 1) && (sub_comp_len & 1)
316         || !(context_xim->old_sub_comp_len & 2) && (sub_comp_len & 2)
317         ||  !(context_xim->old_sub_comp_len & 4) && (sub_comp_len & 4);
318 }
319 #endif
320 
321 
322 ///
323 static gboolean
gtk_im_context_gcin_filter_keypress(GtkIMContext * context,GdkEventKey * event)324 gtk_im_context_gcin_filter_keypress (GtkIMContext *context,
325                                     GdkEventKey  *event)
326 {
327   GtkIMContextGCIN *context_xim = GTK_IM_CONTEXT_GCIN (context);
328 
329   gchar static_buffer[256];
330   unsigned char *buffer = static_buffer;
331 //  char *buffer = static_buffer;
332   gint buffer_size = sizeof(static_buffer) - 1;
333   gsize num_bytes = 0;
334   KeySym keysym = 0;
335   Status status;
336   gboolean result = FALSE;
337 #if !GTK_CHECK_VERSION(3,0,0)
338   GdkWindow *root_window = gdk_screen_get_root_window (gdk_drawable_get_screen (event->window));
339 #else
340   GdkWindow *root_window = NULL;
341   GdkScreen *screen = gdk_window_get_screen (event->window);
342   if (screen)
343     root_window = gdk_screen_get_root_window (screen);
344   else
345     return result;
346 #endif
347 
348   XKeyPressedEvent xevent;
349   xevent.type = (event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease;
350   xevent.serial = 0;            /* hope it doesn't matter */
351   xevent.send_event = event->send_event;
352   xevent.display = GDK_WINDOW_XDISPLAY (event->window);
353   xevent.window = GDK_WINDOW_XID (event->window);
354   xevent.root = GDK_WINDOW_XID (root_window);
355   xevent.subwindow = xevent.window;
356   xevent.time = event->time;
357   xevent.x = xevent.x_root = 0;
358   xevent.y = xevent.y_root = 0;
359   xevent.state = event->state;
360   xevent.keycode = event->hardware_keycode;
361   xevent.same_screen = True;
362   num_bytes = XLookupString (&xevent, buffer, buffer_size, &keysym, NULL);
363 
364   char *rstr = NULL;
365 
366 #if (!FREEBSD || MAC_OS)
367 #if DBG
368   if (event->type == GDK_KEY_PRESS)
369     printf("kval %x %x\n",event->keyval, keysym);
370 #endif
371 
372   int uni = gdk_keyval_to_unicode(event->keyval);
373   if (uni) {
374     gsize rn;
375     GError *err = NULL;
376     char *utf8 = g_convert((char *)&uni, 4, "UTF-8", "UTF-32", &rn, &num_bytes, &err);
377 
378     if (utf8) {
379 //      printf("conv %s\n", utf8);
380       strcpy(buffer, utf8);
381       g_free(utf8);
382     }
383   }
384 #endif
385 
386   gboolean preedit_changed = FALSE;
387   gboolean context_pe_started = context_xim->pe_started;
388   gboolean context_has_str = context_xim->pe_str && context_xim->pe_str[0];
389   char *tstr = NULL;
390   int sub_comp_len = 0;
391   GCIN_PREEDIT_ATTR att[GCIN_PREEDIT_ATTR_MAX_N];
392   int cursor_pos;
393   gboolean has_str = FALSE;
394 
395   if (event->type == GDK_KEY_PRESS) {
396     result = gcin_im_client_forward_key_press(context_xim->gcin_ch,
397       keysym, xevent.state, &rstr);
398   } else {
399 //    printf("release %x %x\n", xevent.state, event->state);
400     result = gcin_im_client_forward_key_release(context_xim->gcin_ch,
401      keysym, xevent.state, &rstr);
402   }
403 
404     preedit_changed = result;
405 
406     int attN = gcin_im_client_get_preedit(context_xim->gcin_ch, &tstr, att, &cursor_pos, &sub_comp_len);
407     has_str = tstr && tstr[0];
408 
409 
410 #if DBG
411     printf("result keysym:%x %d sub_comp_len:%x tstr:%p\n", keysym, result, sub_comp_len, tstr);
412 #endif
413 
414     if (sub_comp_len) {
415       has_str = TRUE;
416 //      preedit_changed = TRUE;
417     }
418 
419     context_xim->old_sub_comp_len = sub_comp_len;
420 
421     if (!context_pe_started && has_str) {
422 #if DBG
423       printf("emit preedit-start\n");
424 #endif
425 #if 1
426       g_signal_emit_by_name (context, "preedit-start");
427 #endif
428       context_pe_started = context_xim->pe_started = TRUE;
429       preedit_changed = TRUE;
430     }
431 
432     if (context_has_str != has_str || (tstr && context_xim->pe_str && strcmp(tstr, context_xim->pe_str))) {
433       if (context_xim->pe_str)
434         free(context_xim->pe_str);
435       context_xim->pe_str = tstr;
436       preedit_changed = TRUE;
437     }
438 
439 
440     int attsz = sizeof(GCIN_PREEDIT_ATTR)*attN;
441     if (context_xim->pe_attN != attN ||
442       context_xim->pe_att && memcmp(context_xim->pe_att, att, attsz)) {
443 //      printf("att changed pe_att:%x:%d %d\n", context_xim->pe_att, context_xim->pe_attN, attN);
444       context_xim->pe_attN = attN;
445       if (context_xim->pe_att)
446         free(context_xim->pe_att);
447 
448       context_xim->pe_att = NULL;
449       if (attN)
450         context_xim->pe_att = malloc(attsz);
451       memcpy(context_xim->pe_att, att, attsz);
452 //      printf("context_xim->pe_att %x\n", context_xim->pe_att);
453       preedit_changed = TRUE;
454     }
455 
456     if (context_xim->pe_cursor != cursor_pos) {
457 #if DBG
458       printf("cursor changed %d %d\n", context_xim->pe_cursor, cursor_pos);
459 #endif
460       context_xim->pe_cursor = cursor_pos;
461       preedit_changed = TRUE;
462     }
463 
464 #if DBG
465     printf("seq:%d rstr:%s result:%d num_bytes:%d %x\n", context_xim->gcin_ch->seq, rstr, result, num_bytes, (unsigned int)buffer[0]);
466 #endif
467     if (event->type == GDK_KEY_PRESS && !rstr && !result && num_bytes &&
468 #if 1
469    buffer[0]>=0x20 && buffer[0]!=0x7f
470 #else
471    (event->keyval < 0xf000 || buffer[0]>=0x20 && buffer[0] < 0x7f)
472 #endif
473         && !(xevent.state & (Mod1Mask|ControlMask))) {
474       rstr = (char *)malloc(num_bytes + 1);
475       memcpy(rstr, buffer, num_bytes);
476       rstr[num_bytes] = 0;
477       result = TRUE;
478     }
479 
480 #if 1
481   if (preedit_changed && (context_xim->flags & FLAG_FIREFOX)) {
482 #if DBG
483     printf("preedit-change\n");
484 #endif
485     g_signal_emit_by_name(context_xim, "preedit_changed");
486   }
487 #endif
488 
489 #if DBG
490   printf("seq:%d event->type:%d iiiii %d  %x %d rstr:%p\n",context_xim->gcin_ch->seq, event->type, result, keysym,
491     num_bytes, rstr);
492 #endif
493   if (rstr) {
494 #if DBG
495     printf("emit %s\n", rstr);
496 #endif
497     g_signal_emit_by_name (context_xim, "commit", rstr);
498     free(rstr);
499   }
500 
501 
502   if (!has_str && context_pe_started) {
503     clear_preedit(context_xim);
504     preedit_changed = FALSE;
505     context_xim->pe_started = FALSE;
506 #if DBG
507     printf("preedit-end %x\n", has_str);
508 #endif
509 #if 1
510 	if (!(context_xim->flags & FLAG_CHROME))
511 		g_signal_emit_by_name(context_xim, "preedit_changed");
512 #endif
513 #if 1
514     g_signal_emit_by_name (context_xim, "preedit-end");
515 #endif
516   }
517 
518 #if 1
519   if (preedit_changed && !(context_xim->flags & FLAG_FIREFOX)) {
520 #if DBG
521     printf("preedit-change\n");
522 #endif
523     g_signal_emit_by_name(context_xim, "preedit_changed");
524   }
525 #endif
526 
527 
528   return result;
529 }
530 
531 ///
532 static void
gtk_im_context_gcin_focus_in(GtkIMContext * context)533 gtk_im_context_gcin_focus_in (GtkIMContext *context)
534 {
535   GtkIMContextGCIN *context_xim = GTK_IM_CONTEXT_GCIN (context);
536 #if DBG
537   printf("gtk_im_context_gcin_focus_in\n");
538 #endif
539   if (context_xim->gcin_ch) {
540     gcin_im_client_focus_in(context_xim->gcin_ch);
541   }
542 
543   return;
544 }
545 
546 static void
gtk_im_context_gcin_focus_out(GtkIMContext * context)547 gtk_im_context_gcin_focus_out (GtkIMContext *context)
548 {
549   GtkIMContextGCIN *context_xim = GTK_IM_CONTEXT_GCIN (context);
550 //  printf("gtk_im_context_gcin_focus_out\n");
551 
552   if (context_xim->gcin_ch) {
553     char *rstr;
554     gcin_im_client_focus_out2(context_xim->gcin_ch , &rstr);
555     context_xim->pe_started = FALSE;
556 
557     if (rstr) {
558       g_signal_emit_by_name (context, "commit", rstr);
559       clear_preedit(context_xim);
560       g_signal_emit_by_name(context, "preedit_changed");
561       free(rstr);
562     }
563 
564   }
565 
566   return;
567 }
568 
569 ///
570 static void
gtk_im_context_gcin_set_cursor_location(GtkIMContext * context,GdkRectangle * area)571 gtk_im_context_gcin_set_cursor_location (GtkIMContext *context,
572                                         GdkRectangle *area)
573 {
574 #if DBG
575   printf("a gtk_im_context_gcin_set_cursor_location %d,%d,%d\n", area->x, area->y, area->height);
576 #endif
577   if (!area) {
578     return;
579   }
580 
581 
582   GtkIMContextGCIN *context_xim = GTK_IM_CONTEXT_GCIN (context);
583 
584   if (!context_xim->gcin_ch) {
585     get_im(context_xim);
586   }
587 
588   if (context_xim->gcin_ch) {
589     gcin_im_client_set_cursor_location(context_xim->gcin_ch, area->x, area->y + area->height);
590   }
591 
592 #if DBG
593   printf("b gtk_im_context_gcin_set_cursor_location %d,%d,%d\n", area->x, area->y, area->height);
594 #endif
595 
596   return;
597 }
598 
599 ///
600 static void
gtk_im_context_gcin_set_use_preedit(GtkIMContext * context,gboolean use_preedit)601 gtk_im_context_gcin_set_use_preedit (GtkIMContext *context,
602                                     gboolean      use_preedit)
603 {
604   GtkIMContextGCIN *context_gcin = GTK_IM_CONTEXT_GCIN (context);
605 #if DBG
606   printf("gtk_im_context_gcin_set_use_preedit %p %d\n", context_gcin->gcin_ch, use_preedit);
607 #endif
608   if (!context_gcin->gcin_ch)
609     return;
610   int ret;
611   if (use_preedit)
612     gcin_im_client_set_flags(context_gcin->gcin_ch, FLAG_GCIN_client_handle_use_preedit, &ret);
613   else
614     gcin_im_client_clear_flags(context_gcin->gcin_ch, FLAG_GCIN_client_handle_use_preedit, &ret);
615 }
616 
617 
618 ///
619 static void
gtk_im_context_gcin_reset(GtkIMContext * context)620 gtk_im_context_gcin_reset (GtkIMContext *context)
621 {
622   GtkIMContextGCIN *context_gcin = GTK_IM_CONTEXT_GCIN (context);
623 #if DBG
624   printf("gtk_im_context_gcin_reset %p\n", context_gcin);
625 #endif
626 
627   context_gcin->pe_started = FALSE;
628 #if 1
629   if (context_gcin->gcin_ch) {
630     gcin_im_client_reset(context_gcin->gcin_ch);
631     if (context_gcin->pe_str && context_gcin->pe_str[0]) {
632 #if DBG
633       printf("clear %p\n", context_gcin);
634 #endif
635       clear_preedit(context_gcin);
636       g_signal_emit_by_name(context, "preedit_changed");
637     }
638   }
639 #endif
640 }
641 
642 /* Mask of feedback bits that we render
643  */
644 
645 static void
add_preedit_attr(PangoAttrList * attrs,const gchar * str,GCIN_PREEDIT_ATTR * att)646 add_preedit_attr (PangoAttrList *attrs,
647 		   const gchar *str,  GCIN_PREEDIT_ATTR *att)
648 {
649 //  printf("att %d %d\n", att->ofs0, att->ofs1);
650   PangoAttribute *attr;
651   gint start_index = g_utf8_offset_to_pointer (str, att->ofs0) - str;
652   gint end_index = g_utf8_offset_to_pointer (str, att->ofs1) - str;
653 
654   if (att->flag & GCIN_PREEDIT_ATTR_FLAG_UNDERLINE)
655     {
656       attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
657       attr->start_index = start_index;
658       attr->end_index = end_index;
659       pango_attr_list_change (attrs, attr);
660     }
661 
662   if (att->flag & GCIN_PREEDIT_ATTR_FLAG_REVERSE)
663     {
664       attr = pango_attr_foreground_new (0xffff, 0xffff, 0xffff);
665       attr->start_index = start_index;
666       attr->end_index = end_index;
667       pango_attr_list_change (attrs, attr);
668 
669       attr = pango_attr_background_new (0, 0, 0);
670       attr->start_index = start_index;
671       attr->end_index = end_index;
672       pango_attr_list_change (attrs, attr);
673     }
674 }
675 
676 static void
gtk_im_context_gcin_get_preedit_string(GtkIMContext * context,gchar ** str,PangoAttrList ** attrs,gint * cursor_pos)677 gtk_im_context_gcin_get_preedit_string (GtkIMContext   *context,
678 				       gchar         **str,
679 				       PangoAttrList **attrs,
680                                        gint           *cursor_pos)
681 {
682   GtkIMContextGCIN *context_gcin = GTK_IM_CONTEXT_GCIN (context);
683 
684 #if DBG
685   printf("gtk_im_context_gcin_get_preedit_string %p %p %p\n", str, attrs, cursor_pos);
686 #endif
687 
688   if (context_gcin->gcin_ch && cursor_pos) {
689     int ret;
690     gcin_im_client_set_flags(context_gcin->gcin_ch, FLAG_GCIN_client_handle_use_preedit, &ret);
691   }
692 
693   if (cursor_pos)
694       *cursor_pos = 0;
695 
696   if (attrs)
697       *attrs = pango_attr_list_new ();
698 
699   if (!str)
700     return;
701 
702 #if 1
703   if (!context_gcin->gcin_ch) {
704 empty:
705     *str=g_strdup("");
706     return;
707   }
708 
709   if (cursor_pos) {
710     *cursor_pos = context_gcin->pe_cursor;
711   }
712 
713   if (context_gcin->pe_str) {
714     *str=g_strdup(context_gcin->pe_str);
715   } else {
716     goto empty;
717   }
718 
719 #if DBG
720   printf("gtk_im_context_gcin_get_preedit_string %p attN:%d '%s'\n", context_gcin->pe_att,
721     context_gcin->pe_attN, *str);
722 #endif
723   int i;
724   if (attrs)
725   for(i=0; i < context_gcin->pe_attN; i++) {
726     add_preedit_attr(*attrs, *str, &(context_gcin->pe_att[i]));
727   }
728 
729 #else
730   if (str)
731     *str = g_strdup("");
732 #endif
733 }
734 
735 
736 /**
737  * gtk_im_context_gcin_shutdown:
738  *
739  * Destroys all the status windows that are kept by the GCIN contexts.  This
740  * function should only be called by the GCIN module exit routine.
741  **/
742 void
gtk_im_context_gcin_shutdown(void)743 gtk_im_context_gcin_shutdown (void)
744 {
745 #if DBG
746  printf("shutdown\n");
747 #endif
748 }
749