]> Pileus Git - ~andy/gtk/blob - gtk/gtkimmulticontext.c
Don't use zip -r on the etc directory, to avoid including editor backup
[~andy/gtk] / gtk / gtkimmulticontext.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2000 Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21
22 #include <string.h>
23 #include <locale.h>
24
25 #include "gtkimmulticontext.h"
26 #include "gtkimmodule.h"
27 #include "gtkmain.h"
28 #include "gtkradiomenuitem.h"
29 #include "gtkintl.h"
30 #include "gtkprivate.h"
31
32 struct _GtkIMMulticontextPrivate
33 {
34   GdkWindow *client_window;
35   GdkRectangle cursor_location;
36
37   guint use_preedit : 1;
38   guint have_cursor_location : 1;
39   guint focus_in : 1;
40 };
41
42 static void     gtk_im_multicontext_class_init         (GtkIMMulticontextClass  *class);
43 static void     gtk_im_multicontext_init               (GtkIMMulticontext       *im_multicontext);
44 static void     gtk_im_multicontext_finalize           (GObject                 *object);
45
46 static void     gtk_im_multicontext_set_slave          (GtkIMMulticontext       *multicontext,
47                                                         GtkIMContext            *slave,
48                                                         gboolean                 finalizing);
49
50 static void     gtk_im_multicontext_set_client_window  (GtkIMContext            *context,
51                                                         GdkWindow               *window);
52 static void     gtk_im_multicontext_get_preedit_string (GtkIMContext            *context,
53                                                         gchar                  **str,
54                                                         PangoAttrList          **attrs,
55                                                         gint                   *cursor_pos);
56 static gboolean gtk_im_multicontext_filter_keypress    (GtkIMContext            *context,
57                                                         GdkEventKey             *event);
58 static void     gtk_im_multicontext_focus_in           (GtkIMContext            *context);
59 static void     gtk_im_multicontext_focus_out          (GtkIMContext            *context);
60 static void     gtk_im_multicontext_reset              (GtkIMContext            *context);
61 static void     gtk_im_multicontext_set_cursor_location (GtkIMContext            *context,
62                                                         GdkRectangle            *area);
63 static void     gtk_im_multicontext_set_use_preedit    (GtkIMContext            *context,
64                                                         gboolean                 use_preedit);
65 static gboolean gtk_im_multicontext_get_surrounding    (GtkIMContext            *context,
66                                                         gchar                  **text,
67                                                         gint                    *cursor_index);
68 static void     gtk_im_multicontext_set_surrounding    (GtkIMContext            *context,
69                                                         const char              *text,
70                                                         gint                     len,
71                                                         gint                     cursor_index);
72
73 static void     gtk_im_multicontext_preedit_start_cb        (GtkIMContext      *slave,
74                                                              GtkIMMulticontext *multicontext);
75 static void     gtk_im_multicontext_preedit_end_cb          (GtkIMContext      *slave,
76                                                              GtkIMMulticontext *multicontext);
77 static void     gtk_im_multicontext_preedit_changed_cb      (GtkIMContext      *slave,
78                                                              GtkIMMulticontext *multicontext);
79 static void     gtk_im_multicontext_commit_cb               (GtkIMContext      *slave,
80                                                              const gchar       *str,
81                                                              GtkIMMulticontext *multicontext);
82 static gboolean gtk_im_multicontext_retrieve_surrounding_cb (GtkIMContext      *slave,
83                                                              GtkIMMulticontext *multicontext);
84 static gboolean gtk_im_multicontext_delete_surrounding_cb   (GtkIMContext      *slave,
85                                                              gint               offset,
86                                                              gint               n_chars,
87                                                              GtkIMMulticontext *multicontext);
88 static GtkIMContextClass *parent_class;
89
90 static const gchar *global_context_id = NULL;
91
92 GType
93 gtk_im_multicontext_get_type (void)
94 {
95   static GType im_multicontext_type = 0;
96  
97   if (!im_multicontext_type)
98     {
99       static const GTypeInfo im_multicontext_info =
100       {
101         sizeof (GtkIMMulticontextClass),
102         (GBaseInitFunc) NULL,
103         (GBaseFinalizeFunc) NULL,
104         (GClassInitFunc) gtk_im_multicontext_class_init,
105         NULL,           /* class_finalize */
106         NULL,           /* class_data */
107         sizeof (GtkIMMulticontext),
108         0,              /* n_preallocs */
109         (GInstanceInitFunc) gtk_im_multicontext_init,
110       };
111       
112       im_multicontext_type =
113         g_type_register_static (GTK_TYPE_IM_CONTEXT, "GtkIMMulticontext",
114                                 &im_multicontext_info, 0);
115     }
116
117   return im_multicontext_type;
118 }
119
120 static void
121 gtk_im_multicontext_class_init (GtkIMMulticontextClass *class)
122 {
123   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
124   GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
125   
126   parent_class = g_type_class_peek_parent (class);
127
128   im_context_class->set_client_window = gtk_im_multicontext_set_client_window;
129   im_context_class->get_preedit_string = gtk_im_multicontext_get_preedit_string;
130   im_context_class->filter_keypress = gtk_im_multicontext_filter_keypress;
131   im_context_class->focus_in = gtk_im_multicontext_focus_in;
132   im_context_class->focus_out = gtk_im_multicontext_focus_out;
133   im_context_class->reset = gtk_im_multicontext_reset;
134   im_context_class->set_cursor_location = gtk_im_multicontext_set_cursor_location;
135   im_context_class->set_use_preedit = gtk_im_multicontext_set_use_preedit;
136   im_context_class->set_surrounding = gtk_im_multicontext_set_surrounding;
137   im_context_class->get_surrounding = gtk_im_multicontext_get_surrounding;
138
139   gobject_class->finalize = gtk_im_multicontext_finalize;
140 }
141
142 static void
143 gtk_im_multicontext_init (GtkIMMulticontext *multicontext)
144 {
145   multicontext->slave = NULL;
146   
147   multicontext->priv = g_new0 (GtkIMMulticontextPrivate, 1);
148   multicontext->priv->use_preedit = TRUE;
149   multicontext->priv->have_cursor_location = FALSE;
150   multicontext->priv->focus_in = FALSE;
151 }
152
153 /**
154  * gtk_im_multicontext_new:
155  *
156  * Creates a new #GtkIMMulticontext.
157  *
158  * Returns: a new #GtkIMMulticontext.
159  **/
160 GtkIMContext *
161 gtk_im_multicontext_new (void)
162 {
163   return g_object_new (GTK_TYPE_IM_MULTICONTEXT, NULL);
164 }
165
166 static void
167 gtk_im_multicontext_finalize (GObject *object)
168 {
169   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (object);
170   
171   gtk_im_multicontext_set_slave (multicontext, NULL, TRUE);
172   g_free (multicontext->priv);
173
174   G_OBJECT_CLASS (parent_class)->finalize (object);
175 }
176
177 static void
178 gtk_im_multicontext_set_slave (GtkIMMulticontext *multicontext,
179                                GtkIMContext      *slave,
180                                gboolean           finalizing)
181 {
182   GtkIMMulticontextPrivate *priv = multicontext->priv;
183   gboolean need_preedit_changed = FALSE;
184   
185   if (multicontext->slave)
186     {
187       if (!finalizing)
188         gtk_im_context_reset (multicontext->slave);
189       
190       g_signal_handlers_disconnect_by_func (multicontext->slave,
191                                             gtk_im_multicontext_preedit_start_cb,
192                                             multicontext);
193       g_signal_handlers_disconnect_by_func (multicontext->slave,
194                                             gtk_im_multicontext_preedit_end_cb,
195                                             multicontext);
196       g_signal_handlers_disconnect_by_func (multicontext->slave,
197                                             gtk_im_multicontext_preedit_changed_cb,
198                                             multicontext);
199       g_signal_handlers_disconnect_by_func (multicontext->slave,
200                                             gtk_im_multicontext_commit_cb,
201                                             multicontext);
202
203       g_object_unref (multicontext->slave);
204       multicontext->slave = NULL;
205
206       if (!finalizing)
207         need_preedit_changed = TRUE;
208     }
209   
210   multicontext->slave = slave;
211
212   if (multicontext->slave)
213     {
214       g_object_ref (multicontext->slave);
215
216       g_signal_connect (multicontext->slave, "preedit_start",
217                         G_CALLBACK (gtk_im_multicontext_preedit_start_cb),
218                         multicontext);
219       g_signal_connect (multicontext->slave, "preedit_end",
220                         G_CALLBACK (gtk_im_multicontext_preedit_end_cb),
221                         multicontext);
222       g_signal_connect (multicontext->slave, "preedit_changed",
223                         G_CALLBACK (gtk_im_multicontext_preedit_changed_cb),
224                         multicontext);
225       g_signal_connect (multicontext->slave, "commit",
226                         G_CALLBACK (gtk_im_multicontext_commit_cb),
227                         multicontext);
228       g_signal_connect (multicontext->slave, "retrieve_surrounding",
229                         G_CALLBACK (gtk_im_multicontext_retrieve_surrounding_cb),
230                         multicontext);
231       g_signal_connect (multicontext->slave, "delete_surrounding",
232                         G_CALLBACK (gtk_im_multicontext_delete_surrounding_cb),
233                         multicontext);
234       
235       if (!priv->use_preedit)   /* Default is TRUE */
236         gtk_im_context_set_use_preedit (slave, FALSE);
237       if (priv->client_window)
238         gtk_im_context_set_client_window (slave, priv->client_window);
239       if (priv->have_cursor_location)
240         gtk_im_context_set_cursor_location (slave, &priv->cursor_location);
241       if (priv->focus_in)
242         gtk_im_context_focus_in (slave);
243     }
244
245   if (need_preedit_changed)
246     g_signal_emit_by_name (multicontext, "preedit_changed");
247 }
248
249 static GtkIMContext *
250 gtk_im_multicontext_get_slave (GtkIMMulticontext *multicontext)
251 {
252   if (!multicontext->slave)
253     {
254       GtkIMContext *slave;
255
256       if (!global_context_id)
257         {
258           gchar *locale = _gtk_get_lc_ctype ();
259           global_context_id = _gtk_im_module_get_default_context_id (locale);
260           g_free (locale);
261         }
262         
263       slave = _gtk_im_module_create (global_context_id);
264       gtk_im_multicontext_set_slave (multicontext, slave, FALSE);
265       g_object_unref (slave);
266
267       multicontext->context_id = global_context_id;
268     }
269
270   return multicontext->slave;
271 }
272
273 static void
274 gtk_im_multicontext_set_client_window (GtkIMContext *context,
275                                        GdkWindow    *window)
276 {
277   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
278   
279   GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
280
281   multicontext->priv->client_window = window;
282   
283   if (slave)
284     gtk_im_context_set_client_window (slave, window);
285 }
286
287 static void
288 gtk_im_multicontext_get_preedit_string (GtkIMContext   *context,
289                                         gchar         **str,
290                                         PangoAttrList **attrs,
291                                         gint           *cursor_pos)
292 {
293   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
294   GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
295
296   if (slave)
297     gtk_im_context_get_preedit_string (slave, str, attrs, cursor_pos);
298   else
299     {
300       if (str)
301         *str = g_strdup ("");
302       if (attrs)
303         *attrs = pango_attr_list_new ();
304     }
305 }
306
307 static gboolean
308 gtk_im_multicontext_filter_keypress (GtkIMContext *context,
309                                      GdkEventKey  *event)
310 {
311   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
312   GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
313
314   if (slave)
315     return gtk_im_context_filter_keypress (slave, event);
316   else
317     return FALSE;
318 }
319
320 static void
321 gtk_im_multicontext_focus_in (GtkIMContext   *context)
322 {
323   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
324   GtkIMContext *slave;
325
326   /* If the global context type is different from the context we were
327    * using before, get rid of the old slave and create a new one
328    * for the new global context type.
329    */
330   if (!multicontext->context_id ||
331       strcmp (global_context_id, multicontext->context_id) != 0)
332     gtk_im_multicontext_set_slave (multicontext, NULL, FALSE);
333
334   slave = gtk_im_multicontext_get_slave (multicontext);
335   
336   multicontext->priv->focus_in = TRUE;
337   
338   if (slave)
339     gtk_im_context_focus_in (slave);
340 }
341
342 static void
343 gtk_im_multicontext_focus_out (GtkIMContext   *context)
344 {
345   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
346   GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
347
348   multicontext->priv->focus_in = FALSE;
349   
350   if (slave)
351     gtk_im_context_focus_out (slave);
352 }
353
354 static void
355 gtk_im_multicontext_reset (GtkIMContext   *context)
356 {
357   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
358   GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
359
360   if (slave)
361     gtk_im_context_reset (slave);
362 }
363
364 static void
365 gtk_im_multicontext_set_cursor_location (GtkIMContext   *context,
366                                          GdkRectangle   *area)
367 {
368   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
369   GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
370
371   multicontext->priv->have_cursor_location = TRUE;
372   multicontext->priv->cursor_location = *area;
373
374   if (slave)
375     gtk_im_context_set_cursor_location (slave, area);
376 }
377
378 static void
379 gtk_im_multicontext_set_use_preedit (GtkIMContext   *context,
380                                      gboolean       use_preedit)
381 {
382   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
383   GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
384
385   use_preedit = use_preedit != FALSE;
386
387   multicontext->priv->use_preedit = use_preedit;
388
389   if (slave)
390     gtk_im_context_set_use_preedit (slave, use_preedit);
391 }
392
393 static gboolean
394 gtk_im_multicontext_get_surrounding (GtkIMContext  *context,
395                                      gchar        **text,
396                                      gint          *cursor_index)
397 {
398   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
399   GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
400
401   if (slave)
402     return gtk_im_context_get_surrounding (slave, text, cursor_index);
403   else
404     {
405       if (text)
406         *text = NULL;
407       if (cursor_index)
408         *cursor_index = 0;
409
410       return FALSE;
411     }
412 }
413
414 static void
415 gtk_im_multicontext_set_surrounding (GtkIMContext *context,
416                                      const char   *text,
417                                      gint          len,
418                                      gint          cursor_index)
419 {
420   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
421   GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
422
423   if (slave)
424     gtk_im_context_set_surrounding (slave, text, len, cursor_index);
425 }
426
427 static void
428 gtk_im_multicontext_preedit_start_cb   (GtkIMContext      *slave,
429                                         GtkIMMulticontext *multicontext)
430 {
431   g_signal_emit_by_name (multicontext, "preedit_start");
432 }
433
434 static void
435 gtk_im_multicontext_preedit_end_cb (GtkIMContext      *slave,
436                                     GtkIMMulticontext *multicontext)
437 {
438   g_signal_emit_by_name (multicontext, "preedit_end");
439 }
440
441 static void
442 gtk_im_multicontext_preedit_changed_cb (GtkIMContext      *slave,
443                                         GtkIMMulticontext *multicontext)
444 {
445   g_signal_emit_by_name (multicontext, "preedit_changed");
446 }
447
448 static void
449 gtk_im_multicontext_commit_cb (GtkIMContext      *slave,
450                                const gchar       *str,
451                                GtkIMMulticontext *multicontext)
452 {
453   g_signal_emit_by_name (multicontext, "commit", str);;
454 }
455
456 static gboolean
457 gtk_im_multicontext_retrieve_surrounding_cb (GtkIMContext      *slave,
458                                              GtkIMMulticontext *multicontext)
459 {
460   gboolean result;
461   
462   g_signal_emit_by_name (multicontext, "retrieve_surrounding", &result);
463
464   return result;
465 }
466
467 static gboolean
468 gtk_im_multicontext_delete_surrounding_cb (GtkIMContext      *slave,
469                                            gint               offset,
470                                            gint               n_chars,
471                                            GtkIMMulticontext *multicontext)
472 {
473   gboolean result;
474   
475   g_signal_emit_by_name (multicontext, "delete_surrounding",
476                          offset, n_chars, &result);
477
478   return result;
479 }
480
481 static void
482 activate_cb (GtkWidget         *menuitem,
483              GtkIMMulticontext *context)
484 {
485   if (GTK_CHECK_MENU_ITEM (menuitem)->active)
486     {
487       const gchar *id = g_object_get_data (G_OBJECT (menuitem), "gtk-context-id");
488
489       gtk_im_context_reset (GTK_IM_CONTEXT (context));
490       
491       global_context_id = id;
492       gtk_im_multicontext_set_slave (context, NULL, FALSE);
493     }
494 }
495
496 /**
497  * gtk_im_multicontext_append_menuitems:
498  * @context: a #GtkIMMultiContext
499  * @menushell: a #GtkMenuShell
500  * 
501  * Add menuitems for various available input methods to a menu;
502  * the menuitems, when selected, will switch the input method
503  * for the context and the global default input method.
504  **/
505 void
506 gtk_im_multicontext_append_menuitems (GtkIMMulticontext *context,
507                                       GtkMenuShell      *menushell)
508 {
509   const GtkIMContextInfo **contexts;
510   guint n_contexts, i;
511   GSList *group = NULL;
512   
513   _gtk_im_module_list (&contexts, &n_contexts);
514
515   for (i=0; i < n_contexts; i++)
516     {
517       GtkWidget *menuitem;
518       const gchar *translated_name;
519 #ifdef ENABLE_NLS
520       if (contexts[i]->domain && contexts[i]->domain_dirname &&
521           contexts[i]->domain[0] && contexts[i]->domain_dirname[0])
522         {
523           if (strcmp (contexts[i]->domain, GETTEXT_PACKAGE) == 0 &&
524               strcmp (contexts[i]->domain_dirname, GTK_LOCALEDIR) == 0)
525             /* Input method may have a name in the GTK+ message catalog */
526             translated_name = _(contexts[i]->context_name);
527           else
528             /* Input method has own message catalog */
529             {
530               bindtextdomain (contexts[i]->domain,
531                               contexts[i]->domain_dirname);
532 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
533               bind_textdomain_codeset (contexts[i]->domain, "UTF-8");
534 #endif
535               translated_name = dgettext (contexts[i]->domain, contexts[i]->context_name);
536             }
537         }
538       else
539         /* Either domain or domain_dirname is NULL or "". We assume that
540          * input method does not want a translated name in this case
541          */
542         translated_name = contexts[i]->context_name;
543 #else
544       translated_name = contexts[i]->context_name;
545 #endif
546       menuitem = gtk_radio_menu_item_new_with_label (group,
547                                                      translated_name);
548       
549       if ((global_context_id == NULL && group == NULL) ||
550           (global_context_id &&
551            strcmp (contexts[i]->context_id, global_context_id) == 0))
552         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem),
553                                         TRUE);
554       
555       group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (menuitem));
556       
557       g_object_set_data (G_OBJECT (menuitem), "gtk-context-id",
558                          (char *)contexts[i]->context_id);
559       g_signal_connect (menuitem, "activate",
560                         G_CALLBACK (activate_cb), context);
561
562       gtk_widget_show (menuitem);
563       gtk_menu_shell_append (menushell, menuitem);
564     }
565
566   g_free (contexts);
567 }
568