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