Blob Blame History Raw
From a3a7b364410511b3a17f2b1566ba4c4c4f0de2cf Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Tue, 21 Jun 2011 17:00:54 +0900
Subject: [PATCH] Add a bridge hotkey which use prev-next engines instead
 of on-off.

---
 bus/Makefile.am            |   20 ++--
 bus/ibusimpl.c             |  244 ++++++++++++++++++++++++++++----------
 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 +
 src/Makefile.am            |    1 +
 src/ibusbus.c              |    6 +
 src/ibusbus.h              |    9 ++
 src/ibusenginedesc.c       |    4 +
 src/ibushotkey.c           |   11 ++
 src/ibushotkey.h           |   11 ++
 ui/gtk/panel.py            |   60 +++++++++-
 xkb/Makefile.am            |    2 +
 xkb/ibus-engine-xkb-main.c |    8 ++
 xkb/xkbxml.c               |    8 +-
 19 files changed, 677 insertions(+), 361 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 074b456..0efaa1b 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 38d6d11..3b2d539 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,8 @@ struct _BusIBusImpl {
     /* engine-specific hotkeys */
     IBusHotkeyProfile *engines_hotkey_profile;
     GHashTable      *hotkey_to_engines_map;
+
+    IBusEngineDesc *prev_hotkey_engine;
 };
 
 struct _BusIBusImplClass {
@@ -285,6 +291,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 +505,17 @@ _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);
+        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);
@@ -1182,28 +1221,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 +1337,39 @@ _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 = ibus_engine_desc_get_hotkeys (current_desc);
+        if (!hotkeys || !*hotkeys) {
+            hotkeys = "Control+space";
+        }
+        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;
 }
 
 /**
@@ -2357,6 +2510,11 @@ bus_ibus_impl_filter_keyboard_shortcuts 
          * 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
          * engine in the list. */
+#if USE_BRIDGE_HOTKEY
+        if (ibus->prev_hotkey_engine) {
+            new_engine_desc = ibus->prev_hotkey_engine;
+        }
+#else
         GList *p = engine_list;
         for (; p->next != NULL; p = p->next) {
             if (current_engine_desc == (IBusEngineDesc *) p->data) {
@@ -2364,9 +2522,14 @@ bus_ibus_impl_filter_keyboard_shortcuts 
                 break;
             }
         }
+#endif
 
         if (current_engine_desc != new_engine_desc) {
+            ibus->prev_hotkey_engine = current_engine_desc;
             bus_ibus_impl_set_context_engine_from_desc (ibus, context, new_engine_desc);
+        } else {
+            g_warning ("The engine %s is registered twice in hotkeys",
+                       ibus_engine_desc_get_name (current_engine_desc));
         }
 
         return TRUE;
@@ -2470,14 +2633,6 @@ 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;
@@ -2489,40 +2644,7 @@ _add_engine_hotkey (IBusEngineDesc *engi
         return;
     }
 
-    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);
+    _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..a6974d4 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=""
+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/src/Makefile.am b/src/Makefile.am
index 6454522..443b0db 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -46,6 +46,7 @@ AM_CPPFLAGS =                                   \
     -DIBUS_DATA_DIR=\"$(pkgdatadir)\"           \
     -DIBUS_COMPILATION                          \
     -DISOCODES_PREFIX=\"$(ISOCODES_PREFIX)\"    \
+    -DUSE_BRIDGE_HOTKEY=$(USE_BRIDGE_HOTKEY)    \
     $(NULL)
 
 # ibus library
diff --git a/src/ibusbus.c b/src/ibusbus.c
index 39ad784..5a8f9a9 100644
--- a/src/ibusbus.c
+++ b/src/ibusbus.c
@@ -1902,3 +1902,9 @@ 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;
+}
diff --git a/src/ibusbus.h b/src/ibusbus.h
index 77d3916..4bdf760 100644
--- a/src/ibusbus.h
+++ b/src/ibusbus.h
@@ -971,5 +971,14 @@ 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);
+
 G_END_DECLS
 #endif
diff --git a/src/ibusenginedesc.c b/src/ibusenginedesc.c
index d3800e1..a9e68be 100644
--- a/src/ibusenginedesc.c
+++ b/src/ibusenginedesc.c
@@ -233,7 +233,11 @@ ibus_engine_desc_class_init (IBusEngineDescClass *class)
                     g_param_spec_string ("hotkeys",
                         "description hotkeys",
                         "The hotkeys of engine description",
+#if USE_BRIDGE_HOTKEY
+                        "Control+space",
+#else
                         "",
+#endif
                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
     /**
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..7f2edcd 100644
--- a/ui/gtk/panel.py
+++ b/ui/gtk/panel.py
@@ -133,6 +133,11 @@ 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_engine = None
+
         self.__xkblayout = ibus.XKBLayout(self.__config)
         use_xkb = self.__config.get_value("general", "use_system_keyboard_layout", False)
         if not use_xkb:
@@ -142,11 +147,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,6 +245,41 @@ 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 = None
+        if default_model == 'default':
+            default_model = self.__xkblayout.get_default_layout()[1]
+        layouts = default_layout.split(',')
+        group = self.__xkblayout.get_group()
+        layout = layouts[group]
+        model = None
+        if default_model != None and default_model != '':
+            models = default_model.split(',')
+            if group < models.length:
+                model = models[group]
+        registry = ibus.XKBConfigRegistry()
+        langs = registry.get_layout_lang()[layout]
+        lang = 'en'
+        im_icon = layout[:2]
+        if langs != None:
+            im_icon = langs[0][:2]
+            lang = str(langs[0])
+        if self.__disabled_engine == None:
+            self.__disabled_engine = registry.engine_desc_new(lang,
+                                                              self.__default_layout,
+                                                              'Default Layout',
+                                                              default_model,
+                                                              None)
+        if self.__focus_ic != None:
+            prev_engine = self.__focus_ic.get_engine()
+        if prev_engine == None or \
+           prev_engine.name != self.__disabled_engine.name:
+            self.__focus_ic.set_bridge_engine()
+
     def focus_in(self, ic):
         self.reset()
         self.__focus_ic = ibus.InputContext(self.__bus, ic)
@@ -240,6 +287,9 @@ class Panel(ibus.PanelBase):
         self.__language_bar.set_enabled(enabled)
 
         if not enabled:
+            if ibus.use_bridge_hotkey():
+                self.__set_default_layout_engine()
+
             self.__set_im_icon(ICON_KEYBOARD)
             self.__set_im_name(None)
             if self.__bus.get_use_sys_layout():
@@ -453,7 +503,12 @@ 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)
+            language = engine.language
+            if ibus.use_bridge_hotkey() and \
+                engine.name == ibus.DEFAULT_BRIDGE_ENGINE_NAME and \
+                self.__disabled_engine != None:
+                language = self.__disabled_engine.language
+            lang = ibus.get_language_name(language)
             item = gtk.ImageMenuItem("%s - %s" % (lang, engine.longname))
             if current_engine and current_engine.name == engine.name:
                 for widget in item.get_children():
@@ -471,7 +526,8 @@ class Panel(ibus.PanelBase):
         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)
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.4.4