summaryrefslogtreecommitdiff
path: root/gtk/gtkimmodule.c
diff options
context:
space:
mode:
Diffstat (limited to 'gtk/gtkimmodule.c')
-rw-r--r--gtk/gtkimmodule.c527
1 files changed, 527 insertions, 0 deletions
diff --git a/gtk/gtkimmodule.c b/gtk/gtkimmodule.c
new file mode 100644
index 0000000000..353100720b
--- /dev/null
+++ b/gtk/gtkimmodule.c
@@ -0,0 +1,527 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * Themes added by The Rasterman <raster@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gmodule.h>
+#include <pango/pango-utils.h>
+#include "gtkimmodule.h"
+#include "gtkimcontextsimple.h"
+#include "gtkrc.h"
+#include "config.h"
+#include "gtkintl.h"
+
+#define SIMPLE_ID "gtk-im-context-simple"
+
+typedef struct _GtkIMModule GtkIMModule;
+typedef struct _GtkIMModuleClass GtkIMModuleClass;
+
+#define GTK_TYPE_IM_MODULE (gtk_im_module_get_type ())
+#define GTK_IM_MODULE(im_module) (G_TYPE_CHECK_INSTANCE_CAST ((im_module), GTK_TYPE_IM_MODULE, GtkIMModule))
+#define GTK_IS_IM_MODULE(im_module) (G_TYPE_CHECK_INSTANCE_TYPE ((im_module), GTK_TYPE_IM_MODULE))
+
+struct _GtkIMModule
+{
+ GTypeModule parent_instance;
+
+ GModule *library;
+
+ void (*list) (const GtkIMContextInfo ***contexts,
+ guint *n_contexts);
+ void (*init) (GTypeModule *module);
+ void (*exit) (void);
+ GtkIMContext *(*create) (const gchar *context_id);
+
+ GtkIMContextInfo **contexts;
+ guint n_contexts;
+
+ gchar *path;
+};
+
+struct _GtkIMModuleClass
+{
+ GTypeModuleClass parent_class;
+};
+
+GType gtk_im_module_get_type (void);
+
+gint n_loaded_contexts = 0;
+static GHashTable *contexts_hash = NULL;
+static GSList *modules_list = NULL;
+
+static GObjectClass *parent_class = NULL;
+
+static gboolean
+gtk_im_module_load (GTypeModule *module)
+{
+ GtkIMModule *im_module = GTK_IM_MODULE (module);
+
+ im_module->library = g_module_open (im_module->path, 0);
+ if (!im_module->library)
+ {
+ g_warning (g_module_error());
+ return FALSE;
+ }
+
+ /* extract symbols from the lib */
+ if (!g_module_symbol (im_module->library, "im_module_init",
+ (gpointer *)&im_module->init) ||
+ !g_module_symbol (im_module->library, "im_module_exit",
+ (gpointer *)&im_module->exit) ||
+ !g_module_symbol (im_module->library, "im_module_list",
+ (gpointer *)&im_module->list) ||
+ !g_module_symbol (im_module->library, "im_module_create",
+ (gpointer *)&im_module->create))
+ {
+ g_warning (g_module_error());
+ g_module_close (im_module->library);
+
+ return FALSE;
+ }
+
+ /* call the theme's init (theme_init) function to let it */
+ /* setup anything it needs to set up. */
+ im_module->init (module);
+
+ return TRUE;
+}
+
+static void
+gtk_im_module_unload (GTypeModule *module)
+{
+ GtkIMModule *im_module = GTK_IM_MODULE (module);
+
+ im_module->exit();
+
+ g_module_close (im_module->library);
+ im_module->library = NULL;
+
+ im_module->init = NULL;
+ im_module->exit = NULL;
+ im_module->list = NULL;
+ im_module->create = NULL;
+}
+
+/* This only will ever be called if an error occurs during
+ * initialization
+ */
+static void
+gtk_im_module_finalize (GObject *object)
+{
+ GtkIMModule *module = GTK_IM_MODULE (object);
+
+ g_free (module->path);
+
+ parent_class->finalize (object);
+}
+
+static void
+gtk_im_module_class_init (GtkIMModuleClass *class)
+{
+ GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (class));
+
+ module_class->load = gtk_im_module_load;
+ module_class->unload = gtk_im_module_unload;
+
+ gobject_class->finalize = gtk_im_module_finalize;
+}
+
+GType
+gtk_im_module_get_type (void)
+{
+ static GType im_module_type = 0;
+
+ if (!im_module_type)
+ {
+ static const GTypeInfo im_module_info = {
+ sizeof (GtkIMModuleClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_im_module_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkIMModule),
+ 0, /* n_preallocs */
+ NULL, /* instance_init */
+ };
+
+ im_module_type = g_type_register_static (G_TYPE_TYPE_MODULE, "GtkIMModule", &im_module_info, 0);
+ }
+
+ return im_module_type;
+}
+
+static void
+free_info (GtkIMContextInfo *info)
+{
+ g_free ((char *)info->context_id);
+ g_free ((char *)info->context_name);
+ g_free ((char *)info->domain);
+ g_free ((char *)info->domain_dirname);
+ g_free ((char *)info->default_locales);
+ g_free (info);
+}
+
+static void
+add_module (GtkIMModule *module, GSList *infos)
+{
+ GSList *tmp_list = infos;
+ gint i = 0;
+ gint n = g_slist_length (infos);
+ module->contexts = g_new (GtkIMContextInfo *, n);
+
+ while (tmp_list)
+ {
+ GtkIMContextInfo *info = tmp_list->data;
+
+ if (g_hash_table_lookup (contexts_hash, info->context_id))
+ {
+ free_info (info); /* Duplicate */
+ }
+ else
+ {
+ g_hash_table_insert (contexts_hash, (char *)info->context_id, module);
+ module->contexts[i++] = tmp_list->data;
+ n_loaded_contexts++;
+ }
+
+ tmp_list = tmp_list->next;
+ }
+ g_slist_free (infos);
+ module->n_contexts = i;
+
+ modules_list = g_slist_prepend (modules_list, module);
+}
+
+static void
+gtk_im_module_init ()
+{
+ GString *line_buf = g_string_new (NULL);
+ GString *tmp_buf = g_string_new (NULL);
+ const gchar *filename = gtk_rc_get_im_module_file();
+ FILE *file;
+ gboolean have_error = FALSE;
+
+ GtkIMModule *module = NULL;
+ GSList *infos = NULL;
+
+ contexts_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+ file = fopen (filename, "r");
+ if (!file)
+ {
+ g_warning ("Can not open Input Method module file '%s': %s",
+ filename, g_strerror (errno));
+ return;
+ }
+
+ while (!have_error && pango_read_line (file, line_buf))
+ {
+ const char *p;
+
+ p = line_buf->str;
+
+ if (!pango_skip_space (&p))
+ {
+ /* Blank line marking the end of a module
+ */
+ if (module && *p != '#')
+ {
+ add_module (module, infos);
+ module = NULL;
+ infos = NULL;
+ }
+
+ continue;
+ }
+
+ if (!module)
+ {
+ /* Read a module location
+ */
+ module = g_object_new (GTK_TYPE_IM_MODULE, NULL);
+
+ if (!pango_scan_string (&p, tmp_buf) ||
+ pango_skip_space (&p))
+ {
+ g_warning ("Error parsing context info in '%s'\n %s",
+ filename, line_buf->str);
+ have_error = TRUE;
+ }
+
+ module->path = g_strdup (tmp_buf->str);
+ g_type_module_set_name (G_TYPE_MODULE (module), module->path);
+ }
+ else
+ {
+ GtkIMContextInfo *info = g_new0 (GtkIMContextInfo, 1);
+
+ /* Read information about a context type
+ */
+ if (!pango_scan_string (&p, tmp_buf))
+ goto context_error;
+ info->context_id = g_strdup (tmp_buf->str);
+
+ if (!pango_scan_string (&p, tmp_buf))
+ goto context_error;
+ info->context_name = g_strdup (tmp_buf->str);
+
+ if (!pango_scan_string (&p, tmp_buf))
+ goto context_error;
+ info->domain = g_strdup (tmp_buf->str);
+
+ if (!pango_scan_string (&p, tmp_buf))
+ goto context_error;
+ info->domain_dirname = g_strdup (tmp_buf->str);
+
+ if (!pango_scan_string (&p, tmp_buf))
+ goto context_error;
+ info->default_locales = g_strdup (tmp_buf->str);
+
+ if (pango_skip_space (&p))
+ goto context_error;
+
+ infos = g_slist_prepend (infos, info);
+ continue;
+
+ context_error:
+ g_warning ("Error parsing context info in '%s'\n %s",
+ filename, line_buf->str);
+ have_error = TRUE;
+ }
+ }
+
+ if (have_error)
+ {
+ GSList *tmp_list = infos;
+ while (tmp_list)
+ {
+ free_info (tmp_list->data);
+ tmp_list = tmp_list->next;
+ }
+ g_slist_free (infos);
+
+ g_object_unref (G_OBJECT (module));
+ }
+ else if (module)
+ add_module (module, infos);
+
+ fclose (file);
+ g_string_free (line_buf, TRUE);
+ g_string_free (tmp_buf, TRUE);
+}
+
+/**
+ * _gtk_im_module_list:
+ * @contexts: location to store an array of pointers to #GtkIMContextInfo
+ * this array should be freed with g_free() when you are finished.
+ * The structures it points are statically allocated and should
+ * not be modified or freed.
+ * @n_contexts: the length of the array stored in @contexts
+ *
+ * List all available types of input method context
+ **/
+void
+_gtk_im_module_list (const GtkIMContextInfo ***contexts,
+ guint *n_contexts)
+{
+ int n = 0;
+
+ static const GtkIMContextInfo simple_context_info = {
+ SIMPLE_ID,
+ "Default",
+ "gtk+",
+ NULL,
+ ""
+ };
+
+ if (!contexts_hash)
+ gtk_im_module_init ();
+
+ if (n_contexts)
+ *n_contexts = (n_loaded_contexts + 1);
+
+ if (contexts)
+ {
+ GSList *tmp_list;
+ int i;
+
+ *contexts = g_new (const GtkIMContextInfo *, n_loaded_contexts + 1);
+
+ (*contexts)[n++] = &simple_context_info;
+
+ tmp_list = modules_list;
+ while (tmp_list)
+ {
+ GtkIMModule *module = tmp_list->data;
+
+ for (i=0; i<module->n_contexts; i++)
+ (*contexts)[n++] = module->contexts[i];
+
+ tmp_list = tmp_list->next;
+ }
+ }
+}
+
+/**
+ * _gtk_im_module_create:
+ * @context_id: the context ID for the context type to create
+ *
+ * Create an IM context of a type specified by the string
+ * ID @context_id.
+ *
+ * Return value: a newly created input context of or @context_id, or
+ * if that could not be created, a newly created GtkIMContextSimple.
+ **/
+GtkIMContext *
+_gtk_im_module_create (const gchar *context_id)
+{
+ GtkIMModule *im_module;
+ GtkIMContext *context = NULL;
+
+ if (!contexts_hash)
+ gtk_im_module_init ();
+
+ if (strcmp (context_id, SIMPLE_ID) != 0)
+ {
+ im_module = g_hash_table_lookup (contexts_hash, context_id);
+ if (!im_module)
+ {
+ g_warning ("Attempt to load unknown IM context type '%s'", context_id);
+ }
+ else
+ {
+ if (g_type_module_use (G_TYPE_MODULE (im_module)))
+ {
+ context = im_module->create (context_id);
+ g_type_module_unuse (G_TYPE_MODULE (im_module));
+ }
+
+ if (!context)
+ g_warning ("Loading IM context type '%s' failed", context_id);
+ }
+ }
+
+ if (!context)
+ return gtk_im_context_simple_new ();
+ else
+ return context;
+}
+
+/* Match @locale against @against.
+ *
+ * 'en_US' against 'en_US' => 3
+ * 'en_US' against 'en' => 2
+ * 'en', 'en_UK' against 'en_US' => 1
+ */
+static gint
+match_locale (const gchar *locale,
+ const gchar *against,
+ gint against_len)
+{
+ if (strcmp (locale, against) == 0)
+ return 3;
+
+ if (strncmp (locale, against, 2) == 0)
+ return (against_len == 2) ? 2 : 1;
+
+ return 0;
+}
+
+/**
+ * _gtk_im_module_get_default_context_id:
+ * @locale: a locale id in the form 'en_US'
+ *
+ * Return the context_id of the best IM context type
+ * for the given locale ID.
+ *
+ * Return value: the context ID (will never be %NULL)
+ * the value is newly allocated and must be freed
+ * with g_free().
+ **/
+const gchar *
+_gtk_im_module_get_default_context_id (const gchar *locale)
+{
+ GSList *tmp_list;
+ const gchar *context_id = NULL;
+ gint best_goodness = 0;
+ gint i;
+ gchar *tmp_locale, *tmp;
+ gchar *envvar;
+
+ if (!contexts_hash)
+ gtk_im_module_init ();
+
+ envvar = g_getenv ("GTK_IM_MODULE");
+ if (envvar && g_hash_table_lookup (contexts_hash, envvar))
+ return g_strdup (envvar);
+
+ /* Strip the locale code down to the essentials
+ */
+ tmp_locale = g_strdup (locale);
+ tmp = strchr (tmp_locale, '.');
+ if (tmp)
+ *tmp = '\0';
+ tmp = strchr (tmp_locale, '@');
+ if (tmp)
+ *tmp = '\0';
+
+ tmp_list = modules_list;
+ while (tmp_list)
+ {
+ GtkIMModule *module = tmp_list->data;
+
+ for (i=0; i<module->n_contexts; i++)
+ {
+ const gchar *p = module->contexts[i]->default_locales;
+ while (p)
+ {
+ const gchar *q = strchr (p, ':');
+ gint goodness = match_locale (tmp_locale, p, q ? q - p : strlen (p));
+
+ if (goodness > best_goodness)
+ {
+ context_id = module->contexts[i]->context_id;
+ best_goodness = goodness;
+ }
+
+ p = q ? q + 1 : NULL;
+ }
+ }
+
+ tmp_list = tmp_list->next;
+ }
+
+ g_free (tmp_locale);
+
+ return g_strdup (context_id ? context_id : SIMPLE_ID);
+}