481f2ee
From a8f02d4be33f87cf97039736407cd13e795b992b Mon Sep 17 00:00:00 2001
60148ca
From: fujiwarat <takao.fujiwara1@gmail.com>
481f2ee
Date: Fri, 18 Nov 2011 18:05:42 +0900
60148ca
Subject: [PATCH] Add XKB layouts
60148ca
60148ca
---
e0a5e11
 Makefile.am                  |    7 +
e0a5e11
 configure.ac                 |   56 +
481f2ee
 data/ibus.schemas.in         |   47 +
e0a5e11
 ibus-1.0.pc.in               |    2 +
e0a5e11
 ibus/Makefile.am             |   26 +
e0a5e11
 ibus/__init__.py             |    2 +
e0a5e11
 ibus/bus.py                  |    3 +
e0a5e11
 ibus/interface/iibus.py      |    3 +
481f2ee
 ibus/xkblayout.py.in         |  360 ++++
e0a5e11
 ibus/xkbxml.py.in            |  419 ++++
e0a5e11
 setup/Makefile.am            |    1 +
e0a5e11
 setup/enginecombobox.py      |    7 +-
e0a5e11
 setup/main.py                |    3 +
e0a5e11
 setup/setup.ui               |  609 ++++++-
e0a5e11
 setup/xkbsetup.py            |  457 +++++
e0a5e11
 src/Makefile.am              |    5 +
e0a5e11
 src/ibus.h                   |    1 +
e0a5e11
 src/ibusxkbxml.c             |  441 +++++
e0a5e11
 src/ibusxkbxml.h             |  172 ++
e0a5e11
 ui/gtk/panel.py              |   41 +
e0a5e11
 xkb/Makefile.am              |  107 +
e0a5e11
 xkb/Makefile.am.orig         |  104 +
e0a5e11
 xkb/gtkimcontextsimpleseqs.h | 4484 ++++++++++++++++++++++++++++++++++++++++++
e0a5e11
 xkb/ibus-engine-xkb-main.c   |  303 +++
e0a5e11
 xkb/ibus-engine-xkb-main.h   |   32 +
5fabd65
 xkb/ibus-simple-engine.c     | 1003 ++++++++++
5fabd65
 xkb/ibus-simple-engine.h     |   13 +
e0a5e11
 xkb/ibus-xkb-main.c          |  112 ++
e0a5e11
 xkb/xkblayout.xml.in         |   16 +
e0a5e11
 xkb/xkblayoutconfig.xml.in   |    6 +
e0a5e11
 xkb/xkblib.c                 |  327 +++
e0a5e11
 xkb/xkblib.h                 |   41 +
e0a5e11
 xkb/xkbxml.c                 |  345 ++++
e0a5e11
 xkb/xkbxml.h                 |  113 ++
481f2ee
 34 files changed, 9664 insertions(+), 4 deletions(-)
60148ca
 create mode 100644 ibus/xkblayout.py.in
60148ca
 create mode 100644 ibus/xkbxml.py.in
b95ef1a
 create mode 100644 setup/xkbsetup.py
63b857f
 create mode 100644 src/ibusxkbxml.c
63b857f
 create mode 100644 src/ibusxkbxml.h
60148ca
 create mode 100644 xkb/Makefile.am
e0a5e11
 create mode 100644 xkb/Makefile.am.orig
e0a5e11
 create mode 100644 xkb/gtkimcontextsimpleseqs.h
60148ca
 create mode 100644 xkb/ibus-engine-xkb-main.c
60148ca
 create mode 100644 xkb/ibus-engine-xkb-main.h
e0a5e11
 create mode 100644 xkb/ibus-simple-engine.c
e0a5e11
 create mode 100644 xkb/ibus-simple-engine.h
60148ca
 create mode 100644 xkb/ibus-xkb-main.c
60148ca
 create mode 100644 xkb/xkblayout.xml.in
60148ca
 create mode 100644 xkb/xkblayoutconfig.xml.in
60148ca
 create mode 100644 xkb/xkblib.c
60148ca
 create mode 100644 xkb/xkblib.h
60148ca
 create mode 100644 xkb/xkbxml.c
60148ca
 create mode 100644 xkb/xkbxml.h
60148ca
60148ca
diff --git a/Makefile.am b/Makefile.am
095f9c1
index ff0fabc..056ddfc 100644
60148ca
--- a/Makefile.am
60148ca
+++ b/Makefile.am
f0777a3
@@ -42,6 +42,12 @@ DAEMON_DIRS =       \
60148ca
 	$(NULL)
60148ca
 endif
60148ca
 
60148ca
+if ENABLE_XKB
f0777a3
+XKB_DIRS =          \
f0777a3
+	xkb             \
60148ca
+	$(NULL)
60148ca
+endif
60148ca
+
f0777a3
 if ENABLE_MEMCONF
f0777a3
 MEMCONF_DIRS = \
f0777a3
 	memconf \
e0a5e11
@@ -66,6 +72,7 @@ SUBDIRS =           \
f0777a3
 	$(DAEMON_DIRS)  \
f0777a3
 	$(PYTHON_DIRS)  \
f0777a3
 	$(GCONF_DIRS)   \
f0777a3
+	$(XKB_DIRS)     \
60148ca
 	$(MEMCONF_DIRS) \
e0a5e11
 	$(DCONF_DIRS)   \
60148ca
 	$(NULL)
60148ca
diff --git a/configure.ac b/configure.ac
5f53fe6
index f452666..227e28e 100644
60148ca
--- a/configure.ac
60148ca
+++ b/configure.ac
5f53fe6
@@ -221,6 +221,60 @@ else
60148ca
     enable_xim="no (disabled, use --enable-xim to enable)"
60148ca
 fi
60148ca
 
60148ca
+AC_ARG_ENABLE(xkb,
60148ca
+    AS_HELP_STRING([--disable-xkb],
60148ca
+                   [Do not build xkb]),
60148ca
+    [enable_xkb=$enableval],
60148ca
+    [enable_xkb=yes]
60148ca
+)
60148ca
+
60148ca
+AM_CONDITIONAL([ENABLE_XKB], [test x"$enable_xkb" = x"yes"])
60148ca
+if test x"$enable_xkb" = x"yes"; then
60148ca
+    PKG_CHECK_MODULES(X11, [
60148ca
+        x11
60148ca
+    ])
60148ca
+    PKG_CHECK_MODULES(XKB,
60148ca
+        [xkbfile],,
60148ca
+        [XKB_LIBS="-lxkbfile"]
60148ca
+    )
60148ca
+    AC_DEFINE(HAVE_XKB, 1, [define to 1 if you have xkbfile])
84d8da5
+    HAVE_IBUS_XKB=true
60148ca
+else
60148ca
+    enable_xkb="no (disabled, use --enable-xkb to enable)"
84d8da5
+    HAVE_IBUS_XKB=false
60148ca
+fi
84d8da5
+AC_SUBST(HAVE_IBUS_XKB)
60148ca
+
60148ca
+# define XKB rules file
60148ca
+AC_ARG_WITH(xkb-rules-xml,
60148ca
+    AS_HELP_STRING([--with-xkb-rules-xml[=$DIR/evdev.xml]],
60148ca
+        [Set evdev.xml file path (default: /usr/share/X11/xkb/rules/evdev.xml)]),
60148ca
+    XKB_RULES_XML_FILE=$with_xkb_rules_xml,
60148ca
+    XKB_RULES_XML_FILE="/usr/share/X11/xkb/rules/evdev.xml"
60148ca
+)
60148ca
+AC_DEFINE_UNQUOTED(XKB_RULES_XML_FILE, "$XKB_RULES_XML_FILE",
60148ca
+    [Define file path of evdev.xml])
60148ca
+AC_SUBST(XKB_RULES_XML_FILE)
60148ca
+
60148ca
+# define XKB preload layouts
60148ca
+AC_ARG_WITH(xkb-preload-layouts,
60148ca
+    AS_HELP_STRING([--with-xkb-preload-layouts[=layout,...]],
60148ca
+        [Set preload xkb layouts (default: us,fr,de,...)]),
60148ca
+    XKB_PRELOAD_LAYOUTS=$with_xkb_preload_layouts,
60148ca
+    [XKB_PRELOAD_LAYOUTS=""\
e5da135
+"us,us(chr),us(dvorak),ad,al,am,ara,az,ba,bd,be,bg,br,bt,by,"\
b95ef1a
+"de,dk,ca,ch,cn(tib),cz,ee,epo,es,et,fi,fo,fr,"\
60148ca
+"gb,ge,ge(dsb),ge(ru),ge(os),gh,gh(akan),gh(ewe),gh(fula),gh(ga),gh(hausa),"\
60148ca
+"gn,gr,hu,hr,ie,ie(CloGaelach),il,"\
60148ca
+"in,in(ben),in(guj),in(guru),in(jhelum),in(kan),in(mal),in(ori),in(tam),"\
b95ef1a
+"in(tel),in(urd-phonetic),in(bolnagri),iq,iq(ku),ir,ir(ku),is,it,"\
b95ef1a
+"kg,kh,kz,la,latam,lk,lk(tam_unicode),lt,lv,ma,ma(tifinagh),mal,mao,"\
60148ca
+"me,mk,mm,mt,mv,ng,ng(hausa),ng,ng(igbo),ng(yoruba),nl,no,no(smi),np,"\
60148ca
+"pk,pl,pl(csb),pt,ro,rs,ru,ru(cv),ru(kom),ru(sah),ru(tt),ru(xal),"\
60148ca
+"se,si,sk,sy,sy(ku),th,tj,tr,ua,uz,vn"]
60148ca
+)
60148ca
+AC_SUBST(XKB_PRELOAD_LAYOUTS)
60148ca
+
60148ca
 # GObject introspection
60148ca
 GOBJECT_INTROSPECTION_CHECK([0.6.8])
60148ca
 
5f53fe6
@@ -478,6 +532,7 @@ bindings/Makefile
60148ca
 bindings/vala/Makefile
e0a5e11
 dconf/Makefile
e0a5e11
 dconf/dconf.xml.in
60148ca
+xkb/Makefile
60148ca
 ])
60148ca
 
60148ca
 AC_OUTPUT
5f53fe6
@@ -493,6 +548,7 @@ Build options:
60148ca
   Build gtk2 immodule       $enable_gtk2
60148ca
   Build gtk3 immodule       $enable_gtk3
60148ca
   Build XIM agent server    $enable_xim
60148ca
+  Build XKB                 $enable_xkb
60148ca
   Build python modules      $enable_python
60148ca
   Build gconf modules       $enable_gconf
60148ca
   Build memconf modules     $enable_memconf
60148ca
diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in
481f2ee
index 663358c..e0a6a37 100644
60148ca
--- a/data/ibus.schemas.in
60148ca
+++ b/data/ibus.schemas.in
481f2ee
@@ -191,6 +191,53 @@
60148ca
       </locale>
60148ca
     </schema>
60148ca
     <schema>
7232d5e
+      <key>/schemas/desktop/ibus/general/system_keyboard_layout</key>
7232d5e
+      <applyto>/desktop/ibus/general/system_keyboard_layout</applyto>
60148ca
+      <owner>ibus</owner>
60148ca
+      <type>string</type>
60148ca
+      <default>default</default>
60148ca
+      <gettext_domain>ibus</gettext_domain>
60148ca
+      <locale name="C">
7232d5e
+        <short>Set system keyboard layout</short>
60148ca
+            <long>Override default system keyboard layout. default is 'default'</long>
60148ca
+      </locale>
60148ca
+    </schema>
60148ca
+    <schema>
7232d5e
+      <key>/schemas/desktop/ibus/general/system_keyboard_option</key>
7232d5e
+      <applyto>/desktop/ibus/general/system_keyboard_option</applyto>
7232d5e
+      <owner>ibus</owner>
7232d5e
+      <type>string</type>
7232d5e
+      <default>default</default>
7232d5e
+      <gettext_domain>ibus</gettext_domain>
7232d5e
+      <locale name="C">
7232d5e
+        <short>Set system keyboard option</short>
7232d5e
+            <long>Override default system keyboard option. default is 'default'</long>
7232d5e
+      </locale>
7232d5e
+    </schema>
7232d5e
+    <schema>
481f2ee
+      <key>/schemas/desktop/ibus/general/use_xmodmap</key>
481f2ee
+      <applyto>/desktop/ibus/general/use_xmodmap</applyto>
481f2ee
+      <owner>ibus</owner>
481f2ee
+      <type>bool</type>
481f2ee
+      <default>true</default>
481f2ee
+      <locale name="C">
481f2ee
+        <short>Use xmodmap</short>
481f2ee
+           <long>Run xmodmap if .xmodmap/.Xmodmap exists.</long>
481f2ee
+      </locale>
481f2ee
+    </schema>
481f2ee
+    <schema>
60148ca
+      <key>/schemas/desktop/ibus/general/xkb_latin_layouts</key>
60148ca
+      <applyto>/desktop/ibus/general/xkb_latin_layouts</applyto>
60148ca
+      <owner>ibus</owner>
60148ca
+      <type>list</type>
60148ca
+      <list_type>string</list_type>
b95ef1a
+      <default>[ara,bg,cz,dev,gr,gur,in,jp(kana),mal,mkd,ru,ua]</default>
60148ca
+      <locale name="C">
60148ca
+        <short>Latin layout which have no ASCII</short>
60148ca
+	    <long>us layout is appended to the latin layouts. variant is not needed.</long>
60148ca
+      </locale>
60148ca
+    </schema>
60148ca
+    <schema>
60148ca
       <key>/schemas/desktop/ibus/panel/use_custom_font</key>
