File: | modules/input/ctkimcontextxim.c |
Warning: | line 966, column 27 Casting a non-structure type to a structure type and accessing a field can lead to memory access errors or data corruption |
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 ())))))); |
Casting a non-structure type to a structure type and accessing a field can lead to memory access errors or data corruption | |
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 != call_data->chg_first || chg_length != call_data->chg_length) |
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 | } |