File: | modules/input/ctkimcontextxim.c |
Warning: | line 1092, column 4 Access of the heap area at negative byte offset |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* CTK - 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, see <http://www.gnu.org/licenses/>. | |||
16 | */ | |||
17 | ||||
18 | #include "config.h" | |||
19 | #include "locale.h" | |||
20 | #include <string.h> | |||
21 | #include <stdlib.h> | |||
22 | ||||
23 | #include "ctkimcontextxim.h" | |||
24 | ||||
25 | #include "ctk/ctkintl.h" | |||
26 | ||||
27 | typedef struct _StatusWindow StatusWindow; | |||
28 | typedef struct _CtkXIMInfo CtkXIMInfo; | |||
29 | ||||
30 | struct _CtkIMContextXIM | |||
31 | { | |||
32 | CtkIMContext object; | |||
33 | ||||
34 | CtkXIMInfo *im_info; | |||
35 | ||||
36 | gchar *locale; | |||
37 | gchar *mb_charset; | |||
38 | ||||
39 | CdkWindow *client_window; | |||
40 | CtkWidget *client_widget; | |||
41 | ||||
42 | /* The status window for this input context; we claim the | |||
43 | * status window when we are focused and have created an XIC | |||
44 | */ | |||
45 | StatusWindow *status_window; | |||
46 | ||||
47 | gint preedit_size; | |||
48 | gint preedit_length; | |||
49 | gunichar *preedit_chars; | |||
50 | XIMFeedback *feedbacks; | |||
51 | ||||
52 | gint preedit_cursor; | |||
53 | ||||
54 | XIMCallback preedit_start_callback; | |||
55 | XIMCallback preedit_done_callback; | |||
56 | XIMCallback preedit_draw_callback; | |||
57 | XIMCallback preedit_caret_callback; | |||
58 | ||||
59 | XIMCallback status_start_callback; | |||
60 | XIMCallback status_done_callback; | |||
61 | XIMCallback status_draw_callback; | |||
62 | ||||
63 | XIMCallback string_conversion_callback; | |||
64 | ||||
65 | XIC ic; | |||
66 | ||||
67 | guint filter_key_release : 1; | |||
68 | guint use_preedit : 1; | |||
69 | guint finalizing : 1; | |||
70 | guint in_toplevel : 1; | |||
71 | guint has_focus : 1; | |||
72 | }; | |||
73 | ||||
74 | struct _CtkXIMInfo | |||
75 | { | |||
76 | CdkScreen *screen; | |||
77 | XIM im; | |||
78 | char *locale; | |||
79 | XIMStyle preedit_style_setting; | |||
80 | XIMStyle status_style_setting; | |||
81 | XIMStyle style; | |||
82 | CtkSettings *settings; | |||
83 | gulong status_set; | |||
84 | gulong preedit_set; | |||
85 | gulong display_closed_cb; | |||
86 | XIMStyles *xim_styles; | |||
87 | GSList *ics; | |||
88 | ||||
89 | guint reconnecting :1; | |||
90 | guint supports_string_conversion; | |||
91 | }; | |||
92 | ||||
93 | /* A context status window; these are kept in the status_windows list. */ | |||
94 | struct _StatusWindow | |||
95 | { | |||
96 | CtkWidget *window; | |||
97 | ||||
98 | /* Toplevel window to which the status window corresponds */ | |||
99 | CtkWidget *toplevel; | |||
100 | ||||
101 | /* Currently focused CtkIMContextXIM for the toplevel, if any */ | |||
102 | CtkIMContextXIM *context; | |||
103 | }; | |||
104 | ||||
105 | static void ctk_im_context_xim_class_init (CtkIMContextXIMClass *class); | |||
106 | static void ctk_im_context_xim_init (CtkIMContextXIM *im_context_xim); | |||
107 | static void ctk_im_context_xim_finalize (GObject *obj); | |||
108 | static void ctk_im_context_xim_set_client_window (CtkIMContext *context, | |||
109 | CdkWindow *client_window); | |||
110 | static gboolean ctk_im_context_xim_filter_keypress (CtkIMContext *context, | |||
111 | CdkEventKey *key); | |||
112 | static void ctk_im_context_xim_reset (CtkIMContext *context); | |||
113 | static void ctk_im_context_xim_focus_in (CtkIMContext *context); | |||
114 | static void ctk_im_context_xim_focus_out (CtkIMContext *context); | |||
115 | static void ctk_im_context_xim_set_cursor_location (CtkIMContext *context, | |||
116 | CdkRectangle *area); | |||
117 | static void ctk_im_context_xim_set_use_preedit (CtkIMContext *context, | |||
118 | gboolean use_preedit); | |||
119 | static void ctk_im_context_xim_get_preedit_string (CtkIMContext *context, | |||
120 | gchar **str, | |||
121 | PangoAttrList **attrs, | |||
122 | gint *cursor_pos); | |||
123 | ||||
124 | static void reinitialize_ic (CtkIMContextXIM *context_xim); | |||
125 | static void set_ic_client_window (CtkIMContextXIM *context_xim, | |||
126 | CdkWindow *client_window); | |||
127 | ||||
128 | static void setup_styles (CtkXIMInfo *info); | |||
129 | ||||
130 | static void update_client_widget (CtkIMContextXIM *context_xim); | |||
131 | static void update_status_window (CtkIMContextXIM *context_xim); | |||
132 | ||||
133 | static StatusWindow *status_window_get (CtkWidget *toplevel); | |||
134 | static void status_window_free (StatusWindow *status_window); | |||
135 | static void status_window_set_text (StatusWindow *status_window, | |||
136 | const gchar *text); | |||
137 | ||||
138 | static void xim_destroy_callback (XIM xim, | |||
139 | XPointer client_data, | |||
140 | XPointer call_data); | |||
141 | ||||
142 | static XIC ctk_im_context_xim_get_ic (CtkIMContextXIM *context_xim); | |||
143 | static void xim_info_display_closed (CdkDisplay *display, | |||
144 | gboolean is_error, | |||
145 | CtkXIMInfo *info); | |||
146 | ||||
147 | static GObjectClass *parent_class; | |||
148 | ||||
149 | GType ctk_type_im_context_xim = 0; | |||
150 | ||||
151 | static GSList *open_ims = NULL((void*)0); | |||
152 | ||||
153 | /* List of status windows for different toplevels */ | |||
154 | static GSList *status_windows = NULL((void*)0); | |||
155 | ||||
156 | void | |||
157 | ctk_im_context_xim_register_type (GTypeModule *type_module) | |||
158 | { | |||
159 | const GTypeInfo im_context_xim_info = | |||
160 | { | |||
161 | .class_size = sizeof (CtkIMContextXIMClass), | |||
162 | .base_init = (GBaseInitFunc) NULL((void*)0), | |||
163 | .base_finalize = (GBaseFinalizeFunc) NULL((void*)0), | |||
164 | .class_init = (GClassInitFunc) ctk_im_context_xim_class_init, | |||
165 | .instance_size = sizeof (CtkIMContextXIM), | |||
166 | .n_preallocs = 0, | |||
167 | .instance_init = (GInstanceInitFunc) ctk_im_context_xim_init, | |||
168 | }; | |||
169 | ||||
170 | ctk_type_im_context_xim = | |||
171 | g_type_module_register_type (type_module, | |||
172 | CTK_TYPE_IM_CONTEXT(ctk_im_context_get_type ()), | |||
173 | "CtkIMContextXIM", | |||
174 | &im_context_xim_info, 0); | |||
175 | } | |||
176 | ||||
177 | #define PREEDIT_MASK(0x0002L | 0x0004L | 0x0001L | 0x0008L | 0x0010L) (XIMPreeditCallbacks0x0002L | XIMPreeditPosition0x0004L | \ | |||
178 | XIMPreeditArea0x0001L | XIMPreeditNothing0x0008L | XIMPreeditNone0x0010L) | |||
179 | #define STATUS_MASK(0x0200L | 0x0100L | 0x0400L | 0x0800L) (XIMStatusCallbacks0x0200L | XIMStatusArea0x0100L | \ | |||
180 | XIMStatusNothing0x0400L | XIMStatusNone0x0800L) | |||
181 | #define ALLOWED_MASK(0x0002L | 0x0008L | 0x0010L | 0x0200L | 0x0400L | 0x0800L) (XIMPreeditCallbacks0x0002L | XIMPreeditNothing0x0008L | XIMPreeditNone0x0010L | \ | |||
182 | XIMStatusCallbacks0x0200L | XIMStatusNothing0x0400L | XIMStatusNone0x0800L) | |||
183 | ||||
184 | static XIMStyle | |||
185 | choose_better_style (XIMStyle style1, XIMStyle style2) | |||
186 | { | |||
187 | XIMStyle s1, s2, u; | |||
188 | ||||
189 | if (style1 == 0) return style2; | |||
190 | if (style2 == 0) return style1; | |||
191 | if ((style1 & (PREEDIT_MASK(0x0002L | 0x0004L | 0x0001L | 0x0008L | 0x0010L) | STATUS_MASK(0x0200L | 0x0100L | 0x0400L | 0x0800L))) | |||
192 | == (style2 & (PREEDIT_MASK(0x0002L | 0x0004L | 0x0001L | 0x0008L | 0x0010L) | STATUS_MASK(0x0200L | 0x0100L | 0x0400L | 0x0800L)))) | |||
193 | return style1; | |||
194 | ||||
195 | s1 = style1 & PREEDIT_MASK(0x0002L | 0x0004L | 0x0001L | 0x0008L | 0x0010L); | |||
196 | s2 = style2 & PREEDIT_MASK(0x0002L | 0x0004L | 0x0001L | 0x0008L | 0x0010L); | |||
197 | u = s1 | s2; | |||
198 | if (s1 != s2) { | |||
199 | if (u & XIMPreeditCallbacks0x0002L) | |||
200 | return (s1 == XIMPreeditCallbacks0x0002L) ? style1 : style2; | |||
201 | else if (u & XIMPreeditPosition0x0004L) | |||
202 | return (s1 == XIMPreeditPosition0x0004L) ? style1 :style2; | |||
203 | else if (u & XIMPreeditArea0x0001L) | |||
204 | return (s1 == XIMPreeditArea0x0001L) ? style1 : style2; | |||
205 | else if (u & XIMPreeditNothing0x0008L) | |||
206 | return (s1 == XIMPreeditNothing0x0008L) ? style1 : style2; | |||
207 | else if (u & XIMPreeditNone0x0010L) | |||
208 | return (s1 == XIMPreeditNone0x0010L) ? style1 : style2; | |||
209 | } else { | |||
210 | s1 = style1 & STATUS_MASK(0x0200L | 0x0100L | 0x0400L | 0x0800L); | |||
211 | s2 = style2 & STATUS_MASK(0x0200L | 0x0100L | 0x0400L | 0x0800L); | |||
212 | u = s1 | s2; | |||
213 | if (u & XIMStatusCallbacks0x0200L) | |||
214 | return (s1 == XIMStatusCallbacks0x0200L) ? style1 : style2; | |||
215 | else if (u & XIMStatusArea0x0100L) | |||
216 | return (s1 == XIMStatusArea0x0100L) ? style1 : style2; | |||
217 | else if (u & XIMStatusNothing0x0400L) | |||
218 | return (s1 == XIMStatusNothing0x0400L) ? style1 : style2; | |||
219 | else if (u & XIMStatusNone0x0800L) | |||
220 | return (s1 == XIMStatusNone0x0800L) ? style1 : style2; | |||
221 | } | |||
222 | return 0; /* Get rid of stupid warning */ | |||
223 | } | |||
224 | ||||
225 | static void | |||
226 | reinitialize_all_ics (CtkXIMInfo *info) | |||
227 | { | |||
228 | GSList *tmp_list; | |||
229 | ||||
230 | for (tmp_list = info->ics; tmp_list; tmp_list = tmp_list->next) | |||
231 | reinitialize_ic (tmp_list->data); | |||
232 | } | |||
233 | ||||
234 | static void | |||
235 | setup_styles (CtkXIMInfo *info) | |||
236 | { | |||
237 | unsigned long settings_preference; | |||
238 | XIMStyles *xim_styles = info->xim_styles; | |||
239 | ||||
240 | settings_preference = info->status_style_setting|info->preedit_style_setting; | |||
241 | info->style = 0; | |||
242 | if (xim_styles) | |||
243 | { | |||
244 | int i; | |||
245 | ||||
246 | for (i = 0; i < xim_styles->count_styles; i++) | |||
247 | if ((xim_styles->supported_styles[i] & ALLOWED_MASK(0x0002L | 0x0008L | 0x0010L | 0x0200L | 0x0400L | 0x0800L)) == xim_styles->supported_styles[i]) | |||
248 | { | |||
249 | if (settings_preference == xim_styles->supported_styles[i]) | |||
250 | { | |||
251 | info->style = settings_preference; | |||
252 | break; | |||
253 | } | |||
254 | info->style = choose_better_style (info->style, | |||
255 | xim_styles->supported_styles[i]); | |||
256 | } | |||
257 | } | |||
258 | if (info->style == 0) | |||
259 | info->style = XIMPreeditNothing0x0008L | XIMStatusNothing0x0400L; | |||
260 | } | |||
261 | ||||
262 | static void | |||
263 | setup_im (CtkXIMInfo *info) | |||
264 | { | |||
265 | XIMValuesList *ic_values = NULL((void*)0); | |||
266 | XIMCallback im_destroy_callback; | |||
267 | CdkDisplay *display; | |||
268 | ||||
269 | if (info->im == NULL((void*)0)) | |||
270 | return; | |||
271 | ||||
272 | im_destroy_callback.client_data = (XPointer)info; | |||
273 | im_destroy_callback.callback = (XIMProc)xim_destroy_callback; | |||
274 | XSetIMValues (info->im, | |||
275 | XNDestroyCallback"destroyCallback", &im_destroy_callback, | |||
276 | NULL((void*)0)); | |||
277 | ||||
278 | XGetIMValues (info->im, | |||
279 | XNQueryInputStyle"queryInputStyle", &info->xim_styles, | |||
280 | XNQueryICValuesList"queryICValuesList", &ic_values, | |||
281 | NULL((void*)0)); | |||
282 | ||||
283 | info->supports_string_conversion = FALSE(0); | |||
284 | if (ic_values) | |||
285 | { | |||
286 | int i; | |||
287 | ||||
288 | for (i = 0; i < ic_values->count_values; i++) | |||
289 | if (strcmp (ic_values->supported_values[i], | |||
290 | XNStringConversionCallback"stringConversionCallback") == 0) | |||
291 | { | |||
292 | info->supports_string_conversion = TRUE(!(0)); | |||
293 | break; | |||
294 | } | |||
295 | ||||
296 | #if 0 | |||
297 | for (i = 0; i < ic_values->count_values; i++) | |||
298 | g_print ("%s\n", ic_values->supported_values[i]); | |||
299 | for (i = 0; i < xim_styles->count_styles; i++) | |||
300 | g_print ("%#x\n", xim_styles->supported_styles[i]); | |||
301 | #endif | |||
302 | ||||
303 | XFree (ic_values); | |||
304 | } | |||
305 | ||||
306 | info->status_style_setting = XIMStatusCallbacks0x0200L; | |||
307 | info->preedit_style_setting = XIMPreeditCallbacks0x0002L; | |||
308 | setup_styles (info); | |||
309 | reinitialize_all_ics (info); | |||
310 | ||||
311 | display = cdk_screen_get_display (info->screen); | |||
312 | info->display_closed_cb = g_signal_connect (display, "closed",g_signal_connect_data ((display), ("closed"), (((GCallback) ( xim_info_display_closed))), (info), ((void*)0), (GConnectFlags ) 0) | |||
313 | G_CALLBACK (xim_info_display_closed), info)g_signal_connect_data ((display), ("closed"), (((GCallback) ( xim_info_display_closed))), (info), ((void*)0), (GConnectFlags ) 0); | |||
314 | } | |||
315 | ||||
316 | static void | |||
317 | xim_info_display_closed (CdkDisplay *display, | |||
318 | gboolean is_error G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
319 | CtkXIMInfo *info) | |||
320 | { | |||
321 | GSList *ics, *tmp_list; | |||
322 | ||||
323 | open_ims = g_slist_remove (open_ims, info); | |||
324 | ||||
325 | ics = info->ics; | |||
326 | info->ics = NULL((void*)0); | |||
327 | ||||
328 | for (tmp_list = ics; tmp_list; tmp_list = tmp_list->next) | |||
329 | set_ic_client_window (tmp_list->data, NULL((void*)0)); | |||
330 | ||||
331 | g_slist_free (ics); | |||
332 | ||||
333 | if (info->status_set) | |||
334 | g_signal_handler_disconnect (info->settings, info->status_set); | |||
335 | if (info->preedit_set) | |||
336 | g_signal_handler_disconnect (info->settings, info->preedit_set); | |||
337 | if (info->display_closed_cb) | |||
338 | g_signal_handler_disconnect (display, info->display_closed_cb); | |||
339 | ||||
340 | if (info->xim_styles) | |||
341 | XFree (info->xim_styles); | |||
342 | g_free (info->locale); | |||
343 | ||||
344 | if (info->im) | |||
345 | XCloseIM (info->im); | |||
346 | ||||
347 | g_free (info); | |||
348 | } | |||
349 | ||||
350 | static void | |||
351 | xim_instantiate_callback (Display *display, XPointer client_data, | |||
352 | XPointer call_data G_GNUC_UNUSED__attribute__ ((__unused__))) | |||
353 | { | |||
354 | CtkXIMInfo *info = (CtkXIMInfo*)client_data; | |||
355 | XIM im = NULL((void*)0); | |||
356 | ||||
357 | im = XOpenIM (display, NULL((void*)0), NULL((void*)0), NULL((void*)0)); | |||
358 | ||||
359 | if (!im) | |||
360 | return; | |||
361 | ||||
362 | info->im = im; | |||
363 | setup_im (info); | |||
364 | ||||
365 | XUnregisterIMInstantiateCallback (display, NULL((void*)0), NULL((void*)0), NULL((void*)0), | |||
366 | xim_instantiate_callback, | |||
367 | (XPointer)info); | |||
368 | info->reconnecting = FALSE(0); | |||
369 | } | |||
370 | ||||
371 | /* initialize info->im */ | |||
372 | static void | |||
373 | xim_info_try_im (CtkXIMInfo *info) | |||
374 | { | |||
375 | CdkScreen *screen = info->screen; | |||
376 | CdkDisplay *display = cdk_screen_get_display (screen); | |||
377 | ||||
378 | g_assert (info->im == NULL)do { if (info->im == ((void*)0)) ; else g_assertion_message_expr (((gchar*) 0), "ctkimcontextxim.c", 378, ((const char*) (__func__ )), "info->im == NULL"); } while (0); | |||
379 | if (info->reconnecting) | |||
380 | return; | |||
381 | ||||
382 | if (XSupportsLocale ()) | |||
383 | { | |||
384 | if (!XSetLocaleModifiers ("")) | |||
385 | g_warning ("Unable to set locale modifiers with XSetLocaleModifiers()"); | |||
386 | info->im = XOpenIM (CDK_DISPLAY_XDISPLAY (display)(cdk_x11_display_get_xdisplay (display)), NULL((void*)0), NULL((void*)0), NULL((void*)0)); | |||
387 | if (!info->im) | |||
388 | { | |||
389 | XRegisterIMInstantiateCallback (CDK_DISPLAY_XDISPLAY(display)(cdk_x11_display_get_xdisplay (display)), | |||
390 | NULL((void*)0), NULL((void*)0), NULL((void*)0), | |||
391 | xim_instantiate_callback, | |||
392 | (XPointer)info); | |||
393 | info->reconnecting = TRUE(!(0)); | |||
394 | return; | |||
395 | } | |||
396 | setup_im (info); | |||
397 | } | |||
398 | } | |||
399 | ||||
400 | static void | |||
401 | xim_destroy_callback (XIM xim G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
402 | XPointer client_data, | |||
403 | XPointer call_data G_GNUC_UNUSED__attribute__ ((__unused__))) | |||
404 | { | |||
405 | CtkXIMInfo *info = (CtkXIMInfo*)client_data; | |||
406 | ||||
407 | info->im = NULL((void*)0); | |||
408 | ||||
409 | g_signal_handler_disconnect (info->settings, info->status_set); | |||
410 | info->status_set = 0; | |||
411 | g_signal_handler_disconnect (info->settings, info->preedit_set); | |||
412 | info->preedit_set = 0; | |||
413 | ||||
414 | reinitialize_all_ics (info); | |||
415 | xim_info_try_im (info); | |||
416 | return; | |||
417 | } | |||
418 | ||||
419 | static CtkXIMInfo * | |||
420 | get_im (CdkWindow *client_window, | |||
421 | const char *locale) | |||
422 | { | |||
423 | GSList *tmp_list; | |||
424 | CtkXIMInfo *info; | |||
425 | CdkScreen *screen = cdk_window_get_screen (client_window); | |||
426 | ||||
427 | info = NULL((void*)0); | |||
428 | tmp_list = open_ims; | |||
429 | while (tmp_list) | |||
430 | { | |||
431 | CtkXIMInfo *tmp_info = tmp_list->data; | |||
432 | if (tmp_info->screen == screen && | |||
433 | strcmp (tmp_info->locale, locale) == 0) | |||
434 | { | |||
435 | if (tmp_info->im) | |||
436 | { | |||
437 | return tmp_info; | |||
438 | } | |||
439 | else | |||
440 | { | |||
441 | tmp_info = tmp_info; | |||
442 | break; | |||
443 | } | |||
444 | } | |||
445 | tmp_list = tmp_list->next; | |||
446 | } | |||
447 | ||||
448 | if (info == NULL((void*)0)) | |||
449 | { | |||
450 | info = g_new (CtkXIMInfo, 1)((CtkXIMInfo *) g_malloc_n ((1), sizeof (CtkXIMInfo))); | |||
451 | open_ims = g_slist_prepend (open_ims, info); | |||
452 | ||||
453 | info->screen = screen; | |||
454 | info->locale = g_strdup (locale)g_strdup_inline (locale); | |||
455 | info->xim_styles = NULL((void*)0); | |||
456 | info->preedit_style_setting = 0; | |||
457 | info->status_style_setting = 0; | |||
458 | info->settings = NULL((void*)0); | |||
459 | info->preedit_set = 0; | |||
460 | info->status_set = 0; | |||
461 | info->display_closed_cb = 0; | |||
462 | info->ics = NULL((void*)0); | |||
463 | info->reconnecting = FALSE(0); | |||
464 | info->im = NULL((void*)0); | |||
465 | } | |||
466 | ||||
467 | xim_info_try_im (info); | |||
468 | return info; | |||
469 | } | |||
470 | ||||
471 | static void | |||
472 | ctk_im_context_xim_class_init (CtkIMContextXIMClass *class) | |||
473 | { | |||
474 | CtkIMContextClass *im_context_class = CTK_IM_CONTEXT_CLASS (class)((((CtkIMContextClass*) (void *) g_type_check_class_cast ((GTypeClass *) ((class)), ((ctk_im_context_get_type ())))))); | |||
475 | GObjectClass *gobject_class = G_OBJECT_CLASS (class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass *) ((class)), (((GType) ((20) << (2)))))))); | |||
476 | ||||
477 | parent_class = g_type_class_peek_parent (class); | |||
478 | ||||
479 | im_context_class->set_client_window = ctk_im_context_xim_set_client_window; | |||
480 | im_context_class->filter_keypress = ctk_im_context_xim_filter_keypress; | |||
481 | im_context_class->reset = ctk_im_context_xim_reset; | |||
482 | im_context_class->get_preedit_string = ctk_im_context_xim_get_preedit_string; | |||
483 | im_context_class->focus_in = ctk_im_context_xim_focus_in; | |||
484 | im_context_class->focus_out = ctk_im_context_xim_focus_out; | |||
485 | im_context_class->set_cursor_location = ctk_im_context_xim_set_cursor_location; | |||
486 | im_context_class->set_use_preedit = ctk_im_context_xim_set_use_preedit; | |||
487 | gobject_class->finalize = ctk_im_context_xim_finalize; | |||
488 | } | |||
489 | ||||
490 | static void | |||
491 | ctk_im_context_xim_init (CtkIMContextXIM *im_context_xim) | |||
492 | { | |||
493 | im_context_xim->use_preedit = TRUE(!(0)); | |||
494 | im_context_xim->filter_key_release = FALSE(0); | |||
495 | im_context_xim->finalizing = FALSE(0); | |||
496 | im_context_xim->has_focus = FALSE(0); | |||
497 | im_context_xim->in_toplevel = FALSE(0); | |||
498 | } | |||
499 | ||||
500 | static void | |||
501 | ctk_im_context_xim_finalize (GObject *obj) | |||
502 | { | |||
503 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (obj)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((obj)), ((ctk_type_im_context_xim)))))); | |||
504 | ||||
505 | context_xim->finalizing = TRUE(!(0)); | |||
506 | ||||
507 | if (context_xim->im_info && !context_xim->im_info->ics->next) | |||
508 | { | |||
509 | if (context_xim->im_info->reconnecting) | |||
510 | { | |||
511 | CdkDisplay *display; | |||
512 | ||||
513 | display = cdk_screen_get_display (context_xim->im_info->screen); | |||
514 | XUnregisterIMInstantiateCallback (CDK_DISPLAY_XDISPLAY (display)(cdk_x11_display_get_xdisplay (display)), | |||
515 | NULL((void*)0), NULL((void*)0), NULL((void*)0), | |||
516 | xim_instantiate_callback, | |||
517 | (XPointer)context_xim->im_info); | |||
518 | } | |||
519 | else if (context_xim->im_info->im) | |||
520 | { | |||
521 | XIMCallback im_destroy_callback; | |||
522 | ||||
523 | im_destroy_callback.client_data = NULL((void*)0); | |||
524 | im_destroy_callback.callback = NULL((void*)0); | |||
525 | XSetIMValues (context_xim->im_info->im, | |||
526 | XNDestroyCallback"destroyCallback", &im_destroy_callback, | |||
527 | NULL((void*)0)); | |||
528 | } | |||
529 | } | |||
530 | ||||
531 | set_ic_client_window (context_xim, NULL((void*)0)); | |||
532 | ||||
533 | g_free (context_xim->locale); | |||
534 | g_free (context_xim->mb_charset); | |||
535 | ||||
536 | G_OBJECT_CLASS (parent_class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass *) ((parent_class)), (((GType) ((20) << (2))))))))->finalize (obj); | |||
537 | } | |||
538 | ||||
539 | static void | |||
540 | reinitialize_ic (CtkIMContextXIM *context_xim) | |||
541 | { | |||
542 | if (context_xim->ic) | |||
543 | { | |||
544 | XDestroyIC (context_xim->ic); | |||
545 | context_xim->ic = NULL((void*)0); | |||
546 | update_status_window (context_xim); | |||
547 | ||||
548 | if (context_xim->preedit_length) | |||
549 | { | |||
550 | context_xim->preedit_length = 0; | |||
551 | if (!context_xim->finalizing) | |||
552 | g_signal_emit_by_name (context_xim, "preedit-changed"); | |||
553 | } | |||
554 | } | |||
555 | /* | |||
556 | reset filter_key_release flag, otherwise keystrokes will be doubled | |||
557 | until reconnecting to XIM. | |||
558 | */ | |||
559 | context_xim->filter_key_release = FALSE(0); | |||
560 | } | |||
561 | ||||
562 | static void | |||
563 | set_ic_client_window (CtkIMContextXIM *context_xim, | |||
564 | CdkWindow *client_window) | |||
565 | { | |||
566 | reinitialize_ic (context_xim); | |||
567 | if (context_xim->client_window) | |||
568 | { | |||
569 | context_xim->im_info->ics = g_slist_remove (context_xim->im_info->ics, context_xim); | |||
570 | context_xim->im_info = NULL((void*)0); | |||
571 | } | |||
572 | ||||
573 | context_xim->client_window = client_window; | |||
574 | ||||
575 | if (context_xim->client_window) | |||
576 | { | |||
577 | context_xim->im_info = get_im (context_xim->client_window, context_xim->locale); | |||
578 | context_xim->im_info->ics = g_slist_prepend (context_xim->im_info->ics, context_xim); | |||
579 | } | |||
580 | ||||
581 | update_client_widget (context_xim); | |||
582 | } | |||
583 | ||||
584 | static void | |||
585 | ctk_im_context_xim_set_client_window (CtkIMContext *context, | |||
586 | CdkWindow *client_window) | |||
587 | { | |||
588 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (context)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((context)), ((ctk_type_im_context_xim)))))); | |||
589 | ||||
590 | set_ic_client_window (context_xim, client_window); | |||
591 | } | |||
592 | ||||
593 | CtkIMContext * | |||
594 | ctk_im_context_xim_new (void) | |||
595 | { | |||
596 | CtkIMContextXIM *result; | |||
597 | const gchar *charset; | |||
598 | ||||
599 | if (!CDK_IS_X11_DISPLAY(cdk_display_get_default())(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) ( (cdk_display_get_default())); GType __t = ((cdk_x11_display_get_type ())); gboolean __r; if (!__inst) __r = (0); else if (__inst-> g_class && __inst->g_class->g_type == __t) __r = (!(0)); else __r = g_type_check_instance_is_a (__inst, __t); __r; }))))) | |||
600 | return NULL((void*)0); | |||
601 | result = g_object_new (CTK_TYPE_IM_CONTEXT_XIM(ctk_type_im_context_xim), NULL((void*)0)); | |||
602 | ||||
603 | result->locale = g_strdup (setlocale (LC_CTYPE, NULL))g_strdup_inline (setlocale (0, ((void*)0))); | |||
604 | ||||
605 | g_get_charset (&charset); | |||
606 | result->mb_charset = g_strdup (charset)g_strdup_inline (charset); | |||
607 | ||||
608 | return CTK_IM_CONTEXT (result)((((CtkIMContext*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((result)), ((ctk_im_context_get_type ())))))); | |||
609 | } | |||
610 | ||||
611 | static char * | |||
612 | mb_to_utf8 (CtkIMContextXIM *context_xim, | |||
613 | const char *str) | |||
614 | { | |||
615 | GError *error = NULL((void*)0); | |||
616 | gchar *result; | |||
617 | ||||
618 | if (strcmp (context_xim->mb_charset, "UTF-8") == 0) | |||
619 | result = g_strdup (str)g_strdup_inline (str); | |||
620 | else | |||
621 | { | |||
622 | result = g_convert (str, -1, | |||
623 | "UTF-8", context_xim->mb_charset, | |||
624 | NULL((void*)0), NULL((void*)0), &error); | |||
625 | if (!result) | |||
626 | { | |||
627 | g_warning ("Error converting text from IM to UTF-8: %s\n", error->message); | |||
628 | g_error_free (error); | |||
629 | } | |||
630 | } | |||
631 | ||||
632 | return result; | |||
633 | } | |||
634 | ||||
635 | static gboolean | |||
636 | ctk_im_context_xim_filter_keypress (CtkIMContext *context, | |||
637 | CdkEventKey *event) | |||
638 | { | |||
639 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (context)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((context)), ((ctk_type_im_context_xim)))))); | |||
640 | XIC ic = ctk_im_context_xim_get_ic (context_xim); | |||
641 | gchar static_buffer[256]; | |||
642 | gchar *buffer = static_buffer; | |||
643 | gint buffer_size = sizeof(static_buffer) - 1; | |||
644 | gint num_bytes = 0; | |||
645 | KeySym keysym; | |||
646 | Statusint status; | |||
647 | gboolean result = FALSE(0); | |||
648 | CdkWindow *root_window; | |||
649 | CdkWindow *window; | |||
650 | XKeyPressedEvent xevent; | |||
651 | ||||
652 | if (context_xim->client_window == NULL((void*)0)) | |||
653 | return FALSE(0); | |||
654 | ||||
655 | if (event->type == CDK_KEY_RELEASE && !context_xim->filter_key_release) | |||
656 | return FALSE(0); | |||
657 | ||||
658 | root_window = cdk_screen_get_root_window (cdk_window_get_screen (event->window)); | |||
659 | window = cdk_window_get_toplevel (event->window); | |||
660 | ||||
661 | xevent.type = (event->type == CDK_KEY_PRESS) ? KeyPress2 : KeyRelease3; | |||
662 | xevent.serial = 0; /* hope it doesn't matter */ | |||
663 | xevent.send_event = event->send_event; | |||
664 | xevent.display = CDK_WINDOW_XDISPLAY (window)((cdk_x11_display_get_xdisplay (cdk_window_get_display (window )))); | |||
665 | xevent.window = CDK_WINDOW_XID (window)(cdk_x11_window_get_xid (window)); | |||
666 | xevent.root = CDK_WINDOW_XID (root_window)(cdk_x11_window_get_xid (root_window)); | |||
667 | xevent.subwindow = xevent.window; | |||
668 | xevent.time = event->time; | |||
669 | xevent.x = xevent.x_root = 0; | |||
670 | xevent.y = xevent.y_root = 0; | |||
671 | xevent.state = event->state; | |||
672 | xevent.keycode = event->hardware_keycode; | |||
673 | xevent.same_screen = True1; | |||
674 | ||||
675 | if (XFilterEvent ((XEvent *)&xevent, CDK_WINDOW_XID (context_xim->client_window)(cdk_x11_window_get_xid (context_xim->client_window)))) | |||
676 | return TRUE(!(0)); | |||
677 | ||||
678 | if (event->state & | |||
679 | (ctk_accelerator_get_default_mod_mask () & ~(CDK_SHIFT_MASK | CDK_CONTROL_MASK))) | |||
680 | return FALSE(0); | |||
681 | ||||
682 | again: | |||
683 | if (ic) | |||
684 | num_bytes = XmbLookupString (ic, &xevent, buffer, buffer_size, &keysym, &status); | |||
685 | else | |||
686 | { | |||
687 | num_bytes = XLookupString (&xevent, buffer, buffer_size, &keysym, NULL((void*)0)); | |||
688 | status = XLookupBoth4; | |||
689 | } | |||
690 | ||||
691 | if (status == XBufferOverflow-1) | |||
692 | { | |||
693 | buffer_size = num_bytes; | |||
694 | if (buffer != static_buffer) | |||
695 | g_free (buffer); | |||
696 | buffer = g_malloc (num_bytes + 1); | |||
697 | goto again; | |||
698 | } | |||
699 | ||||
700 | /* I don't know how we should properly handle XLookupKeysym or XLookupBoth | |||
701 | * here ... do input methods actually change the keysym? we can't really | |||
702 | * feed it back to accelerator processing at this point... | |||
703 | */ | |||
704 | if (status == XLookupChars2 || status == XLookupBoth4) | |||
705 | { | |||
706 | char *result_utf8; | |||
707 | ||||
708 | buffer[num_bytes] = '\0'; | |||
709 | ||||
710 | result_utf8 = mb_to_utf8 (context_xim, buffer); | |||
711 | if (result_utf8) | |||
712 | { | |||
713 | if ((guchar)result_utf8[0] >= 0x20 && | |||
714 | result_utf8[0] != 0x7f) /* Some IM have a nasty habit of converting | |||
715 | * control characters into strings | |||
716 | */ | |||
717 | { | |||
718 | g_signal_emit_by_name (context, "commit", result_utf8); | |||
719 | result = TRUE(!(0)); | |||
720 | } | |||
721 | ||||
722 | g_free (result_utf8); | |||
723 | } | |||
724 | } | |||
725 | ||||
726 | if (buffer != static_buffer) | |||
727 | g_free (buffer); | |||
728 | ||||
729 | return result; | |||
730 | } | |||
731 | ||||
732 | static void | |||
733 | ctk_im_context_xim_focus_in (CtkIMContext *context) | |||
734 | { | |||
735 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (context)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((context)), ((ctk_type_im_context_xim)))))); | |||
736 | ||||
737 | if (!context_xim->has_focus) | |||
738 | { | |||
739 | XIC ic = ctk_im_context_xim_get_ic (context_xim); | |||
740 | ||||
741 | context_xim->has_focus = TRUE(!(0)); | |||
742 | update_status_window (context_xim); | |||
743 | ||||
744 | if (ic) | |||
745 | XSetICFocus (ic); | |||
746 | } | |||
747 | ||||
748 | return; | |||
749 | } | |||
750 | ||||
751 | static void | |||
752 | ctk_im_context_xim_focus_out (CtkIMContext *context) | |||
753 | { | |||
754 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (context)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((context)), ((ctk_type_im_context_xim)))))); | |||
755 | ||||
756 | if (context_xim->has_focus) | |||
757 | { | |||
758 | XIC ic = ctk_im_context_xim_get_ic (context_xim); | |||
759 | ||||
760 | context_xim->has_focus = FALSE(0); | |||
761 | update_status_window (context_xim); | |||
762 | ||||
763 | if (ic) | |||
764 | XUnsetICFocus (ic); | |||
765 | } | |||
766 | ||||
767 | return; | |||
768 | } | |||
769 | ||||
770 | static void | |||
771 | ctk_im_context_xim_set_cursor_location (CtkIMContext *context, | |||
772 | CdkRectangle *area) | |||
773 | { | |||
774 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (context)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((context)), ((ctk_type_im_context_xim)))))); | |||
775 | XIC ic = ctk_im_context_xim_get_ic (context_xim); | |||
776 | ||||
777 | XVaNestedList preedit_attr; | |||
778 | XPoint spot; | |||
779 | ||||
780 | if (!ic) | |||
781 | return; | |||
782 | ||||
783 | spot.x = area->x; | |||
784 | spot.y = area->y + area->height; | |||
785 | ||||
786 | preedit_attr = XVaCreateNestedList (0, | |||
787 | XNSpotLocation"spotLocation", &spot, | |||
788 | NULL((void*)0)); | |||
789 | XSetICValues (ic, | |||
790 | XNPreeditAttributes"preeditAttributes", preedit_attr, | |||
791 | NULL((void*)0)); | |||
792 | XFree(preedit_attr); | |||
793 | ||||
794 | return; | |||
795 | } | |||
796 | ||||
797 | static void | |||
798 | ctk_im_context_xim_set_use_preedit (CtkIMContext *context, | |||
799 | gboolean use_preedit) | |||
800 | { | |||
801 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (context)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((context)), ((ctk_type_im_context_xim)))))); | |||
802 | ||||
803 | use_preedit = use_preedit != FALSE(0); | |||
804 | ||||
805 | if (context_xim->use_preedit != use_preedit) | |||
806 | { | |||
807 | context_xim->use_preedit = use_preedit; | |||
808 | reinitialize_ic (context_xim); | |||
809 | } | |||
810 | ||||
811 | return; | |||
812 | } | |||
813 | ||||
814 | static void | |||
815 | ctk_im_context_xim_reset (CtkIMContext *context) | |||
816 | { | |||
817 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (context)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((context)), ((ctk_type_im_context_xim)))))); | |||
818 | XIC ic = ctk_im_context_xim_get_ic (context_xim); | |||
819 | gchar *result; | |||
820 | ||||
821 | /* restore conversion state after resetting ic later */ | |||
822 | XIMPreeditState preedit_state = XIMPreeditUnKnown0L; | |||
823 | XVaNestedList preedit_attr; | |||
824 | gboolean have_preedit_state = FALSE(0); | |||
825 | ||||
826 | if (!ic) | |||
827 | return; | |||
828 | ||||
829 | ||||
830 | if (context_xim->preedit_length == 0) | |||
831 | return; | |||
832 | ||||
833 | preedit_attr = XVaCreateNestedList(0, | |||
834 | XNPreeditState"preeditState", &preedit_state, | |||
835 | NULL((void*)0)); | |||
836 | if (!XGetICValues(ic, | |||
837 | XNPreeditAttributes"preeditAttributes", preedit_attr, | |||
838 | NULL((void*)0))) | |||
839 | have_preedit_state = TRUE(!(0)); | |||
840 | ||||
841 | XFree(preedit_attr); | |||
842 | ||||
843 | result = XmbResetIC (ic); | |||
844 | ||||
845 | preedit_attr = XVaCreateNestedList(0, | |||
846 | XNPreeditState"preeditState", preedit_state, | |||
847 | NULL((void*)0)); | |||
848 | if (have_preedit_state) | |||
849 | XSetICValues(ic, | |||
850 | XNPreeditAttributes"preeditAttributes", preedit_attr, | |||
851 | NULL((void*)0)); | |||
852 | ||||
853 | XFree(preedit_attr); | |||
854 | ||||
855 | if (result) | |||
856 | { | |||
857 | char *result_utf8 = mb_to_utf8 (context_xim, result); | |||
858 | if (result_utf8) | |||
859 | { | |||
860 | g_signal_emit_by_name (context, "commit", result_utf8); | |||
861 | g_free (result_utf8); | |||
862 | } | |||
863 | } | |||
864 | ||||
865 | if (context_xim->preedit_length) | |||
866 | { | |||
867 | context_xim->preedit_length = 0; | |||
868 | g_signal_emit_by_name (context, "preedit-changed"); | |||
869 | } | |||
870 | ||||
871 | XFree (result); | |||
872 | } | |||
873 | ||||
874 | /* Mask of feedback bits that we render | |||
875 | */ | |||
876 | #define FEEDBACK_MASK(1L | (1L<<1)) (XIMReverse1L | XIMUnderline(1L<<1)) | |||
877 | ||||
878 | static void | |||
879 | add_feedback_attr (PangoAttrList *attrs, | |||
880 | const gchar *str, | |||
881 | XIMFeedback feedback, | |||
882 | gint start_pos, | |||
883 | gint end_pos) | |||
884 | { | |||
885 | PangoAttribute *attr; | |||
886 | ||||
887 | gint start_index = g_utf8_offset_to_pointer (str, start_pos) - str; | |||
888 | gint end_index = g_utf8_offset_to_pointer (str, end_pos) - str; | |||
889 | ||||
890 | if (feedback & XIMUnderline(1L<<1)) | |||
891 | { | |||
892 | attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE); | |||
893 | attr->start_index = start_index; | |||
894 | attr->end_index = end_index; | |||
895 | ||||
896 | pango_attr_list_change (attrs, attr); | |||
897 | } | |||
898 | ||||
899 | if (feedback & XIMReverse1L) | |||
900 | { | |||
901 | attr = pango_attr_foreground_new (0xffff, 0xffff, 0xffff); | |||
902 | attr->start_index = start_index; | |||
903 | attr->end_index = end_index; | |||
904 | ||||
905 | pango_attr_list_change (attrs, attr); | |||
906 | ||||
907 | attr = pango_attr_background_new (0, 0, 0); | |||
908 | attr->start_index = start_index; | |||
909 | attr->end_index = end_index; | |||
910 | ||||
911 | pango_attr_list_change (attrs, attr); | |||
912 | } | |||
913 | ||||
914 | if (feedback & ~FEEDBACK_MASK(1L | (1L<<1))) | |||
915 | g_warning ("Unrendered feedback style: %#lx", feedback & ~FEEDBACK_MASK(1L | (1L<<1))); | |||
916 | } | |||
917 | ||||
918 | static void | |||
919 | ctk_im_context_xim_get_preedit_string (CtkIMContext *context, | |||
920 | gchar **str, | |||
921 | PangoAttrList **attrs, | |||
922 | gint *cursor_pos) | |||
923 | { | |||
924 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (context)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((context)), ((ctk_type_im_context_xim)))))); | |||
925 | gchar *utf8 = g_ucs4_to_utf8 (context_xim->preedit_chars, context_xim->preedit_length, NULL((void*)0), NULL((void*)0), NULL((void*)0)); | |||
926 | ||||
927 | if (attrs) | |||
928 | { | |||
929 | int i; | |||
930 | XIMFeedback last_feedback = 0; | |||
931 | gint start = -1; | |||
932 | ||||
933 | *attrs = pango_attr_list_new (); | |||
934 | ||||
935 | for (i = 0; i < context_xim->preedit_length; i++) | |||
936 | { | |||
937 | XIMFeedback new_feedback = context_xim->feedbacks[i] & FEEDBACK_MASK(1L | (1L<<1)); | |||
938 | if (new_feedback != last_feedback) | |||
939 | { | |||
940 | if (start >= 0) | |||
941 | add_feedback_attr (*attrs, utf8, last_feedback, start, i); | |||
942 | ||||
943 | last_feedback = new_feedback; | |||
944 | start = i; | |||
945 | } | |||
946 | } | |||
947 | ||||
948 | if (start >= 0) | |||
949 | add_feedback_attr (*attrs, utf8, last_feedback, start, i); | |||
950 | } | |||
951 | ||||
952 | if (str) | |||
953 | *str = utf8; | |||
954 | else | |||
955 | g_free (utf8); | |||
956 | ||||
957 | if (cursor_pos) | |||
958 | *cursor_pos = context_xim->preedit_cursor; | |||
959 | } | |||
960 | ||||
961 | static int | |||
962 | preedit_start_callback (XIC xic G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
963 | XPointer client_data, | |||
964 | XPointer call_data G_GNUC_UNUSED__attribute__ ((__unused__))) | |||
965 | { | |||
966 | CtkIMContext *context = CTK_IM_CONTEXT (client_data)((((CtkIMContext*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((client_data)), ((ctk_im_context_get_type ())))))); | |||
967 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (context)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((context)), ((ctk_type_im_context_xim)))))); | |||
968 | ||||
969 | if (!context_xim->finalizing) | |||
970 | g_signal_emit_by_name (context, "preedit-start"); | |||
971 | ||||
972 | return -1; /* No length limit */ | |||
973 | } | |||
974 | ||||
975 | static void | |||
976 | preedit_done_callback (XIC xic G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
977 | XPointer client_data, | |||
978 | XPointer call_data G_GNUC_UNUSED__attribute__ ((__unused__))) | |||
979 | { | |||
980 | CtkIMContext *context = CTK_IM_CONTEXT (client_data)((((CtkIMContext*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((client_data)), ((ctk_im_context_get_type ())))))); | |||
981 | CtkIMContextXIM *context_xim = CTK_IM_CONTEXT_XIM (context)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((context)), ((ctk_type_im_context_xim)))))); | |||
982 | ||||
983 | if (context_xim->preedit_length) | |||
984 | { | |||
985 | context_xim->preedit_length = 0; | |||
986 | if (!context_xim->finalizing) | |||
987 | g_signal_emit_by_name (context_xim, "preedit-changed"); | |||
988 | } | |||
989 | ||||
990 | if (!context_xim->finalizing) | |||
991 | g_signal_emit_by_name (context, "preedit-end"); | |||
992 | } | |||
993 | ||||
994 | static gint | |||
995 | xim_text_to_utf8 (CtkIMContextXIM *context, XIMText *xim_text, gchar **text) | |||
996 | { | |||
997 | gint text_length = 0; | |||
998 | GError *error = NULL((void*)0); | |||
999 | gchar *result = NULL((void*)0); | |||
1000 | ||||
1001 | if (xim_text && xim_text->string.multi_byte) | |||
1002 | { | |||
1003 | if (xim_text->encoding_is_wchar) | |||
1004 | { | |||
1005 | g_warning ("Wide character return from Xlib not currently supported"); | |||
1006 | *text = NULL((void*)0); | |||
1007 | return 0; | |||
1008 | } | |||
1009 | ||||
1010 | if (strcmp (context->mb_charset, "UTF-8") == 0) | |||
1011 | result = g_strdup (xim_text->string.multi_byte)g_strdup_inline (xim_text->string.multi_byte); | |||
1012 | else | |||
1013 | result = g_convert (xim_text->string.multi_byte, | |||
1014 | -1, | |||
1015 | "UTF-8", | |||
1016 | context->mb_charset, | |||
1017 | NULL((void*)0), NULL((void*)0), &error); | |||
1018 | ||||
1019 | if (result) | |||
1020 | { | |||
1021 | text_length = g_utf8_strlen (result, -1); | |||
1022 | ||||
1023 | if (text_length != xim_text->length) | |||
1024 | { | |||
1025 | g_warning ("Size mismatch when converting text from input method: supplied length = %d\n, result length = %d", xim_text->length, text_length); | |||
1026 | } | |||
1027 | } | |||
1028 | else | |||
1029 | { | |||
1030 | g_warning ("Error converting text from IM to UCS-4: %s", error->message); | |||
1031 | g_error_free (error); | |||
1032 | ||||
1033 | *text = NULL((void*)0); | |||
1034 | return 0; | |||
1035 | } | |||
1036 | ||||
1037 | *text = result; | |||
1038 | return text_length; | |||
1039 | } | |||
1040 | else | |||
1041 | { | |||
1042 | *text = NULL((void*)0); | |||
1043 | return 0; | |||
1044 | } | |||
1045 | } | |||
1046 | ||||
1047 | static void | |||
1048 | preedit_draw_callback (XIC xic G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
1049 | XPointer client_data, | |||
1050 | XIMPreeditDrawCallbackStruct *call_data) | |||
1051 | { | |||
1052 | CtkIMContextXIM *context = CTK_IM_CONTEXT_XIM (client_data)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((client_data)), ((ctk_type_im_context_xim)))))); | |||
1053 | ||||
1054 | XIMText *new_xim_text = call_data->text; | |||
1055 | gint new_text_length; | |||
1056 | gunichar *new_text = NULL((void*)0); | |||
1057 | gint i; | |||
1058 | gint diff; | |||
1059 | gint new_length; | |||
1060 | gchar *tmp; | |||
1061 | ||||
1062 | gint chg_first = CLAMP (call_data->chg_first, 0, context->preedit_length)(((call_data->chg_first) > (context->preedit_length) ) ? (context->preedit_length) : (((call_data->chg_first ) < (0)) ? (0) : (call_data->chg_first))); | |||
| ||||
1063 | gint chg_length = CLAMP (call_data->chg_length, 0, context->preedit_length - chg_first)(((call_data->chg_length) > (context->preedit_length - chg_first)) ? (context->preedit_length - chg_first) : ( ((call_data->chg_length) < (0)) ? (0) : (call_data-> chg_length))); | |||
1064 | ||||
1065 | context->preedit_cursor = call_data->caret; | |||
1066 | ||||
1067 | if (chg_first
| |||
1068 | g_warning ("Invalid change to preedit string, first=%d length=%d (orig length == %d)", | |||
1069 | call_data->chg_first, call_data->chg_length, context->preedit_length); | |||
1070 | ||||
1071 | new_text_length = xim_text_to_utf8 (context, new_xim_text, &tmp); | |||
1072 | if (tmp
| |||
1073 | { | |||
1074 | new_text = g_utf8_to_ucs4_fast (tmp, -1, NULL((void*)0)); | |||
1075 | g_free (tmp); | |||
1076 | } | |||
1077 | ||||
1078 | diff = new_text_length - chg_length; | |||
1079 | new_length = context->preedit_length + diff; | |||
1080 | ||||
1081 | if (new_length > context->preedit_size) | |||
1082 | { | |||
1083 | context->preedit_size = new_length; | |||
1084 | context->preedit_chars = g_renew (gunichar, context->preedit_chars, new_length)((gunichar *) g_realloc_n (context->preedit_chars, (new_length ), sizeof (gunichar))); | |||
1085 | context->feedbacks = g_renew (XIMFeedback, context->feedbacks, new_length)((XIMFeedback *) g_realloc_n (context->feedbacks, (new_length ), sizeof (XIMFeedback))); | |||
1086 | } | |||
1087 | ||||
1088 | if (diff < 0) | |||
1089 | { | |||
1090 | for (i = chg_first + chg_length ; i < context->preedit_length; i++) | |||
1091 | { | |||
1092 | context->preedit_chars[i + diff] = context->preedit_chars[i]; | |||
| ||||
1093 | context->feedbacks[i + diff] = context->feedbacks[i]; | |||
1094 | } | |||
1095 | } | |||
1096 | else | |||
1097 | { | |||
1098 | for (i = context->preedit_length - 1; i >= chg_first + chg_length ; i--) | |||
1099 | { | |||
1100 | context->preedit_chars[i + diff] = context->preedit_chars[i]; | |||
1101 | context->feedbacks[i + diff] = context->feedbacks[i]; | |||
1102 | } | |||
1103 | } | |||
1104 | ||||
1105 | for (i = 0; i < new_text_length; i++) | |||
1106 | { | |||
1107 | context->preedit_chars[chg_first + i] = new_text[i]; | |||
1108 | context->feedbacks[chg_first + i] = new_xim_text->feedback[i]; | |||
1109 | } | |||
1110 | ||||
1111 | context->preedit_length += diff; | |||
1112 | ||||
1113 | g_free (new_text); | |||
1114 | ||||
1115 | if (!context->finalizing) | |||
1116 | g_signal_emit_by_name (context, "preedit-changed"); | |||
1117 | } | |||
1118 | ||||
1119 | ||||
1120 | static void | |||
1121 | preedit_caret_callback (XIC xic G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
1122 | XPointer client_data, | |||
1123 | XIMPreeditCaretCallbackStruct *call_data) | |||
1124 | { | |||
1125 | CtkIMContextXIM *context = CTK_IM_CONTEXT_XIM (client_data)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((client_data)), ((ctk_type_im_context_xim)))))); | |||
1126 | ||||
1127 | if (call_data->direction == XIMAbsolutePosition) | |||
1128 | { | |||
1129 | context->preedit_cursor = call_data->position; | |||
1130 | if (!context->finalizing) | |||
1131 | g_signal_emit_by_name (context, "preedit-changed"); | |||
1132 | } | |||
1133 | else | |||
1134 | { | |||
1135 | g_warning ("Caret movement command: %d %d %d not supported", | |||
1136 | call_data->position, call_data->direction, call_data->style); | |||
1137 | } | |||
1138 | } | |||
1139 | ||||
1140 | static void | |||
1141 | status_start_callback (XIC xic G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
1142 | XPointer client_data G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
1143 | XPointer call_data G_GNUC_UNUSED__attribute__ ((__unused__))) | |||
1144 | { | |||
1145 | return; | |||
1146 | } | |||
1147 | ||||
1148 | static void | |||
1149 | status_done_callback (XIC xic G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
1150 | XPointer client_data G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
1151 | XPointer call_data G_GNUC_UNUSED__attribute__ ((__unused__))) | |||
1152 | { | |||
1153 | return; | |||
1154 | } | |||
1155 | ||||
1156 | static void | |||
1157 | status_draw_callback (XIC xic G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
1158 | XPointer client_data, | |||
1159 | XIMStatusDrawCallbackStruct *call_data) | |||
1160 | { | |||
1161 | CtkIMContextXIM *context = CTK_IM_CONTEXT_XIM (client_data)((((CtkIMContextXIM*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((client_data)), ((ctk_type_im_context_xim)))))); | |||
1162 | ||||
1163 | if (call_data->type == XIMTextType) | |||
1164 | { | |||
1165 | gchar *text; | |||
1166 | xim_text_to_utf8 (context, call_data->data.text, &text); | |||
1167 | ||||
1168 | if (context->status_window) | |||
1169 | status_window_set_text (context->status_window, text ? text : ""); | |||
1170 | } | |||
1171 | else /* bitmap */ | |||
1172 | { | |||
1173 | g_print ("Status drawn with bitmap - id = %#lx\n", call_data->data.bitmap); | |||
1174 | } | |||
1175 | } | |||
1176 | ||||
1177 | static void | |||
1178 | string_conversion_callback (XIC xic G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
1179 | XPointer client_data, | |||
1180 | XPointer call_data) | |||
1181 | { | |||
1182 | CtkIMContextXIM *context_xim; | |||
1183 | XIMStringConversionCallbackStruct *conv_data; | |||
1184 | gchar *surrounding; | |||
1185 | gint cursor_index; | |||
1186 | ||||
1187 | context_xim = (CtkIMContextXIM *)client_data; | |||
1188 | conv_data = (XIMStringConversionCallbackStruct *)call_data; | |||
1189 | ||||
1190 | if (ctk_im_context_get_surrounding ((CtkIMContext *)context_xim, | |||
1191 | &surrounding, &cursor_index)) | |||
1192 | { | |||
1193 | gchar *text = NULL((void*)0); | |||
1194 | gsize text_len = 0; | |||
1195 | gint subst_offset = 0, subst_nchars = 0; | |||
1196 | gint i; | |||
1197 | gchar *p = surrounding + cursor_index, *q; | |||
1198 | gshort position = (gshort)conv_data->position; | |||
1199 | ||||
1200 | if (position > 0) | |||
1201 | { | |||
1202 | for (i = position; i > 0 && *p; --i) | |||
1203 | p = g_utf8_next_char (p)((p) + g_utf8_skip[*(const guchar *)(p)]); | |||
1204 | if (i > 0) | |||
1205 | return; | |||
1206 | } | |||
1207 | /* According to X11R6.4 Xlib - C Library Reference Manual | |||
1208 | * section 13.5.7.3 String Conversion Callback, | |||
1209 | * XIMStringConversionPosition is starting position _relative_ | |||
1210 | * to current client's cursor position. So it should be able | |||
1211 | * to be negative, or referring to a position before the cursor | |||
1212 | * would be impossible. But current X protocol defines this as | |||
1213 | * unsigned short. So, compiler may warn about the value range | |||
1214 | * here. We hope the X protocol is fixed soon. | |||
1215 | */ | |||
1216 | else if (position < 0) | |||
1217 | { | |||
1218 | for (i = position; i < 0 && p > surrounding; ++i) | |||
1219 | p = g_utf8_prev_char (p); | |||
1220 | if (i < 0) | |||
1221 | return; | |||
1222 | } | |||
1223 | ||||
1224 | switch (conv_data->direction) | |||
1225 | { | |||
1226 | case XIMForwardChar: | |||
1227 | for (i = conv_data->factor, q = p; i > 0 && *q; --i) | |||
1228 | q = g_utf8_next_char (q)((q) + g_utf8_skip[*(const guchar *)(q)]); | |||
1229 | if (i > 0) | |||
1230 | break; | |||
1231 | text = g_locale_from_utf8 (p, q - p, NULL((void*)0), &text_len, NULL((void*)0)); | |||
1232 | subst_offset = position; | |||
1233 | subst_nchars = conv_data->factor; | |||
1234 | break; | |||
1235 | ||||
1236 | case XIMBackwardChar: | |||
1237 | for (i = conv_data->factor, q = p; i > 0 && q > surrounding; --i) | |||
1238 | q = g_utf8_prev_char (q); | |||
1239 | if (i > 0) | |||
1240 | break; | |||
1241 | text = g_locale_from_utf8 (q, p - q, NULL((void*)0), &text_len, NULL((void*)0)); | |||
1242 | subst_offset = position - conv_data->factor; | |||
1243 | subst_nchars = conv_data->factor; | |||
1244 | break; | |||
1245 | ||||
1246 | case XIMForwardWord: | |||
1247 | case XIMBackwardWord: | |||
1248 | case XIMCaretUp: | |||
1249 | case XIMCaretDown: | |||
1250 | case XIMNextLine: | |||
1251 | case XIMPreviousLine: | |||
1252 | case XIMLineStart: | |||
1253 | case XIMLineEnd: | |||
1254 | case XIMAbsolutePosition: | |||
1255 | case XIMDontChange: | |||
1256 | default: | |||
1257 | break; | |||
1258 | } | |||
1259 | /* block out any failure happenning to "text", including conversion */ | |||
1260 | if (text) | |||
1261 | { | |||
1262 | conv_data->text = (XIMStringConversionText *) | |||
1263 | malloc (sizeof (XIMStringConversionText)); | |||
1264 | if (conv_data->text) | |||
1265 | { | |||
1266 | conv_data->text->length = text_len; | |||
1267 | conv_data->text->feedback = NULL((void*)0); | |||
1268 | conv_data->text->encoding_is_wchar = False0; | |||
1269 | conv_data->text->string.mbs = (char *)malloc (text_len); | |||
1270 | if (conv_data->text->string.mbs) | |||
1271 | memcpy (conv_data->text->string.mbs, text, text_len); | |||
1272 | else | |||
1273 | { | |||
1274 | free (conv_data->text); | |||
1275 | conv_data->text = NULL((void*)0); | |||
1276 | } | |||
1277 | } | |||
1278 | ||||
1279 | g_free (text); | |||
1280 | } | |||
1281 | if (conv_data->operation == XIMStringConversionSubstitution(0x0001) | |||
1282 | && subst_nchars > 0) | |||
1283 | { | |||
1284 | ctk_im_context_delete_surrounding ((CtkIMContext *)context_xim, | |||
1285 | subst_offset, subst_nchars); | |||
1286 | } | |||
1287 | ||||
1288 | g_free (surrounding); | |||
1289 | } | |||
1290 | } | |||
1291 | ||||
1292 | ||||
1293 | static XVaNestedList | |||
1294 | set_preedit_callback (CtkIMContextXIM *context_xim) | |||
1295 | { | |||
1296 | context_xim->preedit_start_callback.client_data = (XPointer)context_xim; | |||
1297 | context_xim->preedit_start_callback.callback = (XIMProc)preedit_start_callback; | |||
1298 | context_xim->preedit_done_callback.client_data = (XPointer)context_xim; | |||
1299 | context_xim->preedit_done_callback.callback = (XIMProc)preedit_done_callback; | |||
1300 | context_xim->preedit_draw_callback.client_data = (XPointer)context_xim; | |||
1301 | context_xim->preedit_draw_callback.callback = (XIMProc)preedit_draw_callback; | |||
1302 | context_xim->preedit_caret_callback.client_data = (XPointer)context_xim; | |||
1303 | context_xim->preedit_caret_callback.callback = (XIMProc)preedit_caret_callback; | |||
1304 | return XVaCreateNestedList (0, | |||
1305 | XNPreeditStartCallback"preeditStartCallback", &context_xim->preedit_start_callback, | |||
1306 | XNPreeditDoneCallback"preeditDoneCallback", &context_xim->preedit_done_callback, | |||
1307 | XNPreeditDrawCallback"preeditDrawCallback", &context_xim->preedit_draw_callback, | |||
1308 | XNPreeditCaretCallback"preeditCaretCallback", &context_xim->preedit_caret_callback, | |||
1309 | NULL((void*)0)); | |||
1310 | } | |||
1311 | ||||
1312 | static XVaNestedList | |||
1313 | set_status_callback (CtkIMContextXIM *context_xim) | |||
1314 | { | |||
1315 | context_xim->status_start_callback.client_data = (XPointer)context_xim; | |||
1316 | context_xim->status_start_callback.callback = (XIMProc)status_start_callback; | |||
1317 | context_xim->status_done_callback.client_data = (XPointer)context_xim; | |||
1318 | context_xim->status_done_callback.callback = (XIMProc)status_done_callback; | |||
1319 | context_xim->status_draw_callback.client_data = (XPointer)context_xim; | |||
1320 | context_xim->status_draw_callback.callback = (XIMProc)status_draw_callback; | |||
1321 | ||||
1322 | return XVaCreateNestedList (0, | |||
1323 | XNStatusStartCallback"statusStartCallback", &context_xim->status_start_callback, | |||
1324 | XNStatusDoneCallback"statusDoneCallback", &context_xim->status_done_callback, | |||
1325 | XNStatusDrawCallback"statusDrawCallback", &context_xim->status_draw_callback, | |||
1326 | NULL((void*)0)); | |||
1327 | } | |||
1328 | ||||
1329 | ||||
1330 | static void | |||
1331 | set_string_conversion_callback (CtkIMContextXIM *context_xim, XIC xic) | |||
1332 | { | |||
1333 | if (!context_xim->im_info->supports_string_conversion) | |||
1334 | return; | |||
1335 | ||||
1336 | context_xim->string_conversion_callback.client_data = (XPointer)context_xim; | |||
1337 | context_xim->string_conversion_callback.callback = (XIMProc)string_conversion_callback; | |||
1338 | ||||
1339 | XSetICValues (xic, | |||
1340 | XNStringConversionCallback"stringConversionCallback", | |||
1341 | (XPointer)&context_xim->string_conversion_callback, | |||
1342 | NULL((void*)0)); | |||
1343 | } | |||
1344 | ||||
1345 | static XIC | |||
1346 | ctk_im_context_xim_get_ic (CtkIMContextXIM *context_xim) | |||
1347 | { | |||
1348 | if (context_xim->im_info == NULL((void*)0) || context_xim->im_info->im == NULL((void*)0)) | |||
1349 | return NULL((void*)0); | |||
1350 | ||||
1351 | if (context_xim->client_window == NULL((void*)0)) | |||
1352 | return NULL((void*)0); | |||
1353 | ||||
1354 | if (!context_xim->ic) | |||
1355 | { | |||
1356 | const char *name1 = NULL((void*)0); | |||
1357 | XVaNestedList list1 = NULL((void*)0); | |||
1358 | const char *name2 = NULL((void*)0); | |||
1359 | XVaNestedList list2 = NULL((void*)0); | |||
1360 | XIMStyle im_style = 0; | |||
1361 | XIC xic = NULL((void*)0); | |||
1362 | ||||
1363 | if (context_xim->use_preedit && | |||
1364 | (context_xim->im_info->style & PREEDIT_MASK(0x0002L | 0x0004L | 0x0001L | 0x0008L | 0x0010L)) == XIMPreeditCallbacks0x0002L) | |||
1365 | { | |||
1366 | im_style |= XIMPreeditCallbacks0x0002L; | |||
1367 | name1 = XNPreeditAttributes"preeditAttributes"; | |||
1368 | list1 = set_preedit_callback (context_xim); | |||
1369 | } | |||
1370 | else if ((context_xim->im_info->style & PREEDIT_MASK(0x0002L | 0x0004L | 0x0001L | 0x0008L | 0x0010L)) == XIMPreeditNone0x0010L) | |||
1371 | im_style |= XIMPreeditNone0x0010L; | |||
1372 | else | |||
1373 | im_style |= XIMPreeditNothing0x0008L; | |||
1374 | ||||
1375 | if ((context_xim->im_info->style & STATUS_MASK(0x0200L | 0x0100L | 0x0400L | 0x0800L)) == XIMStatusCallbacks0x0200L) | |||
1376 | { | |||
1377 | im_style |= XIMStatusCallbacks0x0200L; | |||
1378 | if (name1 == NULL((void*)0)) | |||
1379 | { | |||
1380 | name1 = XNStatusAttributes"statusAttributes"; | |||
1381 | list1 = set_status_callback (context_xim); | |||
1382 | } | |||
1383 | else | |||
1384 | { | |||
1385 | name2 = XNStatusAttributes"statusAttributes"; | |||
1386 | list2 = set_status_callback (context_xim); | |||
1387 | } | |||
1388 | } | |||
1389 | else if ((context_xim->im_info->style & STATUS_MASK(0x0200L | 0x0100L | 0x0400L | 0x0800L)) == XIMStatusNone0x0800L) | |||
1390 | im_style |= XIMStatusNone0x0800L; | |||
1391 | else | |||
1392 | im_style |= XIMStatusNothing0x0400L; | |||
1393 | ||||
1394 | xic = XCreateIC (context_xim->im_info->im, | |||
1395 | XNInputStyle"inputStyle", im_style, | |||
1396 | XNClientWindow"clientWindow", CDK_WINDOW_XID (context_xim->client_window)(cdk_x11_window_get_xid (context_xim->client_window)), | |||
1397 | name1, list1, | |||
1398 | name2, list2, | |||
1399 | NULL((void*)0)); | |||
1400 | if (list1) | |||
1401 | XFree (list1); | |||
1402 | if (list2) | |||
1403 | XFree (list2); | |||
1404 | ||||
1405 | if (xic) | |||
1406 | { | |||
1407 | /* Don't filter key released events with XFilterEvents unless | |||
1408 | * input methods ask for. This is a workaround for Solaris input | |||
1409 | * method bug in C and European locales. It doubles each key | |||
1410 | * stroke if both key pressed and released events are filtered. | |||
1411 | * (bugzilla #81759) | |||
1412 | */ | |||
1413 | gulong mask = 0xaaaaaaaa; | |||
1414 | XGetICValues (xic, | |||
1415 | XNFilterEvents"filterEvents", &mask, | |||
1416 | NULL((void*)0)); | |||
1417 | context_xim->filter_key_release = (mask & KeyReleaseMask(1L<<1)) != 0; | |||
1418 | set_string_conversion_callback (context_xim, xic); | |||
1419 | } | |||
1420 | ||||
1421 | context_xim->ic = xic; | |||
1422 | ||||
1423 | update_status_window (context_xim); | |||
1424 | ||||
1425 | if (xic && context_xim->has_focus) | |||
1426 | XSetICFocus (xic); | |||
1427 | } | |||
1428 | return context_xim->ic; | |||
1429 | } | |||
1430 | ||||
1431 | /***************************************************************** | |||
1432 | * Status Window handling | |||
1433 | * | |||
1434 | * A status window is a small window attached to the toplevel | |||
1435 | * that is used to display information to the user about the | |||
1436 | * current input operation. | |||
1437 | * | |||
1438 | * We claim the toplevel's status window for an input context if: | |||
1439 | * | |||
1440 | * A) The input context has a toplevel | |||
1441 | * B) The input context has the focus | |||
1442 | * C) The input context has an XIC associated with it | |||
1443 | * | |||
1444 | * Tracking A) and C) is pretty reliable since we | |||
1445 | * compute A) and create the XIC for C) ourselves. | |||
1446 | * For B) we basically have to depend on our callers | |||
1447 | * calling ::focus-in and ::focus-out at the right time. | |||
1448 | * | |||
1449 | * The toplevel is computed by walking up the CdkWindow | |||
1450 | * hierarchy from context->client_window until we find a | |||
1451 | * window that is owned by some widget, and then calling | |||
1452 | * ctk_widget_get_toplevel() on that widget. This should | |||
1453 | * handle both cases where we might have CdkWindows without widgets, | |||
1454 | * and cases where CtkWidgets have strange window hierarchies | |||
1455 | * (like a torn off CtkHandleBox.) | |||
1456 | * | |||
1457 | * The status window is visible if and only if there is text | |||
1458 | * for it; whenever a new CtkIMContextXIM claims the status | |||
1459 | * window, we blank out any existing text. We actually only | |||
1460 | * create a CtkWindow for the status window the first time | |||
1461 | * it is shown; this is an important optimization when we are | |||
1462 | * using XIM with something like a simple compose-key input | |||
1463 | * method that never needs a status window. | |||
1464 | *****************************************************************/ | |||
1465 | ||||
1466 | /* Called when we no longer need a status window | |||
1467 | */ | |||
1468 | static void | |||
1469 | disclaim_status_window (CtkIMContextXIM *context_xim) | |||
1470 | { | |||
1471 | if (context_xim->status_window) | |||
1472 | { | |||
1473 | g_assert (context_xim->status_window->context == context_xim)do { if (context_xim->status_window->context == context_xim ) ; else g_assertion_message_expr (((gchar*) 0), "ctkimcontextxim.c" , 1473, ((const char*) (__func__)), "context_xim->status_window->context == context_xim" ); } while (0); | |||
1474 | ||||
1475 | status_window_set_text (context_xim->status_window, ""); | |||
1476 | ||||
1477 | context_xim->status_window->context = NULL((void*)0); | |||
1478 | context_xim->status_window = NULL((void*)0); | |||
1479 | } | |||
1480 | } | |||
1481 | ||||
1482 | /* Called when we need a status window | |||
1483 | */ | |||
1484 | static void | |||
1485 | claim_status_window (CtkIMContextXIM *context_xim) | |||
1486 | { | |||
1487 | if (!context_xim->status_window && context_xim->client_widget) | |||
1488 | { | |||
1489 | CtkWidget *toplevel = ctk_widget_get_toplevel (context_xim->client_widget); | |||
1490 | if (toplevel && ctk_widget_is_toplevel (toplevel)) | |||
1491 | { | |||
1492 | StatusWindow *status_window = status_window_get (toplevel); | |||
1493 | ||||
1494 | if (status_window->context) | |||
1495 | disclaim_status_window (status_window->context); | |||
1496 | ||||
1497 | status_window->context = context_xim; | |||
1498 | context_xim->status_window = status_window; | |||
1499 | } | |||
1500 | } | |||
1501 | } | |||
1502 | ||||
1503 | /* Basic call made whenever something changed that might cause | |||
1504 | * us to need, or not to need a status window. | |||
1505 | */ | |||
1506 | static void | |||
1507 | update_status_window (CtkIMContextXIM *context_xim) | |||
1508 | { | |||
1509 | if (context_xim->ic && context_xim->in_toplevel && context_xim->has_focus) | |||
1510 | claim_status_window (context_xim); | |||
1511 | else | |||
1512 | disclaim_status_window (context_xim); | |||
1513 | } | |||
1514 | ||||
1515 | /* Updates the in_toplevel flag for @context_xim | |||
1516 | */ | |||
1517 | static void | |||
1518 | update_in_toplevel (CtkIMContextXIM *context_xim) | |||
1519 | { | |||
1520 | if (context_xim->client_widget) | |||
1521 | { | |||
1522 | CtkWidget *toplevel = ctk_widget_get_toplevel (context_xim->client_widget); | |||
1523 | ||||
1524 | context_xim->in_toplevel = (toplevel && ctk_widget_is_toplevel (toplevel)); | |||
1525 | } | |||
1526 | else | |||
1527 | context_xim->in_toplevel = FALSE(0); | |||
1528 | ||||
1529 | /* Some paranoia, in case we don't get a focus out */ | |||
1530 | if (!context_xim->in_toplevel) | |||
1531 | context_xim->has_focus = FALSE(0); | |||
1532 | ||||
1533 | update_status_window (context_xim); | |||
1534 | } | |||
1535 | ||||
1536 | /* Callback when @widget's toplevel changes. It will always | |||
1537 | * change from NULL to a window, or a window to NULL; | |||
1538 | * we use that intermediate NULL state to make sure | |||
1539 | * that we disclaim the toplevel status window for the old | |||
1540 | * window. | |||
1541 | */ | |||
1542 | static void | |||
1543 | on_client_widget_hierarchy_changed (CtkWidget *widget G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
1544 | CtkWidget *old_toplevel G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
1545 | CtkIMContextXIM *context_xim) | |||
1546 | { | |||
1547 | update_in_toplevel (context_xim); | |||
1548 | } | |||
1549 | ||||
1550 | /* Finds the CtkWidget that owns the window, or if none, the | |||
1551 | * widget owning the nearest parent that has a widget. | |||
1552 | */ | |||
1553 | static CtkWidget * | |||
1554 | widget_for_window (CdkWindow *window) | |||
1555 | { | |||
1556 | while (window) | |||
1557 | { | |||
1558 | gpointer user_data; | |||
1559 | cdk_window_get_user_data (window, &user_data); | |||
1560 | if (user_data) | |||
1561 | return user_data; | |||
1562 | ||||
1563 | window = cdk_window_get_parent (window); | |||
1564 | } | |||
1565 | ||||
1566 | return NULL((void*)0); | |||
1567 | } | |||
1568 | ||||
1569 | /* Called when context_xim->client_window changes; takes care of | |||
1570 | * removing and/or setting up our watches for the toplevel | |||
1571 | */ | |||
1572 | static void | |||
1573 | update_client_widget (CtkIMContextXIM *context_xim) | |||
1574 | { | |||
1575 | CtkWidget *new_client_widget = widget_for_window (context_xim->client_window); | |||
1576 | ||||
1577 | if (new_client_widget != context_xim->client_widget) | |||
1578 | { | |||
1579 | if (context_xim->client_widget) | |||
1580 | { | |||
1581 | g_signal_handlers_disconnect_by_func (context_xim->client_widget,g_signal_handlers_disconnect_matched ((context_xim->client_widget ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_client_widget_hierarchy_changed ))), (context_xim)) | |||
1582 | G_CALLBACK (on_client_widget_hierarchy_changed),g_signal_handlers_disconnect_matched ((context_xim->client_widget ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_client_widget_hierarchy_changed ))), (context_xim)) | |||
1583 | context_xim)g_signal_handlers_disconnect_matched ((context_xim->client_widget ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_client_widget_hierarchy_changed ))), (context_xim)); | |||
1584 | } | |||
1585 | context_xim->client_widget = new_client_widget; | |||
1586 | if (context_xim->client_widget) | |||
1587 | { | |||
1588 | g_signal_connect (context_xim->client_widget, "hierarchy-changed",g_signal_connect_data ((context_xim->client_widget), ("hierarchy-changed" ), (((GCallback) (on_client_widget_hierarchy_changed))), (context_xim ), ((void*)0), (GConnectFlags) 0) | |||
1589 | G_CALLBACK (on_client_widget_hierarchy_changed),g_signal_connect_data ((context_xim->client_widget), ("hierarchy-changed" ), (((GCallback) (on_client_widget_hierarchy_changed))), (context_xim ), ((void*)0), (GConnectFlags) 0) | |||
1590 | context_xim)g_signal_connect_data ((context_xim->client_widget), ("hierarchy-changed" ), (((GCallback) (on_client_widget_hierarchy_changed))), (context_xim ), ((void*)0), (GConnectFlags) 0); | |||
1591 | } | |||
1592 | ||||
1593 | update_in_toplevel (context_xim); | |||
1594 | } | |||
1595 | } | |||
1596 | ||||
1597 | /* Called when the toplevel is destroyed; frees the status window | |||
1598 | */ | |||
1599 | static void | |||
1600 | on_status_toplevel_destroy (CtkWidget *toplevel G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
1601 | StatusWindow *status_window) | |||
1602 | { | |||
1603 | status_window_free (status_window); | |||
1604 | } | |||
1605 | ||||
1606 | /* Called when the screen for the toplevel changes; updates the | |||
1607 | * screen for the status window to match. | |||
1608 | */ | |||
1609 | static void | |||
1610 | on_status_toplevel_notify_screen (CtkWindow *toplevel, | |||
1611 | GParamSpec *pspec G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
1612 | StatusWindow *status_window) | |||
1613 | { | |||
1614 | if (status_window->window) | |||
1615 | ctk_window_set_screen (CTK_WINDOW (status_window->window)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((status_window->window)), ((ctk_window_get_type ())))) )), | |||
1616 | ctk_widget_get_screen (CTK_WIDGET (toplevel)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((toplevel)), ((ctk_widget_get_type ())))))))); | |||
1617 | } | |||
1618 | ||||
1619 | /* Called when the toplevel window is moved; updates the position of | |||
1620 | * the status window to follow it. | |||
1621 | */ | |||
1622 | static gboolean | |||
1623 | on_status_toplevel_configure (CtkWidget *toplevel, | |||
1624 | CdkEventConfigure *event G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
1625 | StatusWindow *status_window) | |||
1626 | { | |||
1627 | CdkRectangle rect; | |||
1628 | CtkRequisition requisition; | |||
1629 | gint y; | |||
1630 | gint height; | |||
1631 | ||||
1632 | if (status_window->window) | |||
1633 | { | |||
1634 | G_GNUC_BEGIN_IGNORE_DEPRECATIONSclang diagnostic push
clang diagnostic ignored "-Wdeprecated-declarations" | |||
1635 | height = cdk_screen_get_height (ctk_widget_get_screen (toplevel)); | |||
1636 | G_GNUC_END_IGNORE_DEPRECATIONSclang diagnostic pop | |||
1637 | ||||
1638 | cdk_window_get_frame_extents (ctk_widget_get_window (toplevel), | |||
1639 | &rect); | |||
1640 | ctk_widget_get_preferred_size ( (status_window->window), | |||
1641 | &requisition, NULL((void*)0)); | |||
1642 | ||||
1643 | if (rect.y + rect.height + requisition.height < height) | |||
1644 | y = rect.y + rect.height; | |||
1645 | else | |||
1646 | y = height - requisition.height; | |||
1647 | ||||
1648 | ctk_window_move (CTK_WINDOW (status_window->window)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((status_window->window)), ((ctk_window_get_type ())))) )), rect.x, y); | |||
1649 | } | |||
1650 | ||||
1651 | return FALSE(0); | |||
1652 | } | |||
1653 | ||||
1654 | /* Frees a status window and removes its link from the status_windows list | |||
1655 | */ | |||
1656 | static void | |||
1657 | status_window_free (StatusWindow *status_window) | |||
1658 | { | |||
1659 | status_windows = g_slist_remove (status_windows, status_window); | |||
1660 | ||||
1661 | if (status_window->context) | |||
1662 | status_window->context->status_window = NULL((void*)0); | |||
1663 | ||||
1664 | g_signal_handlers_disconnect_by_func (status_window->toplevel,g_signal_handlers_disconnect_matched ((status_window->toplevel ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_status_toplevel_destroy ))), (status_window)) | |||
1665 | G_CALLBACK (on_status_toplevel_destroy),g_signal_handlers_disconnect_matched ((status_window->toplevel ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_status_toplevel_destroy ))), (status_window)) | |||
1666 | status_window)g_signal_handlers_disconnect_matched ((status_window->toplevel ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_status_toplevel_destroy ))), (status_window)); | |||
1667 | g_signal_handlers_disconnect_by_func (status_window->toplevel,g_signal_handlers_disconnect_matched ((status_window->toplevel ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_status_toplevel_notify_screen ))), (status_window)) | |||
1668 | G_CALLBACK (on_status_toplevel_notify_screen),g_signal_handlers_disconnect_matched ((status_window->toplevel ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_status_toplevel_notify_screen ))), (status_window)) | |||
1669 | status_window)g_signal_handlers_disconnect_matched ((status_window->toplevel ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_status_toplevel_notify_screen ))), (status_window)); | |||
1670 | g_signal_handlers_disconnect_by_func (status_window->toplevel,g_signal_handlers_disconnect_matched ((status_window->toplevel ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_status_toplevel_configure ))), (status_window)) | |||
1671 | G_CALLBACK (on_status_toplevel_configure),g_signal_handlers_disconnect_matched ((status_window->toplevel ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_status_toplevel_configure ))), (status_window)) | |||
1672 | status_window)g_signal_handlers_disconnect_matched ((status_window->toplevel ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (on_status_toplevel_configure ))), (status_window)); | |||
1673 | ||||
1674 | if (status_window->window) | |||
1675 | ctk_widget_destroy (status_window->window); | |||
1676 | ||||
1677 | g_object_set_data (G_OBJECT (status_window->toplevel)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((status_window->toplevel)), (((GType) ((20) << ( 2)))))))), "ctk-im-xim-status-window", NULL((void*)0)); | |||
1678 | ||||
1679 | g_free (status_window); | |||
1680 | } | |||
1681 | ||||
1682 | /* Finds the status window object for a toplevel, creating it if necessary. | |||
1683 | */ | |||
1684 | static StatusWindow * | |||
1685 | status_window_get (CtkWidget *toplevel) | |||
1686 | { | |||
1687 | StatusWindow *status_window; | |||
1688 | ||||
1689 | status_window = g_object_get_data (G_OBJECT (toplevel)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((toplevel)), (((GType) ((20) << (2)))))))), "ctk-im-xim-status-window"); | |||
1690 | if (status_window) | |||
1691 | return status_window; | |||
1692 | ||||
1693 | status_window = g_new0 (StatusWindow, 1)((StatusWindow *) g_malloc0_n ((1), sizeof (StatusWindow))); | |||
1694 | status_window->toplevel = toplevel; | |||
1695 | ||||
1696 | status_windows = g_slist_prepend (status_windows, status_window); | |||
1697 | ||||
1698 | g_signal_connect (toplevel, "destroy",g_signal_connect_data ((toplevel), ("destroy"), (((GCallback) (on_status_toplevel_destroy))), (status_window), ((void*)0), (GConnectFlags) 0) | |||
1699 | G_CALLBACK (on_status_toplevel_destroy),g_signal_connect_data ((toplevel), ("destroy"), (((GCallback) (on_status_toplevel_destroy))), (status_window), ((void*)0), (GConnectFlags) 0) | |||
1700 | status_window)g_signal_connect_data ((toplevel), ("destroy"), (((GCallback) (on_status_toplevel_destroy))), (status_window), ((void*)0), (GConnectFlags) 0); | |||
1701 | g_signal_connect (toplevel, "configure-event",g_signal_connect_data ((toplevel), ("configure-event"), (((GCallback ) (on_status_toplevel_configure))), (status_window), ((void*) 0), (GConnectFlags) 0) | |||
1702 | G_CALLBACK (on_status_toplevel_configure),g_signal_connect_data ((toplevel), ("configure-event"), (((GCallback ) (on_status_toplevel_configure))), (status_window), ((void*) 0), (GConnectFlags) 0) | |||
1703 | status_window)g_signal_connect_data ((toplevel), ("configure-event"), (((GCallback ) (on_status_toplevel_configure))), (status_window), ((void*) 0), (GConnectFlags) 0); | |||
1704 | g_signal_connect (toplevel, "notify::screen",g_signal_connect_data ((toplevel), ("notify::screen"), (((GCallback ) (on_status_toplevel_notify_screen))), (status_window), ((void *)0), (GConnectFlags) 0) | |||
1705 | G_CALLBACK (on_status_toplevel_notify_screen),g_signal_connect_data ((toplevel), ("notify::screen"), (((GCallback ) (on_status_toplevel_notify_screen))), (status_window), ((void *)0), (GConnectFlags) 0) | |||
1706 | status_window)g_signal_connect_data ((toplevel), ("notify::screen"), (((GCallback ) (on_status_toplevel_notify_screen))), (status_window), ((void *)0), (GConnectFlags) 0); | |||
1707 | ||||
1708 | g_object_set_data (G_OBJECT (toplevel)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((toplevel)), (((GType) ((20) << (2)))))))), "ctk-im-xim-status-window", status_window); | |||
1709 | ||||
1710 | return status_window; | |||
1711 | } | |||
1712 | ||||
1713 | /* Creates the widgets for the status window; called when we | |||
1714 | * first need to show text for the status window. | |||
1715 | */ | |||
1716 | static void | |||
1717 | status_window_make_window (StatusWindow *status_window) | |||
1718 | { | |||
1719 | CtkWidget *window; | |||
1720 | CtkWidget *status_label; | |||
1721 | ||||
1722 | status_window->window = ctk_window_new (CTK_WINDOW_POPUP); | |||
1723 | window = status_window->window; | |||
1724 | ||||
1725 | ctk_window_set_resizable (CTK_WINDOW (window)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((window)), ((ctk_window_get_type ())))))), FALSE(0)); | |||
1726 | ||||
1727 | status_label = ctk_label_new (""); | |||
1728 | g_object_set (status_label, "margin", 1, NULL((void*)0)); | |||
1729 | ctk_widget_show (status_label); | |||
1730 | ||||
1731 | ctk_container_add (CTK_CONTAINER (window)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((window)), ((ctk_container_get_type ())))))), status_label); | |||
1732 | ||||
1733 | ctk_window_set_screen (CTK_WINDOW (status_window->window)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((status_window->window)), ((ctk_window_get_type ())))) )), | |||
1734 | ctk_widget_get_screen (status_window->toplevel)); | |||
1735 | ||||
1736 | on_status_toplevel_configure (status_window->toplevel, NULL((void*)0), status_window); | |||
1737 | } | |||
1738 | ||||
1739 | /* Updates the text in the status window, hiding or | |||
1740 | * showing the window as necessary. | |||
1741 | */ | |||
1742 | static void | |||
1743 | status_window_set_text (StatusWindow *status_window, | |||
1744 | const gchar *text) | |||
1745 | { | |||
1746 | if (text[0]) | |||
1747 | { | |||
1748 | CtkWidget *label; | |||
1749 | ||||
1750 | if (!status_window->window) | |||
1751 | status_window_make_window (status_window); | |||
1752 | ||||
1753 | label = ctk_bin_get_child (CTK_BIN (status_window->window)((((CtkBin*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((status_window->window)), ((ctk_bin_get_type ()))))))); | |||
1754 | ctk_label_set_text (CTK_LABEL (label)((((CtkLabel*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((label)), ((ctk_label_get_type ())))))), text); | |||
1755 | ||||
1756 | ctk_widget_show (status_window->window); | |||
1757 | } | |||
1758 | else | |||
1759 | { | |||
1760 | if (status_window->window) | |||
1761 | ctk_widget_hide (status_window->window); | |||
1762 | } | |||
1763 | } | |||
1764 | ||||
1765 | /** | |||
1766 | * ctk_im_context_xim_shutdown: | |||
1767 | * | |||
1768 | * Destroys all the status windows that are kept by the XIM contexts. This | |||
1769 | * function should only be called by the XIM module exit routine. | |||
1770 | **/ | |||
1771 | void | |||
1772 | ctk_im_context_xim_shutdown (void) | |||
1773 | { | |||
1774 | while (status_windows) | |||
1775 | status_window_free (status_windows->data); | |||
1776 | ||||
1777 | while (open_ims) | |||
1778 | { | |||
1779 | CtkXIMInfo *info = open_ims->data; | |||
1780 | CdkDisplay *display = cdk_screen_get_display (info->screen); | |||
1781 | ||||
1782 | xim_info_display_closed (display, FALSE(0), info); | |||
1783 | open_ims = g_slist_remove_link (open_ims, open_ims); | |||
1784 | } | |||
1785 | } |