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, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
21 * file for a list of people on the GTK+ Team. See the ChangeLog
22 * files for a list of changes. These files are distributed with
23 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
33 #include <glib/gstdio.h>
35 #include "gtkimmodule.h"
36 #include "gtkimcontextsimple.h"
37 #include "gtksettings.h"
43 /* Do *not* include "gtkprivate.h" in this file. If you do, the
44 * correct_libdir_prefix() and correct_localedir_prefix() functions
45 * below will have to move somewhere else.
48 #ifdef __GTK_PRIVATE_H__
49 #error gtkprivate.h should not be included in this file
52 #define SIMPLE_ID "gtk-im-context-simple"
56 * @context_id: The unique identification string of the input method.
57 * @context_name: The human-readable name of the input method.
58 * @domain: Translation domain to be used with
59 * <function>dgettext()</function>.
60 * @domain_dirname: Name of locale directory for use with
61 * <function>bindtextdomain()</function>.
62 * @default_locales: A colon-separated list of locales where this input method
63 * should be the default. The asterisk "*" sets the default for all locales.
65 * Bookkeeping information about a loadable input method.
68 typedef struct _GtkIMModule GtkIMModule;
69 typedef struct _GtkIMModuleClass GtkIMModuleClass;
71 #define GTK_TYPE_IM_MODULE (gtk_im_module_get_type ())
72 #define GTK_IM_MODULE(im_module) (G_TYPE_CHECK_INSTANCE_CAST ((im_module), GTK_TYPE_IM_MODULE, GtkIMModule))
73 #define GTK_IS_IM_MODULE(im_module) (G_TYPE_CHECK_INSTANCE_TYPE ((im_module), GTK_TYPE_IM_MODULE))
77 GTypeModule parent_instance;
83 void (*list) (const GtkIMContextInfo ***contexts,
85 void (*init) (GTypeModule *module);
87 GtkIMContext *(*create) (const gchar *context_id);
89 GtkIMContextInfo **contexts;
95 struct _GtkIMModuleClass
97 GTypeModuleClass parent_class;
100 static GType gtk_im_module_get_type (void);
102 static gint n_loaded_contexts = 0;
103 static GHashTable *contexts_hash = NULL;
104 static GSList *modules_list = NULL;
106 static GObjectClass *parent_class = NULL;
109 gtk_im_module_load (GTypeModule *module)
111 GtkIMModule *im_module = GTK_IM_MODULE (module);
113 if (!im_module->builtin)
115 im_module->library = g_module_open (im_module->path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
116 if (!im_module->library)
118 g_warning ("%s", g_module_error());
122 /* extract symbols from the lib */
123 if (!g_module_symbol (im_module->library, "im_module_init",
124 (gpointer *)&im_module->init) ||
125 !g_module_symbol (im_module->library, "im_module_exit",
126 (gpointer *)&im_module->exit) ||
127 !g_module_symbol (im_module->library, "im_module_list",
128 (gpointer *)&im_module->list) ||
129 !g_module_symbol (im_module->library, "im_module_create",
130 (gpointer *)&im_module->create))
132 g_warning ("%s", g_module_error());
133 g_module_close (im_module->library);
139 /* call the module's init function to let it */
140 /* setup anything it needs to set up. */
141 im_module->init (module);
147 gtk_im_module_unload (GTypeModule *module)
149 GtkIMModule *im_module = GTK_IM_MODULE (module);
153 if (!im_module->builtin)
155 g_module_close (im_module->library);
156 im_module->library = NULL;
158 im_module->init = NULL;
159 im_module->exit = NULL;
160 im_module->list = NULL;
161 im_module->create = NULL;
165 /* This only will ever be called if an error occurs during
169 gtk_im_module_finalize (GObject *object)
171 GtkIMModule *module = GTK_IM_MODULE (object);
173 g_free (module->path);
175 parent_class->finalize (object);
178 G_DEFINE_TYPE (GtkIMModule, gtk_im_module, G_TYPE_TYPE_MODULE)
181 gtk_im_module_class_init (GtkIMModuleClass *class)
183 GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class);
184 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
186 parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (class));
188 module_class->load = gtk_im_module_load;
189 module_class->unload = gtk_im_module_unload;
191 gobject_class->finalize = gtk_im_module_finalize;
195 gtk_im_module_init (GtkIMModule* object)
200 free_info (GtkIMContextInfo *info)
202 g_free ((char *)info->context_id);
203 g_free ((char *)info->context_name);
204 g_free ((char *)info->domain);
205 g_free ((char *)info->domain_dirname);
206 g_free ((char *)info->default_locales);
211 add_module (GtkIMModule *module, GSList *infos)
213 GSList *tmp_list = infos;
215 gint n = g_slist_length (infos);
216 module->contexts = g_new (GtkIMContextInfo *, n);
220 GtkIMContextInfo *info = tmp_list->data;
222 if (g_hash_table_lookup (contexts_hash, info->context_id))
224 free_info (info); /* Duplicate */
228 g_hash_table_insert (contexts_hash, (char *)info->context_id, module);
229 module->contexts[i++] = tmp_list->data;
233 tmp_list = tmp_list->next;
235 g_slist_free (infos);
236 module->n_contexts = i;
238 modules_list = g_slist_prepend (modules_list, module);
244 correct_libdir_prefix (gchar **path)
246 /* GTK_LIBDIR here is supposed to still have the definition from
247 * Makefile.am, i.e. the build-time value. Do *not* include gtkprivate.h
250 if (strncmp (*path, GTK_LIBDIR, strlen (GTK_LIBDIR)) == 0)
252 /* This is an entry put there by make install on the
253 * packager's system. On Windows a prebuilt GTK+
254 * package can be installed in a random
255 * location. The gtk.immodules file distributed in
256 * such a package contains paths from the package
257 * builder's machine. Replace the path with the real
258 * one on this machine.
260 extern const gchar *_gtk_get_libdir ();
262 *path = g_strconcat (_gtk_get_libdir (), tem + strlen (GTK_LIBDIR), NULL);
268 correct_localedir_prefix (gchar **path)
270 /* As above, but for GTK_LOCALEDIR. Use separate function in case
271 * GTK_LOCALEDIR isn't a subfolder of GTK_LIBDIR.
273 if (strncmp (*path, GTK_LOCALEDIR, strlen (GTK_LOCALEDIR)) == 0)
275 extern const gchar *_gtk_get_localedir ();
277 *path = g_strconcat (_gtk_get_localedir (), tem + strlen (GTK_LOCALEDIR), NULL);
285 add_builtin_module (const gchar *module_name,
286 const GtkIMContextInfo **contexts,
289 GtkIMModule *module = g_object_new (GTK_TYPE_IM_MODULE, NULL);
290 GSList *infos = NULL;
293 for (i = 0; i < n_contexts; i++)
295 GtkIMContextInfo *info = g_new (GtkIMContextInfo, 1);
296 info->context_id = g_strdup (contexts[i]->context_id);
297 info->context_name = g_strdup (contexts[i]->context_name);
298 info->domain = g_strdup (contexts[i]->domain);
299 info->domain_dirname = g_strdup (contexts[i]->domain_dirname);
301 correct_localedir_prefix ((char **) &info->domain_dirname);
303 info->default_locales = g_strdup (contexts[i]->default_locales);
304 infos = g_slist_prepend (infos, info);
307 module->builtin = TRUE;
308 g_type_module_set_name (G_TYPE_MODULE (module), module_name);
309 add_module (module, infos);
315 gtk_im_module_initialize (void)
317 GString *line_buf = g_string_new (NULL);
318 GString *tmp_buf = g_string_new (NULL);
319 gchar *filename = gtk_rc_get_im_module_file();
321 gboolean have_error = FALSE;
323 GtkIMModule *module = NULL;
324 GSList *infos = NULL;
326 contexts_hash = g_hash_table_new (g_str_hash, g_str_equal);
328 #define do_builtin(m) \
330 const GtkIMContextInfo **contexts; \
332 extern void _gtk_immodule_ ## m ## _list (const GtkIMContextInfo ***contexts, \
333 guint *n_contexts); \
334 extern void _gtk_immodule_ ## m ## _init (GTypeModule *module); \
335 extern void _gtk_immodule_ ## m ## _exit (void); \
336 extern GtkIMContext *_gtk_immodule_ ## m ## _create (const gchar *context_id); \
338 _gtk_immodule_ ## m ## _list (&contexts, &n_contexts); \
339 module = add_builtin_module (#m, contexts, n_contexts); \
340 module->init = _gtk_immodule_ ## m ## _init; \
341 module->exit = _gtk_immodule_ ## m ## _exit; \
342 module->create = _gtk_immodule_ ## m ## _create; \
346 #ifdef INCLUDE_IM_am_et
349 #ifdef INCLUDE_IM_cedilla
350 do_builtin (cedilla);
352 #ifdef INCLUDE_IM_cyrillic_translit
353 do_builtin (cyrillic_translit);
355 #ifdef INCLUDE_IM_ime
358 #ifdef INCLUDE_IM_inuktitut
359 do_builtin (inuktitut);
361 #ifdef INCLUDE_IM_ipa
364 #ifdef INCLUDE_IM_multipress
365 do_builtin (multipress);
367 #ifdef INCLUDE_IM_thai
370 #ifdef INCLUDE_IM_ti_er
373 #ifdef INCLUDE_IM_ti_et
376 #ifdef INCLUDE_IM_viqr
379 #ifdef INCLUDE_IM_xim
385 file = g_fopen (filename, "r");
388 /* In case someone wants only the default input method,
389 * we allow no file at all.
391 g_string_free (line_buf, TRUE);
392 g_string_free (tmp_buf, TRUE);
397 while (!have_error && pango_read_line (file, line_buf))
403 if (!pango_skip_space (&p))
405 /* Blank line marking the end of a module
407 if (module && *p != '#')
409 add_module (module, infos);
419 /* Read a module location
421 module = g_object_new (GTK_TYPE_IM_MODULE, NULL);
423 if (!pango_scan_string (&p, tmp_buf) ||
424 pango_skip_space (&p))
426 g_warning ("Error parsing context info in '%s'\n %s",
427 filename, line_buf->str);
431 module->path = g_strdup (tmp_buf->str);
433 correct_libdir_prefix (&module->path);
435 g_type_module_set_name (G_TYPE_MODULE (module), module->path);
439 GtkIMContextInfo *info = g_new0 (GtkIMContextInfo, 1);
441 /* Read information about a context type
443 if (!pango_scan_string (&p, tmp_buf))
445 info->context_id = g_strdup (tmp_buf->str);
447 if (!pango_scan_string (&p, tmp_buf))
449 info->context_name = g_strdup (tmp_buf->str);
451 if (!pango_scan_string (&p, tmp_buf))
453 info->domain = g_strdup (tmp_buf->str);
455 if (!pango_scan_string (&p, tmp_buf))
457 info->domain_dirname = g_strdup (tmp_buf->str);
459 correct_localedir_prefix ((char **) &info->domain_dirname);
462 if (!pango_scan_string (&p, tmp_buf))
464 info->default_locales = g_strdup (tmp_buf->str);
466 if (pango_skip_space (&p))
469 infos = g_slist_prepend (infos, info);
473 g_warning ("Error parsing context info in '%s'\n %s",
474 filename, line_buf->str);
481 GSList *tmp_list = infos;
484 free_info (tmp_list->data);
485 tmp_list = tmp_list->next;
487 g_slist_free (infos);
489 g_object_unref (module);
492 add_module (module, infos);
495 g_string_free (line_buf, TRUE);
496 g_string_free (tmp_buf, TRUE);
501 compare_gtkimcontextinfo_name(const GtkIMContextInfo **a,
502 const GtkIMContextInfo **b)
504 return g_utf8_collate ((*a)->context_name, (*b)->context_name);
508 * _gtk_im_module_list:
509 * @contexts: location to store an array of pointers to #GtkIMContextInfo
510 * this array should be freed with g_free() when you are finished.
511 * The structures it points are statically allocated and should
512 * not be modified or freed.
513 * @n_contexts: the length of the array stored in @contexts
515 * List all available types of input method context
518 _gtk_im_module_list (const GtkIMContextInfo ***contexts,
527 GtkIMContextInfo simple_context_info = {
540 static gboolean beenhere = FALSE;
544 gtk_im_module_initialize ();
550 /* correct_localedir_prefix() requires its parameter to be a
553 simple_context_info.domain_dirname = g_strdup (simple_context_info.domain_dirname);
554 correct_localedir_prefix ((char **) &simple_context_info.domain_dirname);
559 *n_contexts = (n_loaded_contexts + 1);
566 *contexts = g_new (const GtkIMContextInfo *, n_loaded_contexts + 1);
568 (*contexts)[n++] = &simple_context_info;
570 tmp_list = modules_list;
573 GtkIMModule *module = tmp_list->data;
575 for (i=0; i<module->n_contexts; i++)
576 (*contexts)[n++] = module->contexts[i];
578 tmp_list = tmp_list->next;
581 /* fisrt element (Default) should always be at top */
582 qsort ((*contexts)+1, n-1, sizeof (GtkIMContextInfo *), (GCompareFunc)compare_gtkimcontextinfo_name);
587 * _gtk_im_module_create:
588 * @context_id: the context ID for the context type to create
590 * Create an IM context of a type specified by the string
593 * Return value: a newly created input context of or @context_id, or
594 * if that could not be created, a newly created GtkIMContextSimple.
597 _gtk_im_module_create (const gchar *context_id)
599 GtkIMModule *im_module;
600 GtkIMContext *context = NULL;
603 gtk_im_module_initialize ();
605 if (strcmp (context_id, SIMPLE_ID) != 0)
607 im_module = g_hash_table_lookup (contexts_hash, context_id);
610 g_warning ("Attempt to load unknown IM context type '%s'", context_id);
614 if (g_type_module_use (G_TYPE_MODULE (im_module)))
616 context = im_module->create (context_id);
617 g_type_module_unuse (G_TYPE_MODULE (im_module));
621 g_warning ("Loading IM context type '%s' failed", context_id);
626 return gtk_im_context_simple_new ();
631 /* Match @locale against @against.
633 * 'en_US' against 'en_US' => 4
634 * 'en_US' against 'en' => 3
635 * 'en', 'en_UK' against 'en_US' => 2
636 * all locales, against '*' => 1
639 match_locale (const gchar *locale,
640 const gchar *against,
643 if (strcmp (against, "*") == 0)
646 if (g_ascii_strcasecmp (locale, against) == 0)
649 if (g_ascii_strncasecmp (locale, against, 2) == 0)
650 return (against_len == 2) ? 3 : 2;
656 * _gtk_im_module_get_default_context_id:
657 * @client_window: a window
659 * Return the context_id of the best IM context type
660 * for the given window.
662 * Return value: the context ID (will never be %NULL)
665 _gtk_im_module_get_default_context_id (GdkWindow *client_window)
668 const gchar *context_id = NULL;
669 gint best_goodness = 0;
671 gchar *tmp_locale, *tmp;
674 GtkSettings *settings;
677 gtk_im_module_initialize ();
679 envvar = g_getenv ("GTK_IM_MODULE");
681 (strcmp (envvar, SIMPLE_ID) == 0 ||
682 g_hash_table_lookup (contexts_hash, envvar)))
685 /* Check if the certain immodule is set in XSETTINGS.
687 if (client_window != NULL && GDK_IS_DRAWABLE (client_window))
689 screen = gdk_drawable_get_screen (GDK_DRAWABLE (client_window));
691 settings = gtk_settings_get_for_screen (screen);
693 settings = gtk_settings_get_default ();
695 g_object_get (G_OBJECT (settings), "gtk-im-module", &tmp, NULL);
698 if (strcmp (tmp, SIMPLE_ID) == 0)
699 context_id = SIMPLE_ID;
703 module = g_hash_table_lookup (contexts_hash, tmp);
705 context_id = module->contexts[0]->context_id;
714 /* Strip the locale code down to the essentials
716 tmp_locale = _gtk_get_lc_ctype ();
717 tmp = strchr (tmp_locale, '.');
720 tmp = strchr (tmp_locale, '@');
724 tmp_list = modules_list;
727 GtkIMModule *module = tmp_list->data;
729 for (i = 0; i < module->n_contexts; i++)
731 const gchar *p = module->contexts[i]->default_locales;
734 const gchar *q = strchr (p, ':');
735 gint goodness = match_locale (tmp_locale, p, q ? q - p : strlen (p));
737 if (goodness > best_goodness)
739 context_id = module->contexts[i]->context_id;
740 best_goodness = goodness;
743 p = q ? q + 1 : NULL;
747 tmp_list = tmp_list->next;
752 return context_id ? context_id : SIMPLE_ID;