]> Pileus Git - ~andy/gtk/blob - gtk/gtkimmodule.c
Use G_MODULE_BIND_LAZY when opening 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  * Themes added by The Rasterman <raster@redhat.com>
5  * 
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.
10  *
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.
15  *
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.
19  */
20
21 /*
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/. 
26  */
27
28 #include <config.h>
29
30 #include <errno.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include <glib/gstdio.h>
36 #include <gmodule.h>
37 #include <pango/pango-utils.h>
38 #include "gtkimmodule.h"
39 #include "gtkimcontextsimple.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() function below will have to move somewhere
46  * 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   GModule *library;
67
68   void          (*list)   (const GtkIMContextInfo ***contexts,
69                            guint                    *n_contexts);
70   void          (*init)   (GTypeModule              *module);
71   void          (*exit)   (void);
72   GtkIMContext *(*create) (const gchar              *context_id);
73
74   GtkIMContextInfo **contexts;
75   guint n_contexts;
76
77   gchar *path;
78 };
79
80 struct _GtkIMModuleClass 
81 {
82   GTypeModuleClass parent_class;
83 };
84
85 static GType gtk_im_module_get_type (void);
86
87 static gint n_loaded_contexts = 0;
88 static GHashTable *contexts_hash = NULL;
89 static GSList *modules_list = NULL;
90
91 static GObjectClass *parent_class = NULL;
92
93 static gboolean
94 gtk_im_module_load (GTypeModule *module)
95 {
96   GtkIMModule *im_module = GTK_IM_MODULE (module);
97   
98   im_module->library = g_module_open (im_module->path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
99   if (!im_module->library)
100     {
101       g_warning (g_module_error());
102       return FALSE;
103     }
104   
105   /* extract symbols from the lib */
106   if (!g_module_symbol (im_module->library, "im_module_init",
107                         (gpointer *)&im_module->init) ||
108       !g_module_symbol (im_module->library, "im_module_exit", 
109                         (gpointer *)&im_module->exit) ||
110       !g_module_symbol (im_module->library, "im_module_list", 
111                         (gpointer *)&im_module->list) ||
112       !g_module_symbol (im_module->library, "im_module_create", 
113                         (gpointer *)&im_module->create))
114     {
115       g_warning (g_module_error());
116       g_module_close (im_module->library);
117       
118       return FALSE;
119     }
120             
121   /* call the theme's init (theme_init) function to let it */
122   /* setup anything it needs to set up. */
123   im_module->init (module);
124
125   return TRUE;
126 }
127
128 static void
129 gtk_im_module_unload (GTypeModule *module)
130 {
131   GtkIMModule *im_module = GTK_IM_MODULE (module);
132   
133   im_module->exit();
134
135   g_module_close (im_module->library);
136   im_module->library = NULL;
137
138   im_module->init = NULL;
139   im_module->exit = NULL;
140   im_module->list = NULL;
141   im_module->create = NULL;
142 }
143
144 /* This only will ever be called if an error occurs during
145  * initialization
146  */
147 static void
148 gtk_im_module_finalize (GObject *object)
149 {
150   GtkIMModule *module = GTK_IM_MODULE (object);
151
152   g_free (module->path);
153
154   parent_class->finalize (object);
155 }
156
157 static void
158 gtk_im_module_class_init (GtkIMModuleClass *class)
159 {
160   GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class);
161   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
162
163   parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (class));
164   
165   module_class->load = gtk_im_module_load;
166   module_class->unload = gtk_im_module_unload;
167
168   gobject_class->finalize = gtk_im_module_finalize;
169 }
170
171 static GType
172 gtk_im_module_get_type (void)
173 {
174   static GType im_module_type = 0;
175
176   if (!im_module_type)
177     {
178       static const GTypeInfo im_module_info = {
179         sizeof (GtkIMModuleClass),
180         NULL,           /* base_init */
181         NULL,           /* base_finalize */
182         (GClassInitFunc) gtk_im_module_class_init,
183         NULL,           /* class_finalize */
184         NULL,           /* class_data */
185         sizeof (GtkIMModule),
186         0,              /* n_preallocs */
187         NULL,           /* instance_init */
188       };
189
190       im_module_type =
191         g_type_register_static (G_TYPE_TYPE_MODULE, I_("GtkIMModule"),
192                                 &im_module_info, 0);
193     }
194   
195   return im_module_type;
196 }
197
198 static void
199 free_info (GtkIMContextInfo *info)
200 {
201   g_free ((char *)info->context_id);
202   g_free ((char *)info->context_name);
203   g_free ((char *)info->domain);
204   g_free ((char *)info->domain_dirname);
205   g_free ((char *)info->default_locales);
206   g_free (info);
207 }
208
209 static void
210 add_module (GtkIMModule *module, GSList *infos)
211 {
212   GSList *tmp_list = infos;
213   gint i = 0;
214   gint n = g_slist_length (infos);
215   module->contexts = g_new (GtkIMContextInfo *, n);
216
217   while (tmp_list)
218     {
219       GtkIMContextInfo *info = tmp_list->data;
220   
221       if (g_hash_table_lookup (contexts_hash, info->context_id))
222         {
223           free_info (info);     /* Duplicate */
224         }
225       else
226         {
227           g_hash_table_insert (contexts_hash, (char *)info->context_id, module);
228           module->contexts[i++] = tmp_list->data;
229           n_loaded_contexts++;
230         }
231       
232       tmp_list = tmp_list->next;
233     }
234   g_slist_free (infos);
235   module->n_contexts = i;
236
237   modules_list = g_slist_prepend (modules_list, module);
238 }
239
240 #if defined (G_OS_WIN32) && defined (GTK_LIBDIR)
241 /* This is needes on Win32, but not wanted when compiling with MSVC,
242  * as the makefile.msc doesn't define any GTK_LIBDIR value.
243  */
244
245 #define DO_CORRECT_LIBDIR_PREFIX /* Flag to check below whether to call this */
246
247 static void
248 correct_libdir_prefix (gchar **path)
249 {
250   /* GTK_LIBDIR here is supposed to still have the definition from
251    * Makefile.am, i.e. the build-time value. Do *not* include gtkprivate.h
252    * in this file.
253    */
254   if (strncmp (*path, GTK_LIBDIR, strlen (GTK_LIBDIR)) == 0)
255     {
256       /* This is an entry put there by make install on the
257        * packager's system. On Windows a prebuilt GTK+
258        * package can be installed in a random
259        * location. The gtk.immodules file distributed in
260        * such a package contains paths from the package
261        * builder's machine. Replace the path with the real
262        * one on this machine.
263        */
264       extern const gchar *_gtk_get_libdir ();
265       gchar *tem = *path;
266       *path = g_strconcat (_gtk_get_libdir (), tem + strlen (GTK_LIBDIR), NULL);
267       g_free (tem);
268     }
269 }
270 #endif
271
272
273 static void
274 gtk_im_module_init (void)
275 {
276   GString *line_buf = g_string_new (NULL);
277   GString *tmp_buf = g_string_new (NULL);
278   gchar *filename = gtk_rc_get_im_module_file();
279   FILE *file;
280   gboolean have_error = FALSE;
281
282   GtkIMModule *module = NULL;
283   GSList *infos = NULL;
284
285   contexts_hash = g_hash_table_new (g_str_hash, g_str_equal);
286
287   file = g_fopen (filename, "r");
288   if (!file)
289     {
290       /* In case someone wants only the default input method,
291        * we allow no file at all.
292        */
293       g_string_free (line_buf, TRUE);
294       g_string_free (tmp_buf, TRUE);
295       g_free (filename);
296       return;
297     }
298
299   while (!have_error && pango_read_line (file, line_buf))
300     {
301       const char *p;
302       
303       p = line_buf->str;
304
305       if (!pango_skip_space (&p))
306         {
307           /* Blank line marking the end of a module
308            */
309           if (module && *p != '#')
310             {
311               add_module (module, infos);
312               module = NULL;
313               infos = NULL;
314             }
315           
316           continue;
317         }
318
319       if (!module)
320         {
321           /* Read a module location
322            */
323           module = g_object_new (GTK_TYPE_IM_MODULE, NULL);
324
325           if (!pango_scan_string (&p, tmp_buf) ||
326               pango_skip_space (&p))
327             {
328               g_warning ("Error parsing context info in '%s'\n  %s", 
329                          filename, line_buf->str);
330               have_error = TRUE;
331             }
332
333           module->path = g_strdup (tmp_buf->str);
334 #ifdef DO_CORRECT_LIBDIR_PREFIX
335           correct_libdir_prefix (&module->path);
336 #endif
337           g_type_module_set_name (G_TYPE_MODULE (module), module->path);
338         }
339       else
340         {
341           GtkIMContextInfo *info = g_new0 (GtkIMContextInfo, 1);
342           
343           /* Read information about a context type
344            */
345           if (!pango_scan_string (&p, tmp_buf))
346             goto context_error;
347           info->context_id = g_strdup (tmp_buf->str);
348
349           if (!pango_scan_string (&p, tmp_buf))
350             goto context_error;
351           info->context_name = g_strdup (tmp_buf->str);
352
353           if (!pango_scan_string (&p, tmp_buf))
354             goto context_error;
355           info->domain = g_strdup (tmp_buf->str);
356
357           if (!pango_scan_string (&p, tmp_buf))
358             goto context_error;
359           info->domain_dirname = g_strdup (tmp_buf->str);
360 #ifdef DO_CORRECT_LIBDIR_PREFIX
361           correct_libdir_prefix ((char **) &info->domain_dirname);
362 #endif
363
364           if (!pango_scan_string (&p, tmp_buf))
365             goto context_error;
366           info->default_locales = g_strdup (tmp_buf->str);
367
368           if (pango_skip_space (&p))
369             goto context_error;
370
371           infos = g_slist_prepend (infos, info);
372           continue;
373
374         context_error:
375           g_warning ("Error parsing context info in '%s'\n  %s", 
376                      filename, line_buf->str);
377           have_error = TRUE;
378         }
379     }
380
381   if (have_error)
382     {
383       GSList *tmp_list = infos;
384       while (tmp_list)
385         {
386           free_info (tmp_list->data);
387           tmp_list = tmp_list->next;
388         }
389       g_slist_free (infos);
390
391       g_object_unref (module);
392     }
393   else if (module)
394     add_module (module, infos);
395
396   fclose (file);
397   g_string_free (line_buf, TRUE);
398   g_string_free (tmp_buf, TRUE);
399   g_free (filename);
400 }
401
402 static gint
403 compare_gtkimcontextinfo_name(const GtkIMContextInfo **a,
404                               const GtkIMContextInfo **b)
405 {
406   return g_utf8_collate ((*a)->context_name, (*b)->context_name);
407 }
408
409 /**
410  * _gtk_im_module_list:
411  * @contexts: location to store an array of pointers to #GtkIMContextInfo
412  *            this array should be freed with g_free() when you are finished.
413  *            The structures it points are statically allocated and should
414  *            not be modified or freed.
415  * @n_contexts: the length of the array stored in @contexts
416  * 
417  * List all available types of input method context
418  **/
419 void
420 _gtk_im_module_list (const GtkIMContextInfo ***contexts,
421                      guint                    *n_contexts)
422 {
423   int n = 0;
424
425   static const GtkIMContextInfo simple_context_info = {
426     SIMPLE_ID,
427     N_("Default"),
428     GETTEXT_PACKAGE,
429 #ifdef GTK_LOCALEDIR
430     GTK_LOCALEDIR,
431 #else
432     "",
433 #endif
434     ""
435   };
436
437   if (!contexts_hash)
438     gtk_im_module_init ();
439
440   if (n_contexts)
441     *n_contexts = (n_loaded_contexts + 1);
442
443   if (contexts)
444     {
445       GSList *tmp_list;
446       int i;
447       
448       *contexts = g_new (const GtkIMContextInfo *, n_loaded_contexts + 1);
449
450       (*contexts)[n++] = &simple_context_info;
451
452       tmp_list = modules_list;
453       while (tmp_list)
454         {
455           GtkIMModule *module = tmp_list->data;
456
457           for (i=0; i<module->n_contexts; i++)
458             (*contexts)[n++] = module->contexts[i];
459           
460           tmp_list = tmp_list->next;
461         }
462
463       /* fisrt element (Default) should always be at top */
464       qsort ((*contexts)+1, n-1, sizeof (GtkIMContextInfo *), (GCompareFunc)compare_gtkimcontextinfo_name);
465     }
466 }
467
468 /**
469  * _gtk_im_module_create:
470  * @context_id: the context ID for the context type to create
471  * 
472  * Create an IM context of a type specified by the string
473  * ID @context_id.
474  * 
475  * Return value: a newly created input context of or @context_id, or
476  * if that could not be created, a newly created GtkIMContextSimple.
477  **/
478 GtkIMContext *
479 _gtk_im_module_create (const gchar *context_id)
480 {
481   GtkIMModule *im_module;
482   GtkIMContext *context = NULL;
483   
484   if (!contexts_hash)
485     gtk_im_module_init ();
486
487   if (strcmp (context_id, SIMPLE_ID) != 0)
488     {
489       im_module = g_hash_table_lookup (contexts_hash, context_id);
490       if (!im_module)
491         {
492           g_warning ("Attempt to load unknown IM context type '%s'", context_id);
493         }
494       else
495         {
496           if (g_type_module_use (G_TYPE_MODULE (im_module)))
497             {
498               context = im_module->create (context_id);
499               g_type_module_unuse (G_TYPE_MODULE (im_module));
500             }
501           
502           if (!context)
503             g_warning ("Loading IM context type '%s' failed", context_id);
504         }
505     }
506   
507   if (!context)
508      return gtk_im_context_simple_new ();
509   else
510     return context;
511 }
512
513 /* Match @locale against @against.
514  * 
515  * 'en_US' against 'en_US'       => 4
516  * 'en_US' against 'en'          => 3
517  * 'en', 'en_UK' against 'en_US' => 2
518  *  all locales, against '*'     => 1
519  */
520 static gint
521 match_locale (const gchar *locale,
522               const gchar *against,
523               gint         against_len)
524 {
525   if (strcmp (against, "*") == 0)
526     return 1;
527
528   if (g_ascii_strcasecmp (locale, against) == 0)
529     return 4;
530
531   if (g_ascii_strncasecmp (locale, against, 2) == 0)
532     return (against_len == 2) ? 3 : 2;
533
534   return 0;
535 }
536
537 /**
538  * _gtk_im_module_get_default_context_id:
539  * @locale: a locale id in the form 'en_US'
540  * 
541  * Return the context_id of the best IM context type
542  * for the given locale ID.
543  * 
544  * Return value: the context ID (will never be %NULL)
545  *    the value is newly allocated and must be freed
546  *    with g_free().
547  **/
548 const gchar *
549 _gtk_im_module_get_default_context_id (const gchar *locale)
550 {
551   GSList *tmp_list;
552   const gchar *context_id = NULL;
553   gint best_goodness = 0;
554   gint i;
555   gchar *tmp_locale, *tmp;
556   const gchar *envvar;
557       
558   if (!contexts_hash)
559     gtk_im_module_init ();
560
561   envvar = g_getenv ("GTK_IM_MODULE");
562   if (envvar &&
563       (strcmp (envvar, SIMPLE_ID) == 0 ||
564        g_hash_table_lookup (contexts_hash, envvar)))
565     return g_strdup (envvar);
566
567   /* Strip the locale code down to the essentials
568    */
569   tmp_locale = g_strdup (locale);
570   tmp = strchr (tmp_locale, '.');
571   if (tmp)
572     *tmp = '\0';
573   tmp = strchr (tmp_locale, '@');
574   if (tmp)
575     *tmp = '\0';
576   
577   tmp_list = modules_list;
578   while (tmp_list)
579     {
580       GtkIMModule *module = tmp_list->data;
581      
582       for (i=0; i<module->n_contexts; i++)
583         {
584           const gchar *p = module->contexts[i]->default_locales;
585           while (p)
586             {
587               const gchar *q = strchr (p, ':');
588               gint goodness = match_locale (tmp_locale, p, q ? q - p : strlen (p));
589
590               if (goodness > best_goodness)
591                 {
592                   context_id = module->contexts[i]->context_id;
593                   best_goodness = goodness;
594                 }
595
596               p = q ? q + 1 : NULL;
597             }
598         }
599       
600       tmp_list = tmp_list->next;
601     }
602
603   g_free (tmp_locale);
604   
605   return g_strdup (context_id ? context_id : SIMPLE_ID);
606 }