60148ca
       <applyto>/desktop/ibus/panel/use_custom_font</applyto>
60148ca
       <owner>ibus</owner>
84d8da5
diff --git a/ibus-1.0.pc.in b/ibus-1.0.pc.in
84d8da5
index 9f593ab..51eb67a 100644
84d8da5
--- a/ibus-1.0.pc.in
84d8da5
+++ b/ibus-1.0.pc.in
84d8da5
@@ -4,6 +4,8 @@ libdir=@libdir@
84d8da5
 includedir=@includedir@
84d8da5
 datadir=@datadir@
84d8da5
 pkgdatadir=@datadir@/ibus
84d8da5
+have_ibus_xkb=@HAVE_IBUS_XKB@
84d8da5
+ibus_xkb=@libexecdir@/ibus-xkb
84d8da5
 
84d8da5
 Name: IBus
84d8da5
 Description: IBus Library
60148ca
diff --git a/ibus/Makefile.am b/ibus/Makefile.am
e5da135
index c71df1b..508c98f 100644
60148ca
--- a/ibus/Makefile.am
60148ca
+++ b/ibus/Makefile.am
60148ca
@@ -58,12 +58,38 @@ nodist_ibus_PYTHON = \
60148ca
 
60148ca
 ibusdir = @pkgpythondir@
60148ca
 
60148ca
+xkblayout_py_in_files = \
60148ca
+	xkblayout.py.in \
60148ca
+	xkbxml.py.in \
60148ca
+	$(NULL)
60148ca
+xkblayout_py_DATA = $(xkblayout_py_in_files:.py.in=.py)
60148ca
+xkblayout_pydir = @pkgpythondir@
60148ca
+
60148ca
+ibus_PYTHON += $(xkblayout_py_DATA)
60148ca
+
60148ca
+if ENABLE_XKB
cf977d1
+XKB_COMMAND=\\\"$(libexecdir)/ibus-xkb\\\"
60148ca
+HAVE_XKB=True
60148ca
+else
60148ca
+XKB_COMMAND="None"
60148ca
+HAVE_XKB=False
60148ca
+endif
60148ca
+
60148ca
+%.py : %.py.in
60148ca
+	@sed -e "s|\@XKB_COMMAND\@|$(XKB_COMMAND)|g" \
60148ca
+	     -e "s|\@XKB_RULES_XML_FILE\@|$(XKB_RULES_XML_FILE)|g" \
60148ca
+	     -e "s|\@HAVE_XKB\@|$(HAVE_XKB)|g" \
60148ca
+	     -e "s|\@datadir\@|$(datadir)|g" \
60148ca
+	$< > $@
60148ca
+
60148ca
 EXTRA_DIST = \
60148ca
 	_config.py.in \
60148ca
+	$(xkblayout_py_in_files) \
60148ca
 	$(NULL)
60148ca
 
60148ca
 CLEANFILES = \
60148ca
 	*.pyc \
60148ca
+	$(xkblayout_py_DATA) \
60148ca
 	$(NULL)
60148ca
 
60148ca
 DISTCLEANFILES = \
60148ca
diff --git a/ibus/__init__.py b/ibus/__init__.py
60148ca
index 7c8f8be..3c25605 100644
60148ca
--- a/ibus/__init__.py
60148ca
+++ b/ibus/__init__.py
60148ca
@@ -41,4 +41,6 @@ from text import *
60148ca
 from observedpath import *
60148ca
 from enginedesc import *
60148ca
 from component import *
60148ca
+from xkblayout import *
60148ca
+from xkbxml import *
60148ca
 from _config import *
60148ca
diff --git a/ibus/bus.py b/ibus/bus.py
5f53fe6
index a8a458d..84b4140 100644
60148ca
--- a/ibus/bus.py
60148ca
+++ b/ibus/bus.py
5f53fe6
@@ -163,6 +163,9 @@ class Bus(object.Object):
60148ca
             data = serializable.deserialize_object(data)
60148ca
         return data
60148ca
 
60148ca
+    def get_use_sys_layout(self):
60148ca
+        return self.__ibus.GetUseSysLayout();
60148ca
+
60148ca
     def introspect_ibus(self):
60148ca
         return self.__ibus.Introspect()
60148ca
 
60148ca
diff --git a/ibus/interface/iibus.py b/ibus/interface/iibus.py
9d6bb4f
index 678d517..7de56fc 100644
60148ca
--- a/ibus/interface/iibus.py
60148ca
+++ b/ibus/interface/iibus.py
9d6bb4f
@@ -75,6 +75,9 @@ class IIBus(dbus.service.Object):
60148ca
     @method(in_signature="v", out_signature="v")
60148ca
     def Ping(self, data, dbusconn): pass
60148ca
 
60148ca
+    @method(out_signature="b")
60148ca
+    def GetUseSysLayout(self, dbusconn): pass
60148ca
+
60148ca
     @signal(signature="")
60148ca
     def RegistryChanged(self): pass
60148ca
 
