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