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"
40 #define SIMPLE_ID "gtk-im-context-simple"
42 typedef struct _GtkIMModule GtkIMModule;
43 typedef struct _GtkIMModuleClass GtkIMModuleClass;
45 #define GTK_TYPE_IM_MODULE (gtk_im_module_get_type ())
46 #define GTK_IM_MODULE(im_module) (G_TYPE_CHECK_INSTANCE_CAST ((im_module), GTK_TYPE_IM_MODULE, GtkIMModule))
47 #define GTK_IS_IM_MODULE(im_module) (G_TYPE_CHECK_INSTANCE_TYPE ((im_module), GTK_TYPE_IM_MODULE))
51 GTypeModule parent_instance;
55 void (*list) (const GtkIMContextInfo ***contexts,
57 void (*init) (GTypeModule *module);
59 GtkIMContext *(*create) (const gchar *context_id);
61 GtkIMContextInfo **contexts;
67 struct _GtkIMModuleClass
69 GTypeModuleClass parent_class;
72 GType gtk_im_module_get_type (void);
74 gint n_loaded_contexts = 0;
75 static GHashTable *contexts_hash = NULL;
76 static GSList *modules_list = NULL;
78 static GObjectClass *parent_class = NULL;
81 gtk_im_module_load (GTypeModule *module)
83 GtkIMModule *im_module = GTK_IM_MODULE (module);
85 im_module->library = g_module_open (im_module->path, 0);
86 if (!im_module->library)
88 g_warning (g_module_error());
92 /* extract symbols from the lib */
93 if (!g_module_symbol (im_module->library, "im_module_init",
94 (gpointer *)&im_module->init) ||
95 !g_module_symbol (im_module->library, "im_module_exit",
96 (gpointer *)&im_module->exit) ||
97 !g_module_symbol (im_module->library, "im_module_list",
98 (gpointer *)&im_module->list) ||
99 !g_module_symbol (im_module->library, "im_module_create",
100 (gpointer *)&im_module->create))
102 g_warning (g_module_error());
103 g_module_close (im_module->library);
108 /* call the theme's init (theme_init) function to let it */
109 /* setup anything it needs to set up. */
110 im_module->init (module);
116 gtk_im_module_unload (GTypeModule *module)
118 GtkIMModule *im_module = GTK_IM_MODULE (module);
122 g_module_close (im_module->library);
123 im_module->library = NULL;
125 im_module->init = NULL;
126 im_module->exit = NULL;
127 im_module->list = NULL;
128 im_module->create = NULL;
131 /* This only will ever be called if an error occurs during
135 gtk_im_module_finalize (GObject *object)
137 GtkIMModule *module = GTK_IM_MODULE (object);
139 g_free (module->path);
141 parent_class->finalize (object);
145 gtk_im_module_class_init (GtkIMModuleClass *class)
147 GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class);
148 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
150 parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (class));
152 module_class->load = gtk_im_module_load;
153 module_class->unload = gtk_im_module_unload;
155 gobject_class->finalize = gtk_im_module_finalize;
159 gtk_im_module_get_type (void)
161 static GType im_module_type = 0;
165 static const GTypeInfo im_module_info = {
166 sizeof (GtkIMModuleClass),
167 NULL, /* base_init */
168 NULL, /* base_finalize */
169 (GClassInitFunc) gtk_im_module_class_init,
170 NULL, /* class_finalize */
171 NULL, /* class_data */
172 sizeof (GtkIMModule),
174 NULL, /* instance_init */
177 im_module_type = g_type_register_static (G_TYPE_TYPE_MODULE, "GtkIMModule", &im_module_info, 0);
180 return im_module_type;
184 free_info (GtkIMContextInfo *info)
186 g_free ((char *)info->context_id);
187 g_free ((char *)info->context_name);
188 g_free ((char *)info->domain);
189 g_free ((char *)info->domain_dirname);
190 g_free ((char *)info->default_locales);
195 add_module (GtkIMModule *module, GSList *infos)
197 GSList *tmp_list = infos;
199 gint n = g_slist_length (infos);
200 module->contexts = g_new (GtkIMContextInfo *, n);
204 GtkIMContextInfo *info = tmp_list->data;
206 if (g_hash_table_lookup (contexts_hash, info->context_id))
208 free_info (info); /* Duplicate */
212 g_hash_table_insert (contexts_hash, (char *)info->context_id, module);
213 module->contexts[i++] = tmp_list->data;
217 tmp_list = tmp_list->next;
219 g_slist_free (infos);
220 module->n_contexts = i;
222 modules_list = g_slist_prepend (modules_list, module);
226 gtk_im_module_init ()
228 GString *line_buf = g_string_new (NULL);
229 GString *tmp_buf = g_string_new (NULL);
230 gchar *filename = gtk_rc_get_im_module_file();
232 gboolean have_error = FALSE;
234 GtkIMModule *module = NULL;
235 GSList *infos = NULL;
237 contexts_hash = g_hash_table_new (g_str_hash, g_str_equal);
239 file = fopen (filename, "r");
242 g_warning ("Can not open Input Method module file '%s': %s",
243 filename, g_strerror (errno));
244 /* We are leaking all kinds of memory here. */
248 while (!have_error && pango_read_line (file, line_buf))
254 if (!pango_skip_space (&p))
256 /* Blank line marking the end of a module
258 if (module && *p != '#')
260 add_module (module, infos);
270 /* Read a module location
272 module = g_object_new (GTK_TYPE_IM_MODULE, NULL);
274 if (!pango_scan_string (&p, tmp_buf) ||
275 pango_skip_space (&p))
277 g_warning ("Error parsing context info in '%s'\n %s",
278 filename, line_buf->str);
282 module->path = g_strdup (tmp_buf->str);
283 g_type_module_set_name (G_TYPE_MODULE (module), module->path);
287 GtkIMContextInfo *info = g_new0 (GtkIMContextInfo, 1);
289 /* Read information about a context type
291 if (!pango_scan_string (&p, tmp_buf))
293 info->context_id = g_strdup (tmp_buf->str);
295 if (!pango_scan_string (&p, tmp_buf))
297 info->context_name = g_strdup (tmp_buf->str);
299 if (!pango_scan_string (&p, tmp_buf))
301 info->domain = g_strdup (tmp_buf->str);
303 if (!pango_scan_string (&p, tmp_buf))
305 info->domain_dirname = g_strdup (tmp_buf->str);
307 if (!pango_scan_string (&p, tmp_buf))
309 info->default_locales = g_strdup (tmp_buf->str);
311 if (pango_skip_space (&p))
314 infos = g_slist_prepend (infos, info);
318 g_warning ("Error parsing context info in '%s'\n %s",
319 filename, line_buf->str);
326 GSList *tmp_list = infos;
329 free_info (tmp_list->data);
330 tmp_list = tmp_list->next;
332 g_slist_free (infos);
334 g_object_unref (G_OBJECT (module));
337 add_module (module, infos);
340 g_string_free (line_buf, TRUE);
341 g_string_free (tmp_buf, TRUE);
346 * _gtk_im_module_list:
347 * @contexts: location to store an array of pointers to #GtkIMContextInfo
348 * this array should be freed with g_free() when you are finished.
349 * The structures it points are statically allocated and should
350 * not be modified or freed.
351 * @n_contexts: the length of the array stored in @contexts
353 * List all available types of input method context
356 _gtk_im_module_list (const GtkIMContextInfo ***contexts,
361 static const GtkIMContextInfo simple_context_info = {
370 gtk_im_module_init ();
373 *n_contexts = (n_loaded_contexts + 1);
380 *contexts = g_new (const GtkIMContextInfo *, n_loaded_contexts + 1);
382 (*contexts)[n++] = &simple_context_info;
384 tmp_list = modules_list;
387 GtkIMModule *module = tmp_list->data;
389 for (i=0; i<module->n_contexts; i++)
390 (*contexts)[n++] = module->contexts[i];
392 tmp_list = tmp_list->next;
398 * _gtk_im_module_create:
399 * @context_id: the context ID for the context type to create
401 * Create an IM context of a type specified by the string
404 * Return value: a newly created input context of or @context_id, or
405 * if that could not be created, a newly created GtkIMContextSimple.
408 _gtk_im_module_create (const gchar *context_id)
410 GtkIMModule *im_module;
411 GtkIMContext *context = NULL;
414 gtk_im_module_init ();
416 if (strcmp (context_id, SIMPLE_ID) != 0)
418 im_module = g_hash_table_lookup (contexts_hash, context_id);
421 g_warning ("Attempt to load unknown IM context type '%s'", context_id);
425 if (g_type_module_use (G_TYPE_MODULE (im_module)))
427 context = im_module->create (context_id);
428 g_type_module_unuse (G_TYPE_MODULE (im_module));
432 g_warning ("Loading IM context type '%s' failed", context_id);
437 return gtk_im_context_simple_new ();
442 /* Match @locale against @against.
444 * 'en_US' against 'en_US' => 4
445 * 'en_US' against 'en' => 3
446 * 'en', 'en_UK' against 'en_US' => 2
447 * all locales, against '*' => 1
450 match_locale (const gchar *locale,
451 const gchar *against,
454 if (strcmp (against, "*") == 0)
457 if (strcmp (locale, against) == 0)
460 if (strncmp (locale, against, 2) == 0)
461 return (against_len == 2) ? 3 : 2;
467 * _gtk_im_module_get_default_context_id:
468 * @locale: a locale id in the form 'en_US'
470 * Return the context_id of the best IM context type
471 * for the given locale ID.
473 * Return value: the context ID (will never be %NULL)
474 * the value is newly allocated and must be freed
478 _gtk_im_module_get_default_context_id (const gchar *locale)
481 const gchar *context_id = NULL;
482 gint best_goodness = 0;
484 gchar *tmp_locale, *tmp;
488 gtk_im_module_init ();
490 envvar = g_getenv ("GTK_IM_MODULE");
491 if (envvar && g_hash_table_lookup (contexts_hash, envvar))
492 return g_strdup (envvar);
494 /* Strip the locale code down to the essentials
496 tmp_locale = g_strdup (locale);
497 tmp = strchr (tmp_locale, '.');
500 tmp = strchr (tmp_locale, '@');
504 tmp_list = modules_list;
507 GtkIMModule *module = tmp_list->data;
509 for (i=0; i<module->n_contexts; i++)
511 const gchar *p = module->contexts[i]->default_locales;
514 const gchar *q = strchr (p, ':');
515 gint goodness = match_locale (tmp_locale, p, q ? q - p : strlen (p));
517 if (goodness > best_goodness)
519 context_id = module->contexts[i]->context_id;
520 best_goodness = goodness;
523 p = q ? q + 1 : NULL;
527 tmp_list = tmp_list->next;
532 return g_strdup (context_id ? context_id : SIMPLE_ID);