1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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, see <http://www.gnu.org/licenses/>.Free
19 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
32 #include <glib/gstdio.h>
34 #include "gtkimmoduleprivate.h"
35 #include "gtkimcontextsimple.h"
36 #include "gtksettings.h"
37 #include "gtkprivate.h"
41 #undef GDK_DEPRECATED_FOR
42 #define GDK_DEPRECATED
43 #define GDK_DEPRECATED_FOR(f)
45 #include "deprecated/gtkrc.h"
47 #define SIMPLE_ID "gtk-im-context-simple"
51 * @context_id: The unique identification string of the input method.
52 * @context_name: The human-readable name of the input method.
53 * @domain: Translation domain to be used with dgettext()
54 * @domain_dirname: Name of locale directory for use with bindtextdomain()
55 * @default_locales: A colon-separated list of locales where this input method
56 * should be the default. The asterisk "*" sets the default for all locales.
58 * Bookkeeping information about a loadable input method.
61 typedef struct _GtkIMModule GtkIMModule;
62 typedef struct _GtkIMModuleClass GtkIMModuleClass;
64 #define GTK_TYPE_IM_MODULE (gtk_im_module_get_type ())
65 #define GTK_IM_MODULE(im_module) (G_TYPE_CHECK_INSTANCE_CAST ((im_module), GTK_TYPE_IM_MODULE, GtkIMModule))
66 #define GTK_IS_IM_MODULE(im_module) (G_TYPE_CHECK_INSTANCE_TYPE ((im_module), GTK_TYPE_IM_MODULE))
70 GTypeModule parent_instance;
76 void (*list) (const GtkIMContextInfo ***contexts,
78 void (*init) (GTypeModule *module);
80 GtkIMContext *(*create) (const gchar *context_id);
82 GtkIMContextInfo **contexts;
88 struct _GtkIMModuleClass
90 GTypeModuleClass parent_class;
93 static GType gtk_im_module_get_type (void);
95 static gint n_loaded_contexts = 0;
96 static GHashTable *contexts_hash = NULL;
97 static GSList *modules_list = NULL;
99 static GObjectClass *parent_class = NULL;
102 gtk_im_module_load (GTypeModule *module)
104 GtkIMModule *im_module = GTK_IM_MODULE (module);
106 if (!im_module->builtin)
108 im_module->library = g_module_open (im_module->path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
109 if (!im_module->library)
111 g_warning ("%s", g_module_error());
115 /* extract symbols from the lib */
116 if (!g_module_symbol (im_module->library, "im_module_init",
117 (gpointer *)&im_module->init) ||
118 !g_module_symbol (im_module->library, "im_module_exit",
119 (gpointer *)&im_module->exit) ||
120 !g_module_symbol (im_module->library, "im_module_list",
121 (gpointer *)&im_module->list) ||
122 !g_module_symbol (im_module->library, "im_module_create",
123 (gpointer *)&im_module->create))
125 g_warning ("%s", g_module_error());
126 g_module_close (im_module->library);
132 /* call the module's init function to let it */
133 /* setup anything it needs to set up. */
134 im_module->init (module);
140 gtk_im_module_unload (GTypeModule *module)
142 GtkIMModule *im_module = GTK_IM_MODULE (module);
146 if (!im_module->builtin)
148 g_module_close (im_module->library);
149 im_module->library = NULL;
151 im_module->init = NULL;
152 im_module->exit = NULL;
153 im_module->list = NULL;
154 im_module->create = NULL;
158 /* This only will ever be called if an error occurs during
162 gtk_im_module_finalize (GObject *object)
164 GtkIMModule *module = GTK_IM_MODULE (object);
166 g_free (module->path);
168 parent_class->finalize (object);
171 G_DEFINE_TYPE (GtkIMModule, gtk_im_module, G_TYPE_TYPE_MODULE)
174 gtk_im_module_class_init (GtkIMModuleClass *class)
176 GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class);
177 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
179 parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (class));
181 module_class->load = gtk_im_module_load;
182 module_class->unload = gtk_im_module_unload;
184 gobject_class->finalize = gtk_im_module_finalize;
188 gtk_im_module_init (GtkIMModule* object)
193 free_info (GtkIMContextInfo *info)
195 g_free ((char *)info->context_id);
196 g_free ((char *)info->context_name);
197 g_free ((char *)info->domain);
198 g_free ((char *)info->domain_dirname);
199 g_free ((char *)info->default_locales);
204 add_module (GtkIMModule *module, GSList *infos)
206 GSList *tmp_list = infos;
208 gint n = g_slist_length (infos);
209 module->contexts = g_new (GtkIMContextInfo *, n);
213 GtkIMContextInfo *info = tmp_list->data;
215 if (g_hash_table_lookup (contexts_hash, info->context_id))
217 free_info (info); /* Duplicate */
221 g_hash_table_insert (contexts_hash, (char *)info->context_id, module);
222 module->contexts[i++] = tmp_list->data;
226 tmp_list = tmp_list->next;
228 g_slist_free (infos);
229 module->n_contexts = i;
231 modules_list = g_slist_prepend (modules_list, module);
237 correct_libdir_prefix (gchar **path)
239 /* GTK_LIBDIR is the build-time libdir */
240 if (strncmp (*path, GTK_LIBDIR, strlen (GTK_LIBDIR)) == 0)
242 /* This is an entry put there by make install on the
243 * packager's system. On Windows a prebuilt GTK+
244 * package can be installed in a random
245 * location. The gtk.immodules file distributed in
246 * such a package contains paths from the package
247 * builder's machine. Replace the path with the real
248 * one on this machine.
251 *path = g_strconcat (_gtk_get_libdir (), tem + strlen (GTK_LIBDIR), NULL);
257 correct_localedir_prefix (gchar **path)
260 if (strncmp (*path, GTK_LOCALEDIR, strlen (GTK_LOCALEDIR)) == 0)
263 *path = g_strconcat (_gtk_get_localedir (), tem + strlen (GTK_LOCALEDIR), NULL);
270 G_GNUC_UNUSED static GtkIMModule *
271 add_builtin_module (const gchar *module_name,
272 const GtkIMContextInfo **contexts,
275 GtkIMModule *module = g_object_new (GTK_TYPE_IM_MODULE, NULL);
276 GSList *infos = NULL;
279 for (i = 0; i < n_contexts; i++)
281 GtkIMContextInfo *info = g_new (GtkIMContextInfo, 1);
282 info->context_id = g_strdup (contexts[i]->context_id);
283 info->context_name = g_strdup (contexts[i]->context_name);
284 info->domain = g_strdup (contexts[i]->domain);
285 info->domain_dirname = g_strdup (contexts[i]->domain_dirname);
287 correct_localedir_prefix ((char **) &info->domain_dirname);
289 info->default_locales = g_strdup (contexts[i]->default_locales);
290 infos = g_slist_prepend (infos, info);
293 module->builtin = TRUE;
294 g_type_module_set_name (G_TYPE_MODULE (module), module_name);
295 add_module (module, infos);
301 gtk_im_module_initialize (void)
303 GString *line_buf = g_string_new (NULL);
304 GString *tmp_buf = g_string_new (NULL);
305 gchar *filename = gtk_rc_get_im_module_file();
307 gboolean have_error = FALSE;
309 GtkIMModule *module = NULL;
310 GSList *infos = NULL;
312 contexts_hash = g_hash_table_new (g_str_hash, g_str_equal);
314 #define do_builtin(m) \
316 const GtkIMContextInfo **contexts; \
318 extern void _gtk_immodule_ ## m ## _list (const GtkIMContextInfo ***contexts, \
319 guint *n_contexts); \
320 extern void _gtk_immodule_ ## m ## _init (GTypeModule *module); \
321 extern void _gtk_immodule_ ## m ## _exit (void); \
322 extern GtkIMContext *_gtk_immodule_ ## m ## _create (const gchar *context_id); \
324 _gtk_immodule_ ## m ## _list (&contexts, &n_contexts); \
325 module = add_builtin_module (#m, contexts, n_contexts); \
326 module->init = _gtk_immodule_ ## m ## _init; \
327 module->exit = _gtk_immodule_ ## m ## _exit; \
328 module->create = _gtk_immodule_ ## m ## _create; \
332 #ifdef INCLUDE_IM_am_et
335 #ifdef INCLUDE_IM_cedilla
336 do_builtin (cedilla);
338 #ifdef INCLUDE_IM_cyrillic_translit
339 do_builtin (cyrillic_translit);
341 #ifdef INCLUDE_IM_ime
344 #ifdef INCLUDE_IM_inuktitut
345 do_builtin (inuktitut);
347 #ifdef INCLUDE_IM_ipa
350 #ifdef INCLUDE_IM_multipress
351 do_builtin (multipress);
353 #ifdef INCLUDE_IM_thai
356 #ifdef INCLUDE_IM_ti_er
359 #ifdef INCLUDE_IM_ti_et
362 #ifdef INCLUDE_IM_viqr
365 #ifdef INCLUDE_IM_xim
371 file = g_fopen (filename, "r");
374 /* In case someone wants only the default input method,
375 * we allow no file at all.
377 g_string_free (line_buf, TRUE);
378 g_string_free (tmp_buf, TRUE);
383 while (!have_error && pango_read_line (file, line_buf))
389 if (!pango_skip_space (&p))
391 /* Blank line marking the end of a module
393 if (module && *p != '#')
395 add_module (module, infos);
405 /* Read a module location
407 module = g_object_new (GTK_TYPE_IM_MODULE, NULL);
409 if (!pango_scan_string (&p, tmp_buf) ||
410 pango_skip_space (&p))
412 g_warning ("Error parsing context info in '%s'\n %s",
413 filename, line_buf->str);
417 module->path = g_strdup (tmp_buf->str);
419 correct_libdir_prefix (&module->path);
421 g_type_module_set_name (G_TYPE_MODULE (module), module->path);
425 GtkIMContextInfo *info = g_new0 (GtkIMContextInfo, 1);
427 /* Read information about a context type
429 if (!pango_scan_string (&p, tmp_buf))
431 info->context_id = g_strdup (tmp_buf->str);
433 if (!pango_scan_string (&p, tmp_buf))
435 info->context_name = g_strdup (tmp_buf->str);
437 if (!pango_scan_string (&p, tmp_buf))
439 info->domain = g_strdup (tmp_buf->str);
441 if (!pango_scan_string (&p, tmp_buf))
443 info->domain_dirname = g_strdup (tmp_buf->str);
445 correct_localedir_prefix ((char **) &info->domain_dirname);
448 if (!pango_scan_string (&p, tmp_buf))
450 info->default_locales = g_strdup (tmp_buf->str);
452 if (pango_skip_space (&p))
455 infos = g_slist_prepend (infos, info);
459 g_warning ("Error parsing context info in '%s'\n %s",
460 filename, line_buf->str);
467 GSList *tmp_list = infos;
470 free_info (tmp_list->data);
471 tmp_list = tmp_list->next;
473 g_slist_free (infos);
475 g_object_unref (module);
478 add_module (module, infos);
481 g_string_free (line_buf, TRUE);
482 g_string_free (tmp_buf, TRUE);
487 compare_gtkimcontextinfo_name(const GtkIMContextInfo **a,
488 const GtkIMContextInfo **b)
490 return g_utf8_collate ((*a)->context_name, (*b)->context_name);
494 * _gtk_im_module_list:
495 * @contexts: location to store an array of pointers to #GtkIMContextInfo
496 * this array should be freed with g_free() when you are finished.
497 * The structures it points are statically allocated and should
498 * not be modified or freed.
499 * @n_contexts: the length of the array stored in @contexts
501 * List all available types of input method context
504 _gtk_im_module_list (const GtkIMContextInfo ***contexts,
513 GtkIMContextInfo simple_context_info = {
526 static gboolean beenhere = FALSE;
530 gtk_im_module_initialize ();
536 /* correct_localedir_prefix() requires its parameter to be a
539 simple_context_info.domain_dirname = g_strdup (simple_context_info.domain_dirname);
540 correct_localedir_prefix ((char **) &simple_context_info.domain_dirname);
545 *n_contexts = (n_loaded_contexts + 1);
552 *contexts = g_new (const GtkIMContextInfo *, n_loaded_contexts + 1);
554 (*contexts)[n++] = &simple_context_info;
556 tmp_list = modules_list;
559 GtkIMModule *module = tmp_list->data;
561 for (i=0; i<module->n_contexts; i++)
562 (*contexts)[n++] = module->contexts[i];
564 tmp_list = tmp_list->next;
567 /* fisrt element (Default) should always be at top */
568 qsort ((*contexts)+1, n-1, sizeof (GtkIMContextInfo *), (GCompareFunc)compare_gtkimcontextinfo_name);
573 * _gtk_im_module_create:
574 * @context_id: the context ID for the context type to create
576 * Create an IM context of a type specified by the string
579 * Return value: a newly created input context of or @context_id, or
580 * if that could not be created, a newly created GtkIMContextSimple.
583 _gtk_im_module_create (const gchar *context_id)
585 GtkIMModule *im_module;
586 GtkIMContext *context = NULL;
589 gtk_im_module_initialize ();
591 if (strcmp (context_id, SIMPLE_ID) != 0)
593 im_module = g_hash_table_lookup (contexts_hash, context_id);
596 g_warning ("Attempt to load unknown IM context type '%s'", context_id);
600 if (g_type_module_use (G_TYPE_MODULE (im_module)))
602 context = im_module->create (context_id);
603 g_type_module_unuse (G_TYPE_MODULE (im_module));
607 g_warning ("Loading IM context type '%s' failed", context_id);
612 return gtk_im_context_simple_new ();
617 /* Match @locale against @against.
619 * 'en_US' against 'en_US' => 4
620 * 'en_US' against 'en' => 3
621 * 'en', 'en_UK' against 'en_US' => 2
622 * all locales, against '*' => 1
625 match_locale (const gchar *locale,
626 const gchar *against,
629 if (strcmp (against, "*") == 0)
632 if (g_ascii_strcasecmp (locale, against) == 0)
635 if (g_ascii_strncasecmp (locale, against, 2) == 0)
636 return (against_len == 2) ? 3 : 2;
642 lookup_immodule (gchar **immodules_list)
644 while (immodules_list && *immodules_list)
646 if (g_strcmp0 (*immodules_list, SIMPLE_ID) == 0)
652 found = g_hash_table_lookup_extended (contexts_hash, *immodules_list,
653 (gpointer *) &context_id, NULL);
664 * _gtk_im_module_get_default_context_id:
665 * @client_window: a window
667 * Return the context_id of the best IM context type
668 * for the given window.
670 * Return value: the context ID (will never be %NULL)
673 _gtk_im_module_get_default_context_id (GdkWindow *client_window)
676 const gchar *context_id = NULL;
677 gint best_goodness = 0;
679 gchar *tmp_locale, *tmp, **immodules;
682 GtkSettings *settings;
685 gtk_im_module_initialize ();
687 envvar = g_getenv("GTK_IM_MODULE");
690 immodules = g_strsplit(envvar, ":", 0);
691 context_id = lookup_immodule(immodules);
692 g_strfreev(immodules);
698 /* Check if the certain immodule is set in XSETTINGS.
700 if (GDK_IS_WINDOW (client_window))
702 screen = gdk_window_get_screen (client_window);
703 settings = gtk_settings_get_for_screen (screen);
704 g_object_get (G_OBJECT (settings), "gtk-im-module", &tmp, NULL);
707 immodules = g_strsplit(tmp, ":", 0);
708 context_id = lookup_immodule(immodules);
709 g_strfreev(immodules);
717 /* Strip the locale code down to the essentials
719 tmp_locale = _gtk_get_lc_ctype ();
720 tmp = strchr (tmp_locale, '.');
723 tmp = strchr (tmp_locale, '@');
727 tmp_list = modules_list;
730 GtkIMModule *module = tmp_list->data;
732 for (i = 0; i < module->n_contexts; i++)
734 const gchar *p = module->contexts[i]->default_locales;
737 const gchar *q = strchr (p, ':');
738 gint goodness = match_locale (tmp_locale, p, q ? q - p : strlen (p));
740 if (goodness > best_goodness)
742 context_id = module->contexts[i]->context_id;
743 best_goodness = goodness;
746 p = q ? q + 1 : NULL;
750 tmp_list = tmp_list->next;
755 return context_id ? context_id : SIMPLE_ID;