summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorfujiwarat <takao.fujiwara1@gmail.com>2013-07-10 16:55:19 +0900
committerfujiwarat <takao.fujiwara1@gmail.com>2013-07-10 16:55:19 +0900
commit88ce21749e84cc92cbc91c919d27721c0f2e1ceb (patch)
tree816aab8bfc017d92551bfd66f327337a0840b02e /src
parent5ed2b8c8253717b6c26281a8721403b006db04e6 (diff)
Load the system registry cache prior to the user one.
gnome-settings-daemon runs ibus-daemon after the user configures any input method engines and causes a delay to show the engines on UI because no cache exists for ibus-daemon. The system cache can avoid the user timing. Review URL: https://codereview.appspot.com/10364043
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am25
-rw-r--r--src/ibus.h5
-rw-r--r--src/ibusregistry.c557
-rw-r--r--src/ibusregistry.h223
-rw-r--r--src/tests/Makefile.am30
-rw-r--r--src/tests/ibus-registry.c11
6 files changed, 825 insertions, 26 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index b2793c8a..74b6838d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2,8 +2,8 @@
#
# ibus - The Input Bus
#
-# Copyright (c) 2007-2010 Peng Huang <shawn.p.huang@gmail.com>
-# Copyright (c) 2007-2010 Red Hat, Inc.
+# Copyright (c) 2007-2013 Peng Huang <shawn.p.huang@gmail.com>
+# Copyright (c) 2007-2013 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -42,15 +42,16 @@ INTROSPECTION_GIRS =
CLEANFILES =
# C preprocessor flags
-AM_CPPFLAGS = \
- -DG_LOG_DOMAIN=\"IBUS\" \
- @GLIB2_CFLAGS@ \
- @GOBJECT2_CFLAGS@ \
- @GIO2_CFLAGS@ \
- -DIBUS_DATA_DIR=\"$(pkgdatadir)\" \
- -DIBUS_DISABLE_DEPRECATION_WARNINGS \
- -DIBUS_COMPILATION \
- -DISOCODES_PREFIX=\"$(ISOCODES_PREFIX)\" \
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"IBUS\" \
+ @GLIB2_CFLAGS@ \
+ @GOBJECT2_CFLAGS@ \
+ @GIO2_CFLAGS@ \
+ -DIBUS_CACHE_DIR=\""$(localstatedir)/cache/ibus"\" \
+ -DIBUS_DATA_DIR=\"$(pkgdatadir)\" \
+ -DIBUS_DISABLE_DEPRECATION_WARNINGS \
+ -DIBUS_COMPILATION \
+ -DISOCODES_PREFIX=\"$(ISOCODES_PREFIX)\" \
$(NULL)
# ibus library
@@ -97,6 +98,7 @@ ibus_sources = \
ibuscomponent.c \
ibusutil.c \
ibusenginesimple.c \
+ ibusregistry.c \
$(NULL)
libibus_1_0_la_SOURCES = \
ibusmarshalers.c \
@@ -145,6 +147,7 @@ ibus_headers = \
ibuscomponent.h \
ibusutil.h \
ibusenginesimple.h \
+ ibusregistry.h \
$(NULL)
ibusincludedir = $(includedir)/ibus-@IBUS_API_VERSION@
ibus_public_headers = \
diff --git a/src/ibus.h b/src/ibus.h
index ef811a4c..e27f845c 100644
--- a/src/ibus.h
+++ b/src/ibus.h
@@ -1,8 +1,8 @@
/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
/* vim:set et sts=4: */
/* ibus - The Input Bus
- * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
- * Copyright (C) 2008-2010 Red Hat, Inc.
+ * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright (C) 2008-2013 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -55,6 +55,7 @@
#include <ibusconfigservice.h>
#include <ibuspanelservice.h>
#include <ibusutil.h>
+#include <ibusregistry.h>
#ifndef IBUS_DISABLE_DEPRECATED
#include <ibuskeysyms-compat.h>
diff --git a/src/ibusregistry.c b/src/ibusregistry.c
new file mode 100644
index 00000000..d239955e
--- /dev/null
+++ b/src/ibusregistry.c
@@ -0,0 +1,557 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* vim:set et sts=4: */
+/* bus - The Input Bus
+ * Copyright (C) 2013 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright (C) 2013 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include <gio/gio.h>
+#include <glib/gstdio.h>
+#include <string.h>
+
+#include "ibusinternal.h"
+#include "ibusmarshalers.h"
+#include "ibusregistry.h"
+
+enum {
+ CHANGED,
+ LAST_SIGNAL,
+};
+
+static guint _signals[LAST_SIGNAL] = { 0 };
+
+struct _IBusRegistryPrivate {
+ /* a list of IBusObservedPath objects. */
+ GList *observed_paths;
+
+ /* a list of IBusComponent objects that are created from component XML
+ * files (or from the cache of them). */
+ GList *components;
+
+ gboolean changed;
+
+ /* a mapping from GFile to GFileMonitor. */
+ GHashTable *monitor_table;
+
+ guint monitor_timeout_id;
+};
+
+#define IBUS_REGISTRY_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_REGISTRY, IBusRegistryPrivate))
+
+/* functions prototype */
+static void ibus_registry_destroy (IBusRegistry *registry);
+static void ibus_registry_remove_all (IBusRegistry *registry);
+
+G_DEFINE_TYPE (IBusRegistry, ibus_registry, IBUS_TYPE_OBJECT)
+
+static void
+ibus_registry_class_init (IBusRegistryClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (class);
+
+ ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_registry_destroy;
+
+ g_type_class_add_private (class, sizeof (IBusRegistryPrivate));
+
+ /* install signals */
+ /**
+ * IBusRegistry::changed:
+ * @registry: An #IBusRegistry.
+ *
+ * Emitted when any observed paths are changed.
+ * A method is not associated in this class. the "changed"
+ * signal would be handled in other classes.
+ *
+ * See also: ibus_registry_start_monitor_changes().
+ */
+ _signals[CHANGED] =
+ g_signal_new (I_("changed"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ _ibus_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+}
+
+static void
+ibus_registry_init (IBusRegistry *registry)
+{
+ registry->priv = IBUS_REGISTRY_GET_PRIVATE (registry);
+
+ registry->priv->observed_paths = NULL;
+ registry->priv->components = NULL;
+ registry->priv->changed = FALSE;
+ registry->priv->monitor_table =
+ g_hash_table_new_full (g_file_hash,
+ (GEqualFunc) g_file_equal,
+ (GDestroyNotify) g_object_unref,
+ (GDestroyNotify) g_object_unref);
+}
+
+static void
+ibus_registry_destroy (IBusRegistry *registry)
+{
+ ibus_registry_remove_all (registry);
+
+ g_hash_table_destroy (registry->priv->monitor_table);
+ registry->priv->monitor_table = NULL;
+
+ if (registry->priv->monitor_timeout_id > 0) {
+ g_source_remove (registry->priv->monitor_timeout_id);
+ registry->priv->monitor_timeout_id = 0;
+ }
+
+ IBUS_OBJECT_CLASS (ibus_registry_parent_class)->
+ destroy (IBUS_OBJECT (registry));
+}
+
+/**
+ * ibus_registry_remove_all:
+ *
+ * Remove the loaded registry.
+ */
+static void
+ibus_registry_remove_all (IBusRegistry *registry)
+{
+ g_assert (IBUS_IS_REGISTRY (registry));
+
+ g_list_free_full (registry->priv->observed_paths, g_object_unref);
+ registry->priv->observed_paths = NULL;
+
+ g_list_free_full (registry->priv->components, g_object_unref);
+ registry->priv->components = NULL;
+}
+
+void
+ibus_registry_load (IBusRegistry *registry)
+{
+ const gchar *envstr;
+ GPtrArray *path;
+ gchar **d, **search_path;
+
+ g_assert (IBUS_IS_REGISTRY (registry));
+
+ path = g_ptr_array_new();
+
+ envstr = g_getenv ("IBUS_COMPONENT_PATH");
+ if (envstr) {
+ gchar **dirs = g_strsplit (envstr, G_SEARCHPATH_SEPARATOR_S, 0);
+ for (d = dirs; *d != NULL; d++)
+ g_ptr_array_add (path, *d);
+ g_free (dirs);
+ } else {
+ gchar *dirname;
+
+ dirname = g_build_filename (IBUS_DATA_DIR, "component", NULL);
+ g_ptr_array_add (path, dirname);
+
+#if 0
+ /* FIXME Should we support install some IME in user dir? */
+ dirname = g_build_filename (g_get_user_data_dir (),
+ "ibus", "component",
+ NULL);
+ g_ptr_array_add (path, dirname);
+#endif
+ }
+
+ g_ptr_array_add (path, NULL);
+ search_path = (gchar **) g_ptr_array_free (path, FALSE);
+ for (d = search_path; *d != NULL; d++) {
+ ibus_registry_load_in_dir (registry, *d);
+ }
+ g_strfreev (search_path);
+}
+
+gboolean
+ibus_registry_load_cache (IBusRegistry *registry, gboolean is_user)
+{
+ gchar *filename;
+ gboolean retval;
+
+ g_assert (IBUS_IS_REGISTRY (registry));
+
+ if (is_user) {
+ filename = g_build_filename (g_get_user_cache_dir (),
+ "ibus", "bus", "registry.xml", NULL);
+ } else {
+ filename = g_build_filename (IBUS_CACHE_DIR,
+ "bus", "registry.xml", NULL);
+ }
+
+ retval = ibus_registry_load_cache_file (registry, filename);
+ g_free (filename);
+
+ return retval;
+}
+
+gboolean
+ibus_registry_load_cache_file (IBusRegistry *registry, const gchar *filename)
+{
+ XMLNode *node;
+ GList *p;
+
+ g_assert (IBUS_IS_REGISTRY (registry));
+ g_assert (filename != NULL);
+
+ node = ibus_xml_parse_file (filename);
+
+ if (node == NULL) {
+ return FALSE;
+ }
+
+ if (g_strcmp0 (node->name, "ibus-registry") != 0) {
+ ibus_xml_free (node);
+ return FALSE;
+ }
+
+ for (p = node->sub_nodes; p != NULL; p = p->next) {
+ XMLNode *sub_node = (XMLNode *) p->data;
+
+ if (g_strcmp0 (sub_node->name, "observed-paths") == 0) {
+ GList *pp;
+ for (pp = sub_node->sub_nodes; pp != NULL; pp = pp->next) {
+ IBusObservedPath *path;
+ path = ibus_observed_path_new_from_xml_node (pp->data, FALSE);
+ if (path) {
+ g_object_ref_sink (path);
+ registry->priv->observed_paths =
+ g_list_append (registry->priv->observed_paths,
+ path);
+ }
+ }
+ continue;
+ }
+ if (g_strcmp0 (sub_node->name, "components") == 0) {
+ GList *pp;
+ for (pp = sub_node->sub_nodes; pp != NULL; pp = pp->next) {
+ IBusComponent *component;
+ component = ibus_component_new_from_xml_node (pp->data);
+ if (component) {
+ g_object_ref_sink (component);
+ registry->priv->components =
+ g_list_append (registry->priv->components, component);
+ }
+ }
+
+ continue;
+ }
+ g_warning ("Unknown element <%s>", sub_node->name);
+ }
+
+ ibus_xml_free (node);
+ return TRUE;
+}
+
+gboolean
+ibus_registry_check_modification (IBusRegistry *registry)
+{
+ GList *p;
+
+ g_assert (IBUS_IS_REGISTRY (registry));
+
+ for (p = registry->priv->observed_paths; p != NULL; p = p->next) {
+ if (ibus_observed_path_check_modification (
+ (IBusObservedPath *) p->data))
+ return TRUE;
+ }
+
+ for (p = registry->priv->components; p != NULL; p = p->next) {
+ if (ibus_component_check_modification ((IBusComponent *) p->data))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean
+ibus_registry_save_cache (IBusRegistry *registry, gboolean is_user)
+{
+ gchar *filename;
+ gboolean retval;
+
+ g_assert (IBUS_IS_REGISTRY (registry));
+
+ if (is_user) {
+ filename = g_build_filename (g_get_user_cache_dir (),
+ "ibus", "bus", "registry.xml", NULL);
+ } else {
+ filename = g_build_filename (IBUS_CACHE_DIR,
+ "bus", "registry.xml", NULL);
+ }
+
+ retval = ibus_registry_save_cache_file (registry, filename);
+ g_free (filename);
+
+ return retval;
+}
+
+gboolean
+ibus_registry_save_cache_file (IBusRegistry *registry, const gchar *filename)
+{
+ gchar *cachedir;
+ const gchar *user_cachedir;
+ gboolean is_user = TRUE;
+ GString *output;
+ FILE *pf;
+ size_t items = 0;
+
+ g_assert (IBUS_IS_REGISTRY (registry));
+ g_assert (filename != NULL);
+
+ cachedir = g_path_get_dirname (filename);
+ g_mkdir_with_parents (cachedir, 0775);
+ g_free (cachedir);
+ pf = g_fopen (filename, "w");
+
+ if (pf == NULL) {
+ g_warning ("create %s failed", filename);
+ return FALSE;
+ }
+
+ output = g_string_new ("");
+
+ ibus_registry_output (registry, output, 1);
+
+ items = fwrite (output->str, output->len, 1, pf);
+ g_string_free (output, TRUE);
+ fclose (pf);
+
+ user_cachedir = g_get_user_cache_dir ();
+ is_user = (strncmp (user_cachedir, filename, strlen (user_cachedir)) == 0);
+
+ if (!is_user) {
+ g_chmod (filename, 0644);
+ }
+
+ return (items == 1 ? TRUE : FALSE);
+}
+
+#define g_string_append_indent(string, indent) \
+ { \
+ gint i; \
+ for (i = 0; i < (indent); i++) { \
+ g_string_append (string, " "); \
+ } \
+ }
+
+void
+ibus_registry_output (IBusRegistry *registry, GString *output, int indent)
+{
+ GList *p;
+
+ g_assert (IBUS_IS_REGISTRY (registry));
+ g_return_if_fail (output != NULL);
+
+ g_string_append (output, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
+ g_string_append (output, "<!-- \n"
+ " This file was generated by ibus-daemon. "
+ "Please don't modify it.\n"
+ " -->\n");
+ g_string_append (output, "<ibus-registry>\n");
+
+ if (registry->priv->observed_paths) {
+ g_string_append_indent (output, indent);
+ g_string_append (output, "<observed-paths>\n");
+ for (p = registry->priv->observed_paths; p != NULL; p = p->next) {
+ ibus_observed_path_output ((IBusObservedPath *) p->data,
+ output, indent * 2);
+ }
+ g_string_append_indent (output, indent);
+ g_string_append (output, "</observed-paths>\n");
+ }
+
+ if (registry->priv->components) {
+ g_string_append_indent (output, indent);
+ g_string_append (output, "<components>\n");
+ for (p = registry->priv->components; p != NULL; p = p->next) {
+ ibus_component_output ((IBusComponent *) p->data,
+ output, indent * 2);
+ }
+ g_string_append_indent (output, indent);
+ g_string_append (output, "</components>\n");
+ }
+
+ g_string_append (output, "</ibus-registry>\n");
+}
+
+void
+ibus_registry_load_in_dir (IBusRegistry *registry,
+ const gchar *dirname)
+{
+ GError *error = NULL;
+ GDir *dir;
+ IBusObservedPath *observed_path = NULL;
+ const gchar *filename;
+
+ g_assert (IBUS_IS_REGISTRY (registry));
+ g_assert (dirname);
+
+ dir = g_dir_open (dirname, 0, &error);
+
+ if (dir == NULL) {
+ g_warning ("Unable open directory %s : %s", dirname, error->message);
+ g_error_free (error);
+ return;
+ }
+
+ observed_path = ibus_observed_path_new (dirname, TRUE);
+
+ registry->priv->observed_paths =
+ g_list_append (registry->priv->observed_paths,
+ observed_path);
+
+ while ((filename = g_dir_read_name (dir)) != NULL) {
+ glong size;
+ gchar *path;
+ IBusComponent *component;
+
+ size = g_utf8_strlen (filename, -1);
+ if (g_strcmp0 (MAX (filename, filename + size - 4), ".xml") != 0)
+ continue;
+
+ path = g_build_filename (dirname, filename, NULL);
+ component = ibus_component_new_from_file (path);
+ if (component != NULL) {
+ g_object_ref_sink (component);
+ registry->priv->components =
+ g_list_append (registry->priv->components, component);
+ }
+
+ g_free (path);
+ }
+
+ g_dir_close (dir);
+}
+
+
+IBusRegistry *
+ibus_registry_new (void)
+{
+ IBusRegistry *registry;
+ registry = (IBusRegistry *) g_object_new (IBUS_TYPE_REGISTRY, NULL);
+ return registry;
+}
+
+GList *
+ibus_registry_get_components (IBusRegistry *registry)
+{
+ g_assert (IBUS_IS_REGISTRY (registry));
+
+ return g_list_copy (registry->priv->components);
+}
+
+GList *
+ibus_registry_get_observed_paths (IBusRegistry *registry)
+{
+ g_assert (IBUS_IS_REGISTRY (registry));
+
+ return g_list_copy (registry->priv->observed_paths);
+}
+
+static gboolean
+_monitor_timeout_cb (IBusRegistry *registry)
+{
+ g_hash_table_remove_all (registry->priv->monitor_table);
+ registry->priv->changed = TRUE;
+ g_signal_emit (registry, _signals[CHANGED], 0);
+ registry->priv->monitor_timeout_id = 0;
+ return FALSE;
+}
+
+static void
+_monitor_changed_cb (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ IBusRegistry *registry)
+{
+ g_assert (IBUS_IS_REGISTRY (registry));
+
+ if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
+ event_type != G_FILE_MONITOR_EVENT_DELETED &&
+ event_type != G_FILE_MONITOR_EVENT_CREATED &&
+ event_type != G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED)
+ return;
+
+ /* Merge successive file changes into one, with a low priority
+ timeout handler. */
+ if (registry->priv->monitor_timeout_id > 0)
+ return;
+
+ registry->priv->monitor_timeout_id =
+ g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
+ 5000,
+ (GSourceFunc) _monitor_timeout_cb,
+ g_object_ref (registry),
+ (GDestroyNotify) g_object_unref);
+}
+
+void
+ibus_registry_start_monitor_changes (IBusRegistry *registry)
+{
+ GList *observed_paths, *p;
+
+ g_assert (IBUS_IS_REGISTRY (registry));
+
+ g_hash_table_remove_all (registry->priv->monitor_table);
+
+ observed_paths = g_list_copy (registry->priv->observed_paths);
+ for (p = registry->priv->components; p != NULL; p = p->next) {
+ IBusComponent *component = (IBusComponent *) p->data;
+ GList *component_observed_paths =
+ ibus_component_get_observed_paths (component);
+ observed_paths = g_list_concat (observed_paths,
+ component_observed_paths);
+ }
+
+ for (p = observed_paths; p != NULL; p = p->next) {
+ IBusObservedPath *path = (IBusObservedPath *) p->data;
+ GFile *file = g_file_new_for_path (path->path);
+ if (g_hash_table_lookup (registry->priv->monitor_table, file) == NULL) {
+ GFileMonitor *monitor;
+ GError *error;
+
+ error = NULL;
+ monitor = g_file_monitor (file,
+ G_FILE_MONITOR_NONE,
+ NULL,
+ &error);
+
+ if (monitor != NULL) {
+ g_signal_connect (monitor, "changed",
+ G_CALLBACK (_monitor_changed_cb),
+ registry);
+
+ g_hash_table_replace (registry->priv->monitor_table,
+ g_object_ref (file),
+ monitor);
+ } else {
+ g_warning ("Can't monitor directory %s: %s",
+ path->path,
+ error->message);
+ g_error_free (error);
+ }
+ }
+ g_object_unref (file);
+ }
+ g_list_free (observed_paths);
+}
diff --git a/src/ibusregistry.h b/src/ibusregistry.h
new file mode 100644
index 00000000..288dc07f
--- /dev/null
+++ b/src/ibusregistry.h
@@ -0,0 +1,223 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* vim:set et sts=4: */
+/* bus - The Input Bus
+ * Copyright (C) 2013 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright (C) 2013 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__IBUS_H_INSIDE__) && !defined (IBUS_COMPILATION)
+#error "Only <ibus.h> can be included directly"
+#endif
+
+#ifndef __IBUS_REGISTRY_H_
+#define __IBUS_REGISTRY_H_
+
+/**
+ * SECTION: ibusregistry
+ * @short_description: Registry cache handling.
+ * @title: IBusRegistry
+ * @stability: Stable
+ *
+ * An #IBusRegistry loads IBus component files and generates the cache files.
+ *
+ * see_also: #IBusComponent
+ */
+
+#include "ibuscomponent.h"
+
+/*
+ * Type macros.
+ */
+
+/* define GOBJECT macros */
+#define IBUS_TYPE_REGISTRY \
+ (ibus_registry_get_type ())
+#define IBUS_REGISTRY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IBUS_TYPE_REGISTRY, IBusRegistry))
+#define IBUS_REGISTRY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), IBUS_TYPE_REGISTRY, IBusRegistryClass))
+#define IBUS_IS_REGISTRY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IBUS_TYPE_REGISTRY))
+#define IBUS_IS_REGISTRY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), IBUS_TYPE_REGISTRY))
+#define IBUS_REGISTRY_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), IBUS_TYPE_REGISTRY, IBusRegistryClass))
+
+G_BEGIN_DECLS
+
+typedef struct _IBusRegistry IBusRegistry;
+typedef struct _IBusRegistryPrivate IBusRegistryPrivate;
+typedef struct _IBusRegistryClass IBusRegistryClass;
+
+/**
+ * IBusRegistry:
+ *
+ * Registry cache handling.
+ * You can load the registry from compose files or a cache file.
+ */
+struct _IBusRegistry {
+ /* instance members */
+ /*< private >*/
+ IBusObject parent;
+ IBusRegistryPrivate *priv;
+};
+
+struct _IBusRegistryClass {
+ /* class members */
+ /*< private >*/
+ IBusObjectClass parent;
+};
+
+GType ibus_registry_get_type (void);
+
+/**
+ * ibus_registry_new:
+ * @returns: A newly allocated #IBusRegistry.
+ *
+ * New a #IBusRegistry
+ */
+IBusRegistry *ibus_registry_new (void);
+
+/**
+ * ibus_registry_load:
+ * @registry: An #IBusRegistry.
+ *
+ * Read all XML files in a IBus component directory (typically
+ * /usr/share/ibus/component/ *.xml) and update the registry object.
+ * IBUS_COMPONENT_PATH environment valuable is also available for
+ * the custom component directories, whose delimiter is ':'.
+ */
+void ibus_registry_load (IBusRegistry *registry);
+
+/**
+ * ibus_registry_load_in_dir:
+ * @registry: An #IBusRegistry.
+ * @dirname: IBus component directory which includes XML files.
+ *
+ * Read all XML files in @dirname, create a #IBusComponent object for each file,
+ * and add the component objects to the registry.
+ * If @dirname is "/usr/share/ibus/component", this API and
+ * ibus_registry_load() are same.
+ */
+void ibus_registry_load_in_dir (IBusRegistry *registry,
+ const gchar *dirname);
+
+/**
+ * ibus_registry_load_cache:
+ * @registry: An #IBusRegistry.
+ * @is_user: %TRUE if the registry cache is loaded in the user directory.
+ * @returns: %TRUE if the cache exists and is loaded successfully,
+ * %FALSE otherwise.
+ *
+ * Load the user or system registry cache.
+ */
+gboolean ibus_registry_load_cache (IBusRegistry *registry,
+ gboolean is_user);
+
+/**
+ * ibus_registry_load_cache_file:
+ * @registry: An #IBusRegistry.
+ * @filename: The file path of the registry cache
+ * @returns: %TRUE if the cache exists and is loaded successfully,
+ * %FALSE otherwise.
+ *
+ * Load the registry cache @filename.
+ */
+gboolean ibus_registry_load_cache_file (IBusRegistry *registry,
+ const gchar *filename);
+
+/**
+ * ibus_registry_save_cache:
+ * @registry: An #IBusRegistry.
+ * @is_user: %TRUE if the registry cache is saved in the user directory.
+ * @returns: %TRUE if the cache is saved successfully, %FALSE otherwise.
+ *
+ * Save the registry in a user directory or system directory.
+ */
+gboolean ibus_registry_save_cache (IBusRegistry *registry,
+ gboolean is_user);
+
+/**
+ * ibus_registry_save_cache_file:
+ * @registry: An #IBusRegistry.
+ * @filename: The file path of the registry cache
+ * @returns: %TRUE if the cache is saved successfully, %FALSE otherwise.
+ *
+ * Save the registry cache @filename.
+ */
+gboolean ibus_registry_save_cache_file (IBusRegistry *registry,
+ const gchar *filename);
+
+/**
+ * ibus_registry_output:
+ * @registry: An #IBusRegistry.
+ * @output: GString that holds the result.
+ * @indent: level of indent.
+ *
+ * Output #IBusRegistry as an XML-formatted string.
+ * The output string can be then shown on the screen or written to file.
+ */
+void ibus_registry_output (IBusRegistry *registry,
+ GString *output,
+ int indent);
+
+/**
+ * ibus_registry_check_modification:
+ * @registry: An #IBusRegistry.
+ * @returns: %TRUE if mtime is changed; %FALSE otherwise.
+ *
+ * Check if the registry is updated.
+ */
+gboolean ibus_registry_check_modification
+ (IBusRegistry *registry);
+
+/**
+ * ibus_registry_get_components:
+ * @registry: An #IBusRegistry.
+ * @returns: (transfer container) (element-type IBusComponent):
+ * a list of #IBusComponent objects.
+ * The caller has to call g_list_free() for the returned list.
+ *
+ * List components.
+ */
+GList *ibus_registry_get_components (IBusRegistry *registry);
+
+/**
+ * ibus_registry_get_observed_paths:
+ * @registry: An #IBusRegistry.
+ * @returns: (transfer container) (element-type IBusObservedPath):
+ * a list of #IBusObservedPath objects.
+ * The caller has to call g_list_free() for the returned list.
+ *
+ * List observed paths.
+ */
+GList *ibus_registry_get_observed_paths
+ (IBusRegistry *registry);
+
+/**
+ * ibus_registry_start_monitor_changes:
+ * @registry: An #IBusRegistry.
+ *
+ * Start to monitor observed paths.
+ */
+void ibus_registry_start_monitor_changes
+ (IBusRegistry *registry);
+
+G_END_DECLS
+#endif
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
index ad8bb8e5..b5b2da3e 100644
--- a/src/tests/Makefile.am
+++ b/src/tests/Makefile.am
@@ -2,8 +2,8 @@
#
# ibus - The Input Bus
#
-# Copyright (c) 2007-2010 Peng Huang <shawn.p.huang@gmail.com>
-# Copyright (c) 2007-2010 Red Hat, Inc.
+# Copyright (c) 2007-2013 Peng Huang <shawn.p.huang@gmail.com>
+# Copyright (c) 2007-2013 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -40,17 +40,18 @@ prog_ldadd = \
$(NULL)
noinst_PROGRAMS = $(TESTS)
-TESTS = \
- ibus-bus \
- ibus-config \
- ibus-configservice\
- ibus-factory \
- ibus-inputcontext \
- ibus-inputcontext-create \
- ibus-keynames \
- ibus-serializable \
- ibus-share \
- ibus-util \
+TESTS = \
+ ibus-bus \
+ ibus-config \
+ ibus-configservice \
+ ibus-factory \
+ ibus-inputcontext \
+ ibus-inputcontext-create \
+ ibus-keynames \
+ ibus-registry \
+ ibus-serializable \
+ ibus-share \
+ ibus-util \
$(NULL)
if ENABLE_ENGINE
@@ -91,6 +92,9 @@ ibus_inputcontext_create_LDADD = $(prog_ldadd)
ibus_keynames_SOURCES = ibus-keynames.c
ibus_keynames_LDADD = $(prog_ldadd)
+ibus_registry_SOURCES = ibus-registry.c
+ibus_registry_LDADD = $(prog_ldadd)
+
ibus_serializable_SOURCES = ibus-serializable.c
ibus_serializable_LDADD = $(prog_ldadd)
diff --git a/src/tests/ibus-registry.c b/src/tests/ibus-registry.c
new file mode 100644
index 00000000..c1cfd8a1
--- /dev/null
+++ b/src/tests/ibus-registry.c
@@ -0,0 +1,11 @@
+#include <ibus.h>
+
+int main()
+{
+#if !GLIB_CHECK_VERSION(2,35,0)
+ g_type_init ();
+#endif
+ IBusRegistry *registry = ibus_registry_new ();
+ g_object_unref (registry);
+ return 0;
+}