~canonical-dx-team/ubuntu/maverick/gtk+2.0/menuproxy

« back to all changes in this revision

Viewing changes to modules/input/gtkimcontextxim.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2007-05-04 12:24:25 UTC
  • mfrom: (1.1.21 upstream)
  • Revision ID: james.westby@ubuntu.com-20070504122425-0m8midgzrp40y8w2
Tags: 2.10.12-1ubuntu1
* Sync with Debian
* New upstream version:
  Fixed bugs:
  - 379414 file chooser warnings when changing path in the entry
  - 418585 GtkFileChooserDefault sizing code is not DPI independent
  - 419568 Crash in search if start with special letter
  - 435062 build dies with icon cache validation
  - 379399 Segfault to call gtk_print_operation_run twice.
  - 387889 cups backend has problems when there are too many printers
  - 418531 invalid read to gtkicontheme.c gtk_icon_theme_lookup_icon...
  - 423916 crash in color scheme code
  - 424042 Segmentation fault while quickly pressing Alt+arrows
  - 415260 Protect against negative indices when setting values in G...
  - 419171 XGetVisualInfo() may not set nxvisuals
  - 128852 Gdk cursors don't look good on win32
  - 344657 Ctrl-H doesn't toggle "Show Hidden Files" setting
  - 345345 PrintOperation::paginate is not emitted for class handler
  - 347567 GtkPrintOperation::end-print is not emitted if it's cance...
  - 369112 gtk_ui_manager_add_ui should accept unnamed separator
  - 392015 Selected menu item invisible on Windows Vista
  - 399253 MS-Windows Theme Bottom Tab placement rendering glitches
  - 399425 gtk_input_dialog_fill_axes() adds child to gtkscrolledwin...
  - 403251 [patch] little memory leak in GtkPrintJob
  - 403267 [patch] memory leak in GtkPageSetupUnixDialog
  - 403470 MS-Windows Theme tab placement other than on top leaks a ...
  - 404506 Windows system fonts that have multi-byte font names cann...
  - 405089 Incorrect window placement for GtkEventBox private window
  - 405515 Minor leak in gtkfilesystemmodel.c
  - 405539 gdk_pixbuf_save() for PNG saver can return FALSE without ...
  - 415681 gdk_window_clear_area includes an extra line and column o...
  - 418219 GtkRecentChooser should apply filter before sorting and c...
  - 418403 Scroll to printer after selecting it from settings
  - 421985 _gtk_print_operation_platform_backend_launch_preview
  - 421990 gtk_print_job_get_surface
  - 421993 gtk_print_operation_init
  - 423064 Conditional jump or move depends on uninitialised value(s...
  - 423722 Fix printing header in gtk-demo
  - 424168 gtk_print_operation_run on async preview
  - 425655 Don't install gtk+-unix-print-2.0.pc on non-UNIX platforms
  - 425786 GDK segfaults if XineramaQueryScreens fails
  - 428665 Lpr Backend gets stuck in infinite loop during gtk_enumer...
  - 429902 GtkPrintOperation leaks cairo contextes
  - 431997 First delay of GdkPixbufAnimationIter is wrong
  - 433242 Inconsistent scroll arrow position calculations
  - 433972 Placing gtk.Expander inside a gtk.TextView() changes gtk....
  - 434261 _gtk_toolbar_elide_underscores incorrectly handles some s...
  - 383354 ctrl-L should make 'Location' entry disappear
  - 418673 gtk_recent_manager_add_item
  - 429732 gtk_accel_group_finalize accesses invalid memory
  - 435028 WM_CLIENT_LEADER is wrong on the leader_window
  - 431067 Background of the header window is not updated
  - 338843 add recent files support inside the ui manager
  - 148535 add drop shadow to menus, tooltips, etc. under Windows XP
* debian/control.in:
  - Conflicts on ubuntulooks (<= 0.9.11-1)
* debian/patches/15_default-fallback-icon-theme.patch:
  - patch from Debian, fallback on gnome icon theme

Show diffs side-by-side

added added

removed removed

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