1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2000 Red Hat, Inc.
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.
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.
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.
25 #include "gtkimmulticontext.h"
26 #include "gtkimmodule.h"
28 #include "gtkradiomenuitem.h"
30 #include "gtkprivate.h" /* To get redefinition of GTK_LOCALE_DIR on Win32 */
33 struct _GtkIMMulticontextPrivate
35 GdkWindow *client_window;
36 GdkRectangle cursor_location;
38 guint use_preedit : 1;
39 guint have_cursor_location : 1;
43 static void gtk_im_multicontext_finalize (GObject *object);
45 static void gtk_im_multicontext_set_slave (GtkIMMulticontext *multicontext,
49 static void gtk_im_multicontext_set_client_window (GtkIMContext *context,
51 static void gtk_im_multicontext_get_preedit_string (GtkIMContext *context,
53 PangoAttrList **attrs,
55 static gboolean gtk_im_multicontext_filter_keypress (GtkIMContext *context,
57 static void gtk_im_multicontext_focus_in (GtkIMContext *context);
58 static void gtk_im_multicontext_focus_out (GtkIMContext *context);
59 static void gtk_im_multicontext_reset (GtkIMContext *context);
60 static void gtk_im_multicontext_set_cursor_location (GtkIMContext *context,
62 static void gtk_im_multicontext_set_use_preedit (GtkIMContext *context,
63 gboolean use_preedit);
64 static gboolean gtk_im_multicontext_get_surrounding (GtkIMContext *context,
67 static void gtk_im_multicontext_set_surrounding (GtkIMContext *context,
72 static void gtk_im_multicontext_preedit_start_cb (GtkIMContext *slave,
73 GtkIMMulticontext *multicontext);
74 static void gtk_im_multicontext_preedit_end_cb (GtkIMContext *slave,
75 GtkIMMulticontext *multicontext);
76 static void gtk_im_multicontext_preedit_changed_cb (GtkIMContext *slave,
77 GtkIMMulticontext *multicontext);
78 static void gtk_im_multicontext_commit_cb (GtkIMContext *slave,
80 GtkIMMulticontext *multicontext);
81 static gboolean gtk_im_multicontext_retrieve_surrounding_cb (GtkIMContext *slave,
82 GtkIMMulticontext *multicontext);
83 static gboolean gtk_im_multicontext_delete_surrounding_cb (GtkIMContext *slave,
86 GtkIMMulticontext *multicontext);
88 static const gchar *global_context_id = NULL;
90 G_DEFINE_TYPE (GtkIMMulticontext, gtk_im_multicontext, GTK_TYPE_IM_CONTEXT)
93 gtk_im_multicontext_class_init (GtkIMMulticontextClass *class)
95 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
96 GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
98 im_context_class->set_client_window = gtk_im_multicontext_set_client_window;
99 im_context_class->get_preedit_string = gtk_im_multicontext_get_preedit_string;
100 im_context_class->filter_keypress = gtk_im_multicontext_filter_keypress;
101 im_context_class->focus_in = gtk_im_multicontext_focus_in;
102 im_context_class->focus_out = gtk_im_multicontext_focus_out;
103 im_context_class->reset = gtk_im_multicontext_reset;
104 im_context_class->set_cursor_location = gtk_im_multicontext_set_cursor_location;
105 im_context_class->set_use_preedit = gtk_im_multicontext_set_use_preedit;
106 im_context_class->set_surrounding = gtk_im_multicontext_set_surrounding;
107 im_context_class->get_surrounding = gtk_im_multicontext_get_surrounding;
109 gobject_class->finalize = gtk_im_multicontext_finalize;
111 g_type_class_add_private (gobject_class, sizeof (GtkIMMulticontextPrivate));
115 gtk_im_multicontext_init (GtkIMMulticontext *multicontext)
117 multicontext->slave = NULL;
119 multicontext->priv = G_TYPE_INSTANCE_GET_PRIVATE (multicontext, GTK_TYPE_IM_MULTICONTEXT, GtkIMMulticontextPrivate);
120 multicontext->priv->use_preedit = TRUE;
121 multicontext->priv->have_cursor_location = FALSE;
122 multicontext->priv->focus_in = FALSE;
126 * gtk_im_multicontext_new:
128 * Creates a new #GtkIMMulticontext.
130 * Returns: a new #GtkIMMulticontext.
133 gtk_im_multicontext_new (void)
135 return g_object_new (GTK_TYPE_IM_MULTICONTEXT, NULL);
139 gtk_im_multicontext_finalize (GObject *object)
141 GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (object);
143 gtk_im_multicontext_set_slave (multicontext, NULL, TRUE);
145 G_OBJECT_CLASS (gtk_im_multicontext_parent_class)->finalize (object);
149 gtk_im_multicontext_set_slave (GtkIMMulticontext *multicontext,
153 GtkIMMulticontextPrivate *priv = multicontext->priv;
154 gboolean need_preedit_changed = FALSE;
156 if (multicontext->slave)
159 gtk_im_context_reset (multicontext->slave);
161 g_signal_handlers_disconnect_by_func (multicontext->slave,
162 gtk_im_multicontext_preedit_start_cb,
164 g_signal_handlers_disconnect_by_func (multicontext->slave,
165 gtk_im_multicontext_preedit_end_cb,
167 g_signal_handlers_disconnect_by_func (multicontext->slave,
168 gtk_im_multicontext_preedit_changed_cb,
170 g_signal_handlers_disconnect_by_func (multicontext->slave,
171 gtk_im_multicontext_commit_cb,
174 g_object_unref (multicontext->slave);
175 multicontext->slave = NULL;
178 need_preedit_changed = TRUE;
181 multicontext->slave = slave;
183 if (multicontext->slave)
185 g_object_ref (multicontext->slave);
187 g_signal_connect (multicontext->slave, "preedit_start",
188 G_CALLBACK (gtk_im_multicontext_preedit_start_cb),
190 g_signal_connect (multicontext->slave, "preedit_end",
191 G_CALLBACK (gtk_im_multicontext_preedit_end_cb),
193 g_signal_connect (multicontext->slave, "preedit_changed",
194 G_CALLBACK (gtk_im_multicontext_preedit_changed_cb),
196 g_signal_connect (multicontext->slave, "commit",
197 G_CALLBACK (gtk_im_multicontext_commit_cb),
199 g_signal_connect (multicontext->slave, "retrieve_surrounding",
200 G_CALLBACK (gtk_im_multicontext_retrieve_surrounding_cb),
202 g_signal_connect (multicontext->slave, "delete_surrounding",
203 G_CALLBACK (gtk_im_multicontext_delete_surrounding_cb),
206 if (!priv->use_preedit) /* Default is TRUE */
207 gtk_im_context_set_use_preedit (slave, FALSE);
208 if (priv->client_window)
209 gtk_im_context_set_client_window (slave, priv->client_window);
210 if (priv->have_cursor_location)
211 gtk_im_context_set_cursor_location (slave, &priv->cursor_location);
213 gtk_im_context_focus_in (slave);
216 if (need_preedit_changed)
217 g_signal_emit_by_name (multicontext, "preedit_changed");
220 static GtkIMContext *
221 gtk_im_multicontext_get_slave (GtkIMMulticontext *multicontext)
223 if (!multicontext->slave)
227 if (!global_context_id)
229 gchar *locale = _gtk_get_lc_ctype ();
230 global_context_id = _gtk_im_module_get_default_context_id (locale);
234 slave = _gtk_im_module_create (global_context_id);
235 gtk_im_multicontext_set_slave (multicontext, slave, FALSE);
236 g_object_unref (slave);
238 multicontext->context_id = global_context_id;
241 return multicontext->slave;
245 gtk_im_multicontext_set_client_window (GtkIMContext *context,
248 GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
250 GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
252 multicontext->priv->client_window = window;
255 gtk_im_context_set_client_window (slave, window);
259 gtk_im_multicontext_get_preedit_string (GtkIMContext *context,
261 PangoAttrList **attrs,
264 GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
265 GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
268 gtk_im_context_get_preedit_string (slave, str, attrs, cursor_pos);
272 *str = g_strdup ("");
274 *attrs = pango_attr_list_new ();
279 gtk_im_multicontext_filter_keypress (GtkIMContext *context,
282 GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
283 GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
286 return gtk_im_context_filter_keypress (slave, event);
292 gtk_im_multicontext_focus_in (GtkIMContext *context)
294 GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
297 /* If the global context type is different from the context we were
298 * using before, get rid of the old slave and create a new one
299 * for the new global context type.
301 if (!multicontext->context_id ||
302 strcmp (global_context_id, multicontext->context_id) != 0)
303 gtk_im_multicontext_set_slave (multicontext, NULL, FALSE);
305 slave = gtk_im_multicontext_get_slave (multicontext);
307 multicontext->priv->focus_in = TRUE;
310 gtk_im_context_focus_in (slave);
314 gtk_im_multicontext_focus_out (GtkIMContext *context)
316 GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
317 GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
319 multicontext->priv->focus_in = FALSE;
322 gtk_im_context_focus_out (slave);
326 gtk_im_multicontext_reset (GtkIMContext *context)
328 GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
329 GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
332 gtk_im_context_reset (slave);
336 gtk_im_multicontext_set_cursor_location (GtkIMContext *context,
339 GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
340 GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
342 multicontext->priv->have_cursor_location = TRUE;
343 multicontext->priv->cursor_location = *area;
346 gtk_im_context_set_cursor_location (slave, area);
350 gtk_im_multicontext_set_use_preedit (GtkIMContext *context,
351 gboolean use_preedit)
353 GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
354 GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
356 use_preedit = use_preedit != FALSE;
358 multicontext->priv->use_preedit = use_preedit;
361 gtk_im_context_set_use_preedit (slave, use_preedit);
365 gtk_im_multicontext_get_surrounding (GtkIMContext *context,
369 GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
370 GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
373 return gtk_im_context_get_surrounding (slave, text, cursor_index);
386 gtk_im_multicontext_set_surrounding (GtkIMContext *context,
391 GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
392 GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
395 gtk_im_context_set_surrounding (slave, text, len, cursor_index);
399 gtk_im_multicontext_preedit_start_cb (GtkIMContext *slave,
400 GtkIMMulticontext *multicontext)
402 g_signal_emit_by_name (multicontext, "preedit_start");
406 gtk_im_multicontext_preedit_end_cb (GtkIMContext *slave,
407 GtkIMMulticontext *multicontext)
409 g_signal_emit_by_name (multicontext, "preedit_end");
413 gtk_im_multicontext_preedit_changed_cb (GtkIMContext *slave,
414 GtkIMMulticontext *multicontext)
416 g_signal_emit_by_name (multicontext, "preedit_changed");
420 gtk_im_multicontext_commit_cb (GtkIMContext *slave,
422 GtkIMMulticontext *multicontext)
424 g_signal_emit_by_name (multicontext, "commit", str);;
428 gtk_im_multicontext_retrieve_surrounding_cb (GtkIMContext *slave,
429 GtkIMMulticontext *multicontext)
433 g_signal_emit_by_name (multicontext, "retrieve_surrounding", &result);
439 gtk_im_multicontext_delete_surrounding_cb (GtkIMContext *slave,
442 GtkIMMulticontext *multicontext)
446 g_signal_emit_by_name (multicontext, "delete_surrounding",
447 offset, n_chars, &result);
453 activate_cb (GtkWidget *menuitem,
454 GtkIMMulticontext *context)
456 if (GTK_CHECK_MENU_ITEM (menuitem)->active)
458 const gchar *id = g_object_get_data (G_OBJECT (menuitem), "gtk-context-id");
460 gtk_im_context_reset (GTK_IM_CONTEXT (context));
462 global_context_id = id;
463 gtk_im_multicontext_set_slave (context, NULL, FALSE);
468 pathnamecmp (const char *a,
472 return strcmp (a, b);
474 /* Ignore case insensitivity, probably not that relevant here. Just
475 * make sure slash and backslash compare equal.
478 if ((G_IS_DIR_SEPARATOR (*a) && G_IS_DIR_SEPARATOR (*b)) ||
488 * gtk_im_multicontext_append_menuitems:
489 * @context: a #GtkIMMultiContext
490 * @menushell: a #GtkMenuShell
492 * Add menuitems for various available input methods to a menu;
493 * the menuitems, when selected, will switch the input method
494 * for the context and the global default input method.
497 gtk_im_multicontext_append_menuitems (GtkIMMulticontext *context,
498 GtkMenuShell *menushell)
500 const GtkIMContextInfo **contexts;
502 GSList *group = NULL;
504 _gtk_im_module_list (&contexts, &n_contexts);
506 for (i=0; i < n_contexts; i++)
509 const gchar *translated_name;
511 if (contexts[i]->domain && contexts[i]->domain[0])
513 if (strcmp (contexts[i]->domain, GETTEXT_PACKAGE) == 0)
515 /* Same translation domain as GTK+ */
516 if (!(contexts[i]->domain_dirname && contexts[i]->domain_dirname[0]) ||
517 pathnamecmp (contexts[i]->domain_dirname, GTK_LOCALEDIR) == 0)
519 /* Empty or NULL, domain directory, or same as
520 * GTK+. Input method may have a name in the GTK+
523 translated_name = _(contexts[i]->context_name);
527 /* Separate domain directory but the same
528 * translation domain as GTK+. We can't call
529 * bindtextdomain() as that would make GTK+ forget
532 g_warning ("Input method %s should not use GTK's translation domain %s",
533 contexts[i]->context_id, GETTEXT_PACKAGE);
534 /* Try translating the name in GTK+'s domain */
535 translated_name = _(contexts[i]->context_name);
538 else if (contexts[i]->domain_dirname && contexts[i]->domain_dirname[0])
539 /* Input method has own translation domain and message catalog */
541 bindtextdomain (contexts[i]->domain,
542 contexts[i]->domain_dirname);
543 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
544 bind_textdomain_codeset (contexts[i]->domain, "UTF-8");
546 translated_name = dgettext (contexts[i]->domain, contexts[i]->context_name);
550 /* Different translation domain, but no domain directory */
551 translated_name = contexts[i]->context_name;
555 /* Empty or NULL domain. We assume that input method does not
556 * want a translated name in this case.
558 translated_name = contexts[i]->context_name;
560 translated_name = contexts[i]->context_name;
562 menuitem = gtk_radio_menu_item_new_with_label (group,
565 if ((global_context_id == NULL && group == NULL) ||
566 (global_context_id &&
567 strcmp (contexts[i]->context_id, global_context_id) == 0))
568 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem),
571 group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (menuitem));
573 g_object_set_data (G_OBJECT (menuitem), I_("gtk-context-id"),
574 (char *)contexts[i]->context_id);
575 g_signal_connect (menuitem, "activate",
576 G_CALLBACK (activate_cb), context);
578 gtk_widget_show (menuitem);
579 gtk_menu_shell_append (menushell, menuitem);
585 #define __GTK_IM_MULTICONTEXT_C__
586 #include "gtkaliasdef.c"