]> git.pld-linux.org Git - packages/ibus.git/commitdiff
- initial revison based on Fedora package
authorJan Rękorajski <baggins@pld-linux.org>
Tue, 19 Jul 2011 12:01:54 +0000 (12:01 +0000)
committercvs2git <feedback@pld-linux.org>
Sun, 24 Jun 2012 12:13:13 +0000 (12:13 +0000)
Changed files:
    ibus-530711-preload-sys.patch -> 1.1
    ibus-541492-xkb.patch -> 1.1
    ibus-711632-fedora-fallback-icon.patch -> 1.1
    ibus-HEAD.patch -> 1.1
    ibus-xx-bridge-hotkey.patch -> 1.1
    ibus-xx-icon-symbol.patch -> 1.1
    ibus-xx-setup-frequent-lang.patch -> 1.1
    ibus.spec -> 1.1
    ibus.xinputd -> 1.1

ibus-530711-preload-sys.patch [new file with mode: 0644]
ibus-541492-xkb.patch [new file with mode: 0644]
ibus-711632-fedora-fallback-icon.patch [new file with mode: 0644]
ibus-HEAD.patch [new file with mode: 0644]
ibus-xx-bridge-hotkey.patch [new file with mode: 0644]
ibus-xx-icon-symbol.patch [new file with mode: 0644]
ibus-xx-setup-frequent-lang.patch [new file with mode: 0644]
ibus.spec [new file with mode: 0644]
ibus.xinputd [new file with mode: 0644]

diff --git a/ibus-530711-preload-sys.patch b/ibus-530711-preload-sys.patch
new file mode 100644 (file)
index 0000000..7dec5ba
--- /dev/null
@@ -0,0 +1,725 @@
+From 7f81445b3e6613d14f64253506a309095a37c8d7 Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Mon, 20 Jun 2011 19:04:42 +0900
+Subject: [PATCH] Reload preload engines until users customize the list.
+
+The idea is, if users don't customize the preload_engines with ibus-setup,
+users would prefer to load the system default engines again by login.
+The gconf value 'preload_engine_mode' is
+IBUS_PRELOAD_ENGINE_MODE_USER by default but set
+IBUS_PRELOAD_ENGINE_MODE_LANG_RELATIVE for the initial login.
+If preload_engine_mode is IBUS_PRELOAD_ENGINE_MODE_LANG_RELATIVE,
+ibus-daemon loads the system preload engines by langs.
+If preload_engine_mode is IBUS_PRELOAD_ENGINE_MODE_USER,
+ibus-daemon do not update the gconf value preload_engines.
+On the other hand, if users enable the customized engine checkbutton
+on ibus-setup, ibus-setup sets 'preload_engine_mode' as
+IBUS_PRELOAD_ENGINE_MODE_USER and users can customize the value
+'preload_engines'.
+---
+ bus/ibusimpl.c       |  402 +++++++++++++++++++++++++++++++++++++++-----------
+ data/ibus.schemas.in |   13 ++
+ ibus/common.py       |    6 +
+ setup/main.py        |   70 ++++++++-
+ setup/setup.ui       |   21 +++-
+ src/ibustypes.h      |   10 ++
+ 6 files changed, 427 insertions(+), 95 deletions(-)
+
+diff --git a/bus/ibusimpl.c b/bus/ibusimpl.c
+index a7ae52b..bb4b8ae 100644
+--- a/bus/ibusimpl.c
++++ b/bus/ibusimpl.c
+@@ -144,6 +144,9 @@ static void     bus_ibus_impl_set_previous_engine
+ static void     bus_ibus_impl_set_preload_engines
+                                                 (BusIBusImpl        *ibus,
+                                                  GVariant           *value);
++static void     bus_ibus_impl_set_preload_engine_mode
++                                                (BusIBusImpl        *ibus,
++                                                 GVariant           *value);
+ static void     bus_ibus_impl_set_use_sys_layout
+                                                 (BusIBusImpl        *ibus,
+                                                  GVariant           *value);
+@@ -285,6 +288,259 @@ _panel_destroy_cb (BusPanelProxy *panel,
+ }
+ static void
++_config_set_value_done (GObject      *object,
++                        GAsyncResult *res,
++                        gpointer      user_data)
++{
++    IBusConfig *config = (IBusConfig *) object;
++    GVariant *value = (GVariant *) user_data;
++    GError *error = NULL;
++
++    g_assert (IBUS_IS_CONFIG (config));
++
++    if (!ibus_config_set_value_async_finish (config, res, &error)) {
++        if (error) {
++            g_error_free (error);
++        }
++    }
++    g_variant_unref (value);
++}
++
++#ifndef OS_CHROMEOS
++static gint
++_engine_desc_cmp (IBusEngineDesc *desc1,
++                  IBusEngineDesc *desc2)
++{
++    return - ((gint) ibus_engine_desc_get_rank (desc1)) +
++              ((gint) ibus_engine_desc_get_rank (desc2));
++}
++#endif
++
++#ifndef OS_CHROMEOS
++static gint
++_get_config_preload_engine_mode (BusIBusImpl *ibus)
++{
++    GVariant *variant = NULL;
++    gint preload_engine_mode = IBUS_PRELOAD_ENGINE_MODE_USER;
++
++    g_assert (BUS_IS_IBUS_IMPL (ibus));
++
++    if (ibus->config == NULL) {
++        return preload_engine_mode;
++    }
++
++    variant = ibus_config_get_value (ibus->config, "general",
++                                     "preload_engines");
++    if (variant == NULL) {
++        /* Set LANG_RELATIVE mode for the initial login */
++        preload_engine_mode = IBUS_PRELOAD_ENGINE_MODE_LANG_RELATIVE;
++        variant = g_variant_ref_sink (g_variant_new ("i", preload_engine_mode));
++        ibus_config_set_value_async (ibus->config, "general",
++                                     "preload_engine_mode", variant,
++                                     -1,
++                                     NULL,
++                                     _config_set_value_done,
++                                     variant);
++        return preload_engine_mode;
++    } else {
++        g_variant_unref (variant);
++    }
++
++    variant = ibus_config_get_value (ibus->config, "general",
++                                     "preload_engine_mode");
++    if (variant != NULL) {
++        if (g_variant_classify (variant) == G_VARIANT_CLASS_INT32) {
++            preload_engine_mode = g_variant_get_int32 (variant);
++        }
++        g_variant_unref (variant);
++    }
++
++    return preload_engine_mode;
++}
++#endif
++
++static int
++_compare_engine_list_value (GVariant *value_a, GVariant *value_b)
++{
++    GVariant *value;
++    GVariantIter iter;
++    const gchar *engine_name = NULL;
++    gchar *concat_engine_names;
++    gchar *concat_engine_names_a = NULL;
++    gchar *concat_engine_names_b = NULL;
++    int retval = 0;
++
++    value = value_a;
++    concat_engine_names = NULL;
++    if (value != NULL && g_variant_classify (value) == G_VARIANT_CLASS_ARRAY) {
++        g_variant_iter_init (&iter, value);
++        while (g_variant_iter_loop (&iter, "&s", &engine_name)) {
++            gchar *tmp = g_strdup_printf ("%s::%s",
++                                          concat_engine_names ? concat_engine_names : "",
++                                          engine_name ? engine_name : "");
++            g_free (concat_engine_names);
++            concat_engine_names = tmp;
++        }
++    }
++    concat_engine_names_a = concat_engine_names;
++
++    value = value_b;
++    concat_engine_names = NULL;
++    if (value != NULL && g_variant_classify (value) == G_VARIANT_CLASS_ARRAY) {
++        g_variant_iter_init (&iter, value);
++        while (g_variant_iter_loop (&iter, "&s", &engine_name)) {
++            gchar *tmp = g_strdup_printf ("%s::%s",
++                                          concat_engine_names ? concat_engine_names : "",
++                                          engine_name ? engine_name : "");
++            g_free (concat_engine_names);
++            concat_engine_names = tmp;
++        }
++    }
++    concat_engine_names_b = concat_engine_names;
++
++    retval = g_strcmp0 (concat_engine_names_a, concat_engine_names_b);
++    g_free (concat_engine_names_a);
++    g_free (concat_engine_names_b);
++    return retval;
++}
++
++static void
++_preload_engines_config_get_value_done (GObject      *object,
++                                        GAsyncResult *res,
++                                        gpointer      user_data)
++{
++    IBusConfig *config = (IBusConfig *) object;
++    GVariant *new_value = (GVariant *) user_data;
++    GVariant *value = NULL;
++    GError *error = NULL;
++
++    g_assert (IBUS_IS_CONFIG (config));
++
++    value = ibus_config_get_value_async_finish (config, res, &error);
++    if (error) {
++        g_error_free (error);
++    }
++    if (_compare_engine_list_value (value, new_value) != 0) {
++        ibus_config_set_value_async (config, "general",
++                                     "preload_engines", new_value,
++                                     -1,
++                                     NULL,
++                                     _config_set_value_done,
++                                     new_value);
++    } else if (new_value) {
++        g_variant_unref (new_value);
++    }
++    if (value) {
++        g_variant_unref (value);
++    }
++}
++
++static void
++_set_preload_engines (BusIBusImpl *ibus,
++                      GVariant    *value)
++{
++    GList *engine_list = NULL;
++
++    g_assert (BUS_IS_IBUS_IMPL (ibus));
++
++    g_list_foreach (ibus->engine_list, (GFunc) g_object_unref, NULL);
++    g_list_free (ibus->engine_list);
++
++    if (value != NULL && g_variant_classify (value) == G_VARIANT_CLASS_ARRAY) {
++        GVariantIter iter;
++        g_variant_iter_init (&iter, value);
++        const gchar *engine_name = NULL;
++        while (g_variant_iter_loop (&iter, "&s", &engine_name)) {
++            IBusEngineDesc *engine = bus_registry_find_engine_by_name (ibus->registry, engine_name);
++            if (engine == NULL || g_list_find (engine_list, engine) != NULL)
++                continue;
++            engine_list = g_list_append (engine_list, engine);
++        }
++
++        if (engine_list != NULL &&
++            ibus->config != NULL) {
++            /* sync function will effects the startup performance.
++             * We'd always like to save the value so that ibus-setup
++             * get the updated engine list. */
++            ibus_config_get_value_async (ibus->config, "general",
++                                         "preload_engines",
++                                         -1,
++                                         NULL,
++                                         _preload_engines_config_get_value_done,
++                                         g_variant_ref_sink (value));
++        } else {
++            /* We don't update preload_engines with an empty string for safety.
++             * Just unref the floating value. */
++            g_variant_unref (value);
++        }
++    } else if (value != NULL) {
++        g_variant_unref (value);
++    }
++
++    g_list_foreach (engine_list, (GFunc) g_object_ref, NULL);
++    ibus->engine_list = engine_list;
++
++    if (ibus->engine_list) {
++        BusComponent *component = bus_component_from_engine_desc ((IBusEngineDesc *) ibus->engine_list->data);
++        if (component && !bus_component_is_running (component)) {
++            bus_component_start (component, g_verbose);
++        }
++    }
++
++    bus_ibus_impl_check_global_engine (ibus);
++    bus_ibus_impl_update_engines_hotkey_profile (ibus);
++}
++
++#ifndef OS_CHROMEOS
++static void
++_set_language_relative_preload_engines (BusIBusImpl *ibus)
++{
++    gchar *lang = NULL;
++    gchar *p = NULL;
++    GList *engines = NULL;
++    GList *list;
++    GVariantBuilder builder;
++
++    g_assert (BUS_IS_IBUS_IMPL (ibus));
++
++    /* The setlocale call first checks LC_ALL. If it's not available, checks
++     * LC_CTYPE. If it's also not available, checks LANG. */
++    lang = g_strdup (setlocale (LC_CTYPE, NULL));
++    if (lang == NULL) {
++        return;
++    }
++
++    p = index (lang, '.');
++    if (p) {
++        *p = '\0';
++    }
++
++    engines = bus_registry_get_engines_by_language (ibus->registry, lang);
++    if (engines == NULL) {
++        p = index (lang, '_');
++        if (p) {
++            *p = '\0';
++            engines = bus_registry_get_engines_by_language (ibus->registry, lang);
++        }
++    }
++    g_free (lang);
++
++    /* sort engines by rank */
++    engines = g_list_sort (engines, (GCompareFunc) _engine_desc_cmp);
++
++    g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
++    for (list = engines; list != NULL; list = list->next) {
++        IBusEngineDesc *desc = (IBusEngineDesc *)list->data;
++        /* ignore engines with rank <== 0 */
++        if (ibus_engine_desc_get_rank (desc) > 0)
++            g_variant_builder_add (&builder, "s", ibus_engine_desc_get_name (desc));
++    }
++    _set_preload_engines (ibus, g_variant_builder_end (&builder));
++    g_list_free (engines);
++}
++#endif
++
++static void
+ bus_ibus_impl_set_hotkey (BusIBusImpl *ibus,
+                           GQuark       hotkey,
+                           GVariant    *value)
+@@ -394,35 +650,50 @@ static void
+ bus_ibus_impl_set_preload_engines (BusIBusImpl *ibus,
+                                    GVariant    *value)
+ {
+-    GList *engine_list = NULL;
+-
+-    g_list_foreach (ibus->engine_list, (GFunc) g_object_unref, NULL);
+-    g_list_free (ibus->engine_list);
++#ifndef OS_CHROMEOS
++    gint preload_engine_mode = _get_config_preload_engine_mode (ibus);
+-    if (value != NULL && g_variant_classify (value) == G_VARIANT_CLASS_ARRAY) {
+-        GVariantIter iter;
+-        g_variant_iter_init (&iter, value);
+-        const gchar *engine_name = NULL;
+-        while (g_variant_iter_loop (&iter, "&s", &engine_name)) {
+-            IBusEngineDesc *engine = bus_registry_find_engine_by_name (ibus->registry, engine_name);
+-            if (engine == NULL || g_list_find (engine_list, engine) != NULL)
+-                continue;
+-            engine_list = g_list_append (engine_list, engine);
++    if (preload_engine_mode == IBUS_PRELOAD_ENGINE_MODE_USER) {
++        if (value == NULL) {
++            _set_language_relative_preload_engines (ibus);
++        } else {
++            _set_preload_engines (ibus, value);
+         }
+     }
++#else
++    _set_preload_engines (ibus, value);
++#endif
++}
+-    g_list_foreach (engine_list, (GFunc) g_object_ref, NULL);
+-    ibus->engine_list = engine_list;
++/**
++ * bus_ibus_impl_set_preload_engine_mode:
++ *
++ * A function to be called when "preload_engine_mode" config is updated.
++ */
++static void
++bus_ibus_impl_set_preload_engine_mode (BusIBusImpl *ibus,
++                                       GVariant    *value)
++{
++#ifndef OS_CHROMEOS
++    gint preload_engine_mode = IBUS_PRELOAD_ENGINE_MODE_USER;
+-    if (ibus->engine_list) {
+-        BusComponent *component = bus_component_from_engine_desc ((IBusEngineDesc *) ibus->engine_list->data);
+-        if (component && !bus_component_is_running (component)) {
+-            bus_component_start (component, g_verbose);
+-        }
++    /* bus_ibus_impl_reload_config() sets value = NULL.
++     * bus_ibus_impl_reload_config() is always called when
++     * RequestName signal is sent so it is good to get the gconf value
++     * again when value == NULL.
++     */
++    if (value != NULL && g_variant_classify (value) == G_VARIANT_CLASS_INT32) {
++        preload_engine_mode = g_variant_get_int32 (value);
++    } else {
++        preload_engine_mode = _get_config_preload_engine_mode (ibus);
+     }
+-    bus_ibus_impl_check_global_engine (ibus);
+-    bus_ibus_impl_update_engines_hotkey_profile (ibus);
++    if (preload_engine_mode == IBUS_PRELOAD_ENGINE_MODE_USER) {
++        return;
++    }
++
++    _set_language_relative_preload_engines (ibus);
++#endif
+ }
+ /**
+@@ -503,89 +774,47 @@ bus_ibus_impl_set_use_global_engine (BusIBusImpl *ibus,
+     }
+ }
+-#ifndef OS_CHROMEOS
+-static gint
+-_engine_desc_cmp (IBusEngineDesc *desc1,
+-                  IBusEngineDesc *desc2)
+-{
+-    return - ((gint) ibus_engine_desc_get_rank (desc1)) +
+-              ((gint) ibus_engine_desc_get_rank (desc2));
+-}
+-#endif
+-
+ /**
+  * bus_ibus_impl_set_default_preload_engines:
+  *
+- * If the "preload_engines" config variable is not set yet, set the default value which is determined based on a current locale.
++ * bus_ibus_impl_set_default_preload_engines handles the gconf value
++ * /desktop/ibus/general/preload_engines and preload_engine_mode.
++ * The idea is, if users don't customize the preload_engines with ibus-setup,
++ * users would prefer to load the system default engines again by login.
++ * The gconf value 'preload_engine_mode' is
++ * IBUS_PRELOAD_ENGINE_MODE_USER by default but set
++ * IBUS_PRELOAD_ENGINE_MODE_LANG_RELATIVE for the initial login.
++ * If preload_engine_mode is IBUS_PRELOAD_ENGINE_MODE_LANG_RELATIVE,
++ * ibus-daemon loads the system preload engines by langs.
++ * If preload_engine_mode is IBUS_PRELOAD_ENGINE_MODE_USER,
++ * ibus-daemon do not update the gconf value preload_engines.
++ * On the other hand, if users enable the customized engine checkbutton
++ * on ibus-setup, ibus-setup sets 'preload_engine_mode' as
++ * IBUS_PRELOAD_ENGINE_MODE_USER and users can customize the value
++ * 'preload_engines'.
+  */
+ static void
+ bus_ibus_impl_set_default_preload_engines (BusIBusImpl *ibus)
+ {
+ #ifndef OS_CHROMEOS
+-    g_assert (BUS_IS_IBUS_IMPL (ibus));
+-
+     static gboolean done = FALSE;
++    gint preload_engine_mode = IBUS_PRELOAD_ENGINE_MODE_USER;
++
++    g_assert (BUS_IS_IBUS_IMPL (ibus));
+     if (done || ibus->config == NULL) {
+         return;
+     }
+-    GVariant *variant = ibus_config_get_value (ibus->config, "general", "preload_engines");
+-    if (variant != NULL) {
++    preload_engine_mode = _get_config_preload_engine_mode (ibus);
++
++    if (preload_engine_mode == IBUS_PRELOAD_ENGINE_MODE_USER) {
+         done = TRUE;
+-        g_variant_unref (variant);
+         return;
+     }
+     done = TRUE;
+-
+-    /* The setlocale call first checks LC_ALL. If it's not available, checks
+-     * LC_CTYPE. If it's also not available, checks LANG. */
+-    gchar *lang = g_strdup (setlocale (LC_CTYPE, NULL));
+-    if (lang == NULL) {
+-        return;
+-    }
+-
+-    gchar *p = index (lang, '.');
+-    if (p) {
+-        *p = '\0';
+-    }
+-
+-    GList *engines = bus_registry_get_engines_by_language (ibus->registry, lang);
+-    if (engines == NULL) {
+-        p = index (lang, '_');
+-        if (p) {
+-            *p = '\0';
+-            engines = bus_registry_get_engines_by_language (ibus->registry, lang);
+-        }
+-    }
+-    g_free (lang);
+-
+-    /* sort engines by rank */
+-    engines = g_list_sort (engines, (GCompareFunc) _engine_desc_cmp);
+-
+-    GVariantBuilder builder;
+-    g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
+-    GList *list;
+-    for (list = engines; list != NULL; list = list->next) {
+-        IBusEngineDesc *desc = (IBusEngineDesc *) list->data;
+-        /* ignore engines with rank <= 0 */
+-        if (ibus_engine_desc_get_rank (desc) > 0)
+-            g_variant_builder_add (&builder, "s", ibus_engine_desc_get_name (desc));
+-    }
+-
+-    GVariant *value = g_variant_builder_end (&builder);
+-    if (value != NULL) {
+-        if (g_variant_n_children (value) > 0) {
+-            ibus_config_set_value (ibus->config,
+-                                   "general", "preload_engines", value);
+-        } else {
+-            /* We don't update preload_engines with an empty string for safety.
+-             * Just unref the floating value. */
+-            g_variant_unref (value);
+-        }
+-    }
+-    g_list_free (engines);
++    _set_language_relative_preload_engines (ibus);
+ #endif
+ }
+@@ -601,6 +830,7 @@ const static struct {
+     { "general/hotkey", "next_engine_in_menu",   bus_ibus_impl_set_next_engine_in_menu },
+     { "general/hotkey", "previous_engine",       bus_ibus_impl_set_previous_engine },
+     { "general", "preload_engines",              bus_ibus_impl_set_preload_engines },
++    { "general", "preload_engine_mode",          bus_ibus_impl_set_preload_engine_mode },
+     { "general", "use_system_keyboard_layout",   bus_ibus_impl_set_use_sys_layout },
+     { "general", "use_global_engine",            bus_ibus_impl_set_use_global_engine },
+     { "general", "embed_preedit_text",           bus_ibus_impl_set_embed_preedit_text },
+diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in
+index 7ca4899..d4334e1 100644
+--- a/data/ibus.schemas.in
++++ b/data/ibus.schemas.in
+@@ -13,6 +13,19 @@
+       </locale>
+     </schema>
+     <schema>
++      <key>/schemas/desktop/ibus/general/preload_engine_mode</key>
++      <applyto>/desktop/ibus/general/preload_engine_mode</applyto>
++      <owner>ibus</owner>
++      <type>int</type>
++      <default>0</default>
++      <locale name="C">
++        <short>Preload engine mode</short>
++           <long>Preload engines are loaded with this mode.
++                 0 = user customized engines.
++                 1 = language related engines.</long>
++      </locale>
++    </schema>
++    <schema>
+       <key>/schemas/desktop/ibus/general/hotkey/trigger</key>
+       <applyto>/desktop/ibus/general/hotkey/trigger</applyto>
+       <owner>ibus</owner>
+diff --git a/ibus/common.py b/ibus/common.py
+index 6483aae..127ed93 100644
+--- a/ibus/common.py
++++ b/ibus/common.py
+@@ -40,6 +40,8 @@ __all__ = (
+         "BUS_REQUEST_NAME_REPLY_IN_QUEUE",
+         "BUS_REQUEST_NAME_REPLY_EXISTS",
+         "BUS_REQUEST_NAME_REPLY_ALREADY_OWNER",
++        "PRELOAD_ENGINE_MODE_USER",
++        "PRELOAD_ENGINE_MODE_LANG_RELATIVE",
+         "default_reply_handler",
+         "default_error_handler",
+         "DEFAULT_ASYNC_HANDLERS",
+@@ -150,6 +152,10 @@ BUS_REQUEST_NAME_REPLY_IN_QUEUE      = 2
+ BUS_REQUEST_NAME_REPLY_EXISTS        = 3
+ BUS_REQUEST_NAME_REPLY_ALREADY_OWNER = 4
++# define preload engine mode
++PRELOAD_ENGINE_MODE_USER          = 0
++PRELOAD_ENGINE_MODE_LANG_RELATIVE = 1
++
+ def default_reply_handler( *args):
+     pass
+diff --git a/setup/main.py b/setup/main.py
+index 7f4a040..192fb88 100644
+--- a/setup/main.py
++++ b/setup/main.py
+@@ -92,6 +92,7 @@ class Setup(object):
+         # keyboard shortcut
+         # trigger
+         self.__config = self.__bus.get_config()
++        self.__config.connect("value-changed", self.__config_value_changed_cb)
+         shortcuts = self.__config.get_value(
+                         "general/hotkey", "trigger",
+                         ibus.CONFIG_GENERAL_SHORTCUT_TRIGGER_DEFAULT)
+@@ -213,15 +214,22 @@ class Setup(object):
+         self.__checkbutton_use_global_engine.connect("toggled", self.__checkbutton_use_global_engine_toggled_cb)
+         # init engine page
++        preload_engine_mode = self.__config.get_value("general",
++                                                      "preload_engine_mode",
++                                                      ibus.common.PRELOAD_ENGINE_MODE_USER)
++        button = self.__builder.get_object("checkbutton_preload_engine_mode")
++        if preload_engine_mode == ibus.common.PRELOAD_ENGINE_MODE_USER:
++            button.set_active(True)
++            self.__builder.get_object("hbox_customize_active_input_methods").set_sensitive(True)
++        else:
++            button.set_active(False)
++            self.__builder.get_object("hbox_customize_active_input_methods").set_sensitive(False)
++        button.connect("toggled", self.__checkbutton_preload_engine_mode_toggled_cb)
+         self.__engines = self.__bus.list_engines()
+         self.__combobox = self.__builder.get_object("combobox_engines")
+         self.__combobox.set_engines(self.__engines)
+-        tmp_dict = {}
+-        for e in self.__engines:
+-            tmp_dict[e.name] = e
+-        engine_names = self.__config.get_value("general", "preload_engines", [])
+-        engines = [tmp_dict[name] for name in engine_names if name in tmp_dict]
++        engines = self.__bus.list_active_engines()
+         self.__treeview = self.__builder.get_object("treeview_engines")
+         self.__treeview.set_engines(engines)
+@@ -265,6 +273,26 @@ class Setup(object):
+             engine_names = map(lambda e: e.name, engines)
+             self.__config.set_list("general", "preload_engines", engine_names, "s")
++    def __get_engine_descs_from_names(self, engine_names):
++        tmp_dict = {}
++        for e in self.__engines:
++            tmp_dict[e.name] = e
++        engines = [tmp_dict[name] for name in engine_names if name in tmp_dict]
++        return engines
++
++    def __compare_descs(self, engines_a, engines_b):
++        engines = engines_a
++        concat_engine_names = ""
++        for engine in engines:
++            concat_engine_names = "%s::%s" % (concat_engine_names, engine.name)
++        concat_engine_names_a = concat_engine_names
++        engines = engines_b
++        concat_engine_names = ""
++        for engine in engines:
++            concat_engine_names = "%s::%s" % (concat_engine_names, engine.name)
++        concat_engine_names_b = concat_engine_names
++        return concat_engine_names_a == concat_engine_names_b
++
+     def __button_engine_add_cb(self, button):
+         engine = self.__combobox.get_active_engine()
+         self.__treeview.append_engine(engine)
+@@ -276,6 +304,32 @@ class Setup(object):
+             about.run()
+             about.destroy()
++    def __checkbutton_preload_engine_mode_toggled_cb(self, button):
++        if button.get_active():
++            self.__config.set_value("general",
++                                    "preload_engine_mode",
++                                    ibus.common.PRELOAD_ENGINE_MODE_USER)
++            self.__builder.get_object("hbox_customize_active_input_methods").set_sensitive(True)
++            self.__treeview.notify("engines")
++        else:
++            message = _("The list of your saved input methods will be " \
++                        "cleared immediately and the list will be " \
++                        "configured by the login language every time. " \
++                        "Do you agree with this?")
++            dlg = gtk.MessageDialog(type = gtk.MESSAGE_QUESTION,
++                    buttons = gtk.BUTTONS_YES_NO,
++                    message_format = message)
++            id = dlg.run()
++            dlg.destroy()
++            self.__flush_gtk_events()
++            if id != gtk.RESPONSE_YES:
++                button.set_active(True)
++                return
++            self.__config.set_value("general",
++                                    "preload_engine_mode",
++                                    ibus.common.PRELOAD_ENGINE_MODE_LANG_RELATIVE)
++            self.__builder.get_object("hbox_customize_active_input_methods").set_sensitive(False)
++
+     def __init_bus(self):
+         try:
+             self.__bus = ibus.Bus()
+@@ -466,7 +520,11 @@ class Setup(object):
+         self.__config.set_value("general", "use_global_engine", value)
+     def __config_value_changed_cb(self, bus, section, name, value):
+-        pass
++        if section == 'general' and name == 'preload_engines':
++            engines = self.__get_engine_descs_from_names(value)
++            current_engines = self.__treeview.get_engines()
++            if self.__compare_descs(engines, current_engines) == False:
++                self.__treeview.set_engines(engines)
+     def __config_reloaded_cb(self, bus):
+         pass
+diff --git a/setup/setup.ui b/setup/setup.ui
+index f1e6d0b..562c091 100644
+--- a/setup/setup.ui
++++ b/setup/setup.ui
+@@ -582,7 +582,22 @@
+                     <property name="visible">True</property>
+                     <property name="orientation">vertical</property>
+                     <child>
+-                      <object class="GtkHBox" id="hbox1">
++                      <object class="GtkCheckButton" id="checkbutton_preload_engine_mode">
++                        <property name="visible">True</property>
++                        <property name="label" translatable="yes">Customize active input _methods</property>
++                        <property name="use_underline">True</property>
++                        <property name="can_focus">True</property>
++                        <property name="receives_default">False</property>
++                        <property name="tooltip_text" translatable="yes">Customize active input methods</property>
++                        <property name="draw_indicator">True</property>
++                      </object>
++                      <packing>
++                        <property name="expand">False</property>
++                        <property name="position">0</property>
++                      </packing>
++                    </child>
++                    <child>
++                      <object class="GtkHBox" id="hbox_customize_active_input_methods">
+                         <property name="visible">True</property>
+                         <child>
+                           <object class="GtkAlignment" id="alignment6">
+@@ -733,7 +748,7 @@
+                         </child>
+                       </object>
+                       <packing>
+-                        <property name="position">0</property>
++                        <property name="position">1</property>
+                       </packing>
+                     </child>
+                     <child>
+@@ -772,7 +787,7 @@ You may use up/down buttons to change it.&lt;/i&gt;&lt;/small&gt;</property>
+                       </object>
+                       <packing>
+                         <property name="expand">False</property>
+-                        <property name="position">1</property>
++                        <property name="position">2</property>
+                       </packing>
+                     </child>
+                   </object>
+diff --git a/src/ibustypes.h b/src/ibustypes.h
+index 6a31847..14b44fc 100644
+--- a/src/ibustypes.h
++++ b/src/ibustypes.h
+@@ -186,6 +186,16 @@ typedef enum {
+ } IBusError;
+ /**
++ * IBusPreloadEngineMode:
++ * @IBUS_PRELOAD_ENGINE_MODE_USER: user custimized engines
++ * @IBUS_PRELOAD_ENGINE_MODE_LANG_RELATIVE: language related engines.
++ */
++typedef enum {
++    IBUS_PRELOAD_ENGINE_MODE_USER          = 0,
++    IBUS_PRELOAD_ENGINE_MODE_LANG_RELATIVE = 1,
++} IBusPreloadEngineMode;
++
++/**
+  * IBusRectangle:
+  * @x: x coordinate.
+  * @y: y coordinate.
+-- 
+1.7.4.4
+
diff --git a/ibus-541492-xkb.patch b/ibus-541492-xkb.patch
new file mode 100644 (file)
index 0000000..3fc48a6
--- /dev/null
@@ -0,0 +1,4500 @@
+From 14986cb38f431d132332b2e8f9da1ca2b8a5d10e Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Wed, 29 Jun 2011 16:52:08 +0900
+Subject: [PATCH] Add XKB layouts
+
+---
+ Makefile.am                |    7 +
+ configure.ac               |   56 ++++
+ data/ibus.schemas.in       |   36 +++
+ ibus-1.0.pc.in             |    2 +
+ ibus/Makefile.am           |   26 ++
+ ibus/__init__.py           |    2 +
+ ibus/bus.py                |    3 +
+ ibus/interface/iibus.py    |    3 +
+ ibus/xkblayout.py.in       |  215 ++++++++++++++++
+ ibus/xkbxml.py.in          |  413 ++++++++++++++++++++++++++++++
+ setup/Makefile.am          |    1 +
+ setup/enginecombobox.py    |    7 +-
+ setup/main.py              |    3 +
+ setup/setup.ui             |  609 +++++++++++++++++++++++++++++++++++++++++++-
+ setup/xkbsetup.py          |  454 +++++++++++++++++++++++++++++++++
+ src/Makefile.am            |    5 +
+ src/ibus.h                 |    1 +
+ src/ibusfactory.c          |   21 ++-
+ src/ibusfactory.h          |    5 +-
+ src/ibusxkbxml.c           |  440 ++++++++++++++++++++++++++++++++
+ src/ibusxkbxml.h           |  172 +++++++++++++
+ ui/gtk/panel.py            |   39 +++
+ xkb/Makefile.am            |  104 ++++++++
+ xkb/ibus-engine-xkb-main.c |  397 +++++++++++++++++++++++++++++
+ xkb/ibus-engine-xkb-main.h |   46 ++++
+ xkb/ibus-xkb-main.c        |  112 ++++++++
+ xkb/xkblayout.xml.in       |   16 ++
+ xkb/xkblayoutconfig.xml.in |    6 +
+ xkb/xkblib.c               |  327 ++++++++++++++++++++++++
+ xkb/xkblib.h               |   41 +++
+ xkb/xkbxml.c               |  335 ++++++++++++++++++++++++
+ xkb/xkbxml.h               |  110 ++++++++
+ 32 files changed, 4008 insertions(+), 6 deletions(-)
+ create mode 100644 ibus/xkblayout.py.in
+ create mode 100644 ibus/xkbxml.py.in
+ create mode 100644 setup/xkbsetup.py
+ create mode 100644 src/ibusxkbxml.c
+ create mode 100644 src/ibusxkbxml.h
+ create mode 100644 xkb/Makefile.am
+ create mode 100644 xkb/ibus-engine-xkb-main.c
+ create mode 100644 xkb/ibus-engine-xkb-main.h
+ create mode 100644 xkb/ibus-xkb-main.c
+ create mode 100644 xkb/xkblayout.xml.in
+ create mode 100644 xkb/xkblayoutconfig.xml.in
+ create mode 100644 xkb/xkblib.c
+ create mode 100644 xkb/xkblib.h
+ create mode 100644 xkb/xkbxml.c
+ create mode 100644 xkb/xkbxml.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 7be558b..59fbabb 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -42,6 +42,12 @@ DAEMON_DIRS =       \
+       $(NULL)
+ endif
++if ENABLE_XKB
++XKB_DIRS =          \
++      xkb             \
++      $(NULL)
++endif
++
+ if ENABLE_MEMCONF
+ MEMCONF_DIRS = \
+       memconf \
+@@ -60,6 +66,7 @@ SUBDIRS =           \
+       $(DAEMON_DIRS)  \
+       $(PYTHON_DIRS)  \
+       $(GCONF_DIRS)   \
++      $(XKB_DIRS)     \
+       $(MEMCONF_DIRS) \
+       $(NULL)
+diff --git a/configure.ac b/configure.ac
+index 5544dfa..85e5e30 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -185,6 +185,60 @@ else
+     enable_xim="no (disabled, use --enable-xim to enable)"
+ fi
++AC_ARG_ENABLE(xkb,
++    AS_HELP_STRING([--disable-xkb],
++                   [Do not build xkb]),
++    [enable_xkb=$enableval],
++    [enable_xkb=yes]
++)
++
++AM_CONDITIONAL([ENABLE_XKB], [test x"$enable_xkb" = x"yes"])
++if test x"$enable_xkb" = x"yes"; then
++    PKG_CHECK_MODULES(X11, [
++        x11
++    ])
++    PKG_CHECK_MODULES(XKB,
++        [xkbfile],,
++        [XKB_LIBS="-lxkbfile"]
++    )
++    AC_DEFINE(HAVE_XKB, 1, [define to 1 if you have xkbfile])
++    HAVE_IBUS_XKB=true
++else
++    enable_xkb="no (disabled, use --enable-xkb to enable)"
++    HAVE_IBUS_XKB=false
++fi
++AC_SUBST(HAVE_IBUS_XKB)
++
++# define XKB rules file
++AC_ARG_WITH(xkb-rules-xml,
++    AS_HELP_STRING([--with-xkb-rules-xml[=$DIR/evdev.xml]],
++        [Set evdev.xml file path (default: /usr/share/X11/xkb/rules/evdev.xml)]),
++    XKB_RULES_XML_FILE=$with_xkb_rules_xml,
++    XKB_RULES_XML_FILE="/usr/share/X11/xkb/rules/evdev.xml"
++)
++AC_DEFINE_UNQUOTED(XKB_RULES_XML_FILE, "$XKB_RULES_XML_FILE",
++    [Define file path of evdev.xml])
++AC_SUBST(XKB_RULES_XML_FILE)
++
++# define XKB preload layouts
++AC_ARG_WITH(xkb-preload-layouts,
++    AS_HELP_STRING([--with-xkb-preload-layouts[=layout,...]],
++        [Set preload xkb layouts (default: us,fr,de,...)]),
++    XKB_PRELOAD_LAYOUTS=$with_xkb_preload_layouts,
++    [XKB_PRELOAD_LAYOUTS=""\
++"us,us(chr),us(dvorak),ad,al,am,ara,az,ba,bd,be,bg,br,bt,by,"\
++"de,dk,ca,ch,cn(tib),cz,ee,epo,es,et,fi,fo,fr,"\
++"gb,ge,ge(dsb),ge(ru),ge(os),gh,gh(akan),gh(ewe),gh(fula),gh(ga),gh(hausa),"\
++"gn,gr,hu,hr,ie,ie(CloGaelach),il,"\
++"in,in(ben),in(guj),in(guru),in(jhelum),in(kan),in(mal),in(ori),in(tam),"\
++"in(tel),in(urd-phonetic),in(bolnagri),iq,iq(ku),ir,ir(ku),is,it,"\
++"kg,kh,kz,la,latam,lk,lk(tam_unicode),lt,lv,ma,ma(tifinagh),mal,mao,"\
++"me,mk,mm,mt,mv,ng,ng(hausa),ng,ng(igbo),ng(yoruba),nl,no,no(smi),np,"\
++"pk,pl,pl(csb),pt,ro,rs,ru,ru(cv),ru(kom),ru(sah),ru(tt),ru(xal),"\
++"se,si,sk,sy,sy(ku),th,tj,tr,ua,uz,vn"]
++)
++AC_SUBST(XKB_PRELOAD_LAYOUTS)
++
+ # GObject introspection
+ GOBJECT_INTROSPECTION_CHECK([0.6.8])
+@@ -430,6 +484,7 @@ gconf/Makefile
+ gconf/gconf.xml.in
+ bindings/Makefile
+ bindings/vala/Makefile
++xkb/Makefile
+ ])
+ AC_OUTPUT
+@@ -445,6 +500,7 @@ Build options:
+   Build gtk2 immodule       $enable_gtk2
+   Build gtk3 immodule       $enable_gtk3
+   Build XIM agent server    $enable_xim
++  Build XKB                 $enable_xkb
+   Build python modules      $enable_python
+   Build gconf modules       $enable_gconf
+   Build memconf modules     $enable_memconf
+diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in
+index b75295e..7ca4899 100644
+--- a/data/ibus.schemas.in
++++ b/data/ibus.schemas.in
+@@ -190,6 +190,42 @@
+       </locale>
+     </schema>
+     <schema>
++      <key>/schemas/desktop/ibus/general/system_keyboard_layout</key>
++      <applyto>/desktop/ibus/general/system_keyboard_layout</applyto>
++      <owner>ibus</owner>
++      <type>string</type>
++      <default>default</default>
++      <gettext_domain>ibus</gettext_domain>
++      <locale name="C">
++        <short>Set system keyboard layout</short>
++            <long>Override default system keyboard layout. default is 'default'</long>
++      </locale>
++    </schema>
++    <schema>
++      <key>/schemas/desktop/ibus/general/system_keyboard_option</key>
++      <applyto>/desktop/ibus/general/system_keyboard_option</applyto>
++      <owner>ibus</owner>
++      <type>string</type>
++      <default>default</default>
++      <gettext_domain>ibus</gettext_domain>
++      <locale name="C">
++        <short>Set system keyboard option</short>
++            <long>Override default system keyboard option. default is 'default'</long>
++      </locale>
++    </schema>
++    <schema>
++      <key>/schemas/desktop/ibus/general/xkb_latin_layouts</key>
++      <applyto>/desktop/ibus/general/xkb_latin_layouts</applyto>
++      <owner>ibus</owner>
++      <type>list</type>
++      <list_type>string</list_type>
++      <default>[ara,bg,cz,dev,gr,gur,in,jp(kana),mal,mkd,ru,ua]</default>
++      <locale name="C">
++        <short>Latin layout which have no ASCII</short>
++          <long>us layout is appended to the latin layouts. variant is not needed.</long>
++      </locale>
++    </schema>
++    <schema>
+       <key>/schemas/desktop/ibus/panel/use_custom_font</key>
+       <applyto>/desktop/ibus/panel/use_custom_font</applyto>
+       <owner>ibus</owner>
+diff --git a/ibus-1.0.pc.in b/ibus-1.0.pc.in
+index 9f593ab..51eb67a 100644
+--- a/ibus-1.0.pc.in
++++ b/ibus-1.0.pc.in
+@@ -4,6 +4,8 @@ libdir=@libdir@
+ includedir=@includedir@
+ datadir=@datadir@
+ pkgdatadir=@datadir@/ibus
++have_ibus_xkb=@HAVE_IBUS_XKB@
++ibus_xkb=@libexecdir@/ibus-xkb
+ Name: IBus
+ Description: IBus Library
+diff --git a/ibus/Makefile.am b/ibus/Makefile.am
+index c71df1b..508c98f 100644
+--- a/ibus/Makefile.am
++++ b/ibus/Makefile.am
+@@ -58,12 +58,38 @@ nodist_ibus_PYTHON = \
+ ibusdir = @pkgpythondir@
++xkblayout_py_in_files = \
++      xkblayout.py.in \
++      xkbxml.py.in \
++      $(NULL)
++xkblayout_py_DATA = $(xkblayout_py_in_files:.py.in=.py)
++xkblayout_pydir = @pkgpythondir@
++
++ibus_PYTHON += $(xkblayout_py_DATA)
++
++if ENABLE_XKB
++XKB_COMMAND=\\\"$(libexecdir)/ibus-xkb\\\"
++HAVE_XKB=True
++else
++XKB_COMMAND="None"
++HAVE_XKB=False
++endif
++
++%.py : %.py.in
++      @sed -e "s|\@XKB_COMMAND\@|$(XKB_COMMAND)|g" \
++           -e "s|\@XKB_RULES_XML_FILE\@|$(XKB_RULES_XML_FILE)|g" \
++           -e "s|\@HAVE_XKB\@|$(HAVE_XKB)|g" \
++           -e "s|\@datadir\@|$(datadir)|g" \
++      $< > $@
++
+ EXTRA_DIST = \
+       _config.py.in \
++      $(xkblayout_py_in_files) \
+       $(NULL)
+ CLEANFILES = \
+       *.pyc \
++      $(xkblayout_py_DATA) \
+       $(NULL)
+ DISTCLEANFILES = \
+diff --git a/ibus/__init__.py b/ibus/__init__.py
+index 7c8f8be..3c25605 100644
+--- a/ibus/__init__.py
++++ b/ibus/__init__.py
+@@ -41,4 +41,6 @@ from text import *
+ from observedpath import *
+ from enginedesc import *
+ from component import *
++from xkblayout import *
++from xkbxml import *
+ from _config import *
+diff --git a/ibus/bus.py b/ibus/bus.py
+index 5738fad..05ec49e 100644
+--- a/ibus/bus.py
++++ b/ibus/bus.py
+@@ -160,6 +160,9 @@ class Bus(object.Object):
+             data = serializable.deserialize_object(data)
+         return data
++    def get_use_sys_layout(self):
++        return self.__ibus.GetUseSysLayout();
++
+     def introspect_ibus(self):
+         return self.__ibus.Introspect()
+diff --git a/ibus/interface/iibus.py b/ibus/interface/iibus.py
+index 678d517..7de56fc 100644
+--- a/ibus/interface/iibus.py
++++ b/ibus/interface/iibus.py
+@@ -75,6 +75,9 @@ class IIBus(dbus.service.Object):
+     @method(in_signature="v", out_signature="v")
+     def Ping(self, data, dbusconn): pass
++    @method(out_signature="b")
++    def GetUseSysLayout(self, dbusconn): pass
++
+     @signal(signature="")
+     def RegistryChanged(self): pass
+diff --git a/ibus/xkblayout.py.in b/ibus/xkblayout.py.in
+new file mode 100644
+index 0000000..4cb3ffd
+--- /dev/null
++++ b/ibus/xkblayout.py.in
+@@ -0,0 +1,215 @@
++# vim:set et sts=4 sw=4:
++#
++# ibus - The Input Bus
++#
++# Copyright (c) 2011 Takao Fujiwara <takao.fujiwara1@gmail.com>
++# Copyright (c) 2011 Peng Huang <shawn.p.huang@gmail.com>
++# Copyright (c) 2011 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 program; if not, write to the
++# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++# Boston, MA  02111-1307  USA
++
++__all__ = (
++        "XKBLayout",
++    )
++
++import os, sys, time
++
++XKB_COMMAND = @XKB_COMMAND@
++XKB_SESSION_TIME_OUT = 30.0
++
++class XKBLayout():
++    def __init__(self, config = None, command=XKB_COMMAND):
++        self.__config = config
++        self.__command = command
++        self.__use_xkb = True
++        if self.__command == None:
++            self.__use_xkb = False
++        self.__default_layout = self.get_layout()
++        self.__default_model = self.get_model()
++        self.__default_option = self.get_option()
++        self.__time_lag_session_xkb_layout = True
++        self.__time_lag_session_xkb_option = True
++        self.__time_lag_session_xkb_timer = time.time()
++        self.__xkb_latin_layouts = []
++        if config != None:
++            self.__xkb_latin_layouts = list(self.__config.get_value("general",
++                                                                    "xkb_latin_layouts",
++                                                                    []))
++
++
++    def __get_model_from_layout(self, layout):
++        left_bracket = layout.find('(')
++        right_bracket = layout.find(')')
++        if left_bracket >= 0 and right_bracket > left_bracket:
++            return (layout[:left_bracket], \
++                    layout[left_bracket + 1:right_bracket])
++        return (layout, "default")
++
++    def __get_output_from_cmdline(self, arg, string):
++        exec_command = "%s %s" % (self.__command, arg)
++        retval = None
++        for line in os.popen(exec_command).readlines():
++            line = line.strip()
++            if line.startswith(string):
++                retval = line[len(string):]
++                break
++        return retval
++
++    def use_xkb(self, enable):
++        if self.__command == None:
++            return
++        self.__use_xkb = enable
++
++    def get_layout(self):
++        if not self.__use_xkb:
++            return None
++        return self.__get_output_from_cmdline("--get", "layout: ")
++
++    def get_model(self):
++        if not self.__use_xkb:
++            return None
++        return self.__get_output_from_cmdline("--get", "model: ")
++
++    def get_option(self):
++        if not self.__use_xkb:
++            return None
++        return self.__get_output_from_cmdline("--get", "option: ")
++
++    def get_group(self):
++        if not self.__use_xkb:
++            return 0
++        return int(self.__get_output_from_cmdline("--get-group", "group: "))
++
++    def set_layout(self, layout="default", model="default", option="default"):
++        if not self.__use_xkb:
++            return
++        if layout == None:
++            return
++        if self.__default_layout == None:
++            # Maybe opening display was failed in constructor.
++            self.reload_default_layout()
++        if self.__default_layout == None:
++            return
++        layout = str(layout)
++        # if set_default_layout() is not default, the default layout is
++        # pulled from the current XKB. But it's possible gnome-settings-daemon
++        # does not run yet. I added XKB_SESSION_TIME_OUT for the timer.
++        if self.__time_lag_session_xkb_layout == True:
++            self.__default_layout = self.get_layout()
++            self.__default_model = self.get_model()
++        if self.__time_lag_session_xkb_option == True:
++            self.__default_option = self.get_option()
++        if (self.__time_lag_session_xkb_layout == True or \
++            self.__time_lag_session_xkb_option == True ) and \
++           (time.time() - self.__time_lag_session_xkb_timer \
++            > XKB_SESSION_TIME_OUT):
++            self.__time_lag_session_xkb_layout = False
++            self.__time_lag_session_xkb_option = False
++        if layout == "default":
++            layout = self.__default_layout
++        else:
++            self.__time_lag_session_xkb_layout = False
++        if model != None:
++            model = str(model)
++            if model == "default":
++                (layout, model) = self.__get_model_from_layout(layout)
++            if model == "default":
++                model = self.__default_model
++            else:
++                self.__time_lag_session_xkb_layout = False
++        if option != None:
++            option = str(option)
++            if option == "default":
++                option = self.__default_option
++            else:
++                self.__time_lag_session_xkb_option = False
++        need_us_layout = False
++        for latin_layout in self.__xkb_latin_layouts:
++            latin_layout = str(latin_layout)
++            if layout == latin_layout:
++                need_us_layout = True
++                break
++            if model != None and layout + '(' + model + ')' == latin_layout:
++                need_us_layout = True
++                break
++        if need_us_layout:
++            layout = layout + ",us"
++            if model != None:
++                model = model + ","
++        if layout == self.get_layout() and \
++           model == self.get_model() and \
++           option == self.get_option():
++            return
++        args = []
++        args.append(self.__command)
++        args.append(os.path.basename(self.__command))
++        args.append("--layout")
++        args.append(layout)
++        if model != None:
++            args.append("--model")
++            args.append(model)
++        if option != None:
++            args.append("--option")
++            args.append(option)
++        pid = os.spawnl(os.P_NOWAIT, *args)
++        os.waitpid(pid, 0)
++
++    def set_default_layout(self, layout="default", model="default"):
++        if not self.__use_xkb:
++            return
++        if layout == None:
++            print >> sys.stderr, "ibus.xkblayout: None layout"
++            return
++        if model == None:
++            print >> sys.stderr, "ibus.xkblayout: None model"
++            return
++        if layout == 'default':
++            self.__default_layout = self.get_layout()
++            self.__default_model = self.get_model()
++        else:
++            if model == 'default':
++                (layout, model) = self.__get_model_from_layout(layout)
++            self.__default_layout = layout
++            self.__time_lag_session_xkb_layout = False
++            if model == 'default':
++                self.__default_model = None
++            else:
++                self.__default_model = model
++
++    def set_default_option(self, option="default"):
++        if not self.__use_xkb:
++            return
++        if option == None:
++            print >> sys.stderr, "ibus.xkblayout: None option"
++            return
++        if option == 'default':
++            self.__default_option = self.get_option()
++        else:
++            self.__default_option = option
++            self.__time_lag_session_xkb_option = False
++
++    def get_default_layout(self):
++        return [self.__default_layout, self.__default_model];
++
++    def get_default_option(self):
++        return self.__default_option
++
++    def reload_default_layout(self):
++        if not self.__use_xkb:
++            return
++        self.__default_layout = self.get_layout()
++        self.__default_model = self.get_model()
++        self.__default_option = self.get_option()
+diff --git a/ibus/xkbxml.py.in b/ibus/xkbxml.py.in
+new file mode 100644
+index 0000000..7e5a44e
+--- /dev/null
++++ b/ibus/xkbxml.py.in
+@@ -0,0 +1,413 @@
++# vim:set et sts=4 sw=4:
++#
++# ibus - The Input Bus
++#
++# Copyright (c) 2011 Takao Fujiwara <takao.fujiwara1@gmail.com>
++# Copyright (c) 2011 Peng Huang <shawn.p.huang@gmail.com>
++# Copyright (c) 2011 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 program; if not, write to the
++# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++# Boston, MA  02111-1307  USA
++
++__all__ = (
++        "XKBConfigRegistry",
++        "XKBLayoutConfig",
++    )
++
++import os
++import string
++import xml.sax as sax
++import enginedesc
++from xml.sax.saxutils import XMLFilterBase, XMLGenerator
++from xml.sax._exceptions import SAXParseException
++from cStringIO import StringIO
++
++try:
++    from glib import get_user_config_dir
++except ImportError:
++    get_user_config_dir = lambda : None
++
++XKB_RULES_XML_FILE = "@XKB_RULES_XML_FILE@"
++
++class XKBConfigRegistryHandler(XMLFilterBase):
++    def __init__(self, parser=None, root='root'):
++        XMLFilterBase.__init__(self, parser)
++        self.__root = root
++        self.__current_node = root
++        self.__layoutlist_array = {}
++        self.__layoutlist = False
++        self.__layout = False
++        self.__layout_label = None
++        self.__layout_desc = {}
++        self.__layout_lang = {}
++        self.__variantlist = False
++        self.__variant = False
++        self.__variant_label = None
++        self.__variant_desc = {}
++        self.__optionlist_array = {}
++        self.__optionlist = False
++        self.__option_group_desc = {}
++        self.__option_desc = {}
++        self.__option = False
++        self.__option_label = None
++        self.__group = False
++        self.__group_label = None
++
++    def __characters_layoutlist(self, text):
++        if not self.__layout:
++            return
++        if self.__variant:
++            if self.__current_node == "name":
++                self.__variant_label = text
++                if self.__layout_label != None and \
++                   self.__layout_label in self.__layoutlist_array:
++                    self.__layoutlist_array[self.__layout_label].append(text)
++            elif self.__current_node == "description":
++                label =  "%s(%s)" % (self.__layout_label, self.__variant_label)
++                self.__variant_desc[label] = text
++            elif self.__current_node == "iso639Id":
++                label = self.__layout_label
++                if label != None:
++                    label = "%s(%s)" % (label, self.__variant_label)
++                else:
++                    label = self.__variant_label
++                if label not in self.__layout_lang:
++                    self.__layout_lang[label] = []
++                self.__layout_lang[label].append(text)
++            else:
++                pass
++        else:
++            if self.__current_node == "name":
++                self.__layout_label = text
++                self.__layoutlist_array[self.__layout_label] = []
++            elif self.__current_node == "description":
++                self.__layout_desc[self.__layout_label] = text
++            elif self.__current_node == "iso639Id":
++                if self.__layout_label not in self.__layout_lang:
++                    self.__layout_lang[self.__layout_label] = []
++                self.__layout_lang[self.__layout_label].append(text)
++            else:
++                pass
++
++    def __characters_optionlist(self, text):
++        if not self.__group:
++            return
++        if self.__option:
++            if self.__current_node == "name":
++                self.__option_label = text
++                if self.__group_label != None and \
++                   self.__group_label in self.__optionlist_array:
++                    self.__optionlist_array[self.__group_label].append(text)
++            elif self.__current_node == "description":
++                self.__option_desc[self.__option_label] = text
++            else:
++                pass
++        else:
++            if self.__current_node == "name":
++                self.__group_label = text
++                self.__optionlist_array[self.__group_label] = []
++            elif self.__current_node == "description":
++                self.__option_group_desc[self.__group_label] = text
++            else:
++                pass
++
++    def startElement(self, name, attrs):
++        self.__current_node = name
++        if name == "layoutList":
++            self.__layoutlist = True
++        elif name == "layout":
++            self.__layout = True
++            self.__layout_label = None
++        elif name == "variantList":
++            self.__variantlist = True
++        elif name == "variant":
++            self.__variant = True
++            self.__variant_label = None
++        elif name == "optionList":
++            self.__optionlist = True
++        elif name == "option":
++            self.__option = True
++            self.__option_label = None
++        elif name == "group":
++            self.__group = True
++            self.__group_label = None
++
++    def endElement(self, name):
++        self.__current_node = self.__root
++        if name == "layoutList":
++            self.__layoutlist = False
++        elif name == "layout":
++            self.__layout = False
++        elif name == "variantList":
++            self.__variantlist = False
++        elif name == "variant":
++            self.__variant = False
++        elif name == "optionList":
++            self.__optionlist = False
++        elif name == "option":
++            self.__option = False
++        elif name == "group":
++            self.__group = False
++
++    def characters(self, text):
++        if self.__current_node == self.__root:
++            return
++        if self.__layoutlist:
++            self.__characters_layoutlist(text)
++        elif self.__optionlist:
++            self.__characters_optionlist(text)
++
++    def getLayoutList(self):
++        return self.__layoutlist_array
++
++    def getLayoutDesc(self):
++        return self.__layout_desc
++
++    def getLayoutLang(self):
++        return self.__layout_lang
++
++    def getVariantDesc(self):
++        return self.__variant_desc
++
++    def getOptionList(self):
++        return self.__optionlist_array
++
++    def getOptionGroupDesc(self):
++        return self.__option_group_desc
++
++    def getOptionDesc(self):
++        return self.__option_desc
++
++class XKBLayoutConfigHandler(XMLFilterBase):
++    def __init__(self,
++                 parser=None,
++                 downstream=None,
++                 preload_layouts=None,
++                 root='root'):
++        XMLFilterBase.__init__(self, parser)
++        self.__downstream = downstream
++        self.__preload_layouts = preload_layouts
++        self.__root = root
++        self.__current_node = root
++        self.__xkblayout = False 
++        self.__config = False 
++
++    def startDocument(self):
++        if self.__downstream != None:
++            self.__downstream.startDocument()
++
++    def endDocument(self):
++        if self.__downstream != None:
++            self.__downstream.endDocument()
++
++    def startElement(self, name, attrs):
++        self.__current_node = name
++        if name == "xkblayout":
++            self.__xkblayout = True
++        if name == "config":
++            self.__config = True
++        if self.__downstream != None:
++            self.__downstream.startElement(name, {})
++
++    def endElement(self, name):
++        self.__current_node = self.__root
++        if name == "xkblayout":
++            self.__xkblayout = False
++        if name == "config":
++            self.__config = False
++        if self.__downstream != None:
++            self.__downstream.endElement(name)
++
++    def characters(self, text):
++        if self.__current_node == self.__root:
++            return
++        if not self.__xkblayout or not self.__config:
++            return
++        if self.__current_node == "preload_layouts":
++            if self.__preload_layouts == None:
++                self.__preload_layouts = text.split(',')
++                self.__preload_layouts.sort()
++            if self.__downstream != None:
++                self.__downstream.characters(string.join(self.__preload_layouts,
++                                                         ','))
++
++    def getPreloadLayouts(self):
++        return self.__preload_layouts
++
++class XKBConfigRegistry():
++    def __init__(self, file_path=XKB_RULES_XML_FILE):
++        self.__handler = None
++        parser = sax.make_parser()
++        parser.setFeature(sax.handler.feature_namespaces, 0)
++        self.__handler = XKBConfigRegistryHandler(parser)
++        parser.setContentHandler(self.__handler)
++        f = file(file_path, 'r')
++        try:
++            parser.parse(f)
++        except SAXParseException:
++            print "ERROR: invalid file format", file_path
++        finally:
++            f.close()
++
++    def get_layout_list(self):
++        return self.__handler.getLayoutList()
++
++    def get_layout_desc(self):
++        return self.__handler.getLayoutDesc()
++
++    def get_layout_lang(self):
++        return self.__handler.getLayoutLang()
++
++    def get_variant_desc(self):
++        return self.__handler.getVariantDesc()
++
++    def get_option_list(self):
++        return self.__handler.getOptionList()
++
++    def get_option_group_desc(self):
++        return self.__handler.getOptionGroupDesc()
++
++    def get_option_desc(self):
++        return self.__handler.getOptionDesc()
++
++    @classmethod
++    def have_xkb(self):
++        return @HAVE_XKB@
++
++    @classmethod
++    def engine_desc_new(self,
++                        lang,
++                        layout,
++                        layout_desc=None,
++                        variant=None,
++                        variant_desc=None):
++        if variant_desc != None:
++            longname = variant_desc
++        elif layout != None and variant != None:
++            longname = layout + " - " + variant
++        elif layout_desc != None:
++            longname = layout_desc
++        else:
++            longname = layout
++        if variant != None:
++            name = "xkb:layout:" + layout + ":" + variant
++            desc = "XKB " + layout + "(" + variant + ") keyboard layout"
++            engine_layout = layout + "(" + variant + ")"
++        else:
++            name = "xkb:layout:" + layout
++            desc = "XKB " + layout + " keyboard layout"
++            engine_layout = layout
++
++        engine = enginedesc.EngineDesc(name, longname, desc, lang,
++                                       "LGPL2.1",
++                                       "Takao Fujiwara <takao.fujiwara1@gmail.com>",
++                                       "ibus-engine",
++                                       engine_layout)
++        return engine
++
++class XKBLayoutConfig():
++    def __init__(self,
++                 system_config="@datadir@/ibus/xkb/xkblayoutconfig.xml"):
++        self.__user_config = get_user_config_dir()
++        if self.__user_config == None:
++            self.__user_config = os.environ['HOME'] + "/.config"
++        self.__user_config = self.__user_config + \
++            "/ibus/xkb/xkblayoutconfig.xml"
++        self.__system_config = system_config
++        self.__filter_handler = None
++        self.__load()
++
++    def __load(self, downstream=None, preload_layouts=None):
++        parser = sax.make_parser()
++        parser.setFeature(sax.handler.feature_namespaces, 0)
++        self.__filter_handler = XKBLayoutConfigHandler(parser,
++                                                       downstream,
++                                                       preload_layouts)
++        parser.setContentHandler(self.__filter_handler)
++        f = None
++        if os.path.exists(self.__user_config):
++            f = file(self.__user_config)
++        elif os.path.exists(self.__system_config):
++            f = file(self.__system_config)
++        if f == None:
++            return
++        try:
++            parser.parse(f)
++        except SAXParseException:
++            print "ERROR: invalid file format", self.__user_config
++        finally:
++            f.close()
++
++    def get_preload_layouts(self):
++        return self.__filter_handler.getPreloadLayouts()
++
++    def save_preload_layouts(self, layouts):
++        if layouts == None:
++            if os.path.exists(self.__user_config):
++                os.unlink(self.__user_config)
++                return
++        parser = sax.make_parser()
++        parser.setFeature(sax.handler.feature_namespaces, 0)
++        result = StringIO()
++        downstream_handler = XMLGenerator(result, 'utf-8')
++        self.__load(downstream_handler, layouts)
++        contents = result.getvalue()
++        dir = os.path.dirname(self.__user_config)
++        if not os.path.exists(dir):
++            os.makedirs(dir, 0700)
++        f = open(self.__user_config, 'w')
++        f.write(contents)
++        f.close()
++        os.chmod(self.__user_config, 0600)
++
++def test():
++    xkbconfig = XKBConfigRegistry()
++    layout_list = xkbconfig.get_layout_list()
++    layout_desc = xkbconfig.get_layout_desc()
++    layout_lang = xkbconfig.get_layout_lang()
++    variant_desc = xkbconfig.get_variant_desc()
++    for layout in layout_list.keys():
++        if layout not in layout_lang:
++            print "layout name:", layout, "NO-LANG description:", layout_desc[layout]
++            continue
++        lang = layout_lang[layout]
++        print "layout name:", layout, "lang:", lang, "description:", layout_desc[layout]
++        for variant in layout_list[layout]:
++            label = "%s(%s)" % (layout, variant)
++            if label in layout_lang:
++                lang = layout_lang[label]
++            print "  variant name:", variant, "lang:", lang, "description:", variant_desc[variant]
++
++    option_list = xkbconfig.get_option_list()
++    option_group_desc = xkbconfig.get_option_group_desc()
++    option_desc = xkbconfig.get_option_desc()
++    for option_group in option_list.keys():
++        print "option group name:", option_group, "description:", option_group_desc[option_group]
++        for option in option_list[option_group]:
++            print "  option name:", option, "description:", option_desc[option]
++
++def test2():
++    xkblayoutconfig = XKBLayoutConfig("../xkb/xkblayoutconfig.xml")
++    list = xkblayoutconfig.get_preload_layouts()
++    print list
++    if list == None:
++        list = []
++    list.append("gb(test)")
++    list.sort()
++    #xkblayoutconfig.save_preload_layouts(list)
++
++if __name__ == "__main__":
++    test()
++    test2()
+diff --git a/setup/Makefile.am b/setup/Makefile.am
+index 9618d7f..48b1fed 100644
+--- a/setup/Makefile.am
++++ b/setup/Makefile.am
+@@ -28,6 +28,7 @@ ibussetup_PYTHON = \
+       enginetreeview.py \
+       engineabout.py \
+       keyboardshortcut.py \
++      xkbsetup.py \
+       $(NULL)
+ ibussetup_DATA = \
+diff --git a/setup/enginecombobox.py b/setup/enginecombobox.py
+index 2fd8876..7383177 100644
+--- a/setup/enginecombobox.py
++++ b/setup/enginecombobox.py
+@@ -43,6 +43,7 @@ class EngineComboBox(gtk.ComboBox):
+         self.connect("notify::active", self.__notify_active_cb)
+         self.__model = None
++        self.__title = _("Select an input method")
+         renderer = gtk.CellRendererPixbuf()
+         renderer.set_property("xalign", 0)
+@@ -117,7 +118,7 @@ class EngineComboBox(gtk.ComboBox):
+             renderer.set_property("weight", pango.WEIGHT_NORMAL)
+         elif isinstance(engine, int):
+             renderer.set_property("sensitive", True)
+-            renderer.set_property("text", _("Select an input method"))
++            renderer.set_property("text", self.__title)
+             renderer.set_property("weight", pango.WEIGHT_NORMAL)
+         else:
+             renderer.set_property("sensitive", True)
+@@ -140,5 +141,9 @@ class EngineComboBox(gtk.ComboBox):
+     def get_active_engine(self):
+         return self.get_property("active-engine")
++    def get_title(self):
++        return self.__title
++    def set_title(self, title):
++        self.__title = title
+diff --git a/setup/main.py b/setup/main.py
+index a22bb0c..7f4a040 100644
+--- a/setup/main.py
++++ b/setup/main.py
+@@ -37,6 +37,7 @@ from gtk import gdk
+ from enginecombobox import EngineComboBox
+ from enginetreeview import EngineTreeView
+ from engineabout import EngineAbout
++from xkbsetup import XKBSetup
+ from i18n import DOMAINNAME, _, N_, init as i18n_init
+ (
+@@ -241,6 +242,8 @@ class Setup(object):
+         self.__combobox.connect("notify::active-engine", self.__combobox_notify_active_engine_cb)
+         self.__treeview.connect("notify", self.__treeview_notify_cb)
++        XKBSetup(self.__config, self.__builder)
++
+     def __combobox_notify_active_engine_cb(self, combobox, property):
+         engine = self.__combobox.get_active_engine()
+         button = self.__builder.get_object("button_engine_add")
+diff --git a/setup/setup.ui b/setup/setup.ui
+index 0a69df8..f1e6d0b 100644
+--- a/setup/setup.ui
++++ b/setup/setup.ui
+@@ -117,7 +117,6 @@
+                                 <child>
+                                   <object class="GtkLabel" id="label9">
+                                     <property name="visible">True</property>
+-                                    <property name="sensitive">False</property>
+                                     <property name="tooltip_text" translatable="yes">The shortcut keys for switching to previous input method in the list</property>
+                                     <property name="xalign">0</property>
+                                     <property name="label" translatable="yes">Previous input method:</property>
+@@ -204,7 +203,6 @@
+                                     <child>
+                                       <object class="GtkEntry" id="entry_prev_engine">
+                                         <property name="visible">True</property>
+-                                        <property name="sensitive">False</property>
+                                         <property name="can_focus">True</property>
+                                         <property name="editable">False</property>
+                                       </object>
+@@ -216,7 +214,6 @@
+                                       <object class="GtkButton" id="button_prev_engine">
+                                         <property name="label" translatable="yes">...</property>
+                                         <property name="visible">True</property>
+-                                        <property name="sensitive">False</property>
+                                         <property name="can_focus">True</property>
+                                         <property name="receives_default">False</property>
+                                         <property name="use_underline">True</property>
+@@ -825,6 +822,7 @@ You may use up/down buttons to change it.&lt;/i&gt;&lt;/small&gt;</property>
+                                     <property name="visible">True</property>
+                                     <property name="orientation">vertical</property>
+                                     <property name="spacing">6</property>
++                                    <property name="no_show_all">True</property>
+                                     <child>
+                                       <object class="GtkCheckButton" id="checkbutton_use_sys_layout">
+                                         <property name="label" translatable="yes">Use system keyboard layout</property>
+@@ -840,6 +838,57 @@ You may use up/down buttons to change it.&lt;/i&gt;&lt;/small&gt;</property>
+                                         <property name="position">0</property>
+                                       </packing>
+                                     </child>
++                                    <child>
++                                      <object class="GtkHBox" id="hbox_system_keyboard_layout">
++                                        <property name="visible">True</property>
++                                        <property name="spacing">6</property>
++                                        <child>
++                                          <object class="GtkLabel" id="label20">
++                                            <property name="visible">True</property>
++                                            <property name="label" translatable="yes">System Keyboard Layout:</property>
++                                            <property name="use_markup">True</property>
++                                            <property name="justify">center</property>
++                                          </object>
++                                          <packing>
++                                            <property name="expand">False</property>
++                                            <property name="fill">False</property>
++                                            <property name="position">0</property>
++                                          </packing>
++                                        </child>
++                                        <child>
++                                          <object class="GtkButton" id="button_system_keyboard_layout">
++                                            <property name="label"></property>
++                                            <property name="visible">True</property>
++                                            <property name="can_focus">True</property>
++                                            <property name="receives_default">False</property>
++                                          </object>
++                                          <packing>
++                                            <property name="expand">False</property>
++                                            <property name="fill">False</property>
++                                            <property name="position">1</property>
++                                          </packing>
++                                        </child>
++                                      </object>
++                                      <packing>
++                                        <property name="expand">False</property>
++                                        <property name="fill">False</property>
++                                        <property name="position">1</property>
++                                      </packing>
++                                    </child>
++                                    <child>
++                                      <object class="GtkButton" id="button_config_layouts">
++                                        <property name="label">Add or remove layouts in 'Select an input method' list</property>
++                                        <property name="visible">True</property>
++                                        <property name="can_focus">True</property>
++                                        <property name="receives_default">False</property>
++                                        <property name="tooltip_text" translatable="yes">Add or remove keyboard layouts in all input method engnines</property>
++                                      </object>
++                                      <packing>
++                                        <property name="expand">False</property>
++                                        <property name="fill">False</property>
++                                        <property name="position">2</property>
++                                      </packing>
++                                    </child>
+                                   </object>
+                                 </child>
+                               </object>
+@@ -1038,4 +1087,558 @@ Homepage: http://code.google.com/p/ibus
+       </object>
+     </child>
+   </object>
++  <object class="GtkDialog" id="dialog_config_layouts">
++    <property name="title" translatable="yes">Add or Remove Layouts</property>
++    <property name="icon_name">ibus-setup</property>
++    <child internal-child="vbox">
++      <object class="GtkVBox" id="vbox101">
++        <property name="orientation">vertical</property>
++        <property name="visible">True</property>
++        <property name="border-width">10</property>
++        <property name="spacing">12</property>
++        <child>
++          <object class="GtkAlignment" id="alignment101">
++            <property name="visible">True</property>
++            <property name="top_padding">12</property>
++            <property name="left_padding">12</property>
++            <child>
++              <object class="GtkFrame" id="frame101">
++                <property name="visible">True</property>
++                <property name="label_xalign">0</property>
++                <property name="shadow_type">none</property>
++                <child>
++                  <object class="GtkAlignment" id="alignment102">
++                    <property name="visible">True</property>
++                    <property name="top_padding">12</property>
++                    <property name="left_padding">12</property>
++                    <child>
++                      <object class="GtkVBox" id="vbox102">
++                        <property name="visible">True</property>
++                        <property name="orientation">vertical</property>
++                        <property name="spacing">6</property>
++                        <child>
++                          <object class="GtkScrolledWindow" id="scrolledwindow101">
++                            <property name="visible">True</property>
++                            <property name="can_focus">True</property>
++                            <property name="border_width">5</property>
++                            <property name="width_request">450</property>
++                            <property name="height_request">350</property>
++                            <property name="hscrollbar_policy">automatic</property>
++                            <property name="vscrollbar_policy">automatic</property>
++                            <property name="shadow_type">out</property>
++                            <child>
++                              <object class="GtkViewport" id="viewport101">
++                                <property name="visible">True</property>
++                                <property name="shadow_type">none</property>
++                                <child>
++                                  <object class="GtkVBox" id="vbox_all_keyboard_layouts">
++                                    <property name="visible">True</property>
++                                    <property name="orientation">vertical</property>
++                                  </object>
++                                </child>
++                              </object>
++                            </child>
++                          </object>
++                          <packing>
++                            <property name="position">0</property>
++                          </packing>
++                        </child>
++                      </object>
++                    </child>
++                  </object>
++                </child>
++                <child type="label">
++                  <object class="GtkLabel" id="label101">
++                    <property name="visible">True</property>
++                    <property name="label" translatable="yes">&lt;b&gt;Keyboard Layout&lt;/b&gt;</property>
++                    <property name="use_markup">True</property>
++                  </object>
++                </child>
++              </object>
++            </child>
++          </object>
++          <packing>
++            <property name="expand">False</property>
++            <property name="position">0</property>
++          </packing>
++        </child>
++        <child internal-child="action_area">
++          <object class="GtkHButtonBox" id="hbuttonbox101">
++            <property name="visible">True</property>
++            <property name="homogeneous">True</property>
++            <property name="layout_style">end</property>
++            <child>
++              <object class="GtkButton" id="button_config_layouts_cancel">
++                <property name="label">gtk-cancel</property>
++                <property name="visible">True</property>
++                <property name="use_stock">True</property>
++              </object>
++              <packing>
++                <property name="expand">False</property>
++                <property name="fill">False</property>
++                <property name="position">0</property>
++              </packing>
++            </child>
++            <child>
++              <object class="GtkButton" id="button_config_layouts_ok">
++                <property name="label">gtk-ok</property>
++                <property name="visible">True</property>
++                <property name="use_stock">True</property>
++                <property name="receives_default">True</property>
++              </object>
++              <packing>
++                <property name="expand">False</property>
++                <property name="fill">False</property>
++                <property name="position">1</property>
++              </packing>
++            </child>
++          </object>
++          <packing>
++            <property name="expand">False</property>
++            <property name="fill">False</property>
++            <property name="pack_type">end</property>
++            <property name="position">1</property>
++          </packing>
++        </child>
++      </object>
++    </child>
++  </object>
++  <object class="GtkDialog" id="dialog_system_keyboard_layout">
++    <property name="title" translatable="yes">System Keyboard Layout Setup</property>
++    <property name="icon_name">ibus-setup</property>
++    <child internal-child="vbox">
++      <object class="GtkVBox" id="vbox201">
++        <property name="orientation">vertical</property>
++        <property name="visible">True</property>
++        <property name="border-width">10</property>
++        <property name="spacing">12</property>
++        <child>
++          <object class="GtkAlignment" id="alignment201">
++            <property name="visible">True</property>
++            <property name="top_padding">12</property>
++            <property name="left_padding">12</property>
++            <child>
++              <object class="GtkFrame" id="frame201">
++                <property name="visible">True</property>
++                <property name="label_xalign">0</property>
++                <property name="shadow_type">none</property>
++                <child>
++                  <object class="GtkAlignment" id="alignment202">
++                    <property name="visible">True</property>
++                    <property name="top_padding">12</property>
++                    <property name="left_padding">12</property>
++                    <child>
++                      <object class="GtkVBox" id="vbox202">
++                        <property name="visible">True</property>
++                        <property name="orientation">vertical</property>
++                        <property name="spacing">6</property>
++                        <child>
++                          <object class="GtkHBox" id="hbox201">
++                            <property name="visible">True</property>
++                            <child>
++                              <object class="GtkVBox" id="vbox203">
++                                <property name="visible">True</property>
++                                <property name="orientation">vertical</property>
++                                <property name="spacing">6</property>
++                                <child>
++                                  <object class="EngineComboBox" id="combobox_system_keyboard_layout_engines">
++                                    <property name="visible">True</property>
++                                  </object>
++                                  <packing>
++                                    <property name="expand">False</property>
++                                    <property name="position">0</property>
++                                  </packing>
++                                </child>
++                                <child>
++                                  <object class="GtkScrolledWindow" id="scrolledwindow201">
++                                    <property name="height_request">150</property>
++                                    <property name="visible">True</property>
++                                    <property name="can_focus">True</property>
++                                    <property name="hscrollbar_policy">automatic</property>
++                                    <property name="vscrollbar_policy">automatic</property>
++                                    <property name="shadow_type">in</property>
++                                    <child>
++                                      <object class="EngineTreeView" id="treeview_system_keyboard_layout_engines">
++                                        <property name="visible">True</property>
++                                        <property name="can_focus">True</property>
++                                        <property name="height_request">150</property>
++                                      </object>
++                                    </child>
++                                  </object>
++                                  <packing>
++                                    <property name="expand">False</property>
++                                    <property name="position">1</property>
++                                  </packing>
++                                </child>
++                              </object>
++                              <packing>
++                                <property name="position">0</property>
++                              </packing>
++                            </child>
++                            <child>
++                              <object class="GtkVButtonBox" id="vbuttonbox201">
++                                <property name="visible">True</property>
++                                <property name="orientation">vertical</property>
++                                <property name="spacing">5</property>
++                                <property name="layout_style">start</property>
++                                <child>
++                                  <object class="GtkButton" id="button_system_keyboard_layout_engine_add">
++                                    <property name="label">gtk-add</property>
++                                    <property name="visible">True</property>
++                                    <property name="sensitive">False</property>
++                                    <property name="can_focus">True</property>
++                                    <property name="receives_default">True</property>
++                                    <property name="tooltip_text" translatable="yes">Add the selected keyboard layout into the system keyboard layouts</property>
++                                    <property name="use_stock">True</property>
++                                  </object>
++                                  <packing>
++                                    <property name="expand">False</property>
++                                    <property name="fill">False</property>
++                                    <property name="position">0</property>
++                                  </packing>
++                                </child>
++                                <child>
++                                  <object class="GtkButton" id="button_system_keyboard_layout_engine_remove">
++                                    <property name="label">gtk-remove</property>
++                                    <property name="visible">True</property>
++                                    <property name="sensitive">False</property>
++                                    <property name="can_focus">True</property>
++                                    <property name="receives_default">True</property>
++                                    <property name="tooltip_text" translatable="yes">Remove the selected keyboard layout from the system keyboard layouts</property>
++                                    <property name="use_stock">True</property>
++                                  </object>
++                                  <packing>
++                                    <property name="expand">False</property>
++                                    <property name="fill">False</property>
++                                    <property name="position">1</property>
++                                  </packing>
++                                </child>
++                                <child>
++                                  <object class="GtkButton" id="button_system_keyboard_layout_engine_up">
++                                    <property name="label">gtk-go-up</property>
++                                    <property name="visible">True</property>
++                                    <property name="sensitive">False</property>
++                                    <property name="can_focus">True</property>
++                                    <property name="receives_default">True</property>
++                                    <property name="tooltip_text" translatable="yes">Move up the selected keyboard layout in the system keyboard layouts list</property>
++                                    <property name="use_stock">True</property>
++                                  </object>
++                                  <packing>
++                                    <property name="expand">False</property>
++                                    <property name="fill">False</property>
++                                    <property name="position">2</property>
++                                  </packing>
++                                </child>
++                                <child>
++                                  <object class="GtkButton" id="button_system_keyboard_layout_engine_down">
++                                    <property name="label">gtk-go-down</property>
++                                    <property name="visible">True</property>
++                                    <property name="sensitive">False</property>
++                                    <property name="can_focus">True</property>
++                                    <property name="receives_default">True</property>
++                                    <property name="tooltip_text" translatable="yes">Move down the selected keyboard layout in the system keyboard layouts list</property>
++                                    <property name="use_stock">True</property>
++                                  </object>
++                                  <packing>
++                                    <property name="expand">False</property>
++                                    <property name="fill">False</property>
++                                    <property name="position">3</property>
++                                  </packing>
++                                </child>
++                                <child>
++                                  <object class="GtkButton" id="button_system_keyboard_layout_engine_reset">
++                                    <property name="label" translatable="yes">Rese_t</property>
++                                    <property name="visible">True</property>
++                                    <property name="sensitive">False</property>
++                                    <property name="can_focus">True</property>
++                                    <property name="receives_default">True</property>
++                                    <property name="tooltip_text" translatable="yes">Reset the system keyboard layouts list</property>
++                                    <property name="use_underline">True</property>
++                                  </object>
++                                  <packing>
++                                    <property name="expand">False</property>
++                                    <property name="fill">False</property>
++                                    <property name="position">4</property>
++                                  </packing>
++                                </child>
++                              </object>
++                              <packing>
++                                <property name="position">1</property>
++                              </packing>
++                            </child>
++                          </object>
++                          <packing>
++                            <property name="position">0</property>
++                          </packing>
++                        </child>
++                        <child>
++                          <object class="GtkHBox" id="hbox202">
++                            <property name="visible">True</property>
++                            <property name="spacing">6</property>
++                            <child>
++                              <object class="GtkImage" id="image201">
++                                <property name="visible">True</property>
++                                <property name="stock">gtk-info</property>
++                                <property name="icon-size">2</property>
++                              </object>
++                              <packing>
++                                <property name="expand">False</property>
++                                <property name="position">0</property>
++                              </packing>
++                            </child>
++                            <child>
++                              <object class="GtkLabel" id="label_system_keyboard_layout_engines">
++                                <property name="visible">True</property>
++                                <property name="xalign">0</property>
++                                <property name="use_markup">True</property>
++                              </object>
++                              <packing>
++                                <property name="position">1</property>
++                              </packing>
++                            </child>
++                          </object>
++                          <packing>
++                            <property name="position">1</property>
++                          </packing>
++                        </child>
++                      </object>
++                    </child>
++                  </object>
++                </child>
++                <child type="label">
++                  <object class="GtkLabel" id="label201">
++                    <property name="visible">True</property>
++                    <property name="label" translatable="yes">&lt;b&gt;Keyboard Layout&lt;/b&gt;</property>
++                    <property name="use_markup">True</property>
++                  </object>
++                </child>
++              </object>
++            </child>
++          </object>
++          <packing>
++            <property name="expand">False</property>
++            <property name="position">0</property>
++          </packing>
++        </child>
++        <child>
++          <object class="GtkAlignment" id="alignment204">
++            <property name="visible">True</property>
++            <property name="top_padding">12</property>
++            <property name="left_padding">12</property>
++            <child>
++              <object class="GtkFrame" id="frame202">
++                <property name="visible">True</property>
++                <property name="label_xalign">0</property>
++                <property name="shadow_type">none</property>
++                <child>
++                  <object class="GtkAlignment" id="alignment205">
++                    <property name="visible">True</property>
++                    <property name="top_padding">12</property>
++                    <property name="left_padding">12</property>
++                    <child>
++                      <object class="GtkVBox" id="vbox204">
++                        <property name="visible">True</property>
++                        <property name="orientation">vertical</property>
++                        <property name="spacing">6</property>
++                        <child>
++                          <object class="GtkHButtonBox" id="hbuttonbox201">
++                            <property name="visible">True</property>
++                            <property name="homogeneous">True</property>
++                            <property name="layout_style">start</property>
++                            <child>
++                              <object class="GtkButton" id="button_system_keyboard_option_setup">
++                                <property name="visible">True</property>
++                                <property name="label" translatable="yes">_Options...</property>
++                                <property name="can_focus">True</property>
++                                <property name="use_underline">True</property>
++                              </object>
++                              <packing>
++                                <property name="expand">False</property>
++                                <property name="fill">False</property>
++                                <property name="position">0</property>
++                              </packing>
++                            </child>
++                          </object>
++                          <packing>
++                            <property name="expand">False</property>
++                            <property name="fill">False</property>
++                            <property name="position">0</property>
++                          </packing>
++                        </child>
++                      </object>
++                    </child>
++                  </object>
++                </child>
++                <child type="label">
++                  <object class="GtkLabel" id="label202">
++                    <property name="visible">True</property>
++                    <property name="label" translatable="yes">&lt;b&gt;Keyboard Option&lt;/b&gt;</property>
++                    <property name="use_markup">True</property>
++                  </object>
++                </child>
++              </object>
++            </child>
++          </object>
++          <packing>
++            <property name="expand">False</property>
++            <property name="position">1</property>
++          </packing>
++        </child>
++        <child internal-child="action_area">
++          <object class="GtkHButtonBox" id="hbuttonbox202">
++            <property name="visible">True</property>
++            <property name="homogeneous">True</property>
++            <property name="layout_style">end</property>
++            <child>
++              <object class="GtkButton" id="button_system_keyboard_layout_cancel">
++                <property name="label">gtk-cancel</property>
++                <property name="visible">True</property>
++                <property name="use_stock">True</property>
++              </object>
++              <packing>
++                <property name="expand">False</property>
++                <property name="fill">False</property>
++                <property name="position">0</property>
++              </packing>
++            </child>
++            <child>
++              <object class="GtkButton" id="button_system_keyboard_layout_ok">
++                <property name="label">gtk-ok</property>
++                <property name="visible">True</property>
++                <property name="use_stock">True</property>
++                <property name="receives_default">True</property>
++              </object>
++              <packing>
++                <property name="expand">False</property>
++                <property name="fill">False</property>
++                <property name="position">1</property>
++              </packing>
++            </child>
++          </object>
++          <packing>
++            <property name="expand">False</property>
++            <property name="fill">False</property>
++            <property name="pack_type">end</property>
++            <property name="position">1</property>
++          </packing>
++        </child>
++      </object>
++    </child>
++  </object>
++  <object class="GtkDialog" id="dialog_system_keyboard_option">
++    <property name="title" translatable="yes">System Keyboard Option Setup</property>
++    <property name="icon_name">ibus-setup</property>
++    <child internal-child="vbox">
++      <object class="GtkVBox" id="vbox301">
++        <property name="orientation">vertical</property>
++        <property name="visible">True</property>
++        <property name="border-width">10</property>
++        <property name="spacing">12</property>
++        <child>
++          <object class="GtkAlignment" id="alignment301">
++            <property name="visible">True</property>
++            <property name="top_padding">12</property>
++            <property name="left_padding">12</property>
++            <child>
++              <object class="GtkFrame" id="frame301">
++                <property name="visible">True</property>
++                <property name="label_xalign">0</property>
++                <property name="shadow_type">none</property>
++                <child>
++                  <object class="GtkAlignment" id="alignment302">
++                    <property name="visible">True</property>
++                    <property name="top_padding">12</property>
++                    <property name="left_padding">12</property>
++                    <child>
++                      <object class="GtkVBox" id="vbox302">
++                        <property name="visible">True</property>
++                        <property name="orientation">vertical</property>
++                        <property name="spacing">6</property>
++                        <child>
++                          <object class="GtkCheckButton" id="checkbutton_use_system_keyboard_option">
++                            <property name="label" translatable="yes">Use the default keyboard option</property>
++                            <property name="visible">True</property>
++                            <property name="can_focus">True</property>
++                            <property name="receives_default">False</property>
++                            <property name="tooltip_text" translatable="yes">Use the defualt XKB option</property>
++                            <property name="draw_indicator">True</property>
++                          </object>
++                          <packing>
++                            <property name="position">0</property>
++                          </packing>
++                        </child>
++                        <child>
++                          <object class="GtkScrolledWindow" id="scrolledwindow301">
++                            <property name="visible">True</property>
++                            <property name="can_focus">True</property>
++                            <property name="border_width">5</property>
++                            <property name="width_request">450</property>
++                            <property name="height_request">350</property>
++                            <property name="hscrollbar_policy">automatic</property>
++                            <property name="vscrollbar_policy">automatic</property>
++                            <property name="shadow_type">out</property>
++                            <child>
++                              <object class="GtkViewport" id="viewport301">
++                                <property name="visible">True</property>
++                                <property name="shadow_type">none</property>
++                                <child>
++                                  <object class="GtkVBox" id="vbox_system_keyboard_options">
++                                    <property name="visible">True</property>
++                                    <property name="orientation">vertical</property>
++                                  </object>
++                                </child>
++                              </object>
++                            </child>
++                          </object>
++                          <packing>
++                            <property name="position">1</property>
++                          </packing>
++                        </child>
++                      </object>
++                    </child>
++                  </object>
++                </child>
++                <child type="label">
++                  <object class="GtkLabel" id="label301">
++                    <property name="visible">True</property>
++                    <property name="label" translatable="yes">&lt;b&gt;Keyboard Option&lt;/b&gt;</property>
++                    <property name="use_markup">True</property>
++                  </object>
++                </child>
++              </object>
++            </child>
++          </object>
++          <packing>
++            <property name="expand">False</property>
++            <property name="position">0</property>
++          </packing>
++        </child>
++        <child internal-child="action_area">
++          <object class="GtkHButtonBox" id="hbuttonbox301">
++            <property name="visible">True</property>
++            <property name="homogeneous">True</property>
++            <property name="layout_style">end</property>
++            <child>
++              <object class="GtkButton" id="button_system_keyboard_option_close">
++                <property name="label">gtk-close</property>
++                <property name="visible">True</property>
++                <property name="use_stock">True</property>
++              </object>
++              <packing>
++                <property name="expand">False</property>
++                <property name="fill">False</property>
++                <property name="position">0</property>
++              </packing>
++            </child>
++          </object>
++          <packing>
++            <property name="expand">False</property>
++            <property name="fill">False</property>
++            <property name="pack_type">end</property>
++            <property name="position">1</property>
++          </packing>
++        </child>
++      </object>
++    </child>
++  </object>
+ </interface>
+diff --git a/setup/xkbsetup.py b/setup/xkbsetup.py
+new file mode 100644
+index 0000000..1685bff
+--- /dev/null
++++ b/setup/xkbsetup.py
+@@ -0,0 +1,454 @@
++# vim:set et sts=4 sw=4:
++#
++# ibus - The Input Bus
++#
++# Copyright (c) 2011 Takao Fujiwara <takao.fujiwara1@gmail.com>
++# Copyright (c) 2011 Peng Huang <shawn.p.huang@gmail.com>
++# Copyright (c) 2011 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 program; if not, write to the
++# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++# Boston, MA  02111-1307  USA
++
++import gettext
++import gobject
++import gtk
++import ibus
++
++_ = lambda a : gettext.dgettext("ibus", a)
++XKB_MAX_LAYOUTS = 4
++
++class XKBSetup(gobject.GObject):
++    def __init__(self, config, builder):
++        super(XKBSetup, self).__init__()
++
++        self.__config = config
++        self.__builder = builder
++
++        # system keyboard layout setting
++        self.__button_system_keyboard_layout = self.__builder.get_object("button_system_keyboard_layout")
++        text = str(self.__config.get_value("general", "system_keyboard_layout", ''))
++        if text == 'default' or text == '':
++            text = _("Default")
++        self.__button_system_keyboard_layout.set_label(text)
++        if not self.__config.get_value("general", "use_system_keyboard_layout", True):
++            self.__button_system_keyboard_layout.set_sensitive(False)
++        self.__button_system_keyboard_layout.connect("clicked", self.__button_system_keyboard_layout_cb)
++
++        # use system keyboard layout setting
++        button = self.__builder.get_object("checkbutton_use_sys_layout")
++        button.connect("toggled", lambda button: self.__button_system_keyboard_layout.set_sensitive(button.get_active()))
++
++        self.__xkblayoutconfig = None
++        self.__preload_xkb_engines = []
++        self.__other_xkb_engines = []
++        self.__default_xkb_engine = None
++        if ibus.XKBConfigRegistry.have_xkb():
++            self.__xkblayoutconfig = ibus.XKBLayoutConfig()
++
++        # config layouts dialog
++        self.__init_config_layouts()
++
++        # default system keyboard dialog
++        self.__init_system_keyboard()
++
++    def __get_xkb_engines(self):
++        xkb_engines = []
++        xkbconfig = ibus.XKBConfigRegistry()
++        layout_list = xkbconfig.get_layout_list()
++        layout_desc = xkbconfig.get_layout_desc()
++        layout_lang = xkbconfig.get_layout_lang()
++        variant_desc = xkbconfig.get_variant_desc()
++        for layout in layout_list.keys():
++            langs = []
++            if layout in layout_lang:
++                langs = layout_lang[layout]
++            for lang in langs:
++                engine = ibus.XKBConfigRegistry.engine_desc_new(
++                    lang,
++                    layout,
++                    layout_desc[layout],
++                    None,
++                    None)
++                xkb_engines.append(engine)
++            for variant in layout_list[layout]:
++                label = "%s(%s)" % (layout, variant)
++                sub_langs = []
++                if label in layout_lang:
++                    sub_langs = layout_lang[label]
++                else:
++                    sub_langs = langs
++                for lang in sub_langs:
++                    engine = ibus.XKBConfigRegistry.engine_desc_new(
++                        lang,
++                        layout,
++                        layout_desc[layout],
++                        variant,
++                        variant_desc[label])
++                    xkb_engines.append(engine)
++        return xkb_engines
++
++    def __get_default_xkb_engine(self):
++        if self.__default_xkb_engine != None:
++            return self.__default_xkb_engine
++        self.__default_xkb_engine = ibus.XKBConfigRegistry.engine_desc_new(
++            "other",
++            "default",
++            _("Default"),
++            None,
++            None)
++        return self.__default_xkb_engine
++
++    def __init_config_layouts(self):
++        if not ibus.XKBConfigRegistry.have_xkb():
++            button = self.__builder.get_object("button_config_layouts")
++            button.hide()
++            return
++
++        self.__dialog_config_layouts = self.__builder.get_object("dialog_config_layouts")
++        self.__button_config_layouts_cancel = self.__builder.get_object("button_config_layouts_cancel")
++        self.__button_config_layouts_cancel.connect("clicked", self.__button_config_layouts_cancel_cb)
++        self.__button_config_layouts_ok = self.__builder.get_object("button_config_layouts_ok")
++        self.__button_config_layouts_ok.connect("clicked", self.__button_config_layouts_ok_cb)
++        self.__vbox_all_keyboard_layouts = self.__builder.get_object("vbox_all_keyboard_layouts")
++
++        xkb_engines = self.__get_xkb_engines()
++        if len(xkb_engines) > 0:
++            button = self.__builder.get_object("button_config_layouts")
++            button.connect("clicked", self.__button_config_layouts_cb)
++            button.set_sensitive(True)
++
++        engine_dict = {}
++        for engine in xkb_engines:
++            if not engine.name.startswith("xkb:layout:"):
++                continue
++            lang = ibus.get_language_name(engine.language)
++            if lang not in engine_dict:
++                engine_dict[lang] = []
++            engine_dict[lang].append(engine)
++
++        keys = engine_dict.keys()
++        keys.sort()
++        if ibus.get_language_name("Other") in keys:
++            keys.remove(ibus.get_language_name("Other"))
++            keys += [ibus.get_language_name("Other")]
++
++        preload_xkb_engines = self.__xkblayoutconfig.get_preload_layouts()
++        for lang in keys:
++            expander = gtk.Expander("")
++            self.__vbox_all_keyboard_layouts.pack_start(expander, True, True, 0)
++            expander.show()
++            label = expander.get_label_widget()
++            label.set_label(lang)
++            align = gtk.Alignment(0, 0, 1, 0)
++            align.set_padding(6, 0, 18, 0)
++            expander.add(align)
++            align.show()
++            vbox = gtk.VBox(False, 0)
++            align.add(vbox)
++            vbox.show()
++
++            def cmp_engine(a, b):
++                if a.rank == b.rank:
++                    return cmp(a.longname, b.longname)
++                return int(b.rank - a.rank)
++            engine_dict[lang].sort(cmp_engine)
++
++            for engine in engine_dict[lang]:
++                sub_name = engine.name[len("xkb:layout:"):]
++                layout_list = sub_name.split(':')
++                if len(layout_list) > 1:
++                    layout = "%s(%s)" % (layout_list[0], layout_list[1])
++                else:
++                    layout = layout_list[0]
++                has_preloaded = False
++                for preload_name in preload_xkb_engines:
++                    preload_name = str(preload_name)
++                    if len(preload_name) == 0:
++                        continue
++                    if layout == preload_name:
++                        has_preloaded = True
++                        break
++
++                checkbutton = gtk.CheckButton(engine.longname)
++                checkbutton.set_data("layout", layout)
++                if has_preloaded:
++                    checkbutton.set_active(True)
++                vbox.pack_start(checkbutton, False, True, 0)
++                checkbutton.show()
++
++    def __init_system_keyboard_layout(self):
++        self.__dialog_system_keyboard_layout = self.__builder.get_object("dialog_system_keyboard_layout")
++        self.__button_system_keyboard_layout_cancel = self.__builder.get_object("button_system_keyboard_layout_cancel")
++        self.__button_system_keyboard_layout_cancel.connect("clicked", self.__button_system_keyboard_layout_cancel_cb)
++        self.__button_system_keyboard_layout_ok = self.__builder.get_object("button_system_keyboard_layout_ok")
++        self.__button_system_keyboard_layout_ok.connect("clicked", self.__button_system_keyboard_layout_ok_cb)
++
++        # get xkb layouts
++        xkb_engines = self.__get_xkb_engines()
++
++        self.__combobox_system_keyboard_layout = self.__builder.get_object("combobox_system_keyboard_layout_engines")
++        self.__combobox_system_keyboard_layout.set_engines(xkb_engines)
++        self.__combobox_system_keyboard_layout.set_title(_("Select keyboard layouts"))
++        self.__combobox_system_keyboard_layout.connect("notify::active-engine", self.__combobox_notify_active_system_keyboard_layout_cb)
++        self.__treeview_system_keyboard_layout = self.__builder.get_object("treeview_system_keyboard_layout_engines")
++        self.__treeview_system_keyboard_layout.connect("notify", self.__treeview_notify_system_keyboard_layout_cb)
++        column = self.__treeview_system_keyboard_layout.get_column(0)
++        column.set_title(_("Keyboard Layouts"))
++        button = self.__builder.get_object("button_system_keyboard_layout_engine_add")
++        button.connect("clicked", self.__button_system_keyboard_layout_add_cb)
++        button = self.__builder.get_object("button_system_keyboard_layout_engine_remove")
++        button.connect("clicked", self.__button_system_keyboard_layout_remove_cb)
++        button = self.__builder.get_object("button_system_keyboard_layout_engine_up")
++        button.connect("clicked", lambda *args:self.__treeview_system_keyboard_layout.move_up_engine())
++
++        button = self.__builder.get_object("button_system_keyboard_layout_engine_down")
++        button.connect("clicked", lambda *args:self.__treeview_system_keyboard_layout.move_down_engine())
++        button = self.__builder.get_object("button_system_keyboard_layout_engine_reset")
++        button.connect("clicked", self.__button_system_keyboard_layout_reset_cb)
++        button_reset = button
++        text = str(self.__config.get_value("general", "system_keyboard_layout", ''))
++        if text == "default" or text == None:
++            engine = self.__get_default_xkb_engine()
++            self.__treeview_system_keyboard_layout.set_engines([engine])
++            button_reset.set_sensitive(False)
++        else:
++            for layout in text.split(','):
++                layout_engine = None
++                for engine in xkb_engines:
++                    if layout == engine.layout:
++                        layout_engine = engine
++                        break
++                if layout_engine != None:
++                    self.__treeview_system_keyboard_layout.append_engine(layout_engine)
++            button_reset.set_sensitive(True)
++        label = self.__builder.get_object("label_system_keyboard_layout_engines")
++        label.set_markup(_("<small><i>The system keyboard layouts "
++                           "can be set less than or equal to %d.\n"
++                           "You may use Up/Down buttons to change the order."
++                           "</i></small>") % XKB_MAX_LAYOUTS)
++
++    def __init_system_keyboard_option(self):
++        self.__dialog_system_keyboard_option = self.__builder.get_object("dialog_system_keyboard_option")
++        self.__button_system_keyboard_option_close = self.__builder.get_object("button_system_keyboard_option_close")
++        self.__button_system_keyboard_option_close.connect(
++            "clicked", lambda button: self.__dialog_system_keyboard_option.hide())
++
++        button = self.__builder.get_object("button_system_keyboard_option_setup")
++        button.connect("clicked", self.__button_system_keyboard_option_cb)
++        self.__checkbutton_use_system_keyboard_option = self.__builder.get_object("checkbutton_use_system_keyboard_option")
++        self.__vbox_system_keyboard_options = self.__builder.get_object("vbox_system_keyboard_options")
++        option_array = []
++        text = str(self.__config.get_value("general", "system_keyboard_option", ''))
++        if text == None or text == "default":
++            self.__checkbutton_use_system_keyboard_option.set_active(True)
++            self.__vbox_system_keyboard_options.set_sensitive(False)
++        else:
++            self.__checkbutton_use_system_keyboard_option.set_active(False)
++            self.__vbox_system_keyboard_options.set_sensitive(True)
++            option_array = text.split(',')
++        self.__checkbutton_use_system_keyboard_option.connect(
++            "toggled", lambda button: self.__vbox_system_keyboard_options.set_sensitive(not button.get_active()))
++
++        xkbconfig = ibus.XKBConfigRegistry()
++        option_list = xkbconfig.get_option_list()
++        option_group_desc = xkbconfig.get_option_group_desc()
++        option_desc = xkbconfig.get_option_desc()
++        for option_group in option_list.keys():
++            expander = gtk.Expander("")
++            self.__vbox_system_keyboard_options.pack_start(expander, True, True, 0)
++            expander.show()
++            checked = 0
++            label = expander.get_label_widget()
++            label.set_label(option_group_desc[option_group])
++            label.set_data("option_group", option_group)
++            expander.set_data("checked", checked)
++            align = gtk.Alignment(0, 0, 1, 0)
++            align.set_padding(6, 0, 18, 0)
++            expander.add(align)
++            align.show()
++            vbox = gtk.VBox(False, 0)
++            align.add(vbox)
++            vbox.show()
++            for option in option_list[option_group]:
++                checkbutton = gtk.CheckButton(option_desc[option])
++                checkbutton.set_data("option", option)
++                if option in option_array:
++                    checkbutton.set_active(True)
++                    label.set_markup("<b>" +
++                                     option_group_desc[option_group] +
++                                     "</b>")
++                    checked = checked + 1
++                    expander.set_data("checked", checked)
++                checkbutton.connect("toggled",
++                                    self.__checkbutton_system_keyboard_option_toggled_cb,
++                                    expander)
++                vbox.pack_start(checkbutton, False, True, 0)
++                checkbutton.show()
++
++    def __init_system_keyboard(self):
++        if not ibus.XKBConfigRegistry.have_xkb():
++            hbox = self.__builder.get_object("hbox_system_keyboard_layout")
++            hbox.hide()
++            return
++
++        self.__init_system_keyboard_layout()
++        self.__init_system_keyboard_option()
++
++    def __combobox_notify_active_system_keyboard_layout_cb(self, combobox, property):
++        engine = self.__combobox_system_keyboard_layout.get_active_engine()
++        button = self.__builder.get_object("button_system_keyboard_layout_engine_add")
++        engines = self.__treeview_system_keyboard_layout.get_engines()
++        button.set_sensitive(engine != None and \
++                             engine not in engines and \
++                             len(engines) < XKB_MAX_LAYOUTS)
++
++    def __treeview_notify_system_keyboard_layout_cb(self, treeview, property):
++        if property.name != "active-engine" and property.name != "engines":
++            return
++
++        engines = self.__treeview_system_keyboard_layout.get_engines()
++        engine = self.__treeview_system_keyboard_layout.get_active_engine()
++
++        button = self.__builder.get_object("button_system_keyboard_layout_engine_remove")
++        button.set_sensitive(engine != None)
++        button = self.__builder.get_object("button_system_keyboard_layout_engine_up")
++        button.set_sensitive(engine not in engines[:1])
++        button = self.__builder.get_object("button_system_keyboard_layout_engine_down")
++        button.set_sensitive(engine not in engines[-1:])
++
++    def __button_system_keyboard_layout_add_cb(self, button):
++        engines = self.__treeview_system_keyboard_layout.get_engines()
++        engine = self.__combobox_system_keyboard_layout.get_active_engine()
++        if len(engines) > 0 and engines[0].layout == "default":
++            self.__treeview_system_keyboard_layout.set_engines([engine])
++        else:
++            self.__treeview_system_keyboard_layout.append_engine(engine)
++        button_reset = self.__builder.get_object("button_system_keyboard_layout_engine_reset")
++        button_reset.set_sensitive(True)
++        if len(self.__treeview_system_keyboard_layout.get_engines()) >= XKB_MAX_LAYOUTS:
++            button.set_sensitive(False)
++
++    def __button_system_keyboard_layout_remove_cb(self, button):
++        self.__treeview_system_keyboard_layout.remove_engine()
++        if len(self.__treeview_system_keyboard_layout.get_engines()) < XKB_MAX_LAYOUTS:
++            button_add = self.__builder.get_object("button_system_keyboard_layout_engine_add")
++            button_add.set_sensitive(True)
++        button_reset = self.__builder.get_object("button_system_keyboard_layout_engine_reset")
++        button_reset.set_sensitive(True)
++
++    def __button_system_keyboard_layout_reset_cb(self, button):
++        engine = self.__get_default_xkb_engine()
++        self.__treeview_system_keyboard_layout.set_engines([engine])
++        button.set_sensitive(False)
++
++    def __button_config_layouts_cb(self, button):
++        self.__dialog_config_layouts.run()
++        self.__dialog_config_layouts.hide()
++
++    def __button_config_layouts_cancel_cb(self, button):
++        self.__dialog_config_layouts.hide()
++
++    def __button_config_layouts_ok_cb(self, button):
++        self.__dialog_config_layouts.hide()
++        engine_list = []
++        for expander in self.__vbox_all_keyboard_layouts.get_children():
++            align = expander.get_children()[0]
++            vbox = align.get_children()[0]
++            for checkbutton in vbox.get_children():
++                if checkbutton.get_active():
++                    engine_list.append(checkbutton.get_data("layout"))
++        if len(engine_list) == 0:
++            return
++        engine_list.sort()
++        self.__xkblayoutconfig.save_preload_layouts(engine_list)
++        message = _("Please restart IBus to reload your configuration.")
++        dlg = gtk.MessageDialog(type = gtk.MESSAGE_INFO,
++                                buttons = gtk.BUTTONS_OK,
++                                message_format = message)
++        dlg.run()
++        dlg.destroy()
++
++    def __button_system_keyboard_layout_cb(self, button):
++        self.__dialog_system_keyboard_layout.run()
++        self.__dialog_system_keyboard_layout.hide()
++
++    def __button_system_keyboard_layout_cancel_cb(self, button):
++        self.__dialog_system_keyboard_layout.hide()
++
++    def __button_system_keyboard_layout_ok_cb(self, button):
++        self.__dialog_system_keyboard_layout.hide()
++        layout = "default"
++        for engine in self.__treeview_system_keyboard_layout.get_engines():
++            if layout == "default":
++                layout = engine.layout
++            else:
++                layout = "%s,%s" % (layout, engine.layout)
++        if layout == None or layout == "":
++            layout = "default"
++        org_layout = str(self.__config.get_value("general", "system_keyboard_layout", None))
++        if layout != org_layout:
++            self.__config.set_value("general", "system_keyboard_layout", layout)
++        if layout == "default":
++            layout = _("Default")
++        self.__button_system_keyboard_layout.set_label(layout)
++        option = "default"
++        if not self.__checkbutton_use_system_keyboard_option.get_active():
++            for expander in self.__vbox_system_keyboard_options.get_children():
++                align = expander.get_children()[0]
++                vbox = align.get_children()[0]
++                for checkbutton in vbox.get_children():
++                    if checkbutton.get_active():
++                        data = checkbutton.get_data("option")
++                        if option == "default":
++                            option = data
++                        else:
++                            option = "%s,%s" % (option, data)
++        if option == None or option == "":
++            option = "default"
++        if option != "default" and option.find(':') < 0:
++            message = _("The keyboard option cannot be chosen.")
++            dlg = gtk.MessageDialog(type = gtk.MESSAGE_INFO,
++                                    buttons = gtk.BUTTONS_OK,
++                                    message_format = message)
++            dlg.run()
++            dlg.destroy()
++            return
++        org_option = str(self.__config.get_value("general", "system_keyboard_option", None))
++        if option != org_option:
++            self.__config.set_value("general", "system_keyboard_option", option)
++        message = _("Please restart IBus to reload your configuration.")
++        dlg = gtk.MessageDialog(type = gtk.MESSAGE_INFO,
++                                buttons = gtk.BUTTONS_OK,
++                                message_format = message)
++        dlg.run()
++        dlg.destroy()
++
++    def __button_system_keyboard_option_cb(self, button):
++        self.__dialog_system_keyboard_option.run()
++        self.__dialog_system_keyboard_option.hide()
++
++    def __checkbutton_system_keyboard_option_toggled_cb(self, button, user_data):
++        expander = user_data
++        checked = expander.get_data("checked")
++        label = expander.get_label_widget()
++        if button.get_active():
++            checked = checked + 1
++            label.set_markup("<b>" + label.get_text() + "</b>")
++        else:
++            checked = checked - 1
++            if checked <= 0:
++                label.set_text(label.get_text())
++        expander.set_data("checked", checked)
++
+diff --git a/src/Makefile.am b/src/Makefile.am
+index a53bd23..6454522 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -184,6 +184,11 @@ typelibs_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
+ CLEANFILES += $(dist_gir_DATA) $(typelibs_DATA)
+ endif
++if ENABLE_XKB
++ibus_sources += ibusxkbxml.c
++ibus_headers += ibusxkbxml.h
++endif
++
+ # gen enum types
+ ibusenumtypes.h: $(ibus_headers) ibusenumtypes.h.template
+       $(AM_V_GEN) ( top_builddir=`cd $(top_builddir) && pwd`; \
+diff --git a/src/ibus.h b/src/ibus.h
+index c408f3d..6bb9ff5 100644
+--- a/src/ibus.h
++++ b/src/ibus.h
+@@ -44,6 +44,7 @@
+ #include <ibuskeymap.h>
+ #include <ibusenumtypes.h>
+ #include <ibushotkey.h>
++#include <ibusxkbxml.h>
+ #include <ibusxml.h>
+ #include <ibusenginedesc.h>
+ #include <ibusobservedpath.h>
+diff --git a/src/ibusfactory.c b/src/ibusfactory.c
+index 11d9a6d..7770216 100644
+--- a/src/ibusfactory.c
++++ b/src/ibusfactory.c
+@@ -21,6 +21,7 @@
+  */
+ #include "ibusfactory.h"
+ #include "ibusengine.h"
++#include "ibusmarshalers.h"
+ #include "ibusshare.h"
+ #include "ibusinternal.h"
+@@ -28,6 +29,7 @@
+    (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_FACTORY, IBusFactoryPrivate))
+ enum {
++    LOOKUP_ENGINE_NAME,
+     LAST_SIGNAL,
+ };
+@@ -42,6 +44,8 @@ struct _IBusFactoryPrivate {
+     GHashTable     *engine_table;
+ };
++static guint            factory_signals[LAST_SIGNAL] = { 0 };
++
+ /* functions prototype */
+ static void      ibus_factory_destroy        (IBusFactory        *factory);
+ static void      ibus_factory_set_property   (IBusFactory        *engine,
+@@ -113,6 +117,17 @@ ibus_factory_class_init (IBusFactoryClass *class)
+     ibus_service_class_add_interfaces (IBUS_SERVICE_CLASS (class), introspection_xml);
+     g_type_class_add_private (class, sizeof (IBusFactoryPrivate));
++
++    factory_signals[LOOKUP_ENGINE_NAME] =
++        g_signal_new (I_("lookup-engine-name"),
++            G_TYPE_FROM_CLASS (gobject_class),
++            G_SIGNAL_RUN_LAST,
++            G_STRUCT_OFFSET (IBusFactoryClass, lookup_engine_name),
++            NULL, NULL,
++            _ibus_marshal_VOID__STRING,
++            G_TYPE_NONE,
++            1,
++            G_TYPE_STRING);
+ }
+ static void
+@@ -190,8 +205,12 @@ ibus_factory_service_method_call (IBusService           *service,
+     if (g_strcmp0 (method_name, "CreateEngine") == 0) {
+         gchar *engine_name = NULL;
++        GType engine_type;
++
+         g_variant_get (parameters, "(&s)", &engine_name);
+-        GType engine_type = (GType )g_hash_table_lookup (factory->priv->engine_table, engine_name);
++        g_signal_emit (factory, factory_signals[LOOKUP_ENGINE_NAME],
++                       0, engine_name);
++        engine_type = (GType) g_hash_table_lookup (factory->priv->engine_table, engine_name);
+         if (engine_type == G_TYPE_INVALID) {
+             gchar *error_message = g_strdup_printf ("Can not fond engine %s", engine_name);
+diff --git a/src/ibusfactory.h b/src/ibusfactory.h
+index 47c06e0..102081c 100644
+--- a/src/ibusfactory.h
++++ b/src/ibusfactory.h
+@@ -127,10 +127,13 @@ struct _IBusFactoryClass {
+     IBusServiceClass parent;
+     /* signals */
++    void        (* lookup_engine_name)
++                                    (IBusFactory    *factory,
++                                     const gchar    *engine_name);
+     /*< private >*/
+     /* padding */
+-    gpointer pdummy[8];
++    gpointer pdummy[7];
+ };
+ /**
+diff --git a/src/ibusxkbxml.c b/src/ibusxkbxml.c
+new file mode 100644
+index 0000000..c630eb9
+--- /dev/null
++++ b/src/ibusxkbxml.c
+@@ -0,0 +1,440 @@
++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
++/* vim:set et sts=4: */
++/* bus - The Input Bus
++ * Copyright (C) 2011 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2011 Peng Huang <shawn.p.huang@gmail.com>
++ * Copyright (C) 2011 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.
++ */
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <glib.h>
++
++#include "ibus.h"
++
++#ifndef XKB_RULES_XML_FILE
++#define XKB_RULES_XML_FILE "/usr/share/X11/xkb/rules/evdev.xml"
++#endif
++
++#define IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE(o)  \
++   (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistryPrivate))
++
++typedef struct _IBusXKBConfigRegistryPrivate IBusXKBConfigRegistryPrivate;
++
++struct _IBusXKBConfigRegistryPrivate {
++    GHashTable *layout_list;
++    GHashTable *layout_lang;
++    GHashTable *layout_desc;
++    GHashTable *variant_desc;
++};
++
++
++/* functions prototype */
++static void         ibus_xkb_config_registry_destroy
++                                           (IBusXKBConfigRegistry *xkb_config);
++
++G_DEFINE_TYPE (IBusXKBConfigRegistry, ibus_xkb_config_registry, IBUS_TYPE_OBJECT)
++
++static void
++parse_xkb_xml_languagelist_node (IBusXKBConfigRegistryPrivate *priv,
++                                 XMLNode *parent_node,
++                                 const gchar *layout_name)
++{
++    XMLNode *node = parent_node;
++    XMLNode *sub_node;
++    GList *p;
++    GList *lang_list = NULL;
++
++    g_assert (node != NULL);
++    g_assert (layout_name != NULL);
++    for (p = node->sub_nodes; p; p = p->next) {
++        sub_node = (XMLNode *) p->data;
++        if (g_strcmp0 (sub_node->name, "iso639Id") == 0) {
++            lang_list = g_list_append (lang_list,
++                                       (gpointer) g_strdup (sub_node->text));
++            continue;
++        }
++    }
++    if (lang_list == NULL) {
++        /* some nodes have no lang */
++        return;
++    }
++    if (g_hash_table_lookup (priv->layout_lang, layout_name) != NULL) {
++        g_warning ("duplicated name %s exists", layout_name);
++        return;
++    }
++    g_hash_table_insert (priv->layout_lang,
++                         (gpointer) g_strdup (layout_name),
++                         (gpointer) lang_list);
++}
++
++static const gchar *
++parse_xkb_xml_configitem_node (IBusXKBConfigRegistryPrivate *priv,
++                               XMLNode *parent_node)
++{
++    XMLNode *node = parent_node;
++    XMLNode *sub_node;
++    GList *p;
++    gchar *name = NULL;
++    gchar *description = NULL;
++
++    g_assert (node != NULL);
++    for (p = node->sub_nodes; p; p = p->next) {
++        sub_node = (XMLNode *) p->data;
++        if (g_strcmp0 (sub_node->name, "name") == 0) {
++            name = sub_node->text;
++            continue;
++        }
++        if (g_strcmp0 (sub_node->name, "description") == 0) {
++            description = sub_node->text;
++            continue;
++        }
++        if (g_strcmp0 (sub_node->name, "languageList") == 0) {
++            if (name == NULL) {
++                g_warning ("layout name is NULL in node %s", node->name);
++                continue;
++            }
++            parse_xkb_xml_languagelist_node (priv, sub_node, name);
++            continue;
++        }
++    }
++    if (name == NULL) {
++        g_warning ("No name in layout node");
++        return NULL;
++    }
++    if (g_hash_table_lookup (priv->layout_desc, name) != NULL) {
++        g_warning ("duplicated name %s exists", name);
++        return name;
++    }
++    g_hash_table_insert (priv->layout_desc,
++                         (gpointer) g_strdup (name),
++                         (gpointer) g_strdup (description));
++
++    return name;
++}
++
++static const gchar *
++parse_xkb_xml_variant_configitem_node (IBusXKBConfigRegistryPrivate *priv,
++                            XMLNode *parent_node,
++                            const gchar *layout_name)
++{
++    XMLNode *node = parent_node;
++    XMLNode *sub_node;
++    GList *p;
++    gchar *name = NULL;
++    gchar *description = NULL;
++    gchar *variant_lang_name = NULL;
++
++    g_assert (node != NULL);
++    g_assert (layout_name != NULL);
++    for (p = node->sub_nodes; p; p = p->next) {
++        sub_node = (XMLNode *) p->data;
++        if (g_strcmp0 (sub_node->name, "name") == 0) {
++            name = sub_node->text;
++            continue;
++        }
++        if (g_strcmp0 (sub_node->name, "description") == 0) {
++            description = sub_node->text;
++            continue;
++        }
++        if (g_strcmp0 (sub_node->name, "languageList") == 0) {
++            if (name == NULL) {
++                g_warning ("layout name is NULL in node %s", node->name);
++                continue;
++            }
++            variant_lang_name = g_strdup_printf ("%s(%s)", layout_name, name);
++            parse_xkb_xml_languagelist_node (priv, sub_node, variant_lang_name);
++            g_free (variant_lang_name);
++            continue;
++        }
++    }
++    if (name == NULL) {
++        g_warning ("No name in layout node");
++        return NULL;
++    }
++    if (g_hash_table_lookup (priv->variant_desc, name) != NULL) {
++        /* This is an expected case. */
++        return name;
++    }
++    g_hash_table_insert (priv->variant_desc,
++                         (gpointer) g_strdup (name),
++                         (gpointer) g_strdup (description));
++    return name;
++}
++
++static const gchar *
++parse_xkb_xml_variant_node (IBusXKBConfigRegistryPrivate *priv,
++                            XMLNode *parent_node,
++                            const gchar *layout_name)
++{
++    XMLNode *node = parent_node;
++    XMLNode *sub_node;
++    GList *p;
++    const gchar *variant_name = NULL;
++
++    g_assert (node != NULL);
++    g_assert (layout_name != NULL);
++    for (p = node->sub_nodes; p; p = p->next) {
++        sub_node = (XMLNode *) p->data;
++        if (g_strcmp0 (sub_node->name, "configItem") == 0) {
++            variant_name = parse_xkb_xml_variant_configitem_node (priv, sub_node, layout_name);
++            continue;
++        }
++    }
++    return variant_name;
++}
++
++static GList *
++parse_xkb_xml_variantlist_node (IBusXKBConfigRegistryPrivate *priv,
++                                XMLNode *parent_node,
++                                const gchar *layout_name,
++                                GList *variant_list)
++{
++    XMLNode *node = parent_node;
++    XMLNode *sub_node;
++    GList *p;
++    const gchar *variant_name = NULL;
++
++    g_assert (node != NULL);
++    g_assert (layout_name != NULL);
++    for (p = node->sub_nodes; p; p = p->next) {
++        sub_node = (XMLNode *) p->data;
++        if (g_strcmp0 (sub_node->name, "variant") == 0) {
++            variant_name = parse_xkb_xml_variant_node (priv, sub_node, layout_name);
++            if (variant_name != NULL) {
++                variant_list = g_list_append (variant_list,
++                                              (gpointer) g_strdup (variant_name));
++            }
++            continue;
++        }
++    }
++    return variant_list;
++}
++
++static void
++parse_xkb_xml_layout_node (IBusXKBConfigRegistryPrivate *priv,
++                           XMLNode *parent_node)
++{
++    XMLNode *node = parent_node;
++    XMLNode *sub_node;
++    GList *p;
++    const gchar *name = NULL;
++    GList *variant_list = NULL;
++
++    g_assert (node != NULL);
++    for (p = node->sub_nodes; p; p = p->next) {
++        sub_node = (XMLNode *) p->data;
++        if (g_strcmp0 (sub_node->name, "configItem") == 0) {
++            name = parse_xkb_xml_configitem_node (priv, sub_node);
++            continue;
++        }
++        if (g_strcmp0 (sub_node->name, "variantList") == 0) {
++            if (name == NULL) {
++                g_warning ("layout name is NULL in node %s", node->name);
++                continue;
++            }
++            variant_list = parse_xkb_xml_variantlist_node (priv, sub_node,
++                                                           name,
++                                                           variant_list);
++            continue;
++        }
++    }
++    if (g_hash_table_lookup (priv->layout_list, name) != NULL) {
++        g_warning ("duplicated name %s exists", name);
++        return;
++    }
++    g_hash_table_insert (priv->layout_list,
++                         (gpointer) g_strdup (name),
++                         (gpointer) variant_list);
++}
++
++static void
++parse_xkb_xml_top_node (IBusXKBConfigRegistryPrivate *priv,
++                        XMLNode *parent_node)
++{
++    XMLNode *node = parent_node;
++    XMLNode *sub_node;
++    GList *p;
++
++    g_assert (priv != NULL);
++    g_assert (node != NULL);
++
++    if (g_strcmp0 (node->name, "xkbConfigRegistry") != 0) {
++        g_warning ("node has no xkbConfigRegistry name");
++        return;
++    }
++    for (p = node->sub_nodes; p; p = p->next) {
++        sub_node = (XMLNode *) p->data;
++        if (g_strcmp0 (sub_node->name, "layoutList") == 0) {
++            break;
++        }
++    }
++    if (p == NULL) {
++        g_warning ("xkbConfigRegistry node has no layoutList node");
++        return;
++    }
++    node = sub_node;
++    for (p = node->sub_nodes; p; p = p->next) {
++        sub_node = (XMLNode *) p->data;
++        if (g_strcmp0 (sub_node->name, "layout") == 0) {
++            parse_xkb_xml_layout_node (priv, sub_node);
++            continue;
++        }
++    }
++}
++
++static void
++free_lang_list (GList *list)
++{
++    GList *l = list;
++    while (l) {
++        g_free (l->data);
++        l->data = NULL;
++        l = l->next;
++    }
++    g_list_free (list);
++}
++
++static void
++parse_xkb_config_registry_file (IBusXKBConfigRegistryPrivate *priv,
++                                const gchar *file)
++{
++    XMLNode *node;
++
++    g_assert (file != NULL);
++
++    priv->layout_list = g_hash_table_new_full (g_str_hash,
++                                               (GEqualFunc) g_str_equal,
++                                               (GDestroyNotify) g_free,
++                                               (GDestroyNotify) free_lang_list);
++    priv->layout_desc = g_hash_table_new_full (g_str_hash,
++                                               (GEqualFunc) g_str_equal,
++                                               (GDestroyNotify) g_free,
++                                               (GDestroyNotify) g_free);
++    priv->layout_lang = g_hash_table_new_full (g_str_hash,
++                                               (GEqualFunc) g_str_equal,
++                                               (GDestroyNotify) g_free,
++                                               (GDestroyNotify) free_lang_list);
++    priv->variant_desc = g_hash_table_new_full (g_str_hash,
++                                               (GEqualFunc) g_str_equal,
++                                               (GDestroyNotify) g_free,
++                                               (GDestroyNotify) g_free);
++    node = ibus_xml_parse_file (file);
++    parse_xkb_xml_top_node (priv, node);
++    ibus_xml_free (node);
++}
++
++static void
++ibus_xkb_config_registry_init (IBusXKBConfigRegistry *xkb_config)
++{
++    IBusXKBConfigRegistryPrivate *priv;
++    const gchar *file = XKB_RULES_XML_FILE;
++
++    priv = IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE (xkb_config);
++    parse_xkb_config_registry_file (priv, file);
++}
++
++static void
++ibus_xkb_config_registry_destroy (IBusXKBConfigRegistry *xkb_config)
++{
++    IBusXKBConfigRegistryPrivate *priv;
++
++    g_return_if_fail (xkb_config != NULL);
++
++    priv = IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE (xkb_config);
++
++    g_hash_table_destroy (priv->layout_list);
++    priv->layout_list = NULL;
++    g_hash_table_destroy (priv->layout_lang);
++    priv->layout_lang= NULL;
++    g_hash_table_destroy (priv->layout_desc);
++    priv->layout_desc= NULL;
++    g_hash_table_destroy (priv->variant_desc);
++    priv->variant_desc = NULL;
++
++    IBUS_OBJECT_CLASS(ibus_xkb_config_registry_parent_class)->destroy (IBUS_OBJECT (xkb_config));
++}
++
++static void
++ibus_xkb_config_registry_class_init (IBusXKBConfigRegistryClass *klass)
++{
++    IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass);
++
++    g_type_class_add_private (klass, sizeof (IBusXKBConfigRegistryPrivate));
++
++    ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_xkb_config_registry_destroy;
++}
++
++IBusXKBConfigRegistry *
++ibus_xkb_config_registry_new (void)
++{
++    IBusXKBConfigRegistry *xkb_config;
++
++    xkb_config = IBUS_XKB_CONFIG_REGISTRY (g_object_new (IBUS_TYPE_XKB_CONFIG_REGISTRY, NULL));
++    return xkb_config;
++}
++
++#define TABLE_FUNC(field_name) const GHashTable *                       \
++ibus_xkb_config_registry_get_##field_name  (IBusXKBConfigRegistry *xkb_config) \
++{                                                                       \
++    IBusXKBConfigRegistryPrivate *priv;                                 \
++                                                                        \
++    g_return_val_if_fail (xkb_config != NULL, NULL);                    \
++    priv = IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE (xkb_config);           \
++    return priv->field_name;                                            \
++}
++
++TABLE_FUNC (layout_list)
++TABLE_FUNC (layout_lang)
++TABLE_FUNC (layout_desc)
++TABLE_FUNC (variant_desc)
++
++#undef TABLE_FUNC
++
++#define TABLE_LOOKUP_LIST_FUNC(field_name, value) GList *               \
++ibus_xkb_config_registry_##field_name##_get_##value  (IBusXKBConfigRegistry *xkb_config, const gchar *key) \
++{                                                                       \
++    GHashTable *table;                                                  \
++    GList *list = NULL;                                                 \
++                                                                        \
++    table = (GHashTable *)                                              \
++        ibus_xkb_config_registry_get_##field_name (xkb_config);         \
++    list = (GList *) g_hash_table_lookup (table, key);                  \
++    return g_list_copy (list);                                          \
++}
++
++#define TABLE_LOOKUP_STRING_FUNC(field_name, value) gchar *             \
++ibus_xkb_config_registry_##field_name##_get_##value  (IBusXKBConfigRegistry *xkb_config, const gchar *key) \
++{                                                                       \
++    GHashTable *table;                                                  \
++    const gchar *desc = NULL;                                           \
++                                                                        \
++    table = (GHashTable *)                                              \
++        ibus_xkb_config_registry_get_##field_name (xkb_config);         \
++    desc = (const gchar *) g_hash_table_lookup (table, key);            \
++    return g_strdup (desc);                                             \
++}
++
++TABLE_LOOKUP_LIST_FUNC (layout_list, variants)
++TABLE_LOOKUP_LIST_FUNC (layout_lang, langs)
++TABLE_LOOKUP_STRING_FUNC (layout_desc, desc)
++TABLE_LOOKUP_STRING_FUNC (variant_desc, desc)
++
++#undef TABLE_LOOKUP_LIST_FUNC
++#undef TABLE_LOOKUP_STRING_FUNC
+diff --git a/src/ibusxkbxml.h b/src/ibusxkbxml.h
+new file mode 100644
+index 0000000..6986b5c
+--- /dev/null
++++ b/src/ibusxkbxml.h
+@@ -0,0 +1,172 @@
++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
++/* vim:set et sts=4: */
++/* bus - The Input Bus
++ * Copyright (C) 2011 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2011 Peng Huang <shawn.p.huang@gmail.com>
++ * Copyright (C) 2011 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.
++ */
++#ifndef __IBUS_XKBXML_H_
++#define __IBUS_XKBXML_H_
++
++#include "ibus.h"
++
++/*
++ * Type macros.
++ */
++/* define IBusXKBConfigRegistry macros */
++#define IBUS_TYPE_XKB_CONFIG_REGISTRY                   \
++    (ibus_xkb_config_registry_get_type ())
++#define IBUS_XKB_CONFIG_REGISTRY(obj)                   \
++    (G_TYPE_CHECK_INSTANCE_CAST ((obj), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistry))
++#define IBUS_XKB_CONFIG_REGISTRY_CLASS(klass)           \
++    (G_TYPE_CHECK_CLASS_CAST ((klass), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistryClass))
++#define IBUS_IS_XKB_CONFIG_REGISTRY(obj)                \
++    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IBUS_TYPE_XKB_CONFIG_REGISTRY))
++#define IBUS_IS_XKB_CONFIG_REGISTRY_CLASS(klass)        \
++    (G_TYPE_CHECK_CLASS_TYPE ((klass), IBUS_TYPE_XKB_CONFIG_REGISTRY))
++#define IBUS_XKB_CONFIG_REGISTRY_GET_CLASS(obj)         \
++    (G_TYPE_INSTANCE_GET_CLASS ((obj), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistryClass))
++
++G_BEGIN_DECLS
++
++typedef struct _IBusXKBConfigRegistry IBusXKBConfigRegistry;
++typedef struct _IBusXKBConfigRegistryClass IBusXKBConfigRegistryClass;
++
++struct _IBusXKBConfigRegistry {
++    IBusObject parent;
++};
++
++struct _IBusXKBConfigRegistryClass {
++    IBusObjectClass parent;
++    /* signals */
++    /*< private >*/
++    /* padding */
++    gpointer pdummy[8];
++};
++
++
++GType            ibus_xkb_config_registry_get_type
++                                                 (void);
++
++/**
++ * ibus_xkb_config_registry_new:
++ * @returns: A newly allocated IBusXKBConfigRegistry
++ *
++ * New an IBusXKBConfigRegistry.
++ */
++IBusXKBConfigRegistry *
++                 ibus_xkb_config_registry_new
++                                                 (void);
++
++/**
++ * ibus_xkb_config_registry_get_layout_list: (skip)
++ * @xkb_config: An IBusXKBConfigRegistry.
++ * @returns: A const GHashTable
++ *
++ * a const GHashTable
++ */
++const GHashTable *
++                 ibus_xkb_config_registry_get_layout_list
++                                                 (IBusXKBConfigRegistry *xkb_config);
++
++/**
++ * ibus_xkb_config_registry_get_layout_lang: (skip)
++ * @xkb_config: An IBusXKBConfigRegistry.
++ * @returns: A const GHashTable
++ *
++ * a const GHashTable
++ */
++const GHashTable *
++                 ibus_xkb_config_registry_get_layout_lang
++                                                 (IBusXKBConfigRegistry *xkb_config);
++
++/**
++ * ibus_xkb_config_registry_get_layout_desc: (skip)
++ * @xkb_config: An IBusXKBConfigRegistry.
++ * @returns: A const GHashTable
++ *
++ * a const GHashTable
++ */
++const GHashTable *
++                 ibus_xkb_config_registry_get_layout_desc
++                                                 (IBusXKBConfigRegistry *xkb_config);
++
++/**
++ * ibus_xkb_config_registry_get_variant_desc: (skip)
++ * @xkb_config: An IBusXKBConfigRegistry.
++ * @returns: A const GHashTable
++ *
++ * a const GHashTable
++ */
++const GHashTable *
++                 ibus_xkb_config_registry_get_variant_desc
++                                                 (IBusXKBConfigRegistry *xkb_config);
++
++/**
++ * ibus_xkb_config_registry_layout_list_get_variants:
++ * @xkb_config: An IBusXKBConfigRegistry.
++ * @layout: A layout.
++ * @returns: (transfer container) (element-type utf8): A GList
++ *
++ * a GList
++ */
++GList *
++                 ibus_xkb_config_registry_layout_list_get_variants
++                                                 (IBusXKBConfigRegistry *xkb_config,
++                                                  const gchar           *layout);
++
++/**
++ * ibus_xkb_config_registry_layout_lang_get_langs:
++ * @xkb_config: An IBusXKBConfigRegistry.
++ * @layout: A layout.
++ * @returns: (transfer container) (element-type utf8): A GList
++ *
++ * a GList
++ */
++GList *
++                 ibus_xkb_config_registry_layout_lang_get_langs
++                                                 (IBusXKBConfigRegistry *xkb_config,
++                                                  const gchar           *layout);
++
++/**
++ * ibus_xkb_config_registry_layout_desc_get_desc:
++ * @xkb_config: An IBusXKBConfigRegistry.
++ * @layout: A layout.
++ * @returns: A layout description
++ *
++ * a layout description
++ */
++gchar *
++                 ibus_xkb_config_registry_layout_desc_get_desc
++                                                 (IBusXKBConfigRegistry *xkb_config,
++                                                  const gchar           *layout);
++
++/**
++ * ibus_xkb_config_registry_variant_desc_get_desc:
++ * @xkb_config: An IBusXKBConfigRegistry.
++ * @variant: A variant.
++ * @returns: A variant description
++ *
++ * a variant description
++ */
++gchar *
++                 ibus_xkb_config_registry_variant_desc_get_desc
++                                                 (IBusXKBConfigRegistry *xkb_config,
++                                                  const gchar           *variant);
++G_END_DECLS
++#endif
+diff --git a/ui/gtk/panel.py b/ui/gtk/panel.py
+index 90be1d5..de64920 100644
+--- a/ui/gtk/panel.py
++++ b/ui/gtk/panel.py
+@@ -132,6 +132,22 @@ class Panel(ibus.PanelBase):
+         self.__config_load_show_im_name()
+         # self.__bus.request_name(ibus.panel.IBUS_SERVICE_PANEL, 0)
++        # init xkb
++        self.__xkblayout = ibus.XKBLayout(self.__config)
++        use_xkb = self.__config.get_value("general", "use_system_keyboard_layout", False)
++        if not use_xkb:
++            self.__xkblayout.use_xkb(use_xkb)
++        value = str(self.__config.get_value("general", "system_keyboard_layout", ''))
++        if value == '':
++            value = 'default'
++        if value != 'default':
++            self.__xkblayout.set_default_layout(value)
++        value = str(self.__config.get_value("general", "system_keyboard_option", ''))
++        if value == '':
++            value = 'default'
++        if value != 'default':
++            self.__xkblayout.set_default_option(value)
++
+     def set_cursor_location(self, x, y, w, h):
+         self.__candidate_panel.set_cursor_location(x, y, w, h)
+@@ -226,14 +242,20 @@ class Panel(ibus.PanelBase):
+         if not enabled:
+             self.__set_im_icon(ICON_KEYBOARD)
+             self.__set_im_name(None)
++            if self.__bus.get_use_sys_layout():
++                self.__xkblayout.set_layout()
+         else:
+             engine = self.__focus_ic.get_engine()
+             if engine:
+                 self.__set_im_icon(engine.icon)
+                 self.__set_im_name(engine.longname)
++                if self.__bus.get_use_sys_layout():
++                    self.__xkblayout.set_layout(self.__engine_get_layout_wrapper(engine))
+             else:
+                 self.__set_im_icon(ICON_KEYBOARD)
+                 self.__set_im_name(None)
++                if self.__bus.get_use_sys_layout():
++                    self.__xkblayout.set_layout()
+         self.__language_bar.focus_in()
+     def focus_out(self, ic):
+@@ -243,6 +265,8 @@ class Panel(ibus.PanelBase):
+         self.__language_bar.focus_out()
+         self.__set_im_icon(ICON_KEYBOARD)
+         self.__set_im_name(None)
++        if self.__bus.get_use_sys_layout():
++            self.__xkblayout.set_layout()
+     def state_changed(self):
+         if not self.__focus_ic:
+@@ -255,14 +279,20 @@ class Panel(ibus.PanelBase):
+             self.reset()
+             self.__set_im_icon(ICON_KEYBOARD)
+             self.__set_im_name(None)
++            if self.__bus.get_use_sys_layout():
++                self.__xkblayout.set_layout()
+         else:
+             engine = self.__focus_ic.get_engine()
+             if engine:
+                 self.__set_im_icon(engine.icon)
+                 self.__set_im_name(engine.longname)
++                if self.__bus.get_use_sys_layout():
++                    self.__xkblayout.set_layout(self.__engine_get_layout_wrapper(engine))
+             else:
+                 self.__set_im_icon(ICON_KEYBOARD)
+                 self.__set_im_name(None)
++                if self.__bus.get_use_sys_layout():
++                    self.__xkblayout.set_layout()
+     def reset(self):
+@@ -542,3 +572,12 @@ class Panel(ibus.PanelBase):
+                                flags=glib.SPAWN_DO_NOT_REAP_CHILD)[0]
+         self.__setup_pid = pid
+         glib.child_watch_add(self.__setup_pid, self.__child_watch_cb)
++
++    def __engine_get_layout_wrapper(self, engine):
++        # This code is for the back compatibility.
++        # Should we remove the codes after all IM engines are changed
++        # to "default" layout?
++        if engine.name != None and engine.name.startswith("xkb:layout:"):
++            return engine.layout
++        else:
++            return "default"
+diff --git a/xkb/Makefile.am b/xkb/Makefile.am
+new file mode 100644
+index 0000000..ad9cdd9
+--- /dev/null
++++ b/xkb/Makefile.am
+@@ -0,0 +1,104 @@
++# vim:set noet ts=4:
++#
++# ibus - The Input Bus
++#
++# Copyright (C) 2011 Takao Fujiwara <takao.fujiwara1@gmail.com>
++# Copyright (c) 2011 Peng Huang <shawn.p.huang@gmail.com>
++# Copyright (c) 2011 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 program; if not, write to the
++# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++# Boston, MA  02111-1307  USA
++
++libibus = $(top_builddir)/src/libibus-@IBUS_API_VERSION@.la
++
++INCLUDES = \
++        -I$(top_srcdir) \
++        -I$(top_srcdir)/src \
++      -DIBUS_LOCALEDIR=\"$(datadir)/locale\" \
++      -DLIBEXECDIR=\""$(libexecdir)"\" \
++        $(NULL)
++
++noinst_PROGRAMS = $(TESTS)
++libexec_PROGRAMS =
++EXTRA_DIST =
++DISTCLEANFILES =
++
++if ENABLE_XKB
++libexec_PROGRAMS += ibus-xkb
++ibus_xkb_SOURCES = \
++      ibus-xkb-main.c \
++      xkblib.h \
++      xkblib.c \
++      $(NULL)
++ibus_xkb_CFLAGS = \
++      @XKB_CFLAGS@ \
++      @X11_CFLAGS@ \
++      @GLIB2_CFLAGS@ \
++      $(NULL)
++ibus_xkb_LDADD = \
++      @XKB_LIBS@ \
++      @X11_LIBS@ \
++      @GLIB2_LIBS@ \
++      $(libibus) \
++      $(NULL)
++
++libexec_PROGRAMS += ibus-engine-xkb
++ibus_engine_xkb_SOURCES = \
++        ibus-engine-xkb-main.c \
++        ibus-engine-xkb-main.h \
++        xkbxml.c \
++        xkbxml.h \
++        $(NULL)
++ibus_engine_xkb_CFLAGS = \
++      @GLIB2_CFLAGS@ \
++      @GOBJECT2_CFLAGS@ \
++      @GCONF_CFLAGS@ \
++        $(NULL)
++ibus_engine_xkb_LDADD = \
++      @GLIB2_LIBS@ \
++      @GOBJECT2_LIBS@ \
++      @GCONF_LIBS@ \
++      $(libibus) \
++        $(NULL)
++
++xkblayoutdir = $(datadir)/ibus/component
++xkblayout_in_files = xkblayout.xml.in
++xkblayout_DATA = $(xkblayout_in_files:.xml.in=.xml)
++
++xkblayoutconfigdir = $(datadir)/ibus/xkb
++xkblayoutconfig_in_files = xkblayoutconfig.xml.in
++xkblayoutconfig_DATA = $(xkblayoutconfig_in_files:.xml.in=.xml)
++
++%.xml : %.xml.in
++      @sed -e "s|\@libexecdir\@|$(libexecdir)|g" \
++           -e "s|\@datadir\@|$(datadir)|g" \
++           -e "s|\@XKB_PRELOAD_LAYOUTS\@|$(XKB_PRELOAD_LAYOUTS)|g" \
++      $< > $@
++
++INCLUDES += \
++      -DXKBLAYOUTCONFIG_FILE=\""$(xkblayoutconfigdir)/$(xkblayoutconfig_DATA)"\" \
++      $(NULL)
++
++EXTRA_DIST += \
++        $(xkblayout_in_files) \
++        $(xkblayoutconfig_in_files) \
++        $(NULL)
++
++DISTCLEANFILES += \
++        $(xkblayout_DATA) \
++        $(xkblayoutconfig_DATA) \
++        $(NULL)
++
++endif
+diff --git a/xkb/ibus-engine-xkb-main.c b/xkb/ibus-engine-xkb-main.c
+new file mode 100644
+index 0000000..5d748cc
+--- /dev/null
++++ b/xkb/ibus-engine-xkb-main.c
+@@ -0,0 +1,397 @@
++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
++/* vim:set et sts=4: */
++/* bus - The Input Bus
++ * Copyright (C) 2011 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2011 Peng Huang <shawn.p.huang@gmail.com>
++ * Copyright (C) 2011 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.
++ */
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <gconf/gconf-client.h>
++#include <ibus.h>
++#include <stdlib.h>
++
++#ifdef ENABLE_NLS
++#include <locale.h>
++#endif
++
++#include "ibus-engine-xkb-main.h"
++#include "xkbxml.h"
++
++#define IBUS_TYPE_XKB_ENGINE (ibus_xkb_engine_get_type ())
++
++static IBusBus *bus = NULL;
++static IBusFactory *factory = NULL;
++static IBusEngineClass *parent_class = NULL;
++static gboolean ibus = FALSE;
++static gboolean xml = FALSE;
++
++static const GOptionEntry entries[] =
++{
++    { "ibus", 'i', 0, G_OPTION_ARG_NONE, &ibus, "component is executed by ibus", NULL },
++    { "xml", 'x', 0, G_OPTION_ARG_NONE, &xml, "print component xml", NULL },
++    { NULL },
++};
++
++static GObject*
++ibus_xkb_engine_constructor (GType                   type,
++                             guint                   n_construct_params,
++                             GObjectConstructParam  *construct_params)
++{
++    IBusXKBEngine *engine;
++
++    engine = (IBusXKBEngine *) G_OBJECT_CLASS (parent_class)->constructor (type,
++                                               n_construct_params,
++                                               construct_params);
++
++    return (GObject *) engine;
++}
++
++static void
++ibus_xkb_engine_destroy (IBusObject *object)
++{
++    IBUS_OBJECT_CLASS (parent_class)->destroy (object);
++}
++
++static void
++ibus_xkb_engine_enable (IBusEngine *engine)
++{
++    parent_class->enable (engine);
++}
++
++static void
++ibus_xkb_engine_disable (IBusEngine *engine)
++{
++    parent_class->disable (engine);
++}
++
++static void
++ibus_xkb_engine_focus_in (IBusEngine *engine)
++{
++    parent_class->focus_in (engine);
++}
++
++static void
++ibus_xkb_engine_focus_out (IBusEngine *engine)
++{
++    parent_class->focus_out (engine);
++}
++
++static void
++ibus_xkb_engine_class_init (IBusXKBEngineClass *klass)
++{
++    GObjectClass *object_class = G_OBJECT_CLASS (klass);
++    IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass);
++    IBusEngineClass *engine_class = IBUS_ENGINE_CLASS (klass);
++
++    parent_class = (IBusEngineClass *) g_type_class_peek_parent (klass);
++    object_class->constructor = ibus_xkb_engine_constructor;
++    ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_xkb_engine_destroy;
++    engine_class->enable = ibus_xkb_engine_enable;
++    engine_class->disable = ibus_xkb_engine_disable;
++    engine_class->focus_in = ibus_xkb_engine_focus_in;
++    engine_class->focus_out = ibus_xkb_engine_focus_out;
++
++}
++
++static void
++ibus_xkb_engine_init (IBusXKBEngine *engine)
++{
++}
++
++GType
++ibus_xkb_engine_get_type (void)
++{
++    static GType type = 0;
++
++    static const GTypeInfo type_info = {
++        sizeof (IBusXKBEngineClass),
++        (GBaseInitFunc)     NULL,
++        (GBaseFinalizeFunc) NULL,
++        (GClassInitFunc)    ibus_xkb_engine_class_init,
++        NULL,
++        NULL,
++        sizeof (IBusXKBEngine),
++        0,
++        (GInstanceInitFunc) ibus_xkb_engine_init,
++    };
++
++    if (type == 0) {
++            type = g_type_register_static (IBUS_TYPE_ENGINE,
++                                           "IBusXKBEngine",
++                                           &type_info,
++                                           (GTypeFlags) 0);
++    }
++
++    return type;
++}
++
++static void
++ibus_disconnected_cb (IBusBus  *bus,
++                      gpointer  user_data)
++{
++    g_debug ("bus disconnected");
++    ibus_quit ();
++}
++
++static void
++_factory_lookup_engine_name_cb (IBusFactory *factory,
++                                const gchar *engine_name,
++                                gpointer     data)
++{
++    static GList *engine_list = NULL;
++    GList *list;
++    gboolean has_name = FALSE;
++
++    g_return_if_fail (engine_name != NULL);
++
++    if (g_strcmp0 (engine_name, "xkb:layout:us") == 0) {
++        return;
++    }
++    list = engine_list;
++    while (list) {
++        if (g_strcmp0 (list->data, engine_name) == 0) {
++            has_name = TRUE;
++            break;
++        }
++        list = list->next;
++    }
++    if (has_name) {
++        return;
++    }
++
++    ibus_factory_add_engine (factory, engine_name, IBUS_TYPE_XKB_ENGINE);
++    engine_list = g_list_append (engine_list, (gpointer) g_strdup (engine_name));
++}
++
++static void
++start_component (int argc, char **argv)
++{
++    IBusComponent *component;
++
++    ibus_init ();
++
++    bus = ibus_bus_new ();
++    g_signal_connect (bus, "disconnected", G_CALLBACK (ibus_disconnected_cb), NULL);
++
++    component = ibus_component_new ("org.freedesktop.IBus.XKB",
++                                    "XKB Component",
++                                    VERSION,
++                                    "LGPL2.1",
++                                    "Takao Fujiwara <takao.fujiwara1@gmail.com>",
++                                    "http://code.google.com/p/ibus/",
++                                    "",
++                                    GETTEXT_PACKAGE);
++    ibus_component_add_engine (component,
++                               ibus_xkb_engine_desc_new ("eng",
++                                                         "us",
++                                                         "USA",
++                                                         NULL,
++                                                         NULL));
++
++    factory = ibus_factory_new (ibus_bus_get_connection (bus));
++
++    ibus_factory_add_engine (factory, "xkb:layout:us", IBUS_TYPE_XKB_ENGINE);
++
++    g_signal_connect (G_OBJECT (factory), "lookup-engine-name",
++                      G_CALLBACK (_factory_lookup_engine_name_cb),
++                      NULL);
++    if (ibus) {
++        ibus_bus_request_name (bus, "org.freedesktop.IBus.XKB", 0);
++    }
++    else {
++        ibus_bus_register_component (bus, component);
++    }
++
++    g_object_unref (component);
++
++    ibus_main ();
++}
++
++static gboolean
++is_included_engine_in_preload (const GList * preload_xkb_engines,
++                               const gchar *layout,
++                               const gchar *variant)
++{
++    const GList *list = preload_xkb_engines;
++    gchar *key = NULL;
++    gboolean retval = FALSE;
++
++    g_return_val_if_fail (layout != NULL, FALSE);
++
++    if (variant == NULL) {
++        key = g_strdup (layout);
++    } else {
++        key = g_strdup_printf ("%s(%s)", layout, variant);
++    }
++    while (list) {
++        if (list->data == NULL) {
++            continue;
++        }
++        if (g_strcmp0 ((const gchar *) list->data,
++                       (const gchar *) key) == 0) {
++            retval = TRUE;
++            break;
++        }
++        list = list->next;
++    }
++    g_free (key);
++    return retval;
++}
++
++static void
++print_component ()
++{
++    IBusXKBLayoutConfig *layout_config;
++    IBusXKBConfigRegistry *config_registry;
++    GHashTable *layout_list;
++    GHashTable *layout_lang;
++    GHashTable *layout_desc;
++    GHashTable *variant_desc;
++    IBusComponent *component;
++    IBusEngineDesc *engine;
++    const GList *preload_xkb_engines = NULL;
++    GList *keys;
++    GList *variants;
++    GList *langs;
++    gboolean is_preload;
++    gchar *layout_name;
++    const gchar *desc;
++    gchar *output;
++    GString *str;
++
++#ifdef XKBLAYOUTCONFIG_FILE
++    layout_config = ibus_xkb_layout_config_new (XKBLAYOUTCONFIG_FILE);
++    preload_xkb_engines = ibus_xkb_layout_config_get_preload_layouts (layout_config);
++#endif
++
++    config_registry = ibus_xkb_config_registry_new ();
++    layout_list = (GHashTable *) ibus_xkb_config_registry_get_layout_list (config_registry);
++    layout_lang = (GHashTable *) ibus_xkb_config_registry_get_layout_lang (config_registry);
++    layout_desc = (GHashTable *) ibus_xkb_config_registry_get_layout_desc (config_registry);
++    variant_desc = (GHashTable *) ibus_xkb_config_registry_get_variant_desc (config_registry);
++    component = ibus_xkb_component_new ();
++    for (keys = g_hash_table_get_keys (layout_list); keys; keys = keys->next) {
++        if (keys->data == NULL) {
++            continue;
++        }
++        desc = (const gchar *) g_hash_table_lookup (layout_desc, keys->data);
++        langs = (GList *) g_hash_table_lookup (layout_lang, keys->data);
++        for (;langs; langs = langs->next) {
++            if (langs->data == NULL) {
++                continue;
++            }
++            is_preload = FALSE;
++            if (!preload_xkb_engines) {
++                is_preload = TRUE;
++            } else {
++                is_preload = is_included_engine_in_preload (preload_xkb_engines,
++                                                            (const gchar *) keys->data,
++                                                            NULL);
++            }
++            if (is_preload) {
++                engine = ibus_xkb_engine_desc_new ((const gchar *) langs->data,
++                                                   (const gchar *) keys->data,
++                                                   desc,
++                                                   NULL,
++                                                   NULL);
++            ibus_component_add_engine (component, engine);
++            }
++        }
++        variants = (GList *) g_hash_table_lookup (layout_list, keys->data);
++        for (;variants; variants = variants->next) {
++            if (variants->data == NULL) {
++                continue;
++            }
++            layout_name = g_strdup_printf ("%s(%s)", (gchar *) keys->data,
++                                                     (gchar *) variants->data);
++            langs = (GList *) g_hash_table_lookup (layout_lang, layout_name);
++            if (langs == NULL) {
++                g_free (layout_name);
++                layout_name = g_strdup ((gchar *) keys->data);
++                langs = (GList *) g_hash_table_lookup (layout_lang, layout_name);
++            }
++            g_free (layout_name);
++            for (;langs; langs = langs->next) {
++                if (langs->data == NULL) {
++                    continue;
++                }
++                is_preload = FALSE;
++                if (!preload_xkb_engines) {
++                   is_preload = TRUE;
++                } else {
++                    is_preload = is_included_engine_in_preload (preload_xkb_engines,
++                                                                (const gchar *) keys->data,
++                                                                (const gchar *) variants->data);
++                }
++                if (is_preload) {
++                    engine = ibus_xkb_engine_desc_new ((const gchar *) langs->data,
++                                                       (const gchar *) keys->data,
++                                                       desc,
++                                                       (const gchar *) variants->data,
++                                                       (const gchar *) g_hash_table_lookup (variant_desc, variants->data));
++                    ibus_component_add_engine (component, engine);
++                }
++            }
++        }
++    }
++    g_object_unref (G_OBJECT (config_registry));
++#ifdef XKBLAYOUTCONFIG_FILE
++    g_object_unref (G_OBJECT (layout_config));
++#endif
++
++    str = g_string_new (NULL);
++    ibus_component_output_engines (component , str, 0);
++    g_object_unref (G_OBJECT (component));
++
++    output = g_string_free (str, FALSE);
++    g_print ("%s\n", output);
++    g_free (output);
++}
++
++int
++main (int argc, char **argv)
++{
++    GError *error = NULL;
++    GOptionContext *context;
++
++#ifdef ENABLE_NLS
++    setlocale (LC_ALL, "");
++#endif
++
++    g_type_init ();
++
++    context = g_option_context_new ("- ibus xkb engine component");
++
++    g_option_context_add_main_entries (context, entries, "ibus-xbl");
++
++    if (!g_option_context_parse (context, &argc, &argv, &error)) {
++        g_print ("Option parsing failed: %s\n", error->message);
++        exit (-1);
++    }
++
++    if (xml) {
++        print_component ();
++        return 0;
++    }
++    start_component (argc, argv);
++
++    return 0;
++}
+diff --git a/xkb/ibus-engine-xkb-main.h b/xkb/ibus-engine-xkb-main.h
+new file mode 100644
+index 0000000..c17c857
+--- /dev/null
++++ b/xkb/ibus-engine-xkb-main.h
+@@ -0,0 +1,46 @@
++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
++/* vim:set et sts=4: */
++/* bus - The Input Bus
++ * Copyright (C) 2011 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2011 Peng Huang <shawn.p.huang@gmail.com>
++ * Copyright (C) 2011 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.
++ */
++#ifndef __IBUS_ENGINE_XKB_MAIN_H_
++#define __IBUS_ENGINE_XKB_MAIN_H_
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <ibus.h>
++
++G_BEGIN_DECLS
++
++typedef struct _IBusXKBEngine IBusXKBEngine;
++typedef struct _IBusXKBEngineClass IBusXKBEngineClass;
++
++struct _IBusXKBEngine {
++    IBusEngine engine;
++};
++
++struct _IBusXKBEngineClass {
++    IBusEngineClass parent;
++};
++
++G_END_DECLS
++#endif
+diff --git a/xkb/ibus-xkb-main.c b/xkb/ibus-xkb-main.c
+new file mode 100644
+index 0000000..ef57553
+--- /dev/null
++++ b/xkb/ibus-xkb-main.c
+@@ -0,0 +1,112 @@
++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
++/* vim:set et sts=4: */
++/* bus - The Input Bus
++ * Copyright (C) 2011 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2011 Peng Huang <shawn.p.huang@gmail.com>
++ * Copyright (C) 2011 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.
++ */
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <glib.h>
++#include <glib/gprintf.h>
++#include <glib/gi18n.h>
++#include <X11/Xlib.h>
++
++#ifdef ENABLE_NLS
++#include <locale.h>
++#endif
++
++#include "xkblib.h"
++
++static gboolean get_layout = FALSE;
++static gboolean get_group = FALSE;
++static gchar *layout = NULL;
++static gchar *model = NULL;
++static gchar *option = NULL;
++static int group = 0;
++
++static const GOptionEntry entries[] =
++{
++    { "get", 'g', 0, G_OPTION_ARG_NONE, &get_layout, N_("Get current xkb layout"), NULL },
++    /* Translators: the "layout" should not be translated due to a variable. */
++    { "layout", 'l', 0, G_OPTION_ARG_STRING, &layout, N_("Set xkb layout"), "layout" },
++    { "model", 'm', 0, G_OPTION_ARG_STRING, &model, N_("Set xkb model"), "model" },
++    { "option", 'o', 0, G_OPTION_ARG_STRING, &option, N_("Set xkb option"), "option" },
++    { "get-group", 'G', 0, G_OPTION_ARG_NONE, &get_group, N_("Get current xkb state"), NULL },
++    { NULL },
++};
++
++int
++main (int argc, char *argv[])
++{
++    GOptionContext *context;
++    GError *error = NULL;
++    Display *xdisplay;
++
++#ifdef ENABLE_NLS
++    setlocale (LC_ALL, "");
++
++    bindtextdomain (GETTEXT_PACKAGE, IBUS_LOCALEDIR);
++    bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
++#endif
++
++    context = g_option_context_new ("- ibus daemon");
++
++    g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
++    g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
++
++    if (!g_option_context_parse (context, &argc, &argv, &error)) {
++        g_printerr ("Option parsing failed: %s\n", error->message);
++        return -1;
++    }
++
++    xdisplay = XOpenDisplay (NULL);
++    if (xdisplay == NULL) {
++        g_warning ("Could not open display");
++        return -1;
++    }
++    ibus_xkb_init (xdisplay);
++
++    if (layout) {
++        ibus_xkb_set_layout (layout, model, option);
++    }
++    if (get_layout) {
++        layout = ibus_xkb_get_current_layout ();
++        model = ibus_xkb_get_current_model ();
++        option = ibus_xkb_get_current_option ();
++        g_printf ("layout: %s\n"
++                  "model: %s\n"
++                  "option: %s\n",
++                  layout ? layout : "",
++                  model ? model : "",
++                  option ? option : "");
++        g_free (layout);
++        g_free (model);
++        g_free (option);
++    }
++    if (get_group) {
++        group = ibus_xkb_get_current_group ();
++        g_printf ("group: %d\n", group);
++    }
++
++    ibus_xkb_finit ();
++
++    return 0;
++}
+diff --git a/xkb/xkblayout.xml.in b/xkb/xkblayout.xml.in
+new file mode 100644
+index 0000000..0b5a4dc
+--- /dev/null
++++ b/xkb/xkblayout.xml.in
+@@ -0,0 +1,16 @@
++<?xml version="1.0" encoding="utf-8"?>
++<component>
++        <name>org.freedesktop.IBus.XKB</name>
++        <description>XKB Component</description>
++        <exec>@libexecdir@/ibus-engine-xkb --ibus</exec>
++        <version>0.0.0</version>
++        <author>Takao Fujiwara &lt;takao.fujiwara1@gmail.com&gt;</author>
++        <license>LGPL2.1</license>
++        <homepage>http://code.google.com/p/ibus/</homepage>
++        <textdomain>ibus</textdomain>
++        <observed-paths>
++                <path>@datadir@/ibus/xkb/xkblayoutconfig.xml</path>
++                <path>~/.config/ibus/xkb/xkblayoutconfig.xml</path>
++        </observed-paths>
++        <engines exec="@libexecdir@/ibus-engine-xkb --xml"/>
++</component>
+diff --git a/xkb/xkblayoutconfig.xml.in b/xkb/xkblayoutconfig.xml.in
+new file mode 100644
+index 0000000..b1212d1
+--- /dev/null
++++ b/xkb/xkblayoutconfig.xml.in
+@@ -0,0 +1,6 @@
++<?xml version="1.0" encoding="utf-8"?>
++<xkblayout>
++  <config>
++    <preload_layouts>@XKB_PRELOAD_LAYOUTS@</preload_layouts>
++  </config>
++</xkblayout>
+diff --git a/xkb/xkblib.c b/xkb/xkblib.c
+new file mode 100644
+index 0000000..293cdaf
+--- /dev/null
++++ b/xkb/xkblib.c
+@@ -0,0 +1,327 @@
++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
++/* vim:set et sts=4: */
++/* bus - The Input Bus
++ * Copyright (C) 2011 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2011 Peng Huang <shawn.p.huang@gmail.com>
++ * Copyright (C) 2011 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.
++ */
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <glib.h>
++#include <X11/Xlib.h>
++#include <X11/Xatom.h>
++#include <X11/XKBlib.h>
++#include <stdio.h> /* for XKBrules.h */
++#include <X11/extensions/XKBrules.h>
++#include <X11/extensions/XKBstr.h>
++#include <string.h>
++
++#include "xkblib.h"
++
++#ifndef XKB_RULES_XML_FILE
++#define XKB_RULES_XML_FILE "/usr/share/X11/xkb/rules/evdev.xml"
++#endif
++
++static gchar          **default_layouts;
++static gchar          **default_models;
++static gchar          **default_options;
++static int              default_layout_group;
++
++static Display *
++get_xdisplay (Display *xdisplay)
++{
++    static Display *saved_xdisplay = NULL;
++    if (xdisplay != NULL) {
++        saved_xdisplay = xdisplay;
++    }
++    return saved_xdisplay;
++}
++
++static void
++init_xkb_default_layouts (Display *xdisplay)
++{
++    XkbStateRec state;
++    Atom xkb_rules_name, type;
++    int format;
++    unsigned long l, nitems, bytes_after;
++    unsigned char *prop = NULL;
++
++    xkb_rules_name = XInternAtom (xdisplay, "_XKB_RULES_NAMES", TRUE);
++    if (xkb_rules_name == None) {
++        g_warning ("Could not get XKB rules atom");
++        return;
++    }
++    if (XGetWindowProperty (xdisplay,
++                            XDefaultRootWindow (xdisplay),
++                            xkb_rules_name,
++                            0, 1024, FALSE, XA_STRING,
++                            &type, &format, &nitems, &bytes_after, &prop) != Success) {
++        g_warning ("Could not get X property");
++        return;
++    }
++    if (nitems < 3) {
++        g_warning ("Could not get group layout from X property");
++        return;
++    }
++    for (l = 0; l < 2; l++) {
++        prop += strlen ((const char *) prop) + 1;
++    }
++    if (prop == NULL || *prop == '\0') {
++        g_warning ("No layouts form X property");
++        return;
++    }
++    default_layouts = g_strsplit ((gchar *) prop, ",", -1);
++    prop += strlen ((const char *) prop) + 1;
++    default_models = g_strsplit ((gchar *) prop, ",", -1);
++    prop += strlen ((const char *) prop) + 1;
++    default_options = g_strsplit ((gchar *) prop, ",", -1);
++
++    if (XkbGetState (xdisplay, XkbUseCoreKbd, &state) != Success) {
++        g_warning ("Could not get state");
++        return;
++    }
++    default_layout_group = state.group;
++}
++
++static Bool
++set_xkb_rules (Display *xdisplay,
++               const char *rules_file, const char *model,
++               const char *all_layouts, const char *all_variants,
++               const char *all_options)
++{
++    gchar *rules_path;
++    XkbRF_RulesPtr rules;
++    XkbRF_VarDefsRec rdefs;
++    XkbComponentNamesRec rnames;
++    XkbDescPtr xkb;
++
++    rules_path = g_strdup ("./rules/evdev");
++    rules = XkbRF_Load (rules_path, "C", TRUE, TRUE);
++    if (rules == NULL) {
++        g_return_val_if_fail (XKB_RULES_XML_FILE != NULL, FALSE);
++
++        g_free (rules_path);
++        if (g_str_has_suffix (XKB_RULES_XML_FILE, ".xml")) {
++            rules_path = g_strndup (XKB_RULES_XML_FILE,
++                                    strlen (XKB_RULES_XML_FILE) - 4);
++        } else {
++            rules_path = g_strdup (XKB_RULES_XML_FILE);
++        }
++        rules = XkbRF_Load (rules_path, "C", TRUE, TRUE);
++    }
++    g_return_val_if_fail (rules != NULL, FALSE);
++
++    memset (&rdefs, 0, sizeof (XkbRF_VarDefsRec));
++    memset (&rnames, 0, sizeof (XkbComponentNamesRec));
++    rdefs.model = model ? g_strdup (model) : NULL;
++    rdefs.layout = all_layouts ? g_strdup (all_layouts) : NULL;
++    rdefs.variant = all_variants ? g_strdup (all_variants) : NULL;
++    rdefs.options = all_options ? g_strdup (all_options) : NULL;
++    XkbRF_GetComponents (rules, &rdefs, &rnames);
++    xkb = XkbGetKeyboardByName (xdisplay, XkbUseCoreKbd, &rnames,
++                                XkbGBN_AllComponentsMask,
++                                XkbGBN_AllComponentsMask &
++                                (~XkbGBN_GeometryMask), True);
++    if (!xkb) {
++        g_warning ("Cannot load new keyboard description.");
++        return FALSE;
++    }
++    XkbRF_SetNamesProp (xdisplay, rules_path, &rdefs);
++    g_free (rules_path);
++    g_free (rdefs.model);
++    g_free (rdefs.layout);
++    g_free (rdefs.variant);
++    g_free (rdefs.options);
++
++    return TRUE;
++}
++
++static Bool
++update_xkb_properties (Display *xdisplay,
++                       const char *rules_file, const char *model,
++                       const char *all_layouts, const char *all_variants,
++                       const char *all_options)
++{
++    int len;
++    char *pval;
++    char *next;
++    Atom rules_atom;
++    Window root_window;
++
++    len = (rules_file ? strlen (rules_file) : 0);
++    len += (model ? strlen (model) : 0);
++    len += (all_layouts ? strlen (all_layouts) : 0);
++    len += (all_variants ? strlen (all_variants) : 0);
++    len += (all_options ? strlen (all_options) : 0);
++
++    if (len < 1) {
++        return TRUE;
++    }
++    len += 5; /* trailing NULs */
++
++    rules_atom = XInternAtom (xdisplay, _XKB_RF_NAMES_PROP_ATOM, False);
++    root_window = XDefaultRootWindow (xdisplay);
++    pval = next = g_new0 (char, len + 1);
++    if (!pval) {
++        return TRUE;
++    }
++
++    if (rules_file) {
++        strcpy (next, rules_file);
++        next += strlen (rules_file);
++    }
++    *next++ = '\0';
++    if (model) {
++        strcpy (next, model);
++        next += strlen (model);
++    }
++    *next++ = '\0';
++    if (all_layouts) {
++        strcpy (next, all_layouts);
++        next += strlen (all_layouts);
++    }
++    *next++ = '\0';
++    if (all_variants) {
++        strcpy (next, all_variants);
++        next += strlen (all_variants);
++    }
++    *next++ = '\0';
++    if (all_options) {
++        strcpy (next, all_options);
++        next += strlen (all_options);
++    }
++    *next++ = '\0';
++    if ((next - pval) != len) {
++        g_free (pval);
++        return TRUE;
++    }
++
++    XChangeProperty (xdisplay, root_window,
++                    rules_atom, XA_STRING, 8, PropModeReplace,
++                    (unsigned char *) pval, len);
++    XSync(xdisplay, False);
++
++    return TRUE;
++}
++
++void
++ibus_xkb_init (Display *xdisplay)
++{
++    get_xdisplay (xdisplay);
++    init_xkb_default_layouts (xdisplay);
++}
++
++void
++ibus_xkb_finit (void)
++{
++    g_strfreev (default_layouts);
++    default_layouts = NULL;
++    g_strfreev (default_models);
++    default_models = NULL;
++    g_strfreev (default_options);
++    default_options = NULL;
++}
++
++gchar *
++ibus_xkb_get_current_layout (void)
++{
++    if (default_layouts == NULL) {
++        g_warning ("Your system seems not to support XKB.");
++        return NULL;
++    }
++
++    return g_strjoinv (",", (gchar **) default_layouts);
++}
++
++gchar *
++ibus_xkb_get_current_model (void)
++{
++    if (default_models == NULL) {
++        return NULL;
++    }
++
++    return g_strjoinv (",", (gchar **) default_models);
++}
++
++gchar *
++ibus_xkb_get_current_option (void)
++{
++    if (default_options == NULL) {
++        return NULL;
++    }
++
++    return g_strjoinv (",", (gchar **) default_options);
++}
++
++gboolean
++ibus_xkb_set_layout  (const char *layouts,
++                      const char *variants,
++                      const char *options)
++{
++    Display *xdisplay;
++    gboolean retval;
++    gchar *layouts_line;
++
++    if (default_layouts == NULL) {
++        g_warning ("Your system seems not to support XKB.");
++        return FALSE;
++    }
++
++    if (layouts == NULL || g_strcmp0 (layouts, "default") == 0) {
++        layouts_line = g_strjoinv (",", (gchar **) default_layouts);
++    } else {
++        layouts_line = g_strdup (layouts);
++    }
++
++    xdisplay = get_xdisplay (NULL);
++    retval = set_xkb_rules (xdisplay,
++                            "evdev", "evdev",
++                            layouts_line, variants, options);
++    update_xkb_properties (xdisplay,
++                           "evdev", "evdev",
++                           layouts_line, variants, options);
++    g_free (layouts_line);
++
++    return retval;
++}
++
++int
++ibus_xkb_get_current_group (void)
++{
++    Display *xdisplay = get_xdisplay (NULL);
++    XkbStateRec state;
++
++    if (default_layouts == NULL) {
++        g_warning ("Your system seems not to support XKB.");
++        return 0;
++    }
++
++    if (xdisplay == NULL) {
++        g_warning ("ibus-xkb is not initialized.");
++        return 0;
++    }
++
++    if (XkbGetState (xdisplay, XkbUseCoreKbd, &state) != Success) {
++        g_warning ("Could not get state");
++        return 0;
++    }
++
++    return state.group;
++}
+diff --git a/xkb/xkblib.h b/xkb/xkblib.h
+new file mode 100644
+index 0000000..15e5d18
+--- /dev/null
++++ b/xkb/xkblib.h
+@@ -0,0 +1,41 @@
++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
++/* vim:set et sts=4: */
++/* bus - The Input Bus
++ * Copyright (C) 2011 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2011 Peng Huang <shawn.p.huang@gmail.com>
++ * Copyright (C) 2011 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.
++ */
++#ifndef __XKBLIB_H_
++#define __XKBLIB_H_
++
++#include <X11/Xlib.h>
++
++G_BEGIN_DECLS
++
++void             ibus_xkb_init                   (Display *xdisplay);
++void             ibus_xkb_finit                  (void);
++gchar           *ibus_xkb_get_current_layout     (void);
++gchar           *ibus_xkb_get_current_model      (void);
++gchar           *ibus_xkb_get_current_option     (void);
++gboolean         ibus_xkb_set_layout             (const char *layouts,
++                                                  const char *variants,
++                                                  const char *options);
++int              ibus_xkb_get_current_group      (void);
++
++G_END_DECLS
++#endif
+diff --git a/xkb/xkbxml.c b/xkb/xkbxml.c
+new file mode 100644
+index 0000000..2ce7bcf
+--- /dev/null
++++ b/xkb/xkbxml.c
+@@ -0,0 +1,335 @@
++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
++/* vim:set et sts=4: */
++/* bus - The Input Bus
++ * Copyright (C) 2011 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2011 Peng Huang <shawn.p.huang@gmail.com>
++ * Copyright (C) 2011 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.
++ */
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <glib.h>
++
++#include "xkbxml.h"
++#include "ibus.h"
++
++#define IBUS_XKB_LAYOUT_CONFIG_GET_PRIVATE(o)  \
++   (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_XKB_LAYOUT_CONFIG, IBusXKBLayoutConfigPrivate))
++
++typedef struct _IBusXKBLayoutConfigPrivate IBusXKBLayoutConfigPrivate;
++
++enum {
++    PROP_0,
++    PROP_SYSTEM_CONFIG_FILE,
++};
++
++struct _IBusXKBLayoutConfigPrivate {
++    gchar *system_config_file;
++    GList *preload_layouts;
++};
++
++/* functions prototype */
++static void         ibus_xkb_layout_config_destroy
++                                           (IBusXKBLayoutConfig *xkb_layout_config);
++
++G_DEFINE_TYPE (IBusXKBLayoutConfig, ibus_xkb_layout_config, IBUS_TYPE_OBJECT)
++
++static void
++free_lang_list (GList *list)
++{
++    GList *l = list;
++    while (l) {
++        g_free (l->data);
++        l->data = NULL;
++        l = l->next;
++    }
++    g_list_free (list);
++}
++
++static GList *
++parse_xkblayoutconfig_file (gchar *path)
++{
++    XMLNode *node = NULL;
++    XMLNode *sub_node;
++    XMLNode *sub_sub_node;
++    GList *p;
++    GList *retval = NULL;
++    gchar **array;
++    int i;
++
++    node = ibus_xml_parse_file (path);
++    if (node == NULL) {
++        return NULL;
++    }
++    if (g_strcmp0 (node->name, "xkblayout") != 0) {
++        ibus_xml_free (node);
++        return NULL;
++    }
++    for (p = node->sub_nodes; p != NULL; p = p->next) {
++        sub_node = (XMLNode *) p->data;
++        if (g_strcmp0 (sub_node->name, "config") == 0) {
++            GList *pp;
++            for (pp = sub_node->sub_nodes; pp != NULL; pp = pp->next) {
++                sub_sub_node = (XMLNode *) pp->data;
++                if  (g_strcmp0 (sub_sub_node->name, "preload_layouts") == 0) {
++                    if (sub_sub_node->text != NULL) {
++                        array = g_strsplit ((gchar *) sub_sub_node->text,
++                                            ",", -1);
++                        for (i = 0; array[i]; i++) {
++                            retval = g_list_append (retval, g_strdup (array[i]));
++                        }
++                        g_strfreev (array);
++                        break;
++                    }
++                }
++            }
++        }
++        if (retval != NULL) {
++            break;
++        }
++    }
++
++    ibus_xml_free (node);
++    return retval;
++}
++
++static void
++parse_xkb_layout_config (IBusXKBLayoutConfigPrivate *priv)
++{
++    gchar *basename;
++    gchar *user_config;
++    GList *list = NULL;
++
++    g_return_if_fail (priv->system_config_file != NULL);
++
++    basename = g_path_get_basename (priv->system_config_file);
++    user_config = g_build_filename (g_get_user_config_dir (),
++                                    "ibus", "xkb",
++                                    basename, NULL);
++    g_free (basename);
++    list = parse_xkblayoutconfig_file (user_config);
++    g_free (user_config);
++    if (list) {
++        priv->preload_layouts = list;
++        return;
++    }
++    list = parse_xkblayoutconfig_file (priv->system_config_file);
++    priv->preload_layouts = list;
++}
++
++static void
++ibus_xkb_layout_config_init (IBusXKBLayoutConfig *xkb_layout_config)
++{
++    IBusXKBLayoutConfigPrivate *priv;
++
++    priv = IBUS_XKB_LAYOUT_CONFIG_GET_PRIVATE (xkb_layout_config);
++    priv->system_config_file = NULL;
++    priv->preload_layouts = NULL;
++}
++
++static GObject *
++ibus_xkb_layout_config_constructor (GType type,
++                                    guint n_construct_params,
++                                    GObjectConstructParam *construct_params)
++{
++    GObject *obj;
++    IBusXKBLayoutConfig *xkb_layout_config;
++    IBusXKBLayoutConfigPrivate *priv;
++
++    obj = G_OBJECT_CLASS (ibus_xkb_layout_config_parent_class)->constructor (type, n_construct_params, construct_params);
++    xkb_layout_config = IBUS_XKB_LAYOUT_CONFIG (obj);
++    priv = IBUS_XKB_LAYOUT_CONFIG_GET_PRIVATE (xkb_layout_config);
++    parse_xkb_layout_config (priv);
++
++    return obj;
++}
++
++static void
++ibus_xkb_layout_config_destroy (IBusXKBLayoutConfig *xkb_layout_config)
++{
++    IBusXKBLayoutConfigPrivate *priv;
++
++    g_return_if_fail (xkb_layout_config != NULL);
++
++    priv = IBUS_XKB_LAYOUT_CONFIG_GET_PRIVATE (xkb_layout_config);
++
++    g_free (priv->system_config_file);
++    priv->system_config_file = NULL;
++    free_lang_list (priv->preload_layouts);
++    priv->preload_layouts = NULL;
++}
++
++static void
++ibus_xkb_layout_config_set_property (IBusXKBLayoutConfig *xkb_layout_config,
++                                     guint                prop_id,
++                                     const GValue        *value,
++                                     GParamSpec          *pspec)
++{
++    IBusXKBLayoutConfigPrivate *priv;
++
++    g_return_if_fail (xkb_layout_config != NULL);
++    priv = IBUS_XKB_LAYOUT_CONFIG_GET_PRIVATE (xkb_layout_config);
++
++    switch (prop_id) {
++    case PROP_SYSTEM_CONFIG_FILE:
++        g_assert (priv->system_config_file == NULL);
++        priv->system_config_file = g_strdup (g_value_get_string (value));
++        break;
++    default:
++        G_OBJECT_WARN_INVALID_PROPERTY_ID (xkb_layout_config, prop_id, pspec);
++    }
++}
++
++static void
++ibus_xkb_layout_config_get_property (IBusXKBLayoutConfig *xkb_layout_config,
++                                     guint                prop_id,
++                                     GValue              *value,
++                                     GParamSpec          *pspec)
++{
++    IBusXKBLayoutConfigPrivate *priv;
++
++    g_return_if_fail (xkb_layout_config != NULL);
++    priv = IBUS_XKB_LAYOUT_CONFIG_GET_PRIVATE (xkb_layout_config);
++
++    switch (prop_id) {
++    case PROP_SYSTEM_CONFIG_FILE:
++        g_value_set_string (value, priv->system_config_file);
++        break;
++    default:
++        G_OBJECT_WARN_INVALID_PROPERTY_ID (xkb_layout_config, prop_id, pspec);
++
++    }
++}
++
++static void
++ibus_xkb_layout_config_class_init (IBusXKBLayoutConfigClass *klass)
++{
++    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
++    IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass);
++
++    g_type_class_add_private (klass, sizeof (IBusXKBLayoutConfigPrivate));
++
++    gobject_class->constructor = ibus_xkb_layout_config_constructor;
++    gobject_class->set_property = (GObjectSetPropertyFunc) ibus_xkb_layout_config_set_property;
++    gobject_class->get_property = (GObjectGetPropertyFunc) ibus_xkb_layout_config_get_property;
++    ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_xkb_layout_config_destroy;
++
++    /**
++     * IBusProxy:interface:
++     *
++     * The interface of the proxy object.
++     */
++    g_object_class_install_property (gobject_class,
++                    PROP_SYSTEM_CONFIG_FILE,
++                    g_param_spec_string ("system_config_file",
++                        "system_config_file",
++                        "The system file of xkblayoutconfig",
++                        NULL,
++                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
++}
++
++IBusComponent *
++ibus_xkb_component_new (void)
++{
++    IBusComponent *component;
++
++    component = ibus_component_new ("org.freedesktop.IBus.XKB",
++                                    "XKB Component",
++                                    VERSION,
++                                    "LGPL2.1",
++                                    "Takao Fujiwara <takao.fujiwara1@gmail.com>",
++                                    "http://code.google.com/p/ibus/",
++                                    LIBEXECDIR "/ibus-engine-xkb --ibus",
++                                    GETTEXT_PACKAGE);
++
++    return component;
++}
++
++IBusEngineDesc *
++ibus_xkb_engine_desc_new (const gchar *lang,
++                          const gchar *layout,
++                          const gchar *layout_desc,
++                          const gchar *variant,
++                          const gchar *variant_desc)
++{
++    IBusEngineDesc *engine;
++    gchar *name = NULL;
++    gchar *longname = NULL;
++    gchar *desc = NULL;
++    gchar *engine_layout = NULL;
++
++    g_return_val_if_fail (lang != NULL && layout != NULL, NULL);
++
++    if (variant_desc) {
++        longname = g_strdup (variant_desc);
++    } else if (layout && variant) {
++        longname = g_strdup_printf ("%s - %s", layout, variant);
++    } else if (layout_desc) {
++        longname = g_strdup (layout_desc);
++    } else {
++        longname = g_strdup (layout);
++    }
++    if (variant) {
++        name = g_strdup_printf ("xkb:layout:%s:%s", layout, variant);
++        desc = g_strdup_printf ("XKB %s(%s) keyboard layout", layout, variant);
++        engine_layout = g_strdup_printf ("%s(%s)", layout, variant);
++    } else {
++        name = g_strdup_printf ("xkb:layout:%s", layout);
++        desc = g_strdup_printf ("XKB %s keyboard layout", layout);
++        engine_layout = g_strdup (layout);
++    }
++
++    engine = ibus_engine_desc_new (name,
++                                   longname,
++                                   desc,
++                                   lang,
++                                   "LGPL2.1",
++                                   "Takao Fujiwara <takao.fujiwara1@gmail.com>",
++                                   "ibus-engine",
++                                   engine_layout);
++
++    g_free (name);
++    g_free (longname);
++    g_free (desc);
++    g_free (engine_layout);
++
++    return engine;
++}
++
++IBusXKBLayoutConfig *
++ibus_xkb_layout_config_new (const gchar *system_config_file)
++{
++    IBusXKBLayoutConfig *xkb_layout_config;
++
++    xkb_layout_config = IBUS_XKB_LAYOUT_CONFIG (g_object_new (IBUS_TYPE_XKB_LAYOUT_CONFIG,
++                                                              "system_config_file",
++                                                              system_config_file,
++                                                              NULL));
++    return xkb_layout_config;
++}
++
++const GList *
++ibus_xkb_layout_config_get_preload_layouts (IBusXKBLayoutConfig *xkb_layout_config)
++{
++    IBusXKBLayoutConfigPrivate *priv;
++
++    g_return_val_if_fail (xkb_layout_config != NULL, NULL);
++    priv = IBUS_XKB_LAYOUT_CONFIG_GET_PRIVATE (xkb_layout_config);
++    return (const GList *) priv->preload_layouts;
++}
+diff --git a/xkb/xkbxml.h b/xkb/xkbxml.h
+new file mode 100644
+index 0000000..56811ef
+--- /dev/null
++++ b/xkb/xkbxml.h
+@@ -0,0 +1,110 @@
++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
++/* vim:set et sts=4: */
++/* bus - The Input Bus
++ * Copyright (C) 2011 Takao Fujiwara <takao.fujiwara1@gmail.com>
++ * Copyright (C) 2011 Peng Huang <shawn.p.huang@gmail.com>
++ * Copyright (C) 2011 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.
++ */
++#ifndef __XKBXML_H_
++#define __XKBXML_H_
++
++#include "ibus.h"
++
++/*
++ * Type macros.
++ */
++/* define IBusXKBLayoutConfig macros */
++#define IBUS_TYPE_XKB_LAYOUT_CONFIG                     \
++    (ibus_xkb_layout_config_get_type ())
++#define IBUS_XKB_LAYOUT_CONFIG(obj)                     \
++    (G_TYPE_CHECK_INSTANCE_CAST ((obj), IBUS_TYPE_XKB_LAYOUT_CONFIG, IBusXKBLayoutConfig))
++#define IBUS_XKB_LAYOUT_CONFIG_CLASS(klass)             \
++    (G_TYPE_CHECK_CLASS_CAST ((klass), IBUS_TYPE_XKB_LAYOUT_CONFIG, IBusXKBLayoutConfigClass))
++#define IBUS_IS_XKB_LAYOUT_CONFIG(obj)                  \
++    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IBUS_TYPE_XKB_LAYOUT_CONFIG))
++#define IBUS_IS_XKB_LAYOUT_CONFIG_CLASS(klass)          \
++    (G_TYPE_CHECK_CLASS_TYPE ((klass), IBUS_TYPE_XKB_LAYOUT_CONFIG))
++#define IBUS_XKB_LAYOUT_CONFIG_GET_CLASS(obj)           \
++    (G_TYPE_INSTANCE_GET_CLASS ((obj), IBUS_TYPE_XKB_LAYOUT_CONFIG, IBusXKBLayoutConfigClass))
++
++G_BEGIN_DECLS
++
++typedef struct _IBusXKBLayoutConfig IBusXKBLayoutConfig;
++typedef struct _IBusXKBLayoutConfigClass IBusXKBLayoutConfigClass;
++
++struct _IBusXKBLayoutConfig {
++    IBusObject parent;
++};
++
++struct _IBusXKBLayoutConfigClass {
++    IBusObjectClass parent;
++    /* signals */
++    /*< private >*/
++    /* padding */
++    gpointer pdummy[8];
++};
++
++
++/**
++ * ibus_xkb_component_new:
++ * @returns: A newly allocated IBusComponent.
++ *
++ * New an IBusComponent.
++ */
++IBusComponent   *ibus_xkb_component_new          (void);
++
++/**
++ * ibus_xkb_engine_desc_new:
++ * @lang: Language (e.g. zh, jp) supported by this input method engine.
++ * @layout: Keyboard layout
++ * @layout_desc: Keyboard layout description for engine description
++ * @variant: Keyboard variant
++ * @variant_desc: Keyboard variant description for engine description
++ * @returns: A newly allocated IBusEngineDesc.
++ *
++ * New a IBusEngineDesc.
++ */
++IBusEngineDesc  *ibus_xkb_engine_desc_new        (const gchar *lang,
++                                                  const gchar *layout,
++                                                  const gchar *layout_desc,
++                                                  const gchar *variant,
++                                                  const gchar *variant_desc);
++
++GType            ibus_xkb_layout_config_get_type (void);
++
++/**
++ * ibus_xkb_layout_config_new:
++ * @returns: A newly allocated IBusXKBLayoutConfig
++ *
++ * New an IBusXKBLayoutConfig
++ */
++IBusXKBLayoutConfig *
++                 ibus_xkb_layout_config_new      (const gchar *system_config_file);
++
++/**
++ * ibus_xkb_layout_config_get_preload_layouts:
++ * @xkb_layout_config: An IBusXKBLayoutConfig.
++ * @returns: A const GList
++ *
++ * a const GList
++ */
++const GList *    ibus_xkb_layout_config_get_preload_layouts
++                                                 (IBusXKBLayoutConfig *xkb_layout_config);
++
++G_END_DECLS
++#endif
+-- 
+1.7.5.4
+
diff --git a/ibus-711632-fedora-fallback-icon.patch b/ibus-711632-fedora-fallback-icon.patch
new file mode 100644 (file)
index 0000000..8ad9e9a
--- /dev/null
@@ -0,0 +1,23 @@
+--- ibus/ui/gtk/main.py.orig   2011-04-25 12:33:39.296999692 +0900
++++ ibus/ui/gtk/main.py        2011-06-08 11:53:44.027232075 +0900
+@@ -86,6 +86,9 @@ class UIApplication:
+             pass
+ def launch_panel(replace):
++    settings = gtk.settings_get_default()
++    if settings.get_property('gtk-fallback-icon-theme') == None:
++        settings.set_property('gtk-fallback-icon-theme', 'gnome')
+     # gtk.settings_get_default().props.gtk_theme_name = "/home/phuang/.themes/aud-Default/gtk-2.0/gtkrc"
+     # gtk.rc_parse("./themes/default/gtkrc")
+     UIApplication(replace).run()
+--- ibus/ui/gtk/panel.py.orig  2011-06-08 12:16:40.107577438 +0900
++++ ibus/ui/gtk/panel.py       2011-06-08 12:17:00.440460268 +0900
+@@ -507,7 +507,7 @@ class Panel(ibus.PanelBase):
+             menu = gtk.Menu()
+             item = gtk.ImageMenuItem(_("No input window"))
+             size = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
+-            item.set_image(_icon.IconWidget("gtk-info", size[0]))
++            item.set_image(_icon.IconWidget("gtk-dialog-info", size[0]))
+             menu.add(item)
+             menu.show_all()
+         else:
diff --git a/ibus-HEAD.patch b/ibus-HEAD.patch
new file mode 100644 (file)
index 0000000..fab6c88
--- /dev/null
@@ -0,0 +1,1068 @@
+From 80e5bd0785ca91a70f0b5fe511a3bd8e143d8d05 Mon Sep 17 00:00:00 2001
+From: Takao Fujiwara <takao.fujiwara1@gmail.com>
+Date: Wed, 27 Apr 2011 07:48:50 -0400
+Subject: [PATCH] Fix the zombie process of ibus-gconf when ibus-daemon
+ restarts.
+
+- Fix the typo in bus_dbus_impl_destroy() (dbusimpl.c)
+- Modify bus_server_run() and _ibus_exit() (ibusimpl.c, server.c)
+  bus_ibus_impl_destroy() needs to be called so that waitpid()
+  prevents processes from becoming zombie.
+- Change the declaration of bus_server_quit(). (server.h)
+
+BUG=redhat#697471
+TEST=Linux desktop
+
+Review URL: http://codereview.appspot.com/4440059
+Patch from Takao Fujiwara <takao.fujiwara1@gmail.com>.
+---
+ bus/dbusimpl.c |    5 ++---
+ bus/ibusimpl.c |   40 +---------------------------------------
+ bus/server.c   |   50 +++++++++++++++++++++++++++++++++++++++++++++++++-
+ bus/server.h   |    3 ++-
+ src/ibusbus.c  |    8 +++++++-
+ 5 files changed, 61 insertions(+), 45 deletions(-)
+
+diff --git a/bus/dbusimpl.c b/bus/dbusimpl.c
+index d67a3ce..5616222 100644
+--- a/bus/dbusimpl.c
++++ b/bus/dbusimpl.c
+@@ -577,11 +577,10 @@ bus_dbus_impl_destroy (BusDBusImpl *dbus)
+     dbus->rules = NULL;
+     for (p = dbus->connections; p != NULL; p = p->next) {
+-        GDBusConnection *connection = G_DBUS_CONNECTION (p->data);
++        BusConnection *connection = BUS_CONNECTION (p->data);
+         g_signal_handlers_disconnect_by_func (connection,
+                 bus_dbus_impl_connection_destroy_cb, dbus);
+-        /* FIXME should handle result? */
+-        g_dbus_connection_close (connection, NULL, NULL, NULL);
++        ibus_object_destroy (IBUS_OBJECT (connection));
+         g_object_unref (connection);
+     }
+     g_list_free (dbus->connections);
+diff --git a/bus/ibusimpl.c b/bus/ibusimpl.c
+index a7ae52b..b356b2c 100644
+--- a/bus/ibusimpl.c
++++ b/bus/ibusimpl.c
+@@ -24,9 +24,7 @@
+ #include <sys/types.h>
+ #include <sys/wait.h>
+ #include <signal.h>
+-#include <stdlib.h>
+ #include <locale.h>
+-#include <string.h>
+ #include <strings.h>
+ #include "types.h"
+ #include "ibusimpl.h"
+@@ -937,7 +935,6 @@ bus_ibus_impl_destroy (BusIBusImpl *ibus)
+         ibus->fake_context = NULL;
+     }
+-    bus_server_quit ();
+     IBUS_OBJECT_CLASS (bus_ibus_impl_parent_class)->destroy (IBUS_OBJECT (ibus));
+ }
+@@ -1682,43 +1679,8 @@ _ibus_exit (BusIBusImpl           *ibus,
+     g_dbus_connection_flush_sync (g_dbus_method_invocation_get_connection (invocation),
+                                   NULL,
+                                   NULL);
+-    bus_server_quit ();
+-    if (!restart) {
+-        exit (0);
+-    }
+-    else {
+-        extern gchar **g_argv;
+-        gchar *exe;
+-        gint fd;
+-
+-        exe = g_strdup_printf ("/proc/%d/exe", getpid ());
+-        exe = g_file_read_link (exe, NULL);
+-
+-        if (exe == NULL)
+-            exe = BINDIR "/ibus-daemon";
+-
+-        /* close all fds except stdin, stdout, stderr */
+-        for (fd = 3; fd <= sysconf (_SC_OPEN_MAX); fd ++) {
+-            close (fd);
+-        }
+-
+-        execv (exe, g_argv);
+-
+-        /* If the server binary is replaced while the server is running,
+-         * "readlink /proc/[pid]/exe" might return a path with " (deleted)"
+-         * suffix. */
+-        const gchar suffix[] = " (deleted)";
+-        if (g_str_has_suffix (exe, suffix)) {
+-            exe [strlen (exe) - sizeof (suffix) + 1] = '\0';
+-            execv (exe, g_argv);
+-        }
+-        g_warning ("execv %s failed!", g_argv[0]);
+-        exit (-1);
+-    }
+-
+-    /* should not reach here */
+-    g_assert_not_reached ();
++    bus_server_quit (restart);
+ }
+ /**
+diff --git a/bus/server.c b/bus/server.c
+index d180513..c2ab9a4 100644
+--- a/bus/server.c
++++ b/bus/server.c
+@@ -21,6 +21,8 @@
+  */
+ #include "server.h"
+ #include <gio/gio.h>
++#include <stdlib.h>
++#include <string.h>
+ #include "dbusimpl.h"
+ #include "ibusimpl.h"
+ #include "option.h"
+@@ -30,6 +32,40 @@ static GMainLoop *mainloop = NULL;
+ static BusDBusImpl *dbus = NULL;
+ static BusIBusImpl *ibus = NULL;
+ static gchar *address = NULL;
++static gboolean _restart = FALSE;
++
++static void
++_restart_server (void)
++{
++    extern gchar **g_argv;
++    gchar *exe;
++    gint fd;
++
++    exe = g_strdup_printf ("/proc/%d/exe", getpid ());
++    exe = g_file_read_link (exe, NULL);
++
++    if (exe == NULL)
++        exe = BINDIR "/ibus-daemon";
++
++    /* close all fds except stdin, stdout, stderr */
++    for (fd = 3; fd <= sysconf (_SC_OPEN_MAX); fd ++) {
++        close (fd);
++    }
++
++    _restart = FALSE;
++    execv (exe, g_argv);
++
++    /* If the server binary is replaced while the server is running,
++     * "readlink /proc/[pid]/exe" might return a path with " (deleted)"
++     * suffix. */
++    const gchar suffix[] = " (deleted)";
++    if (g_str_has_suffix (exe, suffix)) {
++        exe [strlen (exe) - sizeof (suffix) + 1] = '\0';
++        execv (exe, g_argv);
++    }
++    g_warning ("execv %s failed!", g_argv[0]);
++    exit (-1);
++}
+ /**
+  * bus_new_connection_cb:
+@@ -112,11 +148,23 @@ bus_server_run (void)
+     mainloop = NULL;
+     g_free (address);
+     address = NULL;
++
++    /* When _ibus_exit() is called, bus_ibus_impl_destroy() needs
++     * to be called so that waitpid() prevents the processes from
++     * becoming the daemons. So we run execv() after
++     * ibus_object_destroy(ibus) is called here. */
++    if (_restart) {
++        _restart_server ();
++
++        /* should not reach here */
++        g_assert_not_reached ();
++    }
+ }
+ void
+-bus_server_quit (void)
++bus_server_quit (gboolean restart)
+ {
++    _restart = restart;
+     if (mainloop)
+         g_main_loop_quit (mainloop);
+ }
+diff --git a/bus/server.h b/bus/server.h
+index 6dfd79a..e1cb3ec 100644
+--- a/bus/server.h
++++ b/bus/server.h
+@@ -43,10 +43,11 @@ void         bus_server_run         (void);
+ /**
+  * bus_server_quit:
++ * @restart: TRUE if ibus-daemon restarts.
+  *
+  * Quit the glib main loop.
+  */
+-void         bus_server_quit        (void);
++void         bus_server_quit        (gboolean restart);
+ /**
+  * bus_server_get_address:
+diff --git a/src/ibusbus.c b/src/ibusbus.c
+index 0e9418e..39ad784 100644
+--- a/src/ibusbus.c
++++ b/src/ibusbus.c
+@@ -236,7 +236,13 @@ _connection_closed_cb (GDBusConnection  *connection,
+                        IBusBus          *bus)
+ {
+     if (error) {
+-        g_warning ("_connection_closed_cb: %s", error->message);
++        /* We replaced g_warning with g_debug here because
++         * currently when ibus-daemon restarts, GTK client calls this and
++         * _g_dbus_worker_do_read_cb() sets the error message:
++         * "Underlying GIOStream returned 0 bytes on an async read"
++         * http://git.gnome.org/browse/glib/tree/gio/gdbusprivate.c#n693
++         * However we think the error message is almost harmless. */
++        g_debug ("_connection_closed_cb: %s", error->message);
+     }
+     g_assert (bus->priv->connection == connection);
+-- 
+1.7.4.4
+
+From d059132885d3c90647f08f3083e39daa9f82b700 Mon Sep 17 00:00:00 2001
+From: Ryo Onodera <onodera@clear-code.com>
+Date: Tue, 17 May 2011 20:07:40 +0900
+Subject: [PATCH] fix wrong forward key event signature
+
+---
+ ibus/engine.py            |    4 ++--
+ ibus/interface/iengine.py |    4 ++--
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/ibus/engine.py b/ibus/engine.py
+index 8cbcee3..fe5dd98 100644
+--- a/ibus/engine.py
++++ b/ibus/engine.py
+@@ -114,8 +114,8 @@ class EngineBase(object.Object):
+         text = serializable.serialize_object(text)
+         return self.__proxy.CommitText(text)
+-    def forward_key_event(self, keyval, state):
+-        return self.__proxy.ForwardKeyEvent(keyval, state)
++    def forward_key_event(self, keyval, keycode, state):
++        return self.__proxy.ForwardKeyEvent(keyval, keycode, state)
+     def update_preedit_text(self, text, cursor_pos, visible, mode=common.IBUS_ENGINE_PREEDIT_CLEAR):
+         text = serializable.serialize_object(text)
+diff --git a/ibus/interface/iengine.py b/ibus/interface/iengine.py
+index 0e0f4ee..9e0d981 100644
+--- a/ibus/interface/iengine.py
++++ b/ibus/interface/iengine.py
+@@ -104,8 +104,8 @@ class IEngine(dbus.service.Object):
+     @signal(signature="v")
+     def CommitText(self, text): pass
+-    @signal(signature="uu")
+-    def ForwardKeyEvent(self, keyval, state): pass
++    @signal(signature="uuu")
++    def ForwardKeyEvent(self, keyval, keycode, state): pass
+     @signal(signature="vubu")
+     def UpdatePreeditText(self, text, cursor_pos, visible, mode): pass
+-- 
+1.7.4.4
+
+From d3e750eab6db7035f494fcdb328b87b2923e33a2 Mon Sep 17 00:00:00 2001
+From: Yusuke Sato <yusukes@chromium.org>
+Date: Wed, 1 Jun 2011 23:37:14 +0900
+Subject: [PATCH] Send the new capabilities to ibus-daemon in
+ ibus_im_context_set_use_preedit.
+
+BUG=none
+TEST=none
+
+Review URL: http://codereview.appspot.com/4529103
+---
+ client/gtk2/ibusimcontext.c |    2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c
+index ebae09d..4a894b0 100644
+--- a/client/gtk2/ibusimcontext.c
++++ b/client/gtk2/ibusimcontext.c
+@@ -942,6 +942,8 @@ ibus_im_context_set_use_preedit (GtkIMContext *context, gboolean use_preedit)
+         else {
+             ibusimcontext->caps &= ~IBUS_CAP_PREEDIT_TEXT;
+         }
++        ibus_input_context_set_capabilities (ibusimcontext->ibuscontext,
++                                             ibusimcontext->caps);
+     }
+     gtk_im_context_set_use_preedit (ibusimcontext->slave, use_preedit);
+ }
+-- 
+1.7.4.4
+
+From 52425daa537a32bed1781958e1ef62dbf199ad8b Mon Sep 17 00:00:00 2001
+From: Peng Huang <shawn.p.huang@gmail.com>
+Date: Mon, 6 Jun 2011 09:30:27 -0400
+Subject: [PATCH] Fix Python input context binding.
+
+Export "forward-key-event" and "delete-surrounding-text" signals to Python; clear __needs_surrounding_text property on "enabled" and "disabled" signals.
+
+BUG=none
+TEST=briefly tested, at least I don't see any regression
+
+Review URL: http://codereview.appspot.com/4437062
+---
+ ibus/inputcontext.py            |   26 +++++++++++++++++++++++++-
+ ibus/interface/iinputcontext.py |    3 +++
+ 2 files changed, 28 insertions(+), 1 deletions(-)
+
+diff --git a/ibus/inputcontext.py b/ibus/inputcontext.py
+index d143727..ceeb56d 100644
+--- a/ibus/inputcontext.py
++++ b/ibus/inputcontext.py
+@@ -116,6 +116,16 @@ class InputContext(object.Object):
+             gobject.TYPE_NONE,
+             ()
+         ),
++        "forward-key-event" : (
++            gobject.SIGNAL_RUN_LAST,
++            gobject.TYPE_NONE,
++            (gobject.TYPE_UINT, gobject.TYPE_UINT, gobject.TYPE_UINT)
++        ),
++        "delete-surrounding-text" : (
++            gobject.SIGNAL_RUN_LAST,
++            gobject.TYPE_NONE,
++            (gobject.TYPE_INT, gobject.TYPE_UINT)
++        ),
+     }
+     def __init__(self, bus, path, watch_signals=False):
+@@ -142,8 +152,14 @@ class InputContext(object.Object):
+         self.__signal_matches.append(m)
+         m = self.__context.connect_to_signal("RequireSurroundingText", self.__require_surrounding_text_cb)
+         self.__signal_matches.append(m)
++        m = self.__context.connect_to_signal("Enabled", self.__enabled_cb)
++        self.__signal_matches.append(m)
++        m = self.__context.connect_to_signal("Disabled", self.__disabled_cb)
++        self.__signal_matches.append(m)
+-        m = self.__context.connect_to_signal("Enabled",             lambda *args: self.emit("enabled"))
++        m = self.__context.connect_to_signal("ForwardKeyEvent",            lambda *args: self.emit("forward-key-event", *args))
++        self.__signal_matches.append(m)
++        m = self.__context.connect_to_signal("DeleteSurroundingText",            lambda *args: self.emit("delete-surrounding-text", *args))
+         self.__signal_matches.append(m)
+         m = self.__context.connect_to_signal("Disabled",            lambda *args: self.emit("disabled"))
+         self.__signal_matches.append(m)
+@@ -168,6 +184,14 @@ class InputContext(object.Object):
+         m = self.__context.connect_to_signal("CursorDownLookupTable", lambda *args: self.emit("cursor-down-lookup-table"))
+         self.__signal_matches.append(m)
++    def __enabled_cb(self, *args):
++        self.__needs_surrounding_text = False
++        self.emit("enabled")
++
++    def __disabled_cb(self, *args):
++        self.__needs_surrounding_text = False
++        self.emit("disabled")
++
+     def __commit_text_cb(self, *args):
+         text = serializable.deserialize_object(args[0])
+         self.emit("commit-text", text)
+diff --git a/ibus/interface/iinputcontext.py b/ibus/interface/iinputcontext.py
+index 2db1c9b..1d3cd2a 100644
+--- a/ibus/interface/iinputcontext.py
++++ b/ibus/interface/iinputcontext.py
+@@ -95,6 +95,9 @@ class IInputContext(dbus.service.Object):
+     @signal(signature="uuu")
+     def ForwardKeyEvent(self, keyval, keycode, state): pass
++    @signal(signature="iu")
++    def DeleteSurroundingText(self, offset_from_cursor, nchars): pass
++
+     @signal(signature="vub")
+     def UpdatePreeditText(self, text, cursor_pos, visible): pass
+-- 
+1.7.4.4
+
+From 59ce675e335e599ed18d74ab8849b9a5fe75d4be Mon Sep 17 00:00:00 2001
+From: Peng Huang <shawn.p.huang@gmail.com>
+Date: Mon, 13 Jun 2011 13:18:29 -0400
+Subject: [PATCH] Fix some race condition between idle and timeout
+ events. Also fix a memory leak.
+
+BUG=http://crosbug.com/16387
+TEST=Linux desktop
+
+Review URL: http://codereview.appspot.com/4568072
+---
+ bus/engineproxy.c |   46 ++++++++++++++++++++++++++++++----------------
+ 1 files changed, 30 insertions(+), 16 deletions(-)
+
+diff --git a/bus/engineproxy.c b/bus/engineproxy.c
+index 0c6f45d..f74af12 100644
+--- a/bus/engineproxy.c
++++ b/bus/engineproxy.c
+@@ -603,7 +603,8 @@ bus_engine_proxy_new_internal (const gchar     *path,
+     g_assert (IBUS_IS_ENGINE_DESC (desc));
+     g_assert (G_IS_DBUS_CONNECTION (connection));
+-
++    GDBusProxyFlags flags = G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
++                            G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES;
+     BusEngineProxy *engine =
+         (BusEngineProxy *) g_initable_new (BUS_TYPE_ENGINE_PROXY,
+                                            NULL,
+@@ -613,7 +614,7 @@ bus_engine_proxy_new_internal (const gchar     *path,
+                                            "g-interface-name",  IBUS_INTERFACE_ENGINE,
+                                            "g-object-path",     path,
+                                            "g-default-timeout", g_gdbus_timeout,
+-                                           "g-flags",           G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
++                                           "g-flags",           flags,
+                                            NULL);
+     const gchar *layout = ibus_engine_desc_get_layout (desc);
+     if (layout != NULL && layout[0] != '\0') {
+@@ -638,24 +639,33 @@ static void
+ engine_proxy_new_data_free (EngineProxyNewData *data)
+ {
+     if (data->simple != NULL) {
+-        if (data->handler_id != 0)
+-            g_signal_handler_disconnect (data->component, data->handler_id);
+         g_object_unref (data->simple);
+     }
+-    if (data->component != NULL)
++    if (data->desc != NULL) {
++        g_object_unref (data->desc);
++    }
++
++    if (data->component != NULL) {
++        if (data->handler_id != 0) {
++            g_signal_handler_disconnect (data->component, data->handler_id);
++        }
+         g_object_unref (data->component);
++    }
+-    if (data->factory != NULL)
++    if (data->factory != NULL) {
+         g_object_unref (data->factory);
++    }
+-    if (data->timeout_id != 0)
++    if (data->timeout_id != 0) {
+         g_source_remove (data->timeout_id);
++    }
+     if (data->cancellable != NULL) {
+-        if (data->cancelled_handler_id != 0)
++        if (data->cancelled_handler_id != 0) {
+             g_cancellable_disconnect (data->cancellable,
+-                data->cancelled_handler_id);
++                                      data->cancelled_handler_id);
++        }
+         g_object_unref (data->cancellable);
+     }
+@@ -772,7 +782,8 @@ timeout_cb (EngineProxyNewData *data)
+ /**
+  * cancelled_cb:
+  *
+- * A callback function to be called when someone calls g_cancellable_cancel() for the cancellable object for bus_engine_proxy_new.
++ * A callback function to be called when someone calls g_cancellable_cancel()
++ * for the cancellable object for bus_engine_proxy_new.
+  * Call the GAsyncReadyCallback.
+  */
+ static gboolean
+@@ -793,8 +804,12 @@ static void
+ cancelled_cb (GCancellable       *cancellable,
+               EngineProxyNewData *data)
+ {
+-    /* Cancel the bus_engine_proxy_new() in idle to avoid deadlock */
+-    g_idle_add ((GSourceFunc) cancelled_idle_cb, data);
++    /* Cancel the bus_engine_proxy_new() in idle to avoid deadlock.
++     * And use HIGH priority to avoid timeout event happening before
++     * idle callback. */
++    g_idle_add_full (G_PRIORITY_HIGH,
++                    (GSourceFunc) cancelled_idle_cb,
++                    data, NULL);
+ }
+ void
+@@ -831,13 +846,12 @@ bus_engine_proxy_new (IBusEngineDesc      *desc,
+     data->simple = simple;
+     data->timeout = timeout;
+-    g_object_set_data ((GObject *)data->simple, "EngineProxyNewData", data);
+-
+     data->factory = bus_component_get_factory (data->component);
+     if (data->factory == NULL) {
+-        /* The factory is not ready yet. Create the factory first, and wait for the "notify::factory" signal.
+-         * In the handler of "notify::factory", we'll create the engine proxy. */
++        /* The factory is not ready yet. Create the factory first, and wait for
++         * the "notify::factory" signal. In the handler of "notify::factory",
++         * we'll create the engine proxy. */
+         data->handler_id = g_signal_connect (data->component,
+                                              "notify::factory",
+                                              G_CALLBACK (notify_factory_cb),
+-- 
+1.7.4.4
+
+From fc9dedec30f724e91e7b3bb9111177e96b58ee43 Mon Sep 17 00:00:00 2001
+From: Peng Huang <shawn.p.huang@gmail.com>
+Date: Wed, 15 Jun 2011 10:38:17 -0400
+Subject: [PATCH] Add IBUS_ERROR domain and reply IBUS_ERROR_NO_ENGINE
+ in org.freedesktop.IBus.InputContext.GetEngine
+
+BUG=None
+TEST=Manually
+
+Review URL: http://codereview.appspot.com/4528140
+---
+ bus/inputcontext.c                |    7 ++++-
+ src/Makefile.am                   |    2 +
+ src/ibus.h                        |    1 +
+ src/ibuserror.c                   |   41 +++++++++++++++++++++++++++++++++
+ src/ibuserror.h                   |   46 +++++++++++++++++++++++++++++++++++++
+ src/ibusinputcontext.c            |   15 +++++++++--
+ src/ibusshare.c                   |    1 +
+ src/ibustypes.h                   |    9 +++++++
+ src/tests/ibus-gi-inputcontext.py |   34 +++++++++++++++++++++++++++
+ 9 files changed, 151 insertions(+), 5 deletions(-)
+ create mode 100644 src/ibuserror.c
+ create mode 100644 src/ibuserror.h
+ create mode 100755 src/tests/ibus-gi-inputcontext.py
+
+diff --git a/bus/inputcontext.c b/bus/inputcontext.c
+index bad90ec..1567c5f 100644
+--- a/bus/inputcontext.c
++++ b/bus/inputcontext.c
+@@ -1040,8 +1040,11 @@ _ic_get_engine (BusInputContext       *context,
+                         g_variant_new ("(v)", ibus_serializable_serialize ((IBusSerializable *)desc)));
+     }
+     else {
+-        g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+-                        "Input context does not have engine.");
++        g_dbus_method_invocation_return_error (
++                invocation,
++                IBUS_ERROR,
++                IBUS_ERROR_NO_ENGINE,
++                "Input context does not have engine.");
+     }
+ }
+diff --git a/src/Makefile.am b/src/Makefile.am
+index 632fc72..a53bd23 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -70,6 +70,7 @@ ibus_sources =              \
+     ibusservice.c           \
+     ibusfactory.c           \
+     ibusengine.c            \
++    ibuserror.c                               \
+     ibustext.c              \
+     ibuskeymap.c            \
+     ibusattribute.c         \
+@@ -114,6 +115,7 @@ ibus_headers =              \
+     ibusservice.h           \
+     ibusfactory.h           \
+     ibusengine.h            \
++    ibuserror.h                               \
+     ibustext.h              \
+     ibuskeymap.h            \
+     ibusattribute.h         \
+diff --git a/src/ibus.h b/src/ibus.h
+index 8df7160..c408f3d 100644
+--- a/src/ibus.h
++++ b/src/ibus.h
+@@ -35,6 +35,7 @@
+ #include <ibusservice.h>
+ #include <ibusfactory.h>
+ #include <ibusengine.h>
++#include <ibuserror.h>
+ #include <ibusproperty.h>
+ #include <ibusproplist.h>
+ #include <ibuslookuptable.h>
+diff --git a/src/ibuserror.c b/src/ibuserror.c
+new file mode 100644
+index 0000000..c50c164
+--- /dev/null
++++ b/src/ibuserror.c
+@@ -0,0 +1,41 @@
++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
++/* vim:set et sts=4: */
++/* ibus - The Input Bus
++ * Copyright (C) 2011 Peng Huang <shawn.p.huang@gmail.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., 59 Temple Place - Suite 330,
++ * Boston, MA 02111-1307, USA.
++ */
++
++#include "ibuserror.h"
++
++#include <gio/gio.h>
++#include "ibustypes.h"
++
++static const GDBusErrorEntry ibus_error_entries[] =
++{
++    { IBUS_ERROR_NO_ENGINE,         "org.freedesktop.IBus.Error.NoEngine" },
++};
++
++GQuark
++ibus_error_quark (void)
++{
++    static volatile gsize quark_volatile = 0;
++    g_dbus_error_register_error_domain ("ibus-error-quark",
++                                        &quark_volatile,
++                                        ibus_error_entries,
++                                        G_N_ELEMENTS (ibus_error_entries));
++    return (GQuark) quark_volatile;
++}
+diff --git a/src/ibuserror.h b/src/ibuserror.h
+new file mode 100644
+index 0000000..75c64b9
+--- /dev/null
++++ b/src/ibuserror.h
+@@ -0,0 +1,46 @@
++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
++/* vim:set et sts=4: */
++/* ibus - The Input Bus
++ * Copyright (C) 2011 Peng Huang <shawn.p.huang@gmail.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., 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
++
++/**
++ * SECTION: ibusshare
++ * @short_description: Shared utility functions and definition.
++ * @stability: Stable
++ *
++ * This file defines some utility functions and definition
++ * which are shared among ibus component and services.
++ */
++
++#ifndef __IBUS_ERROR_H_
++#define __IBUS_ERROR_H_
++
++#include <glib.h>
++
++G_BEGIN_DECLS
++
++#define IBUS_ERROR ibus_error_quark()
++GQuark ibus_error_quark (void);
++
++G_END_DECLS
++#endif
+diff --git a/src/ibusinputcontext.c b/src/ibusinputcontext.c
+index e6f97e8..78d454e 100644
+--- a/src/ibusinputcontext.c
++++ b/src/ibusinputcontext.c
+@@ -27,6 +27,7 @@
+ #include "ibusattribute.h"
+ #include "ibuslookuptable.h"
+ #include "ibusproplist.h"
++#include "ibuserror.h"
+ #define IBUS_INPUT_CONTEXT_GET_PRIVATE(o)  \
+    (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_INPUT_CONTEXT, IBusInputContextPrivate))
+@@ -1164,7 +1165,7 @@ IBusEngineDesc *
+ ibus_input_context_get_engine (IBusInputContext *context)
+ {
+     g_assert (IBUS_IS_INPUT_CONTEXT (context));
+-    GVariant *result;
++    GVariant *result = NULL;
+     GError *error = NULL;
+     result = g_dbus_proxy_call_sync ((GDBusProxy *) context,
+                                      "GetEngine",               /* method_name */
+@@ -1174,9 +1175,17 @@ ibus_input_context_get_engine (IBusInputContext *context)
+                                      NULL,                      /* cancellable */
+                                      &error                     /* error */
+                                      );
+-
+     if (result == NULL) {
+-        g_warning ("%s.GetEngine: %s", IBUS_INTERFACE_INPUT_CONTEXT, error->message);
++        if (g_error_matches (error, IBUS_ERROR, IBUS_ERROR_NO_ENGINE)) {
++            g_debug ("%s.GetEngine: %s",
++                     IBUS_INTERFACE_INPUT_CONTEXT,
++                     error->message);
++        }
++        else {
++            g_warning ("%s.GetEngine: %s",
++                       IBUS_INTERFACE_INPUT_CONTEXT,
++                       error->message);
++        }
+         g_error_free (error);
+         return NULL;
+     }
+diff --git a/src/ibusshare.c b/src/ibusshare.c
+index 1b8ae2a..19f9f65 100644
+--- a/src/ibusshare.c
++++ b/src/ibusshare.c
+@@ -318,6 +318,7 @@ void
+ ibus_init (void)
+ {
+     g_type_init ();
++    IBUS_ERROR;
+     IBUS_TYPE_TEXT;
+     IBUS_TYPE_ATTRIBUTE;
+     IBUS_TYPE_ATTR_LIST;
+diff --git a/src/ibustypes.h b/src/ibustypes.h
+index 6a31847..8146719 100644
+--- a/src/ibustypes.h
++++ b/src/ibustypes.h
+@@ -177,6 +177,15 @@ typedef enum {
+ } IBusBusRequestNameReply;
+ /**
++ * IBusError:
++ * @IBUS_ERROR_NO_ENGINE:
++ * There is no engine associated with input context.
++ */
++typedef enum {
++    IBUS_ERROR_NO_ENGINE,
++} IBusError;
++
++/**
+  * IBusRectangle:
+  * @x: x coordinate.
+  * @y: y coordinate.
+diff --git a/src/tests/ibus-gi-inputcontext.py b/src/tests/ibus-gi-inputcontext.py
+new file mode 100755
+index 0000000..80fb97b
+--- /dev/null
++++ b/src/tests/ibus-gi-inputcontext.py
+@@ -0,0 +1,34 @@
++#!/usr/bin/env python
++# vim:set et sts=4 sw=4:
++#
++# ibus - The Input Bus
++#
++# Copyright (c) 2011 Peng Huang <shawn.p.huang@gmail.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 program; if not, write to the
++# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++# Boston, MA  02111-1307  USA
++
++
++import glib
++import gio
++from gi.repository import IBus
++IBus.init()
++main = glib.MainLoop()
++bus = IBus.Bus()
++ic = bus.create_input_context("ibus-test")
++ic.get_engine()
++ic.get_engine()
++ic.get_engine()
++ic.get_engine()
+-- 
+1.7.4.4
+
+From aec97ac090980dfcd7eeef55c1755f6cd3f87a01 Mon Sep 17 00:00:00 2001
+From: Daiki Ueno <daiki.ueno@gmail.com>
+Date: Sat, 18 Jun 2011 00:03:07 -0400
+Subject: [PATCH] Simplify surrounding-text initialization.
+
+Currently the immodule tries to retrieve surrounding-text unconditionally
+on focus_in and enabled.  These calls could be eliminated if engine were
+able to proclaim that it will need surrounding-text.
+
+This patch extends ibus_engine_get_surrounding_text() to allow this.
+Engines that need surrounding-text are expected to have:
+
+    /* Indicate we will use surrounding-text. */
+    ibus_engine_get_surrounding_text (engine, NULL, NULL);
+
+in their enable() method.  This would work because enable() is called before
+SetCapabilities DBus call.
+
+BUG=none
+TEST=manually with ibus-m17n, with the above change.
+
+Review URL: http://codereview.appspot.com/4613043
+Patch from Daiki Ueno <daiki.ueno@gmail.com>.
+---
+ client/gtk2/ibusimcontext.c |   23 +++++++++--------------
+ src/ibusengine.c            |   10 ++++++----
+ src/ibusengine.h            |    9 +++++++--
+ 3 files changed, 22 insertions(+), 20 deletions(-)
+
+diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c
+index ec764ef..a4e7a16 100644
+--- a/client/gtk2/ibusimcontext.c
++++ b/client/gtk2/ibusimcontext.c
+@@ -147,8 +147,7 @@ static gboolean _slave_delete_surroundin
+                                              gint                offset_from_cursor,
+                                              guint               nchars,
+                                              IBusIMContext      *context);
+-static void     _request_surrounding_text   (IBusIMContext      *context,
+-                                             gboolean            force);
++static void     _request_surrounding_text   (IBusIMContext      *context);
+ static void     _create_fake_input_context  (void);
+@@ -263,17 +262,13 @@ _process_key_event_done (GObject      *o
+ /* emit "retrieve-surrounding" glib signal of GtkIMContext, if
+  * context->caps has IBUS_CAP_SURROUNDING_TEXT and the current IBus
+  * engine needs surrounding-text.
+- *
+- * if "force" is TRUE, emit the signal regardless of whether the
+- * engine needs surrounding-text.
+  */
+ static void
+-_request_surrounding_text (IBusIMContext *context, gboolean force)
++_request_surrounding_text (IBusIMContext *context)
+ {
+-    if (context->enable &&
++    if (context && context->enable &&
+         (context->caps & IBUS_CAP_SURROUNDING_TEXT) != 0 &&
+-        (force ||
+-         ibus_input_context_needs_surrounding_text (context->ibuscontext))) {
++        ibus_input_context_needs_surrounding_text (context->ibuscontext)) {
+         gboolean return_value;
+         IDEBUG ("requesting surrounding text");
+         g_signal_emit (context, _signal_retrieve_surrounding_id, 0,
+@@ -368,9 +363,8 @@ _key_snooper_cb (GtkWidget   *widget,
+     } while (0);
+-    _request_surrounding_text (ibusimcontext, FALSE);
+-
+     if (ibusimcontext != NULL) {
++        _request_surrounding_text (ibusimcontext);
+         ibusimcontext->time = event->time;
+     }
+@@ -680,7 +674,7 @@ ibus_im_context_filter_keypress (GtkIMCo
+         if (ibusimcontext->client_window == NULL && event->window != NULL)
+             gtk_im_context_set_client_window ((GtkIMContext *)ibusimcontext, event->window);
+-        _request_surrounding_text (ibusimcontext, FALSE);
++        _request_surrounding_text (ibusimcontext);
+         if (ibusimcontext != NULL) {
+             ibusimcontext->time = event->time;
+@@ -763,7 +757,7 @@ ibus_im_context_focus_in (GtkIMContext *
+     /* retrieve the initial surrounding-text (regardless of whether
+      * the current IBus engine needs surrounding-text) */
+-    _request_surrounding_text (ibusimcontext, TRUE);
++    _request_surrounding_text (ibusimcontext);
+     g_object_add_weak_pointer ((GObject *) context,
+                                (gpointer *) &_focus_im_context);
+@@ -1000,7 +996,7 @@ _ibus_context_commit_text_cb (IBusInputC
+     g_signal_emit (ibusimcontext, _signal_commit_id, 0, text->text);
+-    _request_surrounding_text (ibusimcontext, FALSE);
++    _request_surrounding_text (ibusimcontext);
+ }
+ static gboolean
+@@ -1296,7 +1292,7 @@ _ibus_context_show_preedit_text_cb (IBus
+     g_signal_emit (ibusimcontext, _signal_preedit_start_id, 0);
+     g_signal_emit (ibusimcontext, _signal_preedit_changed_id, 0);
+-    _request_surrounding_text (ibusimcontext, FALSE);
++    _request_surrounding_text (ibusimcontext);
+ }
+ static void
+@@ -1323,7 +1319,7 @@ _ibus_context_enabled_cb (IBusInputConte
+     /* retrieve the initial surrounding-text (regardless of whether
+      * the current IBus engine needs surrounding-text) */
+-    _request_surrounding_text (ibusimcontext, TRUE);
++    _request_surrounding_text (ibusimcontext);
+ }
+ static void
+diff --git a/src/ibusengine.c b/src/ibusengine.c
+index f545bef..620d07f 100644
+--- a/src/ibusengine.c
++++ b/src/ibusengine.c
+@@ -1382,13 +1382,15 @@ ibus_engine_get_surrounding_text (IBusEngine   *engine,
+     IBusEnginePrivate *priv;
+     g_return_if_fail (IBUS_IS_ENGINE (engine));
+-    g_return_if_fail (text != NULL);
+-    g_return_if_fail (cursor_pos != NULL);
++    g_return_if_fail ((text != NULL && cursor_pos != NULL) ||
++                      (text == NULL && cursor_pos == NULL));
+     priv = IBUS_ENGINE_GET_PRIVATE (engine);
+-    *text = g_object_ref (priv->surrounding_text);
+-    *cursor_pos = priv->surrounding_cursor_pos;
++    if (text && cursor_pos) {
++        *text = g_object_ref (priv->surrounding_text);
++        *cursor_pos = priv->surrounding_cursor_pos;
++    }
+     /* tell the client that this engine will utilize surrounding-text
+      * feature, which causes periodical update.  Note that the client
+diff --git a/src/ibusengine.h b/src/ibusengine.h
+index 29b8f1d..6da342a 100644
+--- a/src/ibusengine.h
++++ b/src/ibusengine.h
+@@ -407,11 +407,16 @@ void ibus_engine_delete_surrounding_text(IBusEngine         *engine,
+ /**
+  * ibus_engine_get_surrounding_text:
+  * @engine: An IBusEngine.
+- * @text: Location to store surrounding text.
+- * @cursor_pos: Cursor position in characters in @text.
++ * @text: (allow-none): Location to store surrounding text.
++ * @cursor_pos: (allow-none): Cursor position in characters in @text.
+  *
+  * Get surrounding text.
+  *
++ * It is also used to tell the input-context that the engine will
++ * utilize surrounding-text.  In that case, it must be called in
++ * #IBusEngine::enable handler, with both @text and @cursor set to
++ * %NULL.
++ *
+  * @see_also #IBusEngine::set-surrounding-text
+  */
+ void ibus_engine_get_surrounding_text(IBusEngine         *engine,
+-- 
+1.7.4.4
+
+From a25187a315e9dfbb36a3e4a4f8e96f18e2cc6e0d Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Tue, 5 Jul 2011 12:15:55 +0900
+Subject: [PATCH] Fix SEGV in ibus_keymap_lookup_keysym
+
+---
+ bus/engineproxy.c |    2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/bus/engineproxy.c b/bus/engineproxy.c
+index 95e9e0b..a49d6fd 100644
+--- a/bus/engineproxy.c
++++ b/bus/engineproxy.c
+@@ -907,7 +907,7 @@ bus_engine_proxy_process_key_event (BusEngineProxy      *engine,
+         if (keymap == NULL)
+             keymap = BUS_DEFAULT_KEYMAP;
+         if (keymap != NULL) {
+-            guint t = ibus_keymap_lookup_keysym (engine->keymap, keycode, state);
++            guint t = ibus_keymap_lookup_keysym (keymap, keycode, state);
+             if (t != IBUS_VoidSymbol) {
+                 keyval = t;
+             }
+-- 
+1.7.5.4
+
+From 83d4b3ac538320bfb8e872dd9282ca5bbedf4652 Mon Sep 17 00:00:00 2001
+From: Peng Huang <shawn.p.huang@gmail.com>
+Date: Mon, 4 Jul 2011 03:27:23 +0800
+Subject: [PATCH] Fix BusEngineProxy instance leak.
+
+BUG=none
+TEST=manually with / without global-engine setting
+
+Review URL: http://codereview.appspot.com/4662043
+---
+ bus/engineproxy.c  |    9 ---------
+ bus/inputcontext.c |    5 +----
+ 2 files changed, 1 insertions(+), 13 deletions(-)
+
+diff --git a/bus/engineproxy.c b/bus/engineproxy.c
+index f74af12..95e9e0b 100644
+--- a/bus/engineproxy.c
++++ b/bus/engineproxy.c
+@@ -397,15 +397,6 @@ bus_engine_proxy_real_destroy (IBusProxy *proxy)
+ {
+     BusEngineProxy *engine = (BusEngineProxy *)proxy;
+-    g_dbus_proxy_call ((GDBusProxy *)proxy,
+-                       "org.freedesktop.IBus.Service.Destroy",
+-                       NULL,
+-                       G_DBUS_CALL_FLAGS_NONE,
+-                       -1,
+-                       NULL,
+-                       NULL,
+-                       NULL);
+-
+     if (engine->desc) {
+         g_object_unref (engine->desc);
+         engine->desc = NULL;
+diff --git a/bus/inputcontext.c b/bus/inputcontext.c
+index 1567c5f..2164e7c 100644
+--- a/bus/inputcontext.c
++++ b/bus/inputcontext.c
+@@ -1020,8 +1020,6 @@ _ic_set_engine (BusInputContext       *context,
+                             NULL,
+                             (GAsyncReadyCallback)_ic_set_engine_done,
+                             invocation);
+-
+-    g_object_unref (desc);
+ }
+ /**
+@@ -2091,7 +2089,6 @@ bus_input_context_enable (BusInputContext *context)
+                             NULL, /* we do not cancel the call. */
+                             NULL, /* use the default callback function. */
+                             NULL);
+-            g_object_unref (desc);
+         }
+     }
+@@ -2192,7 +2189,6 @@ bus_input_context_unset_engine (BusInputContext *context)
+         for (i = 0; engine_signals[i].name != NULL; i++) {
+             g_signal_handlers_disconnect_by_func (context->engine, engine_signals[i].callback, context);
+         }
+-        /* Do not destroy the engine anymore, because of global engine feature */
+         g_object_unref (context->engine);
+         context->engine = NULL;
+     }
+@@ -2291,6 +2287,7 @@ new_engine_cb (GObject             *obj,
+         }
+         else {
+             bus_input_context_set_engine (data->context, engine);
++            g_object_unref (engine);
+             bus_input_context_enable (data->context);
+             g_simple_async_result_set_op_res_gboolean (data->simple, TRUE);
+         }
+-- 
+1.7.5.4
+
diff --git a/ibus-xx-bridge-hotkey.patch b/ibus-xx-bridge-hotkey.patch
new file mode 100644 (file)
index 0000000..7346ace
--- /dev/null
@@ -0,0 +1,1257 @@
+From 2ae147c6d4b15b4aa40a915e86d04fb795d3024b Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Tue, 5 Jul 2011 13:51:11 +0900
+Subject: [PATCH] Add a bridge hotkey which use prev-next engines instead
+ of on-off.
+
+---
+ bus/Makefile.am            |   20 ++-
+ bus/ibusimpl.c             |  357 ++++++++++++++++++++++++++++++++++++--------
+ bus/registry.c             |   35 +++++
+ configure.ac               |   31 ++++
+ data/Makefile.am           |    6 +-
+ data/ibus.schemas.in       |  286 -----------------------------------
+ data/ibus.schemas.in.in    |  286 +++++++++++++++++++++++++++++++++++
+ ibus/_config.py.in         |    6 +
+ ibus/inputcontext.py       |    4 +
+ setup/enginecombobox.py    |    3 +
+ setup/enginetreeview.py    |   16 ++-
+ src/Makefile.am            |   18 ++-
+ src/ibusbus.c              |   12 ++
+ src/ibusbus.h              |   18 +++
+ src/ibushotkey.c           |   11 ++
+ src/ibushotkey.h           |   11 ++
+ ui/gtk/panel.py            |  189 +++++++++++++++++++++---
+ xkb/Makefile.am            |    2 +
+ xkb/ibus-engine-xkb-main.c |    8 +
+ xkb/xkbxml.c               |    8 +-
+ 20 files changed, 939 insertions(+), 388 deletions(-)
+ delete mode 100644 data/ibus.schemas.in
+ create mode 100644 data/ibus.schemas.in.in
+
+diff --git a/bus/Makefile.am b/bus/Makefile.am
+index b356b2c..f9417ad 100644
+--- a/bus/Makefile.am
++++ b/bus/Makefile.am
+@@ -29,15 +29,17 @@ INCLUDES =                \
+       -I$(top_builddir)/src \
+       $(NULL)
+-AM_CFLAGS =                        \
+-      @GLIB2_CFLAGS@                 \
+-      @GIO2_CFLAGS@                  \
+-      @GTHREAD2_CFLAGS@              \
+-      -DG_LOG_DOMAIN=\"IBUS\"        \
+-      -DPKGDATADIR=\"$(pkgdatadir)\" \
+-      -DLIBEXECDIR=\"$(libexecdir)\" \
+-      -DBINDIR=\"@bindir@\"          \
+-      $(INCLUDES)                    \
++AM_CFLAGS =                                                            \
++      @GLIB2_CFLAGS@                                                 \
++      @GIO2_CFLAGS@                                                  \
++      @GTHREAD2_CFLAGS@                                              \
++      -DG_LOG_DOMAIN=\"IBUS\"                                        \
++      -DPKGDATADIR=\"$(pkgdatadir)\"                                 \
++      -DLIBEXECDIR=\"$(libexecdir)\"                                 \
++      -DBINDIR=\"@bindir@\"                                          \
++      -DUSE_BRIDGE_HOTKEY=$(USE_BRIDGE_HOTKEY)                       \
++      -DDEFAULT_BRIDGE_ENGINE_NAME=\"$(DEFAULT_BRIDGE_ENGINE_NAME)\" \
++      $(INCLUDES)                                                    \
+       $(NULL)
+ AM_LDADD =                  \
+       @GOBJECT2_LIBS@         \
+diff --git a/bus/ibusimpl.c b/bus/ibusimpl.c
+index b356b2c..ffea37a 100644
+--- a/bus/ibusimpl.c
++++ b/bus/ibusimpl.c
+@@ -20,6 +20,10 @@
+  * Boston, MA 02111-1307, USA.
+  */
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
+ #include <unistd.h>
+ #include <sys/types.h>
+ #include <sys/wait.h>
+@@ -79,6 +83,10 @@ struct _BusIBusImpl {
+     /* engine-specific hotkeys */
+     IBusHotkeyProfile *engines_hotkey_profile;
+     GHashTable      *hotkey_to_engines_map;
++
++#if USE_BRIDGE_HOTKEY
++    IBusEngineDesc *prev_hotkey_engine;
++#endif
+ };
+ struct _BusIBusImplClass {
+@@ -99,6 +107,8 @@ enum {
+ static guint            _signals[LAST_SIGNAL] = { 0 };
+ */
++static gchar           *_bridge_trigger_keys = NULL;
++
+ /* functions prototype */
+ static void      bus_ibus_impl_destroy           (BusIBusImpl        *ibus);
+ static void      bus_ibus_impl_service_method_call
+@@ -285,6 +295,30 @@ _panel_destroy_cb (BusPanelProxy *panel,
+     g_object_unref (panel);
+ }
++static IBusEngineDesc *
++_find_engine_desc_by_name (BusIBusImpl *ibus,
++                           const gchar *engine_name)
++{
++    IBusEngineDesc *desc = NULL;
++    GList *p;
++
++    /* find engine in registered engine list */
++    for (p = ibus->register_engine_list; p != NULL; p = p->next) {
++        desc = (IBusEngineDesc *) p->data;
++        if (g_strcmp0 (ibus_engine_desc_get_name (desc), engine_name) == 0)
++            return desc;
++    }
++
++    /* find engine in preload engine list */
++    for (p = ibus->engine_list; p != NULL; p = p->next) {
++        desc = (IBusEngineDesc *) p->data;
++        if (g_strcmp0 (ibus_engine_desc_get_name (desc), engine_name) == 0)
++            return desc;
++    }
++
++    return NULL;
++}
++
+ static void
+ _config_set_value_done (GObject      *object,
+                         GAsyncResult *res,
+@@ -475,8 +509,21 @@ _set_preload_engines (BusIBusImpl *ibus,
+         g_variant_unref (value);
+     }
+-    g_list_foreach (engine_list, (GFunc) g_object_ref, NULL);
+     ibus->engine_list = engine_list;
++#if USE_BRIDGE_HOTKEY
++    if (_find_engine_desc_by_name (ibus, DEFAULT_BRIDGE_ENGINE_NAME) == NULL) {
++        IBusEngineDesc *engine = bus_registry_find_engine_by_name (ibus->registry,
++                                                                   DEFAULT_BRIDGE_ENGINE_NAME);
++        g_assert (engine != NULL);
++        if (g_list_length (engine_list) > 0) {
++            engine_list = g_list_insert (engine_list, engine, 1);
++        } else {
++            engine_list = g_list_append (engine_list, engine);
++       }
++        ibus->engine_list = engine_list;
++    }
++#endif
++    g_list_foreach (engine_list, (GFunc) g_object_ref, NULL);
+     if (ibus->engine_list) {
+         BusComponent *component = bus_component_from_engine_desc ((IBusEngineDesc *) ibus->engine_list->data);
+@@ -562,6 +609,43 @@ bus_ibus_impl_set_hotkey (BusIBusImpl *i
+ }
++#if USE_BRIDGE_HOTKEY
++static void
++bus_ibus_impl_set_bridge_trigger_keys (BusIBusImpl *ibus,
++                                       GQuark       hotkey,
++                                       GVariant    *value)
++{
++    g_assert (BUS_IS_IBUS_IMPL (ibus));
++
++    ibus_hotkey_profile_remove_hotkey_by_event (ibus->hotkey_profile, hotkey);
++
++    if (value == NULL) {
++        return;
++    }
++
++    GVariantIter iter;
++    g_variant_iter_init (&iter, value);
++    const gchar *str = NULL;
++
++    g_free (_bridge_trigger_keys);
++    _bridge_trigger_keys = NULL;
++
++    while (g_variant_iter_loop (&iter,"&s", &str)) {
++       if (str != NULL) {
++           gchar *tmp =NULL;
++
++           if (_bridge_trigger_keys) {
++               tmp = g_strdup_printf ("%s,%s", _bridge_trigger_keys, str);
++           } else {
++               tmp = g_strdup (str);
++           }
++           g_free (_bridge_trigger_keys);
++           _bridge_trigger_keys = tmp;
++       }
++    }
++}
++#endif
++
+ /**
+  * bus_ibus_impl_set_trigger:
+  *
+@@ -573,7 +657,11 @@ bus_ibus_impl_set_trigger (BusIBusImpl *
+ {
+     GQuark hotkey = g_quark_from_static_string ("trigger");
+     if (value != NULL) {
++#if USE_BRIDGE_HOTKEY
++        bus_ibus_impl_set_bridge_trigger_keys (ibus, hotkey, value);
++#else
+         bus_ibus_impl_set_hotkey (ibus, hotkey, value);
++#endif
+     }
+ #ifndef OS_CHROMEOS
+     else {
+@@ -1182,28 +1270,110 @@ _ibus_get_address (BusIBusImpl          
+                                            g_variant_new ("(s)", bus_server_get_address ()));
+ }
+-static IBusEngineDesc *
+-_find_engine_desc_by_name (BusIBusImpl *ibus,
+-                           const gchar *engine_name)
+-{
+-    IBusEngineDesc *desc = NULL;
+-    GList *p;
++/**
++ * _foreach_remove_engine_hotkey:
++ *
++ * Remove the engine-specific hot key of the engine, and update ibus->engines_hotkey_profile.
++ */
++gboolean
++_foreach_remove_engine_hotkey (gpointer        key,
++                               gpointer        value,
++                               gpointer        data)
++{
++    GQuark event = GPOINTER_TO_UINT (value);
++    struct _impl_and_desc {
++        BusIBusImpl    *ibus;
++        IBusEngineDesc *desc;
++    } *id = (struct _impl_and_desc *) data;
++    BusIBusImpl *ibus = id->ibus;
++    IBusEngineDesc *desc = id->desc;
++    GList *engine_list;
+-    /* find engine in registered engine list */
+-    for (p = ibus->register_engine_list; p != NULL; p = p->next) {
+-        desc = (IBusEngineDesc *) p->data;
+-        if (g_strcmp0 (ibus_engine_desc_get_name (desc), engine_name) == 0)
+-            return desc;
++    g_assert (ibus != NULL);
++    g_assert (desc != NULL);
++
++    if (event == 0) {
++        return FALSE;
+     }
+-    /* find engine in preload engine list */
+-    for (p = ibus->engine_list; p != NULL; p = p->next) {
+-        desc = (IBusEngineDesc *) p->data;
+-        if (g_strcmp0 (ibus_engine_desc_get_name (desc), engine_name) == 0)
+-            return desc;
++    engine_list = g_hash_table_lookup (ibus->hotkey_to_engines_map,
++                                       GUINT_TO_POINTER (event));
++
++    /* As we will rebuild the engines hotkey map whenever an engine was
++     * added or removed, we don't need to hold a reference of the engine
++     * here. */
++    if (engine_list && g_list_find (engine_list, desc) != NULL) {
++        engine_list = g_list_remove (engine_list, desc);
+     }
+-    return NULL;
++    /* We need to steal the value before adding it back, otherwise it will
++     * be destroyed. */
++    g_hash_table_steal (ibus->hotkey_to_engines_map, GUINT_TO_POINTER (event));
++
++    if (engine_list != NULL) {
++        g_hash_table_insert (ibus->hotkey_to_engines_map,
++                             GUINT_TO_POINTER (event), engine_list);
++    }
++
++    return FALSE;
++}
++
++/**
++ * _add_engine_hotkey_with_hotkeys:
++ *
++ * Check the engine-specific hot key of the engine, and update ibus->engines_hotkey_profile.
++ */
++static void
++_add_engine_hotkey_with_hotkeys (IBusEngineDesc *engine,
++                                 BusIBusImpl    *ibus,
++                                 const gchar    *hotkeys)
++{
++    gchar **hotkey_list;
++    gchar **p;
++    gchar *hotkey;
++    GList *engine_list;
++
++    GQuark event;
++    guint keyval;
++    guint modifiers;
++
++    g_assert (engine != NULL);
++    g_assert (hotkeys && *hotkeys);
++
++    hotkey_list = g_strsplit_set (hotkeys, ";,", 0);
++
++    for (p = hotkey_list; p && *p; ++p) {
++        hotkey = g_strstrip (*p);
++        if (!*hotkey || !ibus_key_event_from_string (hotkey, &keyval, &modifiers)) {
++            continue;
++        }
++
++        /* If the hotkey already exists, we won't need to add it again. */
++        event = ibus_hotkey_profile_lookup_hotkey (ibus->engines_hotkey_profile,
++                                                   keyval, modifiers);
++        if (event == 0) {
++            event = g_quark_from_string (hotkey);
++            ibus_hotkey_profile_add_hotkey (ibus->engines_hotkey_profile,
++                                            keyval, modifiers, event);
++        }
++
++        engine_list = g_hash_table_lookup (ibus->hotkey_to_engines_map,
++                                           GUINT_TO_POINTER (event));
++
++        /* As we will rebuild the engines hotkey map whenever an engine was
++         * added or removed, we don't need to hold a reference of the engine
++         * here. */
++        engine_list = g_list_append (engine_list, engine);
++
++        /* We need to steal the value before adding it back, otherwise it will
++         * be destroyed. */
++        g_hash_table_steal (ibus->hotkey_to_engines_map, GUINT_TO_POINTER (event));
++
++        g_hash_table_insert (ibus->hotkey_to_engines_map,
++                             GUINT_TO_POINTER (event), engine_list);
++    }
++
++    g_strfreev (hotkey_list);
+ }
+ /**
+@@ -1216,7 +1386,61 @@ _context_request_engine_cb (BusInputCont
+                             const gchar     *engine_name,
+                             BusIBusImpl     *ibus)
+ {
+-    return bus_ibus_impl_get_engine_desc (ibus, engine_name);
++    IBusEngineDesc *desc = bus_ibus_impl_get_engine_desc (ibus, engine_name);
++    struct _impl_and_desc {
++        BusIBusImpl    *ibus;
++        IBusEngineDesc *desc;
++    } id = {ibus, desc};
++
++#if USE_BRIDGE_HOTKEY
++    IBusEngineDesc *current_desc = NULL;
++
++    if (context) {
++        BusEngineProxy *engine = bus_input_context_get_engine (context);
++        if (engine != NULL) {
++            current_desc = bus_engine_proxy_get_desc (engine);
++        }
++    }
++
++    if (current_desc) {
++        ibus->prev_hotkey_engine = current_desc;
++    }
++
++    if (current_desc != NULL && desc != NULL &&
++        g_strcmp0 (ibus_engine_desc_get_name (current_desc),
++                   ibus_engine_desc_get_name (desc)) != 0 &&
++        g_strcmp0 (ibus_engine_desc_get_name (desc),
++                   DEFAULT_BRIDGE_ENGINE_NAME) == 0) {
++        const gchar *hotkeys = NULL;
++
++        /* If the user customized the trigger key, the trigger key is used for
++         * any IBus engines. */
++        if (_bridge_trigger_keys != NULL &&
++            *_bridge_trigger_keys != '\0' &&
++            g_strcmp0 (_bridge_trigger_keys, "Control+space") != 0) {
++
++            hotkeys = (const gchar *) _bridge_trigger_keys;
++        } else {
++            hotkeys = ibus_engine_desc_get_hotkeys (current_desc);
++        }
++
++        /* If engine hotkeys are not defined in the compose xml file, IBus trigger
++         * keys are used. */
++        if (!hotkeys || !*hotkeys) {
++            hotkeys = (const gchar *) _bridge_trigger_keys;
++        }
++
++        if (!hotkeys || !*hotkeys) {
++            return desc;
++        }
++
++        ibus_hotkey_profile_foreach_hotkey (ibus->engines_hotkey_profile,
++                                            _foreach_remove_engine_hotkey,
++                                            &id);
++        _add_engine_hotkey_with_hotkeys (desc, ibus, hotkeys);
++    }
++#endif
++    return desc;
+ }
+ /**
+@@ -2353,6 +2577,15 @@ bus_ibus_impl_filter_keyboard_shortcuts 
+         g_assert (new_engine_desc);
++#if USE_BRIDGE_HOTKEY
++        /* If the previous engine is not included in engine_list,
++         * this enables a new engine instead of toggling the engines
++         * so should not enable the previous engine. */
++        if (ibus->prev_hotkey_engine &&
++            g_list_find (engine_list, ibus->prev_hotkey_engine) != NULL) {
++            new_engine_desc = ibus->prev_hotkey_engine;
++        }
++#else
+         /* Find out what engine we should switch to. If the current engine has
+          * the same hotkey, then we should switch to the next engine with the
+          * same hotkey in the list. Otherwise, we just switch to the first
+@@ -2364,8 +2597,32 @@ bus_ibus_impl_filter_keyboard_shortcuts 
+                 break;
+             }
+         }
++#endif
++#if USE_BRIDGE_HOTKEY
++        if (current_engine_desc != new_engine_desc ||
++            g_strcmp0 (ibus_engine_desc_get_name (new_engine_desc),
++                       DEFAULT_BRIDGE_ENGINE_NAME) == 0) {
++            ibus->prev_hotkey_engine = current_engine_desc;
++
++            /* If the previous engine is not included in engine_list and
++             * the current engine is the defualt bridge engine,
++             * the current engine is also not included in engine_list.
++             * So the engine is added here. */
++            if (current_engine_desc != NULL &&
++                g_list_find (engine_list, current_engine_desc) == NULL &&
++                g_strcmp0 (ibus_engine_desc_get_name (current_engine_desc),
++                           DEFAULT_BRIDGE_ENGINE_NAME) == 0) {
++                engine_list = g_list_append (engine_list, current_engine_desc);
++
++                g_hash_table_steal (ibus->hotkey_to_engines_map,
++                                    GUINT_TO_POINTER (event));
++                g_hash_table_insert (ibus->hotkey_to_engines_map,
++                                     GUINT_TO_POINTER (event), engine_list);
++            }
++#else
+         if (current_engine_desc != new_engine_desc) {
++#endif
+             bus_ibus_impl_set_context_engine_from_desc (ibus, context, new_engine_desc);
+         }
+@@ -2470,59 +2727,39 @@ static void
+ _add_engine_hotkey (IBusEngineDesc *engine, BusIBusImpl *ibus)
+ {
+     const gchar *hotkeys;
+-    gchar **hotkey_list;
+-    gchar **p;
+-    gchar *hotkey;
+-    GList *engine_list;
+-
+-    GQuark event;
+-    guint keyval;
+-    guint modifiers;
+     if (!engine) {
+         return;
+     }
++#if USE_BRIDGE_HOTKEY
++    /* If the user customized the trigger key, the trigger key is used for
++     * any IBus engines. */
++    if (_bridge_trigger_keys != NULL &&
++        *_bridge_trigger_keys != '\0' &&
++        g_strcmp0 (_bridge_trigger_keys, "Control+space") != 0) {
++
++        hotkeys = (const gchar *) _bridge_trigger_keys;
++    } else {
++        hotkeys = ibus_engine_desc_get_hotkeys (engine);
++    }
++#else
+     hotkeys = ibus_engine_desc_get_hotkeys (engine);
++#endif
++#if USE_BRIDGE_HOTKEY
++    /* If engine hotkeys are not defined in the compose xml file, IBus trigger
++     * keys are used. */
+     if (!hotkeys || !*hotkeys) {
+-        return;
++        hotkeys = (const gchar *) _bridge_trigger_keys;
+     }
++#endif
+-    hotkey_list = g_strsplit_set (hotkeys, ";,", 0);
+-
+-    for (p = hotkey_list; p && *p; ++p) {
+-        hotkey = g_strstrip (*p);
+-        if (!*hotkey || !ibus_key_event_from_string (hotkey, &keyval, &modifiers)) {
+-            continue;
+-        }
+-
+-        /* If the hotkey already exists, we won't need to add it again. */
+-        event = ibus_hotkey_profile_lookup_hotkey (ibus->engines_hotkey_profile,
+-                                                   keyval, modifiers);
+-        if (event == 0) {
+-            event = g_quark_from_string (hotkey);
+-            ibus_hotkey_profile_add_hotkey (ibus->engines_hotkey_profile,
+-                                            keyval, modifiers, event);
+-        }
+-
+-        engine_list = g_hash_table_lookup (ibus->hotkey_to_engines_map,
+-                                           GUINT_TO_POINTER (event));
+-
+-        /* As we will rebuild the engines hotkey map whenever an engine was
+-         * added or removed, we don't need to hold a reference of the engine
+-         * here. */
+-        engine_list = g_list_append (engine_list, engine);
+-
+-        /* We need to steal the value before adding it back, otherwise it will
+-         * be destroyed. */
+-        g_hash_table_steal (ibus->hotkey_to_engines_map, GUINT_TO_POINTER (event));
+-
+-        g_hash_table_insert (ibus->hotkey_to_engines_map,
+-                             GUINT_TO_POINTER (event), engine_list);
++    if (!hotkeys || !*hotkeys) {
++        return;
+     }
+-    g_strfreev (hotkey_list);
++    _add_engine_hotkey_with_hotkeys (engine, ibus, hotkeys);
+ }
+ /**
+diff --git a/bus/registry.c b/bus/registry.c
+index bc6680d..f47f727 100644
+--- a/bus/registry.c
++++ b/bus/registry.c
+@@ -19,6 +19,11 @@
+  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  * Boston, MA 02111-1307, USA.
+  */
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
+ #include "registry.h"
+ #include <glib/gstdio.h>
+ #include <gio/gio.h>
+@@ -101,6 +106,9 @@ bus_registry_init (BusRegistry *registry)
+     registry->observed_paths = NULL;
+     registry->components = NULL;
+     registry->engine_table = g_hash_table_new (g_str_hash, g_str_equal);
++#if USE_BRIDGE_HOTKEY
++    gboolean has_default_engine = FALSE;
++#endif
+ #ifdef G_THREADS_ENABLED
+     /* If glib supports thread, we'll create a thread to monitor changes in IME
+@@ -145,12 +153,39 @@ bus_registry_init (BusRegistry *registry)
+         GList *p1;
+         for (p1 = engines; p1 != NULL; p1 = p1->next) {
+             IBusEngineDesc *desc = (IBusEngineDesc *) p1->data;
++#if USE_BRIDGE_HOTKEY
++            if (g_strcmp0 (ibus_engine_desc_get_name (desc),
++                           DEFAULT_BRIDGE_ENGINE_NAME) == 0) {
++                has_default_engine = TRUE;
++            }
++#endif
+             g_hash_table_insert (registry->engine_table,
+                                  (gpointer) ibus_engine_desc_get_name (desc),
+                                  desc);
+         }
+         g_list_free (engines);
+     }
++
++#if USE_BRIDGE_HOTKEY
++    if (has_default_engine == FALSE) {
++        bus_registry_remove_all (registry);
++        bus_registry_load (registry);
++        bus_registry_save_cache (registry);
++
++        for (p = registry->components; p != NULL; p = p->next) {
++            BusComponent *comp = (BusComponent *) p->data;
++            GList *engines = bus_component_get_engines (comp);
++            GList *p1;
++            for (p1 = engines; p1 != NULL; p1 = p1->next) {
++                IBusEngineDesc *desc = (IBusEngineDesc *) p1->data;
++                g_hash_table_insert (registry->engine_table,
++                                     (gpointer) ibus_engine_desc_get_name (desc),
++                                     desc);
++            }
++            g_list_free (engines);
++        }
++    }
++#endif
+ }
+ static void
+diff --git a/configure.ac b/configure.ac
+index 85e5e30..3ada2f8 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -438,6 +438,34 @@ else
+     enable_surrounding_text="no (disabled, use --enable-surrounding-text to enable)"
+ fi
++# option for bridge hotkey
++AC_ARG_ENABLE(bridge-hotkey,
++    AS_HELP_STRING([--enable-bridge-hotkey],
++        [Enable bridge hotkey instead of ON/OFF hotkey]),
++    [enable_bridge_hotkey=$enableval],
++    [enable_bridge_hotkey=no]
++)
++
++if test x"$enable_bridge_hotkey" = x"yes"; then
++    USE_BRIDGE_HOTKEY=1
++    TRIGGER_HOTKEYS="Control+space"
++else
++    USE_BRIDGE_HOTKEY=0
++    TRIGGER_HOTKEYS="Control+space,Zenkaku_Hankaku,Alt+Kanji,Alt+grave,Hangul,Alt+Release+Alt_R"
++    enable_bridge_hotkey="no (disabled, use --enable-bridge-hotkey to enable)"
++fi
++AC_SUBST(USE_BRIDGE_HOTKEY)
++AC_SUBST(TRIGGER_HOTKEYS)
++
++# define default bridge engine name
++AC_ARG_WITH(bridge-engine,
++    AS_HELP_STRING([--with-bridge-engine[=bridge_engine_name]],
++        [Set bridge engine name in IM bridge hotkey. (default: xkb:layout:default)]),
++    [DEFAULT_BRIDGE_ENGINE_NAME=$with_bridge_engine],
++    [DEFAULT_BRIDGE_ENGINE_NAME="xkb:layout:default"]
++)
++AC_SUBST(DEFAULT_BRIDGE_ENGINE_NAME)
++
+ # check iso-codes
+ PKG_CHECK_MODULES(ISOCODES, [
+     iso-codes
+@@ -464,6 +492,7 @@ bus/Makefile
+ util/Makefile
+ util/IMdkit/Makefile
+ data/Makefile
++data/ibus.schemas.in
+ data/icons/Makefile
+ data/keymaps/Makefile
+ docs/Makefile
+@@ -512,5 +541,7 @@ Build options:
+   No snooper regexes        "$NO_SNOOPER_APPS"
+   Panel icon                "$IBUS_ICON_KEYBOARD"
+   Enable surrounding-text   $enable_surrounding_text
++  Enable bridge hotkey      $enable_bridge_hotkey
++  Default bridge engine     $DEFAULT_BRIDGE_ENGINE_NAME
+ ])
+diff --git a/data/Makefile.am b/data/Makefile.am
+index ba9f4bb..a909e5b 100644
+--- a/data/Makefile.am
++++ b/data/Makefile.am
+@@ -26,7 +26,8 @@ SUBDIRS = \
+       $(NULL)
+ schemasdir = $(GCONF_SCHEMA_FILE_DIR)
+-schemas_in_files = ibus.schemas.in
++schemas_in_in_files = ibus.schemas.in.in
++schemas_in_files = $(schemas_in_in_files:.schemas.in.in=.schemas.in)
+ schemas_DATA = $(schemas_in_files:.schemas.in=.schemas)
+ @INTLTOOL_SCHEMAS_RULE@
+@@ -41,11 +42,12 @@ if GCONF_SCHEMAS_INSTALL
+ endif
+ EXTRA_DIST = \
+-      $(schemas_in_files) \
++      $(schemas_in_in_files) \
+       $(NULL)
+ DISTCLEANFILES = \
+       $(schemas_DATA) \
++      $(schemas_in_files) \
+       $(NULL)
+ -include $(top_srcdir)/git.mk
+diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in.in
+index 7ca4899..42d9297
+--- a/data/ibus.schemas.in
++++ b/data/ibus.schemas.in.in
+@@ -31,7 +31,7 @@
+       <owner>ibus</owner>
+       <type>list</type>
+       <list_type>string</list_type>
+-      <default>[Control+space,Zenkaku_Hankaku,Alt+Kanji,Alt+grave,Hangul,Alt+Release+Alt_R]</default>
++      <default>[@TRIGGER_HOTKEYS@]</default>
+       <locale name="C">
+         <short>Trigger shortcut keys</short>
+           <long>The shortcut keys for turning input method on or off</long>
+diff --git a/ibus/_config.py.in b/ibus/_config.py.in
+index a830136..4c3c980 100644
+--- a/ibus/_config.py.in
++++ b/ibus/_config.py.in
+@@ -25,6 +25,8 @@ __all__ = (
+     "get_copyright",
+     "get_license",
+     "get_ICON_KEYBOARD",
++    "use_bridge_hotkey",
++    "DEFAULT_BRIDGE_ENGINE_NAME",
+     "ISOCODES_PREFIX",
+     "_"
+ )
+@@ -51,4 +53,8 @@ def get_ICON_KEYBOARD():
+         icon = 'ibus-keyboard'
+     return icon
++def use_bridge_hotkey():
++    return True if @USE_BRIDGE_HOTKEY@ == 1 else False
++
++DEFAULT_BRIDGE_ENGINE_NAME='@DEFAULT_BRIDGE_ENGINE_NAME@'
+ ISOCODES_PREFIX='@ISOCODES_PREFIX@'
+diff --git a/ibus/inputcontext.py b/ibus/inputcontext.py
+index ceeb56d..2694fa3 100644
+--- a/ibus/inputcontext.py
++++ b/ibus/inputcontext.py
+@@ -28,6 +28,7 @@ import sys
+ import gobject
+ import dbus
+ import dbus.lowlevel
++import _config
+ import object
+ import common
+ import serializable
+@@ -282,6 +283,9 @@ class InputContext(object.Object):
+     def set_engine(self, engine):
+         return self.__context.SetEngine(engine.name)
++    def set_bridge_engine(self):
++        return self.__context.SetEngine(_config.DEFAULT_BRIDGE_ENGINE_NAME)
++
+     def introspect(self):
+         return self.__context.Introspect()
+diff --git a/setup/enginecombobox.py b/setup/enginecombobox.py
+index 7383177..247facc 100644
+--- a/setup/enginecombobox.py
++++ b/setup/enginecombobox.py
+@@ -64,6 +64,9 @@ class EngineComboBox(gtk.ComboBox):
+         self.__model.set(iter1, 0, 0)
+         lang = {}
+         for e in engines:
++            if ibus.use_bridge_hotkey() and \
++               e.name == ibus.DEFAULT_BRIDGE_ENGINE_NAME:
++                continue
+             l = ibus.get_language_name(e.language)
+             if l not in lang:
+                 lang[l] = []
+diff --git a/setup/enginetreeview.py b/setup/enginetreeview.py
+index f620361..0e50ad5 100644
+--- a/setup/enginetreeview.py
++++ b/setup/enginetreeview.py
+@@ -162,6 +162,14 @@ class EngineTreeView(gtk.TreeView):
+             return row[0]
+         elif property.name == "engines":
+             engines = [ r[0] for r in self.__model if r[0] != None]
++            for i, e in enumerate(self.__engines):
++                if ibus.use_bridge_hotkey() and \
++                   e.name == ibus.DEFAULT_BRIDGE_ENGINE_NAME:
++                    if i < len(engines):
++                        engines.insert(i, e)
++                    else:
++                        engines.append(e)
++                    break
+             return engines
+         else:
+             raise AttributeError, 'unknown property %s' % property.name
+@@ -172,8 +180,12 @@ class EngineTreeView(gtk.TreeView):
+         for e in engines:
+             if e in self.__engines:
+                 continue
+-            iter = self.__model.append(None)
+-            self.__model.set(iter, 0, e)
++            if ibus.use_bridge_hotkey() and \
++               e.name == ibus.DEFAULT_BRIDGE_ENGINE_NAME:
++                pass
++            else:
++                iter = self.__model.append(None)
++                self.__model.set(iter, 0, e)
+             self.__engines.add(e)
+         self.__emit_changed()
+diff --git a/src/Makefile.am b/src/Makefile.am
+index 6454522..319df3c 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -38,14 +38,16 @@ INTROSPECTION_GIRS =
+ CLEANFILES =
+ # C preprocessor flags
+-AM_CPPFLAGS =                                   \
+-    -DG_LOG_DOMAIN=\"IBUS\"                     \
+-    @GLIB2_CFLAGS@                              \
+-    @GOBJECT2_CFLAGS@                           \
+-    @GIO2_CFLAGS@                               \
+-    -DIBUS_DATA_DIR=\"$(pkgdatadir)\"           \
+-    -DIBUS_COMPILATION                          \
+-    -DISOCODES_PREFIX=\"$(ISOCODES_PREFIX)\"    \
++AM_CPPFLAGS =                                                           \
++    -DG_LOG_DOMAIN=\"IBUS\"                                             \
++    @GLIB2_CFLAGS@                                                      \
++    @GOBJECT2_CFLAGS@                                                   \
++    @GIO2_CFLAGS@                                                       \
++    -DIBUS_DATA_DIR=\"$(pkgdatadir)\"                                   \
++    -DIBUS_COMPILATION                                                  \
++    -DISOCODES_PREFIX=\"$(ISOCODES_PREFIX)\"                            \
++    -DUSE_BRIDGE_HOTKEY=$(USE_BRIDGE_HOTKEY)                            \
++    -DDEFAULT_BRIDGE_ENGINE_NAME=\"$(DEFAULT_BRIDGE_ENGINE_NAME)\"      \
+     $(NULL)
+ # ibus library
+diff --git a/src/ibusbus.c b/src/ibusbus.c
+index 39ad784..abc4331 100644
+--- a/src/ibusbus.c
++++ b/src/ibusbus.c
+@@ -1902,3 +1902,15 @@ ibus_bus_call_async (IBusBus            *bus,
+                             (GAsyncReadyCallback) ibus_bus_call_async_done,
+                             simple);
+ }
++
++gboolean
++ibus_bus_use_bridge_hotkey (IBusBus *bus)
++{
++    return (USE_BRIDGE_HOTKEY == 1) ? TRUE : FALSE;
++}
++
++gchar *
++ibus_bus_get_default_bridge_engine_name (IBusBus *bus)
++{
++    return g_strdup (DEFAULT_BRIDGE_ENGINE_NAME);
++}
+diff --git a/src/ibusbus.h b/src/ibusbus.h
+index 77d3916..f560671 100644
+--- a/src/ibusbus.h
++++ b/src/ibusbus.h
+@@ -971,5 +971,23 @@ void         ibus_bus_set_watch_ibus_signal
+  */
+ IBusConfig  *ibus_bus_get_config        (IBusBus        *bus);
++/**
++ * ibus_bus_use_bridge_hotkey:
++ * @bus: An #IBusBus.
++ * @returns: %TRUE if @bus use bridge hotkey, %FALSE otherwise.
++ *
++ * Return %TRUE if @bus use bridge hotkey.
++ */
++gboolean     ibus_bus_use_bridge_hotkey (IBusBus *bus);
++
++/**
++ * ibus_bus_get_default_bridge_engine_name:
++ * @bus: An #IBusBus.
++ * @returns: A default bridge engine name.
++ *
++ * Return A default bridge engine name. Need to be freed.
++ */
++gchar       *ibus_bus_get_default_bridge_engine_name (IBusBus *bus);
++
+ G_END_DECLS
+ #endif
+diff --git a/src/ibushotkey.c b/src/ibushotkey.c
+index 32f8338..bef7dfc 100644
+--- a/src/ibushotkey.c
++++ b/src/ibushotkey.c
+@@ -562,3 +562,14 @@ ibus_hotkey_profile_lookup_hotkey (IBusHotkeyProfile *profile,
+     return (GQuark) GPOINTER_TO_UINT (g_tree_lookup (priv->hotkeys, &hotkey));
+ }
++
++void
++ibus_hotkey_profile_foreach_hotkey (IBusHotkeyProfile *profile,
++                                    GTraverseFunc      func,
++                                    gpointer           user_data)
++{
++    IBusHotkeyProfilePrivate *priv;
++    priv = IBUS_HOTKEY_PROFILE_GET_PRIVATE (profile);
++
++    g_tree_foreach (priv->hotkeys, func, user_data);
++}
+diff --git a/src/ibushotkey.h b/src/ibushotkey.h
+index 9a341f6..92ec6af 100644
+--- a/src/ibushotkey.h
++++ b/src/ibushotkey.h
+@@ -179,5 +179,16 @@ GQuark           ibus_hotkey_profile_lookup_hotkey
+                                                  guint               keyval,
+                                                  guint               modifiers);
++/**
++ * ibus_hotkey_profile_foreach_hotkey:
++ * @profile: An IBusHotkeyProfile.
++ * @func: (scope call): A GTraverseFunc for g_tree_traverse.
++ * @user_data: A gpointer for g_tree_traverse.
++ */
++void             ibus_hotkey_profile_foreach_hotkey
++                                                (IBusHotkeyProfile  *profile,
++                                                 GTraverseFunc       func,
++                                                 gpointer            user_data);
++
+ G_END_DECLS
+ #endif
+diff --git a/ui/gtk/panel.py b/ui/gtk/panel.py
+index de64920..55acd5a 100644
+--- a/ui/gtk/panel.py
++++ b/ui/gtk/panel.py
+@@ -133,6 +133,15 @@ class Panel(ibus.PanelBase):
+         # self.__bus.request_name(ibus.panel.IBUS_SERVICE_PANEL, 0)
+         # init xkb
++        self.__default_layout = 'default'
++        self.__default_model = 'default'
++        self.__default_option = 'default'
++        self.__disabled_engines = None
++        self.__disabled_engines_id = -1
++        self.__disabled_engines_prev_id = -1
++        self.__disabled_engines_swapped = 0
++        self.__show = 0
++
+         self.__xkblayout = ibus.XKBLayout(self.__config)
+         use_xkb = self.__config.get_value("general", "use_system_keyboard_layout", False)
+         if not use_xkb:
+@@ -142,11 +151,18 @@ class Panel(ibus.PanelBase):
+             value = 'default'
+         if value != 'default':
+             self.__xkblayout.set_default_layout(value)
++            if value.find('(') >= 0:
++                self.__default_layout = value.split('(')[0]
++                self.__default_model = value.split('(')[1].split(')')[0]
++            else:
++                self.__default_layout = value
++                self.__default_model = None
+         value = str(self.__config.get_value("general", "system_keyboard_option", ''))
+         if value == '':
+             value = 'default'
+         if value != 'default':
+             self.__xkblayout.set_default_option(value)
++            self.__default_option = value
+     def set_cursor_location(self, x, y, w, h):
+         self.__candidate_panel.set_cursor_location(x, y, w, h)
+@@ -233,12 +249,69 @@ class Panel(ibus.PanelBase):
+     def __set_im_name(self, name):
+         self.__language_bar.set_im_name(name)
++    def __set_default_layout_engine(self):
++        default_layout = self.__default_layout
++        default_model = self.__default_model
++        if default_layout == 'default':
++            default_layout = self.__xkblayout.get_default_layout()[0]
++            default_model = self.__xkblayout.get_default_layout()[1]
++        if default_model == 'default':
++            default_model = self.__xkblayout.get_default_layout()[1]
++        layouts = default_layout.split(',')
++        models = None
++        if default_model != None and default_model != '':
++            models = default_model.split(',')
++        if self.__disabled_engines == None or self.__disabled_engines == []:
++            self.__disabled_engines = []
++            for i, layout in enumerate(layouts):
++                registry = ibus.XKBConfigRegistry()
++                langs = registry.get_layout_lang()[layout]
++                lang = 'en'
++                if langs != None:
++                    lang = str(langs[0])
++                model = None
++                if i == 0:
++                    layout = default_layout
++                    model = default_model
++                elif i < len(models):
++                    model = models[i]
++                if model == '':
++                    model = None
++                model_desc = _("Default Layout")
++                if model != None:
++                    model_desc = model_desc + " (" + model + ")"
++                engine = registry.engine_desc_new(lang,
++                                                  layout,
++                                                  _("Default Layout"),
++                                                  model,
++                                                  model_desc)
++                self.__disabled_engines.append(engine)
++            self.__disabled_engines_id = self.__xkblayout.get_group()
++        if self.__focus_ic == None:
++            return
++        if not self.__focus_ic.is_enabled():
++            self.__focus_ic.set_bridge_engine()
++
+     def focus_in(self, ic):
+         self.reset()
+         self.__focus_ic = ibus.InputContext(self.__bus, ic)
+         enabled = self.__focus_ic.is_enabled()
+-        self.__language_bar.set_enabled(enabled)
++        if ibus.use_bridge_hotkey():
++            self.__set_default_layout_engine()
++            if self.__show != 1:
++                self.__language_bar.set_enabled(enabled)
++            elif enabled:
++                engine = self.__focus_ic.get_engine()
++                if engine != None and \
++                   engine.name != ibus.DEFAULT_BRIDGE_ENGINE_NAME:
++                    self.__language_bar.set_enabled(enabled)
++                else:
++                    self.__language_bar.set_enabled(False)
++            else:
++                self.__language_bar.set_enabled(False)
++        else:
++            self.__language_bar.set_enabled(enabled)
+         if not enabled:
+             self.__set_im_icon(ICON_KEYBOARD)
+             self.__set_im_name(None)
+@@ -250,7 +323,7 @@ class Panel(ibus.PanelBase):
+                 self.__set_im_icon(engine.icon)
+                 self.__set_im_name(engine.longname)
+                 if self.__bus.get_use_sys_layout():
+-                    self.__xkblayout.set_layout(self.__engine_get_layout_wrapper(engine))
++                    self.__xkblayout.set_layout(self.__engine_get_layout_wrapper(engine, False))
+             else:
+                 self.__set_im_icon(ICON_KEYBOARD)
+                 self.__set_im_name(None)
+@@ -273,7 +346,21 @@ class Panel(ibus.PanelBase):
+             return
+         enabled = self.__focus_ic.is_enabled()
+-        self.__language_bar.set_enabled(enabled)
++
++        if ibus.use_bridge_hotkey():
++            if self.__show != 1:
++                self.__language_bar.set_enabled(enabled)
++            elif enabled:
++                engine = self.__focus_ic.get_engine()
++                if engine != None and \
++                   engine.name != ibus.DEFAULT_BRIDGE_ENGINE_NAME:
++                    self.__language_bar.set_enabled(enabled)
++                else:
++                    self.__language_bar.set_enabled(False)
++            else:
++                self.__language_bar.set_enabled(False)
++        else:
++            self.__language_bar.set_enabled(enabled)
+         if enabled == False:
+             self.reset()
+@@ -287,7 +374,7 @@ class Panel(ibus.PanelBase):
+                 self.__set_im_icon(engine.icon)
+                 self.__set_im_name(engine.longname)
+                 if self.__bus.get_use_sys_layout():
+-                    self.__xkblayout.set_layout(self.__engine_get_layout_wrapper(engine))
++                    self.__xkblayout.set_layout(self.__engine_get_layout_wrapper(engine, True))
+             else:
+                 self.__set_im_icon(ICON_KEYBOARD)
+                 self.__set_im_name(None)
+@@ -315,6 +402,7 @@ class Panel(ibus.PanelBase):
+     def __config_load_show(self):
+         show = self.__config.get_value("panel", "show", 0)
++        self.__show = show
+         self.__language_bar.set_show(show)
+     def __config_load_position(self):
+@@ -443,6 +531,21 @@ class Panel(ibus.PanelBase):
+     #     menu.set_take_focus(False)
+     #     return menu
++    def __add_engine_in_menu(self, menu, engine, is_bold, size):
++        language = engine.language
++        lang = ibus.get_language_name(language)
++        item = gtk.ImageMenuItem("%s - %s" % (lang, engine.longname))
++        if is_bold:
++            for widget in item.get_children():
++                if isinstance(widget, gtk.Label):
++                    widget.set_markup("<b>%s</b>" % widget.get_text())
++        if engine.icon:
++            item.set_image(_icon.IconWidget(engine.icon, size[0]))
++        else:
++            item.set_image(_icon.IconWidget(ICON_ENGINE, size[0]))
++        item.connect("activate", self.__im_menu_item_activate_cb, engine)
++        menu.add(item)
++
+     def __create_im_menu(self):
+         engines = self.__bus.list_active_engines()
+         current_engine = \
+@@ -453,25 +556,31 @@ class Panel(ibus.PanelBase):
+         size = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
+         menu = gtk.Menu()
+         for i, engine in enumerate(engines):
+-            lang = ibus.get_language_name(engine.language)
+-            item = gtk.ImageMenuItem("%s - %s" % (lang, engine.longname))
+-            if current_engine and current_engine.name == engine.name:
+-                for widget in item.get_children():
+-                    if isinstance(widget, gtk.Label):
+-                        widget.set_markup("<b>%s</b>" % widget.get_text())
+-            if engine.icon:
+-                item.set_image(_icon.IconWidget(engine.icon, size[0]))
+-            else:
+-                item.set_image(_icon.IconWidget(ICON_ENGINE, size[0]))
+-            item.connect("activate", self.__im_menu_item_activate_cb, engine)
+-            menu.add(item)
++            if ibus.use_bridge_hotkey() and \
++                engine.name == ibus.DEFAULT_BRIDGE_ENGINE_NAME and \
++                self.__disabled_engines != None:
++                for j, kb_engine in enumerate(self.__disabled_engines):
++                    kb_engine.is_bridge = True
++                    kb_engine.disabled_engines_id = j
++                    is_bold = True if (current_engine != None and \
++                            current_engine.name == engine.name and \
++                            j == self.__disabled_engines_id) else False
++                    self.__add_engine_in_menu(menu, kb_engine,
++                                              is_bold,
++                                              size)
++                continue
++            engine.is_bridge = False
++            is_bold = True if (current_engine != None and \
++                    current_engine.name == engine.name) else False
++            self.__add_engine_in_menu(menu, engine, is_bold, size)
+         item = gtk.ImageMenuItem(_("Turn off input method"))
+         item.set_image(_icon.IconWidget("gtk-close", size[0]))
+         item.connect("activate", self.__im_menu_item_activate_cb, None)
+         if self.__focus_ic == None or not self.__focus_ic.is_enabled():
+             item.set_sensitive(False)
+-        menu.add(item)
++        if not ibus.use_bridge_hotkey():
++            menu.add(item)
+         menu.show_all()
+         menu.set_take_focus(False)
+@@ -523,8 +632,25 @@ class Panel(ibus.PanelBase):
+         if not self.__focus_ic:
+             return
+         if engine:
+-            self.__focus_ic.set_engine(engine)
++            if ibus.use_bridge_hotkey() and engine.is_bridge:
++                engines = self.__bus.list_active_engines()
++                current_engine = \
++                    (self.__focus_ic != None and self.__focus_ic.get_engine()) or \
++                    (engines and engines[0]) or \
++                    None
++                if current_engine and \
++                   current_engine.name == ibus.DEFAULT_BRIDGE_ENGINE_NAME:
++                    self.__disabled_engines_prev_id = self.__disabled_engines_id
++                    self.__disabled_engines_swapped = 0
++                else:
++                    self.__disabled_engines_prev_id = -1
++                self.__disabled_engines_id = engine.disabled_engines_id
++                self.__focus_ic.set_bridge_engine()
++            else:
++                self.__disabled_engines_prev_id = -1
++                self.__focus_ic.set_engine(engine)
+         else:
++            self.__disabled_engines_prev_id = -1
+             self.__focus_ic.disable()
+     def __sys_menu_item_activate_cb(self, item, command):
+@@ -573,11 +699,34 @@ class Panel(ibus.PanelBase):
+         self.__setup_pid = pid
+         glib.child_watch_add(self.__setup_pid, self.__child_watch_cb)
+-    def __engine_get_layout_wrapper(self, engine):
++    def __engine_get_layout_wrapper(self, engine, changed_state):
+         # This code is for the back compatibility.
+         # Should we remove the codes after all IM engines are changed
+         # to "default" layout?
+-        if engine.name != None and engine.name.startswith("xkb:layout:"):
++        if engine.name != None and engine.name.startswith("xkb:layout:") and \
++           not ibus.use_bridge_hotkey():
++            return engine.layout
++        elif engine.name != None and \
++             engine.name.startswith("xkb:layout:") and \
++             ibus.use_bridge_hotkey() and \
++             engine.name != ibus.DEFAULT_BRIDGE_ENGINE_NAME:
+             return engine.layout
++        elif ibus.use_bridge_hotkey() and \
++           self.__disabled_engines_id >= 0 and \
++           self.__disabled_engines != None and \
++           self.__disabled_engines_id < len(self.__disabled_engines):
++            if changed_state and self.__disabled_engines_prev_id != -1:
++                # state_changed is always called twice because we change
++                # the engine. So the first two calls are ignored here.
++                if self.__disabled_engines_swapped < 2:
++                    self.__disabled_engines_swapped = \
++                            self.__disabled_engines_swapped + 1
++                else:
++                    x = self.__disabled_engines_prev_id
++                    self.__disabled_engines_prev_id = self.__disabled_engines_id
++                    self.__disabled_engines_id = x
++                    self.__disabled_engines_swapped = 1
++            retval = self.__disabled_engines[self.__disabled_engines_id].layout
++            return retval
+         else:
+             return "default"
+diff --git a/xkb/Makefile.am b/xkb/Makefile.am
+index ad9cdd9..c4d5afb 100644
+--- a/xkb/Makefile.am
++++ b/xkb/Makefile.am
+@@ -28,6 +28,8 @@ INCLUDES = \
+         -I$(top_srcdir)/src \
+       -DIBUS_LOCALEDIR=\"$(datadir)/locale\" \
+       -DLIBEXECDIR=\""$(libexecdir)"\" \
++      -DUSE_BRIDGE_HOTKEY=$(USE_BRIDGE_HOTKEY) \
++      -DDEFAULT_BRIDGE_ENGINE_NAME=\"$(DEFAULT_BRIDGE_ENGINE_NAME)\" \
+         $(NULL)
+ noinst_PROGRAMS = $(TESTS)
+diff --git a/xkb/ibus-engine-xkb-main.c b/xkb/ibus-engine-xkb-main.c
+index 5d748cc..a80f349 100644
+--- a/xkb/ibus-engine-xkb-main.c
++++ b/xkb/ibus-engine-xkb-main.c
+@@ -288,6 +288,14 @@ print_component ()
+     layout_desc = (GHashTable *) ibus_xkb_config_registry_get_layout_desc (config_registry);
+     variant_desc = (GHashTable *) ibus_xkb_config_registry_get_variant_desc (config_registry);
+     component = ibus_xkb_component_new ();
++#if USE_BRIDGE_HOTKEY
++    engine = ibus_xkb_engine_desc_new ("en",
++                                       "default",
++                                       "Default Layout",
++                                       NULL,
++                                       NULL);
++    ibus_component_add_engine (component, engine);
++#endif
+     for (keys = g_hash_table_get_keys (layout_list); keys; keys = keys->next) {
+         if (keys->data == NULL) {
+             continue;
+diff --git a/xkb/xkbxml.c b/xkb/xkbxml.c
+index 2ce7bcf..de6648f 100644
+--- a/xkb/xkbxml.c
++++ b/xkb/xkbxml.c
+@@ -273,6 +273,7 @@ ibus_xkb_engine_desc_new (const gchar *lang,
+     gchar *longname = NULL;
+     gchar *desc = NULL;
+     gchar *engine_layout = NULL;
++    const gchar *icon = "ibus-engine";
+     g_return_val_if_fail (lang != NULL && layout != NULL, NULL);
+@@ -294,6 +295,11 @@ ibus_xkb_engine_desc_new (const gchar *lang,
+         desc = g_strdup_printf ("XKB %s keyboard layout", layout);
+         engine_layout = g_strdup (layout);
+     }
++#if USE_BRIDGE_HOTKEY
++    if (g_strcmp0 (name, DEFAULT_BRIDGE_ENGINE_NAME) == 0) {
++        icon = "input-keyboard-symbolic";
++    }
++#endif
+     engine = ibus_engine_desc_new (name,
+                                    longname,
+@@ -301,7 +307,7 @@ ibus_xkb_engine_desc_new (const gchar *lang,
+                                    lang,
+                                    "LGPL2.1",
+                                    "Takao Fujiwara <takao.fujiwara1@gmail.com>",
+-                                   "ibus-engine",
++                                   icon,
+                                    engine_layout);
+     g_free (name);
+-- 
+1.7.5.4
+
diff --git a/ibus-xx-icon-symbol.patch b/ibus-xx-icon-symbol.patch
new file mode 100644 (file)
index 0000000..0432938
--- /dev/null
@@ -0,0 +1,255 @@
+From cf1fa1bc72d6d7ad71e928df1c3aa938069cfd9e Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Wed, 29 Jun 2011 16:50:51 +0900
+Subject: [PATCH] Add symbol property in IBusEngineDesc.
+
+---
+ ibus/enginedesc.py   |   15 +++++++++------
+ src/ibusenginedesc.c |   40 ++++++++++++++++++++++++++++++++++++++++
+ src/ibusenginedesc.h |   10 ++++++++++
+ 3 files changed, 59 insertions(+), 6 deletions(-)
+
+diff --git a/ibus/enginedesc.py b/ibus/enginedesc.py
+index e8a8982..3ca7f24 100644
+--- a/ibus/enginedesc.py
++++ b/ibus/enginedesc.py
+@@ -31,7 +31,7 @@ from serializable import *
+ class EngineDesc(Serializable):
+     __gtype_name__ = "PYIBusEngineDesc"
+     __NAME__ = "IBusEngineDesc"
+-    def __init__(self, name="", longname="", description="", language="", license="", author="", icon="", layout="", hotkeys="", rank=0):
++    def __init__(self, name="", longname="", description="", language="", license="", author="", icon="", layout="", hotkeys="", rank=0, symbol=""):
+         super(EngineDesc, self).__init__()
+         self.__name = name
+         self.__longname = longname
+@@ -43,6 +43,7 @@ class EngineDesc(Serializable):
+         self.__layout = layout
+         self.__rank = rank
+         self.__hotkeys = hotkeys
++        self.__symbol = symbol
+     def get_name(self):
+         return self.__name
+@@ -74,6 +75,9 @@ class EngineDesc(Serializable):
+     def get_hotkeys(self):
+         return self.__hotkeys
++    def get_symbol(self):
++        return self.__symbol
++
+     name        = property(get_name)
+     longname    = property(get_longname)
+     description = property(get_description)
+@@ -84,6 +88,7 @@ class EngineDesc(Serializable):
+     layout      = property(get_layout)
+     rank        = property(get_rank)
+     hotkeys     = property(get_hotkeys)
++    symbol      = property(get_symbol)
+     def serialize(self, struct):
+         super(EngineDesc, self).serialize(struct)
+@@ -97,8 +102,7 @@ class EngineDesc(Serializable):
+         struct.append(dbus.String(self.__layout))
+         struct.append(dbus.UInt32(self.__rank))
+         struct.append(dbus.String(self.__hotkeys))
+-        # New properties of EngineDesc will use dict for serialize
+-        struct.append(dbus.Array({}, signature=None))
++        struct.append(dbus.String(self.__symbol))
+     def deserialize(self, struct):
+         super(EngineDesc, self).deserialize(struct)
+@@ -112,11 +116,10 @@ class EngineDesc(Serializable):
+         self.__layout = struct.pop(0)
+         self.__rank = struct.pop(0)
+         self.__hotkeys = struct.pop(0)
+-        # New properties of EngineDesc will use dict for serialize
+-        #value = struct.pop(0)
++        self.__symbol = struct.pop(0)
+ def test():
+-    engine = EngineDesc("Hello", "", "", "", "", "", "", "", "")
++    engine = EngineDesc("Hello", "", "", "", "", "", "", "", "", 0, "")
+     value = serialize_object(engine)
+     engine = deserialize_object(value)
+diff --git a/src/ibusenginedesc.c b/src/ibusenginedesc.c
+index ca5ef60..fa3a768 100644
+--- a/src/ibusenginedesc.c
++++ b/src/ibusenginedesc.c
+@@ -39,6 +39,7 @@ enum {
+     PROP_LAYOUT,
+     PROP_RANK,
+     PROP_HOTKEYS,
++    PROP_SYMBOL,
+ };
+@@ -54,6 +55,7 @@ struct _IBusEngineDescPrivate {
+     gchar      *layout;
+     guint       rank;
+     gchar      *hotkeys;
++    gchar      *symbol;
+ };
+ #define IBUS_ENGINE_DESC_GET_PRIVATE(o)  \
+@@ -232,6 +234,19 @@ ibus_engine_desc_class_init (IBusEngineDescClass *class)
+                         "The hotkeys of engine description",
+                         "",
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
++
++    /**
++     * IBusEngineDesc:symbol:
++     *
++     * The symbol chars of engine description instead of icon image
++     */
++    g_object_class_install_property (gobject_class,
++                    PROP_SYMBOL,
++                    g_param_spec_string ("symbol",
++                        "description symbol",
++                        "The icon symbol chars of engine description",
++                        "",
++                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ }
+ static void
+@@ -249,6 +264,7 @@ ibus_engine_desc_init (IBusEngineDesc *desc)
+     desc->priv->layout = NULL;
+     desc->priv->rank = 0;
+     desc->priv->hotkeys = NULL;
++    desc->priv->symbol = NULL;
+ }
+ static void
+@@ -263,6 +279,7 @@ ibus_engine_desc_destroy (IBusEngineDesc *desc)
+     g_free (desc->priv->icon);
+     g_free (desc->priv->layout);
+     g_free (desc->priv->hotkeys);
++    g_free (desc->priv->symbol);
+     IBUS_OBJECT_CLASS (ibus_engine_desc_parent_class)->destroy (IBUS_OBJECT (desc));
+ }
+@@ -313,6 +330,10 @@ ibus_engine_desc_set_property (IBusEngineDesc *desc,
+         g_assert (desc->priv->hotkeys == NULL);
+         desc->priv->hotkeys = g_value_dup_string (value);
+         break;
++    case PROP_SYMBOL:
++        g_assert (desc->priv->symbol == NULL);
++        desc->priv->symbol = g_value_dup_string (value);
++        break;
+     default:
+         G_OBJECT_WARN_INVALID_PROPERTY_ID (desc, prop_id, pspec);
+     }
+@@ -355,6 +376,9 @@ ibus_engine_desc_get_property (IBusEngineDesc *desc,
+     case PROP_HOTKEYS:
+         g_value_set_string (value, ibus_engine_desc_get_hotkeys (desc));
+         break;
++    case PROP_SYMBOL:
++        g_value_set_string (value, ibus_engine_desc_get_symbol (desc));
++        break;
+     default:
+         G_OBJECT_WARN_INVALID_PROPERTY_ID (desc, prop_id, pspec);
+     }
+@@ -371,6 +395,10 @@ ibus_engine_desc_serialize (IBusEngineDesc  *desc,
+     /* End dict iter */
+ #define NOTNULL(s) ((s) != NULL ? (s) : "")
++    /* If you will add a new property, you can append it at the end and
++     * you should not change the serialized order of name, longname,
++     * description, ... because the order is also used in other applications
++     * likes ibus-qt. */
+     g_variant_builder_add (builder, "s", NOTNULL (desc->priv->name));
+     g_variant_builder_add (builder, "s", NOTNULL (desc->priv->longname));
+     g_variant_builder_add (builder, "s", NOTNULL (desc->priv->description));
+@@ -381,7 +409,9 @@ ibus_engine_desc_serialize (IBusEngineDesc  *desc,
+     g_variant_builder_add (builder, "s", NOTNULL (desc->priv->layout));
+     g_variant_builder_add (builder, "u", desc->priv->rank);
+     g_variant_builder_add (builder, "s", NOTNULL (desc->priv->hotkeys));
++    g_variant_builder_add (builder, "s", NOTNULL (desc->priv->symbol));
+ #undef NOTNULL
++
+     return TRUE;
+ }
+@@ -394,6 +424,10 @@ ibus_engine_desc_deserialize (IBusEngineDesc *desc,
+     retval = IBUS_SERIALIZABLE_CLASS (ibus_engine_desc_parent_class)->deserialize ((IBusSerializable *)desc, variant);
+     g_return_val_if_fail (retval, 0);
++    /* If you will add a new property, you can append it at the end and
++     * you should not change the serialized order of name, longname,
++     * description, ... because the order is also used in other applications
++     * likes ibus-qt. */
+     g_variant_get_child (variant, retval++, "s", &desc->priv->name);
+     g_variant_get_child (variant, retval++, "s", &desc->priv->longname);
+     g_variant_get_child (variant, retval++, "s", &desc->priv->description);
+@@ -404,6 +438,7 @@ ibus_engine_desc_deserialize (IBusEngineDesc *desc,
+     g_variant_get_child (variant, retval++, "s", &desc->priv->layout);
+     g_variant_get_child (variant, retval++, "u", &desc->priv->rank);
+     g_variant_get_child (variant, retval++, "s", &desc->priv->hotkeys);
++    g_variant_get_child (variant, retval++, "s", &desc->priv->symbol);
+     return retval;
+ }
+@@ -428,6 +463,7 @@ ibus_engine_desc_copy (IBusEngineDesc       *dest,
+     dest->priv->layout           = g_strdup (src->priv->layout);
+     dest->priv->rank             = src->priv->rank;
+     dest->priv->hotkeys          = g_strdup (src->priv->hotkeys);
++    dest->priv->symbol           = g_strdup (src->priv->symbol);
+     return TRUE;
+ }
+@@ -465,6 +501,7 @@ ibus_engine_desc_output (IBusEngineDesc *desc,
+     OUTPUT_ENTRY_1(icon);
+     OUTPUT_ENTRY_1(layout);
+     OUTPUT_ENTRY_1(hotkeys);
++    OUTPUT_ENTRY_1(symbol);
+     g_string_append_indent (output, indent + 1);
+     g_string_append_printf (output, "<rank>%u</rank>\n", desc->priv->rank);
+ #undef OUTPUT_ENTRY
+@@ -498,6 +535,7 @@ ibus_engine_desc_parse_xml_node (IBusEngineDesc *desc,
+         PARSE_ENTRY_1(icon);
+         PARSE_ENTRY_1(layout);
+         PARSE_ENTRY_1(hotkeys);
++        PARSE_ENTRY_1(symbol);
+ #undef PARSE_ENTRY
+ #undef PARSE_ENTRY_1
+         if (g_strcmp0 (sub_node->name , "rank") == 0) {
+@@ -526,6 +564,7 @@ IBUS_ENGINE_DESC_GET_PROPERTY (icon, const gchar *)
+ IBUS_ENGINE_DESC_GET_PROPERTY (layout, const gchar *)
+ IBUS_ENGINE_DESC_GET_PROPERTY (rank, guint)
+ IBUS_ENGINE_DESC_GET_PROPERTY (hotkeys, const gchar *)
++IBUS_ENGINE_DESC_GET_PROPERTY (symbol, const gchar *)
+ #undef IBUS_ENGINE_DESC_GET_PROPERTY
+ IBusEngineDesc *
+@@ -573,6 +612,7 @@ ibus_engine_desc_new_varargs (const gchar *first_property_name, ...)
+     g_assert (desc->priv->icon);
+     g_assert (desc->priv->layout);
+     g_assert (desc->priv->hotkeys);
++    g_assert (desc->priv->symbol);
+     return desc;
+ }
+diff --git a/src/ibusenginedesc.h b/src/ibusenginedesc.h
+index 9718b15..76a7adc 100644
+--- a/src/ibusenginedesc.h
++++ b/src/ibusenginedesc.h
+@@ -249,6 +249,16 @@ guint            ibus_engine_desc_get_rank      (IBusEngineDesc *info);
+ const gchar     *ibus_engine_desc_get_hotkeys   (IBusEngineDesc *info);
+ /**
++ * ibus_engine_desc_get_symbol:
++ * @info: An IBusEngineDesc
++ * @returns: symbol property in IBusEngineDesc
++ *
++ * Return the symbol property in IBusEngineDesc. It should not be freed.
++ */
++const gchar     *ibus_engine_desc_get_symbol
++                                                (IBusEngineDesc *info);
++
++/**
+  * ibus_engine_desc_output:
+  * @info: An IBusEngineDesc
+  * @output: XML-formatted Input method engine description.
+-- 
+1.7.5.4
+
diff --git a/ibus-xx-setup-frequent-lang.patch b/ibus-xx-setup-frequent-lang.patch
new file mode 100644 (file)
index 0000000..936dd5a
--- /dev/null
@@ -0,0 +1,436 @@
+From 735a4d98ac057ded317dae9b85777ce56d8af0fd Mon Sep 17 00:00:00 2001
+From: fujiwarat <takao.fujiwara1@gmail.com>
+Date: Mon, 20 Jun 2011 19:04:51 +0900
+Subject: [PATCH] Enable ibus-setup to show the frequently used languages
+ only in IME list.
+
+---
+ data/ibus.schemas.in    |  168 +++++++++++++++++++++++++++++++++++++++++++++++
+ setup/enginecombobox.py |  153 ++++++++++++++++++++++++++++++++++++------
+ setup/main.py           |    1 +
+ 3 files changed, 300 insertions(+), 22 deletions(-)
+
+diff --git a/data/ibus.schemas.in.in b/data/ibus.schemas.in.in
+index d4334e1..5ef2ac2 100644
+--- a/data/ibus.schemas.in.in
++++ b/data/ibus.schemas.in.in
+@@ -239,6 +239,174 @@
+       </locale>
+     </schema>
+     <schema>
++      <key>/schemas/desktop/ibus/general/xkblayoutconfig/system/group_list</key>
++      <applyto>/desktop/ibus/general/xkblayoutconfig/system/group_list</applyto>
++      <owner>ibus</owner>
++      <type>list</type>
++      <list_type>string</list_type>
++      <default>[west_europe,south_europe,east_europe,north_europe,west_asia,center_asia,east_asia,india,australia]</default>
++      <locale name="C">
++        <short>List of system keyboard layout groups on ibus-setup</short>
++          <long>The group list is used not to show all the system
++                  keyboard layouts by default. The list item will be
++                  appended at the end of gconf key. e.g.
++                  .../xkblayoutconfig/system/item1</long>
++      </locale>
++    </schema>
++    <schema>
++      <key>/schemas/desktop/ibus/general/xkblayoutconfig/system/west_europe</key>
++      <applyto>/desktop/ibus/general/xkblayoutconfig/system/west_europe</applyto>
++      <owner>ibus</owner>
++      <type>list</type>
++      <list_type>string</list_type>
++      <!--
++      <default>[ad,be,br,de,ca,ch,cz,es,fr,gn,hu,ie,ie(CloGaelach),it,latam,nl,pt,si,sk]</default>
++      -->
++      <default>[ca,cs,de,en,es,fr,gd,hu,it,nl,pt,sk,sl]</default>
++      <locale name="C">
++        <short>List of European languages on ibus-setup</short>
++          <long>ibus-setup shows the languages only in input method list
++                  when you run ibus-setup on one of the languages.
++                  Other languages are hidden under an extended button.</long>
++      </locale>
++    </schema>
++    <schema>
++      <key>/schemas/desktop/ibus/general/xkblayoutconfig/system/south_europe</key>
++      <applyto>/desktop/ibus/general/xkblayoutconfig/system/south_europe</applyto>
++      <owner>ibus</owner>
++      <type>list</type>
++      <list_type>string</list_type>
++      <!--
++      <default>[al,ba,bg,gr,me,mk,mt,ro,rs]</default>
++      -->
++      <default>[bg,bs,el,mk,mt,ro,sq,sr]</default>
++      <locale name="C">
++        <short>List of European languages on ibus-setup</short>
++          <long>ibus-setup shows the languages only in input method list
++                  when you run ibus-setup on one of the languages.
++                  Other languages are hidden under an extended button.</long>
++      </locale>
++    </schema>
++    <schema>
++      <key>/schemas/desktop/ibus/general/xkblayoutconfig/system/east_europe</key>
++      <applyto>/desktop/ibus/general/xkblayoutconfig/system/east_europe</applyto>
++      <owner>ibus</owner>
++      <type>list</type>
++      <list_type>string</list_type>
++      <!--
++      <default>[by,ee,epo,ge,ge(dsb),ge(ru),ge(os),hr,kg,kz,lt,lv,pl,pl(csb),ru,ru(cv),ru(kom),ru(sah),ru(tt),ru(xal),ua,uz]</default>
++      -->
++      <default>[be,csb,cv,et,ka,kk,ky,lt,lv,pl,ru,tt,uk,uz]</default>
++      <locale name="C">
++        <short>List of European languages on ibus-setup</short>
++          <long>ibus-setup shows the languages only in input method list
++                  when you run ibus-setup on one of the languages.
++                  Other languages are hidden under an extended button.</long>
++      </locale>
++    </schema>
++    <schema>
++      <key>/schemas/desktop/ibus/general/xkblayoutconfig/system/north_europe</key>
++      <applyto>/desktop/ibus/general/xkblayoutconfig/system/north_europe</applyto>
++      <owner>ibus</owner>
++      <type>list</type>
++      <list_type>string</list_type>
++      <!--
++      <default>[dk,fi,fo,is,no,no(smi),se]</default>
++      -->
++      <default>[da,fi,fo,is,no,se,sv]</default>
++      <locale name="C">
++        <short>List of European languages on ibus-setup</short>
++          <long>ibus-setup shows the languages only in input method list
++                  when you run ibus-setup on one of the languages.
++                  Other languages are hidden under an extended button.</long>
++      </locale>
++    </schema>
++    <schema>
++      <key>/schemas/desktop/ibus/general/xkblayoutconfig/system/west_asia</key>
++      <applyto>/desktop/ibus/general/xkblayoutconfig/system/west_asia</applyto>
++      <owner>ibus</owner>
++      <type>list</type>
++      <list_type>string</list_type>
++      <!--
++      <default>[am,ara,az,et,gh,gh(akan),gh(ewe),gh(fula),gh(ga),gh(hausa),il,iq,iq(ku),ir,ir(ku),ma,ma(tifinagh),ng,ng(hausa),ng,ng(igbo),ng(yoruba),sy,sy(ku),tj,tr]</default>
++      -->
++      <default>[am,ar,az,ber,fa,ha,he,hy,ig,ku,tg,tr,yo]</default>
++      <locale name="C">
++        <short>List of Asian languages on ibus-setup</short>
++          <long>ibus-setup shows the languages only in input method list
++                  when you run ibus-setup on one of the languages.
++                  Other languages are hidden under an extended button.</long>
++      </locale>
++    </schema>
++    <schema>
++      <key>/schemas/desktop/ibus/general/xkblayoutconfig/system/center_asia</key>
++      <applyto>/desktop/ibus/general/xkblayoutconfig/system/center_asia</applyto>
++      <owner>ibus</owner>
++      <type>list</type>
++      <list_type>string</list_type>
++      <!--
++      <default>[cn(tib)]</default>
++      -->
++      <default>[bo,zh]</default>
++      <locale name="C">
++        <short>List of Asian languages on ibus-setup</short>
++          <long>ibus-setup shows the languages only in input method list
++                  when you run ibus-setup on one of the languages.
++                  Other languages are hidden under an extended button.</long>
++      </locale>
++    </schema>
++    <schema>
++      <key>/schemas/desktop/ibus/general/xkblayoutconfig/system/east_asia</key>
++      <applyto>/desktop/ibus/general/xkblayoutconfig/system/east_asia</applyto>
++      <owner>ibus</owner>
++      <type>list</type>
++      <list_type>string</list_type>
++      <!--
++      <default>[bt,kh,la,mal,mm,th,vn]</default>
++      -->
++      <default>[dz,km,lo,my,th,vi]</default>
++      <locale name="C">
++        <short>List of Asian languages on ibus-setup</short>
++          <long>ibus-setup shows the languages only in input method list
++                  when you run ibus-setup on one of the languages.
++                  Other languages are hidden under an extended button.</long>
++      </locale>
++    </schema>
++    <schema>
++      <key>/schemas/desktop/ibus/general/xkblayoutconfig/system/india</key>
++      <applyto>/desktop/ibus/general/xkblayoutconfig/system/india</applyto>
++      <owner>ibus</owner>
++      <type>list</type>
++      <list_type>string</list_type>
++      <!--
++      <default>[bd,in,in(ben),in(guj),in(guru),in(jhelum),in(kan),in(mal),in(ori),in(tam),in(tel),in(urd-phonetic),in(bolnagri),lk,lk(tam_unicode),mv,np,pk]</default>
++      -->
++      <default>[bn,dv,gu,hi,kn,ml,ne,or,pa,si,ta,te,ur]</default>
++      <locale name="C">
++        <short>List of Asian languages on ibus-setup</short>
++          <long>ibus-setup shows the languages only in input method list
++                  when you run ibus-setup on one of the languages.
++                  Other languages are hidden under an extended button.</long>
++      </locale>
++    </schema>
++    <schema>
++      <key>/schemas/desktop/ibus/general/xkblayoutconfig/system/australia</key>
++      <applyto>/desktop/ibus/general/xkblayoutconfig/system/australia</applyto>
++      <owner>ibus</owner>
++      <type>list</type>
++      <list_type>string</list_type>
++      <!--
++      <default>[mao]</default>
++      -->
++      <default>[mi]</default>
++      <locale name="C">
++        <short>List of Asian languages on ibus-setup</short>
++          <long>ibus-setup shows the languages only in input method list
++                  when you run ibus-setup on one of the languages.
++                  Other languages are hidden under an extended button.</long>
++      </locale>
++    </schema>
++    <schema>
+       <key>/schemas/desktop/ibus/panel/use_custom_font</key>
+       <applyto>/desktop/ibus/panel/use_custom_font</applyto>
+       <owner>ibus</owner>
+diff --git a/setup/enginecombobox.py b/setup/enginecombobox.py
+index 7383177..bff2407 100644
+--- a/setup/enginecombobox.py
++++ b/setup/enginecombobox.py
+@@ -43,7 +43,10 @@ class EngineComboBox(gtk.ComboBox):
+         self.connect("notify::active", self.__notify_active_cb)
+         self.__model = None
++        self.__all_model = None
++        self.__config = None
+         self.__title = _("Select an input method")
++        self.__show_sub_lang = False
+         renderer = gtk.CellRendererPixbuf()
+         renderer.set_property("xalign", 0)
+@@ -57,21 +60,51 @@ class EngineComboBox(gtk.ComboBox):
+         self.pack_start(renderer, True)
+         self.set_cell_data_func(renderer, self.__name_cell_data_cb)
+-    def set_engines(self, engines):
+-        self.__model = gtk.TreeStore(gobject.TYPE_PYOBJECT)
+-
+-        iter1 = self.__model.append(None)
+-        self.__model.set(iter1, 0, 0)
+-        lang = {}
+-        for e in engines:
+-            if ibus.use_bridge_hotkey() and \
+-               e.name == ibus.DEFAULT_BRIDGE_ENGINE_NAME:
+-                continue
+-            l = ibus.get_language_name(e.language)
+-            if l not in lang:
+-                lang[l] = []
+-            lang[l].append(e)
++    def __gconf_get_lang_list_from_locale(self):
++        common_list = ['en', 'Other']
++        if  self.__config == None:
++            return None
++        loc = None
++        try:
++            loc = locale.setlocale (locale.LC_ALL)
++        except:
++            pass
++        if loc == None:
++            return common_list
++        current_lang = ibus.get_language_name(loc)
++        if current_lang == None:
++            return common_list
++        group_list = self.__config.get_value("general/xkblayoutconfig/system",
++                                             "group_list", None)
++        if group_list == None:
++            return [loc] + common_list
++        group_list = list(group_list)
++        lang_list = None
++        for group in group_list:
++            group = str(group)
++            langs = self.__config.get_value("general/xkblayoutconfig/system",
++                                            group, [])
++            for lang in langs:
++                lang = str(lang)
++                if current_lang == ibus.get_language_name(lang):
++                    lang_list = langs
++                    break
++            if lang_list != None:
++                break
++        if lang_list == None:
++            return [loc] + common_list
++        return lang_list + common_list
++
++    def __has_engine_in_lang_list(self, engine, lang_list):
++        retval = False
++        for lang in lang_list:
++            if ibus.get_language_name(lang) == \
++                ibus.get_language_name(engine.language):
++                retval = True
++                break
++        return retval
++    def __model_append_langs(self, model, lang, visible):
+         keys = lang.keys()
+         keys.sort(locale.strcoll)
+         #add "Others" to the end of the combo box
+@@ -79,29 +112,89 @@ class EngineComboBox(gtk.ComboBox):
+             keys.remove(ibus.get_language_name("Other"))
+             keys += [ibus.get_language_name("Other")]
+         for l in keys:
+-            iter1 = self.__model.append(None)
+-            self.__model.set(iter1, 0, l)
++            iter1 = model.append(None)
++            model.set(iter1, 0, l)
+             def cmp_engine(a, b):
+                 if a.rank == b.rank:
+                     return locale.strcoll(a.longname, b.longname)
+                 return int(b.rank - a.rank)
+             lang[l].sort(cmp_engine)
+             for e in lang[l]:
+-                iter2 = self.__model.append(iter1)
+-                self.__model.set(iter2, 0, e)
++                iter2 = model.append(iter1)
++                model.set(iter2, 0, e)
++
++    def set_engines(self, engines):
++        self.__model = gtk.TreeStore(gobject.TYPE_PYOBJECT)
+-        self.set_model(self.__model)
++        iter1 = self.__model.append(None)
++        self.__model.set(iter1, 0, 0)
++        lang_list = self.__gconf_get_lang_list_from_locale()
++        lang = {}
++        sub_lang = {}
++        for e in engines:
++            if ibus.use_bridge_hotkey() and \
++               e.name == ibus.DEFAULT_BRIDGE_ENGINE_NAME:
++                continue
++            l = ibus.get_language_name(e.language)
++            if lang_list == None or \
++                self.__has_engine_in_lang_list(e, lang_list):
++                if l not in lang:
++                    lang[l] = []
++                lang[l].append(e)
++            else:
++                if l not in sub_lang:
++                    sub_lang[l] = []
++                sub_lang[l].append(e)
++
++        self.__model_append_langs(self.__model, lang, True)
++        iter1 = self.__model.append(None)
++        self.__model.set(iter1, 0, -1)
++
++        self.__all_model = gtk.TreeStore(gobject.TYPE_PYOBJECT)
++        iter1 = self.__all_model.append(None)
++        self.__all_model.set(iter1, 0, 0)
++        self.__model_append_langs(self.__all_model, lang, False)
++        iter1 = self.__all_model.append(None)
++        self.__all_model.set(iter1, 0, -1)
++        self.__model_append_langs(self.__all_model, sub_lang, False)
++
++        self.__toggle_sub_lang()
++
++    def __toggle_sub_lang(self):
++        self.set_model(None)
++        if self.__show_sub_lang:
++            self.set_model(self.__all_model)
++        else:
++            self.set_model(self.__model)
+         self.set_active(0)
+     def __icon_cell_data_cb(self, celllayout, renderer, model, iter):
+-        engine = self.__model.get_value(iter, 0)
++        model = self.get_model()
++        engine = model.get_value(iter, 0)
+         if isinstance(engine, str) or isinstance (engine, unicode):
+             renderer.set_property("visible", False)
+             renderer.set_property("sensitive", False)
+         elif isinstance(engine, int):
+-            renderer.set_property("visible", False)
+-            renderer.set_property("sensitive", False)
++            if engine == 0:
++                renderer.set_property("visible", False)
++                renderer.set_property("sensitive", False)
++                renderer.set_property("pixbuf", None)
++            elif engine < 0:
++                if not self.__show_sub_lang:
++                    pixbuf = load_icon("list-add", gtk.ICON_SIZE_LARGE_TOOLBAR)
++                else:
++                    pixbuf = load_icon("list-remove", gtk.ICON_SIZE_LARGE_TOOLBAR)
++                if pixbuf == None:
++                    pixbuf = load_icon("gtk-missing-image",
++                                       gtk.ICON_SIZE_LARGE_TOOLBAR)
++                if pixbuf == None:
++                    renderer.set_property("visible", False)
++                    renderer.set_property("sensitive", False)
++                    return
++                renderer.set_property("visible", True)
++                renderer.set_property("sensitive", True)
++                renderer.set_property("pixbuf", pixbuf)
+         else:
+             renderer.set_property("visible", True)
+             renderer.set_property("sensitive", True)
+@@ -113,7 +206,8 @@ class EngineComboBox(gtk.ComboBox):
+             renderer.set_property("pixbuf", pixbuf)
+     def __name_cell_data_cb(self, celllayout, renderer, model, iter):
+-        engine = self.__model.get_value(iter, 0)
++        model = self.get_model()
++        engine = model.get_value(iter, 0)
+         if isinstance (engine, str) or isinstance (engine, unicode):
+             renderer.set_property("sensitive", False)
+@@ -121,8 +215,15 @@ class EngineComboBox(gtk.ComboBox):
+             renderer.set_property("weight", pango.WEIGHT_NORMAL)
+         elif isinstance(engine, int):
+             renderer.set_property("sensitive", True)
+-            renderer.set_property("text", self.__title)
+-            renderer.set_property("weight", pango.WEIGHT_NORMAL)
++            if engine == 0:
++                renderer.set_property("text", self.__title)
++                renderer.set_property("weight", pango.WEIGHT_NORMAL)
++            elif engine < 0:
++                if not self.__show_sub_lang:
++                    renderer.set_property("text", _("Show all input methods"))
++                else:
++                    renderer.set_property("text", _("Show only input methods for your region"))
++                renderer.set_property("weight", pango.WEIGHT_BOLD)
+         else:
+             renderer.set_property("sensitive", True)
+             renderer.set_property("text", engine.longname)
+@@ -137,13 +238,21 @@ class EngineComboBox(gtk.ComboBox):
+             if i == 0 or i == -1:
+                 return None
+             iter = self.get_active_iter()
+-            return self.get_model()[iter][0]
++            model = self.get_model()
++            if model[iter][0] == -1:
++                self.__show_sub_lang = not self.__show_sub_lang
++                self.__toggle_sub_lang()
++                return None
++            return model[iter][0]
+         else:
+             raise AttributeError, 'unknown property %s' % property.name
+     def get_active_engine(self):
+         return self.get_property("active-engine")
++    def set_config(self, config):
++        self.__config = config
++
+     def get_title(self):
+         return self.__title
+diff --git a/setup/main.py b/setup/main.py
+index 192fb88..5a0170d 100644
+--- a/setup/main.py
++++ b/setup/main.py
+@@ -227,6 +227,7 @@ class Setup(object):
+         button.connect("toggled", self.__checkbutton_preload_engine_mode_toggled_cb)
+         self.__engines = self.__bus.list_engines()
+         self.__combobox = self.__builder.get_object("combobox_engines")
++        self.__combobox.set_config(self.__config)
+         self.__combobox.set_engines(self.__engines)
+         engines = self.__bus.list_active_engines()
+-- 
+1.7.4.4
+
diff --git a/ibus.spec b/ibus.spec
new file mode 100644 (file)
index 0000000..7c68ccb
--- /dev/null
+++ b/ibus.spec
@@ -0,0 +1,280 @@
+#
+# Conditional build:
+%bcond_without gjsfile         # https://bugzilla.redhat.com/show_bug.cgi?id=657165
+#
+Summary:       Intelligent Input Bus for Linux OS
+Name:          ibus
+Version:       1.3.99.20110419
+Release:       0.1
+License:       LGPL v2+
+Group:         Libraries
+URL:           http://code.google.com/p/ibus/
+Source0:       http://ibus.googlecode.com/files/%{name}-%{version}.tar.gz
+# Source0-md5: d4f2729fecb92ae6b41f26c770b1a772
+Source1:       %{name}.xinputd
+Source100:     http://fujiwara.fedorapeople.org/ibus/gnome-shell/%{name}-gjs-1.3.99.20110714.tar.gz
+# Source100-md5:       57df6a7d6a9ca0f4b30a8fe135fdcb89
+Patch0:                %{name}-HEAD.patch
+Patch1:                %{name}-530711-preload-sys.patch
+Patch2:                %{name}-xx-icon-symbol.patch
+Patch3:                %{name}-541492-xkb.patch
+Patch4:                %{name}-xx-bridge-hotkey.patch
+Patch5:                %{name}-xx-setup-frequent-lang.patch
+# Workaround for oxygen-gtk icon theme until bug 699103 is fixed.
+Patch91:       %{name}-711632-fedora-fallback-icon.patch
+BuildRequires: GConf2-devel
+BuildRequires: autoconf
+BuildRequires: automake
+BuildRequires: dbus-glib-devel
+BuildRequires: desktop-file-utils
+BuildRequires: gettext-devel
+BuildRequires: gtk+2-devel
+BuildRequires: gtk+3-devel
+BuildRequires: gtk-doc
+BuildRequires: intltool
+BuildRequires: iso-codes
+BuildRequires: libtool
+BuildRequires: python
+BuildRequires: rpmbuild(macros) >= 1.596
+BuildRequires: rpm-pythonprov
+BuildRequires: python-dbus-devel >= 0.83.0
+BuildRequires: python-pygobject-devel
+BuildRequires: xorg-lib-libxkbfile-devel
+Requires:      %{name}-gtk2 = %{version}-%{release}
+Requires:      %{name}-gtk3 = %{version}-%{release}
+Requires:      %{name}-libs = %{version}-%{release}
+Requires:      GConf2
+Requires:      im-chooser
+Requires:      iso-codes
+Requires:      python-dbus >= 0.83.0
+Requires:      python-pygtk-gtk
+Requires:      python-pynotify
+Requires:      python-pyxdg
+Requires:      hicolor-icon-theme
+Requires:      gtk-update-icon-cache
+Requires(post):        GConf2
+Requires(preun):       GConf2
+BuildRoot:     %{tmpdir}/%{name}-%{version}-root-%(id -u -n)
+
+%description
+IBus means Intelligent Input Bus. It is an input framework for Linux
+OS.
+
+%package libs
+Summary:       IBus libraries
+Group:         Libraries
+Requires:      dbus >= 1.2.4
+Requires:      glib2 >= 1:2.26.0
+
+%description libs
+This package contains the libraries for IBus
+
+%package gtk2
+Summary:       IBus im module for gtk2
+Group:         Libraries
+Requires:      %{name} = %{version}-%{release}
+Requires:      %{name}-libs = %{version}-%{release}
+Requires:      imsettings-gnome2
+Requires(post):        glib2 >= 1:2.26.0
+
+%description gtk2
+This package contains ibus im module for gtk2
+
+%package gtk3
+Summary:       IBus im module for gtk3
+Group:         Libraries
+Requires:      %{name} = %{version}-%{release}
+Requires:      %{name}-libs = %{version}-%{release}
+Requires:      imsettings-gnome3
+Requires(post):        glib2 >= 1:2.26.0
+
+%description gtk3
+This package contains ibus im module for gtk3
+
+%package gnome3
+Summary:       IBus gnome-shell-extension for GNOME3
+Group:         Libraries
+Requires:      %{name} = %{version}-%{release}
+Requires:      %{name}-libs = %{version}-%{release}
+Requires:      gnome-shell
+
+%description gnome3
+This is a transitional package which allows users to try out new IBus
+GUI for GNOME3 in development. Note that this package will be marked
+as obsolete once the integration has completed in the GNOME3 upstream.
+
+%package devel
+Summary:       Development tools for ibus
+Group:         Development/Libraries
+Requires:      %{name}-libs = %{version}-%{release}
+Requires:      dbus-devel
+Requires:      glib2-devel
+
+%description devel
+The ibus-devel package contains the header files and developer docs
+for ibus.
+
+%prep
+%setup -q
+%if %{with gjsfile}
+zcat %SOURCE100 | tar xf -
+%endif
+%patch0 -p1
+cp client/gtk2/ibusimcontext.c client/gtk3/ibusimcontext.c
+%patch1 -p1
+%patch2 -p1
+%patch3 -p1
+mv data/ibus.schemas.in data/ibus.schemas.in.in
+%patch4 -p1
+%patch5 -p1
+
+%patch91 -p1
+
+%build
+%if %{with gjsfile}
+d=`basename %SOURCE100 .tar.gz`
+cd $d
+%configure
+%{__make}
+cd ..
+%endif
+
+%{__aclocal} -I m4
+%{__autoheader}
+%{__autoconf}
+%{__automake}
+%configure \
+       --enable-gtk2 \
+       --enable-gtk3 \
+       --enable-xim \
+       --enable-vala \
+       --enable-xkb \
+       --disable-gtk-doc \
+       --enable-gconf \
+       --enable-python \
+       --with-html-dir=%{_gtkdocdir} \
+       --with-no-snooper-apps='gnome-do,Do.*,firefox.*,.*chrome.*,.*chromium.*' \
+       --enable-surrounding-text \
+       --enable-bridge-hotkey \
+       --enable-introspection
+
+%{__make}
+
+%install
+rm -rf $RPM_BUILD_ROOT
+install -d $RPM_BUILD_ROOT%{_sysconfdir}/{X11/xinit/xinput.d,xdg/autostart}
+
+%{__make} install \
+       DESTDIR=$RPM_BUILD_ROOT
+
+%{__sed} -e 's|@@LIB@@|%{_lib}|g' %{SOURCE1} >$RPM_BUILD_ROOT%{_sysconfdir}/X11/xinit/xinput.d/ibus.conf
+
+# correct location in upstream.
+mv $RPM_BUILD_ROOT{%{_desktopdir},%{_sysconfdir}/xdg/autostart}/ibus.desktop
+
+%if %{with gjsfile}
+d=`basename %SOURCE100 .tar.gz`
+cd $d
+%{__make} install \
+       DESTDIR=$RPM_BUILD_ROOT
+%{__rm} $RPM_BUILD_ROOT%{_datadir}/locale/*/LC_MESSAGES/ibus-gjs.mo
+cd ..
+%endif
+
+%{__rm} $RPM_BUILD_ROOT%{_libdir}/*.la
+%{__rm} $RPM_BUILD_ROOT%{_libdir}/gtk*/*/immodules/*.la
+
+%find_lang %{name}10
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+%update_icon_cache hicolor
+%gconf_schema_install ibus
+
+%preun
+%gconf_schema_uninstall ibus
+
+%postun
+%update_icon_cache hicolor
+
+%post libs -p /sbin/ldconfig
+%postun libs -p /sbin/ldconfig
+
+%post gtk2
+%if "%{_lib}" != "lib"
+%{_bindir}/gtk-query-immodules-2.0-64 > %{_sysconfdir}/gtk64-2.0/gtk.immodules
+%else
+%{_bindir}/gtk-query-immodules-2.0 > %{_sysconfdir}/gtk-2.0/gtk.immodules
+%endif
+
+%postun gtk2
+%if "%{_lib}" != "lib"
+%{_bindir}/gtk-query-immodules-2.0-64 > %{_sysconfdir}/gtk64-2.0/gtk.immodules
+%else
+%{_bindir}/gtk-query-immodules-2.0 > %{_sysconfdir}/gtk-2.0/gtk.immodules
+%endif
+
+%post gtk3
+%if "%{_lib}" != "lib"
+%{_bindir}/gtk-query-immodules-3.0-64 --update-cache
+%else
+%{_bindir}/gtk-query-immodules-3.0 --update-cache
+%endif
+
+%postun gtk3
+%if "%{_lib}" != "lib"
+%{_bindir}/gtk-query-immodules-3.0-64 --update-cache
+%else
+%{_bindir}/gtk-query-immodules-3.0 --update-cache
+%endif
+
+%files -f %{name}10.lang
+%defattr(644,root,root,755)
+%doc AUTHORS README
+%config %{_sysconfdir}/X11/xinit/xinput.d/ibus.conf
+# imsettings will start this daemon for us
+#%{_sysconfdir}/xdg/autostart/ibus.desktop
+%{_sysconfdir}/gconf/schemas/ibus.schemas
+%attr(755,root,root) %{_bindir}/ibus-daemon
+%attr(755,root,root) %{_bindir}/ibus-setup
+%attr(755,root,root) %{_libdir}/ibus-gconf
+%attr(755,root,root) %{_libdir}/ibus-ui-gtk
+%attr(755,root,root) %{_libdir}/ibus-x11
+%attr(755,root,root) %{_libdir}/ibus-engine-xkb
+%attr(755,root,root) %{_libdir}/ibus-xkb
+%dir %{py_sitescriptdir}/ibus
+%{py_sitescriptdir}/ibus/*
+%{_datadir}/ibus
+%{_desktopdir}/*
+%{_iconsdir}/hicolor/*/apps/*
+
+%files libs
+%defattr(644,root,root,755)
+%attr(755,root,root) %{_libdir}/libibus-1.0.so.*.*.*
+%attr(755,root,root) %ghost %{_libdir}/libibus-1.0.so.[0-9]
+%{_libdir}/girepository-1.0/IBus-1.0.typelib
+
+%files gtk2
+%defattr(644,root,root,755)
+%attr(755,root,root) %{_libdir}/gtk-2.0/*/immodules/im-ibus.so
+
+%files gtk3
+%defattr(644,root,root,755)
+%attr(755,root,root) %{_libdir}/gtk-3.0/*/immodules/im-ibus.so
+
+%files gnome3
+%defattr(644,root,root,755)
+%{_datadir}/gnome-shell/js/ui/status/ibus
+%{_datadir}/gnome-shell/extensions/ibus-indicator@example.com
+
+%files devel
+%defattr(644,root,root,755)
+%doc %{_gtkdocdir}/ibus
+%attr(755,root,root) %{_libdir}/lib*.so
+%{_pkgconfigdir}/ibus-1.0.pc
+%{_includedir}/ibus-1.0
+%{_datadir}/gir-1.0/IBus-1.0.gir
+%{_datadir}/vala/vapi/ibus-1.0.vapi
+%{_datadir}/vala/vapi/ibus-1.0.deps
diff --git a/ibus.xinputd b/ibus.xinputd
new file mode 100644 (file)
index 0000000..b00650d
--- /dev/null
@@ -0,0 +1,13 @@
+XIM=ibus
+XIM_PROGRAM="/usr/bin/ibus-daemon"
+ICON="ibus"
+XIM_ARGS="-r --xim"
+PREFERENCE_PROGRAM=/usr/bin/ibus-setup
+SHORT_DESC="IBus"
+GTK_IM_MODULE=ibus
+
+if [ -x /usr/@@LIB@@/qt4/plugins/inputmethods/libqtim-ibus.so ]; then
+       QT_IM_MODULE=ibus
+else
+       QT_IM_MODULE=xim
+fi
This page took 0.488122 seconds and 4 git commands to generate.