]> git.pld-linux.org Git - packages/ibus.git/blob - ibus-541492-xkb.patch
- updated to 1.4.99.20121109
[packages/ibus.git] / ibus-541492-xkb.patch
1 From 15a9eb2a8f9a278b5e357d7dd7ffebedd05d0c5e Mon Sep 17 00:00:00 2001
2 From: fujiwarat <takao.fujiwara1@gmail.com>
3 Date: Tue, 9 Oct 2012 17:21:01 +0900
4 Subject: [PATCH] Add ibus-xkb and libgnomekbd.
5
6 ---
7  bindings/vala/Gkbd-3.0.metadata |    1 +
8  bindings/vala/Makefile.am       |   23 ++
9  bindings/vala/Xkl-1.0.metadata  |    3 +
10  client/x11/gdk-private.c        |    3 +-
11  configure.ac                    |   60 +++++
12  data/ibus.schemas.in            |   94 +++++++
13  engine/Makefile.am              |    6 +
14  engine/ibus-xkb/Makefile.am     |   64 +++++
15  engine/ibus-xkb/ibus-xkb-main.c |  111 ++++++++
16  engine/ibus-xkb/xkblib.c        |  327 ++++++++++++++++++++++++
17  engine/ibus-xkb/xkblib.h        |   41 +++
18  engine/main.vala                |   86 +++++++
19  engine/simple.xml.in.in         |  531 +--------------------------------------
20  ibus-1.0.pc.in                  |    4 +
21  ibus.spec.in                    |   12 +
22  setup/main.py                   |   38 ++-
23  src/Makefile.am                 |    5 +
24  src/ibus.h                      |    1 +
25  src/ibusxkbxml.c                |  466 ++++++++++++++++++++++++++++++++++
26  src/ibusxkbxml.h                |  187 ++++++++++++++
27  ui/gtk3/Makefile.am             |   39 +++
28  ui/gtk3/gkbdlayout.vala.false   |   63 +++++
29  ui/gtk3/gkbdlayout.vala.true    |  108 ++++++++
30  ui/gtk3/keybindingmanager.vala  |   14 +-
31  ui/gtk3/panel.vala              |  365 +++++++++++++++++++++++----
32  ui/gtk3/switcher.vala           |   49 ++--
33  ui/gtk3/xkblayout.vala          |  464 ++++++++++++++++++++++++++++++++++
34  27 files changed, 2562 insertions(+), 603 deletions(-)
35  create mode 100644 bindings/vala/Gkbd-3.0.metadata
36  create mode 100644 bindings/vala/Xkl-1.0.metadata
37  create mode 100644 engine/ibus-xkb/Makefile.am
38  create mode 100644 engine/ibus-xkb/ibus-xkb-main.c
39  create mode 100644 engine/ibus-xkb/xkblib.c
40  create mode 100644 engine/ibus-xkb/xkblib.h
41  create mode 100644 src/ibusxkbxml.c
42  create mode 100644 src/ibusxkbxml.h
43  create mode 100644 ui/gtk3/gkbdlayout.vala.false
44  create mode 100644 ui/gtk3/gkbdlayout.vala.true
45  create mode 100644 ui/gtk3/xkblayout.vala
46
47 diff --git a/bindings/vala/Gkbd-3.0.metadata b/bindings/vala/Gkbd-3.0.metadata
48 new file mode 100644
49 index 0000000..661e6fd
50 --- /dev/null
51 +++ b/bindings/vala/Gkbd-3.0.metadata
52 @@ -0,0 +1 @@
53 +Configuration cheader_filename="libgnomekbd/gkbd-configuration.h"
54 diff --git a/bindings/vala/Makefile.am b/bindings/vala/Makefile.am
55 index abcc543..307a161 100644
56 --- a/bindings/vala/Makefile.am
57 +++ b/bindings/vala/Makefile.am
58 @@ -27,12 +27,22 @@ dist_vapi_DATA = \
59         $(NULL)
60  
61  # Don't rebuild vapi every time gir is updated.
62 +if ENABLE_LIBGNOMEKBD
63  vapi_deps = \
64         $(srcdir)/IBus-1.0.metadata \
65         $(srcdir)/IBus-1.0-custom.vala \
66         | \
67 +       $(builddir)/gkbd.vapi \
68         $(top_srcdir)/src/IBus-@IBUS_API_VERSION@.gir \
69         $(NULL)
70 +else
71 +vapi_deps = \
72 +       $(srcdir)/IBus-1.0.metadata \
73 +       $(srcdir)/IBus-1.0-custom.vala \
74 +       | \
75 +       $(top_srcdir)/src/IBus-@IBUS_API_VERSION@.gir \
76 +       $(NULL)
77 +endif
78  
79  ibus-@IBUS_API_VERSION@.vapi: $(vapi_deps)
80         $(AM_V_GEN) $(VAPIGEN) --library ibus-@IBUS_API_VERSION@ \
81 @@ -41,13 +51,26 @@ ibus-@IBUS_API_VERSION@.vapi: $(vapi_deps)
82                         $(top_srcdir)/src/IBus-@IBUS_API_VERSION@.gir \
83                         $(srcdir)/IBus-1.0-custom.vala
84  
85 +if ENABLE_LIBGNOMEKBD
86 +$(builddir)/gkbd.vapi:
87 +       $(AM_V_GEN) $(VAPIGEN) --library gkbd \
88 +           --metadatadir $(srcdir) \
89 +           --pkg gtk+-3.0 --pkg glib-2.0 --pkg gmodule-2.0 \
90 +           /usr/share/gir-1.0/Gkbd-3.0.gir
91 +       $(NULL)
92 +endif
93 +
94  EXTRA_DIST = \
95 +       Gkbd-3.0.metadata \
96         IBus-1.0.metadata \
97         IBus-1.0-custom.vala \
98         config.vapi \
99         xi.vapi \
100 +       Xkl-1.0.metadata \
101         $(NULL)
102  
103 +CLEANFILES = gkbd.vapi
104 +
105  MAINTAINERCLEANFILES = ibus-@IBUS_API_VERSION@.vapi
106  
107  -include $(top_srcdir)/git.mk
108 diff --git a/bindings/vala/Xkl-1.0.metadata b/bindings/vala/Xkl-1.0.metadata
109 new file mode 100644
110 index 0000000..4961d0c
111 --- /dev/null
112 +++ b/bindings/vala/Xkl-1.0.metadata
113 @@ -0,0 +1,3 @@
114 +Xkl cheader_filename="libxklavier/xklavier.h"
115 +Engine
116 +    .filter_events.evt ref type="X.Event"
117 diff --git a/client/x11/gdk-private.c b/client/x11/gdk-private.c
118 index 009a5b0..20689c3 100644
119 --- a/client/x11/gdk-private.c
120 +++ b/client/x11/gdk-private.c
121 @@ -27,8 +27,7 @@
122  #include <gdk/gdkx.h>
123  #include <gdk/gdkkeysyms.h>
124  
125 -#ifdef HAVE_X11_XKBLIB_H
126 -#  define HAVE_XKB
127 +#ifdef HAVE_XKB
128  #  include <X11/XKBlib.h>
129  #endif
130  
131 diff --git a/configure.ac b/configure.ac
132 index cc7d0e0..df0d4a7 100644
133 --- a/configure.ac
134 +++ b/configure.ac
135 @@ -252,6 +252,63 @@ else
136      enable_xim="no (disabled, use --enable-xim to enable)"
137  fi
138  
139 +# --enable-xkb option.
140 +AC_ARG_ENABLE(xkb,
141 +    AS_HELP_STRING([--enable-xkb],
142 +                   [Build xkb]),
143 +    [enable_xkb=$enableval],
144 +    [enable_xkb=no]
145 +)
146 +AM_CONDITIONAL([ENABLE_XKB], [test x"$enable_xkb" = x"yes"])
147 +if test x"$enable_xkb" = x"yes"; then
148 +    PKG_CHECK_MODULES(X11, [
149 +        x11
150 +    ])
151 +    PKG_CHECK_MODULES(XKB,
152 +        [xkbfile],,
153 +        [XKB_LIBS="-lxkbfile"]
154 +    )
155 +    AC_DEFINE(HAVE_XKB, 1, [define to 1 if you have xkbfile])
156 +    HAVE_IBUS_XKB=true
157 +else
158 +    enable_xkb="no (disabled, use --enable-xkb to enable)"
159 +    HAVE_IBUS_XKB=false
160 +fi
161 +AC_SUBST(HAVE_IBUS_XKB)
162 +
163 +# --enable-libgnomekbd option.
164 +AC_ARG_ENABLE(libgnomekbd,
165 +    AS_HELP_STRING([--enable-libgnomekbd],
166 +                   [Use libgnomekbd to handle the keymaps]),
167 +    [enable_libgnomekbd=$enableval],
168 +    [enable_libgnomekbd=no]
169 +)
170 +AM_CONDITIONAL([ENABLE_LIBGNOMEKBD], [test x"$enable_libgnomekbd" = x"yes"])
171 +if test x"$enable_libgnomekbd" = x"yes"; then
172 +    # check for libgnomekbd
173 +    PKG_CHECK_MODULES(LIBGNOMEKBDUI, [
174 +        libgnomekbdui
175 +    ])
176 +    PKG_CHECK_MODULES(ATK, [
177 +        atk
178 +    ])
179 +    HAVE_IBUS_GKBD=true
180 +else
181 +    enable_libgnomekbd="no (disabled, use --enable-libgnomekbd to enable)"
182 +    HAVE_IBUS_GKBD=false
183 +fi
184 +AC_SUBST(HAVE_IBUS_GKBD)
185 +
186 +# Define XKB rules file
187 +AC_ARG_WITH(xkb-rules-xml,
188 +    AS_HELP_STRING([--with-xkb-rules-xml[=$DIR/evdev.xml]],
189 +        [Set evdev.xml file path (default: /usr/share/X11/xkb/rules/evdev.xml)]),
190 +    XKB_RULES_XML_FILE=$with_xkb_rules_xml,
191 +    XKB_RULES_XML_FILE="/usr/share/X11/xkb/rules/evdev.xml"
192 +)
193 +AC_DEFINE_UNQUOTED(XKB_RULES_XML_FILE, "$XKB_RULES_XML_FILE",
194 +    [Define file path of evdev.xml])
195 +
196  # GObject introspection
197  GOBJECT_INTROSPECTION_CHECK([0.6.8])
198  
199 @@ -517,6 +574,7 @@ src/ibusversion.h
200  src/tests/Makefile
201  bus/Makefile
202  engine/Makefile
203 +engine/ibus-xkb/Makefile
204  engine/simple.xml.in
205  util/Makefile
206  util/IMdkit/Makefile
207 @@ -577,5 +635,7 @@ Build options:
208    Panel icon                "$IBUS_ICON_KEYBOARD"
209    Enable surrounding-text   $enable_surrounding_text
210    Run test cases            $enable_tests
211 +  Build XKB                 $enable_xkb
212 +  Build libgnomebkd         $enable_libgnomekbd
213  ])
214  
215 diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in
216 index 53ba05c..07169ae 100644
217 --- a/data/ibus.schemas.in
218 +++ b/data/ibus.schemas.in
219 @@ -26,6 +26,52 @@
220        </locale>
221      </schema>
222      <schema>
223 +      <key>/schemas/desktop/ibus/general/use_xmodmap</key>
224 +      <applyto>/desktop/ibus/general/use_xmodmap</applyto>
225 +      <owner>ibus</owner>
226 +      <type>bool</type>
227 +      <default>true</default>
228 +      <locale name="C">
229 +        <short>Use xmodmap</short>
230 +           <long>Run xmodmap if .xmodmap/.Xmodmap exists.</long>
231 +      </locale>
232 +    </schema>
233 +    <schema>
234 +      <key>/schemas/desktop/ibus/general/xkb_latin_layouts</key>
235 +      <applyto>/desktop/ibus/general/xkb_latin_layouts</applyto>
236 +      <owner>ibus</owner>
237 +      <type>list</type>
238 +      <list_type>string</list_type>
239 +      <default>[ara,bg,cz,dev,gr,gur,in,jp(kana),mal,mkd,ru,ua]</default>
240 +      <locale name="C">
241 +        <short>Latin layout which have no ASCII</short>
242 +           <long>us layout is appended to the latin layouts. variant is not needed.</long>
243 +      </locale>
244 +    </schema>
245 +    <schema>
246 +      <key>/schemas/desktop/ibus/general/load_xkb_layouts</key>
247 +      <applyto>/desktop/ibus/general/load_xkb_layouts</applyto>
248 +      <owner>ibus</owner>
249 +      <type>list</type>
250 +      <list_type>string</list_type>
251 +      <default>[us,us(chr),us(dvorak),ad,al,am,ara,az,ba,bd,be,bg,br,bt,by,
252 +de,dk,ca,ch,cn(tib),cz,ee,epo,es,et,fi,fo,fr,
253 +gb,ge,ge(dsb),ge(ru),ge(os),gh,gh(akan),gh(ewe),gh(fula),gh(ga),gh(hausa),
254 +gn,gr,hu,hr,ie,ie(CloGaelach),il,
255 +in,
256 +in(tel),in(bolnagri),iq,iq(ku),ir,ir(ku),is,it,jp,
257 +kg,kh,kz,la,latam,lk,lk(tam_unicode),lt,lv,ma,ma(tifinagh),mal,mao,
258 +me,mk,mm,mt,mv,ng,ng(hausa),ng,ng(igbo),ng(yoruba),nl,no,no(smi),np,
259 +pk,pl,pl(csb),pt,ro,rs,ru,ru(cv),ru(kom),ru(sah),ru(tt),ru(xal),
260 +se,si,sk,sy,sy(ku),th,tj,tr,ua,uz,vn
261 +]</default>
262 +      <locale name="C">
263 +        <short>XKB layout list which is shown on ibus-setup</short>
264 +           <long>XKB layout list which is shown on ibus-setup.
265 +                 The format is "layout" or "layout(variant)".</long>
266 +      </locale>
267 +    </schema>
268 +    <schema>
269        <key>/schemas/desktop/ibus/general/hotkey/trigger</key>
270        <applyto>/desktop/ibus/general/hotkey/trigger</applyto>
271        <owner>ibus</owner>
272 @@ -34,6 +80,54 @@
273        <default>[Control+space,Zenkaku_Hankaku,Alt+Kanji,Alt+grave,Hangul,Alt+Release+Alt_R]</default>
274        <locale name="C">
275          <short>Trigger shortcut keys</short>
276 +            <long>The shortcut keys for turning input method on or off</long>
277 +      </locale>
278 +    </schema>
279 +    <schema>
280 +      <key>/schemas/desktop/ibus/general/hotkey/trigger_accel</key>
281 +      <applyto>/desktop/ibus/general/hotkey/trigger_accel</applyto>
282 +      <owner>ibus</owner>
283 +      <type>list</type>
284 +      <list_type>string</list_type>
285 +      <default>[&lt;Control&gt;space]</default>
286 +      <locale name="C">
287 +        <short>Trigger shortcut keys for gtk_accelerator_parse</short>
288 +            <long>The shortcut keys for turning input method on or off</long>
289 +      </locale>
290 +    </schema>
291 +    <schema>
292 +      <key>/schemas/desktop/ibus/general/hotkey/trigger_accel_backward</key>
293 +      <applyto>/desktop/ibus/general/hotkey/trigger_accel_backward</applyto>
294 +      <owner>ibus</owner>
295 +      <type>list</type>
296 +      <list_type>string</list_type>
297 +      <default>[&lt;Control&gt;&lt;Shift&gt;space]</default>
298 +      <locale name="C">
299 +        <short>Trigger reverse shortcut keys for gtk_accelerator_parse</short>
300 +            <long>The reverse shortcut keys for turning input method on or off</long>
301 +      </locale>
302 +    </schema>
303 +    <schema>
304 +      <key>/schemas/desktop/ibus/general/hotkey/trigger_ja</key>
305 +      <applyto>/desktop/ibus/general/hotkey/trigger_ja</applyto>
306 +      <owner>ibus</owner>
307 +      <type>list</type>
308 +      <list_type>string</list_type>
309 +      <default>[Zenkaku_Hankaku]</default>
310 +      <locale name="C">
311 +        <short>Trigger shortcut keys for ja gtk_accelerator_parse</short>
312 +           <long>The shortcut keys for turning input method on or off</long>
313 +      </locale>
314 +    </schema>
315 +    <schema>
316 +      <key>/schemas/desktop/ibus/general/hotkey/trigger_ko</key>
317 +      <applyto>/desktop/ibus/general/hotkey/trigger_ko</applyto>
318 +      <owner>ibus</owner>
319 +      <type>list</type>
320 +      <list_type>string</list_type>
321 +      <default>[Hangul, Alt_R]</default>
322 +      <locale name="C">
323 +        <short>Trigger shortcut keys for ko gtk_accelerator_parse</short>
324             <long>The shortcut keys for turning input method on or off</long>
325        </locale>
326      </schema>
327 diff --git a/engine/Makefile.am b/engine/Makefile.am
328 index b3b46be..7a806fc 100644
329 --- a/engine/Makefile.am
330 +++ b/engine/Makefile.am
331 @@ -22,6 +22,12 @@
332  
333  libibus = $(top_builddir)/src/libibus-@IBUS_API_VERSION@.la
334  
335 +SUBDIRS =
336 +
337 +if ENABLE_XKB
338 +SUBDIRS += ibus-xkb
339 +endif
340 +
341  INCLUDES = \
342         -I$(top_srcdir)/src \
343         -I$(top_builddir)/src \
344 diff --git a/engine/ibus-xkb/Makefile.am b/engine/ibus-xkb/Makefile.am
345 new file mode 100644
346 index 0000000..65ede37
347 --- /dev/null
348 +++ b/engine/ibus-xkb/Makefile.am
349 @@ -0,0 +1,64 @@
350 +# vim:set noet ts=4:
351 +#
352 +# ibus - The Input Bus
353 +#
354 +# Copyright (c) 2012 Takao Fujiwara <takao.fujiwara1@gmail.com>
355 +# Copyright (c) 2012 Red Hat, Inc.
356 +#
357 +# This library is free software; you can redistribute it and/or
358 +# modify it under the terms of the GNU Lesser General Public
359 +# License as published by the Free Software Foundation; either
360 +# version 2 of the License, or (at your option) any later version.
361 +#
362 +# This library is distributed in the hope that it will be useful,
363 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
364 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
365 +# GNU Lesser General Public License for more details.
366 +#
367 +# You should have received a copy of the GNU Lesser General Public
368 +# License along with this program; if not, write to the
369 +# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
370 +# Boston, MA  02111-1307  USA
371 +
372 +libibus = $(top_builddir)/src/libibus-@IBUS_API_VERSION@.la
373 +
374 +INCLUDES = \
375 +       -I$(top_srcdir)/src \
376 +       -I$(top_builddir)/src \
377 +       $(NULL)
378 +
379 +AM_CFLAGS = \
380 +       $(INCLUDES) \
381 +       -DG_LOG_DOMAIN=\"IBUS\" \
382 +       -DIBUS_DISABLE_DEPRECATED \
383 +       -Wno-unused-variable \
384 +       -Wno-unused-but-set-variable \
385 +       -Wno-unused-function \
386 +       $(NULL)
387 +
388 +AM_LDADD = \
389 +       $(NULL)
390 +
391 +libexec_PROGRAMS = ibus-xkb
392 +
393 +ibus_xkb_SOURCES = \
394 +       ibus-xkb-main.c \
395 +       xkblib.h \
396 +       xkblib.c \
397 +       $(NULL)
398 +ibus_xkb_CFLAGS = \
399 +       @XKB_CFLAGS@ \
400 +       @X11_CFLAGS@ \
401 +       @GLIB2_CFLAGS@ \
402 +       $(NULL)
403 +ibus_xkb_LDADD = \
404 +       @XKB_LIBS@ \
405 +       @X11_LIBS@ \
406 +       @GLIB2_LIBS@ \
407 +       $(libibus) \
408 +       $(NULL)
409 +
410 +$(libibus):
411 +       $(MAKE) -C $(top_builddir)/src
412 +
413 +-include $(top_srcdir)/git.mk
414 diff --git a/engine/ibus-xkb/ibus-xkb-main.c b/engine/ibus-xkb/ibus-xkb-main.c
415 new file mode 100644
416 index 0000000..2529e4d
417 --- /dev/null
418 +++ b/engine/ibus-xkb/ibus-xkb-main.c
419 @@ -0,0 +1,111 @@
420 +/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
421 +/* vim:set et sts=4: */
422 +/* bus - The Input Bus
423 + * Copyright (C) 2012 Takao Fujiwara <takao.fujiwara1@gmail.com>
424 + * Copyright (C) 2012 Peng Huang <shawn.p.huang@gmail.com>
425 + * Copyright (C) 2012 Red Hat, Inc.
426 + *
427 + * This library is free software; you can redistribute it and/or
428 + * modify it under the terms of the GNU Lesser General Public
429 + * License as published by the Free Software Foundation; either
430 + * version 2 of the License, or (at your option) any later version.
431 + *
432 + * This library is distributed in the hope that it will be useful,
433 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
434 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
435 + * Lesser General Public License for more details.
436 + *
437 + * You should have received a copy of the GNU Lesser General Public
438 + * License along with this library; if not, write to the
439 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
440 + * Boston, MA 02111-1307, USA.
441 + */
442 +#ifdef HAVE_CONFIG_H
443 +#include <config.h>
444 +#endif
445 +
446 +#include <glib.h>
447 +#include <glib/gprintf.h>
448 +#include <glib/gi18n.h>
449 +#include <X11/Xlib.h>
450 +
451 +#ifdef ENABLE_NLS
452 +#include <locale.h>
453 +#endif
454 +
455 +#include "xkblib.h"
456 +
457 +static gboolean get_layout = FALSE;
458 +static gboolean get_group = FALSE;
459 +static gchar *layout = NULL;
460 +static gchar *variant = NULL;
461 +static gchar *option = NULL;
462 +static int group = 0;
463 +
464 +static const GOptionEntry entries[] =
465 +{
466 +    { "get", 'g', 0, G_OPTION_ARG_NONE, &get_layout, N_("Get current xkb layout"), NULL },
467 +    { "layout", 'l', 0, G_OPTION_ARG_STRING, &layout, N_("Set xkb LAYOUT"), N_("LAYOUT") },
468 +    { "variant", 'v', 0, G_OPTION_ARG_STRING, &variant, N_("Set xkb VARIANT"), N_("VARIANT") },
469 +    { "option", 'o', 0, G_OPTION_ARG_STRING, &option, N_("Set xkb OPTION"), N_("OPTION") },
470 +    { "get-group", 'G', 0, G_OPTION_ARG_NONE, &get_group, N_("Get current xkb state"), NULL },
471 +    { NULL },
472 +};
473 +
474 +int
475 +main (int argc, char *argv[])
476 +{
477 +    GOptionContext *context;
478 +    GError *error = NULL;
479 +    Display *xdisplay;
480 +
481 +#ifdef ENABLE_NLS
482 +    setlocale (LC_ALL, "");
483 +
484 +    bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
485 +    bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
486 +#endif
487 +
488 +    context = g_option_context_new ("- ibus daemon");
489 +
490 +    g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
491 +    g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
492 +
493 +    if (!g_option_context_parse (context, &argc, &argv, &error)) {
494 +        g_printerr ("Option parsing failed: %s\n", error->message);
495 +        return -1;
496 +    }
497 +
498 +    xdisplay = XOpenDisplay (NULL);
499 +    if (xdisplay == NULL) {
500 +        g_warning ("Could not open display");
501 +        return -1;
502 +    }
503 +    ibus_xkb_init (xdisplay);
504 +
505 +    if (layout) {
506 +        ibus_xkb_set_layout (layout, variant, option);
507 +    }
508 +    if (get_layout) {
509 +        layout = ibus_xkb_get_current_layout ();
510 +        variant = ibus_xkb_get_current_variant ();
511 +        option = ibus_xkb_get_current_option ();
512 +        g_printf ("layout: %s\n"
513 +                  "variant: %s\n"
514 +                  "option: %s\n",
515 +                  layout ? layout : "",
516 +                  variant ? variant : "",
517 +                  option ? option : "");
518 +        g_free (layout);
519 +        g_free (variant);
520 +        g_free (option);
521 +    }
522 +    if (get_group) {
523 +        group = ibus_xkb_get_current_group ();
524 +        g_printf ("group: %d\n", group);
525 +    }
526 +
527 +    ibus_xkb_finit ();
528 +
529 +    return 0;
530 +}
531 diff --git a/engine/ibus-xkb/xkblib.c b/engine/ibus-xkb/xkblib.c
532 new file mode 100644
533 index 0000000..bb25455
534 --- /dev/null
535 +++ b/engine/ibus-xkb/xkblib.c
536 @@ -0,0 +1,327 @@
537 +/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
538 +/* vim:set et sts=4: */
539 +/* bus - The Input Bus
540 + * Copyright (C) 2012 Takao Fujiwara <takao.fujiwara1@gmail.com>
541 + * Copyright (C) 2012 Peng Huang <shawn.p.huang@gmail.com>
542 + * Copyright (C) 2012 Red Hat, Inc.
543 + *
544 + * This library is free software; you can redistribute it and/or
545 + * modify it under the terms of the GNU Lesser General Public
546 + * License as published by the Free Software Foundation; either
547 + * version 2 of the License, or (at your option) any later version.
548 + *
549 + * This library is distributed in the hope that it will be useful,
550 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
551 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
552 + * Lesser General Public License for more details.
553 + *
554 + * You should have received a copy of the GNU Lesser General Public
555 + * License along with this library; if not, write to the
556 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
557 + * Boston, MA 02111-1307, USA.
558 + */
559 +#ifdef HAVE_CONFIG_H
560 +#include <config.h>
561 +#endif
562 +
563 +#include <glib.h>
564 +#include <X11/Xlib.h>
565 +#include <X11/Xatom.h>
566 +#include <X11/XKBlib.h>
567 +#include <stdio.h> /* for XKBrules.h */
568 +#include <X11/extensions/XKBrules.h>
569 +#include <X11/extensions/XKBstr.h>
570 +#include <string.h>
571 +
572 +#include "xkblib.h"
573 +
574 +#ifndef XKB_RULES_XML_FILE
575 +#define XKB_RULES_XML_FILE "/usr/share/X11/xkb/rules/evdev.xml"
576 +#endif
577 +
578 +static gchar          **default_layouts;
579 +static gchar          **default_variants;
580 +static gchar          **default_options;
581 +static int              default_layout_group;
582 +
583 +static Display *
584 +get_xdisplay (Display *xdisplay)
585 +{
586 +    static Display *saved_xdisplay = NULL;
587 +    if (xdisplay != NULL) {
588 +        saved_xdisplay = xdisplay;
589 +    }
590 +    return saved_xdisplay;
591 +}
592 +
593 +static void
594 +init_xkb_default_layouts (Display *xdisplay)
595 +{
596 +    XkbStateRec state;
597 +    Atom xkb_rules_name, type;
598 +    int format;
599 +    unsigned long l, nitems, bytes_after;
600 +    unsigned char *prop = NULL;
601 +
602 +    xkb_rules_name = XInternAtom (xdisplay, "_XKB_RULES_NAMES", TRUE);
603 +    if (xkb_rules_name == None) {
604 +        g_warning ("Could not get XKB rules atom");
605 +        return;
606 +    }
607 +    if (XGetWindowProperty (xdisplay,
608 +                            XDefaultRootWindow (xdisplay),
609 +                            xkb_rules_name,
610 +                            0, 1024, FALSE, XA_STRING,
611 +                            &type, &format, &nitems, &bytes_after, &prop) != Success) {
612 +        g_warning ("Could not get X property");
613 +        return;
614 +    }
615 +    if (nitems < 3) {
616 +        g_warning ("Could not get group layout from X property");
617 +        return;
618 +    }
619 +    for (l = 0; l < 2; l++) {
620 +        prop += strlen ((const char *) prop) + 1;
621 +    }
622 +    if (prop == NULL || *prop == '\0') {
623 +        g_warning ("No layouts form X property");
624 +        return;
625 +    }
626 +    default_layouts = g_strsplit ((gchar *) prop, ",", -1);
627 +    prop += strlen ((const char *) prop) + 1;
628 +    default_variants = g_strsplit ((gchar *) prop, ",", -1);
629 +    prop += strlen ((const char *) prop) + 1;
630 +    default_options = g_strsplit ((gchar *) prop, ",", -1);
631 +
632 +    if (XkbGetState (xdisplay, XkbUseCoreKbd, &state) != Success) {
633 +        g_warning ("Could not get state");
634 +        return;
635 +    }
636 +    default_layout_group = state.group;
637 +}
638 +
639 +static Bool
640 +set_xkb_rules (Display *xdisplay,
641 +               const char *rules_file, const char *model,
642 +               const char *all_layouts, const char *all_variants,
643 +               const char *all_options)
644 +{
645 +    gchar *rules_path;
646 +    XkbRF_RulesPtr rules;
647 +    XkbRF_VarDefsRec rdefs;
648 +    XkbComponentNamesRec rnames;
649 +    XkbDescPtr xkb;
650 +
651 +    rules_path = g_strdup ("./rules/evdev");
652 +    rules = XkbRF_Load (rules_path, "C", TRUE, TRUE);
653 +    if (rules == NULL) {
654 +        g_return_val_if_fail (XKB_RULES_XML_FILE != NULL, FALSE);
655 +
656 +        g_free (rules_path);
657 +        if (g_str_has_suffix (XKB_RULES_XML_FILE, ".xml")) {
658 +            rules_path = g_strndup (XKB_RULES_XML_FILE,
659 +                                    strlen (XKB_RULES_XML_FILE) - 4);
660 +        } else {
661 +            rules_path = g_strdup (XKB_RULES_XML_FILE);
662 +        }
663 +        rules = XkbRF_Load (rules_path, "C", TRUE, TRUE);
664 +    }
665 +    g_return_val_if_fail (rules != NULL, FALSE);
666 +
667 +    memset (&rdefs, 0, sizeof (XkbRF_VarDefsRec));
668 +    memset (&rnames, 0, sizeof (XkbComponentNamesRec));
669 +    rdefs.model = model ? g_strdup (model) : NULL;
670 +    rdefs.layout = all_layouts ? g_strdup (all_layouts) : NULL;
671 +    rdefs.variant = all_variants ? g_strdup (all_variants) : NULL;
672 +    rdefs.options = all_options ? g_strdup (all_options) : NULL;
673 +    XkbRF_GetComponents (rules, &rdefs, &rnames);
674 +    xkb = XkbGetKeyboardByName (xdisplay, XkbUseCoreKbd, &rnames,
675 +                                XkbGBN_AllComponentsMask,
676 +                                XkbGBN_AllComponentsMask &
677 +                                (~XkbGBN_GeometryMask), True);
678 +    if (!xkb) {
679 +        g_warning ("Cannot load new keyboard description.");
680 +        return FALSE;
681 +    }
682 +    XkbRF_SetNamesProp (xdisplay, rules_path, &rdefs);
683 +    g_free (rules_path);
684 +    g_free (rdefs.model);
685 +    g_free (rdefs.layout);
686 +    g_free (rdefs.variant);
687 +    g_free (rdefs.options);
688 +
689 +    return TRUE;
690 +}
691 +
692 +static Bool
693 +update_xkb_properties (Display *xdisplay,
694 +                       const char *rules_file, const char *model,
695 +                       const char *all_layouts, const char *all_variants,
696 +                       const char *all_options)
697 +{
698 +    int len;
699 +    char *pval;
700 +    char *next;
701 +    Atom rules_atom;
702 +    Window root_window;
703 +
704 +    len = (rules_file ? strlen (rules_file) : 0);
705 +    len += (model ? strlen (model) : 0);
706 +    len += (all_layouts ? strlen (all_layouts) : 0);
707 +    len += (all_variants ? strlen (all_variants) : 0);
708 +    len += (all_options ? strlen (all_options) : 0);
709 +
710 +    if (len < 1) {
711 +        return TRUE;
712 +    }
713 +    len += 5; /* trailing NULs */
714 +
715 +    rules_atom = XInternAtom (xdisplay, _XKB_RF_NAMES_PROP_ATOM, False);
716 +    root_window = XDefaultRootWindow (xdisplay);
717 +    pval = next = g_new0 (char, len + 1);
718 +    if (!pval) {
719 +        return TRUE;
720 +    }
721 +
722 +    if (rules_file) {
723 +        strcpy (next, rules_file);
724 +        next += strlen (rules_file);
725 +    }
726 +    *next++ = '\0';
727 +    if (model) {
728 +        strcpy (next, model);
729 +        next += strlen (model);
730 +    }
731 +    *next++ = '\0';
732 +    if (all_layouts) {
733 +        strcpy (next, all_layouts);
734 +        next += strlen (all_layouts);
735 +    }
736 +    *next++ = '\0';
737 +    if (all_variants) {
738 +        strcpy (next, all_variants);
739 +        next += strlen (all_variants);
740 +    }
741 +    *next++ = '\0';
742 +    if (all_options) {
743 +        strcpy (next, all_options);
744 +        next += strlen (all_options);
745 +    }
746 +    *next++ = '\0';
747 +    if ((next - pval) != len) {
748 +        g_free (pval);
749 +        return TRUE;
750 +    }
751 +
752 +    XChangeProperty (xdisplay, root_window,
753 +                    rules_atom, XA_STRING, 8, PropModeReplace,
754 +                    (unsigned char *) pval, len);
755 +    XSync(xdisplay, False);
756 +
757 +    return TRUE;
758 +}
759 +
760 +void
761 +ibus_xkb_init (Display *xdisplay)
762 +{
763 +    get_xdisplay (xdisplay);
764 +    init_xkb_default_layouts (xdisplay);
765 +}
766 +
767 +void
768 +ibus_xkb_finit (void)
769 +{
770 +    g_strfreev (default_layouts);
771 +    default_layouts = NULL;
772 +    g_strfreev (default_variants);
773 +    default_variants = NULL;
774 +    g_strfreev (default_options);
775 +    default_options = NULL;
776 +}
777 +
778 +gchar *
779 +ibus_xkb_get_current_layout (void)
780 +{
781 +    if (default_layouts == NULL) {
782 +        g_warning ("Your system seems not to support XKB.");
783 +        return NULL;
784 +    }
785 +
786 +    return g_strjoinv (",", (gchar **) default_layouts);
787 +}
788 +
789 +gchar *
790 +ibus_xkb_get_current_variant (void)
791 +{
792 +    if (default_variants == NULL) {
793 +        return NULL;
794 +    }
795 +
796 +    return g_strjoinv (",", (gchar **) default_variants);
797 +}
798 +
799 +gchar *
800 +ibus_xkb_get_current_option (void)
801 +{
802 +    if (default_options == NULL) {
803 +        return NULL;
804 +    }
805 +
806 +    return g_strjoinv (",", (gchar **) default_options);
807 +}
808 +
809 +gboolean
810 +ibus_xkb_set_layout  (const char *layouts,
811 +                      const char *variants,
812 +                      const char *options)
813 +{
814 +    Display *xdisplay;
815 +    gboolean retval;
816 +    gchar *layouts_line;
817 +
818 +    if (default_layouts == NULL) {
819 +        g_warning ("Your system seems not to support XKB.");
820 +        return FALSE;
821 +    }
822 +
823 +    if (layouts == NULL || g_strcmp0 (layouts, "default") == 0) {
824 +        layouts_line = g_strjoinv (",", (gchar **) default_layouts);
825 +    } else {
826 +        layouts_line = g_strdup (layouts);
827 +    }
828 +
829 +    xdisplay = get_xdisplay (NULL);
830 +    retval = set_xkb_rules (xdisplay,
831 +                            "evdev", "evdev",
832 +                            layouts_line, variants, options);
833 +    update_xkb_properties (xdisplay,
834 +                           "evdev", "evdev",
835 +                           layouts_line, variants, options);
836 +    g_free (layouts_line);
837 +
838 +    return retval;
839 +}
840 +
841 +int
842 +ibus_xkb_get_current_group (void)
843 +{
844 +    Display *xdisplay = get_xdisplay (NULL);
845 +    XkbStateRec state;
846 +
847 +    if (default_layouts == NULL) {
848 +        g_warning ("Your system seems not to support XKB.");
849 +        return 0;
850 +    }
851 +
852 +    if (xdisplay == NULL) {
853 +        g_warning ("ibus-xkb is not initialized.");
854 +        return 0;
855 +    }
856 +
857 +    if (XkbGetState (xdisplay, XkbUseCoreKbd, &state) != Success) {
858 +        g_warning ("Could not get state");
859 +        return 0;
860 +    }
861 +
862 +    return state.group;
863 +}
864 diff --git a/engine/ibus-xkb/xkblib.h b/engine/ibus-xkb/xkblib.h
865 new file mode 100644
866 index 0000000..36597c3
867 --- /dev/null
868 +++ b/engine/ibus-xkb/xkblib.h
869 @@ -0,0 +1,41 @@
870 +/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
871 +/* vim:set et sts=4: */
872 +/* bus - The Input Bus
873 + * Copyright (C) 2012 Takao Fujiwara <takao.fujiwara1@gmail.com>
874 + * Copyright (C) 2012 Peng Huang <shawn.p.huang@gmail.com>
875 + * Copyright (C) 2012 Red Hat, Inc.
876 + *
877 + * This library is free software; you can redistribute it and/or
878 + * modify it under the terms of the GNU Lesser General Public
879 + * License as published by the Free Software Foundation; either
880 + * version 2 of the License, or (at your option) any later version.
881 + *
882 + * This library is distributed in the hope that it will be useful,
883 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
884 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
885 + * Lesser General Public License for more details.
886 + *
887 + * You should have received a copy of the GNU Lesser General Public
888 + * License along with this library; if not, write to the
889 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
890 + * Boston, MA 02111-1307, USA.
891 + */
892 +#ifndef __XKBLIB_H_
893 +#define __XKBLIB_H_
894 +
895 +#include <X11/Xlib.h>
896 +
897 +G_BEGIN_DECLS
898 +
899 +void             ibus_xkb_init                   (Display *xdisplay);
900 +void             ibus_xkb_finit                  (void);
901 +gchar           *ibus_xkb_get_current_layout     (void);
902 +gchar           *ibus_xkb_get_current_variant    (void);
903 +gchar           *ibus_xkb_get_current_option     (void);
904 +gboolean         ibus_xkb_set_layout             (const char *layouts,
905 +                                                  const char *variants,
906 +                                                  const char *options);
907 +int              ibus_xkb_get_current_group      (void);
908 +
909 +G_END_DECLS
910 +#endif
911 diff --git a/engine/main.vala b/engine/main.vala
912 index acfa737..afadca0 100644
913 --- a/engine/main.vala
914 +++ b/engine/main.vala
915 @@ -21,6 +21,85 @@
916   */
917  
918  using IBus;
919 +using GLib;
920 +
921 +private void print_xml(string  layout,
922 +                       string  layout_desc,
923 +                       string? variant,
924 +                       string? variant_desc,
925 +                       string  lang) {
926 +    string name = "xkb:%s:%s:%s".printf(layout, variant ?? "", lang);
927 +    string keymap = layout;
928 +    string desc = layout_desc;
929 +    string symbol = lang;
930 +
931 +    if (variant != null) {
932 +        keymap = "%s(%s)".printf(layout, variant);
933 +    }
934 +
935 +    if (variant_desc != null) {
936 +        desc = variant_desc;
937 +    }
938 +
939 +    desc = desc.replace("<", "&lt;").replace(">", "&gt;");
940 +
941 +    if (lang.length > 2) {
942 +        symbol = lang[0:2];
943 +    }
944 +
945 +    string engine = "
946 +              <engine>
947 +                        <name>%s</name>
948 +                        <language>%s</language>
949 +                        <license>GPL</license>
950 +                        <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
951 +                        <layout>%s</layout>
952 +                        <longname>%s</longname>
953 +                        <description>%s</description>
954 +                        <icon>ibus-keyboard</icon>
955 +                        <symbol>%s</symbol>
956 +                        <rank>%d</rank>
957 +              </engine>".printf(name, lang, keymap, desc, desc, symbol, 99);
958 +    print (engine);
959 +}
960 +
961 +private void print_component() {
962 +    IBus.XKBConfigRegistry registry = new IBus.XKBConfigRegistry();
963 +    GLib.List layouts = registry.layout_list_get_layouts();
964 +    GLib.List variants;
965 +    GLib.List langs;
966 +    string layout_desc;
967 +    const string header = "<engines>";
968 +    const string footer = "
969 +</engines>";
970 +
971 +    print (header);
972 +    for (unowned GLib.List<string> l = layouts; l != null; l = l.next) {
973 +        variants = registry.layout_list_get_variants(l.data);
974 +        langs = registry.layout_lang_get_langs(l.data);
975 +        layout_desc = registry.layout_desc_get_desc(l.data);
976 +        for (unowned GLib.List<string> lg = langs; lg != null; lg = lg.next) {
977 +            print_xml(l.data, layout_desc, null, null, lg.data);
978 +        }
979 +        for (unowned GLib.List<string> v = variants; v != null; v = v.next) {
980 +            var l_v = "%s(%s)".printf(l.data, v.data);
981 +            unowned GLib.List<string> l_v_langs = null;
982 +            GLib.List<string> _l_v_langs = registry.layout_lang_get_langs(l_v);
983 +            l_v_langs = _l_v_langs;
984 +            if (l_v_langs == null) {
985 +                l_v_langs = langs;
986 +            }
987 +            for (unowned GLib.List<string> lg = l_v_langs; lg != null; lg = lg.next) {
988 +                print_xml(l.data,
989 +                          layout_desc,
990 +                          v.data,
991 +                          registry.variant_desc_get_desc(l_v),
992 +                          lg.data);
993 +            }
994 +        }
995 +    }
996 +    print (footer);
997 +}
998  
999  class DummyEngine : IBus.EngineSimple {
1000  }
1001 @@ -28,6 +107,13 @@ class DummyEngine : IBus.EngineSimple {
1002  public int main(string[] args) {
1003      IBus.init();
1004  
1005 +    if (args.length >= 2) {
1006 +        if (args[1] == "--xml" || args[1] == "-x") {
1007 +            print_component();
1008 +            return 0;
1009 +        }
1010 +    }
1011 +
1012      IBus.Bus bus = new IBus.Bus();
1013      if (!bus.is_connected()) {
1014          warning("ibus-daemon does not exist.");
1015 diff --git a/engine/simple.xml.in.in b/engine/simple.xml.in.in
1016 index 25db578..7de949a 100644
1017 --- a/engine/simple.xml.in.in
1018 +++ b/engine/simple.xml.in.in
1019 @@ -7,534 +7,5 @@
1020         <license>GPL</license>
1021         <homepage>http://code.google.com/p/ibus</homepage>
1022         <textdomain>ibus</textdomain>
1023 -       <engines>
1024 -               <engine>
1025 -                       <name>xkb:us::eng</name>
1026 -                       <language>eng</language>
1027 -                       <license>GPL</license>
1028 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1029 -                       <layout>us</layout>
1030 -                       <longname>English (US)</longname>
1031 -                       <description>English (US)</description>
1032 -                        <icon>ibus-keyboard</icon>
1033 -                       <rank>99</rank>
1034 -               </engine>
1035 -               <engine>
1036 -                       <name>xkb:us:intl:eng</name>
1037 -                       <language>eng</language>
1038 -                       <license>GPL</license>
1039 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1040 -                       <layout>us(intl)</layout>
1041 -                       <longname>English (US, international with dead keys)</longname>
1042 -                       <description>English (US, international with dead keys)</description>
1043 -                        <icon>ibus-keyboard</icon>
1044 -                       <rank>99</rank>
1045 -               </engine>
1046 -               <engine>
1047 -                       <name>xkb:us:colemak:eng</name>
1048 -                       <language>eng</language>
1049 -                       <license>GPL</license>
1050 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1051 -                       <layout>us(colemak)</layout>
1052 -                       <longname>English (Colemak)</longname>
1053 -                       <description>English (Colemak)</description>
1054 -                        <icon>ibus-keyboard</icon>
1055 -                       <rank>99</rank>
1056 -               </engine>
1057 -               <engine>
1058 -                       <name>xkb:us:dvorak:eng</name>
1059 -                       <language>eng</language>
1060 -                       <license>GPL</license>
1061 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1062 -                       <layout>us(dvorak)</layout>
1063 -                       <longname>English (Dvorak)</longname>
1064 -                       <description>English (Dvorak)</description>
1065 -                        <icon>ibus-keyboard</icon>
1066 -                       <rank>99</rank>
1067 -               </engine>
1068 -               <engine>
1069 -                       <name>xkb:us:altgr-intl:eng</name>
1070 -                       <language>eng</language>
1071 -                       <license>GPL</license>
1072 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1073 -                       <layout>us(altgr-intl)</layout>
1074 -                       <longname>English (international AltGr dead keys)</longname>
1075 -                       <description>English (international AltGr dead keys)</description>
1076 -                        <icon>ibus-keyboard</icon>
1077 -                       <rank>99</rank>
1078 -               </engine>
1079 -               <engine>
1080 -                       <name>xkb:us:altgr-intl:eng</name>
1081 -                       <language>eng</language>
1082 -                       <license>GPL</license>
1083 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1084 -                       <layout>us(altgr-intl)</layout>
1085 -                       <longname>English (international AltGr dead keys)</longname>
1086 -                       <description>English (international AltGr dead keys)</description>
1087 -                        <icon>ibus-keyboard</icon>
1088 -                       <rank>99</rank>
1089 -               </engine>
1090 -               <engine>
1091 -                       <name>xkb:be::ger</name>
1092 -                       <language>ger</language>
1093 -                       <license>GPL</license>
1094 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1095 -                       <layout>be</layout>
1096 -                       <longname>Belgian</longname>
1097 -                       <description>Belgian</description>
1098 -                        <icon>ibus-keyboard</icon>
1099 -                       <rank>99</rank>
1100 -               </engine>
1101 -               <engine>
1102 -                       <name>xkb:be::nld</name>
1103 -                       <language>nld</language>
1104 -                       <license>GPL</license>
1105 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1106 -                       <layout>be</layout>
1107 -                       <longname>Belgian</longname>
1108 -                       <description>Belgian</description>
1109 -                        <icon>ibus-keyboard</icon>
1110 -                       <rank>99</rank>
1111 -               </engine>
1112 -               <engine>
1113 -                       <name>xkb:be::fra</name>
1114 -                       <language>fra</language>
1115 -                       <license>GPL</license>
1116 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1117 -                       <layout>be</layout>
1118 -                       <longname>Belgian</longname>
1119 -                       <description>Belgian</description>
1120 -                        <icon>ibus-keyboard</icon>
1121 -                       <rank>99</rank>
1122 -               </engine>
1123 -               <engine>
1124 -                       <name>xkb:br::por</name>
1125 -                       <language>por</language>
1126 -                       <license>GPL</license>
1127 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1128 -                       <layout>br</layout>
1129 -                       <longname>Portuguese (Brazil)</longname>
1130 -                       <description>Portuguese (Brazil)</description>
1131 -                        <icon>ibus-keyboard</icon>
1132 -                       <rank>99</rank>
1133 -               </engine>
1134 -               <engine>
1135 -                       <name>xkb:bg::bul</name>
1136 -                       <language>bul</language>
1137 -                       <license>GPL</license>
1138 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1139 -                       <layout>bg</layout>
1140 -                       <longname>Bulgarian</longname>
1141 -                       <description>Bulgarian</description>
1142 -                        <icon>ibus-keyboard</icon>
1143 -                       <rank>99</rank>
1144 -               </engine>
1145 -               <engine>
1146 -                       <name>xkb:bg:phonetic:bul</name>
1147 -                       <language>bul</language>
1148 -                       <license>GPL</license>
1149 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1150 -                       <layout>bg(phonetic)</layout>
1151 -                       <longname>Bulgarian (traditional phonetic)</longname>
1152 -                       <description>Bulgarian (traditional phonetic)</description>
1153 -                        <icon>ibus-keyboard</icon>
1154 -                       <rank>99</rank>
1155 -               </engine>
1156 -               <engine>
1157 -                       <name>xkb:ca::fra</name>
1158 -                       <language>fra</language>
1159 -                       <license>GPL</license>
1160 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1161 -                       <layout>ca</layout>
1162 -                       <longname>French (Canada)</longname>
1163 -                       <description>French (Canada)</description>
1164 -                        <icon>ibus-keyboard</icon>
1165 -                       <rank>99</rank>
1166 -               </engine>
1167 -               <engine>
1168 -                       <name>xkb:ca:eng:eng</name>
1169 -                       <language>eng</language>
1170 -                       <license>GPL</license>
1171 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1172 -                       <layout>ca(eng)</layout>
1173 -                       <longname>English (Canada)</longname>
1174 -                       <description>English (Canada)</description>
1175 -                        <icon>ibus-keyboard</icon>
1176 -                       <rank>99</rank>
1177 -               </engine>
1178 -               <engine>
1179 -                       <name>xkb:hr::scr</name>
1180 -                       <language>scr</language>
1181 -                       <license>GPL</license>
1182 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1183 -                       <layout>hr</layout>
1184 -                       <longname>Croatian</longname>
1185 -                       <description>Croatian</description>
1186 -                        <icon>ibus-keyboard</icon>
1187 -                       <rank>99</rank>
1188 -               </engine>
1189 -               <engine>
1190 -                       <name>xkb:cz::cze</name>
1191 -                       <language>cze</language>
1192 -                       <license>GPL</license>
1193 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1194 -                       <layout>cz</layout>
1195 -                       <longname>Czech</longname>
1196 -                       <description>Czech</description>
1197 -                        <icon>ibus-keyboard</icon>
1198 -                       <rank>99</rank>
1199 -               </engine>
1200 -               <engine>
1201 -                       <name>xkb:dk::dan</name>
1202 -                       <language>dan</language>
1203 -                       <license>GPL</license>
1204 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1205 -                       <layout>dk</layout>
1206 -                       <longname>Danish</longname>
1207 -                       <description>Danish</description>
1208 -                        <icon>ibus-keyboard</icon>
1209 -                       <rank>99</rank>
1210 -               </engine>
1211 -               <engine>
1212 -                       <name>xkb:ee::est</name>
1213 -                       <language>est</language>
1214 -                       <license>GPL</license>
1215 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1216 -                       <layout>ee</layout>
1217 -                       <longname>Estonian</longname>
1218 -                       <description>Estonian</description>
1219 -                        <icon>ibus-keyboard</icon>
1220 -                       <rank>99</rank>
1221 -               </engine>
1222 -               <engine>
1223 -                       <name>xkb:fi::fin</name>
1224 -                       <language>fin</language>
1225 -                       <license>GPL</license>
1226 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1227 -                       <layout>fi</layout>
1228 -                       <longname>Finnish</longname>
1229 -                       <description>Finnish</description>
1230 -                        <icon>ibus-keyboard</icon>
1231 -                       <rank>99</rank>
1232 -               </engine>
1233 -               <engine>
1234 -                       <name>xkb:fr::fra</name>
1235 -                       <language>fra</language>
1236 -                       <license>GPL</license>
1237 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1238 -                       <layout>fr</layout>
1239 -                       <longname>French</longname>
1240 -                       <description>French</description>
1241 -                        <icon>ibus-keyboard</icon>
1242 -                       <rank>99</rank>
1243 -               </engine>
1244 -               <engine>
1245 -                       <name>xkb:de::ger</name>
1246 -                       <language>ger</language>
1247 -                       <license>GPL</license>
1248 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1249 -                       <layout>de</layout>
1250 -                       <longname>German</longname>
1251 -                       <description>German</description>
1252 -                        <icon>ibus-keyboard</icon>
1253 -                       <rank>99</rank>
1254 -               </engine>
1255 -               <engine>
1256 -                       <name>xkb:de:neo:ger</name>
1257 -                       <language>ger</language>
1258 -                       <license>GPL</license>
1259 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1260 -                       <layout>de(neo)</layout>
1261 -                       <longname>German (Neo 2)</longname>
1262 -                       <description>German (Neo 2)</description>
1263 -                        <icon>ibus-keyboard</icon>
1264 -                       <rank>99</rank>
1265 -               </engine>
1266 -               <engine>
1267 -                       <name>xkb:gr::gre</name>
1268 -                       <language>gre</language>
1269 -                       <license>GPL</license>
1270 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1271 -                       <layout>gr</layout>
1272 -                       <longname>Greek</longname>
1273 -                       <description>Greek</description>
1274 -                        <icon>ibus-keyboard</icon>
1275 -                       <rank>99</rank>
1276 -               </engine>
1277 -               <engine>
1278 -                       <name>xkb:hu::hun</name>
1279 -                       <language>hun</language>
1280 -                       <license>GPL</license>
1281 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1282 -                       <layout>hu</layout>
1283 -                       <longname>Hungarian</longname>
1284 -                       <description>Hungarian</description>
1285 -                        <icon>ibus-keyboard</icon>
1286 -                       <rank>99</rank>
1287 -               </engine>
1288 -               <engine>
1289 -                       <name>xkb:il::heb</name>
1290 -                       <language>heb</language>
1291 -                       <license>GPL</license>
1292 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1293 -                       <layout>il</layout>
1294 -                       <longname>Hebrew</longname>
1295 -                       <description>Hebrew</description>
1296 -                        <icon>ibus-keyboard</icon>
1297 -                       <rank>99</rank>
1298 -               </engine>
1299 -               <engine>
1300 -                       <name>xkb:it::ita</name>
1301 -                       <language>ita</language>
1302 -                       <license>GPL</license>
1303 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1304 -                       <layout>it</layout>
1305 -                       <longname>Italian</longname>
1306 -                       <description>Italian</description>
1307 -                        <icon>ibus-keyboard</icon>
1308 -                       <rank>99</rank>
1309 -               </engine>
1310 -               <engine>
1311 -                       <name>xkb:jp::jpn</name>
1312 -                       <language>jpn</language>
1313 -                       <license>GPL</license>
1314 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1315 -                       <layout>jp</layout>
1316 -                       <longname>Japanese</longname>
1317 -                       <description>Japanese</description>
1318 -                        <icon>ibus-keyboard</icon>
1319 -                       <rank>99</rank>
1320 -               </engine>
1321 -               <engine>
1322 -                       <name>xkb:latam::spa</name>
1323 -                       <language>spa</language>
1324 -                       <license>GPL</license>
1325 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1326 -                       <layout>latam</layout>
1327 -                       <longname>Spanish (Latin American)</longname>
1328 -                       <description>Spanish (Latin American)</description>
1329 -                        <icon>ibus-keyboard</icon>
1330 -                       <rank>99</rank>
1331 -               </engine>
1332 -               <engine>
1333 -                       <name>xkb:lt::lit</name>
1334 -                       <language>lit</language>
1335 -                       <license>GPL</license>
1336 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1337 -                       <layout>lt</layout>
1338 -                       <longname>Lithuanian</longname>
1339 -                       <description>Lithuanian</description>
1340 -                        <icon>ibus-keyboard</icon>
1341 -                       <rank>99</rank>
1342 -               </engine>
1343 -               <engine>
1344 -                       <name>xkb:lv:apostrophe:lav</name>
1345 -                       <language>lav</language>
1346 -                       <license>GPL</license>
1347 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1348 -                       <layout>lv(apostrophe)</layout>
1349 -                       <longname>Latvian (apostrophe variant)</longname>
1350 -                       <description>Latvian (apostrophe variant)</description>
1351 -                        <icon>ibus-keyboard</icon>
1352 -                       <rank>99</rank>
1353 -               </engine>
1354 -               <engine>
1355 -                       <name>xkb:pl::pol</name>
1356 -                       <language>pol</language>
1357 -                       <license>GPL</license>
1358 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1359 -                       <layout>pl</layout>
1360 -                       <longname>Polish</longname>
1361 -                       <description>Polish</description>
1362 -                        <icon>ibus-keyboard</icon>
1363 -                       <rank>99</rank>
1364 -               </engine>
1365 -               <engine>
1366 -                       <name>xkb:pt::por</name>
1367 -                       <language>por</language>
1368 -                       <license>GPL</license>
1369 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1370 -                       <layout>pt</layout>
1371 -                       <longname>Portuguese</longname>
1372 -                       <description>Portuguese</description>
1373 -                        <icon>ibus-keyboard</icon>
1374 -                       <rank>99</rank>
1375 -               </engine>
1376 -               <engine>
1377 -                       <name>xkb:ro::rum</name>
1378 -                       <language>rum</language>
1379 -                       <license>GPL</license>
1380 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1381 -                       <layout>ro</layout>
1382 -                       <longname>Romanian</longname>
1383 -                       <description>Romanian</description>
1384 -                        <icon>ibus-keyboard</icon>
1385 -                       <rank>99</rank>
1386 -               </engine>
1387 -               <engine>
1388 -                       <name>xkb:ru::rus</name>
1389 -                       <language>rus</language>
1390 -                       <license>GPL</license>
1391 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1392 -                       <layout>ru</layout>
1393 -                       <longname>Russian</longname>
1394 -                       <description>Russian</description>
1395 -                        <icon>ibus-keyboard</icon>
1396 -                       <rank>99</rank>
1397 -               </engine>
1398 -               <engine>
1399 -                       <name>xkb:ru:phonetic:rus</name>
1400 -                       <language>rus</language>
1401 -                       <license>GPL</license>
1402 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1403 -                       <layout>ru(phonetic)</layout>
1404 -                       <longname>Russian (phonetic)</longname>
1405 -                       <description>Russian (phonetic)</description>
1406 -                        <icon>ibus-keyboard</icon>
1407 -                       <rank>99</rank>
1408 -               </engine>
1409 -               <engine>
1410 -                       <name>xkb:rs::srp</name>
1411 -                       <language>srp</language>
1412 -                       <license>GPL</license>
1413 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1414 -                       <layout>rs</layout>
1415 -                       <longname>Serbian</longname>
1416 -                       <description>Serbian</description>
1417 -                        <icon>ibus-keyboard</icon>
1418 -                       <rank>99</rank>
1419 -               </engine>
1420 -               <engine>
1421 -                       <name>xkb:si::slv</name>
1422 -                       <language>slv</language>
1423 -                       <license>GPL</license>
1424 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1425 -                       <layout>si</layout>
1426 -                       <longname>Slovenian</longname>
1427 -                       <description>Slovenian</description>
1428 -                        <icon>ibus-keyboard</icon>
1429 -                       <rank>99</rank>
1430 -               </engine>
1431 -               <engine>
1432 -                       <name>xkb:sk::slo</name>
1433 -                       <language>slo</language>
1434 -                       <license>GPL</license>
1435 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1436 -                       <layout>sk</layout>
1437 -                       <longname>Slovak</longname>
1438 -                       <description>Slovak</description>
1439 -                        <icon>ibus-keyboard</icon>
1440 -                       <rank>99</rank>
1441 -               </engine>
1442 -               <engine>
1443 -                       <name>xkb:es::spa</name>
1444 -                       <language>spa</language>
1445 -                       <license>GPL</license>
1446 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1447 -                       <layout>es</layout>
1448 -                       <longname>Spanish</longname>
1449 -                       <description>Spanish</description>
1450 -                        <icon>ibus-keyboard</icon>
1451 -                       <rank>99</rank>
1452 -               </engine>
1453 -               <engine>
1454 -                       <name>xkb:es:cat:cat</name>
1455 -                       <language>cat</language>
1456 -                       <license>GPL</license>
1457 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1458 -                       <layout>es(cat)</layout>
1459 -                       <longname>Catalan (Spain, with middle-dot L)</longname>
1460 -                       <description>Catalan (Spain, with middle-dot L)</description>
1461 -                        <icon>ibus-keyboard</icon>
1462 -                       <rank>99</rank>
1463 -               </engine>
1464 -               <engine>
1465 -                       <name>xkb:se::swe</name>
1466 -                       <language>swe</language>
1467 -                       <license>GPL</license>
1468 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1469 -                       <layout>se</layout>
1470 -                       <longname>Swedish</longname>
1471 -                       <description>Swedish</description>
1472 -                        <icon>ibus-keyboard</icon>
1473 -                       <rank>99</rank>
1474 -               </engine>
1475 -               <engine>
1476 -                       <name>xkb:ch::ger</name>
1477 -                       <language>ger</language>
1478 -                       <license>GPL</license>
1479 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1480 -                       <layout>ch</layout>
1481 -                       <longname>German (Switzerland)</longname>
1482 -                       <description>German (Switzerland)</description>
1483 -                        <icon>ibus-keyboard</icon>
1484 -                       <rank>99</rank>
1485 -               </engine>
1486 -               <engine>
1487 -                       <name>xkb:ch:fr:fra</name>
1488 -                       <language>fra</language>
1489 -                       <license>GPL</license>
1490 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1491 -                       <layout>ch(fr)</layout>
1492 -                       <longname>French (Switzerland)</longname>
1493 -                       <description>French (Switzerland)</description>
1494 -                        <icon>ibus-keyboard</icon>
1495 -                       <rank>99</rank>
1496 -               </engine>
1497 -               <engine>
1498 -                       <name>xkb:tr::tur</name>
1499 -                       <language>tur</language>
1500 -                       <license>GPL</license>
1501 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1502 -                       <layout>tr</layout>
1503 -                       <longname>Turkish</longname>
1504 -                       <description>Turkish</description>
1505 -                        <icon>ibus-keyboard</icon>
1506 -                       <rank>99</rank>
1507 -               </engine>
1508 -               <engine>
1509 -                       <name>xkb:ua::ukr</name>
1510 -                       <language>ukr</language>
1511 -                       <license>GPL</license>
1512 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1513 -                       <layout>ua</layout>
1514 -                       <longname>Ukrainian</longname>
1515 -                       <description>Ukrainian</description>
1516 -                        <icon>ibus-keyboard</icon>
1517 -                       <rank>99</rank>
1518 -               </engine>
1519 -               <engine>
1520 -                       <name>xkb:gb:extd:eng</name>
1521 -                       <language>eng</language>
1522 -                       <license>GPL</license>
1523 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1524 -                       <layout>gb(extd)</layout>
1525 -                       <longname>English (UK, extended WinKeys)</longname>
1526 -                       <description>English (UK, extended WinKeys)</description>
1527 -                        <icon>ibus-keyboard</icon>
1528 -                       <rank>99</rank>
1529 -               </engine>
1530 -               <engine>
1531 -                       <name>xkb:gb:dvorak:eng</name>
1532 -                       <language>eng</language>
1533 -                       <license>GPL</license>
1534 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1535 -                       <layout>gb(dvorak)</layout>
1536 -                       <longname>English (UK, Dvorak)</longname>
1537 -                       <description>English (UK, Dvorak)</description>
1538 -                        <icon>ibus-keyboard</icon>
1539 -                       <rank>99</rank>
1540 -               </engine>
1541 -               <engine>
1542 -                       <name>xkb:kr:kr104:kor</name>
1543 -                       <language>kor</language>
1544 -                       <license>GPL</license>
1545 -                       <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
1546 -                       <layout>kr(kr104)</layout>
1547 -                       <longname>Korean (101/104 key compatible)</longname>
1548 -                       <description>Korean (101/104 key compatible)</description>
1549 -                        <icon>ibus-keyboard</icon>
1550 -                       <rank>99</rank>
1551 -               </engine>
1552 -       </engines>
1553 +       <engines exec=\"${libexecdir}/ibus-engine-simple --xml\" />
1554  </component>
1555 diff --git a/ibus-1.0.pc.in b/ibus-1.0.pc.in
1556 index 9f593ab..66b902a 100644
1557 --- a/ibus-1.0.pc.in
1558 +++ b/ibus-1.0.pc.in
1559 @@ -1,9 +1,13 @@
1560  prefix=@prefix@
1561  exec_prefix=@exec_prefix@
1562  libdir=@libdir@
1563 +libexecdir=@libexecdir@
1564  includedir=@includedir@
1565  datadir=@datadir@
1566  pkgdatadir=@datadir@/ibus
1567 +have_ibus_xkb=@HAVE_IBUS_XKB@
1568 +ibus_xkb=${libexecdir}/ibus-xkb
1569 +have_ibus_gkbd=@HAVE_IBUS_GKBD@
1570  
1571  Name: IBus
1572  Description: IBus Library
1573 diff --git a/ibus.spec.in b/ibus.spec.in
1574 index 58cac38..4b6f869 100644
1575 --- a/ibus.spec.in
1576 +++ b/ibus.spec.in
1577 @@ -4,6 +4,7 @@
1578  
1579  # Build flags
1580  %define build_python_library 0
1581 +%define build_xkb 0
1582  
1583  %define glib_ver %([ -a %{_libdir}/pkgconfig/glib-2.0.pc ] && pkg-config --modversion glib-2.0 | cut -d. -f 1,2 || echo -n "999")
1584  %define gconf2_version 2.12.0
1585 @@ -38,6 +39,10 @@ BuildRequires:  GConf2-devel
1586  BuildRequires:  pygobject2-devel
1587  BuildRequires:  intltool
1588  BuildRequires:  iso-codes-devel
1589 +%if %{build_xkb}
1590 +BuildRequires:  libxkbfile-devel
1591 +BuildRequires:  libgnomekbd-devel
1592 +%endif
1593  
1594  Requires:   %{name}-libs = %{version}-%{release}
1595  Requires:   %{name}-gtk2 = %{version}-%{release}
1596 @@ -51,6 +56,9 @@ Requires:   im-chooser >= %{im_chooser_version}
1597  Requires:   GConf2 >= %{gconf2_version}
1598  Requires:   notify-python
1599  Requires:   librsvg2
1600 +%if %{build_xkb}
1601 +Requires:   libgnomekbd
1602 +%endif
1603  
1604  Requires(post):  desktop-file-utils
1605  Requires(postun):  desktop-file-utils
1606 @@ -132,6 +140,10 @@ OPTIONS="$OPTIONS --enable-python-library"
1607  OPTIONS="$OPTIONS --disable-python-library"
1608  %endif
1609  
1610 +%if %{build_xkb}
1611 +OPTIONS="$OPTIONS --enable-xkb --enable-libgnomekbd"
1612 +%endif
1613 +
1614  %configure $OPTIONS
1615  
1616  # make -C po update-gmo
1617 diff --git a/setup/main.py b/setup/main.py
1618 index 8a2391d..2a19c49 100644
1619 --- a/setup/main.py
1620 +++ b/setup/main.py
1621 @@ -67,6 +67,13 @@ class Setup(object):
1622      def __init__(self):
1623          super(Setup, self).__init__()
1624  
1625 +        # In the latest pygobject3 3.3.4 or later, g_variant_dup_strv
1626 +        # returns the allocated strv but in the previous release,
1627 +        # it returned the tuple of (strv, length)
1628 +        self.__tuple_for_variant_strv = False
1629 +        if type(GLib.Variant.new_strv([]).dup_strv()) == tuple:
1630 +            self.__tuple_for_variant_strv = True
1631 +
1632          # IBus.Bus() calls ibus_bus_new().
1633          # Gtk.Builder().add_from_file() also calls ibus_bus_new_async()
1634          # via ibus_im_context_new().
1635 @@ -191,14 +198,22 @@ class Setup(object):
1636  
1637          # init engine page
1638          self.__engines = self.__bus.list_engines()
1639 +        value = self.__config.get_value("general", "load_xkb_layouts")
1640 +        load_layouts = []
1641 +        if value != None:
1642 +            load_layouts = self.__variant_dup_strv(value)
1643 +        engines = []
1644 +        for engine in self.__engines:
1645 +            if not engine.get_name().startswith('xkb:'):
1646 +                engines.append(engine)
1647 +            elif engine.get_layout() in load_layouts:
1648 +                engines.append(engine)
1649 +
1650          self.__combobox = self.__builder.get_object("combobox_engines")
1651 -        self.__combobox.set_engines(self.__engines)
1652 +        self.__combobox.set_engines(engines)
1653  
1654 -        tmp_dict = {}
1655 -        for e in self.__engines:
1656 -            tmp_dict[e.get_name()] = e
1657          engine_names = values.get("preload_engines", [])
1658 -        engines = [tmp_dict[name] for name in engine_names if name in tmp_dict]
1659 +        engines = self.__get_engine_descs_from_names(engine_names)
1660  
1661          self.__treeview = self.__builder.get_object("treeview_engines")
1662          self.__treeview.set_engines(engines)
1663 @@ -247,6 +262,12 @@ class Setup(object):
1664          self.__init_panel()
1665          self.__init_general()
1666  
1667 +    def __variant_dup_strv(self, variant):
1668 +        if self.__tuple_for_variant_strv:
1669 +            return variant.dup_strv()[0]
1670 +        else:
1671 +            return variant.dup_strv()
1672 +
1673      def __combobox_notify_active_engine_cb(self, combobox, property):
1674          engine = self.__combobox.get_active_engine()
1675          button = self.__builder.get_object("button_engine_add")
1676 @@ -271,6 +292,13 @@ class Setup(object):
1677              args.append(path.basename(setup_path))
1678          return args
1679  
1680 +    def __get_engine_descs_from_names(self, engine_names):
1681 +        tmp_dict = {}
1682 +        for e in self.__engines:
1683 +            tmp_dict[e.get_name()] = e
1684 +        engines = [tmp_dict[name] for name in engine_names if name in tmp_dict]
1685 +        return engines
1686 +
1687      def __treeview_notify_cb(self, treeview, prop):
1688          if prop.name not in ("active-engine", "engines"):
1689              return
1690 diff --git a/src/Makefile.am b/src/Makefile.am
1691 index df4ada3..e249ada 100644
1692 --- a/src/Makefile.am
1693 +++ b/src/Makefile.am
1694 @@ -194,6 +194,11 @@ typelibs_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
1695  CLEANFILES += $(dist_gir_DATA) $(typelibs_DATA)
1696  endif
1697  
1698 +if ENABLE_XKB
1699 +ibus_sources += ibusxkbxml.c
1700 +ibus_headers += ibusxkbxml.h
1701 +endif
1702 +
1703  # gen enum types
1704  ibusenumtypes.h: $(ibus_headers) ibusenumtypes.h.template
1705         $(AM_V_GEN) ( top_builddir=`cd $(top_builddir) && pwd`; \
1706 diff --git a/src/ibus.h b/src/ibus.h
1707 index ef811a4..f82a162 100644
1708 --- a/src/ibus.h
1709 +++ b/src/ibus.h
1710 @@ -47,6 +47,7 @@
1711  #include <ibuskeys.h>
1712  #include <ibusenumtypes.h>
1713  #include <ibushotkey.h>
1714 +#include <ibusxkbxml.h>
1715  #include <ibusxml.h>
1716  #include <ibusenginedesc.h>
1717  #include <ibusobservedpath.h>
1718 diff --git a/src/ibusxkbxml.c b/src/ibusxkbxml.c
1719 new file mode 100644
1720 index 0000000..4792664
1721 --- /dev/null
1722 +++ b/src/ibusxkbxml.c
1723 @@ -0,0 +1,466 @@
1724 +/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
1725 +/* vim:set et sts=4: */
1726 +/* bus - The Input Bus
1727 + * Copyright (C) 2012 Takao Fujiwara <takao.fujiwara1@gmail.com>
1728 + * Copyright (C) 2012 Peng Huang <shawn.p.huang@gmail.com>
1729 + * Copyright (C) 2012 Red Hat, Inc.
1730 + *
1731 + * This library is free software; you can redistribute it and/or
1732 + * modify it under the terms of the GNU Lesser General Public
1733 + * License as published by the Free Software Foundation; either
1734 + * version 2 of the License, or (at your option) any later version.
1735 + *
1736 + * This library is distributed in the hope that it will be useful,
1737 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1738 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
1739 + * Lesser General Public License for more details.
1740 + *
1741 + * You should have received a copy of the GNU Lesser General Public
1742 + * License along with this library; if not, write to the
1743 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1744 + * Boston, MA 02111-1307, USA.
1745 + */
1746 +#ifdef HAVE_CONFIG_H
1747 +#include <config.h>
1748 +#endif
1749 +
1750 +#include <glib.h>
1751 +
1752 +#include "ibus.h"
1753 +#include "ibusxkbxml.h"
1754 +
1755 +#ifndef XKB_RULES_XML_FILE
1756 +#define XKB_RULES_XML_FILE "/usr/share/X11/xkb/rules/evdev.xml"
1757 +#endif
1758 +
1759 +#define IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE(o)  \
1760 +   (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistryPrivate))
1761 +
1762 +typedef struct _IBusXKBConfigRegistryPrivate IBusXKBConfigRegistryPrivate;
1763 +
1764 +struct _IBusXKBConfigRegistryPrivate {
1765 +    GHashTable *layout_list;
1766 +    GHashTable *layout_lang;
1767 +    GHashTable *layout_desc;
1768 +    GHashTable *variant_desc;
1769 +};
1770 +
1771 +
1772 +/* functions prototype */
1773 +static void         ibus_xkb_config_registry_destroy
1774 +                                           (IBusXKBConfigRegistry *xkb_config);
1775 +
1776 +G_DEFINE_TYPE (IBusXKBConfigRegistry, ibus_xkb_config_registry, IBUS_TYPE_OBJECT)
1777 +
1778 +static void
1779 +parse_xkb_xml_languagelist_node (IBusXKBConfigRegistryPrivate *priv,
1780 +                                 XMLNode *parent_node,
1781 +                                 const gchar *layout_name)
1782 +{
1783 +    XMLNode *node = parent_node;
1784 +    XMLNode *sub_node;
1785 +    GList *p;
1786 +    GList *lang_list = NULL;
1787 +
1788 +    g_assert (node != NULL);
1789 +    g_assert (layout_name != NULL);
1790 +    for (p = node->sub_nodes; p; p = p->next) {
1791 +        sub_node = (XMLNode *) p->data;
1792 +        if (g_strcmp0 (sub_node->name, "iso639Id") == 0) {
1793 +            lang_list = g_list_append (lang_list,
1794 +                                       (gpointer) g_strdup (sub_node->text));
1795 +            continue;
1796 +        }
1797 +    }
1798 +    if (lang_list == NULL) {
1799 +        /* some nodes have no lang */
1800 +        return;
1801 +    }
1802 +    if (g_hash_table_lookup (priv->layout_lang, layout_name) != NULL) {
1803 +        g_warning ("duplicated name %s exists", layout_name);
1804 +        return;
1805 +    }
1806 +    g_hash_table_insert (priv->layout_lang,
1807 +                         (gpointer) g_strdup (layout_name),
1808 +                         (gpointer) lang_list);
1809 +}
1810 +
1811 +static const gchar *
1812 +parse_xkb_xml_configitem_node (IBusXKBConfigRegistryPrivate *priv,
1813 +                               XMLNode *parent_node)
1814 +{
1815 +    XMLNode *node = parent_node;
1816 +    XMLNode *sub_node;
1817 +    GList *p;
1818 +    gchar *name = NULL;
1819 +    gchar *description = NULL;
1820 +
1821 +    g_assert (node != NULL);
1822 +    for (p = node->sub_nodes; p; p = p->next) {
1823 +        sub_node = (XMLNode *) p->data;
1824 +        if (g_strcmp0 (sub_node->name, "name") == 0) {
1825 +            name = sub_node->text;
1826 +            continue;
1827 +        }
1828 +        if (g_strcmp0 (sub_node->name, "description") == 0) {
1829 +            description = sub_node->text;
1830 +            continue;
1831 +        }
1832 +        if (g_strcmp0 (sub_node->name, "languageList") == 0) {
1833 +            if (name == NULL) {
1834 +                g_warning ("layout name is NULL in node %s", node->name);
1835 +                continue;
1836 +            }
1837 +            parse_xkb_xml_languagelist_node (priv, sub_node, name);
1838 +            continue;
1839 +        }
1840 +    }
1841 +    if (name == NULL) {
1842 +        g_warning ("No name in layout node");
1843 +        return NULL;
1844 +    }
1845 +    if (g_hash_table_lookup (priv->layout_desc, name) != NULL) {
1846 +        g_warning ("duplicated name %s exists", name);
1847 +        return name;
1848 +    }
1849 +    g_hash_table_insert (priv->layout_desc,
1850 +                         (gpointer) g_strdup (name),
1851 +                         (gpointer) g_strdup (description));
1852 +
1853 +    return name;
1854 +}
1855 +
1856 +static const gchar *
1857 +parse_xkb_xml_variant_configitem_node (IBusXKBConfigRegistryPrivate *priv,
1858 +                            XMLNode *parent_node,
1859 +                            const gchar *layout_name)
1860 +{
1861 +    XMLNode *node = parent_node;
1862 +    XMLNode *sub_node;
1863 +    GList *p;
1864 +    gchar *name = NULL;
1865 +    gchar *description = NULL;
1866 +    gchar *variant_lang_name = NULL;
1867 +
1868 +    g_assert (node != NULL);
1869 +    g_assert (layout_name != NULL);
1870 +    for (p = node->sub_nodes; p; p = p->next) {
1871 +        sub_node = (XMLNode *) p->data;
1872 +        if (g_strcmp0 (sub_node->name, "name") == 0) {
1873 +            name = sub_node->text;
1874 +            continue;
1875 +        }
1876 +        if (g_strcmp0 (sub_node->name, "description") == 0) {
1877 +            description = sub_node->text;
1878 +            continue;
1879 +        }
1880 +        if (g_strcmp0 (sub_node->name, "languageList") == 0) {
1881 +            if (name == NULL) {
1882 +                g_warning ("layout name is NULL in node %s", node->name);
1883 +                continue;
1884 +            }
1885 +            variant_lang_name = g_strdup_printf ("%s(%s)", layout_name, name);
1886 +            parse_xkb_xml_languagelist_node (priv, sub_node, variant_lang_name);
1887 +            g_free (variant_lang_name);
1888 +            continue;
1889 +        }
1890 +    }
1891 +    if (name == NULL) {
1892 +        g_warning ("No name in layout node");
1893 +        return NULL;
1894 +    }
1895 +    if (g_hash_table_lookup (priv->variant_desc, name) != NULL) {
1896 +        /* This is an expected case. */
1897 +        return name;
1898 +    }
1899 +    variant_lang_name = g_strdup_printf ("%s(%s)", layout_name, name);
1900 +    g_hash_table_insert (priv->variant_desc,
1901 +                         (gpointer) variant_lang_name,
1902 +                         (gpointer) g_strdup (description));
1903 +    return name;
1904 +}
1905 +
1906 +static const gchar *
1907 +parse_xkb_xml_variant_node (IBusXKBConfigRegistryPrivate *priv,
1908 +                            XMLNode *parent_node,
1909 +                            const gchar *layout_name)
1910 +{
1911 +    XMLNode *node = parent_node;
1912 +    XMLNode *sub_node;
1913 +    GList *p;
1914 +    const gchar *variant_name = NULL;
1915 +
1916 +    g_assert (node != NULL);
1917 +    g_assert (layout_name != NULL);
1918 +    for (p = node->sub_nodes; p; p = p->next) {
1919 +        sub_node = (XMLNode *) p->data;
1920 +        if (g_strcmp0 (sub_node->name, "configItem") == 0) {
1921 +            variant_name = parse_xkb_xml_variant_configitem_node (priv, sub_node, layout_name);
1922 +            continue;
1923 +        }
1924 +    }
1925 +    return variant_name;
1926 +}
1927 +
1928 +static GList *
1929 +parse_xkb_xml_variantlist_node (IBusXKBConfigRegistryPrivate *priv,
1930 +                                XMLNode *parent_node,
1931 +                                const gchar *layout_name,
1932 +                                GList *variant_list)
1933 +{
1934 +    XMLNode *node = parent_node;
1935 +    XMLNode *sub_node;
1936 +    GList *p;
1937 +    const gchar *variant_name = NULL;
1938 +
1939 +    g_assert (node != NULL);
1940 +    g_assert (layout_name != NULL);
1941 +    for (p = node->sub_nodes; p; p = p->next) {
1942 +        sub_node = (XMLNode *) p->data;
1943 +        if (g_strcmp0 (sub_node->name, "variant") == 0) {
1944 +            variant_name = parse_xkb_xml_variant_node (priv, sub_node, layout_name);
1945 +            if (variant_name != NULL) {
1946 +                variant_list = g_list_append (variant_list,
1947 +                                              (gpointer) g_strdup (variant_name));
1948 +            }
1949 +            continue;
1950 +        }
1951 +    }
1952 +    return variant_list;
1953 +}
1954 +
1955 +static void
1956 +parse_xkb_xml_layout_node (IBusXKBConfigRegistryPrivate *priv,
1957 +                           XMLNode *parent_node)
1958 +{
1959 +    XMLNode *node = parent_node;
1960 +    XMLNode *sub_node;
1961 +    GList *p;
1962 +    const gchar *name = NULL;
1963 +    GList *variant_list = NULL;
1964 +
1965 +    g_assert (node != NULL);
1966 +    for (p = node->sub_nodes; p; p = p->next) {
1967 +        sub_node = (XMLNode *) p->data;
1968 +        if (g_strcmp0 (sub_node->name, "configItem") == 0) {
1969 +            name = parse_xkb_xml_configitem_node (priv, sub_node);
1970 +            continue;
1971 +        }
1972 +        if (g_strcmp0 (sub_node->name, "variantList") == 0) {
1973 +            if (name == NULL) {
1974 +                g_warning ("layout name is NULL in node %s", node->name);
1975 +                continue;
1976 +            }
1977 +            variant_list = parse_xkb_xml_variantlist_node (priv, sub_node,
1978 +                                                           name,
1979 +                                                           variant_list);
1980 +            continue;
1981 +        }
1982 +    }
1983 +    if (g_hash_table_lookup (priv->layout_list, name) != NULL) {
1984 +        g_warning ("duplicated name %s exists", name);
1985 +        return;
1986 +    }
1987 +    g_hash_table_insert (priv->layout_list,
1988 +                         (gpointer) g_strdup (name),
1989 +                         (gpointer) variant_list);
1990 +}
1991 +
1992 +static void
1993 +parse_xkb_xml_top_node (IBusXKBConfigRegistryPrivate *priv,
1994 +                        XMLNode *parent_node)
1995 +{
1996 +    XMLNode *node = parent_node;
1997 +    XMLNode *sub_node;
1998 +    GList *p;
1999 +
2000 +    g_assert (priv != NULL);
2001 +    g_assert (node != NULL);
2002 +
2003 +    if (g_strcmp0 (node->name, "xkbConfigRegistry") != 0) {
2004 +        g_warning ("node has no xkbConfigRegistry name");
2005 +        return;
2006 +    }
2007 +    for (p = node->sub_nodes; p; p = p->next) {
2008 +        sub_node = (XMLNode *) p->data;
2009 +        if (g_strcmp0 (sub_node->name, "layoutList") == 0) {
2010 +            break;
2011 +        }
2012 +    }
2013 +    if (p == NULL) {
2014 +        g_warning ("xkbConfigRegistry node has no layoutList node");
2015 +        return;
2016 +    }
2017 +    node = sub_node;
2018 +    for (p = node->sub_nodes; p; p = p->next) {
2019 +        sub_node = (XMLNode *) p->data;
2020 +        if (g_strcmp0 (sub_node->name, "layout") == 0) {
2021 +            parse_xkb_xml_layout_node (priv, sub_node);
2022 +            continue;
2023 +        }
2024 +    }
2025 +}
2026 +
2027 +static void
2028 +free_lang_list (GList *list)
2029 +{
2030 +    GList *l = list;
2031 +    while (l) {
2032 +        g_free (l->data);
2033 +        l->data = NULL;
2034 +        l = l->next;
2035 +    }
2036 +    g_list_free (list);
2037 +}
2038 +
2039 +static void
2040 +parse_xkb_config_registry_file (IBusXKBConfigRegistryPrivate *priv,
2041 +                                const gchar *file)
2042 +{
2043 +    XMLNode *node;
2044 +
2045 +    g_assert (file != NULL);
2046 +
2047 +    priv->layout_list = g_hash_table_new_full (g_str_hash,
2048 +                                               (GEqualFunc) g_str_equal,
2049 +                                               (GDestroyNotify) g_free,
2050 +                                               (GDestroyNotify) free_lang_list);
2051 +    priv->layout_desc = g_hash_table_new_full (g_str_hash,
2052 +                                               (GEqualFunc) g_str_equal,
2053 +                                               (GDestroyNotify) g_free,
2054 +                                               (GDestroyNotify) g_free);
2055 +    priv->layout_lang = g_hash_table_new_full (g_str_hash,
2056 +                                               (GEqualFunc) g_str_equal,
2057 +                                               (GDestroyNotify) g_free,
2058 +                                               (GDestroyNotify) free_lang_list);
2059 +    priv->variant_desc = g_hash_table_new_full (g_str_hash,
2060 +                                               (GEqualFunc) g_str_equal,
2061 +                                               (GDestroyNotify) g_free,
2062 +                                               (GDestroyNotify) g_free);
2063 +    node = ibus_xml_parse_file (file);
2064 +    parse_xkb_xml_top_node (priv, node);
2065 +    ibus_xml_free (node);
2066 +}
2067 +
2068 +static void
2069 +ibus_xkb_config_registry_init (IBusXKBConfigRegistry *xkb_config)
2070 +{
2071 +    IBusXKBConfigRegistryPrivate *priv;
2072 +    const gchar *file = XKB_RULES_XML_FILE;
2073 +
2074 +    priv = IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE (xkb_config);
2075 +    parse_xkb_config_registry_file (priv, file);
2076 +}
2077 +
2078 +static void
2079 +ibus_xkb_config_registry_destroy (IBusXKBConfigRegistry *xkb_config)
2080 +{
2081 +    IBusXKBConfigRegistryPrivate *priv;
2082 +
2083 +    g_return_if_fail (xkb_config != NULL);
2084 +
2085 +    priv = IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE (xkb_config);
2086 +
2087 +    g_hash_table_destroy (priv->layout_list);
2088 +    priv->layout_list = NULL;
2089 +    g_hash_table_destroy (priv->layout_lang);
2090 +    priv->layout_lang= NULL;
2091 +    g_hash_table_destroy (priv->layout_desc);
2092 +    priv->layout_desc= NULL;
2093 +    g_hash_table_destroy (priv->variant_desc);
2094 +    priv->variant_desc = NULL;
2095 +
2096 +    IBUS_OBJECT_CLASS(ibus_xkb_config_registry_parent_class)->destroy (IBUS_OBJECT (xkb_config));
2097 +}
2098 +
2099 +static void
2100 +ibus_xkb_config_registry_class_init (IBusXKBConfigRegistryClass *klass)
2101 +{
2102 +    IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass);
2103 +
2104 +    g_type_class_add_private (klass, sizeof (IBusXKBConfigRegistryPrivate));
2105 +
2106 +    ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_xkb_config_registry_destroy;
2107 +}
2108 +
2109 +IBusXKBConfigRegistry *
2110 +ibus_xkb_config_registry_new (void)
2111 +{
2112 +    IBusXKBConfigRegistry *xkb_config;
2113 +
2114 +    xkb_config = IBUS_XKB_CONFIG_REGISTRY (g_object_new (IBUS_TYPE_XKB_CONFIG_REGISTRY, NULL));
2115 +    return xkb_config;
2116 +}
2117 +
2118 +#define TABLE_FUNC(field_name) const GHashTable *                       \
2119 +ibus_xkb_config_registry_get_##field_name  (IBusXKBConfigRegistry *xkb_config) \
2120 +{                                                                       \
2121 +    IBusXKBConfigRegistryPrivate *priv;                                 \
2122 +                                                                        \
2123 +    g_return_val_if_fail (xkb_config != NULL, NULL);                    \
2124 +    priv = IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE (xkb_config);           \
2125 +    return priv->field_name;                                            \
2126 +}
2127 +
2128 +TABLE_FUNC (layout_list)
2129 +TABLE_FUNC (layout_lang)
2130 +TABLE_FUNC (layout_desc)
2131 +TABLE_FUNC (variant_desc)
2132 +
2133 +#undef TABLE_FUNC
2134 +
2135 +GList *
2136 +ibus_xkb_config_registry_layout_list_get_layouts (IBusXKBConfigRegistry *xkb_config)
2137 +{
2138 +    GHashTable *table;
2139 +    GList *list = NULL;
2140 +
2141 +    table = (GHashTable *)
2142 +        ibus_xkb_config_registry_get_layout_list (xkb_config);
2143 +    list = (GList *) g_hash_table_get_keys (table);
2144 +    return list;
2145 +}
2146 +
2147 +/* vala could use GLib.List<string> for the returned pointer and
2148 + * the declaration calls g_list_foreach (retval, g_free, NULL).
2149 + * When I think about GLib.List<string> v.s. GLib.List, probably
2150 + * I think GLib.List<string> is better for the function and set
2151 + * g_strdup() here. I do not know about GJS implementation.
2152 + */
2153 +#define TABLE_LOOKUP_LIST_FUNC(field_name, value) GList *               \
2154 +ibus_xkb_config_registry_##field_name##_get_##value  (IBusXKBConfigRegistry *xkb_config, const gchar *key) \
2155 +{                                                                       \
2156 +    GHashTable *table;                                                  \
2157 +    GList *list = NULL;                                                 \
2158 +    GList *retval= NULL;                                                \
2159 +    GList *p = NULL;                                                    \
2160 +                                                                        \
2161 +    table = (GHashTable *)                                              \
2162 +        ibus_xkb_config_registry_get_##field_name (xkb_config);         \
2163 +    list = (GList *) g_hash_table_lookup (table, key);                  \
2164 +    retval = g_list_copy (list);                                        \
2165 +    for (p = retval; p; p = p->next) {                                  \
2166 +        p->data = g_strdup (p->data);                                   \
2167 +    }                                                                   \
2168 +    return retval;                                                      \
2169 +}
2170 +
2171 +#define TABLE_LOOKUP_STRING_FUNC(field_name, value) gchar *             \
2172 +ibus_xkb_config_registry_##field_name##_get_##value  (IBusXKBConfigRegistry *xkb_config, const gchar *key) \
2173 +{                                                                       \
2174 +    GHashTable *table;                                                  \
2175 +    const gchar *desc = NULL;                                           \
2176 +                                                                        \
2177 +    table = (GHashTable *)                                              \
2178 +        ibus_xkb_config_registry_get_##field_name (xkb_config);         \
2179 +    desc = (const gchar *) g_hash_table_lookup (table, key);            \
2180 +    return g_strdup (desc);                                             \
2181 +}
2182 +
2183 +TABLE_LOOKUP_LIST_FUNC (layout_list, variants)
2184 +TABLE_LOOKUP_LIST_FUNC (layout_lang, langs)
2185 +TABLE_LOOKUP_STRING_FUNC (layout_desc, desc)
2186 +TABLE_LOOKUP_STRING_FUNC (variant_desc, desc)
2187 +
2188 +#undef TABLE_LOOKUP_LIST_FUNC
2189 +#undef TABLE_LOOKUP_STRING_FUNC
2190 diff --git a/src/ibusxkbxml.h b/src/ibusxkbxml.h
2191 new file mode 100644
2192 index 0000000..6f5b7bd
2193 --- /dev/null
2194 +++ b/src/ibusxkbxml.h
2195 @@ -0,0 +1,187 @@
2196 +/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2197 +/* vim:set et sts=4: */
2198 +/* bus - The Input Bus
2199 + * Copyright (C) 2012 Takao Fujiwara <takao.fujiwara1@gmail.com>
2200 + * Copyright (C) 2012 Peng Huang <shawn.p.huang@gmail.com>
2201 + * Copyright (C) 2012 Red Hat, Inc.
2202 + *
2203 + * This library is free software; you can redistribute it and/or
2204 + * modify it under the terms of the GNU Lesser General Public
2205 + * License as published by the Free Software Foundation; either
2206 + * version 2 of the License, or (at your option) any later version.
2207 + *
2208 + * This library is distributed in the hope that it will be useful,
2209 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
2210 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
2211 + * Lesser General Public License for more details.
2212 + *
2213 + * You should have received a copy of the GNU Lesser General Public
2214 + * License along with this library; if not, write to the
2215 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
2216 + * Boston, MA 02111-1307, USA.
2217 + */
2218 +#ifndef __IBUS_XKBXML_H_
2219 +#define __IBUS_XKBXML_H_
2220 +
2221 +#if !defined (__IBUS_H_INSIDE__) && !defined (IBUS_COMPILATION)
2222 +#error "Only <ibus.h> can be included directly"
2223 +#endif
2224 +
2225 +#include "ibus.h"
2226 +
2227 +/*
2228 + * Type macros.
2229 + */
2230 +/* define IBusXKBConfigRegistry macros */
2231 +#define IBUS_TYPE_XKB_CONFIG_REGISTRY                   \
2232 +    (ibus_xkb_config_registry_get_type ())
2233 +#define IBUS_XKB_CONFIG_REGISTRY(obj)                   \
2234 +    (G_TYPE_CHECK_INSTANCE_CAST ((obj), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistry))
2235 +#define IBUS_XKB_CONFIG_REGISTRY_CLASS(klass)           \
2236 +    (G_TYPE_CHECK_CLASS_CAST ((klass), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistryClass))
2237 +#define IBUS_IS_XKB_CONFIG_REGISTRY(obj)                \
2238 +    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IBUS_TYPE_XKB_CONFIG_REGISTRY))
2239 +#define IBUS_IS_XKB_CONFIG_REGISTRY_CLASS(klass)        \
2240 +    (G_TYPE_CHECK_CLASS_TYPE ((klass), IBUS_TYPE_XKB_CONFIG_REGISTRY))
2241 +#define IBUS_XKB_CONFIG_REGISTRY_GET_CLASS(obj)         \
2242 +    (G_TYPE_INSTANCE_GET_CLASS ((obj), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistryClass))
2243 +
2244 +G_BEGIN_DECLS
2245 +
2246 +typedef struct _IBusXKBConfigRegistry IBusXKBConfigRegistry;
2247 +typedef struct _IBusXKBConfigRegistryClass IBusXKBConfigRegistryClass;
2248 +
2249 +struct _IBusXKBConfigRegistry {
2250 +    IBusObject parent;
2251 +};
2252 +
2253 +struct _IBusXKBConfigRegistryClass {
2254 +    IBusObjectClass parent;
2255 +    /* signals */
2256 +    /*< private >*/
2257 +    /* padding */
2258 +    gpointer pdummy[8];
2259 +};
2260 +
2261 +
2262 +GType            ibus_xkb_config_registry_get_type
2263 +                                                 (void);
2264 +
2265 +/**
2266 + * ibus_xkb_config_registry_new:
2267 + * @returns: A newly allocated IBusXKBConfigRegistry
2268 + *
2269 + * New an IBusXKBConfigRegistry.
2270 + */
2271 +IBusXKBConfigRegistry *
2272 +                 ibus_xkb_config_registry_new
2273 +                                                 (void);
2274 +
2275 +/**
2276 + * ibus_xkb_config_registry_get_layout_list: (skip)
2277 + * @xkb_config: An IBusXKBConfigRegistry.
2278 + * @returns: A const GHashTable
2279 + *
2280 + * a const GHashTable
2281 + */
2282 +const GHashTable *
2283 +                 ibus_xkb_config_registry_get_layout_list
2284 +                                                 (IBusXKBConfigRegistry *xkb_config);
2285 +
2286 +/**
2287 + * ibus_xkb_config_registry_get_layout_lang: (skip)
2288 + * @xkb_config: An IBusXKBConfigRegistry.
2289 + * @returns: A const GHashTable
2290 + *
2291 + * a const GHashTable
2292 + */
2293 +const GHashTable *
2294 +                 ibus_xkb_config_registry_get_layout_lang
2295 +                                                 (IBusXKBConfigRegistry *xkb_config);
2296 +
2297 +/**
2298 + * ibus_xkb_config_registry_get_layout_desc: (skip)
2299 + * @xkb_config: An IBusXKBConfigRegistry.
2300 + * @returns: A const GHashTable
2301 + *
2302 + * a const GHashTable
2303 + */
2304 +const GHashTable *
2305 +                 ibus_xkb_config_registry_get_layout_desc
2306 +                                                 (IBusXKBConfigRegistry *xkb_config);
2307 +
2308 +/**
2309 + * ibus_xkb_config_registry_get_variant_desc: (skip)
2310 + * @xkb_config: An IBusXKBConfigRegistry.
2311 + * @returns: A const GHashTable
2312 + *
2313 + * a const GHashTable
2314 + */
2315 +const GHashTable *
2316 +                 ibus_xkb_config_registry_get_variant_desc
2317 +                                                 (IBusXKBConfigRegistry *xkb_config);
2318 +
2319 +/**
2320 + * ibus_xkb_config_registry_layout_list_get_layouts:
2321 + * @xkb_config: An IBusXKBConfigRegistry.
2322 + * @returns: (transfer container) (element-type utf8): A GList of layouts
2323 + *
2324 + * a GList of layouts
2325 + */
2326 +GList *
2327 +                 ibus_xkb_config_registry_layout_list_get_layouts
2328 +                                                 (IBusXKBConfigRegistry *xkb_config);
2329 +
2330 +/**
2331 + * ibus_xkb_config_registry_layout_list_get_variants:
2332 + * @xkb_config: An IBusXKBConfigRegistry.
2333 + * @layout: A layout.
2334 + * @returns: (transfer container) (element-type utf8): A GList
2335 + *
2336 + * a GList
2337 + */
2338 +GList *
2339 +                 ibus_xkb_config_registry_layout_list_get_variants
2340 +                                                 (IBusXKBConfigRegistry *xkb_config,
2341 +                                                  const gchar           *layout);
2342 +
2343 +/**
2344 + * ibus_xkb_config_registry_layout_lang_get_langs:
2345 + * @xkb_config: An IBusXKBConfigRegistry.
2346 + * @layout: A layout.
2347 + * @returns: (transfer container) (element-type utf8): A GList
2348 + *
2349 + * a GList
2350 + */
2351 +GList *
2352 +                 ibus_xkb_config_registry_layout_lang_get_langs
2353 +                                                 (IBusXKBConfigRegistry *xkb_config,
2354 +                                                  const gchar           *layout);
2355 +
2356 +/**
2357 + * ibus_xkb_config_registry_layout_desc_get_desc:
2358 + * @xkb_config: An IBusXKBConfigRegistry.
2359 + * @layout: A layout.
2360 + * @returns: A layout description
2361 + *
2362 + * a layout description
2363 + */
2364 +gchar *
2365 +                 ibus_xkb_config_registry_layout_desc_get_desc
2366 +                                                 (IBusXKBConfigRegistry *xkb_config,
2367 +                                                  const gchar           *layout);
2368 +
2369 +/**
2370 + * ibus_xkb_config_registry_variant_desc_get_desc:
2371 + * @xkb_config: An IBusXKBConfigRegistry.
2372 + * @variant: A variant.
2373 + * @returns: A variant description
2374 + *
2375 + * a variant description
2376 + */
2377 +gchar *
2378 +                 ibus_xkb_config_registry_variant_desc_get_desc
2379 +                                                 (IBusXKBConfigRegistry *xkb_config,
2380 +                                                  const gchar           *variant);
2381 +G_END_DECLS
2382 +#endif
2383 diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
2384 index 5473027..cbd746c 100644
2385 --- a/ui/gtk3/Makefile.am
2386 +++ b/ui/gtk3/Makefile.am
2387 @@ -47,6 +47,9 @@ USE_SYMBOL_ICON = FALSE
2388  # force include config.h before gi18n.h.
2389  AM_CPPFLAGS = -include $(CONFIG_HEADER)
2390  
2391 +HAVE_IBUS_XKB_C = $(strip $(subst false, FALSE, $(subst true, TRUE, $(HAVE_IBUS_XKB))))
2392 +HAVE_IBUS_GKBD_C = $(strip $(subst false, FALSE, $(subst true, TRUE, $(HAVE_IBUS_GKBD))))
2393 +
2394  AM_CFLAGS = \
2395         @GLIB2_CFLAGS@ \
2396         @GIO2_CFLAGS@ \
2397 @@ -58,6 +61,10 @@ AM_CFLAGS = \
2398         -DBINDIR=\"$(bindir)\" \
2399         -DIBUS_DISABLE_DEPRECATED \
2400         -DSWITCHER_USE_SYMBOL_ICON=$(USE_SYMBOL_ICON) \
2401 +       -DHAVE_IBUS_XKB=$(HAVE_IBUS_XKB_C) \
2402 +       -DHAVE_IBUS_GKBD=$(HAVE_IBUS_GKBD_C) \
2403 +       -DIBUS_XKB_COMMAND=\"$(libexecdir)/ibus-xkb\" \
2404 +       -DXKB_LAYOUTS_MAX_LENGTH=4 \
2405         -Wno-unused-variable \
2406         -Wno-unused-but-set-variable \
2407         -Wno-unused-function \
2408 @@ -91,6 +98,7 @@ ibus_ui_gtk3_SOURCES = \
2409         application.vala \
2410         candidatearea.vala \
2411         candidatepanel.vala \
2412 +       gkbdlayout.vala \
2413         handle.vala \
2414         iconwidget.vala \
2415         keybindingmanager.vala \
2416 @@ -99,17 +107,48 @@ ibus_ui_gtk3_SOURCES = \
2417         property.vala \
2418         separator.vala \
2419         switcher.vala \
2420 +       xkblayout.vala \
2421         $(NULL)
2422  
2423  ibus_ui_gtk3_LDADD = \
2424         $(AM_LDADD) \
2425         $(NULL)
2426  
2427 +if ENABLE_LIBGNOMEKBD
2428 +AM_CFLAGS += \
2429 +       @LIBGNOMEKBDUI_CFLAGS@ \
2430 +       @ATK_CFLAGS@ \
2431 +       $(NULL)
2432 +
2433 +AM_LDADD += \
2434 +       @LIBGNOMEKBDUI_LIBS@ \
2435 +       @ATK_LIBS@ \
2436 +       $(NULL)
2437 +
2438 +AM_VALAFLAGS += \
2439 +       --vapidir=. \
2440 +       --metadatadir=$(top_srcdir)/bindings/vala \
2441 +       --pkg=glib-2.0 \
2442 +       --pkg=gmodule-2.0 \
2443 +       --pkg=gkbd \
2444 +       --pkg=Xkl-1.0 \
2445 +       $(NULL)
2446 +
2447 +$(srcdir)/gkbdlayout.vala: $(top_builddir)/bindings/vala/gkbd.vapi
2448 +       @cp $(srcdir)/gkbdlayout.vala.true $(srcdir)/gkbdlayout.vala
2449 +else
2450 +$(srcdir)/gkbdlayout.vala:
2451 +       @cp $(srcdir)/gkbdlayout.vala.false $(srcdir)/gkbdlayout.vala
2452 +endif
2453 +
2454  CLEANFILES = \
2455 +       gkbdlayout.vala \
2456         gtkpanel.xml \
2457         $(NULL)
2458  
2459  EXTRA_DIST = \
2460 +       gkbdlayout.vala.false \
2461 +       gkbdlayout.vala.true \
2462         gtkpanel.xml.in.in \
2463         $(NULL)
2464  
2465 diff --git a/ui/gtk3/gkbdlayout.vala.false b/ui/gtk3/gkbdlayout.vala.false
2466 new file mode 100644
2467 index 0000000..a387de9
2468 --- /dev/null
2469 +++ b/ui/gtk3/gkbdlayout.vala.false
2470 @@ -0,0 +1,63 @@
2471 +/* vim:set et sts=4 sw=4:
2472 + *
2473 + * ibus - The Input Bus
2474 + *
2475 + * Copyright 2012 Red Hat, Inc.
2476 + * Copyright(c) 2012 Peng Huang <shawn.p.huang@gmail.com>
2477 + * Copyright(c) 2012 Takao Fujiwara <tfujiwar@redhat.com>
2478 + *
2479 + * This library is free software; you can redistribute it and/or
2480 + * modify it under the terms of the GNU Lesser General Public
2481 + * License as published by the Free Software Foundation; either
2482 + * version 2 of the License, or(at your option) any later version.
2483 + *
2484 + * This library is distributed in the hope that it will be useful,
2485 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
2486 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2487 + * GNU Lesser General Public License for more details.
2488 + *
2489 + * You should have received a copy of the GNU Lesser General Public
2490 + * License along with this program; if not, write to the
2491 + * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
2492 + * Boston, MA  02111-1307  USA
2493 + */
2494 +
2495 +public class GkbdLayout
2496 +{
2497 +    public signal void changed();
2498 +    public signal void group_changed (int object);
2499 +
2500 +    public GkbdLayout() {
2501 +    }
2502 +
2503 +    public string[] get_layouts() {
2504 +        return new string[0];
2505 +    }
2506 +
2507 +    public string[] get_group_names() {
2508 +        return new string[0];
2509 +    }
2510 +
2511 +    public void lock_group(int id) {
2512 +    }
2513 +
2514 +    public void start_listen() {
2515 +    }
2516 +
2517 +    public void stop_listen() {
2518 +    }
2519 +
2520 +    /*
2521 +    public static int main(string[] args) {
2522 +        GkbdLayout ibus_layouts = new GkbdLayout();
2523 +
2524 +        string[] layouts = ibus_layouts.get_layouts();
2525 +        string[] names = ibus_layouts.get_group_names();
2526 +        for (int i = 0; layouts != null && i < layouts.length; i++) {
2527 +            stdout.printf("%s %s\n", layouts[i], names[i]);
2528 +        }
2529 +
2530 +        return 0;
2531 +    }
2532 +    */
2533 +}
2534 diff --git a/ui/gtk3/gkbdlayout.vala.true b/ui/gtk3/gkbdlayout.vala.true
2535 new file mode 100644
2536 index 0000000..2b78c69
2537 --- /dev/null
2538 +++ b/ui/gtk3/gkbdlayout.vala.true
2539 @@ -0,0 +1,108 @@
2540 +/* vim:set et sts=4 sw=4:
2541 + *
2542 + * ibus - The Input Bus
2543 + *
2544 + * Copyright 2012 Red Hat, Inc.
2545 + * Copyright(c) 2012 Peng Huang <shawn.p.huang@gmail.com>
2546 + * Copyright(c) 2012 Takao Fujiwara <tfujiwar@redhat.com>
2547 + *
2548 + * This library is free software; you can redistribute it and/or
2549 + * modify it under the terms of the GNU Lesser General Public
2550 + * License as published by the Free Software Foundation; either
2551 + * version 2 of the License, or(at your option) any later version.
2552 + *
2553 + * This library is distributed in the hope that it will be useful,
2554 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
2555 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2556 + * GNU Lesser General Public License for more details.
2557 + *
2558 + * You should have received a copy of the GNU Lesser General Public
2559 + * License along with this program; if not, write to the
2560 + * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
2561 + * Boston, MA  02111-1307  USA
2562 + */
2563 +
2564 +public class GkbdLayout
2565 +{
2566 +    public signal void changed();
2567 +    public signal void group_changed (int object);
2568 +
2569 +    private Gkbd.Configuration m_config = null;
2570 +
2571 +    public GkbdLayout() {
2572 +        m_config = Gkbd.Configuration.get();
2573 +        if (m_config != null) {
2574 +            m_config.changed.connect(config_changed_cb);
2575 +            m_config.group_changed.connect(config_group_changed_cb);
2576 +        }
2577 +    }
2578 +
2579 +    ~GkbdLayout() {
2580 +        if (m_config != null) {
2581 +            m_config.changed.disconnect(config_changed_cb);
2582 +            m_config.group_changed.disconnect(config_group_changed_cb);
2583 +            /* gkbd_configuration_get reuses the object and do not
2584 +             * destroy m_config here. */
2585 +            m_config.ref();
2586 +            m_config = null;
2587 +        }
2588 +    }
2589 +
2590 +    private void config_changed_cb() {
2591 +        changed();
2592 +    }
2593 +
2594 +    private void config_group_changed_cb(int object) {
2595 +        group_changed(object);
2596 +    }
2597 +
2598 +    public string[] get_layouts() {
2599 +        if (m_config == null) {
2600 +            return new string[0];
2601 +        }
2602 +        return m_config.get_short_group_names();
2603 +    }
2604 +
2605 +    public string[] get_group_names() {
2606 +        if (m_config == null) {
2607 +            return new string[0];
2608 +        }
2609 +        return m_config.get_group_names();
2610 +    }
2611 +
2612 +    public void lock_group(int id) {
2613 +        if (m_config == null) {
2614 +            return;
2615 +        }
2616 +        m_config.lock_group(id);
2617 +    }
2618 +
2619 +    public void start_listen() {
2620 +        if (m_config == null) {
2621 +            return;
2622 +        }
2623 +        m_config.start_listen();
2624 +    }
2625 +
2626 +    public void stop_listen() {
2627 +        if (m_config == null) {
2628 +            return;
2629 +        }
2630 +        m_config.stop_listen();
2631 +    }
2632 +
2633 +    /*
2634 +    public static int main(string[] args) {
2635 +        Gtk.init(ref args);
2636 +        GkbdLayout ibus_layouts = new GkbdLayout();
2637 +
2638 +        string[] layouts = ibus_layouts.get_layouts();
2639 +        string[] names = ibus_layouts.get_group_names();
2640 +        for (int i = 0; layouts != null && i < layouts.length; i++) {
2641 +            stdout.printf("%s %s\n", layouts[i], names[i]);
2642 +        }
2643 +
2644 +        return 0;
2645 +    }
2646 +    */
2647 +}
2648 diff --git a/ui/gtk3/keybindingmanager.vala b/ui/gtk3/keybindingmanager.vala
2649 index 5ff0c65..bfe560c 100644
2650 --- a/ui/gtk3/keybindingmanager.vala
2651 +++ b/ui/gtk3/keybindingmanager.vala
2652 @@ -41,15 +41,18 @@ public class KeybindingManager : GLib.Object {
2653      private class Keybinding {
2654          public Keybinding(uint keysym,
2655                            Gdk.ModifierType modifiers,
2656 -                          KeybindingHandlerFunc handler) {
2657 +                          KeybindingHandlerFunc handler,
2658 +                          bool reverse) {
2659              this.keysym = keysym;
2660              this.modifiers = modifiers;
2661              this.handler = handler;
2662 +            this.reverse = reverse;
2663          }
2664  
2665          public uint keysym { get; set; }
2666          public Gdk.ModifierType modifiers { get; set; }
2667          public unowned KeybindingHandlerFunc handler { get; set; }
2668 +        public bool reverse { get; set; }
2669      }
2670  
2671      /**
2672 @@ -57,7 +60,7 @@ public class KeybindingManager : GLib.Object {
2673       *
2674       * @param event passing on gdk event
2675       */
2676 -    public delegate void KeybindingHandlerFunc(Gdk.Event event);
2677 +    public delegate void KeybindingHandlerFunc(Gdk.Event event, bool reverse);
2678  
2679  
2680      private  KeybindingManager() {
2681 @@ -73,7 +76,8 @@ public class KeybindingManager : GLib.Object {
2682       */
2683      public bool bind(uint keysym,
2684                       Gdk.ModifierType modifiers,
2685 -                     KeybindingHandlerFunc handler) {
2686 +                     KeybindingHandlerFunc handler,
2687 +                     bool reverse) {
2688          unowned X.Display display = Gdk.x11_get_default_xdisplay();
2689  
2690          int keycode = display.keysym_to_keycode(keysym);
2691 @@ -84,7 +88,7 @@ public class KeybindingManager : GLib.Object {
2692          grab_keycode (Gdk.Display.get_default(), keysym, modifiers);
2693  
2694          // store binding
2695 -        Keybinding binding = new Keybinding(keysym, modifiers, handler);
2696 +        Keybinding binding = new Keybinding(keysym, modifiers, handler, reverse);
2697          m_bindings.append(binding);
2698  
2699          return true;
2700 @@ -198,7 +202,7 @@ public class KeybindingManager : GLib.Object {
2701                      if (event.key.keyval != binding.keysym ||
2702                          modifiers != binding.modifiers)
2703                          continue;
2704 -                    binding.handler(event);
2705 +                    binding.handler(event, binding.reverse);
2706                      return;
2707                  }
2708              }
2709 diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
2710 index 40079ec..8e6f756 100644
2711 --- ibus-1.4.99.20121109/ui/gtk3/panel.vala.orig        2012-11-09 07:16:19.000000000 +0100
2712 +++ ibus-1.4.99.20121109/ui/gtk3/panel.vala     2012-11-17 19:22:03.176142457 +0100
2713 @@ -21,6 +21,20 @@
2714   */
2715  
2716  class Panel : IBus.PanelService {
2717 +    private class Keybinding {
2718 +        public Keybinding(uint keysym,
2719 +                          Gdk.ModifierType modifiers,
2720 +                          bool reverse) {
2721 +            this.keysym = keysym;
2722 +            this.modifiers = modifiers;
2723 +            this.reverse = reverse;
2724 +        }
2725 +
2726 +        public uint keysym { get; set; }
2727 +        public Gdk.ModifierType modifiers { get; set; }
2728 +        public bool reverse { get; set; }
2729 +    }
2730 +
2731      private IBus.Bus m_bus;
2732      private IBus.Config m_config;
2733      private Gtk.StatusIcon m_status_icon;
2734 @@ -34,10 +48,17 @@
2735      private Gtk.AboutDialog m_about_dialog;
2736      private Gtk.CssProvider m_css_provider;
2737      private int m_switcher_delay_time = 400;
2738 +    private GkbdLayout m_gkbdlayout = null;
2739 +    private XKBLayout m_xkblayout = null;
2740 +    private string[] m_layouts = {};
2741 +    private string[] m_variants = {};
2742 +    private int m_fallback_lock_id = -1;
2743 +    private bool m_changed_xkb_option = false;
2744 +    private GLib.Timer m_changed_layout_timer;
2745      private const string ACCELERATOR_SWITCH_IME_FOREWARD = "<Control>space";
2746 +    private const string ACCELERATOR_SWITCH_IME_BACKWARD = "<Control><Shift>space";
2747  
2748 -    private uint m_switch_keysym = 0;
2749 -    private Gdk.ModifierType m_switch_modifiers = 0;
2750 +    private GLib.List<Keybinding> m_keybindings = new GLib.List<Keybinding>();
2751  
2752      public Panel(IBus.Bus bus) {
2753          GLib.assert(bus.is_connected());
2754 @@ -60,7 +81,6 @@
2755          m_candidate_panel.page_down.connect((w) => this.page_down());
2756  
2757          m_switcher = new Switcher();
2758 -        bind_switch_shortcut();
2759  
2760          if (m_switcher_delay_time >= 0) {
2761              m_switcher.set_popup_delay_time((uint) m_switcher_delay_time);
2762 @@ -76,64 +96,135 @@
2763  
2764      ~Panel() {
2765          unbind_switch_shortcut();
2766 -    }
2767  
2768 -    private void bind_switch_shortcut() {
2769 -        var keybinding_manager = KeybindingManager.get_instance();
2770 +        if (HAVE_IBUS_GKBD && m_gkbdlayout != null) {
2771 +            m_gkbdlayout.changed.disconnect(gkbdlayout_changed_cb);
2772 +            m_gkbdlayout.stop_listen();
2773 +            m_gkbdlayout = null;
2774 +        }
2775 +
2776 +        m_xkblayout = null;
2777 +    }
2778  
2779 -        var accelerator = ACCELERATOR_SWITCH_IME_FOREWARD;
2780 +    private void keybinding_manager_bind(KeybindingManager keybinding_manager,
2781 +                                         string? accelerator,
2782 +                                         bool reverse) {
2783 +        uint switch_keysym = 0;
2784 +        Gdk.ModifierType switch_modifiers = 0;
2785          Gtk.accelerator_parse(accelerator,
2786 -                out m_switch_keysym, out m_switch_modifiers);
2787 +                out switch_keysym, out switch_modifiers);
2788  
2789 -        // Map virtual modifiers to (i.e.Mod2, Mod3, ...)
2790 +        // Map virtual modifiers to (i.e. Mod2, Mod3, ...)
2791          const Gdk.ModifierType VIRTUAL_MODIFIERS = (
2792                  Gdk.ModifierType.SUPER_MASK |
2793                  Gdk.ModifierType.HYPER_MASK |
2794                  Gdk.ModifierType.META_MASK);
2795 -        if ((m_switch_modifiers & VIRTUAL_MODIFIERS) != 0) {
2796 +        if ((switch_modifiers & VIRTUAL_MODIFIERS) != 0) {
2797          // workaround a bug in gdk vapi vala > 0.18
2798          // https://bugzilla.gnome.org/show_bug.cgi?id=677559
2799  #if VALA_0_18
2800              Gdk.Keymap.get_default().map_virtual_modifiers(
2801 -                    ref m_switch_modifiers);
2802 +                    ref switch_modifiers);
2803  #else
2804 -            if ((m_switch_modifiers & Gdk.ModifierType.SUPER_MASK) != 0)
2805 -                m_switch_modifiers |= Gdk.ModifierType.MOD4_MASK;
2806 -            if ((m_switch_modifiers & Gdk.ModifierType.HYPER_MASK) != 0)
2807 -                m_switch_modifiers |= Gdk.ModifierType.MOD4_MASK;
2808 +            if ((switch_modifiers & Gdk.ModifierType.SUPER_MASK) != 0)
2809 +                switch_modifiers |= Gdk.ModifierType.MOD4_MASK;
2810 +            if ((switch_modifiers & Gdk.ModifierType.HYPER_MASK) != 0)
2811 +                switch_modifiers |= Gdk.ModifierType.MOD4_MASK;
2812  #endif
2813 -            m_switch_modifiers &= ~VIRTUAL_MODIFIERS;
2814 +            switch_modifiers &= ~VIRTUAL_MODIFIERS;
2815          }
2816  
2817 -        if (m_switch_keysym == 0 && m_switch_modifiers == 0) {
2818 +        if (switch_keysym == 0 && switch_modifiers == 0) {
2819              warning("Parse accelerator '%s' failed!", accelerator);
2820              return;
2821          }
2822  
2823 -        keybinding_manager.bind(m_switch_keysym, m_switch_modifiers,
2824 -                (e) => handle_engine_switch(e, false));
2825 +        Keybinding keybinding = new Keybinding(switch_keysym,
2826 +                                               switch_modifiers,
2827 +                                               reverse);
2828 +        m_keybindings.append(keybinding);
2829 +
2830 +        keybinding_manager.bind(switch_keysym, switch_modifiers,
2831 +                (e, _reverse) => handle_engine_switch(e, _reverse),
2832 +                reverse);
2833 +    }
2834  
2835 -        // accelerator already has Shift mask
2836 -        if ((m_switch_modifiers & Gdk.ModifierType.SHIFT_MASK) != 0)
2837 -            return;
2838 +    // ToDo: Customize the input method with ibus-setup
2839 +    private void bind_switch_shortcut() {
2840 +        string locale = GLib.Intl.setlocale(GLib.LocaleCategory.ALL,
2841 +                                            null);
2842 +        if (locale == null) {
2843 +            locale = "C";
2844 +        }
2845 +
2846 +        string[] ACCELERATOR_IME_HOTKEYS = {};
2847 +        ACCELERATOR_IME_HOTKEYS += ACCELERATOR_SWITCH_IME_FOREWARD;
2848  
2849 -        keybinding_manager.bind(m_switch_keysym,
2850 -                m_switch_modifiers | Gdk.ModifierType.SHIFT_MASK,
2851 -                (e) => handle_engine_switch(e, true));
2852 +        if (m_config != null) {
2853 +            GLib.Variant variant = m_config.get_value("general/hotkey",
2854 +                                                      "trigger_accel");
2855 +            if (variant != null) {
2856 +                ACCELERATOR_IME_HOTKEYS = {};
2857 +                for (int i = 0; i < variant.n_children(); i++) {
2858 +                    ACCELERATOR_IME_HOTKEYS += variant.get_child_value(i).dup_string();
2859 +                }
2860 +            }
2861 +        }
2862 +
2863 +        if (ACCELERATOR_IME_HOTKEYS.length == 1 &&
2864 +            ACCELERATOR_IME_HOTKEYS[0] == ACCELERATOR_SWITCH_IME_FOREWARD) {
2865 +            // FIXME: When us keyboard is used, Zenkaku_Hankaku does not work.
2866 +            /*
2867 +            if (locale[0:2] == "ja") {
2868 +                ACCELERATOR_IME_HOTKEYS += "Zenkaku_Hankaku";
2869 +            }
2870 +            */
2871 +            if (locale[0:2] == "ko") {
2872 +                ACCELERATOR_IME_HOTKEYS += "Hangul";
2873 +                ACCELERATOR_IME_HOTKEYS += "Alt_R";
2874 +            }
2875 +        }
2876 +
2877 +        var keybinding_manager = KeybindingManager.get_instance();
2878 +
2879 +        foreach (var accelerator in ACCELERATOR_IME_HOTKEYS) {
2880 +            keybinding_manager_bind(keybinding_manager, accelerator, false);
2881 +        }
2882 +
2883 +        ACCELERATOR_IME_HOTKEYS = {};
2884 +        ACCELERATOR_IME_HOTKEYS += ACCELERATOR_SWITCH_IME_BACKWARD;
2885 +
2886 +        if (m_config != null) {
2887 +            GLib.Variant variant = m_config.get_value("general/hotkey",
2888 +                                                      "trigger_accel_backward");
2889 +            if (variant != null) {
2890 +                ACCELERATOR_IME_HOTKEYS = {};
2891 +                for (int i = 0; i < variant.n_children(); i++) {
2892 +                    ACCELERATOR_IME_HOTKEYS += variant.get_child_value(i).dup_string();
2893 +                }
2894 +            }
2895 +        }
2896 +
2897 +        foreach (var accelerator in ACCELERATOR_IME_HOTKEYS) {
2898 +            keybinding_manager_bind(keybinding_manager, accelerator, true);
2899 +        }
2900      }
2901  
2902      private void unbind_switch_shortcut() {
2903          var keybinding_manager = KeybindingManager.get_instance();
2904  
2905 -        if (m_switch_keysym == 0 && m_switch_modifiers == 0)
2906 -            return;
2907 +        unowned GLib.List<Keybinding> keybindings = m_keybindings;
2908 +
2909 +        while (keybindings != null) {
2910 +            Keybinding keybinding = keybindings.data;
2911  
2912 -        keybinding_manager.unbind(m_switch_keysym, m_switch_modifiers);
2913 -        keybinding_manager.unbind(m_switch_keysym,
2914 -                m_switch_modifiers | Gdk.ModifierType.SHIFT_MASK);
2915 +            keybinding_manager.unbind(keybinding.keysym,
2916 +                                      keybinding.modifiers);
2917  
2918 -        m_switch_keysym = 0;
2919 -        m_switch_modifiers = 0;
2920 +            // Need to get keybindings.next before GList.remove is called.
2921 +            keybindings = keybindings.next;
2922 +            m_keybindings.remove(keybinding);
2923 +        }
2924      }
2925  
2926      private void set_custom_font() {
2927 @@ -220,13 +311,17 @@
2928          }
2929  
2930          m_config = config;
2931 +        bind_switch_shortcut();
2932          if (m_config != null) {
2933              m_config.value_changed.connect(config_value_changed_cb);
2934              m_config.watch("general", "preload_engines");
2935              m_config.watch("general", "engines_order");
2936              m_config.watch("general", "switcher_delay_time");
2937 +            m_config.watch("general/hotkey", "trigger_accel");
2938 +            m_config.watch("general/hotkey", "trigger_accel_backward");
2939              m_config.watch("panel", "custom_font");
2940              m_config.watch("panel", "use_custom_font");
2941 +            init_engines_order();
2942              update_engines(m_config.get_value("general", "preload_engines"),
2943                             m_config.get_value("general", "engines_order"));
2944              set_switcher_delay_time(null);
2945 @@ -282,6 +377,192 @@
2946          }
2947      }
2948  
2949 +    private void gkbdlayout_changed_cb() {
2950 +        /* The callback is called four times after set_layout is called
2951 +         * so check the elapsed and take the first signal only. */
2952 +        double elapsed = m_changed_layout_timer.elapsed();
2953 +        if (elapsed < 1.0 && elapsed > 0.0) {
2954 +            return;
2955 +        }
2956 +
2957 +        if (m_fallback_lock_id != -1) {
2958 +            /* Call lock_group only when set_layout is called. */
2959 +            m_gkbdlayout.lock_group(m_fallback_lock_id);
2960 +            m_fallback_lock_id = -1;
2961 +        } else {
2962 +            /* Reset default layout when gnome-control-center is called. */
2963 +            m_xkblayout.reset_layout();
2964 +        }
2965 +
2966 +        update_xkb_engines();
2967 +        m_changed_layout_timer.reset();
2968 +    }
2969 +
2970 +    private void init_gkbd() {
2971 +        m_gkbdlayout = new GkbdLayout();
2972 +        m_gkbdlayout.changed.connect(gkbdlayout_changed_cb);
2973 +
2974 +        /* Probably we cannot support both keyboard and ibus indicators
2975 +         * How can I get the engine from keymap of group_id?
2976 +         * e.g. 'en' could be owned by xkb:en and pinyin engines. */
2977 +        //m_gkbdlayout.group_changed.connect((object) => {});
2978 +
2979 +        m_changed_layout_timer = new GLib.Timer();
2980 +        m_changed_layout_timer.start();
2981 +        m_gkbdlayout.start_listen();
2982 +    }
2983 +
2984 +    private void init_engines_order() {
2985 +        if (m_config == null) {
2986 +            return;
2987 +        }
2988 +
2989 +        m_xkblayout = new XKBLayout(m_config);
2990 +
2991 +        if (HAVE_IBUS_GKBD) {
2992 +            init_gkbd();
2993 +        }
2994 +
2995 +        update_xkb_engines();
2996 +    }
2997 +
2998 +    private void update_xkb_engines() {
2999 +        string var_layout = m_xkblayout.get_layout();
3000 +        string var_variant = m_xkblayout.get_variant();
3001 +        if (var_layout == "") {
3002 +            return;
3003 +        }
3004 +
3005 +        m_layouts = var_layout.split(",");
3006 +        m_variants = var_variant.split(",");
3007 +
3008 +        IBus.XKBConfigRegistry registry = new IBus.XKBConfigRegistry();
3009 +        string[] var_xkb_engine_names = {};
3010 +        for (int i = 0; i < m_layouts.length; i++) {
3011 +            string name = m_layouts[i];
3012 +            string lang = null;
3013 +
3014 +            if (i < m_variants.length && m_variants[i] != "") {
3015 +                name = "%s:%s".printf(name, m_variants[i]);
3016 +                string layout = "%s(%s)".printf(name, m_variants[i]);
3017 +                GLib.List<string> langs =
3018 +                        registry.layout_lang_get_langs(layout);
3019 +                if (langs.length() != 0) {
3020 +                    lang = langs.data;
3021 +                }
3022 +            } else {
3023 +                name = "%s:".printf(name);
3024 +            }
3025 +
3026 +            if (lang == null) {
3027 +                GLib.List<string> langs =
3028 +                        registry.layout_lang_get_langs(m_layouts[i]);
3029 +                if (langs.length() != 0) {
3030 +                    lang = langs.data;
3031 +                }
3032 +            }
3033 +
3034 +            var_xkb_engine_names += "%s:%s:%s".printf("xkb", name, lang);
3035 +        }
3036 +
3037 +        GLib.Variant var_engines = 
3038 +           m_config.get_value("general", "preload_engines");
3039 +        string[] engine_names = {};
3040 +        bool updated_engine_names = false;
3041 +
3042 +        if (var_engines != null) {
3043 +            engine_names = var_engines.dup_strv();
3044 +        }
3045 +
3046 +        foreach (string name in var_xkb_engine_names) {
3047 +            if (name in engine_names)
3048 +                continue;
3049 +            updated_engine_names = true;
3050 +            engine_names += name;
3051 +        }
3052 +
3053 +        if (updated_engine_names) {
3054 +            m_config.set_value("general",
3055 +                               "preload_engines",
3056 +                               new GLib.Variant.strv(engine_names));
3057 +        }
3058 +
3059 +        GLib.Variant var_order =
3060 +            m_config.get_value("general", "engines_order");
3061 +        string[] order_names = {};
3062 +        bool updated_order_names = false;
3063 +
3064 +        if (var_order != null) {
3065 +            order_names = var_order.dup_strv();
3066 +        }
3067 +
3068 +        foreach (var name in var_xkb_engine_names) {
3069 +            if (name in order_names)
3070 +                continue;
3071 +            order_names += name;
3072 +            updated_order_names = true;
3073 +        }
3074 +
3075 +        if (updated_order_names) {
3076 +            m_config.set_value("general",
3077 +                               "engines_order",
3078 +                               new GLib.Variant.strv(order_names));
3079 +        }
3080 +    }
3081 +
3082 +    private void set_xkb_group_layout(string layout) {
3083 +        int[] retval = m_xkblayout.set_layout(layout);
3084 +        if (retval[0] >= 0) {
3085 +            /* If an XKB keymap is added into the XKB group,
3086 +             * this._gkbdlayout.lock_group will be called after
3087 +             * 'group-changed' signal is received. */
3088 +            m_fallback_lock_id = retval[0];
3089 +            m_changed_xkb_option = (retval[1] != 0) ? true : false;
3090 +        }
3091 +    }
3092 +
3093 +    private bool set_gkbd_layout(string layout) {
3094 +        /* If a previous ibus engine changed XKB options, need to set the
3095 +         * default XKB option. */
3096 +        if (m_changed_xkb_option == true) {
3097 +            m_changed_xkb_option = false;
3098 +            return false;
3099 +        }
3100 +
3101 +        int gkbd_len = m_gkbdlayout.get_group_names().length;
3102 +        for (int i = 0; i < m_layouts.length && i < gkbd_len; i++) {
3103 +            string sys_layout = m_layouts[i];
3104 +            if (i < m_variants.length && m_variants[i] != "") {
3105 +                sys_layout = "%s(%s)".printf(sys_layout, m_variants[i]);
3106 +            }
3107 +            if (sys_layout == layout) {
3108 +                m_gkbdlayout.lock_group(i);
3109 +                return true;
3110 +            }
3111 +        }
3112 +        return false;
3113 +    }
3114 +
3115 +    private void set_layout(string layout) {
3116 +        if (layout == "default" || layout == null) {
3117 +            return;
3118 +        }
3119 +
3120 +        if (m_xkblayout == null) {
3121 +            init_engines_order();
3122 +        }
3123 +
3124 +        if (HAVE_IBUS_GKBD) {
3125 +            if (set_gkbd_layout(layout)) {
3126 +                return;
3127 +            }
3128 +            set_xkb_group_layout(layout);
3129 +            return;
3130 +        }
3131 +
3132 +        m_xkblayout.set_layout(layout);
3133 +    }
3134 +
3135      private void switch_engine(int i, bool force = false) {
3136          GLib.assert(i >= 0 && i < m_engines.length);
3137  
3138 @@ -296,7 +577,7 @@
3139              return;
3140          }
3141          // set xkb layout
3142 -        exec_setxkbmap(engine);
3143 +        set_layout(engine.get_layout());
3144      }
3145  
3146      private void config_value_changed_cb(IBus.Config config,
3147 @@ -308,6 +589,13 @@
3148              return;
3149          }
3150  
3151 +        if (section == "general/hotkey" &&
3152 +            name.length >= 13 && name[0:13] == "trigger_accel") {
3153 +            unbind_switch_shortcut();
3154 +            bind_switch_shortcut();
3155 +            return;
3156 +        }
3157 +
3158          if (section == "panel" && (name == "custom_font" ||
3159                                     name == "use_custom_font")) {
3160              set_custom_font();
3161 @@ -331,8 +619,7 @@
3162                  event, primary_modifiers);
3163          if (pressed && m_switcher_delay_time >= 0) {
3164              int i = revert ? m_engines.length - 1 : 1;
3165 -            i = m_switcher.run(m_switch_keysym, m_switch_modifiers, event,
3166 -                    m_engines, i);
3167 +            i = m_switcher.run(m_keybindings, event, m_engines, i);
3168              if (i < 0) {
3169                  debug("switch cancelled");
3170              } else {
3171 diff --git a/ui/gtk3/switcher.vala b/ui/gtk3/switcher.vala
3172 index ab2040b..dff5c96 100644
3173 --- ibus-1.4.99.20121109/ui/gtk3/switcher.vala.orig     2012-11-17 19:05:59.422829209 +0100
3174 +++ ibus-1.4.99.20121109/ui/gtk3/switcher.vala  2012-11-17 19:23:40.539473761 +0100
3175 @@ -63,13 +63,26 @@
3176          public string longname { get; set; }
3177      }
3178  
3179 +    private class Keybinding {
3180 +        public Keybinding(uint keysym,
3181 +                          Gdk.ModifierType modifiers,
3182 +                          bool reverse) {
3183 +            this.keysym = keysym;
3184 +            this.modifiers = modifiers;
3185 +            this.reverse = reverse;
3186 +        }
3187 +
3188 +        public uint keysym { get; set; }
3189 +        public Gdk.ModifierType modifiers { get; set; }
3190 +        public bool reverse { get; set; }
3191 +    }
3192 +
3193      private Gtk.Box m_box;
3194      private Gtk.Label m_label;
3195      private IBusEngineButton[] m_buttons = {};
3196      private IBus.EngineDesc[] m_engines;
3197      private uint m_selected_engine;
3198 -    private uint m_keyval;
3199 -    private uint m_modifiers;
3200 +    private unowned GLib.List<Keybinding> m_keybindings;
3201      private Gdk.ModifierType m_primary_modifier;
3202      private GLib.MainLoop m_loop;
3203      private int m_result;
3204 @@ -109,19 +122,17 @@
3205          grab_focus();
3206      }
3207  
3208 -    public int run(uint keyval,
3209 -                   uint state,
3210 +    public int run(GLib.List keybindings,
3211                     Gdk.Event event,
3212                     IBus.EngineDesc[] engines,
3213                     int index) {
3214          assert (m_loop == null);
3215          assert (index < engines.length);
3216  
3217 -        m_keyval = keyval;
3218 -        m_modifiers = state;
3219 +        m_keybindings = (GLib.List<Keybinding>) keybindings;
3220          m_primary_modifier =
3221              KeybindingManager.get_primary_modifier(
3222 -                state & KeybindingManager.MODIFIER_FILTER);
3223 +                event.key.state & KeybindingManager.MODIFIER_FILTER);
3224  
3225          update_engines(engines);
3226          /* Let gtk recalculate the window size. */
3227 @@ -328,27 +339,29 @@
3228      public override bool key_press_event(Gdk.EventKey e) {
3229          bool retval = true;
3230          Gdk.EventKey *pe = &e;
3231 +        uint modifiers = KeybindingManager.MODIFIER_FILTER & pe->state;
3232  
3233          if (m_popup_delay_time > 0) {
3234              restore_window_position("pressed");
3235          }
3236  
3237 -        do {
3238 -            uint modifiers = KeybindingManager.MODIFIER_FILTER & pe->state;
3239 -
3240 -            if ((modifiers != m_modifiers) &&
3241 -                (modifiers != (m_modifiers | Gdk.ModifierType.SHIFT_MASK))) {
3242 -                break;
3243 -            }
3244 -
3245 -            if (pe->keyval == m_keyval) {
3246 -                if (modifiers == m_modifiers)
3247 +        for (unowned GLib.List<Keybinding> keybindings = m_keybindings;
3248 +             keybindings != null;
3249 +             keybindings = keybindings.next) {
3250 +            Keybinding keybinding = keybindings.data;
3251 +
3252 +            if (pe->keyval == keybinding.keysym &&
3253 +                modifiers == (uint) keybinding.modifiers) {
3254 +                if (!keybinding.reverse) {
3255                      next_engine();
3256 -                else // modififers == m_modifiers | SHIFT_MASK
3257 +                } else {
3258                      previous_engine();
3259 -                break;
3260 +                }
3261 +                return true;
3262              }
3263 +        }
3264  
3265 +        do {
3266              switch (pe->keyval) {
3267                  case 0x08fb: /* leftarrow */
3268                  case 0xff51: /* Left */
3269 diff --git a/ui/gtk3/xkblayout.vala b/ui/gtk3/xkblayout.vala
3270 new file mode 100644
3271 index 0000000..33e9d9d
3272 --- /dev/null
3273 +++ b/ui/gtk3/xkblayout.vala
3274 @@ -0,0 +1,464 @@
3275 +/* vim:set et sts=4 sw=4:
3276 + *
3277 + * ibus - The Input Bus
3278 + *
3279 + * Copyright 2012 Red Hat, Inc.
3280 + * Copyright(c) 2012 Peng Huang <shawn.p.huang@gmail.com>
3281 + * Copyright(c) 2012 Takao Fujiwara <tfujiwar@redhat.com>
3282 + *
3283 + * This library is free software; you can redistribute it and/or
3284 + * modify it under the terms of the GNU Lesser General Public
3285 + * License as published by the Free Software Foundation; either
3286 + * version 2 of the License, or(at your option) any later version.
3287 + *
3288 + * This library is distributed in the hope that it will be useful,
3289 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
3290 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
3291 + * GNU Lesser General Public License for more details.
3292 + *
3293 + * You should have received a copy of the GNU Lesser General Public
3294 + * License along with this program; if not, write to the
3295 + * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
3296 + * Boston, MA  02111-1307  USA
3297 + */
3298 +
3299 +public extern const bool HAVE_IBUS_GKBD;
3300 +public extern const bool HAVE_IBUS_XKB;
3301 +public extern const int XKB_LAYOUTS_MAX_LENGTH;
3302 +public extern const string IBUS_XKB_COMMAND;
3303 +
3304 +class XKBLayout
3305 +{
3306 +    string m_xkb_command = IBUS_XKB_COMMAND;
3307 +    IBus.Config m_config = null;
3308 +    string[] m_xkb_latin_layouts = {};
3309 +    GLib.Pid m_xkb_pid = -1;
3310 +    GLib.Pid m_xmodmap_pid = -1;
3311 +    string m_xmodmap_command = "xmodmap";
3312 +    bool m_use_xmodmap = true;
3313 +    string[] m_xmodmap_known_files = {".xmodmap", ".xmodmaprc",
3314 +                                      ".Xmodmap", ".Xmodmaprc"};
3315 +    string m_default_layout = "";
3316 +    string m_default_variant = "";
3317 +    string m_default_option = "";
3318 +
3319 +    public XKBLayout(IBus.Config? config) {
3320 +        if (!HAVE_IBUS_XKB) {
3321 +            m_xkb_command = "setxkbmap";
3322 +        }
3323 +
3324 +        m_config = config;
3325 +
3326 +        if (config != null) {
3327 +            var value  = config.get_value("general", "xkb_latin_layouts");
3328 +            for (int i = 0; value != null && i < value.n_children(); i++) {
3329 +                m_xkb_latin_layouts +=
3330 +                        value.get_child_value(i).dup_string();
3331 +            }
3332 +            if (m_use_xmodmap) {
3333 +                m_use_xmodmap = config.get_value("general", "use_xmodmap").get_boolean();
3334 +            }
3335 +        }
3336 +    }
3337 +
3338 +    private string get_output_from_cmdline(string arg, string element) {
3339 +        string[] exec_command = {};
3340 +        exec_command += m_xkb_command;
3341 +        exec_command += arg;
3342 +        string standard_output = null;
3343 +        string standard_error = null;
3344 +        int exit_status = 0;
3345 +        string retval = "";
3346 +        try {
3347 +            GLib.Process.spawn_sync(null,
3348 +                                    exec_command,
3349 +                                    null,
3350 +                                    GLib.SpawnFlags.SEARCH_PATH,
3351 +                                    null,
3352 +                                    out standard_output,
3353 +                                    out standard_error,
3354 +                                    out exit_status);
3355 +        } catch (GLib.SpawnError err) {
3356 +            stderr.printf("IBUS_ERROR: %s\n", err.message);
3357 +        }
3358 +        if (exit_status != 0) {
3359 +            stderr.printf("IBUS_ERROR: %s\n", standard_error ?? "");
3360 +        }
3361 +        if (standard_output == null) {
3362 +            return "";
3363 +        }
3364 +        foreach (string line in standard_output.split("\n")) {
3365 +            if (element.length <= line.length &&
3366 +                line[0:element.length] == element) {
3367 +                retval = line[element.length:line.length];
3368 +            }
3369 +        }
3370 +        return retval;
3371 +    }
3372 +
3373 +    private void set_layout_cb(GLib.Pid pid, int status) {
3374 +        if (m_xkb_pid != pid) {
3375 +            stderr.printf("IBUS_ERROR: set_layout_cb has another pid\n");
3376 +            return;
3377 +        }
3378 +        GLib.Process.close_pid(m_xkb_pid);
3379 +        m_xkb_pid = -1;
3380 +        set_xmodmap();
3381 +    }
3382 +
3383 +    private void set_xmodmap_cb(GLib.Pid pid, int status) {
3384 +        if (m_xmodmap_pid != pid) {
3385 +            stderr.printf("IBUS_ERROR: set_xmodmap_cb has another pid\n");
3386 +            return;
3387 +        }
3388 +        GLib.Process.close_pid(m_xmodmap_pid);
3389 +        m_xmodmap_pid = -1;
3390 +    }
3391 +
3392 +    private string get_fullpath(string command) {
3393 +        string envpath = GLib.Environment.get_variable("PATH");
3394 +        foreach (string dir in envpath.split(":")) {
3395 +            string filepath = GLib.Path.build_filename(dir, command);
3396 +            if (GLib.FileUtils.test(filepath, GLib.FileTest.EXISTS)) {
3397 +                return filepath;
3398 +            }
3399 +        }
3400 +        return "";
3401 +    }
3402 +
3403 +    private string[] get_xkb_group_layout (string layout,
3404 +                                           string variant,
3405 +                                           int layouts_max_length) {
3406 +        int group_id = 0;
3407 +        int i = 0;
3408 +        string[] layouts = m_default_layout.split(",");
3409 +        string[] variants = m_default_variant.split(",");
3410 +        string group_layouts = "";
3411 +        string group_variants = "";
3412 +        bool has_variant = false;
3413 +        bool include_keymap = false;
3414 +
3415 +        for (i = 0; i < layouts.length; i++) {
3416 +            if (i >= layouts_max_length - 1) {
3417 +                break;
3418 +            }
3419 +
3420 +            if (i == 0) {
3421 +                group_layouts = layouts[i];
3422 +            } else {
3423 +                group_layouts = "%s,%s".printf(group_layouts, layouts[i]);
3424 +            }
3425 +
3426 +            if (i >= variants.length) {
3427 +                if (i == 0) {
3428 +                    group_variants = "";
3429 +                } else {
3430 +                    group_variants += ",";
3431 +                }
3432 +                if (layout == layouts[i] && variant == "") {
3433 +                    include_keymap = true;
3434 +                    group_id = i;
3435 +                }
3436 +                continue;
3437 +            }
3438 +            if (layout == layouts[i] && variant == variants[i]) {
3439 +                include_keymap = true;
3440 +                group_id = i;
3441 +            }
3442 +
3443 +            if (variants[i] != "") {
3444 +                has_variant = true;
3445 +            }
3446 +
3447 +            if (i == 0) {
3448 +                group_variants = variants[i];
3449 +            } else {
3450 +                group_variants = "%s,%s".printf(group_variants, variants[i]);
3451 +            }
3452 +        }
3453 +
3454 +        if (variant != "") {
3455 +            has_variant = true;
3456 +        }
3457 +
3458 +        if (!include_keymap) {
3459 +            group_layouts = "%s,%s".printf(group_layouts, layout);
3460 +            group_variants = "%s,%s".printf(group_variants, variant);
3461 +            group_id = i;
3462 +        }
3463 +
3464 +        if (!has_variant) {
3465 +            group_variants = null;
3466 +        }
3467 +
3468 +        return {group_layouts, group_variants, group_id.to_string()};
3469 +    }
3470 +
3471 +    public string[] get_variant_from_layout(string layout) {
3472 +        int left_bracket = layout.index_of("(");
3473 +        int right_bracket = layout.index_of(")");
3474 +        if (left_bracket >= 0 && right_bracket > left_bracket) {
3475 +            return {layout[0:left_bracket] +
3476 +                    layout[right_bracket + 1:layout.length],
3477 +                    layout[left_bracket + 1:right_bracket]};
3478 +        }
3479 +        return {layout, "default"};
3480 +    }
3481 +
3482 +    public string[] get_option_from_layout(string layout) {
3483 +        int left_bracket = layout.index_of("[");
3484 +        int right_bracket = layout.index_of("]");
3485 +        if (left_bracket >= 0 && right_bracket > left_bracket) {
3486 +            return {layout[0:left_bracket] +
3487 +                    layout[right_bracket + 1:layout.length],
3488 +                    layout[left_bracket + 1:right_bracket]};
3489 +        }
3490 +        return {layout, "default"};
3491 +    }
3492 +
3493 +    public string get_layout() {
3494 +        if (HAVE_IBUS_XKB) {
3495 +            return get_output_from_cmdline("--get", "layout: ");
3496 +        } else {
3497 +            return get_output_from_cmdline("-query", "layout: ");
3498 +        }
3499 +    }
3500 +
3501 +    public string get_variant() {
3502 +        if (HAVE_IBUS_XKB) {
3503 +            return get_output_from_cmdline("--get", "variant: ");
3504 +        } else {
3505 +            return get_output_from_cmdline("-query", "variant: ");
3506 +        }
3507 +    }
3508 +
3509 +    public string get_option() {
3510 +        if (HAVE_IBUS_XKB) {
3511 +            return get_output_from_cmdline("--get", "option: ");
3512 +        } else {
3513 +            return get_output_from_cmdline("-query", "options: ");
3514 +        }
3515 +    }
3516 +
3517 +    /*
3518 +    public string get_group() {
3519 +        return get_output_from_cmdline("--get-group", "group: ");
3520 +    }
3521 +    */
3522 +
3523 +    public int[] set_layout(string _layout="default",
3524 +                            string _variant="default",
3525 +                            string _option="default") {
3526 +        assert (_layout != null);
3527 +
3528 +        int xkb_group_id = 0;
3529 +        int changed_option = 0;
3530 +
3531 +        if (m_xkb_pid != -1) {
3532 +            return {-1, 0};
3533 +        }
3534 +
3535 +        if (_layout == "default" && _variant == "default" &&
3536 +            _option == "default") {
3537 +            return {-1, 0};
3538 +        }
3539 +        // const gchar to gchar
3540 +        string layout = _layout;
3541 +        string variant = _variant;
3542 +        string option = _option;
3543 +
3544 +        if (variant == "default") {
3545 +            string[] array = get_variant_from_layout(layout);
3546 +            layout = array[0];
3547 +            variant = array[1];
3548 +        }
3549 +
3550 +        if (option == "default") {
3551 +            string[] array = get_option_from_layout(layout);
3552 +            layout = array[0];
3553 +            option = array[1];
3554 +        }
3555 +
3556 +        bool need_us_layout = false;
3557 +        foreach (string latin_layout in m_xkb_latin_layouts) {
3558 +            if (layout == latin_layout && variant != "eng") {
3559 +                need_us_layout = true;
3560 +                break;
3561 +            }
3562 +            if (variant != null &&
3563 +                "%s(%s)".printf(layout, variant) == latin_layout) {
3564 +                need_us_layout = true;
3565 +                break;
3566 +            }
3567 +        }
3568 +
3569 +        int layouts_max_length =  XKB_LAYOUTS_MAX_LENGTH;
3570 +        if (need_us_layout) {
3571 +            layouts_max_length--;
3572 +        }
3573 +
3574 +        if (m_default_layout == "") {
3575 +            m_default_layout = get_layout();
3576 +        }
3577 +        if (m_default_variant  == "") {
3578 +            m_default_variant  = get_variant();
3579 +        }
3580 +        if (m_default_option == "") {
3581 +            m_default_option = get_option();
3582 +        }
3583 +
3584 +        if (layout == "default") {
3585 +            layout = m_default_layout;
3586 +            variant = m_default_variant;
3587 +        } else {
3588 +            if (HAVE_IBUS_GKBD) {
3589 +                if (variant == "default") {
3590 +                    variant = "";
3591 +                }
3592 +                string[] retval = get_xkb_group_layout (layout, variant,
3593 +                                                        layouts_max_length);
3594 +                layout = retval[0];
3595 +                variant = retval[1];
3596 +                xkb_group_id = int.parse(retval[2]);
3597 +            }
3598 +        }
3599 +
3600 +        if (layout == "") {
3601 +            warning("Could not get the correct layout");
3602 +            return {-1, 0};
3603 +        }
3604 +
3605 +        if (variant == "default" || variant == "") {
3606 +            variant = null;
3607 +        }
3608 +
3609 +        if (option == "default" || option == "") {
3610 +            option = m_default_option;
3611 +        } else {
3612 +            if (!(option in m_default_option.split(","))) {
3613 +                option = "%s,%s".printf(m_default_option, option);
3614 +                changed_option = 1;
3615 +            } else {
3616 +                option = m_default_option;
3617 +            }
3618 +        }
3619 +
3620 +        if (option == "") {
3621 +            option = null;
3622 +        }
3623 +
3624 +        if (need_us_layout) {
3625 +            layout += ",us";
3626 +            if (variant != null) {
3627 +                variant += ",";
3628 +            }
3629 +        }
3630 +
3631 +        string[] args = {};
3632 +        args += m_xkb_command;
3633 +        if (HAVE_IBUS_XKB) {
3634 +            args += "--layout";
3635 +            args += layout;
3636 +            if (variant != null) {
3637 +                args += "--variant";
3638 +                args += variant;
3639 +            }
3640 +            if (option != null) {
3641 +                args += "--option";
3642 +                args += option;
3643 +            }
3644 +        } else {
3645 +            args += layout;
3646 +            if (variant != null) {
3647 +                args += variant;
3648 +            }
3649 +            if (option != null) {
3650 +                args += option;
3651 +            }
3652 +        }
3653 +
3654 +        GLib.Pid child_pid;
3655 +        try {
3656 +            GLib.Process.spawn_async(null,
3657 +                                     args,
3658 +                                     null,
3659 +                                     GLib.SpawnFlags.DO_NOT_REAP_CHILD |
3660 +                                         GLib.SpawnFlags.SEARCH_PATH,
3661 +                                     null,
3662 +                                     out child_pid);
3663 +        } catch (GLib.SpawnError err) {
3664 +            stderr.printf("IBUS_ERROR: %s\n", err.message);
3665 +            return {-1, 0};
3666 +        }
3667 +        m_xkb_pid = child_pid;
3668 +        GLib.ChildWatch.add(m_xkb_pid, set_layout_cb);
3669 +
3670 +        return {xkb_group_id, changed_option};
3671 +    }
3672 +
3673 +    public void set_xmodmap() {
3674 +        if (!m_use_xmodmap) {
3675 +            return;
3676 +        }
3677 +
3678 +        if (m_xmodmap_pid != -1) {
3679 +            return;
3680 +        }
3681 +
3682 +        string xmodmap_cmdpath = get_fullpath(m_xmodmap_command);
3683 +        if (xmodmap_cmdpath == "") {
3684 +            xmodmap_cmdpath = m_xmodmap_command;
3685 +        }
3686 +        string homedir = GLib.Environment.get_home_dir();
3687 +        foreach (string xmodmap_file in m_xmodmap_known_files) {
3688 +            string xmodmap_filepath = GLib.Path.build_filename(homedir, xmodmap_file);
3689 +            if (!GLib.FileUtils.test(xmodmap_filepath, GLib.FileTest.EXISTS)) {
3690 +                continue;
3691 +            }
3692 +            string[] args = {xmodmap_cmdpath, xmodmap_filepath};
3693 +
3694 +            GLib.Pid child_pid;
3695 +            try {
3696 +                GLib.Process.spawn_async(null,
3697 +                                         args,
3698 +                                         null,
3699 +                                         GLib.SpawnFlags.DO_NOT_REAP_CHILD |
3700 +                                             GLib.SpawnFlags.SEARCH_PATH,
3701 +                                         null,
3702 +                                         out child_pid);
3703 +            } catch (GLib.SpawnError err) {
3704 +                stderr.printf("IBUS_ERROR: %s\n", err.message);
3705 +                return;
3706 +            }
3707 +            m_xmodmap_pid = child_pid;
3708 +            GLib.ChildWatch.add(m_xmodmap_pid, set_xmodmap_cb);
3709 +
3710 +            break;
3711 +        }
3712 +    }
3713 +
3714 +    public void reset_layout() {
3715 +        m_default_layout = get_layout();
3716 +        m_default_variant = get_variant();
3717 +        m_default_option = get_option();
3718 +    }
3719 +
3720 +    /*
3721 +    public static int main(string[] args) {
3722 +        IBus.Bus bus = new IBus.Bus();
3723 +        IBus.Config config = bus.get_config();
3724 +        XKBLayout xkblayout = new XKBLayout(config);
3725 +        stdout.printf ("layout: %s\n", xkblayout.get_layout());
3726 +        stdout.printf ("variant: %s\n", xkblayout.get_variant());
3727 +        stdout.printf ("option: %s\n", xkblayout.get_option());
3728 +        xkblayout.set_layout("jp");
3729 +        if (config != null) {
3730 +            IBus.main();
3731 +        } else {
3732 +            Gtk.init (ref args);
3733 +            Gtk.main();
3734 +        }
3735 +        return 0;
3736 +    }
3737 +    */
3738 +}
3739 -- 
3740 1.7.10.4
3741
This page took 0.419398 seconds and 3 git commands to generate.