--- /dev/null
+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.</i></small></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.</i></small></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"><b>Keyboard Layout</b></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"><b>Keyboard Layout</b></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"><b>Keyboard Option</b></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"><b>Keyboard Option</b></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 <takao.fujiwara1@gmail.com></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
+
--- /dev/null
+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
+
--- /dev/null
+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
+