1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
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.
8  *
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.
13  *
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.
17  */
18 
19 /*
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/.
24  */
25 
26 #include "config.h"
27 
28 #include <errno.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include <glib/gstdio.h>
34 #include <gmodule.h>
35 #include "gtkimmodule.h"
36 #include "gtkimcontextsimple.h"
37 #include "gtksettings.h"
38 #include "gtkmain.h"
39 #include "gtkrc.h"
40 #include "gtkintl.h"
41 #include "gtkalias.h"
42 
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.
46  */
47 
48 #ifdef __GTK_PRIVATE_H__
49 #error gtkprivate.h should not be included in this file
50 #endif
51 
52 #define SIMPLE_ID "gtk-im-context-simple"
53 
54 /**
55  * GtkIMContextInfo:
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 dgettext()
59  * @domain_dirname: Name of locale directory for use with bindtextdomain()
60  * @default_locales: A colon-separated list of locales where this input method
61  *   should be the default. The asterisk "*" sets the default for all locales.
62  *
63  * Bookkeeping information about a loadable input method.
64  */
65 
66 typedef struct _GtkIMModule      GtkIMModule;
67 typedef struct _GtkIMModuleClass GtkIMModuleClass;
68 
69 #define GTK_TYPE_IM_MODULE          (gtk_im_module_get_type ())
70 #define GTK_IM_MODULE(im_module)    (G_TYPE_CHECK_INSTANCE_CAST ((im_module), GTK_TYPE_IM_MODULE, GtkIMModule))
71 #define GTK_IS_IM_MODULE(im_module) (G_TYPE_CHECK_INSTANCE_TYPE ((im_module), GTK_TYPE_IM_MODULE))
72 
73 struct _GtkIMModule
74 {
75   GTypeModule parent_instance;
76 
77   gboolean builtin;
78 
79   GModule *library;
80 
81   void          (*list)   (const GtkIMContextInfo ***contexts,
82  		           guint                    *n_contexts);
83   void          (*init)   (GTypeModule              *module);
84   void          (*exit)   (void);
85   GtkIMContext *(*create) (const gchar              *context_id);
86 
87   GtkIMContextInfo **contexts;
88   guint n_contexts;
89 
90   gchar *path;
91 };
92 
93 struct _GtkIMModuleClass
94 {
95   GTypeModuleClass parent_class;
96 };
97 
98 static GType gtk_im_module_get_type (void);
99 
100 static gint n_loaded_contexts = 0;
101 static GHashTable *contexts_hash = NULL;
102 static GSList *modules_list = NULL;
103 
104 static GObjectClass *parent_class = NULL;
105 
106 static gboolean
gtk_im_module_load(GTypeModule * module)107 gtk_im_module_load (GTypeModule *module)
108 {
109   GtkIMModule *im_module = GTK_IM_MODULE (module);
110 
111   if (!im_module->builtin)
112     {
113       im_module->library = g_module_open (im_module->path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
114       if (!im_module->library)
115 	{
116 	  g_warning ("%s", g_module_error());
117 	  return FALSE;
118 	}
119 
120       /* extract symbols from the lib */
121       if (!g_module_symbol (im_module->library, "im_module_init",
122 			    (gpointer *)&im_module->init) ||
123 	  !g_module_symbol (im_module->library, "im_module_exit",
124 			    (gpointer *)&im_module->exit) ||
125 	  !g_module_symbol (im_module->library, "im_module_list",
126 			    (gpointer *)&im_module->list) ||
127 	  !g_module_symbol (im_module->library, "im_module_create",
128 			    (gpointer *)&im_module->create))
129 	{
130 	  g_warning ("%s", g_module_error());
131 	  g_module_close (im_module->library);
132 
133 	  return FALSE;
134 	}
135     }
136 
137   /* call the module's init function to let it */
138   /* setup anything it needs to set up. */
139   im_module->init (module);
140 
141   return TRUE;
142 }
143 
144 static void
gtk_im_module_unload(GTypeModule * module)145 gtk_im_module_unload (GTypeModule *module)
146 {
147   GtkIMModule *im_module = GTK_IM_MODULE (module);
148 
149   im_module->exit();
150 
151   if (!im_module->builtin)
152     {
153       g_module_close (im_module->library);
154       im_module->library = NULL;
155 
156       im_module->init = NULL;
157       im_module->exit = NULL;
158       im_module->list = NULL;
159       im_module->create = NULL;
160     }
161 }
162 
163 /* This only will ever be called if an error occurs during
164  * initialization
165  */
166 static void
gtk_im_module_finalize(GObject * object)167 gtk_im_module_finalize (GObject *object)
168 {
169   GtkIMModule *module = GTK_IM_MODULE (object);
170 
171   g_free (module->path);
172 
173   parent_class->finalize (object);
174 }
175 
G_DEFINE_TYPE(GtkIMModule,gtk_im_module,G_TYPE_TYPE_MODULE)176 G_DEFINE_TYPE (GtkIMModule, gtk_im_module, G_TYPE_TYPE_MODULE)
177 
178 static void
179 gtk_im_module_class_init (GtkIMModuleClass *class)
180 {
181   GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class);
182   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
183 
184   parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (class));
185 
186   module_class->load = gtk_im_module_load;
187   module_class->unload = gtk_im_module_unload;
188 
189   gobject_class->finalize = gtk_im_module_finalize;
190 }
191 
192 static void
gtk_im_module_init(GtkIMModule * object)193 gtk_im_module_init (GtkIMModule* object)
194 {
195 }
196 
197 static void
free_info(GtkIMContextInfo * info)198 free_info (GtkIMContextInfo *info)
199 {
200   g_free ((char *)info->context_id);
201   g_free ((char *)info->context_name);
202   g_free ((char *)info->domain);
203   g_free ((char *)info->domain_dirname);
204   g_free ((char *)info->default_locales);
205   g_free (info);
206 }
207 
208 static void
add_module(GtkIMModule * module,GSList * infos)209 add_module (GtkIMModule *module, GSList *infos)
210 {
211   GSList *tmp_list = infos;
212   gint i = 0;
213   gint n = g_slist_length (infos);
214   module->contexts = g_new (GtkIMContextInfo *, n);
215 
216   while (tmp_list)
217     {
218       GtkIMContextInfo *info = tmp_list->data;
219 
220       if (g_hash_table_lookup (contexts_hash, info->context_id))
221 	{
222 	  free_info (info);	/* Duplicate */
223 	}
224       else
225 	{
226 	  g_hash_table_insert (contexts_hash, (char *)info->context_id, module);
227 	  module->contexts[i++] = tmp_list->data;
228 	  n_loaded_contexts++;
229 	}
230 
231       tmp_list = tmp_list->next;
232     }
233   g_slist_free (infos);
234   module->n_contexts = i;
235 
236   modules_list = g_slist_prepend (modules_list, module);
237 }
238 
239 #ifdef G_OS_WIN32
240 
241 static void
correct_libdir_prefix(gchar ** path)242 correct_libdir_prefix (gchar **path)
243 {
244   /* GTK_LIBDIR here is supposed to still have the definition from
245    * Makefile.am, i.e. the build-time value. Do *not* include gtkprivate.h
246    * in this file.
247    */
248   if (strncmp (*path, GTK_LIBDIR, strlen (GTK_LIBDIR)) == 0)
249     {
250       /* This is an entry put there by make install on the
251        * packager's system. On Windows a prebuilt GTK+
252        * package can be installed in a random
253        * location. The immodules.cache file distributed in
254        * such a package contains paths from the package
255        * builder's machine. Replace the path with the real
256        * one on this machine.
257        */
258       extern const gchar *_gtk_get_libdir ();
259       gchar *tem = *path;
260       *path = g_strconcat (_gtk_get_libdir (), tem + strlen (GTK_LIBDIR), NULL);
261       g_free (tem);
262     }
263 }
264 
265 static void
correct_localedir_prefix(gchar ** path)266 correct_localedir_prefix (gchar **path)
267 {
268   /* As above, but for GTK_LOCALEDIR. Use separate function in case
269    * GTK_LOCALEDIR isn't a subfolder of GTK_LIBDIR.
270    */
271   if (strncmp (*path, GTK_LOCALEDIR, strlen (GTK_LOCALEDIR)) == 0)
272     {
273       extern const gchar *_gtk_get_localedir ();
274       gchar *tem = *path;
275       *path = g_strconcat (_gtk_get_localedir (), tem + strlen (GTK_LOCALEDIR), NULL);
276       g_free (tem);
277     }
278 }
279 #endif
280 
281 
282 G_GNUC_UNUSED static GtkIMModule *
add_builtin_module(const gchar * module_name,const GtkIMContextInfo ** contexts,int n_contexts)283 add_builtin_module (const gchar             *module_name,
284 		    const GtkIMContextInfo **contexts,
285 		    int                      n_contexts)
286 {
287   GtkIMModule *module = g_object_new (GTK_TYPE_IM_MODULE, NULL);
288   GSList *infos = NULL;
289   int i;
290 
291   for (i = 0; i < n_contexts; i++)
292     {
293       GtkIMContextInfo *info = g_new (GtkIMContextInfo, 1);
294       info->context_id = g_strdup (contexts[i]->context_id);
295       info->context_name = g_strdup (contexts[i]->context_name);
296       info->domain = g_strdup (contexts[i]->domain);
297       info->domain_dirname = g_strdup (contexts[i]->domain_dirname);
298 #ifdef G_OS_WIN32
299       correct_localedir_prefix ((char **) &info->domain_dirname);
300 #endif
301       info->default_locales = g_strdup (contexts[i]->default_locales);
302       infos = g_slist_prepend (infos, info);
303     }
304 
305   module->builtin = TRUE;
306   g_type_module_set_name (G_TYPE_MODULE (module), module_name);
307   add_module (module, infos);
308 
309   return module;
310 }
311 
312 static void
gtk_im_module_initialize(void)313 gtk_im_module_initialize (void)
314 {
315   GString *line_buf = g_string_new (NULL);
316   GString *tmp_buf = g_string_new (NULL);
317   gchar *filename = gtk_rc_get_im_module_file();
318   FILE *file;
319   gboolean have_error = FALSE;
320 
321   GtkIMModule *module = NULL;
322   GSList *infos = NULL;
323 
324   contexts_hash = g_hash_table_new (g_str_hash, g_str_equal);
325 
326 #define do_builtin(m)							\
327   {									\
328     const GtkIMContextInfo **contexts;					\
329     int n_contexts;							\
330     extern void _gtk_immodule_ ## m ## _list (const GtkIMContextInfo ***contexts, \
331 					      guint                    *n_contexts); \
332     extern void _gtk_immodule_ ## m ## _init (GTypeModule *module);	\
333     extern void _gtk_immodule_ ## m ## _exit (void);			\
334     extern GtkIMContext *_gtk_immodule_ ## m ## _create (const gchar *context_id); \
335 									\
336     _gtk_immodule_ ## m ## _list (&contexts, &n_contexts);		\
337     module = add_builtin_module (#m, contexts, n_contexts);		\
338     module->init = _gtk_immodule_ ## m ## _init;			\
339     module->exit = _gtk_immodule_ ## m ## _exit;			\
340     module->create = _gtk_immodule_ ## m ## _create;			\
341     module = NULL;							\
342   }
343 
344 #ifdef INCLUDE_IM_am_et
345   do_builtin (am_et);
346 #endif
347 #ifdef INCLUDE_IM_cedilla
348   do_builtin (cedilla);
349 #endif
350 #ifdef INCLUDE_IM_cyrillic_translit
351   do_builtin (cyrillic_translit);
352 #endif
353 #ifdef INCLUDE_IM_ime
354   do_builtin (ime);
355 #endif
356 #ifdef INCLUDE_IM_inuktitut
357   do_builtin (inuktitut);
358 #endif
359 #ifdef INCLUDE_IM_ipa
360   do_builtin (ipa);
361 #endif
362 #ifdef INCLUDE_IM_multipress
363   do_builtin (multipress);
364 #endif
365 #ifdef INCLUDE_IM_thai
366   do_builtin (thai);
367 #endif
368 #ifdef INCLUDE_IM_ti_er
369   do_builtin (ti_er);
370 #endif
371 #ifdef INCLUDE_IM_ti_et
372   do_builtin (ti_et);
373 #endif
374 #ifdef INCLUDE_IM_viqr
375   do_builtin (viqr);
376 #endif
377 #ifdef INCLUDE_IM_xim
378   do_builtin (xim);
379 #endif
380 
381 #undef do_builtin
382 
383   file = g_fopen (filename, "r");
384   if (!file)
385     {
386       /* In case someone wants only the default input method,
387        * we allow no file at all.
388        */
389       g_string_free (line_buf, TRUE);
390       g_string_free (tmp_buf, TRUE);
391       g_free (filename);
392       return;
393     }
394 
395   while (!have_error && pango_read_line (file, line_buf))
396     {
397       const char *p;
398 
399       p = line_buf->str;
400 
401       if (!pango_skip_space (&p))
402 	{
403 	  /* Blank line marking the end of a module
404 	   */
405 	  if (module && *p != '#')
406 	    {
407 	      add_module (module, infos);
408 	      module = NULL;
409 	      infos = NULL;
410 	    }
411 
412 	  continue;
413 	}
414 
415       if (!module)
416 	{
417 	  /* Read a module location
418 	   */
419 	  module = g_object_new (GTK_TYPE_IM_MODULE, NULL);
420 
421 	  if (!pango_scan_string (&p, tmp_buf) ||
422 	      pango_skip_space (&p))
423 	    {
424 	      g_warning ("Error parsing context info in '%s'\n  %s",
425 			 filename, line_buf->str);
426 	      have_error = TRUE;
427 	    }
428 
429 	  module->path = g_strdup (tmp_buf->str);
430 #ifdef G_OS_WIN32
431 	  correct_libdir_prefix (&module->path);
432 #endif
433 	  g_type_module_set_name (G_TYPE_MODULE (module), module->path);
434 	}
435       else
436 	{
437 	  GtkIMContextInfo *info = g_new0 (GtkIMContextInfo, 1);
438 
439 	  /* Read information about a context type
440 	   */
441 	  if (!pango_scan_string (&p, tmp_buf))
442 	    goto context_error;
443 	  info->context_id = g_strdup (tmp_buf->str);
444 
445 	  if (!pango_scan_string (&p, tmp_buf))
446 	    goto context_error;
447 	  info->context_name = g_strdup (tmp_buf->str);
448 
449 	  if (!pango_scan_string (&p, tmp_buf))
450 	    goto context_error;
451 	  info->domain = g_strdup (tmp_buf->str);
452 
453 	  if (!pango_scan_string (&p, tmp_buf))
454 	    goto context_error;
455 	  info->domain_dirname = g_strdup (tmp_buf->str);
456 #ifdef G_OS_WIN32
457 	  correct_localedir_prefix ((char **) &info->domain_dirname);
458 #endif
459 
460 	  if (!pango_scan_string (&p, tmp_buf))
461 	    goto context_error;
462 	  info->default_locales = g_strdup (tmp_buf->str);
463 
464 	  if (pango_skip_space (&p))
465 	    goto context_error;
466 
467 	  infos = g_slist_prepend (infos, info);
468 	  continue;
469 
470 	context_error:
471 	  g_warning ("Error parsing context info in '%s'\n  %s",
472 		     filename, line_buf->str);
473 	  have_error = TRUE;
474 	}
475     }
476 
477   if (have_error)
478     {
479       GSList *tmp_list = infos;
480       while (tmp_list)
481 	{
482 	  free_info (tmp_list->data);
483 	  tmp_list = tmp_list->next;
484 	}
485       g_slist_free (infos);
486 
487       g_object_unref (module);
488     }
489   else if (module)
490     add_module (module, infos);
491 
492   fclose (file);
493   g_string_free (line_buf, TRUE);
494   g_string_free (tmp_buf, TRUE);
495   g_free (filename);
496 }
497 
498 static gint
compare_gtkimcontextinfo_name(const GtkIMContextInfo ** a,const GtkIMContextInfo ** b)499 compare_gtkimcontextinfo_name(const GtkIMContextInfo **a,
500 			      const GtkIMContextInfo **b)
501 {
502   return g_utf8_collate ((*a)->context_name, (*b)->context_name);
503 }
504 
505 /**
506  * _gtk_im_module_list:
507  * @contexts: location to store an array of pointers to #GtkIMContextInfo
508  *            this array should be freed with g_free() when you are finished.
509  *            The structures it points are statically allocated and should
510  *            not be modified or freed.
511  * @n_contexts: the length of the array stored in @contexts
512  *
513  * List all available types of input method context
514  */
515 void
_gtk_im_module_list(const GtkIMContextInfo *** contexts,guint * n_contexts)516 _gtk_im_module_list (const GtkIMContextInfo ***contexts,
517 		     guint                    *n_contexts)
518 {
519   int n = 0;
520 
521   static
522 #ifndef G_OS_WIN32
523 	  const
524 #endif
525 		GtkIMContextInfo simple_context_info = {
526     SIMPLE_ID,
527     N_("Simple"),
528     GETTEXT_PACKAGE,
529 #ifdef GTK_LOCALEDIR
530     GTK_LOCALEDIR,
531 #else
532     "",
533 #endif
534     ""
535   };
536 
537 #ifdef G_OS_WIN32
538   static gboolean beenhere = FALSE;
539 #endif
540 
541   if (!contexts_hash)
542     gtk_im_module_initialize ();
543 
544 #ifdef G_OS_WIN32
545   if (!beenhere)
546     {
547       beenhere = TRUE;
548       /* correct_localedir_prefix() requires its parameter to be a
549        * malloced string
550        */
551       simple_context_info.domain_dirname = g_strdup (simple_context_info.domain_dirname);
552       correct_localedir_prefix ((char **) &simple_context_info.domain_dirname);
553     }
554 #endif
555 
556   if (n_contexts)
557     *n_contexts = (n_loaded_contexts + 1);
558 
559   if (contexts)
560     {
561       GSList *tmp_list;
562       int i;
563 
564       *contexts = g_new (const GtkIMContextInfo *, n_loaded_contexts + 1);
565 
566       (*contexts)[n++] = &simple_context_info;
567 
568       tmp_list = modules_list;
569       while (tmp_list)
570 	{
571 	  GtkIMModule *module = tmp_list->data;
572 
573 	  for (i=0; i<module->n_contexts; i++)
574 	    (*contexts)[n++] = module->contexts[i];
575 
576 	  tmp_list = tmp_list->next;
577 	}
578 
579       /* fisrt element (Default) should always be at top */
580       qsort ((*contexts)+1, n-1, sizeof (GtkIMContextInfo *), (GCompareFunc)compare_gtkimcontextinfo_name);
581     }
582 }
583 
584 /**
585  * _gtk_im_module_create:
586  * @context_id: the context ID for the context type to create
587  *
588  * Create an IM context of a type specified by the string
589  * ID @context_id.
590  *
591  * Return value: a newly created input context of or @context_id, or
592  *     if that could not be created, a newly created GtkIMContextSimple.
593  */
594 GtkIMContext *
_gtk_im_module_create(const gchar * context_id)595 _gtk_im_module_create (const gchar *context_id)
596 {
597   GtkIMModule *im_module;
598   GtkIMContext *context = NULL;
599 
600   if (!contexts_hash)
601     gtk_im_module_initialize ();
602 
603   if (strcmp (context_id, SIMPLE_ID) != 0)
604     {
605       im_module = g_hash_table_lookup (contexts_hash, context_id);
606       if (!im_module)
607 	{
608 	  g_warning ("Attempt to load unknown IM context type '%s'", context_id);
609 	}
610       else
611 	{
612 	  if (g_type_module_use (G_TYPE_MODULE (im_module)))
613 	    {
614 	      context = im_module->create (context_id);
615 	      g_type_module_unuse (G_TYPE_MODULE (im_module));
616 	    }
617 
618 	  if (!context)
619 	    g_warning ("Loading IM context type '%s' failed", context_id);
620 	}
621     }
622 
623   if (!context)
624      return gtk_im_context_simple_new ();
625   else
626     return context;
627 }
628 
629 /* Match @locale against @against.
630  *
631  * 'en_US' against 'en_US'       => 4
632  * 'en_US' against 'en'          => 3
633  * 'en', 'en_UK' against 'en_US' => 2
634  *  all locales, against '*' 	 => 1
635  */
636 static gint
match_locale(const gchar * locale,const gchar * against,gint against_len)637 match_locale (const gchar *locale,
638 	      const gchar *against,
639 	      gint         against_len)
640 {
641   if (strcmp (against, "*") == 0)
642     return 1;
643 
644   if (g_ascii_strcasecmp (locale, against) == 0)
645     return 4;
646 
647   if (g_ascii_strncasecmp (locale, against, 2) == 0)
648     return (against_len == 2) ? 3 : 2;
649 
650   return 0;
651 }
652 
653 static const gchar *
lookup_immodule(gchar ** immodules_list)654 lookup_immodule (gchar **immodules_list)
655 {
656   while (immodules_list && *immodules_list)
657     {
658       if (g_strcmp0 (*immodules_list, SIMPLE_ID) == 0)
659         return SIMPLE_ID;
660       else
661 	{
662 	  gboolean found;
663 	  gchar *context_id;
664 	  found = g_hash_table_lookup_extended (contexts_hash, *immodules_list,
665 						&context_id, NULL);
666 	  if (found)
667 	    return context_id;
668 	}
669       immodules_list++;
670     }
671 
672   return NULL;
673 }
674 
675 /**
676  * _gtk_im_module_get_default_context_id:
677  * @client_window: a window
678  *
679  * Return the context_id of the best IM context type
680  * for the given window.
681  *
682  * Return value: the context ID (will never be %NULL)
683  */
684 const gchar *
_gtk_im_module_get_default_context_id(GdkWindow * client_window)685 _gtk_im_module_get_default_context_id (GdkWindow *client_window)
686 {
687   GSList *tmp_list;
688   const gchar *context_id = NULL;
689   gint best_goodness = 0;
690   gint i;
691   gchar *tmp_locale, *tmp, **immodules;
692   const gchar *envvar;
693   GdkScreen *screen;
694   GtkSettings *settings;
695 
696   if (!contexts_hash)
697     gtk_im_module_initialize ();
698 
699   envvar = g_getenv("GTK_IM_MODULE");
700   if (envvar)
701     {
702         immodules = g_strsplit(envvar, ":", 0);
703         context_id = lookup_immodule(immodules);
704         g_strfreev(immodules);
705 
706         if (context_id)
707           return context_id;
708     }
709 
710   /* Check if the certain immodule is set in XSETTINGS.
711    */
712   if (GDK_IS_DRAWABLE (client_window))
713     {
714       screen = gdk_window_get_screen (client_window);
715       settings = gtk_settings_get_for_screen (screen);
716       g_object_get (G_OBJECT (settings), "gtk-im-module", &tmp, NULL);
717       if (tmp)
718         {
719           immodules = g_strsplit(tmp, ":", 0);
720           context_id = lookup_immodule(immodules);
721           g_strfreev(immodules);
722           g_free (tmp);
723 
724        	  if (context_id)
725             return context_id;
726         }
727     }
728 
729   /* Strip the locale code down to the essentials
730    */
731   tmp_locale = _gtk_get_lc_ctype ();
732   tmp = strchr (tmp_locale, '.');
733   if (tmp)
734     *tmp = '\0';
735   tmp = strchr (tmp_locale, '@');
736   if (tmp)
737     *tmp = '\0';
738 
739   tmp_list = modules_list;
740   while (tmp_list)
741     {
742       GtkIMModule *module = tmp_list->data;
743 
744       for (i = 0; i < module->n_contexts; i++)
745 	{
746 	  const gchar *p = module->contexts[i]->default_locales;
747 	  while (p)
748 	    {
749 	      const gchar *q = strchr (p, ':');
750 	      gint goodness = match_locale (tmp_locale, p, q ? q - p : strlen (p));
751 
752 	      if (goodness > best_goodness)
753 		{
754 		  context_id = module->contexts[i]->context_id;
755 		  best_goodness = goodness;
756 		}
757 
758 	      p = q ? q + 1 : NULL;
759 	    }
760 	}
761 
762       tmp_list = tmp_list->next;
763     }
764 
765   g_free (tmp_locale);
766 
767   return context_id ? context_id : SIMPLE_ID;
768 }
769