60148ca
diff --git a/ibus/xkblayout.py.in b/ibus/xkblayout.py.in
60148ca
new file mode 100644
481f2ee
index 0000000..c0c95ce
60148ca
--- /dev/null
60148ca
+++ b/ibus/xkblayout.py.in
481f2ee
@@ -0,0 +1,360 @@
60148ca
+# vim:set et sts=4 sw=4:
60148ca
+#
60148ca
+# ibus - The Input Bus
60148ca
+#
63b857f
+# Copyright (c) 2011 Takao Fujiwara <takao.fujiwara1@gmail.com>
63b857f
+# Copyright (c) 2011 Peng Huang <shawn.p.huang@gmail.com>
63b857f
+# Copyright (c) 2011 Red Hat, Inc.
60148ca
+#
60148ca
+# This library is free software; you can redistribute it and/or
60148ca
+# modify it under the terms of the GNU Lesser General Public
60148ca
+# License as published by the Free Software Foundation; either
60148ca
+# version 2 of the License, or (at your option) any later version.
60148ca
+#
60148ca
+# This library is distributed in the hope that it will be useful,
60148ca
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
60148ca
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
60148ca
+# GNU Lesser General Public License for more details.
60148ca
+#
60148ca
+# You should have received a copy of the GNU Lesser General Public
60148ca
+# License along with this program; if not, write to the
60148ca
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
60148ca
+# Boston, MA  02111-1307  USA
60148ca
+
60148ca
+__all__ = (
095f9c1
+        'XKBLayout',
60148ca
+    )
60148ca
+
cf977d1
+import os, sys, time
481f2ee
+import glib
60148ca
+
60148ca
+XKB_COMMAND = @XKB_COMMAND@
cf977d1
+XKB_SESSION_TIME_OUT = 30.0
481f2ee
+XMODMAP_CMD = 'xmodmap'
481f2ee
+XMODMAP_KNOWN_FILES = ['.xmodmap', '.xmodmaprc', '.Xmodmap', '.Xmodmaprc']
60148ca
+
60148ca
+class XKBLayout():
60148ca
+    def __init__(self, config = None, command=XKB_COMMAND):
60148ca
+        self.__config = config
60148ca
+        self.__command = command
7232d5e
+        self.__use_xkb = True
481f2ee
+        self.__xkb_pid = None
481f2ee
+        self.__xmodmap_pid = None
481f2ee
+        self.__use_xmodmap = True
7232d5e
+        if self.__command == None:
7232d5e
+            self.__use_xkb = False
60148ca
+        self.__default_layout = self.get_layout()
7232d5e
+        self.__default_model = self.get_model()
7232d5e
+        self.__default_option = self.get_option()
cf977d1
+        self.__time_lag_session_xkb_layout = True
cf977d1
+        self.__time_lag_session_xkb_option = True
cf977d1
+        self.__time_lag_session_xkb_timer = time.time()
9b3b748
+        self.__xkb_latin_layouts = []
9b3b748
+        if config != None:
095f9c1
+            self.__xkb_latin_layouts = list(self.__config.get_value('general',
095f9c1
+                                                                    'xkb_latin_layouts',
9b3b748
+                                                                    []))
481f2ee
+            self.__use_xmodmap = bool(self.__config.get_value('general',
481f2ee
+                                                              'use_xmodmap',
481f2ee
+                                                              True))
60148ca
+
60148ca
+
7232d5e
+    def __get_model_from_layout(self, layout):
47affbc
+        left_bracket = layout.find('(')
47affbc
+        right_bracket = layout.find(')')
47affbc
+        if left_bracket >= 0 and right_bracket > left_bracket:
095f9c1
+            return (layout[:left_bracket] + layout[right_bracket + 1:], \
47affbc
+                    layout[left_bracket + 1:right_bracket])
095f9c1
+        return (layout, 'default')
095f9c1
+
095f9c1
+    def __get_option_from_layout(self, layout):
095f9c1
+        left_bracket = layout.find('[')
095f9c1
+        right_bracket = layout.find(']')
095f9c1
+        if left_bracket >= 0 and right_bracket > left_bracket:
095f9c1
+            return (layout[:left_bracket] + layout[right_bracket + 1:], \
095f9c1
+                    layout[left_bracket + 1:right_bracket])
095f9c1
+        return (layout, 'default')
7232d5e
+
63b857f
+    def __get_output_from_cmdline(self, arg, string):
095f9c1
+        exec_command = '%s %s' % (self.__command, arg)
63b857f
+        retval = None
63b857f
+        for line in os.popen(exec_command).readlines():
63b857f
+            line = line.strip()
63b857f
+            if line.startswith(string):
63b857f
+                retval = line[len(string):]
63b857f
+                break
63b857f
+        return retval
63b857f
+
481f2ee
+    def __get_userhome(self):
481f2ee
+        if 'HOME' not in os.environ:
481f2ee
+            import pwd
481f2ee
+            userhome = pwd.getpwuid(os.getuid()).pw_dir
481f2ee
+        else:
481f2ee
+            userhome = os.environ['HOME']
481f2ee
+        userhome = userhome.rstrip('/')
481f2ee
+        return userhome
481f2ee
+
481f2ee
+    def __get_fullpath(self, command):
481f2ee
+        if 'PATH' in os.environ:
481f2ee
+            envpath = os.environ['PATH']
481f2ee
+        else:
481f2ee
+            envpath = os.defpath
481f2ee
+        PATH = envpath.split(os.pathsep)
481f2ee
+        for dir in PATH:
481f2ee
+            filepath = os.path.join(dir, command)
481f2ee
+            if os.path.exists(filepath):
481f2ee
+                return filepath
481f2ee
+        return None
481f2ee
+
481f2ee
+    def __set_layout_cb(self, pid, status):
481f2ee
+        if self.__xkb_pid != pid:
481f2ee
+            print >> sys.stderr, \
481f2ee
+                'ibus.xkblayout: set_layout_cb has another pid'
481f2ee
+            return
481f2ee
+        self.__xkb_pid.close()
481f2ee
+        self.__xkb_pid = None
481f2ee
+        self.set_xmodmap()
481f2ee
+
481f2ee
+    def __set_xmodmap_cb(self, pid, status):
481f2ee
+        if self.__xmodmap_pid != pid:
481f2ee
+            print >> sys.stderr, \
481f2ee
+                'ibus.xkblayout: set_xmodmap_cb has another pid'
481f2ee
+            return
481f2ee
+        self.__xmodmap_pid.close()
481f2ee
+        self.__xmodmap_pid = None
481f2ee
+
7232d5e
+    def use_xkb(self, enable):
60148ca
+        if self.__command == None:
7232d5e
+            return
7232d5e
+        self.__use_xkb = enable
7232d5e
+
7232d5e
+    def get_layout(self):
7232d5e
+        if not self.__use_xkb:
60148ca
+            return None
095f9c1
+        return self.__get_output_from_cmdline('--get', 'layout: ')
60148ca
+
7232d5e
+    def get_model(self):
7232d5e
+        if not self.__use_xkb:
7232d5e
+            return None
095f9c1
+        return self.__get_output_from_cmdline('--get', 'model: ')
7232d5e
+
7232d5e
+    def get_option(self):
7232d5e
+        if not self.__use_xkb:
7232d5e
+            return None
095f9c1
+        return self.__get_output_from_cmdline('--get', 'option: ')
63b857f
+
63b857f
+    def get_group(self):
63b857f
+        if not self.__use_xkb:
63b857f
+            return 0
095f9c1
+        return int(self.__get_output_from_cmdline('--get-group', 'group: '))
7232d5e
+
095f9c1
+    def set_layout(self, layout='default', model='default', option='default'):
7232d5e
+        if not self.__use_xkb:
7232d5e
+            return
7232d5e
+        if layout == None:
60148ca
+            return
1e452ff
+        if self.__default_layout == None:
1e452ff
+            # Maybe opening display was failed in constructor.
1e452ff
+            self.reload_default_layout()
1e452ff
+        if self.__default_layout == None:
1e452ff
+            return
60148ca
+        layout = str(layout)
cf977d1
+        # if set_default_layout() is not default, the default layout is
cf977d1
+        # pulled from the current XKB. But it's possible gnome-settings-daemon
cf977d1
+        # does not run yet. I added XKB_SESSION_TIME_OUT for the timer.
cf977d1
+        if self.__time_lag_session_xkb_layout == True:
cf977d1
+            self.__default_layout = self.get_layout()
cf977d1
+            self.__default_model = self.get_model()
cf977d1
+        if self.__time_lag_session_xkb_option == True:
cf977d1
+            self.__default_option = self.get_option()
cf977d1
+        if (self.__time_lag_session_xkb_layout == True or \
cf977d1
+            self.__time_lag_session_xkb_option == True ) and \
84d8da5
+           (time.time() - self.__time_lag_session_xkb_timer \
cf977d1
+            > XKB_SESSION_TIME_OUT):
cf977d1
+            self.__time_lag_session_xkb_layout = False
cf977d1
+            self.__time_lag_session_xkb_option = False
095f9c1
+        if layout == 'default':
60148ca
+            layout = self.__default_layout
cf977d1
+        else:
cf977d1
+            self.__time_lag_session_xkb_layout = False
7232d5e
+        if model != None:
7232d5e
+            model = str(model)
095f9c1
+            if model == 'default':
7232d5e
+                (layout, model) = self.__get_model_from_layout(layout)
095f9c1
+            if model == 'default':
7232d5e
+                model = self.__default_model
cf977d1
+            else:
cf977d1
+                self.__time_lag_session_xkb_layout = False
7232d5e
+        if option != None:
7232d5e
+            option = str(option)
095f9c1
+            if option == 'default':
095f9c1
+                (layout, engine_option) = self.__get_option_from_layout(layout)
095f9c1
+                if engine_option != None and engine_option != 'default':
095f9c1
+                    option = self.__default_option
095f9c1
+                    if option == None:
095f9c1
+                        option = engine_option
095f9c1
+                    else:
095f9c1
+                        option = '%s,%s' % (option, engine_option)
095f9c1
+                    self.__time_lag_session_xkb_option = False
095f9c1
+            if option == 'default':
7232d5e
+                option = self.__default_option
60148ca
+        need_us_layout = False
60148ca
+        for latin_layout in self.__xkb_latin_layouts:
60148ca
+            latin_layout = str(latin_layout)
3ef409f
+            # layout 'in' and model 'eng' is English layout.
3ef409f
+            if layout == latin_layout and model != 'eng':
60148ca
+                need_us_layout = True
60148ca
+                break
b95ef1a
+            if model != None and layout + '(' + model + ')' == latin_layout:
b95ef1a
+                need_us_layout = True
b95ef1a
+                break
60148ca
+        if need_us_layout:
095f9c1
+            layout = layout + ',us'
7232d5e
+            if model != None:
095f9c1
+                model = model + ','
7232d5e
+        if layout == self.get_layout() and \
7232d5e
+           model == self.get_model() and \
7232d5e
+           option == self.get_option():
9b3b748
+            return
9b3b748
+        args = []
9b3b748
+        args.append(self.__command)
095f9c1
+        args.append('--layout')
60148ca
+        args.append(layout)
7232d5e
+        if model != None:
095f9c1
+            args.append('--model')
7232d5e
+            args.append(model)
7232d5e
+        if option != None:
095f9c1
+            args.append('--option')
7232d5e
+            args.append(option)
481f2ee
+        pid = glib.spawn_async(argv=args,
481f2ee
+                               flags=glib.SPAWN_DO_NOT_REAP_CHILD)[0]
481f2ee
+        self.__xkb_pid = pid
481f2ee
+        glib.child_watch_add(self.__xkb_pid, self.__set_layout_cb)
60148ca
+
095f9c1
+    def set_default_layout(self, layout='default', model='default'):
7232d5e
+        if not self.__use_xkb:
60148ca
+            return
cf977d1
+        if layout == None:
095f9c1
+            print >> sys.stderr, 'ibus.xkblayout: None layout'
cf977d1
+            return
cf977d1
+        if model == None:
095f9c1
+            print >> sys.stderr, 'ibus.xkblayout: None model'
cf977d1
+            return
60148ca
+        if layout == 'default':
60148ca
+            self.__default_layout = self.get_layout()
b95ef1a
+            self.__default_model = self.get_model()
60148ca
+        else:
b95ef1a
+            if model == 'default':
b95ef1a
+                (layout, model) = self.__get_model_from_layout(layout)
60148ca
+            self.__default_layout = layout
cf977d1
+            self.__time_lag_session_xkb_layout = False
b95ef1a
+            if model == 'default':
b95ef1a
+                self.__default_model = None
b95ef1a
+            else:
b95ef1a
+                self.__default_model = model
60148ca
+
095f9c1
+    def set_default_option(self, option='default'):
7232d5e
+        if not self.__use_xkb:
7232d5e
+            return
cf977d1
+        if option == None:
095f9c1
+            print >> sys.stderr, 'ibus.xkblayout: None option'
cf977d1
+            return
7232d5e
+        if option == 'default':
7232d5e
+            self.__default_option = self.get_option()
7232d5e
+        else:
7232d5e
+            self.__default_option = option
cf977d1
+            self.__time_lag_session_xkb_option = False
7232d5e
+
63b857f
+    def get_default_layout(self):
63b857f
+        return [self.__default_layout, self.__default_model];
63b857f
+
63b857f
+    def get_default_option(self):
63b857f
+        return self.__default_option
63b857f
+
60148ca
+    def reload_default_layout(self):
7232d5e
+        if not self.__use_xkb:
60148ca
+            return
60148ca
+        self.__default_layout = self.get_layout()
7232d5e
+        self.__default_model = self.get_model()
7232d5e
+        self.__default_option = self.get_option()
481f2ee
+
481f2ee
+    def set_xmodmap(self):
481f2ee
+        if not self.__use_xmodmap:
481f2ee
+            return
481f2ee
+        if self.__xmodmap_pid != None:
481f2ee
+            return
481f2ee
+        xmodmap_cmdpath = self.__get_fullpath(XMODMAP_CMD)
481f2ee
+        if xmodmap_cmdpath == None:
481f2ee
+            xmodmap_cmdpath = XMODMAP_CMD
481f2ee
+        for xmodmap_file in XMODMAP_KNOWN_FILES:
481f2ee
+            xmodmap_filepath = os.path.join(self.__get_userhome(), xmodmap_file)
481f2ee
+            if not os.path.exists(xmodmap_filepath):
481f2ee
+                continue
481f2ee
+            pid = glib.spawn_async(argv=[xmodmap_cmdpath, xmodmap_filepath],
481f2ee
+                                   flags=glib.SPAWN_DO_NOT_REAP_CHILD)[0]
481f2ee
+            self.__xmodmap_pid = pid
481f2ee
+            glib.child_watch_add(self.__xmodmap_pid, self.__set_xmodmap_cb)
481f2ee
+            break
481f2ee
+
481f2ee
+
481f2ee
+def test():
481f2ee
+    import gtk
481f2ee
+    import ibus
481f2ee
+
481f2ee
+    window = None
481f2ee
+    config = None
481f2ee
+    xkblayout = None
481f2ee
+
481f2ee
+    def __destroy(*args):
481f2ee
+        window.hide()
481f2ee
+        gtk.main_quit()
481f2ee
+
481f2ee
+    def __test_set_session_xkb(button):
481f2ee
+        xkblayout.set_layout('default')
481f2ee
+        print 'Reset the default keymap'
481f2ee
+        print 'Layout:', xkblayout.get_default_layout()
481f2ee
+        print 'Option:', xkblayout.get_default_option()
481f2ee
+
481f2ee
+    def __test_set_user_xkb(button):
481f2ee
+        layout = 'us'
481f2ee
+        model = 'default'
481f2ee
+        option = 'default'
481f2ee
+        if len(sys.argv) > 1:
481f2ee
+            layout = sys.argv[1]
481f2ee
+        if len(sys.argv) > 2:
481f2ee
+            model = sys.argv[2]
481f2ee
+            if model == 'None':
481f2ee
+                model = None
481f2ee
+        if len(sys.argv) > 3:
481f2ee
+            option = sys.argv[3]
481f2ee
+            if option == 'None':
481f2ee
+                optoin = None
481f2ee
+        xkblayout.set_layout(layout, model, option)
481f2ee
+        print 'Test set_layout:', layout, model, option
481f2ee
+
481f2ee
+    if ibus.get_address() != None:
481f2ee
+        bus = ibus.Bus()
481f2ee
+        config = bus.get_config()
481f2ee
+    else:
481f2ee
+        print 'no ibus'
481f2ee
+    xkblayout = XKBLayout(config)
481f2ee
+    print 'Layout:', xkblayout.get_default_layout()
481f2ee
+    print 'Option:', xkblayout.get_default_option()
481f2ee
+    window = gtk.Window(gtk.WINDOW_TOPLEVEL)
481f2ee
+    window.connect('destroy', __destroy)
481f2ee
+    vb = gtk.VBox()
481f2ee
+    window.add(vb)
481f2ee
+    b = gtk.Button('Test xkb')
481f2ee
+    b.connect('clicked', __test_set_user_xkb)
481f2ee
+    vb.add(b)
481f2ee
+    b = gtk.Button('Reset the default xkb')
481f2ee
+    b.connect('clicked', __test_set_session_xkb)
481f2ee
+    vb.add(b)
481f2ee
+    window.show_all()
481f2ee
+    if config != None:
481f2ee
+        ibus.main()
481f2ee
+    else:
481f2ee
+        gtk.main()
481f2ee
+
481f2ee
+if __name__ == '__main__':
481f2ee
+    test()
60148ca
diff --git a/ibus/xkbxml.py.in b/ibus/xkbxml.py.in
60148ca
new file mode 100644
0318d1b
index 0000000..9407c13
60148ca
--- /dev/null
60148ca
+++ b/ibus/xkbxml.py.in
0318d1b
@@ -0,0 +1,419 @@
60148ca
+# vim:set et sts=4 sw=4:
60148ca
+#
60148ca
+# ibus - The Input Bus
60148ca
+#
63b857f
+# Copyright (c) 2011 Takao Fujiwara <takao.fujiwara1@gmail.com>
63b857f
+# Copyright (c) 2011 Peng Huang <shawn.p.huang@gmail.com>
63b857f
+# Copyright (c) 2011 Red Hat, Inc.
60148ca
+#
60148ca
+# This library is free software; you can redistribute it and/or
60148ca
+# modify it under the terms of the GNU Lesser General Public
60148ca
+# License as published by the Free Software Foundation; either
60148ca
+# version 2 of the License, or (at your option) any later version.
60148ca
+#
60148ca
+# This library is distributed in the hope that it will be useful,
60148ca
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
60148ca
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
60148ca
+# GNU Lesser General Public License for more details.
60148ca
+#
60148ca
+# You should have received a copy of the GNU Lesser General Public
60148ca
+# License along with this program; if not, write to the
60148ca
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
60148ca
+# Boston, MA  02111-1307  USA
60148ca
+
60148ca
+__all__ = (
095f9c1
+        'XKBConfigRegistry',
095f9c1
+        'XKBLayoutConfig',
60148ca
+    )
60148ca
+
60148ca
+import os
60148ca
+import string
60148ca
+import xml.sax as sax
60148ca
+import enginedesc
60148ca
+from xml.sax.saxutils import XMLFilterBase, XMLGenerator
60148ca
+from xml.sax._exceptions import SAXParseException
60148ca
+from cStringIO import StringIO
60148ca
+
60148ca
+try:
60148ca
+    from glib import get_user_config_dir
60148ca
+except ImportError:
60148ca
+    get_user_config_dir = lambda : None
60148ca
+
095f9c1
+XKB_RULES_XML_FILE = '@XKB_RULES_XML_FILE@'
60148ca
+
60148ca
+class XKBConfigRegistryHandler(XMLFilterBase):
60148ca
+    def __init__(self, parser=None, root='root'):
60148ca
+        XMLFilterBase.__init__(self, parser)
60148ca
+        self.__root = root
60148ca
+        self.__current_node = root
7232d5e
+        self.__layoutlist_array = {}
7232d5e
+        self.__layoutlist = False
60148ca
+        self.__layout = False
60148ca
+        self.__layout_label = None
60148ca
+        self.__layout_desc = {}
60148ca
+        self.__layout_lang = {}
60148ca
+        self.__variantlist = False
60148ca
+        self.__variant = False
60148ca
+        self.__variant_label = None
60148ca
+        self.__variant_desc = {}
7232d5e
+        self.__optionlist_array = {}
7232d5e
+        self.__optionlist = False
7232d5e
+        self.__option_group_desc = {}
7232d5e
+        self.__option_desc = {}
7232d5e
+        self.__option = False
7232d5e
+        self.__option_label = None
7232d5e
+        self.__group = False
7232d5e
+        self.__group_label = None
7232d5e
+
7232d5e
+    def __characters_layoutlist(self, text):
60148ca
+        if not self.__layout:
60148ca
+            return
60148ca
+        if self.__variant:
095f9c1
+            if self.__current_node == 'name':
60148ca
+                self.__variant_label = text
60148ca
+                if self.__layout_label != None and \
7232d5e
+                   self.__layout_label in self.__layoutlist_array:
7232d5e
+                    self.__layoutlist_array[self.__layout_label].append(text)
095f9c1
+            elif self.__current_node == 'description':
095f9c1
+                label =  '%s(%s)' % (self.__layout_label, self.__variant_label)
e5da135
+                self.__variant_desc[label] = text
095f9c1
+            elif self.__current_node == 'iso639Id':
60148ca
+                label = self.__layout_label
60148ca
+                if label != None:
095f9c1
+                    label = '%s(%s)' % (label, self.__variant_label)
60148ca
+                else:
60148ca
+                    label = self.__variant_label
60148ca
+                if label not in self.__layout_lang:
60148ca
+                    self.__layout_lang[label] = []
60148ca
+                self.__layout_lang[label].append(text)
60148ca
+            else:
60148ca
+                pass
60148ca
+        else:
095f9c1
+            if self.__current_node == 'name':
60148ca
+                self.__layout_label = text
7232d5e
+                self.__layoutlist_array[self.__layout_label] = []
095f9c1
+            elif self.__current_node == 'description':
60148ca
+                self.__layout_desc[self.__layout_label] = text
095f9c1
+            elif self.__current_node == 'iso639Id':
60148ca
+                if self.__layout_label not in self.__layout_lang:
60148ca
+                    self.__layout_lang[self.__layout_label] = []
60148ca
+                self.__layout_lang[self.__layout_label].append(text)
60148ca
+            else:
60148ca
+                pass
60148ca
+
7232d5e
+    def __characters_optionlist(self, text):
7232d5e
+        if not self.__group:
7232d5e
+            return
7232d5e
+        if self.__option:
095f9c1
+            if self.__current_node == 'name':
7232d5e
+                self.__option_label = text
7232d5e
+                if self.__group_label != None and \
7232d5e
+                   self.__group_label in self.__optionlist_array:
7232d5e
+                    self.__optionlist_array[self.__group_label].append(text)
095f9c1
+            elif self.__current_node == 'description':
7232d5e
+                self.__option_desc[self.__option_label] = text
7232d5e
+            else:
7232d5e
+                pass
7232d5e
+        else:
095f9c1
+            if self.__current_node == 'name':
7232d5e
+                self.__group_label = text
7232d5e
+                self.__optionlist_array[self.__group_label] = []
095f9c1
+            elif self.__current_node == 'description':
7232d5e
+                self.__option_group_desc[self.__group_label] = text
7232d5e
+            else:
7232d5e
+                pass
7232d5e
+
7232d5e
+    def startElement(self, name, attrs):
7232d5e
+        self.__current_node = name
095f9c1
+        if name == 'layoutList':
7232d5e
+            self.__layoutlist = True
095f9c1
+        elif name == 'layout':
7232d5e
+            self.__layout = True
7232d5e
+            self.__layout_label = None
095f9c1
+        elif name == 'variantList':
7232d5e
+            self.__variantlist = True
095f9c1
+        elif name == 'variant':
7232d5e
+            self.__variant = True
7232d5e
+            self.__variant_label = None
095f9c1
+        elif name == 'optionList':
7232d5e
+            self.__optionlist = True
095f9c1
+        elif name == 'option':
7232d5e
+            self.__option = True
7232d5e
+            self.__option_label = None
095f9c1
+        elif name == 'group':
7232d5e
+            self.__group = True
7232d5e
+            self.__group_label = None
7232d5e
+
7232d5e
+    def endElement(self, name):
7232d5e
+        self.__current_node = self.__root
095f9c1
+        if name == 'layoutList':
7232d5e
+            self.__layoutlist = False
095f9c1
+        elif name == 'layout':
7232d5e
+            self.__layout = False
095f9c1
+        elif name == 'variantList':
7232d5e
+            self.__variantlist = False
095f9c1
+        elif name == 'variant':
7232d5e
+            self.__variant = False
095f9c1
+        elif name == 'optionList':
7232d5e
+            self.__optionlist = False
095f9c1
+        elif name == 'option':
7232d5e
+            self.__option = False
095f9c1
+        elif name == 'group':
7232d5e
+            self.__group = False
7232d5e
+
7232d5e
+    def characters(self, text):
7232d5e
+        if self.__current_node == self.__root:
7232d5e
+            return
7232d5e
+        if self.__layoutlist:
7232d5e
+            self.__characters_layoutlist(text)
7232d5e
+        elif self.__optionlist:
7232d5e
+            self.__characters_optionlist(text)
7232d5e
+
60148ca
+    def getLayoutList(self):
7232d5e
+        return self.__layoutlist_array
60148ca
+
60148ca
+    def getLayoutDesc(self):
60148ca
+        return self.__layout_desc
60148ca
+
60148ca
+    def getLayoutLang(self):
60148ca
+        return self.__layout_lang
60148ca
+
60148ca
+    def getVariantDesc(self):
60148ca
+        return self.__variant_desc
60148ca
+
7232d5e
+    def getOptionList(self):
7232d5e
+        return self.__optionlist_array
7232d5e
+
7232d5e
+    def getOptionGroupDesc(self):
7232d5e
+        return self.__option_group_desc
7232d5e
+
7232d5e
+    def getOptionDesc(self):
7232d5e
+        return self.__option_desc
7232d5e
+
60148ca
+class XKBLayoutConfigHandler(XMLFilterBase):
60148ca
+    def __init__(self,
60148ca
+                 parser=None,
60148ca
+                 downstream=None,
60148ca
+                 preload_layouts=None,
60148ca
+                 root='root'):
60148ca
+        XMLFilterBase.__init__(self, parser)
60148ca
+        self.__downstream = downstream
60148ca
+        self.__preload_layouts = preload_layouts
60148ca
+        self.__root = root
60148ca
+        self.__current_node = root
60148ca
+        self.__xkblayout = False 
60148ca
+        self.__config = False 
60148ca
+
60148ca
+    def startDocument(self):
60148ca
+        if self.__downstream != None:
60148ca
+            self.__downstream.startDocument()
60148ca
+
60148ca
+    def endDocument(self):
60148ca
+        if self.__downstream != None:
60148ca
+            self.__downstream.endDocument()
60148ca
+
60148ca
+    def startElement(self, name, attrs):
60148ca
+        self.__current_node = name
095f9c1
+        if name == 'xkblayout':
60148ca
+            self.__xkblayout = True
095f9c1
+        if name == 'config':
60148ca
+            self.__config = True
60148ca
+        if self.__downstream != None:
60148ca
+            self.__downstream.startElement(name, {})
60148ca
+
60148ca
+    def endElement(self, name):
60148ca
+        self.__current_node = self.__root
095f9c1
+        if name == 'xkblayout':
60148ca
+            self.__xkblayout = False
095f9c1
+        if name == 'config':
60148ca
+            self.__config = False
60148ca
+        if self.__downstream != None:
60148ca
+            self.__downstream.endElement(name)
60148ca
+
60148ca
+    def characters(self, text):
60148ca
+        if self.__current_node == self.__root:
60148ca
+            return
60148ca
+        if not self.__xkblayout or not self.__config:
60148ca
+            return
095f9c1
+        if self.__current_node == 'preload_layouts':
60148ca
+            if self.__preload_layouts == None:
60148ca
+                self.__preload_layouts = text.split(',')
60148ca
+                self.__preload_layouts.sort()
60148ca
+            if self.__downstream != None:
60148ca
+                self.__downstream.characters(string.join(self.__preload_layouts,
60148ca
+                                                         ','))
60148ca
+
60148ca
+    def getPreloadLayouts(self):
60148ca
+        return self.__preload_layouts
60148ca
+
60148ca
+class XKBConfigRegistry():
60148ca
+    def __init__(self, file_path=XKB_RULES_XML_FILE):
60148ca
+        self.__handler = None
60148ca
+        parser = sax.make_parser()
60148ca
+        parser.setFeature(sax.handler.feature_namespaces, 0)
60148ca
+        self.__handler = XKBConfigRegistryHandler(parser)
60148ca
+        parser.setContentHandler(self.__handler)
60148ca
+        f = file(file_path, 'r')
60148ca
+        try:
60148ca
+            parser.parse(f)
60148ca
+        except SAXParseException:
095f9c1
+            print 'ERROR: invalid file format', file_path
60148ca
+        finally:
60148ca
+            f.close()
60148ca
+
60148ca
+    def get_layout_list(self):
60148ca
+        return self.__handler.getLayoutList()
60148ca
+
60148ca
+    def get_layout_desc(self):
60148ca
+        return self.__handler.getLayoutDesc()
60148ca
+
60148ca
+    def get_layout_lang(self):
60148ca
+        return self.__handler.getLayoutLang()
60148ca
+
60148ca
+    def get_variant_desc(self):
60148ca
+        return self.__handler.getVariantDesc()
60148ca
+
7232d5e
+    def get_option_list(self):
7232d5e
+        return self.__handler.getOptionList()
7232d5e
+
7232d5e
+    def get_option_group_desc(self):
7232d5e
+        return self.__handler.getOptionGroupDesc()
7232d5e
+
7232d5e
+    def get_option_desc(self):
7232d5e
+        return self.__handler.getOptionDesc()
7232d5e
+
60148ca
+    @classmethod
60148ca
+    def have_xkb(self):
60148ca
+        return @HAVE_XKB@
60148ca
+
60148ca
+    @classmethod
60148ca
+    def engine_desc_new(self,
60148ca
+                        lang,
60148ca
+                        layout,
60148ca
+                        layout_desc=None,
60148ca
+                        variant=None,
095f9c1
+                        variant_desc=None,
0318d1b
+                        name=None):
692f6ca
+        if variant_desc != None:
692f6ca
+            longname = variant_desc
60148ca
+        elif layout != None and variant != None:
095f9c1
+            longname = layout + ' - ' + variant
60148ca
+        elif layout_desc != None:
60148ca
+            longname = layout_desc
60148ca
+        else:
60148ca
+            longname = layout
0318d1b
+        name_prefix='xkb:layout:'
60148ca
+        if variant != None:
0318d1b
+            if name == None:
0318d1b
+                name = name_prefix + layout + ':' + variant
095f9c1
+            desc = 'XKB ' + layout + '(' + variant + ') keyboard layout'
095f9c1
+            engine_layout = layout + '(' + variant + ')'
60148ca
+        else:
0318d1b
+            if name == None:
0318d1b
+                name = name_prefix + layout
095f9c1
+            desc = 'XKB ' + layout + ' keyboard layout'
60148ca
+            engine_layout = layout
60148ca
+
095f9c1
+        icon = 'ibus-engine'
095f9c1
+
60148ca
+        engine = enginedesc.EngineDesc(name, longname, desc, lang,
095f9c1
+                                       'LGPL2.1',
095f9c1
+                                       'Takao Fujiwara <takao.fujiwara1@gmail.com>',
095f9c1
+                                       icon,
60148ca
+                                       engine_layout)
60148ca
+        return engine
60148ca
+
60148ca
+class XKBLayoutConfig():
60148ca
+    def __init__(self,
095f9c1
+                 system_config='@datadir@/ibus/xkb/xkblayoutconfig.xml'):
60148ca
+        self.__user_config = get_user_config_dir()
60148ca
+        if self.__user_config == None:
095f9c1
+            self.__user_config = os.environ['HOME'] + '/.config'
60148ca
+        self.__user_config = self.__user_config + \
095f9c1
+            '/ibus/xkb/xkblayoutconfig.xml'
60148ca
+        self.__system_config = system_config
60148ca
+        self.__filter_handler = None
60148ca
+        self.__load()
60148ca
+
60148ca
+    def __load(self, downstream=None, preload_layouts=None):
60148ca
+        parser = sax.make_parser()
60148ca
+        parser.setFeature(sax.handler.feature_namespaces, 0)
60148ca
+        self.__filter_handler = XKBLayoutConfigHandler(parser,
60148ca
+                                                       downstream,
60148ca
+                                                       preload_layouts)
60148ca
+        parser.setContentHandler(self.__filter_handler)
60148ca
+        f = None
60148ca
+        if os.path.exists(self.__user_config):
60148ca
+            f = file(self.__user_config)
60148ca
+        elif os.path.exists(self.__system_config):
60148ca
+            f = file(self.__system_config)
60148ca
+        if f == None:
60148ca
+            return
60148ca
+        try:
60148ca
+            parser.parse(f)
60148ca
+        except SAXParseException:
095f9c1
+            print 'ERROR: invalid file format', self.__user_config
60148ca
+        finally:
60148ca
+            f.close()
60148ca
+
60148ca
+    def get_preload_layouts(self):
60148ca
+        return self.__filter_handler.getPreloadLayouts()
60148ca
+
60148ca
+    def save_preload_layouts(self, layouts):
60148ca
+        if layouts == None:
60148ca
+            if os.path.exists(self.__user_config):
60148ca
+                os.unlink(self.__user_config)
60148ca
+                return
60148ca
+        parser = sax.make_parser()
60148ca
+        parser.setFeature(sax.handler.feature_namespaces, 0)
60148ca
+        result = StringIO()
60148ca
+        downstream_handler = XMLGenerator(result, 'utf-8')
60148ca
+        self.__load(downstream_handler, layouts)
60148ca
+        contents = result.getvalue()
60148ca
+        dir = os.path.dirname(self.__user_config)
60148ca
+        if not os.path.exists(dir):
60148ca
+            os.makedirs(dir, 0700)
60148ca
+        f = open(self.__user_config, 'w')
60148ca
+        f.write(contents)
60148ca
+        f.close()
60148ca
+        os.chmod(self.__user_config, 0600)
60148ca
+
60148ca
+def test():
60148ca
+    xkbconfig = XKBConfigRegistry()
60148ca
+    layout_list = xkbconfig.get_layout_list()
60148ca
+    layout_desc = xkbconfig.get_layout_desc()
60148ca
+    layout_lang = xkbconfig.get_layout_lang()
60148ca
+    variant_desc = xkbconfig.get_variant_desc()
60148ca
+    for layout in layout_list.keys():
60148ca
+        if layout not in layout_lang:
095f9c1
+            print 'layout name:', layout, 'NO-LANG description:', layout_desc[layout]
60148ca
+            continue
60148ca
+        lang = layout_lang[layout]
095f9c1
+        print 'layout name:', layout, 'lang:', lang, 'description:', layout_desc[layout]
60148ca
+        for variant in layout_list[layout]:
095f9c1
+            label = '%s(%s)' % (layout, variant)
60148ca
+            if label in layout_lang:
60148ca
+                lang = layout_lang[label]
095f9c1
+            print '  variant name:', variant, 'lang:', lang, 'description:', variant_desc[variant]
60148ca
+
7232d5e
+    option_list = xkbconfig.get_option_list()
7232d5e
+    option_group_desc = xkbconfig.get_option_group_desc()
7232d5e
+    option_desc = xkbconfig.get_option_desc()
7232d5e
+    for option_group in option_list.keys():
095f9c1
+        print 'option group name:', option_group, 'description:', option_group_desc[option_group]
7232d5e
+        for option in option_list[option_group]:
095f9c1
+            print '  option name:', option, 'description:', option_desc[option]
7232d5e
+
60148ca
+def test2():
095f9c1
+    xkblayoutconfig = XKBLayoutConfig('../xkb/xkblayoutconfig.xml')
60148ca
+    list = xkblayoutconfig.get_preload_layouts()
60148ca
+    print list
60148ca
+    if list == None:
60148ca
+        list = []
095f9c1
+    list.append('gb(test)')
60148ca
+    list.sort()
60148ca
+    #xkblayoutconfig.save_preload_layouts(list)
60148ca
+
095f9c1
+if __name__ == '__main__':
60148ca
+    test()
60148ca
+    test2()
7232d5e
diff --git a/setup/Makefile.am b/setup/Makefile.am
e5da135
index 9618d7f..48b1fed 100644
7232d5e
--- a/setup/Makefile.am
7232d5e
+++ b/setup/Makefile.am
f0777a3
@@ -28,6 +28,7 @@ ibussetup_PYTHON = \
7232d5e
 	enginetreeview.py \
