]> Pileus Git - ~andy/gtk/blob - gtk/gtkimmulticontext.c
fc40b5f1fba66878d82a8a6b9aa5ed1f873aeec3
[~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 <string.h>
21
22 #ifdef GDK_WINDOWING_X11
23 #include <X11/Xlocale.h>        /* so we get the right setlocale */
24 #else
25 #include <locale.h>
26 #endif
27
28 #include "gtksignal.h"
29 #include "gtkimmulticontext.h"
30 #include "gtkimmodule.h"
31 #include "gtkmenuitem.h"
32
33 static void     gtk_im_multicontext_class_init         (GtkIMMulticontextClass  *class);
34 static void     gtk_im_multicontext_init               (GtkIMMulticontext       *im_multicontext);
35 static void     gtk_im_multicontext_finalize           (GObject                 *object);
36
37 static void     gtk_im_multicontext_set_slave          (GtkIMMulticontext       *multicontext,
38                                                         GtkIMContext            *slave);
39
40 static void     gtk_im_multicontext_set_client_window  (GtkIMContext            *context,
41                                                         GdkWindow               *window);
42 static void     gtk_im_multicontext_get_preedit_string (GtkIMContext            *context,
43                                                         gchar                  **str,
44                                                         PangoAttrList          **attrs,
45                                                         gint                   *cursor_pos);
46 static gboolean gtk_im_multicontext_filter_keypress    (GtkIMContext            *context,
47                                                         GdkEventKey             *event);
48 static void     gtk_im_multicontext_focus_in           (GtkIMContext            *context);
49 static void     gtk_im_multicontext_focus_out          (GtkIMContext            *context);
50 static void     gtk_im_multicontext_reset              (GtkIMContext            *context);
51 static void     gtk_im_multicontext_set_cursor_pos     (GtkIMContext            *context,
52                                                         GdkRectangle            *area);
53
54 void            gtk_im_multicontext_preedit_start_cb   (GtkIMContext            *slave,
55                                                         GtkIMMulticontext       *multicontext);
56 void            gtk_im_multicontext_preedit_end_cb     (GtkIMContext            *slave,
57                                                         GtkIMMulticontext       *multicontext);
58 void            gtk_im_multicontext_preedit_changed_cb (GtkIMContext            *slave,
59                                                         GtkIMMulticontext       *multicontext);
60 void            gtk_im_multicontext_commit_cb          (GtkIMContext            *slave,
61                                                         const gchar             *str,
62                                                         GtkIMMulticontext       *multicontext);
63
64 static GtkIMContextClass *parent_class;
65
66 static const gchar *global_context_id = NULL;
67
68 GtkType
69 gtk_im_multicontext_get_type (void)
70 {
71   static GtkType im_multicontext_type = 0;
72
73   if (!im_multicontext_type)
74     {
75       static const GtkTypeInfo im_multicontext_info =
76       {
77         "GtkIMMulticontext",
78         sizeof (GtkIMMulticontext),
79         sizeof (GtkIMMulticontextClass),
80         (GtkClassInitFunc) gtk_im_multicontext_class_init,
81         (GtkObjectInitFunc) gtk_im_multicontext_init,
82         /* reserved_1 */ NULL,
83         /* reserved_2 */ NULL,
84         (GtkClassInitFunc) NULL,
85       };
86
87       im_multicontext_type = gtk_type_unique (GTK_TYPE_IM_CONTEXT, &im_multicontext_info);
88     }
89
90   return im_multicontext_type;
91 }
92
93 static void
94 gtk_im_multicontext_class_init (GtkIMMulticontextClass *class)
95 {
96   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
97   GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
98   
99   parent_class = g_type_class_peek_parent (class);
100
101   im_context_class->set_client_window = gtk_im_multicontext_set_client_window;
102   im_context_class->get_preedit_string = gtk_im_multicontext_get_preedit_string;
103   im_context_class->filter_keypress = gtk_im_multicontext_filter_keypress;
104   im_context_class->focus_in = gtk_im_multicontext_focus_in;
105   im_context_class->focus_out = gtk_im_multicontext_focus_out;
106   im_context_class->reset = gtk_im_multicontext_reset;
107   im_context_class->set_cursor_pos = gtk_im_multicontext_set_cursor_pos;
108
109   gobject_class->finalize = gtk_im_multicontext_finalize;
110 }
111
112 static void
113 gtk_im_multicontext_init (GtkIMMulticontext *multicontext)
114 {
115   multicontext->slave = NULL;
116 }
117
118 GtkIMContext *
119 gtk_im_multicontext_new (void)
120 {
121   return GTK_IM_CONTEXT (gtk_type_new (GTK_TYPE_IM_MULTICONTEXT));
122 }
123
124 static void
125 gtk_im_multicontext_finalize (GObject *object)
126 {
127   gtk_im_multicontext_set_slave (GTK_IM_MULTICONTEXT (object), NULL);
128
129   G_OBJECT_CLASS (parent_class)->finalize (object);
130 }
131
132 static void
133 gtk_im_multicontext_set_slave (GtkIMMulticontext *multicontext,
134                                GtkIMContext      *slave)
135 {
136   if (multicontext->slave)
137     {
138       gtk_signal_disconnect_by_data (GTK_OBJECT (multicontext->slave), multicontext);
139       gtk_object_unref (GTK_OBJECT (multicontext->slave));
140     }
141   
142   multicontext->slave = slave;
143
144   if (multicontext->slave)
145     {
146       gtk_object_ref (GTK_OBJECT (multicontext->slave));
147       gtk_object_sink (GTK_OBJECT (multicontext->slave));
148
149       gtk_signal_connect (GTK_OBJECT (multicontext->slave), "preedit_start",
150                           GTK_SIGNAL_FUNC (gtk_im_multicontext_preedit_start_cb),
151                           multicontext);
152       gtk_signal_connect (GTK_OBJECT (multicontext->slave), "preedit_end",
153                           GTK_SIGNAL_FUNC (gtk_im_multicontext_preedit_end_cb),
154                           multicontext);
155       gtk_signal_connect (GTK_OBJECT (multicontext->slave), "preedit_changed",
156                           GTK_SIGNAL_FUNC (gtk_im_multicontext_preedit_changed_cb),
157                           multicontext);
158       gtk_signal_connect (GTK_OBJECT (multicontext->slave), "commit",
159                           GTK_SIGNAL_FUNC (gtk_im_multicontext_commit_cb),
160                           multicontext);
161
162       if (multicontext->client_window)
163         gtk_im_context_set_client_window (slave, multicontext->client_window);
164     }
165 }
166
167 static GtkIMContext *
168 gtk_im_multicontext_get_slave (GtkIMMulticontext *multicontext)
169 {
170   if (!multicontext->slave)
171     {
172       if (!global_context_id)
173         {
174           const char *locale;
175           
176 #ifdef HAVE_LC_MESSAGES
177           locale = setlocale (LC_MESSAGES, NULL);
178 #else
179           locale = setlocale (LC_CTYPE, NULL);
180 #endif
181           global_context_id = _gtk_im_module_get_default_context_id (locale);
182         }
183         
184       gtk_im_multicontext_set_slave (multicontext, _gtk_im_module_create (global_context_id));
185       multicontext->context_id = global_context_id;
186     }
187
188   return multicontext->slave;
189 }
190
191 static void
192 gtk_im_multicontext_set_client_window (GtkIMContext *context,
193                                        GdkWindow    *window)
194 {
195   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
196   GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
197
198   multicontext->client_window = window;
199   
200   if (slave)
201     gtk_im_context_set_client_window (slave, window);
202 }
203
204 static void
205 gtk_im_multicontext_get_preedit_string (GtkIMContext   *context,
206                                         gchar         **str,
207                                         PangoAttrList **attrs,
208                                         gint           *cursor_pos)
209 {
210   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
211   GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
212
213   if (slave)
214     gtk_im_context_get_preedit_string (slave, str, attrs, cursor_pos);
215   else
216     {
217       if (str)
218         *str = g_strdup ("");
219       if (attrs)
220         *attrs = pango_attr_list_new ();
221     }
222 }
223
224 static gboolean
225 gtk_im_multicontext_filter_keypress (GtkIMContext *context,
226                                      GdkEventKey  *event)
227 {
228   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
229   GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
230
231   if (slave)
232     return gtk_im_context_filter_keypress (slave, event);
233   else
234     return FALSE;
235 }
236
237 static void
238 gtk_im_multicontext_focus_in (GtkIMContext   *context)
239 {
240   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
241   GtkIMContext *slave;
242
243   /* If the global context type is different from the context we were
244    * using before, get rid of the old slave and create a new one
245    * for the new global context type.
246    */
247   if (!multicontext->context_id ||
248       strcmp (global_context_id, multicontext->context_id) != 0)
249     gtk_im_multicontext_set_slave (multicontext, NULL);
250
251   slave = gtk_im_multicontext_get_slave (multicontext);
252
253   if (slave)
254     gtk_im_context_focus_in (slave);
255 }
256
257 static void
258 gtk_im_multicontext_focus_out (GtkIMContext   *context)
259 {
260   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
261   GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
262
263   if (slave)
264     gtk_im_context_focus_out (slave);
265 }
266
267 static void
268 gtk_im_multicontext_reset (GtkIMContext   *context)
269 {
270   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
271   GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
272
273   if (slave)
274     gtk_im_context_reset (slave);
275 }
276
277 static void
278 gtk_im_multicontext_set_cursor_pos (GtkIMContext   *context,
279                                     GdkRectangle   *area)
280 {
281   GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
282   GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
283
284   if (slave)
285     gtk_im_context_set_cursor_pos (slave, area);
286 }
287
288 void
289 gtk_im_multicontext_preedit_start_cb   (GtkIMContext      *slave,
290                                         GtkIMMulticontext *multicontext)
291 {
292   gtk_signal_emit_by_name (GTK_OBJECT (multicontext), "preedit_start");
293 }
294
295 void
296 gtk_im_multicontext_preedit_end_cb (GtkIMContext      *slave,
297                                     GtkIMMulticontext *multicontext)
298 {
299   gtk_signal_emit_by_name (GTK_OBJECT (multicontext), "preedit_end");
300 }
301
302 void
303 gtk_im_multicontext_preedit_changed_cb (GtkIMContext      *slave,
304                                         GtkIMMulticontext *multicontext)
305 {
306   gtk_signal_emit_by_name (GTK_OBJECT (multicontext), "preedit_changed");
307 }
308
309 void
310 gtk_im_multicontext_commit_cb (GtkIMContext      *slave,
311                                const gchar       *str,
312                                GtkIMMulticontext *multicontext)
313 {
314   gtk_signal_emit_by_name (GTK_OBJECT (multicontext), "commit", str);;
315 }
316
317 static void
318 activate_cb (GtkWidget         *menuitem,
319              GtkIMMulticontext *context)
320 {
321   const gchar *id = gtk_object_get_data (GTK_OBJECT (menuitem), "gtk-context-id");
322
323   gtk_im_context_reset (GTK_IM_CONTEXT (context));
324
325   global_context_id = id;
326   gtk_im_multicontext_set_slave (context, NULL);
327 }
328
329 /**
330  * gtk_im_multicontext_append_menuitems:
331  * @context: a #GtkIMMultiContext
332  * @menushell: a #GtkMenuShell
333  * 
334  * Add menuitems for various available input methods to a menu;
335  * the menuitems, when selected, will switch the input method
336  * for the context and the global default input method.
337  **/
338 void
339 gtk_im_multicontext_append_menuitems (GtkIMMulticontext *context,
340                                       GtkMenuShell      *menushell)
341 {
342   const GtkIMContextInfo **contexts;
343   gint n_contexts, i;
344   
345   _gtk_im_module_list (&contexts, &n_contexts);
346
347   for (i=0; i < n_contexts; i++)
348     {
349       GtkWidget *menuitem;
350
351       menuitem = gtk_menu_item_new_with_label (contexts[i]->context_name);
352
353       gtk_object_set_data (GTK_OBJECT (menuitem), "gtk-context-id",
354                            (char *)contexts[i]->context_id);
355       gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
356                           activate_cb, context);
357
358       gtk_widget_show (menuitem);
359       gtk_menu_shell_append (menushell, menuitem);
360     }
361 }
362