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