7232d5e
 	engineabout.py \
7232d5e
 	keyboardshortcut.py \
b95ef1a
+	xkbsetup.py \
7232d5e
 	$(NULL)
7232d5e
 
7232d5e
 ibussetup_DATA = \
b95ef1a
diff --git a/setup/enginecombobox.py b/setup/enginecombobox.py
f0777a3
index 2fd8876..7383177 100644
b95ef1a
--- a/setup/enginecombobox.py
b95ef1a
+++ b/setup/enginecombobox.py
f0777a3
@@ -43,6 +43,7 @@ class EngineComboBox(gtk.ComboBox):
b95ef1a
         self.connect("notify::active", self.__notify_active_cb)
b95ef1a
 
b95ef1a
         self.__model = None
b95ef1a
+        self.__title = _("Select an input method")
b95ef1a
 
b95ef1a
         renderer = gtk.CellRendererPixbuf()
b95ef1a
         renderer.set_property("xalign", 0)
b95ef1a
@@ -117,7 +118,7 @@ class EngineComboBox(gtk.ComboBox):
b95ef1a
             renderer.set_property("weight", pango.WEIGHT_NORMAL)
b95ef1a
         elif isinstance(engine, int):
b95ef1a
             renderer.set_property("sensitive", True)
b95ef1a
-            renderer.set_property("text", _("Select an input method"))
b95ef1a
+            renderer.set_property("text", self.__title)
b95ef1a
             renderer.set_property("weight", pango.WEIGHT_NORMAL)
