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