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 const 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));
247 while (!have_error && pango_read_line (file, line_buf))
253 if (!pango_skip_space (&p))
255 /* Blank line marking the end of a module
257 if (module && *p != '#')
259 add_module (module, infos);
269 /* Read a module location
271 module = g_object_new (GTK_TYPE_IM_MODULE, NULL);
273 if (!pango_scan_string (&p, tmp_buf) ||
274 pango_skip_space (&p))
276 g_warning ("Error parsing context info in '%s'\n %s",
277 filename, line_buf->str);
281 module->path = g_strdup (tmp_buf->str);
282 g_type_module_set_name (G_TYPE_MODULE (module), module->path);
286 GtkIMContextInfo *info = g_new0 (GtkIMContextInfo, 1);
288 /* Read information about a context type
290 if (!pango_scan_string (&p, tmp_buf))
292 info->context_id = g_strdup (tmp_buf->str);
294 if (!pango_scan_string (&p, tmp_buf))
296 info->context_name = g_strdup (tmp_buf->str);
298 if (!pango_scan_string (&p, tmp_buf))
300 info->domain = g_strdup (tmp_buf->str);
302 if (!pango_scan_string (&p, tmp_buf))
304 info->domain_dirname = g_strdup (tmp_buf->str);
306 if (!pango_scan_string (&p, tmp_buf))
308 info->default_locales = g_strdup (tmp_buf->str);
310 if (pango_skip_space (&p))
313 infos = g_slist_prepend (infos, info);
317 g_warning ("Error parsing context info in '%s'\n %s",
318 filename, line_buf->str);
325 GSList *tmp_list = infos;
328 free_info (tmp_list->data);
329 tmp_list = tmp_list->next;
331 g_slist_free (infos);
333 g_object_unref (G_OBJECT (module));
336 add_module (module, infos);
339 g_string_free (line_buf, TRUE);
340 g_string_free (tmp_buf, TRUE);
344 * _gtk_im_module_list:
345 * @contexts: location to store an array of pointers to #GtkIMContextInfo
346 * this array should be freed with g_free() when you are finished.
347 * The structures it points are statically allocated and should
348 * not be modified or freed.
349 * @n_contexts: the length of the array stored in @contexts
351 * List all available types of input method context
354 _gtk_im_module_list (const GtkIMContextInfo ***contexts,
359 static const GtkIMContextInfo simple_context_info = {
368 gtk_im_module_init ();
371 *n_contexts = (n_loaded_contexts + 1);
378 *contexts = g_new (const GtkIMContextInfo *, n_loaded_contexts + 1);
380 (*contexts)[n++] = &simple_context_info;
382 tmp_list = modules_list;
385 GtkIMModule *module = tmp_list->data;
387 for (i=0; i<module->n_contexts; i++)
388 (*contexts)[n++] = module->contexts[i];
390 tmp_list = tmp_list->next;
396 * _gtk_im_module_create:
397 * @context_id: the context ID for the context type to create
399 * Create an IM context of a type specified by the string
402 * Return value: a newly created input context of or @context_id, or
403 * if that could not be created, a newly created GtkIMContextSimple.
406 _gtk_im_module_create (const gchar *context_id)
408 GtkIMModule *im_module;
409 GtkIMContext *context = NULL;
412 gtk_im_module_init ();
414 if (strcmp (context_id, SIMPLE_ID) != 0)
416 im_module = g_hash_table_lookup (contexts_hash, context_id);
419 g_warning ("Attempt to load unknown IM context type '%s'", context_id);
423 if (g_type_module_use (G_TYPE_MODULE (im_module)))
425 context = im_module->create (context_id);
426 g_type_module_unuse (G_TYPE_MODULE (im_module));
430 g_warning ("Loading IM context type '%s' failed", context_id);
435 return gtk_im_context_simple_new ();
440 /* Match @locale against @against.
442 * 'en_US' against 'en_US' => 3
443 * 'en_US' against 'en' => 2
444 * 'en', 'en_UK' against 'en_US' => 1
447 match_locale (const gchar *locale,
448 const gchar *against,
451 if (strcmp (locale, against) == 0)
454 if (strncmp (locale, against, 2) == 0)
455 return (against_len == 2) ? 2 : 1;
461 * _gtk_im_module_get_default_context_id:
462 * @locale: a locale id in the form 'en_US'
464 * Return the context_id of the best IM context type
465 * for the given locale ID.
467 * Return value: the context ID (will never be %NULL)
468 * the value is newly allocated and must be freed
472 _gtk_im_module_get_default_context_id (const gchar *locale)
475 const gchar *context_id = NULL;
476 gint best_goodness = 0;
478 gchar *tmp_locale, *tmp;
482 gtk_im_module_init ();
484 envvar = g_getenv ("GTK_IM_MODULE");
485 if (envvar && g_hash_table_lookup (contexts_hash, envvar))
486 return g_strdup (envvar);
488 /* Strip the locale code down to the essentials
490 tmp_locale = g_strdup (locale);
491 tmp = strchr (tmp_locale, '.');
494 tmp = strchr (tmp_locale, '@');
498 tmp_list = modules_list;
501 GtkIMModule *module = tmp_list->data;
503 for (i=0; i<module->n_contexts; i++)
505 const gchar *p = module->contexts[i]->default_locales;
508 const gchar *q = strchr (p, ':');
509 gint goodness = match_locale (tmp_locale, p, q ? q - p : strlen (p));
511 if (goodness > best_goodness)
513 context_id = module->contexts[i]->context_id;
514 best_goodness = goodness;
517 p = q ? q + 1 : NULL;
521 tmp_list = tmp_list->next;
526 return g_strdup (context_id ? context_id : SIMPLE_ID);