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