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