1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * Themes added by The Rasterman <raster@redhat.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free
18 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
23 * file for a list of people on the GTK+ Team. See the ChangeLog
24 * files for a list of changes. These files are distributed with
25 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
35 #include <glib/gstdio.h>
37 #include <pango/pango-utils.h>
38 #include "gtkimmodule.h"
39 #include "gtkimcontextsimple.h"
40 #include "gtksettings.h"
46 /* Do *not* include "gtkprivate.h" in this file. If you do, the
47 * correct_libdir_prefix() and correct_localedir_prefix() functions
48 * below will have to move somewhere else.
51 #ifdef __GTK_PRIVATE_H__
52 #error gtkprivate.h should not be included in this file
55 #define SIMPLE_ID "gtk-im-context-simple"
57 typedef struct _GtkIMModule GtkIMModule;
58 typedef struct _GtkIMModuleClass GtkIMModuleClass;
60 #define GTK_TYPE_IM_MODULE (gtk_im_module_get_type ())
61 #define GTK_IM_MODULE(im_module) (G_TYPE_CHECK_INSTANCE_CAST ((im_module), GTK_TYPE_IM_MODULE, GtkIMModule))
62 #define GTK_IS_IM_MODULE(im_module) (G_TYPE_CHECK_INSTANCE_TYPE ((im_module), GTK_TYPE_IM_MODULE))
66 GTypeModule parent_instance;
70 void (*list) (const GtkIMContextInfo ***contexts,
72 void (*init) (GTypeModule *module);
74 GtkIMContext *(*create) (const gchar *context_id);
76 GtkIMContextInfo **contexts;
82 struct _GtkIMModuleClass
84 GTypeModuleClass parent_class;
87 static GType gtk_im_module_get_type (void);
89 static gint n_loaded_contexts = 0;
90 static GHashTable *contexts_hash = NULL;
91 static GSList *modules_list = NULL;
93 static GObjectClass *parent_class = NULL;
96 gtk_im_module_load (GTypeModule *module)
98 GtkIMModule *im_module = GTK_IM_MODULE (module);
100 im_module->library = g_module_open (im_module->path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
101 if (!im_module->library)
103 g_warning (g_module_error());
107 /* extract symbols from the lib */
108 if (!g_module_symbol (im_module->library, "im_module_init",
109 (gpointer *)&im_module->init) ||
110 !g_module_symbol (im_module->library, "im_module_exit",
111 (gpointer *)&im_module->exit) ||
112 !g_module_symbol (im_module->library, "im_module_list",
113 (gpointer *)&im_module->list) ||
114 !g_module_symbol (im_module->library, "im_module_create",
115 (gpointer *)&im_module->create))
117 g_warning (g_module_error());
118 g_module_close (im_module->library);
123 /* call the theme's init (theme_init) function to let it */
124 /* setup anything it needs to set up. */
125 im_module->init (module);
131 gtk_im_module_unload (GTypeModule *module)
133 GtkIMModule *im_module = GTK_IM_MODULE (module);
137 g_module_close (im_module->library);
138 im_module->library = NULL;
140 im_module->init = NULL;
141 im_module->exit = NULL;
142 im_module->list = NULL;
143 im_module->create = NULL;
146 /* This only will ever be called if an error occurs during
150 gtk_im_module_finalize (GObject *object)
152 GtkIMModule *module = GTK_IM_MODULE (object);
154 g_free (module->path);
156 parent_class->finalize (object);
159 G_DEFINE_TYPE (GtkIMModule, gtk_im_module, G_TYPE_TYPE_MODULE)
162 gtk_im_module_class_init (GtkIMModuleClass *class)
164 GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class);
165 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
167 parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (class));
169 module_class->load = gtk_im_module_load;
170 module_class->unload = gtk_im_module_unload;
172 gobject_class->finalize = gtk_im_module_finalize;
176 gtk_im_module_init (GtkIMModule* object)
181 free_info (GtkIMContextInfo *info)
183 g_free ((char *)info->context_id);
184 g_free ((char *)info->context_name);
185 g_free ((char *)info->domain);
186 g_free ((char *)info->domain_dirname);
187 g_free ((char *)info->default_locales);
192 add_module (GtkIMModule *module, GSList *infos)
194 GSList *tmp_list = infos;
196 gint n = g_slist_length (infos);
197 module->contexts = g_new (GtkIMContextInfo *, n);
201 GtkIMContextInfo *info = tmp_list->data;
203 if (g_hash_table_lookup (contexts_hash, info->context_id))
205 free_info (info); /* Duplicate */
209 g_hash_table_insert (contexts_hash, (char *)info->context_id, module);
210 module->contexts[i++] = tmp_list->data;
214 tmp_list = tmp_list->next;
216 g_slist_free (infos);
217 module->n_contexts = i;
219 modules_list = g_slist_prepend (modules_list, module);
225 correct_libdir_prefix (gchar **path)
227 /* GTK_LIBDIR here is supposed to still have the definition from
228 * Makefile.am, i.e. the build-time value. Do *not* include gtkprivate.h
231 if (strncmp (*path, GTK_LIBDIR, strlen (GTK_LIBDIR)) == 0)
233 /* This is an entry put there by make install on the
234 * packager's system. On Windows a prebuilt GTK+
235 * package can be installed in a random
236 * location. The gtk.immodules file distributed in
237 * such a package contains paths from the package
238 * builder's machine. Replace the path with the real
239 * one on this machine.
241 extern const gchar *_gtk_get_libdir ();
243 *path = g_strconcat (_gtk_get_libdir (), tem + strlen (GTK_LIBDIR), NULL);
249 correct_localedir_prefix (gchar **path)
251 /* As above, but for GTK_LOCALEDIR. Use separate function in case
252 * GTK_LOCALEDIR isn't a subfolder of GTK_LIBDIR.
254 if (strncmp (*path, GTK_LOCALEDIR, strlen (GTK_LOCALEDIR)) == 0)
256 extern const gchar *_gtk_get_localedir ();
258 *path = g_strconcat (_gtk_get_localedir (), tem + strlen (GTK_LOCALEDIR), NULL);
266 gtk_im_module_initialize (void)
268 GString *line_buf = g_string_new (NULL);
269 GString *tmp_buf = g_string_new (NULL);
270 gchar *filename = gtk_rc_get_im_module_file();
272 gboolean have_error = FALSE;
274 GtkIMModule *module = NULL;
275 GSList *infos = NULL;
277 contexts_hash = g_hash_table_new (g_str_hash, g_str_equal);
279 file = g_fopen (filename, "r");
282 /* In case someone wants only the default input method,
283 * we allow no file at all.
285 g_string_free (line_buf, TRUE);
286 g_string_free (tmp_buf, TRUE);
291 while (!have_error && pango_read_line (file, line_buf))
297 if (!pango_skip_space (&p))
299 /* Blank line marking the end of a module
301 if (module && *p != '#')
303 add_module (module, infos);
313 /* Read a module location
315 module = g_object_new (GTK_TYPE_IM_MODULE, NULL);
317 if (!pango_scan_string (&p, tmp_buf) ||
318 pango_skip_space (&p))
320 g_warning ("Error parsing context info in '%s'\n %s",
321 filename, line_buf->str);
325 module->path = g_strdup (tmp_buf->str);
327 correct_libdir_prefix (&module->path);
329 g_type_module_set_name (G_TYPE_MODULE (module), module->path);
333 GtkIMContextInfo *info = g_new0 (GtkIMContextInfo, 1);
335 /* Read information about a context type
337 if (!pango_scan_string (&p, tmp_buf))
339 info->context_id = g_strdup (tmp_buf->str);
341 if (!pango_scan_string (&p, tmp_buf))
343 info->context_name = g_strdup (tmp_buf->str);
345 if (!pango_scan_string (&p, tmp_buf))
347 info->domain = g_strdup (tmp_buf->str);
349 if (!pango_scan_string (&p, tmp_buf))
351 info->domain_dirname = g_strdup (tmp_buf->str);
353 correct_localedir_prefix ((char **) &info->domain_dirname);
356 if (!pango_scan_string (&p, tmp_buf))
358 info->default_locales = g_strdup (tmp_buf->str);
360 if (pango_skip_space (&p))
363 infos = g_slist_prepend (infos, info);
367 g_warning ("Error parsing context info in '%s'\n %s",
368 filename, line_buf->str);
375 GSList *tmp_list = infos;
378 free_info (tmp_list->data);
379 tmp_list = tmp_list->next;
381 g_slist_free (infos);
383 g_object_unref (module);
386 add_module (module, infos);
389 g_string_free (line_buf, TRUE);
390 g_string_free (tmp_buf, TRUE);
395 compare_gtkimcontextinfo_name(const GtkIMContextInfo **a,
396 const GtkIMContextInfo **b)
398 return g_utf8_collate ((*a)->context_name, (*b)->context_name);
402 * _gtk_im_module_list:
403 * @contexts: location to store an array of pointers to #GtkIMContextInfo
404 * this array should be freed with g_free() when you are finished.
405 * The structures it points are statically allocated and should
406 * not be modified or freed.
407 * @n_contexts: the length of the array stored in @contexts
409 * List all available types of input method context
412 _gtk_im_module_list (const GtkIMContextInfo ***contexts,
421 GtkIMContextInfo simple_context_info = {
434 static gboolean beenhere = FALSE;
438 gtk_im_module_initialize ();
444 /* correct_localedir_prefix() requires its parameter to be a
447 simple_context_info.domain_dirname = g_strdup (simple_context_info.domain_dirname);
448 correct_localedir_prefix ((char **) &simple_context_info.domain_dirname);
453 *n_contexts = (n_loaded_contexts + 1);
460 *contexts = g_new (const GtkIMContextInfo *, n_loaded_contexts + 1);
462 (*contexts)[n++] = &simple_context_info;
464 tmp_list = modules_list;
467 GtkIMModule *module = tmp_list->data;
469 for (i=0; i<module->n_contexts; i++)
470 (*contexts)[n++] = module->contexts[i];
472 tmp_list = tmp_list->next;
475 /* fisrt element (Default) should always be at top */
476 qsort ((*contexts)+1, n-1, sizeof (GtkIMContextInfo *), (GCompareFunc)compare_gtkimcontextinfo_name);
481 * _gtk_im_module_create:
482 * @context_id: the context ID for the context type to create
484 * Create an IM context of a type specified by the string
487 * Return value: a newly created input context of or @context_id, or
488 * if that could not be created, a newly created GtkIMContextSimple.
491 _gtk_im_module_create (const gchar *context_id)
493 GtkIMModule *im_module;
494 GtkIMContext *context = NULL;
497 gtk_im_module_initialize ();
499 if (strcmp (context_id, SIMPLE_ID) != 0)
501 im_module = g_hash_table_lookup (contexts_hash, context_id);
504 g_warning ("Attempt to load unknown IM context type '%s'", context_id);
508 if (g_type_module_use (G_TYPE_MODULE (im_module)))
510 context = im_module->create (context_id);
511 g_type_module_unuse (G_TYPE_MODULE (im_module));
515 g_warning ("Loading IM context type '%s' failed", context_id);
520 return gtk_im_context_simple_new ();
525 /* Match @locale against @against.
527 * 'en_US' against 'en_US' => 4
528 * 'en_US' against 'en' => 3
529 * 'en', 'en_UK' against 'en_US' => 2
530 * all locales, against '*' => 1
533 match_locale (const gchar *locale,
534 const gchar *against,
537 if (strcmp (against, "*") == 0)
540 if (g_ascii_strcasecmp (locale, against) == 0)
543 if (g_ascii_strncasecmp (locale, against, 2) == 0)
544 return (against_len == 2) ? 3 : 2;
550 * _gtk_im_module_get_default_context_id:
551 * @client_window: a window
553 * Return the context_id of the best IM context type
554 * for the given window.
556 * Return value: the context ID (will never be %NULL)
559 _gtk_im_module_get_default_context_id (GdkWindow *client_window)
562 const gchar *context_id = NULL;
563 gint best_goodness = 0;
565 gchar *tmp_locale, *tmp;
568 GtkSettings *settings;
571 gtk_im_module_initialize ();
573 envvar = g_getenv ("GTK_IM_MODULE");
575 (strcmp (envvar, SIMPLE_ID) == 0 ||
576 g_hash_table_lookup (contexts_hash, envvar)))
579 /* Check if the certain immodule is set in XSETTINGS.
581 if (client_window != NULL && GDK_IS_DRAWABLE (client_window))
583 screen = gdk_drawable_get_screen (GDK_DRAWABLE (client_window));
585 settings = gtk_settings_get_for_screen (screen);
587 settings = gtk_settings_get_default ();
589 g_object_get (G_OBJECT (settings), "gtk-im-module", &tmp, NULL);
592 if (strcmp (tmp, SIMPLE_ID) == 0)
593 context_id = SIMPLE_ID;
597 module = g_hash_table_lookup (contexts_hash, tmp);
599 context_id = module->contexts[0]->context_id;
608 /* Strip the locale code down to the essentials
610 tmp_locale = _gtk_get_lc_ctype ();
611 tmp = strchr (tmp_locale, '.');
614 tmp = strchr (tmp_locale, '@');
618 tmp_list = modules_list;
621 GtkIMModule *module = tmp_list->data;
623 for (i = 0; i < module->n_contexts; i++)
625 const gchar *p = module->contexts[i]->default_locales;
628 const gchar *q = strchr (p, ':');
629 gint goodness = match_locale (tmp_locale, p, q ? q - p : strlen (p));
631 if (goodness > best_goodness)
633 context_id = module->contexts[i]->context_id;
634 best_goodness = goodness;
637 p = q ? q + 1 : NULL;
641 tmp_list = tmp_list->next;
646 return context_id ? context_id : SIMPLE_ID;