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/.
33 #include <pango/pango-utils.h>
34 #include "gtkimmodule.h"
35 #include "gtkimcontextsimple.h"
36 #include "gtkprivate.h"
41 #define SIMPLE_ID "gtk-im-context-simple"
43 typedef struct _GtkIMModule GtkIMModule;
44 typedef struct _GtkIMModuleClass GtkIMModuleClass;
46 #define GTK_TYPE_IM_MODULE (gtk_im_module_get_type ())
47 #define GTK_IM_MODULE(im_module) (G_TYPE_CHECK_INSTANCE_CAST ((im_module), GTK_TYPE_IM_MODULE, GtkIMModule))
48 #define GTK_IS_IM_MODULE(im_module) (G_TYPE_CHECK_INSTANCE_TYPE ((im_module), GTK_TYPE_IM_MODULE))
52 GTypeModule parent_instance;
56 void (*list) (const GtkIMContextInfo ***contexts,
58 void (*init) (GTypeModule *module);
60 GtkIMContext *(*create) (const gchar *context_id);
62 GtkIMContextInfo **contexts;
68 struct _GtkIMModuleClass
70 GTypeModuleClass parent_class;
73 GType gtk_im_module_get_type (void);
75 gint n_loaded_contexts = 0;
76 static GHashTable *contexts_hash = NULL;
77 static GSList *modules_list = NULL;
79 static GObjectClass *parent_class = NULL;
82 gtk_im_module_load (GTypeModule *module)
84 GtkIMModule *im_module = GTK_IM_MODULE (module);
86 im_module->library = g_module_open (im_module->path, 0);
87 if (!im_module->library)
89 g_warning (g_module_error());
93 /* extract symbols from the lib */
94 if (!g_module_symbol (im_module->library, "im_module_init",
95 (gpointer *)&im_module->init) ||
96 !g_module_symbol (im_module->library, "im_module_exit",
97 (gpointer *)&im_module->exit) ||
98 !g_module_symbol (im_module->library, "im_module_list",
99 (gpointer *)&im_module->list) ||
100 !g_module_symbol (im_module->library, "im_module_create",
101 (gpointer *)&im_module->create))
103 g_warning (g_module_error());
104 g_module_close (im_module->library);
109 /* call the theme's init (theme_init) function to let it */
110 /* setup anything it needs to set up. */
111 im_module->init (module);
117 gtk_im_module_unload (GTypeModule *module)
119 GtkIMModule *im_module = GTK_IM_MODULE (module);
123 g_module_close (im_module->library);
124 im_module->library = NULL;
126 im_module->init = NULL;
127 im_module->exit = NULL;
128 im_module->list = NULL;
129 im_module->create = NULL;
132 /* This only will ever be called if an error occurs during
136 gtk_im_module_finalize (GObject *object)
138 GtkIMModule *module = GTK_IM_MODULE (object);
140 g_free (module->path);
142 parent_class->finalize (object);
146 gtk_im_module_class_init (GtkIMModuleClass *class)
148 GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class);
149 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
151 parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (class));
153 module_class->load = gtk_im_module_load;
154 module_class->unload = gtk_im_module_unload;
156 gobject_class->finalize = gtk_im_module_finalize;
160 gtk_im_module_get_type (void)
162 static GType im_module_type = 0;
166 static const GTypeInfo im_module_info = {
167 sizeof (GtkIMModuleClass),
168 NULL, /* base_init */
169 NULL, /* base_finalize */
170 (GClassInitFunc) gtk_im_module_class_init,
171 NULL, /* class_finalize */
172 NULL, /* class_data */
173 sizeof (GtkIMModule),
175 NULL, /* instance_init */
178 im_module_type = g_type_register_static (G_TYPE_TYPE_MODULE, "GtkIMModule", &im_module_info, 0);
181 return im_module_type;
185 free_info (GtkIMContextInfo *info)
187 g_free ((char *)info->context_id);
188 g_free ((char *)info->context_name);
189 g_free ((char *)info->domain);
190 g_free ((char *)info->domain_dirname);
191 g_free ((char *)info->default_locales);
196 add_module (GtkIMModule *module, GSList *infos)
198 GSList *tmp_list = infos;
200 gint n = g_slist_length (infos);
201 module->contexts = g_new (GtkIMContextInfo *, n);
205 GtkIMContextInfo *info = tmp_list->data;
207 if (g_hash_table_lookup (contexts_hash, info->context_id))
209 free_info (info); /* Duplicate */
213 g_hash_table_insert (contexts_hash, (char *)info->context_id, module);
214 module->contexts[i++] = tmp_list->data;
218 tmp_list = tmp_list->next;
220 g_slist_free (infos);
221 module->n_contexts = i;
223 modules_list = g_slist_prepend (modules_list, module);
228 correct_libdir_prefix (gchar **path)
230 if (strncmp (*path, GTK_LIBDIR, strlen (GTK_LIBDIR)) == 0)
232 /* This is an entry put there by make install on the
233 * packager's system. On Windows a prebuilt GTK+
234 * package can be installed in a random
235 * location. The gtk.immodules file distributed in
236 * such a package contains paths from the package
237 * builder's machine. Replace the path with the real
238 * one on this machine.
241 *path = g_strconcat (GTK_LIBDIR, tem + strlen (GTK_LIBDIR), NULL);
249 gtk_im_module_init ()
251 GString *line_buf = g_string_new (NULL);
252 GString *tmp_buf = g_string_new (NULL);
253 gchar *filename = gtk_rc_get_im_module_file();
255 gboolean have_error = FALSE;
257 GtkIMModule *module = NULL;
258 GSList *infos = NULL;
260 contexts_hash = g_hash_table_new (g_str_hash, g_str_equal);
262 file = fopen (filename, "r");
265 g_warning ("Can not open Input Method module file '%s': %s",
266 filename, g_strerror (errno));
267 /* We are leaking all kinds of memory here. */
271 while (!have_error && pango_read_line (file, line_buf))
277 if (!pango_skip_space (&p))
279 /* Blank line marking the end of a module
281 if (module && *p != '#')
283 add_module (module, infos);
293 /* Read a module location
295 module = g_object_new (GTK_TYPE_IM_MODULE, NULL);
297 if (!pango_scan_string (&p, tmp_buf) ||
298 pango_skip_space (&p))
300 g_warning ("Error parsing context info in '%s'\n %s",
301 filename, line_buf->str);
305 module->path = g_strdup (tmp_buf->str);
307 correct_libdir_prefix (&module->path);
309 g_type_module_set_name (G_TYPE_MODULE (module), module->path);
313 GtkIMContextInfo *info = g_new0 (GtkIMContextInfo, 1);
315 /* Read information about a context type
317 if (!pango_scan_string (&p, tmp_buf))
319 info->context_id = g_strdup (tmp_buf->str);
321 if (!pango_scan_string (&p, tmp_buf))
323 info->context_name = g_strdup (tmp_buf->str);
325 if (!pango_scan_string (&p, tmp_buf))
327 info->domain = g_strdup (tmp_buf->str);
329 if (!pango_scan_string (&p, tmp_buf))
331 info->domain_dirname = g_strdup (tmp_buf->str);
333 correct_libdir_prefix (&info->domain_dirname);
336 if (!pango_scan_string (&p, tmp_buf))
338 info->default_locales = g_strdup (tmp_buf->str);
340 if (pango_skip_space (&p))
343 infos = g_slist_prepend (infos, info);
347 g_warning ("Error parsing context info in '%s'\n %s",
348 filename, line_buf->str);
355 GSList *tmp_list = infos;
358 free_info (tmp_list->data);
359 tmp_list = tmp_list->next;
361 g_slist_free (infos);
363 g_object_unref (G_OBJECT (module));
366 add_module (module, infos);
369 g_string_free (line_buf, TRUE);
370 g_string_free (tmp_buf, TRUE);
375 * _gtk_im_module_list:
376 * @contexts: location to store an array of pointers to #GtkIMContextInfo
377 * this array should be freed with g_free() when you are finished.
378 * The structures it points are statically allocated and should
379 * not be modified or freed.
380 * @n_contexts: the length of the array stored in @contexts
382 * List all available types of input method context
385 _gtk_im_module_list (const GtkIMContextInfo ***contexts,
390 static const GtkIMContextInfo simple_context_info = {
399 gtk_im_module_init ();
402 *n_contexts = (n_loaded_contexts + 1);
409 *contexts = g_new (const GtkIMContextInfo *, n_loaded_contexts + 1);
411 (*contexts)[n++] = &simple_context_info;
413 tmp_list = modules_list;
416 GtkIMModule *module = tmp_list->data;
418 for (i=0; i<module->n_contexts; i++)
419 (*contexts)[n++] = module->contexts[i];
421 tmp_list = tmp_list->next;
427 * _gtk_im_module_create:
428 * @context_id: the context ID for the context type to create
430 * Create an IM context of a type specified by the string
433 * Return value: a newly created input context of or @context_id, or
434 * if that could not be created, a newly created GtkIMContextSimple.
437 _gtk_im_module_create (const gchar *context_id)
439 GtkIMModule *im_module;
440 GtkIMContext *context = NULL;
443 gtk_im_module_init ();
445 if (strcmp (context_id, SIMPLE_ID) != 0)
447 im_module = g_hash_table_lookup (contexts_hash, context_id);
450 g_warning ("Attempt to load unknown IM context type '%s'", context_id);
454 if (g_type_module_use (G_TYPE_MODULE (im_module)))
456 context = im_module->create (context_id);
457 g_type_module_unuse (G_TYPE_MODULE (im_module));
461 g_warning ("Loading IM context type '%s' failed", context_id);
466 return gtk_im_context_simple_new ();
471 /* Match @locale against @against.
473 * 'en_US' against 'en_US' => 4
474 * 'en_US' against 'en' => 3
475 * 'en', 'en_UK' against 'en_US' => 2
476 * all locales, against '*' => 1
479 match_locale (const gchar *locale,
480 const gchar *against,
483 if (strcmp (against, "*") == 0)
486 if (strcmp (locale, against) == 0)
489 if (strncmp (locale, against, 2) == 0)
490 return (against_len == 2) ? 3 : 2;
496 * _gtk_im_module_get_default_context_id:
497 * @locale: a locale id in the form 'en_US'
499 * Return the context_id of the best IM context type
500 * for the given locale ID.
502 * Return value: the context ID (will never be %NULL)
503 * the value is newly allocated and must be freed
507 _gtk_im_module_get_default_context_id (const gchar *locale)
510 const gchar *context_id = NULL;
511 gint best_goodness = 0;
513 gchar *tmp_locale, *tmp;
517 gtk_im_module_init ();
519 envvar = g_getenv ("GTK_IM_MODULE");
520 if (envvar && g_hash_table_lookup (contexts_hash, envvar))
521 return g_strdup (envvar);
523 /* Strip the locale code down to the essentials
525 tmp_locale = g_strdup (locale);
526 tmp = strchr (tmp_locale, '.');
529 tmp = strchr (tmp_locale, '@');
533 tmp_list = modules_list;
536 GtkIMModule *module = tmp_list->data;
538 for (i=0; i<module->n_contexts; i++)
540 const gchar *p = module->contexts[i]->default_locales;
543 const gchar *q = strchr (p, ':');
544 gint goodness = match_locale (tmp_locale, p, q ? q - p : strlen (p));
546 if (goodness > best_goodness)
548 context_id = module->contexts[i]->context_id;
549 best_goodness = goodness;
552 p = q ? q + 1 : NULL;
556 tmp_list = tmp_list->next;
561 return g_strdup (context_id ? context_id : SIMPLE_ID);