b95ef1a
         else:
b95ef1a
             renderer.set_property("sensitive", True)
b95ef1a
@@ -140,5 +141,9 @@ class EngineComboBox(gtk.ComboBox):
b95ef1a
     def get_active_engine(self):
b95ef1a
         return self.get_property("active-engine")
b95ef1a
 
b95ef1a
+    def get_title(self):
b95ef1a
+        return self.__title
b95ef1a
 
b95ef1a
+    def set_title(self, title):
b95ef1a
+        self.__title = title
b95ef1a
 
60148ca
diff --git a/setup/main.py b/setup/main.py
9d6bb4f
index a22bb0c..7f4a040 100644
60148ca
--- a/setup/main.py
60148ca
+++ b/setup/main.py
f0777a3
@@ -37,6 +37,7 @@ from gtk import gdk
7232d5e
 from enginecombobox import EngineComboBox
7232d5e
 from enginetreeview import EngineTreeView
7232d5e
 from engineabout import EngineAbout
b95ef1a
+from xkbsetup import XKBSetup
3b5789d
 from i18n import DOMAINNAME, _, N_, init as i18n_init
7232d5e
 
f0777a3
 (
9d6bb4f
@@ -241,6 +242,8 @@ class Setup(object):
60148ca
         self.__combobox.connect("notify::active-engine", self.__combobox_notify_active_engine_cb)
60148ca
         self.__treeview.connect("notify", self.__treeview_notify_cb)
60148ca
 
b95ef1a
+        XKBSetup(self.__config, self.__builder)
60148ca
+
60148ca
     def __combobox_notify_active_engine_cb(self, combobox, property):
60148ca
         engine = self.__combobox.get_active_engine()
60148ca
         button = self.__builder.get_object("button_engine_add")
60148ca
diff --git a/setup/setup.ui b/setup/setup.ui
e5da135
index 0a69df8..f1e6d0b 100644
60148ca
--- a/setup/setup.ui
60148ca
+++ b/setup/setup.ui
9d6bb4f
@@ -117,7 +117,6 @@
60148ca
                                 <child>
60148ca
                                   <object class="GtkLabel" id="label9">
60148ca
                                     <property name="visible">True</property>
60148ca
-                                    <property name="sensitive">False</property>
60148ca
                                     <property name="tooltip_text" translatable="yes">The shortcut keys for switching to previous input method in the list</property>
60148ca
                                     <property name="xalign">0</property>
60148ca
                                     <property name="label" translatable="yes">Previous input method:</property>
9d6bb4f
@@ -204,7 +203,6 @@
60148ca
                                     <child>
60148ca
                                       <object class="GtkEntry" id="entry_prev_engine">
60148ca
                                         <property name="visible">True</property>
60148ca
-                                        <property name="sensitive">False</property>
60148ca
                                         <property name="can_focus">True</property>
60148ca
                                         <property name="editable">False</property>
60148ca
                                       </object>
9d6bb4f
@@ -216,7 +214,6 @@
60148ca
                                       <object class="GtkButton" id="button_prev_engine">
60148ca
                                         <property name="label" translatable="yes">...</property>
60148ca
                                         <property name="visible">True</property>
60148ca
-                                        <property name="sensitive">False</property>
60148ca
                                         <property name="can_focus">True</property>
60148ca
                                         <property name="receives_default">False</property>
60148ca
                                         <property name="use_underline">True</property>
9d6bb4f
@@ -825,6 +822,7 @@ You may use up/down buttons to change it.</i></small></property>
60148ca
                                     <property name="visible">True</property>
60148ca
                                     <property name="orientation">vertical</property>
60148ca
                                     <property name="spacing">6</property>
60148ca
+                                    <property name="no_show_all">True</property>
60148ca
                                     <child>
60148ca
                                       <object class="GtkCheckButton" id="checkbutton_use_sys_layout">
60148ca
                                         <property name="label" translatable="yes">Use system keyboard layout</property>
9d6bb4f
@@ -840,6 +838,57 @@ You may use up/down buttons to change it.</i></small></property>
60148ca
                                         <property name="position">0</property>
60148ca
                                       </packing>
60148ca
                                     </child>
60148ca
+                                    <child>
7232d5e
+                                      <object class="GtkHBox" id="hbox_system_keyboard_layout">
60148ca
+                                        <property name="visible">True</property>
60148ca
+                                        <property name="spacing">6</property>
60148ca
+                                        <child>
9d6bb4f
+                                          <object class="GtkLabel" id="label20">
60148ca
+                                            <property name="visible">True</property>
7232d5e
+                                            <property name="label" translatable="yes">System Keyboard Layout:</property>
60148ca
+                                            <property name="use_markup">True</property>
60148ca
+                                            <property name="justify">center</property>
60148ca
+                                          </object>
60148ca
+                                          <packing>
60148ca
+                                            <property name="expand">False</property>
60148ca
+                                            <property name="fill">False</property>
60148ca
+                                            <property name="position">0</property>
60148ca
+                                          </packing>
60148ca
+                                        </child>
60148ca
+                                        <child>
7232d5e
+                                          <object class="GtkButton" id="button_system_keyboard_layout">
60148ca
+                                            <property name="label"></property>
60148ca
+                                            <property name="visible">True</property>
60148ca
+                                            <property name="can_focus">True</property>
60148ca
+                                            <property name="receives_default">False</property>
60148ca
+                                          </object>
60148ca
+                                          <packing>
60148ca
+                                            <property name="expand">False</property>
60148ca
+                                            <property name="fill">False</property>
60148ca
+                                            <property name="position">1</property>
60148ca
+                                          </packing>
60148ca
+                                        </child>
60148ca
+                                      </object>
60148ca
+                                      <packing>
60148ca
+                                        <property name="expand">False</property>
60148ca
+                                        <property name="fill">False</property>
60148ca
+                                        <property name="position">1</property>
60148ca
+                                      </packing>
60148ca
+                                    </child>
6cc8efa
+                                    <child>
6cc8efa
+                                      <object class="GtkButton" id="button_config_layouts">
6cc8efa
+                                        <property name="label">Add or remove layouts in 'Select an input method' list</property>
6cc8efa
+                                        <property name="visible">True</property>
6cc8efa
+                                        <property name="can_focus">True</property>
6cc8efa
+                                        <property name="receives_default">False</property>
6cc8efa
+                                        <property name="tooltip_text" translatable="yes">Add or remove keyboard layouts in all input method engnines</property>
6cc8efa
+                                      </object>
6cc8efa
+                                      <packing>
6cc8efa
+                                        <property name="expand">False</property>
6cc8efa
+                                        <property name="fill">False</property>
6cc8efa
+                                        <property name="position">2</property>
6cc8efa
+                                      </packing>
6cc8efa
+                                    </child>
60148ca
                                   </object>
60148ca
                                 </child>
60148ca
                               </object>
9d6bb4f
@@ -1038,4 +1087,558 @@ Homepage: http://code.google.com/p/ibus
60148ca
       </object>
60148ca
     </child>
60148ca
   </object>
60148ca
+  <object class="GtkDialog" id="dialog_config_layouts">
b95ef1a
+    <property name="title" translatable="yes">Add or Remove Layouts</property>
60148ca
+    <property name="icon_name">ibus-setup</property>
60148ca
+    <child internal-child="vbox">
60148ca
+      <object class="GtkVBox" id="vbox101">
60148ca
+        <property name="orientation">vertical</property>
60148ca
+        <property name="visible">True</property>
60148ca
+        <property name="border-width">10</property>
60148ca
+        <property name="spacing">12</property>
60148ca
+        <child>
b95ef1a
+          <object class="GtkAlignment" id="alignment101">
60148ca
+            <property name="visible">True</property>
b95ef1a
+            <property name="top_padding">12</property>
b95ef1a
+            <property name="left_padding">12</property>
60148ca
+            <child>
b95ef1a
+              <object class="GtkFrame" id="frame101">
60148ca
+                <property name="visible">True</property>
b95ef1a
+                <property name="label_xalign">0</property>
b95ef1a
+                <property name="shadow_type">none</property>
60148ca
+                <child>
b95ef1a
+                  <object class="GtkAlignment" id="alignment102">
b95ef1a
+                    <property name="visible">True</property>
b95ef1a
+                    <property name="top_padding">12</property>
b95ef1a
+                    <property name="left_padding">12</property>
b95ef1a
+                    <child>
b95ef1a
+                      <object class="GtkVBox" id="vbox102">
b95ef1a
+                        <property name="visible">True</property>
b95ef1a
+                        <property name="orientation">vertical</property>
b95ef1a
+                        <property name="spacing">6</property>
b95ef1a
+                        <child>
b95ef1a
+                          <object class="GtkScrolledWindow" id="scrolledwindow101">
b95ef1a
+                            <property name="visible">True</property>
b95ef1a
+                            <property name="can_focus">True</property>
b95ef1a
+                            <property name="border_width">5</property>
b95ef1a
+                            <property name="width_request">450</property>
b95ef1a
+                            <property name="height_request">350</property>
b95ef1a
+                            <property name="hscrollbar_policy">automatic</property>
b95ef1a
+                            <property name="vscrollbar_policy">automatic</property>
b95ef1a
+                            <property name="shadow_type">out</property>
b95ef1a
+                            <child>
b95ef1a
+                              <object class="GtkViewport" id="viewport101">
b95ef1a
+                                <property name="visible">True</property>
b95ef1a
+                                <property name="shadow_type">none</property>
b95ef1a
+                                <child>
b95ef1a
+                                  <object class="GtkVBox" id="vbox_all_keyboard_layouts">
b95ef1a
+                                    <property name="visible">True</property>
b95ef1a
+                                    <property name="orientation">vertical</property>
b95ef1a
+                                  </object>
b95ef1a
+                                </child>
b95ef1a
+                              </object>
b95ef1a
+                            </child>
b95ef1a
+                          </object>
b95ef1a
+                          <packing>
b95ef1a
+                            <property name="position">0</property>
b95ef1a
+                          </packing>
b95ef1a
+                        </child>
b95ef1a
+                      </object>
b95ef1a
+                    </child>
60148ca
+                  </object>
60148ca
+                </child>
b95ef1a
+                <child type="label">
b95ef1a
+                  <object class="GtkLabel" id="label101">
b95ef1a
+                    <property name="visible">True</property>
b95ef1a
+                    <property name="label" translatable="yes"><b>Keyboard Layout</b></property>
b95ef1a
+                    <property name="use_markup">True</property>
60148ca
+                  </object>
60148ca
+                </child>
60148ca
+              </object>
60148ca
+            </child>
60148ca
+          </object>
60148ca
+          <packing>
60148ca
+            <property name="expand">False</property>
60148ca
+            <property name="position">0</property>
60148ca
+          </packing>
60148ca
+        </child>
60148ca
+        <child internal-child="action_area">
60148ca
+          <object class="GtkHButtonBox" id="hbuttonbox101">
60148ca
+            <property name="visible">True</property>
60148ca
+            <property name="homogeneous">True</property>
60148ca
+            <property name="layout_style">end</property>
60148ca
+            <child>
60148ca
+              <object class="GtkButton" id="button_config_layouts_cancel">
60148ca
+                <property name="label">gtk-cancel</property>
60148ca
+                <property name="visible">True</property>
60148ca
+                <property name="use_stock">True</property>
60148ca
+              </object>
60148ca
+              <packing>
60148ca
+                <property name="expand">False</property>
60148ca
+                <property name="fill">False</property>
60148ca
+                <property name="position">0</property>
60148ca
+              </packing>
60148ca
+            </child>
60148ca
+            <child>
60148ca
+              <object class="GtkButton" id="button_config_layouts_ok">
60148ca
+                <property name="label">gtk-ok</property>
60148ca
+                <property name="visible">True</property>
60148ca
+                <property name="use_stock">True</property>
60148ca
+                <property name="receives_default">True</property>
60148ca
+              </object>
60148ca
+              <packing>
60148ca
+                <property name="expand">False</property>
60148ca
+                <property name="fill">False</property>
60148ca
+                <property name="position">1</property>
60148ca
+              </packing>
60148ca
+            </child>
60148ca
+          </object>
60148ca
+          <packing>
60148ca
+            <property name="expand">False</property>
60148ca
+            <property name="fill">False</property>
60148ca
+            <property name="pack_type">end</property>
60148ca
+            <property name="position">1</property>
60148ca
+          </packing>
60148ca
+        </child>
60148ca
+      </object>
60148ca
+    </child>
60148ca
+  </object>
7232d5e
+  <object class="GtkDialog" id="dialog_system_keyboard_layout">
7232d5e
+    <property name="title" translatable="yes">System Keyboard Layout Setup</property>
60148ca
+    <property name="icon_name">ibus-setup</property>
60148ca
+    <child internal-child="vbox">
60148ca
+      <object class="GtkVBox" id="vbox201">
60148ca
+        <property name="orientation">vertical</property>
60148ca
+        <property name="visible">True</property>
60148ca
+        <property name="border-width">10</property>
60148ca
+        <property name="spacing">12</property>
60148ca
+        <child>
7232d5e
+          <object class="GtkAlignment" id="alignment201">
60148ca
+            <property name="visible">True</property>
7232d5e
+            <property name="top_padding">12</property>
7232d5e
+            <property name="left_padding">12</property>
60148ca
+            <child>
7232d5e
+              <object class="GtkFrame" id="frame201">
60148ca
+                <property name="visible">True</property>
7232d5e
+                <property name="label_xalign">0</property>
7232d5e
+                <property name="shadow_type">none</property>
60148ca
+                <child>
7232d5e
+                  <object class="GtkAlignment" id="alignment202">
60148ca
+                    <property name="visible">True</property>
7232d5e
+                    <property name="top_padding">12</property>
7232d5e
+                    <property name="left_padding">12</property>
7232d5e
+                    <child>
b95ef1a
+                      <object class="GtkVBox" id="vbox202">
7232d5e
+                        <property name="visible">True</property>
b95ef1a
+                        <property name="orientation">vertical</property>
b95ef1a
+                        <property name="spacing">6</property>
7232d5e
+                        <child>
b95ef1a
+                          <object class="GtkHBox" id="hbox201">
7232d5e
+                            <property name="visible">True</property>
7232d5e
+                            <child>
b95ef1a
+                              <object class="GtkVBox" id="vbox203">
7232d5e
+                                <property name="visible">True</property>
b95ef1a
+                                <property name="orientation">vertical</property>
b95ef1a
+                                <property name="spacing">6</property>
b95ef1a
+                                <child>
b95ef1a
+                                  <object class="EngineComboBox" id="combobox_system_keyboard_layout_engines">
b95ef1a
+                                    <property name="visible">True</property>
b95ef1a
+                                  </object>
b95ef1a
+                                  <packing>
b95ef1a
+                                    <property name="expand">False</property>
b95ef1a
+                                    <property name="position">0</property>
b95ef1a
+                                  </packing>
b95ef1a
+                                </child>
b95ef1a
+                                <child>
b95ef1a
+                                  <object class="GtkScrolledWindow" id="scrolledwindow201">
b95ef1a
+                                    <property name="height_request">150</property>
b95ef1a
+                                    <property name="visible">True</property>
b95ef1a
+                                    <property name="can_focus">True</property>
b95ef1a
+                                    <property name="hscrollbar_policy">automatic</property>
b95ef1a
+                                    <property name="vscrollbar_policy">automatic</property>
b95ef1a
+                                    <property name="shadow_type">in</property>
b95ef1a
+                                    <child>
b95ef1a
+                                      <object class="EngineTreeView" id="treeview_system_keyboard_layout_engines">
b95ef1a
+                                        <property name="visible">True</property>
b95ef1a
+                                        <property name="can_focus">True</property>
b95ef1a
+                                        <property name="height_request">150</property>
b95ef1a
+                                      </object>
b95ef1a
+                                    </child>
b95ef1a
+                                  </object>
b95ef1a
+                                  <packing>
b95ef1a
+                                    <property name="expand">False</property>
b95ef1a
+                                    <property name="position">1</property>
b95ef1a
+                                  </packing>
b95ef1a
+                                </child>
7232d5e
+                              </object>
b95ef1a
+                              <packing>
b95ef1a
+                                <property name="position">0</property>
b95ef1a
+                              </packing>
b95ef1a
+                            </child>
b95ef1a
+                            <child>
b95ef1a
+                              <object class="GtkVButtonBox" id="vbuttonbox201">
b95ef1a
+                                <property name="visible">True</property>
b95ef1a
+                                <property name="orientation">vertical</property>
b95ef1a
+                                <property name="spacing">5</property>
b95ef1a
+                                <property name="layout_style">start</property>
b95ef1a
+                                <child>
b95ef1a
+                                  <object class="GtkButton" id="button_system_keyboard_layout_engine_add">
b95ef1a
+                                    <property name="label">gtk-add</property>
b95ef1a
+                                    <property name="visible">True</property>
b95ef1a
+                                    <property name="sensitive">False</property>
b95ef1a
+                                    <property name="can_focus">True</property>
b95ef1a
+                                    <property name="receives_default">True</property>
b95ef1a
+                                    <property name="tooltip_text" translatable="yes">Add the selected keyboard layout into the system keyboard layouts</property>
b95ef1a
+                                    <property name="use_stock">True</property>
b95ef1a
+                                  </object>
b95ef1a
+                                  <packing>
b95ef1a
+                                    <property name="expand">False</property>
b95ef1a
+                                    <property name="fill">False</property>
b95ef1a
+                                    <property name="position">0</property>
b95ef1a
+                                  </packing>
b95ef1a
+                                </child>
b95ef1a
+                                <child>
b95ef1a
+                                  <object class="GtkButton" id="button_system_keyboard_layout_engine_remove">
b95ef1a
+                                    <property name="label">gtk-remove</property>
b95ef1a
+                                    <property name="visible">True</property>
b95ef1a
+                                    <property name="sensitive">False</property>
b95ef1a
+                                    <property name="can_focus">True</property>
b95ef1a
+                                    <property name="receives_default">True</property>
b95ef1a
+                                    <property name="tooltip_text" translatable="yes">Remove the selected keyboard layout from the system keyboard layouts</property>
b95ef1a
+                                    <property name="use_stock">True</property>
b95ef1a
+                                  </object>
b95ef1a
+                                  <packing>
b95ef1a
+                                    <property name="expand">False</property>
b95ef1a
+                                    <property name="fill">False</property>
b95ef1a
+                                    <property name="position">1</property>
b95ef1a
+                                  </packing>
b95ef1a
+                                </child>
b95ef1a
+                                <child>
b95ef1a
+                                  <object class="GtkButton" id="button_system_keyboard_layout_engine_up">
b95ef1a
+                                    <property name="label">gtk-go-up</property>
b95ef1a
+                                    <property name="visible">True</property>
b95ef1a
+                                    <property name="sensitive">False</property>
b95ef1a
+                                    <property name="can_focus">True</property>
b95ef1a
+                                    <property name="receives_default">True</property>
b95ef1a
+                                    <property name="tooltip_text" translatable="yes">Move up the selected keyboard layout in the system keyboard layouts list</property>
b95ef1a
+                                    <property name="use_stock">True</property>
b95ef1a
+                                  </object>
b95ef1a
+                                  <packing>
b95ef1a
+                                    <property name="expand">False</property>
b95ef1a
+                                    <property name="fill">False</property>
b95ef1a
+                                    <property name="position">2</property>
b95ef1a
+                                  </packing>
b95ef1a
+                                </child>
b95ef1a
+                                <child>
b95ef1a
+                                  <object class="GtkButton" id="button_system_keyboard_layout_engine_down">
b95ef1a
+                                    <property name="label">gtk-go-down</property>
b95ef1a
+                                    <property name="visible">True</property>
b95ef1a
+                                    <property name="sensitive">False</property>
b95ef1a
+                                    <property name="can_focus">True</property>
b95ef1a
+                                    <property name="receives_default">True</property>
b95ef1a
+                                    <property name="tooltip_text" translatable="yes">Move down the selected keyboard layout in the system keyboard layouts list</property>
b95ef1a
+                                    <property name="use_stock">True</property>
b95ef1a
+                                  </object>
b95ef1a
+                                  <packing>
b95ef1a
+                                    <property name="expand">False</property>
b95ef1a
+                                    <property name="fill">False</property>
b95ef1a
+                                    <property name="position">3</property>
b95ef1a
+                                  </packing>
b95ef1a
+                                </child>
b95ef1a
+                                <child>
b95ef1a
+                                  <object class="GtkButton" id="button_system_keyboard_layout_engine_reset">
b95ef1a
+                                    <property name="label" translatable="yes">Rese_t</property>
b95ef1a
+                                    <property name="visible">True</property>
b95ef1a
+                                    <property name="sensitive">False</property>
b95ef1a
+                                    <property name="can_focus">True</property>
b95ef1a
+                                    <property name="receives_default">True</property>
b95ef1a
+                                    <property name="tooltip_text" translatable="yes">Reset the system keyboard layouts list</property>
b95ef1a
+                                    <property name="use_underline">True</property>
b95ef1a
+                                  </object>
b95ef1a
+                                  <packing>
b95ef1a
+                                    <property name="expand">False</property>
b95ef1a
+                                    <property name="fill">False</property>
b95ef1a
+                                    <property name="position">4</property>
b95ef1a
+                                  </packing>
b95ef1a
+                                </child>
b95ef1a
+                              </object>
b95ef1a
+                              <packing>
b95ef1a
+                                <property name="position">1</property>
b95ef1a
+                              </packing>
7232d5e
+                            </child>
7232d5e
+                          </object>
7232d5e
+                          <packing>
b95ef1a
+                            <property name="position">0</property>
7232d5e
+                          </packing>
7232d5e
+                        </child>
7232d5e
+                        <child>
b95ef1a
+                          <object class="GtkHBox" id="hbox202">
7232d5e
+                            <property name="visible">True</property>
b95ef1a
+                            <property name="spacing">6</property>
b95ef1a
+                            <child>
b95ef1a
+                              <object class="GtkImage" id="image201">
b95ef1a
+                                <property name="visible">True</property>
b95ef1a
+                                <property name="stock">gtk-info</property>
b95ef1a
+                                <property name="icon-size">2</property>
b95ef1a
+                              </object>
b95ef1a
+                              <packing>
b95ef1a
+                                <property name="expand">False</property>
b95ef1a
+                                <property name="position">0</property>
b95ef1a
+                              </packing>
b95ef1a
+                            </child>
b95ef1a
+                            <child>
b95ef1a
+                              <object class="GtkLabel" id="label_system_keyboard_layout_engines">
b95ef1a
+                                <property name="visible">True</property>
b95ef1a
+                                <property name="xalign">0</property>
b95ef1a
+                                <property name="use_markup">True</property>
b95ef1a
+                              </object>
b95ef1a
+                              <packing>
b95ef1a
+                                <property name="position">1</property>
b95ef1a
+                              </packing>
b95ef1a
+                            </child>
7232d5e
+                          </object>
7232d5e
+                          <packing>
b95ef1a
+                            <property name="position">1</property>
7232d5e
+                          </packing>
7232d5e
+                        </child>
7232d5e
+                      </object>
7232d5e
+                    </child>
7232d5e
+                  </object>
7232d5e
+                </child>
7232d5e
+                <child type="label">
7232d5e
+                  <object class="GtkLabel" id="label201">
7232d5e
+                    <property name="visible">True</property>
7232d5e
+                    <property name="label" translatable="yes"><b>Keyboard Layout</b></property>
7232d5e
+                    <property name="use_markup">True</property>
60148ca
+                  </object>
60148ca
+                </child>
60148ca
+              </object>
60148ca
+            </child>
7232d5e
+          </object>
7232d5e
+          <packing>
7232d5e
+            <property name="expand">False</property>
7232d5e
+            <property name="position">0</property>
7232d5e
+          </packing>
7232d5e
+        </child>
7232d5e
+        <child>
7232d5e
+          <object class="GtkAlignment" id="alignment204">
7232d5e
+            <property name="visible">True</property>
7232d5e
+            <property name="top_padding">12</property>
7232d5e
+            <property name="left_padding">12</property>
60148ca
+            <child>
7232d5e
+              <object class="GtkFrame" id="frame202">
60148ca
+                <property name="visible">True</property>
7232d5e
+                <property name="label_xalign">0</property>
7232d5e
+                <property name="shadow_type">none</property>
7232d5e
+                <child>
7232d5e
+                  <object class="GtkAlignment" id="alignment205">
7232d5e
+                    <property name="visible">True</property>
7232d5e
+                    <property name="top_padding">12</property>
7232d5e
+                    <property name="left_padding">12</property>
7232d5e
+                    <child>
b95ef1a
+                      <object class="GtkVBox" id="vbox204">
7232d5e
+                        <property name="visible">True</property>
b95ef1a
+                        <property name="orientation">vertical</property>
b95ef1a
+                        <property name="spacing">6</property>
7232d5e
+                        <child>
b95ef1a
+                          <object class="GtkHButtonBox" id="hbuttonbox201">
7232d5e
+                            <property name="visible">True</property>
b95ef1a
+                            <property name="homogeneous">True</property>
b95ef1a
+                            <property name="layout_style">start</property>
7232d5e
+                            <child>
b95ef1a
+                              <object class="GtkButton" id="button_system_keyboard_option_setup">
7232d5e
+                                <property name="visible">True</property>
b95ef1a
+                                <property name="label" translatable="yes">_Options...</property>
b95ef1a
+                                <property name="can_focus">True</property>
b95ef1a
+                                <property name="use_underline">True</property>
7232d5e
+                              </object>
b95ef1a
+                              <packing>
b95ef1a
+                                <property name="expand">False</property>
b95ef1a
+                                <property name="fill">False</property>
b95ef1a
+                                <property name="position">0</property>
b95ef1a
+                              </packing>
7232d5e
+                            </child>
7232d5e
+                          </object>
7232d5e
+                          <packing>
b95ef1a
+                            <property name="expand">False</property>
b95ef1a
+                            <property name="fill">False</property>
b95ef1a
+                            <property name="position">0</property>
7232d5e
+                          </packing>
7232d5e
+                        </child>
7232d5e
+                      </object>
7232d5e
+                    </child>
7232d5e
+                  </object>
7232d5e
+                </child>
7232d5e
+                <child type="label">
7232d5e
+                  <object class="GtkLabel" id="label202">
7232d5e
+                    <property name="visible">True</property>
7232d5e
+                    <property name="label" translatable="yes"><b>Keyboard Option</b></property>
7232d5e
+                    <property name="use_markup">True</property>
7232d5e
+                  </object>
7232d5e
+                </child>
60148ca
+              </object>
60148ca
+            </child>
60148ca
+          </object>
60148ca
+          <packing>
60148ca
+            <property name="expand">False</property>
7232d5e
+            <property name="position">1</property>
60148ca
+          </packing>
60148ca
+        </child>
60148ca
+        <child internal-child="action_area">
b95ef1a
+          <object class="GtkHButtonBox" id="hbuttonbox202">
60148ca
+            <property name="visible">True</property>
60148ca
+            <property name="homogeneous">True</property>
60148ca
+            <property name="layout_style">end</property>
60148ca
+            <child>
7232d5e
+              <object class="GtkButton" id="button_system_keyboard_layout_cancel">
60148ca
+                <property name="label">gtk-cancel</property>
60148ca
+                <property name="visible">True</property>
60148ca
+                <property name="use_stock">True</property>
60148ca
+              </object>
60148ca
+              <packing>
60148ca
+                <property name="expand">False</property>
60148ca
+                <property name="fill">False</property>
60148ca
+                <property name="position">0</property>
60148ca
+              </packing>
60148ca
+            </child>
60148ca
+            <child>
7232d5e
+              <object class="GtkButton" id="button_system_keyboard_layout_ok">
60148ca
+                <property name="label">gtk-ok</property>
60148ca
+                <property name="visible">True</property>
60148ca
+                <property name="use_stock">True</property>
60148ca
+                <property name="receives_default">True</property>
60148ca
+              </object>
60148ca
+              <packing>
60148ca
+                <property name="expand">False</property>
60148ca
+                <property name="fill">False</property>
60148ca
+                <property name="position">1</property>
60148ca
+              </packing>
60148ca
+            </child>
60148ca
+          </object>
60148ca
+          <packing>
60148ca
+            <property name="expand">False</property>
60148ca
+            <property name="fill">False</property>
60148ca
+            <property name="pack_type">end</property>
60148ca
+            <property name="position">1</property>
60148ca
+          </packing>
60148ca
+        </child>
60148ca
+      </object>
60148ca
+    </child>
60148ca
+  </object>
b95ef1a
+  <object class="GtkDialog" id="dialog_system_keyboard_option">
b95ef1a
+    <property name="title" translatable="yes">System Keyboard Option Setup</property>
b95ef1a
+    <property name="icon_name">ibus-setup</property>
b95ef1a
+    <child internal-child="vbox">
b95ef1a
+      <object class="GtkVBox" id="vbox301">
b95ef1a
+        <property name="orientation">vertical</property>
b95ef1a
+        <property name="visible">True</property>
b95ef1a
+        <property name="border-width">10</property>
b95ef1a
+        <property name="spacing">12</property>
b95ef1a
+        <child>
b95ef1a
+          <object class="GtkAlignment" id="alignment301">
b95ef1a
+            <property name="visible">True</property>
b95ef1a
+            <property name="top_padding">12</property>
b95ef1a
+            <property name="left_padding">12</property>
b95ef1a
+            <child>
b95ef1a
+              <object class="GtkFrame" id="frame301">
b95ef1a
+                <property name="visible">True</property>
b95ef1a
+                <property name="label_xalign">0</property>
b95ef1a
+                <property name="shadow_type">none</property>
b95ef1a
+                <child>
b95ef1a
+                  <object class="GtkAlignment" id="alignment302">
b95ef1a
+                    <property name="visible">True</property>
b95ef1a
+                    <property name="top_padding">12</property>
b95ef1a
+                    <property name="left_padding">12</property>
b95ef1a
+                    <child>
b95ef1a
+                      <object class="GtkVBox" id="vbox302">
b95ef1a
+                        <property name="visible">True</property>
b95ef1a
+                        <property name="orientation">vertical</property>
b95ef1a
+                        <property name="spacing">6</property>
b95ef1a
+                        <child>
b95ef1a
+                          <object class="GtkCheckButton" id="checkbutton_use_system_keyboard_option">
b95ef1a
+                            <property name="label" translatable="yes">Use the default keyboard option</property>
b95ef1a
+                            <property name="visible">True</property>
b95ef1a
+                            <property name="can_focus">True</property>
b95ef1a
+                            <property name="receives_default">False</property>
b95ef1a
+                            <property name="tooltip_text" translatable="yes">Use the defualt XKB option</property>
b95ef1a
+                            <property name="draw_indicator">True</property>
b95ef1a
+                          </object>
b95ef1a
+                          <packing>
b95ef1a
+                            <property name="position">0</property>
b95ef1a
+                          </packing>
b95ef1a
+                        </child>
b95ef1a
+                        <child>
b95ef1a
+                          <object class="GtkScrolledWindow" id="scrolledwindow301">
b95ef1a
+                            <property name="visible">True</property>
b95ef1a
+                            <property name="can_focus">True</property>
b95ef1a
+                            <property name="border_width">5</property>
b95ef1a
+                            <property name="width_request">450</property>
b95ef1a
+                            <property name="height_request">350</property>
b95ef1a
+                            <property name="hscrollbar_policy">automatic</property>
b95ef1a
+                            <property name="vscrollbar_policy">automatic</property>
b95ef1a
+                            <property name="shadow_type">out</property>
b95ef1a
+                            <child>
b95ef1a
+                              <object class="GtkViewport" id="viewport301">
b95ef1a
+                                <property name="visible">True</property>
b95ef1a
+                                <property name="shadow_type">none</property>
b95ef1a
+                                <child>
b95ef1a
+                                  <object class="GtkVBox" id="vbox_system_keyboard_options">
b95ef1a
+                                    <property name="visible">True</property>
b95ef1a
+                                    <property name="orientation">vertical</property>
b95ef1a
+                                  </object>
b95ef1a
+                                </child>
b95ef1a
+                              </object>
b95ef1a
+                            </child>
b95ef1a
+                          </object>
b95ef1a
+                          <packing>
b95ef1a
+                            <property name="position">1</property>
b95ef1a
+                          </packing>
b95ef1a
+                        </child>
b95ef1a
+                      </object>
b95ef1a
+                    </child>
b95ef1a
+                  </object>
b95ef1a
+                </child>
b95ef1a
+                <child type="label">
b95ef1a
+                  <object class="GtkLabel" id="label301">
b95ef1a
+                    <property name="visible">True</property>
b95ef1a
+                    <property name="label" translatable="yes"><b>Keyboard Option</b></property>
b95ef1a
+                    <property name="use_markup">True</property>
b95ef1a
+                  </object>
b95ef1a
+                </child>
b95ef1a
+              </object>
b95ef1a
+            </child>
b95ef1a
+          </object>
b95ef1a
+          <packing>
b95ef1a
+            <property name="expand">False</property>
b95ef1a
+            <property name="position">0</property>
b95ef1a
+          </packing>
b95ef1a
+        </child>
b95ef1a
+        <child internal-child="action_area">
b95ef1a
+          <object class="GtkHButtonBox" id="hbuttonbox301">
b95ef1a
+            <property name="visible">True</property>
b95ef1a
+            <property name="homogeneous">True</property>
b95ef1a
+            <property name="layout_style">end</property>
b95ef1a
+            <child>
b95ef1a
+              <object class="GtkButton" id="button_system_keyboard_option_close">
b95ef1a
+                <property name="label">gtk-close</property>
b95ef1a
+                <property name="visible">True</property>
b95ef1a
+                <property name="use_stock">True</property>
b95ef1a
+              </object>
b95ef1a
+              <packing>
b95ef1a
+                <property name="expand">False</property>
b95ef1a
+                <property name="fill">False</property>
b95ef1a
+                <property name="position">0</property>
b95ef1a
+              </packing>
b95ef1a
+            </child>
b95ef1a
+          </object>
b95ef1a
+          <packing>
b95ef1a
+            <property name="expand">False</property>
b95ef1a
+            <property name="fill">False</property>
b95ef1a
+            <property name="pack_type">end</property>
b95ef1a
+            <property name="position">1</property>
b95ef1a
+          </packing>
b95ef1a
+        </child>
b95ef1a
+      </object>
b95ef1a
+    </child>
b95ef1a
+  </object>
60148ca
 </interface>
b95ef1a
diff --git a/setup/xkbsetup.py b/setup/xkbsetup.py
7232d5e
new file mode 100644
e0a5e11
index 0000000..af9ec53
7232d5e
--- /dev/null
b95ef1a
+++ b/setup/xkbsetup.py
e0a5e11
@@ -0,0 +1,457 @@
7232d5e
+# vim:set et sts=4 sw=4:
7232d5e
+#
7232d5e
+# ibus - The Input Bus
7232d5e
+#
63b857f
+# Copyright (c) 2011 Takao Fujiwara <takao.fujiwara1@gmail.com>
63b857f
+# Copyright (c) 2011 Peng Huang <shawn.p.huang@gmail.com>
63b857f
+# Copyright (c) 2011 Red Hat, Inc.
7232d5e
+#
7232d5e
+# This library is free software; you can redistribute it and/or
7232d5e
+# modify it under the terms of the GNU Lesser General Public
7232d5e
+# License as published by the Free Software Foundation; either
7232d5e
+# version 2 of the License, or (at your option) any later version.
7232d5e
+#
7232d5e
+# This library is distributed in the hope that it will be useful,
7232d5e
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
7232d5e
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
7232d5e
+# GNU Lesser General Public License for more details.
7232d5e
+#
7232d5e
+# You should have received a copy of the GNU Lesser General Public
7232d5e
+# License along with this program; if not, write to the
7232d5e
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
7232d5e
+# Boston, MA  02111-1307  USA
7232d5e
+
b95ef1a
+import gettext
7232d5e
+import gobject
b95ef1a
+import gtk
7232d5e
+import ibus
7232d5e
+
7232d5e
+_ = lambda a : gettext.dgettext("ibus", a)
b95ef1a
+XKB_MAX_LAYOUTS = 4
7232d5e
+
b95ef1a
+class XKBSetup(gobject.GObject):
b95ef1a
+    def __init__(self, config, builder):
b95ef1a
+        super(XKBSetup, self).__init__()
7232d5e
+
b95ef1a
+        self.__config = config
b95ef1a
+        self.__builder = builder
7232d5e
+
b95ef1a
+        # system keyboard layout setting
b95ef1a
+        self.__button_system_keyboard_layout = self.__builder.get_object("button_system_keyboard_layout")
b95ef1a
+        text = str(self.__config.get_value("general", "system_keyboard_layout", ''))
b95ef1a
+        if text == 'default' or text == '':
b95ef1a
+            text = _("Default")
b95ef1a
+        self.__button_system_keyboard_layout.set_label(text)
b95ef1a
+        if not self.__config.get_value("general", "use_system_keyboard_layout", True):
b95ef1a
+            self.__button_system_keyboard_layout.set_sensitive(False)
b95ef1a
+        self.__button_system_keyboard_layout.connect("clicked", self.__button_system_keyboard_layout_cb)
b95ef1a
+
b95ef1a
+        # use system keyboard layout setting
e0a5e11
+        self.__config.connect("value-changed", self.__config_value_changed_cb)
b95ef1a
+
b95ef1a
+        self.__xkblayoutconfig = None
b95ef1a
+        self.__preload_xkb_engines = []
b95ef1a
+        self.__other_xkb_engines = []
b95ef1a
+        self.__default_xkb_engine = None
b95ef1a
+        if ibus.XKBConfigRegistry.have_xkb():
b95ef1a
+            self.__xkblayoutconfig = ibus.XKBLayoutConfig()
7232d5e
+
b95ef1a
+        # config layouts dialog
b95ef1a
+        self.__init_config_layouts()
7232d5e
+
b95ef1a
+        # default system keyboard dialog
b95ef1a
+        self.__init_system_keyboard()
b95ef1a
+
e0a5e11
+    def __config_value_changed_cb(self, bus, section, name, value):
e0a5e11
+        if section == "general" and name == "use_system_keyboard_layout":
e0a5e11
+            self.__button_system_keyboard_layout.set_sensitive(value)
e0a5e11
+
b95ef1a
+    def __get_xkb_engines(self):
b95ef1a
+        xkb_engines = []
b95ef1a
+        xkbconfig = ibus.XKBConfigRegistry()
b95ef1a
+        layout_list = xkbconfig.get_layout_list()
b95ef1a
+        layout_desc = xkbconfig.get_layout_desc()
b95ef1a
+        layout_lang = xkbconfig.get_layout_lang()
b95ef1a
+        variant_desc = xkbconfig.get_variant_desc()
b95ef1a
+        for layout in layout_list.keys():
48fb145
+            langs = []
48fb145
+            if layout in layout_lang:
48fb145
+                langs = layout_lang[layout]
b95ef1a
+            for lang in langs:
b95ef1a
+                engine = ibus.XKBConfigRegistry.engine_desc_new(
b95ef1a
+                    lang,
b95ef1a
+                    layout,
b95ef1a
+                    layout_desc[layout],
b95ef1a
+                    None,
b95ef1a
+                    None)
b95ef1a
+                xkb_engines.append(engine)
b95ef1a
+            for variant in layout_list[layout]:
b95ef1a
+                label = "%s(%s)" % (layout, variant)
e5da135
+                sub_langs = []
b95ef1a
+                if label in layout_lang:
e5da135
+                    sub_langs = layout_lang[label]
e5da135
+                else:
e5da135
+                    sub_langs = langs
e5da135
+                for lang in sub_langs:
b95ef1a
+                    engine = ibus.XKBConfigRegistry.engine_desc_new(
b95ef1a
+                        lang,
b95ef1a
+                        layout,
b95ef1a
+                        layout_desc[layout],
b95ef1a
+                        variant,
e5da135
+                        variant_desc[label])
b95ef1a
+                    xkb_engines.append(engine)
b95ef1a
+        return xkb_engines
b95ef1a
+
b95ef1a
+    def __get_default_xkb_engine(self):
b95ef1a
+        if self.__default_xkb_engine != None:
b95ef1a
+            return self.__default_xkb_engine
b95ef1a
+        self.__default_xkb_engine = ibus.XKBConfigRegistry.engine_desc_new(
b95ef1a
+            "other",
b95ef1a
+            "default",
b95ef1a
+            _("Default"),
b95ef1a
+            None,
b95ef1a
+            None)
b95ef1a
+        return self.__default_xkb_engine
b95ef1a
+
b95ef1a
+    def __init_config_layouts(self):
b95ef1a
+        if not ibus.XKBConfigRegistry.have_xkb():
b95ef1a
+            button = self.__builder.get_object("button_config_layouts")
b95ef1a
+            button.hide()
b95ef1a
+            return
b95ef1a
+
b95ef1a
+        self.__dialog_config_layouts = self.__builder.get_object("dialog_config_layouts")
b95ef1a
+        self.__button_config_layouts_cancel = self.__builder.get_object("button_config_layouts_cancel")
b95ef1a
+        self.__button_config_layouts_cancel.connect("clicked", self.__button_config_layouts_cancel_cb)
b95ef1a
+        self.__button_config_layouts_ok = self.__builder.get_object("button_config_layouts_ok")
b95ef1a
+        self.__button_config_layouts_ok.connect("clicked", self.__button_config_layouts_ok_cb)
b95ef1a
+        self.__vbox_all_keyboard_layouts = self.__builder.get_object("vbox_all_keyboard_layouts")
b95ef1a
+
b95ef1a
+        xkb_engines = self.__get_xkb_engines()
b95ef1a
+        if len(xkb_engines) > 0:
b95ef1a
+            button = self.__builder.get_object("button_config_layouts")
b95ef1a
+            button.connect("clicked", self.__button_config_layouts_cb)
b95ef1a
+            button.set_sensitive(True)
b95ef1a
+
b95ef1a
+        engine_dict = {}
b95ef1a
+        for engine in xkb_engines:
b95ef1a
+            if not engine.name.startswith("xkb:layout:"):
b95ef1a
+                continue
b95ef1a
+            lang = ibus.get_language_name(engine.language)
b95ef1a
+            if lang not in engine_dict:
b95ef1a
+                engine_dict[lang] = []
b95ef1a
+            engine_dict[lang].append(engine)
b95ef1a
+
b95ef1a
+        keys = engine_dict.keys()
b95ef1a
+        keys.sort()
b95ef1a
+        if ibus.get_language_name("Other") in keys:
b95ef1a
+            keys.remove(ibus.get_language_name("Other"))
b95ef1a
+            keys += [ibus.get_language_name("Other")]
b95ef1a
+
b95ef1a
+        preload_xkb_engines = self.__xkblayoutconfig.get_preload_layouts()
b95ef1a
+        for lang in keys:
b95ef1a
+            expander = gtk.Expander("")
b95ef1a
+            self.__vbox_all_keyboard_layouts.pack_start(expander, True, True, 0)
b95ef1a
+            expander.show()
b95ef1a
+            label = expander.get_label_widget()
b95ef1a
+            label.set_label(lang)
b95ef1a
+            align = gtk.Alignment(0, 0, 1, 0)
b95ef1a
+            align.set_padding(6, 0, 18, 0)
b95ef1a
+            expander.add(align)
b95ef1a
+            align.show()
b95ef1a
+            vbox = gtk.VBox(False, 0)
b95ef1a
+            align.add(vbox)
b95ef1a
+            vbox.show()
b95ef1a
+
b95ef1a
+            def cmp_engine(a, b):
b95ef1a
+                if a.rank == b.rank:
b95ef1a
+                    return cmp(a.longname, b.longname)
b95ef1a
+                return int(b.rank - a.rank)
b95ef1a
+            engine_dict[lang].sort(cmp_engine)
b95ef1a
+
b95ef1a
+            for engine in engine_dict[lang]:
b95ef1a
+                sub_name = engine.name[len("xkb:layout:"):]
b95ef1a
+                layout_list = sub_name.split(':')
b95ef1a
+                if len(layout_list) > 1:
b95ef1a
+                    layout = "%s(%s)" % (layout_list[0], layout_list[1])
b95ef1a
+                else:
b95ef1a
+                    layout = layout_list[0]
b95ef1a
+                has_preloaded = False
b95ef1a
+                for preload_name in preload_xkb_engines:
b95ef1a
+                    preload_name = str(preload_name)
b95ef1a
+                    if len(preload_name) == 0:
b95ef1a
+                        continue
b95ef1a
+                    if layout == preload_name:
b95ef1a
+                        has_preloaded = True