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