~ubuntu-branches/debian/sid/ibus/sid

« back to all changes in this revision

Viewing changes to debian/patches/ibus-541492-xkb.patch

  • Committer: Package Import Robot
  • Author(s): Osamu Aoki, Aron Xu, Osamu Aoki
  • Date: 2014-08-04 22:42:19 UTC
  • mfrom: (1.5.7)
  • Revision ID: package-import@ubuntu.com-20140804224219-mjprlql1jdwi5sxm
Tags: 1.5.8-1
[ Aron Xu ]
* Allow parallel building

[ Osamu Aoki ]
* Update ibus document with Qt5 support

[ Aron Xu ]
* Imported Upstream version 1.5.8
* Remove patch for chasing HEAD, imported new upstream release
* Remove the patch for updating IBusKeymap jp, applied upstream
* Drop gtk2 version of the password patch because it's never executed
* Drop ibus-541492-xkb.patch
* Drop ibus-530711-preload-sys.patch
* Drop ibus-810211-no-switch-by-no-trigger.patch
* Drop 999-update-po.patch

[ Osamu Aoki ]
* linux-any for ibus-wayland
* Remove some symbols matching ibus-541492-xkb.patch
* Remove old patch for GTK+ 3.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
From bce8b85886075fbdbf28bd5795be8bd41f3a984e Mon Sep 17 00:00:00 2001
2
 
From: fujiwarat <takao.fujiwara1@gmail.com>
3
 
Date: Thu, 13 Mar 2014 15:49:11 +0900
4
 
Subject: [PATCH] Add libgnomekbd and load preload engines.
5
 
 
6
 
---
7
 
 bindings/vala/Gkbd-3.0.metadata |   1 +
8
 
 bindings/vala/Makefile.am       |  19 +-
9
 
 bindings/vala/Xkl-1.0.metadata  |   3 +
10
 
 bindings/vala/gkbd.deps         |   3 +
11
 
 bus/ibusimpl.c                  |  12 +-
12
 
 configure.ac                    |  40 ++++
13
 
 data/ibus.schemas.in            |  59 +++++
14
 
 ibus-1.0.pc.in                  |   1 +
15
 
 ibus.spec.in                    |  12 ++
16
 
 src/Makefile.am                 |   3 +
17
 
 src/ibus.h                      |   1 +
18
 
 src/ibusxkbxml.c                | 466 ++++++++++++++++++++++++++++++++++++++++
19
 
 src/ibusxkbxml.h                | 187 ++++++++++++++++
20
 
 ui/gtk3/Makefile.am             |  36 ++++
21
 
 ui/gtk3/gkbdlayout.vala.false   |  63 ++++++
22
 
 ui/gtk3/gkbdlayout.vala.true    | 108 ++++++++++
23
 
 ui/gtk3/panel.vala              | 230 +++++++++++++++++++-
24
 
 ui/gtk3/xkblayout.vala          | 429 ++++++++++++++++++++++++++++++++++++
25
 
 18 files changed, 1668 insertions(+), 5 deletions(-)
26
 
 create mode 100644 bindings/vala/Gkbd-3.0.metadata
27
 
 create mode 100644 bindings/vala/Xkl-1.0.metadata
28
 
 create mode 100644 bindings/vala/gkbd.deps
29
 
 create mode 100644 src/ibusxkbxml.c
30
 
 create mode 100644 src/ibusxkbxml.h
31
 
 create mode 100644 ui/gtk3/gkbdlayout.vala.false
32
 
 create mode 100644 ui/gtk3/gkbdlayout.vala.true
33
 
 create mode 100644 ui/gtk3/xkblayout.vala
34
 
 
35
 
diff --git a/bindings/vala/Gkbd-3.0.metadata b/bindings/vala/Gkbd-3.0.metadata
36
 
new file mode 100644
37
 
index 0000000..661e6fd
38
 
--- /dev/null
39
 
+++ b/bindings/vala/Gkbd-3.0.metadata
40
 
@@ -0,0 +1 @@
41
 
+Configuration cheader_filename="libgnomekbd/gkbd-configuration.h"
42
 
diff --git a/bindings/vala/Makefile.am b/bindings/vala/Makefile.am
43
 
index 29cc1eb..1d28501 100644
44
 
--- a/bindings/vala/Makefile.am
45
 
+++ b/bindings/vala/Makefile.am
46
 
@@ -28,8 +28,6 @@ vapi_deps = \
47
 
        $(top_builddir)/src/IBus-1.0.gir \
48
 
        $(NULL)
49
 
 
50
 
-ibus-1.0.vapi: $(vapi_deps)
51
 
-
52
 
 VAPIGEN_VAPIS = ibus-1.0.vapi
53
 
 
54
 
 ibus_1_0_vapi_DEPS = gio-2.0
55
 
@@ -39,19 +37,34 @@ ibus_1_0_vapi_FILES = \
56
 
        $(srcdir)/IBus-1.0-custom.vala \
57
 
        $(NULL)
58
 
 
59
 
+if ENABLE_LIBGNOMEKBD
60
 
+ibus-1.0.vapi: $(vapi_deps) gkbd.vapi
61
 
+
62
 
+VAPIGEN_VAPIS += gkbd.vapi
63
 
+
64
 
+gkbd_vapi_DEPS = gtk+-3.0 glib-2.0 gmodule-2.0
65
 
+gkbd_vapi_METADATADIRS = $(srcdir)
66
 
+gkbd_vapi_FILES = /usr/share/gir-1.0/Gkbd-3.0.gir
67
 
+else
68
 
+ibus-1.0.vapi: $(vapi_deps)
69
 
+endif
70
 
+
71
 
 vapidir = $(datadir)/vala/vapi
72
 
-vapi_DATA = $(VAPIGEN_VAPIS) $(VAPIGEN_VAPIS:.vapi=.deps)
73
 
+vapi_DATA = ibus-1.0.vapi ibus-1.0.deps
74
 
 
75
 
 MAINTAINERCLEANFILES = $(VAPIGEN_VAPIS)
76
 
 
77
 
 EXTRA_DIST = \
78
 
        $(VAPIGEN_VAPIS) \
79
 
+       Gkbd-3.0.metadata \
80
 
+       gkbd.deps \
81
 
        IBus-1.0.metadata \
82
 
        IBus-1.0-custom.vala \
83
 
        ibus-1.0.deps \
84
 
        ibus-private.vapi \
85
 
        config.vapi \
86
 
        xi.vapi \
87
 
+       Xkl-1.0.metadata \
88
 
        $(NULL)
89
 
 
90
 
 -include $(top_srcdir)/git.mk
91
 
diff --git a/bindings/vala/Xkl-1.0.metadata b/bindings/vala/Xkl-1.0.metadata
92
 
new file mode 100644
93
 
index 0000000..4961d0c
94
 
--- /dev/null
95
 
+++ b/bindings/vala/Xkl-1.0.metadata
96
 
@@ -0,0 +1,3 @@
97
 
+Xkl cheader_filename="libxklavier/xklavier.h"
98
 
+Engine
99
 
+    .filter_events.evt ref type="X.Event"
100
 
diff --git a/bindings/vala/gkbd.deps b/bindings/vala/gkbd.deps
101
 
new file mode 100644
102
 
index 0000000..172632c
103
 
--- /dev/null
104
 
+++ b/bindings/vala/gkbd.deps
105
 
@@ -0,0 +1,3 @@
106
 
+gtk+-3.0
107
 
+glib-2.0
108
 
+gmodule-2.0
109
 
diff --git a/bus/ibusimpl.c b/bus/ibusimpl.c
110
 
index eec6da3..f84c034 100644
111
 
--- a/bus/ibusimpl.c
112
 
+++ b/bus/ibusimpl.c
113
 
@@ -1135,7 +1135,17 @@ _ibus_get_engines_by_names (BusIBusImpl           *ibus,
114
 
     g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
115
 
     while (names[i] != NULL) {
116
 
         IBusEngineDesc *desc = (IBusEngineDesc *) g_hash_table_lookup (
117
 
-                ibus->engine_table, names[i++]);
118
 
+                ibus->engine_table, names[i]);
119
 
+
120
 
+        /* preload engines return user XKB so if the engine does not
121
 
+         * exist in simple.xml, fall back to 'us' layout. */
122
 
+        if (desc == NULL && g_str_has_prefix (names[i], "xkb:")) {
123
 
+            desc = (IBusEngineDesc *) g_hash_table_lookup (
124
 
+                    ibus->engine_table, "xkb:us::eng");
125
 
+        }
126
 
+
127
 
+        i++;
128
 
+
129
 
         if (desc == NULL)
130
 
             continue;
131
 
         g_variant_builder_add (
132
 
diff --git a/configure.ac b/configure.ac
133
 
index 9a502ec..3ec629d 100644
134
 
--- a/configure.ac
135
 
+++ b/configure.ac
136
 
@@ -261,6 +261,45 @@ else
137
 
     enable_wayland="no (disabled, use --enable-wayland to enable)"
138
 
 fi
139
 
 
140
 
+# Option for XKB command.
141
 
+PKG_CHECK_MODULES(XKB,
142
 
+    [xkbfile],,
143
 
+    [XKB_LIBS="-lxkbfile"]
144
 
+)
145
 
+
146
 
+# --enable-libgnomekbd option.
147
 
+AC_ARG_ENABLE(libgnomekbd,
148
 
+    AS_HELP_STRING([--enable-libgnomekbd],
149
 
+                   [Use libgnomekbd to handle the keymaps]),
150
 
+    [enable_libgnomekbd=$enableval],
151
 
+    [enable_libgnomekbd=no]
152
 
+)
153
 
+AM_CONDITIONAL([ENABLE_LIBGNOMEKBD], [test x"$enable_libgnomekbd" = x"yes"])
154
 
+if test x"$enable_libgnomekbd" = x"yes"; then
155
 
+    # check for libgnomekbd
156
 
+    PKG_CHECK_MODULES(LIBGNOMEKBDUI, [
157
 
+        libgnomekbdui
158
 
+    ])
159
 
+    PKG_CHECK_MODULES(ATK, [
160
 
+        atk
161
 
+    ])
162
 
+    HAVE_IBUS_GKBD=true
163
 
+else
164
 
+    enable_libgnomekbd="no (disabled, use --enable-libgnomekbd to enable)"
165
 
+    HAVE_IBUS_GKBD=false
166
 
+fi
167
 
+AC_SUBST(HAVE_IBUS_GKBD)
168
 
+
169
 
+# Define XKB rules file
170
 
+AC_ARG_WITH(xkb-rules-xml,
171
 
+    AS_HELP_STRING([--with-xkb-rules-xml[=$DIR/evdev.xml]],
172
 
+        [Set evdev.xml file path (default: /usr/share/X11/xkb/rules/evdev.xml)]),
173
 
+    XKB_RULES_XML_FILE=$with_xkb_rules_xml,
174
 
+    XKB_RULES_XML_FILE="/usr/share/X11/xkb/rules/evdev.xml"
175
 
+)
176
 
+AC_DEFINE_UNQUOTED(XKB_RULES_XML_FILE, "$XKB_RULES_XML_FILE",
177
 
+    [Define file path of evdev.xml])
178
 
+
179
 
 # GObject introspection
180
 
 GOBJECT_INTROSPECTION_CHECK([0.6.8])
181
 
 
182
 
@@ -640,6 +679,7 @@ Build options:
183
 
   Panel icon                "$IBUS_ICON_KEYBOARD"
184
 
   Enable surrounding-text   $enable_surrounding_text
185
 
   Enable libnotify          $enable_libnotify
186
 
+  Build libgnomebkd         $enable_libgnomekbd
187
 
   Run test cases            $enable_tests
188
 
 ])
189
 
 
190
 
diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in
191
 
index 2f76ce3..caec315 100644
192
 
--- a/data/ibus.schemas.in
193
 
+++ b/data/ibus.schemas.in
194
 
@@ -56,6 +56,52 @@
195
 
       </locale>
196
 
     </schema>
197
 
     <schema>
198
 
+      <key>/schemas/desktop/ibus/general/use_xmodmap</key>
199
 
+      <applyto>/desktop/ibus/general/use_xmodmap</applyto>
200
 
+      <owner>ibus</owner>
201
 
+      <type>bool</type>
202
 
+      <default>true</default>
203
 
+      <locale name="C">
204
 
+        <short>Use xmodmap</short>
205
 
+           <long>Run xmodmap if .xmodmap/.Xmodmap exists.</long>
206
 
+      </locale>
207
 
+    </schema>
208
 
+    <schema>
209
 
+      <key>/schemas/desktop/ibus/general/xkb_latin_layouts</key>
210
 
+      <applyto>/desktop/ibus/general/xkb_latin_layouts</applyto>
211
 
+      <owner>ibus</owner>
212
 
+      <type>list</type>
213
 
+      <list_type>string</list_type>
214
 
+      <default>[ara,bg,cz,dev,gr,gur,in,jp(kana),mal,mkd,ru,ua]</default>
215
 
+      <locale name="C">
216
 
+        <short>Latin layout which have no ASCII</short>
217
 
+           <long>us layout is appended to the latin layouts. variant is not needed.</long>
218
 
+      </locale>
219
 
+    </schema>
220
 
+    <schema>
221
 
+      <key>/schemas/desktop/ibus/general/load_xkb_layouts</key>
222
 
+      <applyto>/desktop/ibus/general/load_xkb_layouts</applyto>
223
 
+      <owner>ibus</owner>
224
 
+      <type>list</type>
225
 
+      <list_type>string</list_type>
226
 
+      <default>[us,us(chr),us(dvorak),ad,al,am,ara,az,ba,bd,be,bg,br,bt,by,
227
 
+de,dk,ca,ch,cn(tib),cz,ee,epo,es,et,fi,fo,fr,
228
 
+gb,ge,ge(dsb),ge(ru),ge(os),gh,gh(akan),gh(ewe),gh(fula),gh(ga),gh(hausa),
229
 
+gn,gr,hu,hr,ie,ie(CloGaelach),il,
230
 
+in,
231
 
+in(tel),in(bolnagri),iq,iq(ku),ir,ir(ku),is,it,jp,
232
 
+kg,kh,kz,la,latam,lk,lk(tam_unicode),lt,lv,ma,ma(tifinagh),mal,mao,
233
 
+me,mk,mm,mt,mv,ng,ng(hausa),ng,ng(igbo),ng(yoruba),nl,no,no(smi),np,
234
 
+pk,pl,pl(csb),pt,ro,rs,ru,ru(cv),ru(kom),ru(sah),ru(tt),ru(xal),
235
 
+se,si,sk,sy,sy(ku),th,tj,tr,ua,uz,vn
236
 
+]</default>
237
 
+      <locale name="C">
238
 
+        <short>XKB layout list which is shown on ibus-setup</short>
239
 
+           <long>XKB layout list which is shown on ibus-setup.
240
 
+                 The format is "layout" or "layout(variant)".</long>
241
 
+      </locale>
242
 
+    </schema>
243
 
+    <schema>
244
 
       <key>/schemas/desktop/ibus/general/hotkey/trigger</key>
245
 
       <applyto>/desktop/ibus/general/hotkey/trigger</applyto>
246
 
       <owner>ibus</owner>
247
 
@@ -80,6 +126,19 @@
248
 
       </locale>
249
 
     </schema>
250
 
     <schema>
251
 
+      <key>/schemas/desktop/ibus/general/hotkey/triggers-no-modifiers</key>
252
 
+      <applyto>/desktop/ibus/general/hotkey/triggers-no-modifiers</applyto>
253
 
+      <owner>ibus</owner>
254
 
+      <type>list</type>
255
 
+      <list_type>string</list_type>
256
 
+      <default>[]</default>
257
 
+      <locale name="C">
258
 
+        <short>Trigger shortcut keys without modifier keys</short>
259
 
+          <long>Trigger shortcut keys without modifier keys.
260
 
+                The list is used by ibus-gjs.</long>
261
 
+      </locale>
262
 
+    </schema>
263
 
+    <schema>
264
 
       <key>/schemas/desktop/ibus/general/hotkey/enable_unconditional</key>
265
 
       <applyto>/desktop/ibus/general/hotkey/enable_unconditional</applyto>
266
 
       <owner>ibus</owner>
267
 
diff --git a/ibus-1.0.pc.in b/ibus-1.0.pc.in
268
 
index 9f593ab..c93a0ed 100644
269
 
--- a/ibus-1.0.pc.in
270
 
+++ b/ibus-1.0.pc.in
271
 
@@ -4,6 +4,7 @@ libdir=@libdir@
272
 
 includedir=@includedir@
273
 
 datadir=@datadir@
274
 
 pkgdatadir=@datadir@/ibus
275
 
+have_ibus_gkbd=@HAVE_IBUS_GKBD@
276
 
 
277
 
 Name: IBus
278
 
 Description: IBus Library
279
 
diff --git a/ibus.spec.in b/ibus.spec.in
280
 
index 334f37e..2017af9 100644
281
 
--- a/ibus.spec.in
282
 
+++ b/ibus.spec.in
283
 
@@ -5,6 +5,7 @@
284
 
 
285
 
 # Build flags
286
 
 %define build_python_library 0
287
 
+%define build_libgnomekbd 0
288
 
 
289
 
 %define glib_ver %([ -a %{_libdir}/pkgconfig/glib-2.0.pc ] && pkg-config --modversion glib-2.0 | cut -d. -f 1,2 || echo -n "999")
290
 
 %define gconf2_version 2.12.0
291
 
@@ -40,6 +41,10 @@ BuildRequires:  dconf-devel
292
 
 BuildRequires:  pygobject2-devel
293
 
 BuildRequires:  intltool
294
 
 BuildRequires:  iso-codes-devel
295
 
+%if %{build_libgnomekbd}
296
 
+BuildRequires:  libxkbfile-devel
297
 
+BuildRequires:  libgnomekbd-devel
298
 
+%endif
299
 
 
300
 
 Requires:   %{name}-libs = %{version}-%{release}
301
 
 Requires:   %{name}-gtk2 = %{version}-%{release}
302
 
@@ -52,6 +57,9 @@ Requires:   dbus-python >= %{dbus_python_version}
303
 
 Requires:   im-chooser >= %{im_chooser_version}
304
 
 Requires:   notify-python
305
 
 Requires:   librsvg2
306
 
+%if %{build_libgnomekbd}
307
 
+Requires:   libgnomekbd
308
 
+%endif
309
 
 
310
 
 Requires(post):  desktop-file-utils
311
 
 Requires(postun):  desktop-file-utils
312
 
@@ -152,6 +160,10 @@ OPTIONS="$OPTIONS --enable-python-library"
313
 
 OPTIONS="$OPTIONS --disable-python-library"
314
 
 %endif
315
 
 
316
 
+%if %{build_libgnomekbd}
317
 
+OPTIONS="$OPTIONS --enable-libgnomekbd"
318
 
+%endif
319
 
+
320
 
 %configure $OPTIONS
321
 
 
322
 
 # make -C po update-gmo
323
 
diff --git a/src/Makefile.am b/src/Makefile.am
324
 
index 404e1d2..f00fab7 100644
325
 
--- a/src/Makefile.am
326
 
+++ b/src/Makefile.am
327
 
@@ -202,6 +202,9 @@ typelibs_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
328
 
 CLEANFILES += $(dist_gir_DATA) $(typelibs_DATA)
329
 
 endif
330
 
 
331
 
+ibus_sources += ibusxkbxml.c
332
 
+ibus_headers += ibusxkbxml.h
333
 
+
334
 
 # gen enum types
335
 
 ibusenumtypes.h: $(ibus_headers) ibusenumtypes.h.template
336
 
        $(AM_V_GEN) ( top_builddir=`cd $(top_builddir) && pwd`; \
337
 
diff --git a/src/ibus.h b/src/ibus.h
338
 
index d8e226e..f0a9456 100644
339
 
--- a/src/ibus.h
340
 
+++ b/src/ibus.h
341
 
@@ -47,6 +47,7 @@
342
 
 #include <ibuskeys.h>
343
 
 #include <ibusenumtypes.h>
344
 
 #include <ibushotkey.h>
345
 
+#include <ibusxkbxml.h>
346
 
 #include <ibusxml.h>
347
 
 #include <ibusenginedesc.h>
348
 
 #include <ibusobservedpath.h>
349
 
diff --git a/src/ibusxkbxml.c b/src/ibusxkbxml.c
350
 
new file mode 100644
351
 
index 0000000..f815e5d
352
 
--- /dev/null
353
 
+++ b/src/ibusxkbxml.c
354
 
@@ -0,0 +1,466 @@
355
 
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
356
 
+/* vim:set et sts=4: */
357
 
+/* bus - The Input Bus
358
 
+ * Copyright (C) 2013 Takao Fujiwara <takao.fujiwara1@gmail.com>
359
 
+ * Copyright (C) 2013 Peng Huang <shawn.p.huang@gmail.com>
360
 
+ * Copyright (C) 2013 Red Hat, Inc.
361
 
+ *
362
 
+ * This library is free software; you can redistribute it and/or
363
 
+ * modify it under the terms of the GNU Lesser General Public
364
 
+ * License as published by the Free Software Foundation; either
365
 
+ * version 2 of the License, or (at your option) any later version.
366
 
+ *
367
 
+ * This library is distributed in the hope that it will be useful,
368
 
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
369
 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
370
 
+ * Lesser General Public License for more details.
371
 
+ *
372
 
+ * You should have received a copy of the GNU Lesser General Public
373
 
+ * License along with this library; if not, write to the
374
 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
375
 
+ * Boston, MA 02111-1307, USA.
376
 
+ */
377
 
+#ifdef HAVE_CONFIG_H
378
 
+#include <config.h>
379
 
+#endif
380
 
+
381
 
+#include <glib.h>
382
 
+
383
 
+#include "ibus.h"
384
 
+#include "ibusxkbxml.h"
385
 
+
386
 
+#ifndef XKB_RULES_XML_FILE
387
 
+#define XKB_RULES_XML_FILE "/usr/share/X11/xkb/rules/evdev.xml"
388
 
+#endif
389
 
+
390
 
+#define IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE(o)  \
391
 
+   (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistryPrivate))
392
 
+
393
 
+typedef struct _IBusXKBConfigRegistryPrivate IBusXKBConfigRegistryPrivate;
394
 
+
395
 
+struct _IBusXKBConfigRegistryPrivate {
396
 
+    GHashTable *layout_list;
397
 
+    GHashTable *layout_lang;
398
 
+    GHashTable *layout_desc;
399
 
+    GHashTable *variant_desc;
400
 
+};
401
 
+
402
 
+
403
 
+/* functions prototype */
404
 
+static void         ibus_xkb_config_registry_destroy
405
 
+                                           (IBusXKBConfigRegistry *xkb_config);
406
 
+
407
 
+G_DEFINE_TYPE (IBusXKBConfigRegistry, ibus_xkb_config_registry, IBUS_TYPE_OBJECT)
408
 
+
409
 
+static void
410
 
+parse_xkb_xml_languagelist_node (IBusXKBConfigRegistryPrivate *priv,
411
 
+                                 XMLNode *parent_node,
412
 
+                                 const gchar *layout_name)
413
 
+{
414
 
+    XMLNode *node = parent_node;
415
 
+    XMLNode *sub_node;
416
 
+    GList *p;
417
 
+    GList *lang_list = NULL;
418
 
+
419
 
+    g_assert (node != NULL);
420
 
+    g_assert (layout_name != NULL);
421
 
+    for (p = node->sub_nodes; p; p = p->next) {
422
 
+        sub_node = (XMLNode *) p->data;
423
 
+        if (g_strcmp0 (sub_node->name, "iso639Id") == 0) {
424
 
+            lang_list = g_list_append (lang_list,
425
 
+                                       (gpointer) g_strdup (sub_node->text));
426
 
+            continue;
427
 
+        }
428
 
+    }
429
 
+    if (lang_list == NULL) {
430
 
+        /* some nodes have no lang */
431
 
+        return;
432
 
+    }
433
 
+    if (g_hash_table_lookup (priv->layout_lang, layout_name) != NULL) {
434
 
+        g_warning ("duplicated name %s exists", layout_name);
435
 
+        return;
436
 
+    }
437
 
+    g_hash_table_insert (priv->layout_lang,
438
 
+                         (gpointer) g_strdup (layout_name),
439
 
+                         (gpointer) lang_list);
440
 
+}
441
 
+
442
 
+static const gchar *
443
 
+parse_xkb_xml_configitem_node (IBusXKBConfigRegistryPrivate *priv,
444
 
+                               XMLNode *parent_node)
445
 
+{
446
 
+    XMLNode *node = parent_node;
447
 
+    XMLNode *sub_node;
448
 
+    GList *p;
449
 
+    gchar *name = NULL;
450
 
+    gchar *description = NULL;
451
 
+
452
 
+    g_assert (node != NULL);
453
 
+    for (p = node->sub_nodes; p; p = p->next) {
454
 
+        sub_node = (XMLNode *) p->data;
455
 
+        if (g_strcmp0 (sub_node->name, "name") == 0) {
456
 
+            name = sub_node->text;
457
 
+            continue;
458
 
+        }
459
 
+        if (g_strcmp0 (sub_node->name, "description") == 0) {
460
 
+            description = sub_node->text;
461
 
+            continue;
462
 
+        }
463
 
+        if (g_strcmp0 (sub_node->name, "languageList") == 0) {
464
 
+            if (name == NULL) {
465
 
+                g_warning ("layout name is NULL in node %s", node->name);
466
 
+                continue;
467
 
+            }
468
 
+            parse_xkb_xml_languagelist_node (priv, sub_node, name);
469
 
+            continue;
470
 
+        }
471
 
+    }
472
 
+    if (name == NULL) {
473
 
+        g_warning ("No name in layout node");
474
 
+        return NULL;
475
 
+    }
476
 
+    if (g_hash_table_lookup (priv->layout_desc, name) != NULL) {
477
 
+        g_warning ("duplicated name %s exists", name);
478
 
+        return name;
479
 
+    }
480
 
+    g_hash_table_insert (priv->layout_desc,
481
 
+                         (gpointer) g_strdup (name),
482
 
+                         (gpointer) g_strdup (description));
483
 
+
484
 
+    return name;
485
 
+}
486
 
+
487
 
+static const gchar *
488
 
+parse_xkb_xml_variant_configitem_node (IBusXKBConfigRegistryPrivate *priv,
489
 
+                            XMLNode *parent_node,
490
 
+                            const gchar *layout_name)
491
 
+{
492
 
+    XMLNode *node = parent_node;
493
 
+    XMLNode *sub_node;
494
 
+    GList *p;
495
 
+    gchar *name = NULL;
496
 
+    gchar *description = NULL;
497
 
+    gchar *variant_lang_name = NULL;
498
 
+
499
 
+    g_assert (node != NULL);
500
 
+    g_assert (layout_name != NULL);
501
 
+    for (p = node->sub_nodes; p; p = p->next) {
502
 
+        sub_node = (XMLNode *) p->data;
503
 
+        if (g_strcmp0 (sub_node->name, "name") == 0) {
504
 
+            name = sub_node->text;
505
 
+            continue;
506
 
+        }
507
 
+        if (g_strcmp0 (sub_node->name, "description") == 0) {
508
 
+            description = sub_node->text;
509
 
+            continue;
510
 
+        }
511
 
+        if (g_strcmp0 (sub_node->name, "languageList") == 0) {
512
 
+            if (name == NULL) {
513
 
+                g_warning ("layout name is NULL in node %s", node->name);
514
 
+                continue;
515
 
+            }
516
 
+            variant_lang_name = g_strdup_printf ("%s(%s)", layout_name, name);
517
 
+            parse_xkb_xml_languagelist_node (priv, sub_node, variant_lang_name);
518
 
+            g_free (variant_lang_name);
519
 
+            continue;
520
 
+        }
521
 
+    }
522
 
+    if (name == NULL) {
523
 
+        g_warning ("No name in layout node");
524
 
+        return NULL;
525
 
+    }
526
 
+    if (g_hash_table_lookup (priv->variant_desc, name) != NULL) {
527
 
+        /* This is an expected case. */
528
 
+        return name;
529
 
+    }
530
 
+    variant_lang_name = g_strdup_printf ("%s(%s)", layout_name, name);
531
 
+    g_hash_table_insert (priv->variant_desc,
532
 
+                         (gpointer) variant_lang_name,
533
 
+                         (gpointer) g_strdup (description));
534
 
+    return name;
535
 
+}
536
 
+
537
 
+static const gchar *
538
 
+parse_xkb_xml_variant_node (IBusXKBConfigRegistryPrivate *priv,
539
 
+                            XMLNode *parent_node,
540
 
+                            const gchar *layout_name)
541
 
+{
542
 
+    XMLNode *node = parent_node;
543
 
+    XMLNode *sub_node;
544
 
+    GList *p;
545
 
+    const gchar *variant_name = NULL;
546
 
+
547
 
+    g_assert (node != NULL);
548
 
+    g_assert (layout_name != NULL);
549
 
+    for (p = node->sub_nodes; p; p = p->next) {
550
 
+        sub_node = (XMLNode *) p->data;
551
 
+        if (g_strcmp0 (sub_node->name, "configItem") == 0) {
552
 
+            variant_name = parse_xkb_xml_variant_configitem_node (priv, sub_node, layout_name);
553
 
+            continue;
554
 
+        }
555
 
+    }
556
 
+    return variant_name;
557
 
+}
558
 
+
559
 
+static GList *
560
 
+parse_xkb_xml_variantlist_node (IBusXKBConfigRegistryPrivate *priv,
561
 
+                                XMLNode *parent_node,
562
 
+                                const gchar *layout_name,
563
 
+                                GList *variant_list)
564
 
+{
565
 
+    XMLNode *node = parent_node;
566
 
+    XMLNode *sub_node;
567
 
+    GList *p;
568
 
+    const gchar *variant_name = NULL;
569
 
+
570
 
+    g_assert (node != NULL);
571
 
+    g_assert (layout_name != NULL);
572
 
+    for (p = node->sub_nodes; p; p = p->next) {
573
 
+        sub_node = (XMLNode *) p->data;
574
 
+        if (g_strcmp0 (sub_node->name, "variant") == 0) {
575
 
+            variant_name = parse_xkb_xml_variant_node (priv, sub_node, layout_name);
576
 
+            if (variant_name != NULL) {
577
 
+                variant_list = g_list_append (variant_list,
578
 
+                                              (gpointer) g_strdup (variant_name));
579
 
+            }
580
 
+            continue;
581
 
+        }
582
 
+    }
583
 
+    return variant_list;
584
 
+}
585
 
+
586
 
+static void
587
 
+parse_xkb_xml_layout_node (IBusXKBConfigRegistryPrivate *priv,
588
 
+                           XMLNode *parent_node)
589
 
+{
590
 
+    XMLNode *node = parent_node;
591
 
+    XMLNode *sub_node;
592
 
+    GList *p;
593
 
+    const gchar *name = NULL;
594
 
+    GList *variant_list = NULL;
595
 
+
596
 
+    g_assert (node != NULL);
597
 
+    for (p = node->sub_nodes; p; p = p->next) {
598
 
+        sub_node = (XMLNode *) p->data;
599
 
+        if (g_strcmp0 (sub_node->name, "configItem") == 0) {
600
 
+            name = parse_xkb_xml_configitem_node (priv, sub_node);
601
 
+            continue;
602
 
+        }
603
 
+        if (g_strcmp0 (sub_node->name, "variantList") == 0) {
604
 
+            if (name == NULL) {
605
 
+                g_warning ("layout name is NULL in node %s", node->name);
606
 
+                continue;
607
 
+            }
608
 
+            variant_list = parse_xkb_xml_variantlist_node (priv, sub_node,
609
 
+                                                           name,
610
 
+                                                           variant_list);
611
 
+            continue;
612
 
+        }
613
 
+    }
614
 
+    if (g_hash_table_lookup (priv->layout_list, name) != NULL) {
615
 
+        g_warning ("duplicated name %s exists", name);
616
 
+        return;
617
 
+    }
618
 
+    g_hash_table_insert (priv->layout_list,
619
 
+                         (gpointer) g_strdup (name),
620
 
+                         (gpointer) variant_list);
621
 
+}
622
 
+
623
 
+static void
624
 
+parse_xkb_xml_top_node (IBusXKBConfigRegistryPrivate *priv,
625
 
+                        XMLNode *parent_node)
626
 
+{
627
 
+    XMLNode *node = parent_node;
628
 
+    XMLNode *sub_node;
629
 
+    GList *p;
630
 
+
631
 
+    g_assert (priv != NULL);
632
 
+    g_assert (node != NULL);
633
 
+
634
 
+    if (g_strcmp0 (node->name, "xkbConfigRegistry") != 0) {
635
 
+        g_warning ("node has no xkbConfigRegistry name");
636
 
+        return;
637
 
+    }
638
 
+    for (p = node->sub_nodes; p; p = p->next) {
639
 
+        sub_node = (XMLNode *) p->data;
640
 
+        if (g_strcmp0 (sub_node->name, "layoutList") == 0) {
641
 
+            break;
642
 
+        }
643
 
+    }
644
 
+    if (p == NULL) {
645
 
+        g_warning ("xkbConfigRegistry node has no layoutList node");
646
 
+        return;
647
 
+    }
648
 
+    node = sub_node;
649
 
+    for (p = node->sub_nodes; p; p = p->next) {
650
 
+        sub_node = (XMLNode *) p->data;
651
 
+        if (g_strcmp0 (sub_node->name, "layout") == 0) {
652
 
+            parse_xkb_xml_layout_node (priv, sub_node);
653
 
+            continue;
654
 
+        }
655
 
+    }
656
 
+}
657
 
+
658
 
+static void
659
 
+free_lang_list (GList *list)
660
 
+{
661
 
+    GList *l = list;
662
 
+    while (l) {
663
 
+        g_free (l->data);
664
 
+        l->data = NULL;
665
 
+        l = l->next;
666
 
+    }
667
 
+    g_list_free (list);
668
 
+}
669
 
+
670
 
+static void
671
 
+parse_xkb_config_registry_file (IBusXKBConfigRegistryPrivate *priv,
672
 
+                                const gchar *file)
673
 
+{
674
 
+    XMLNode *node;
675
 
+
676
 
+    g_assert (file != NULL);
677
 
+
678
 
+    priv->layout_list = g_hash_table_new_full (g_str_hash,
679
 
+                                               (GEqualFunc) g_str_equal,
680
 
+                                               (GDestroyNotify) g_free,
681
 
+                                               (GDestroyNotify) free_lang_list);
682
 
+    priv->layout_desc = g_hash_table_new_full (g_str_hash,
683
 
+                                               (GEqualFunc) g_str_equal,
684
 
+                                               (GDestroyNotify) g_free,
685
 
+                                               (GDestroyNotify) g_free);
686
 
+    priv->layout_lang = g_hash_table_new_full (g_str_hash,
687
 
+                                               (GEqualFunc) g_str_equal,
688
 
+                                               (GDestroyNotify) g_free,
689
 
+                                               (GDestroyNotify) free_lang_list);
690
 
+    priv->variant_desc = g_hash_table_new_full (g_str_hash,
691
 
+                                               (GEqualFunc) g_str_equal,
692
 
+                                               (GDestroyNotify) g_free,
693
 
+                                               (GDestroyNotify) g_free);
694
 
+    node = ibus_xml_parse_file (file);
695
 
+    parse_xkb_xml_top_node (priv, node);
696
 
+    ibus_xml_free (node);
697
 
+}
698
 
+
699
 
+static void
700
 
+ibus_xkb_config_registry_init (IBusXKBConfigRegistry *xkb_config)
701
 
+{
702
 
+    IBusXKBConfigRegistryPrivate *priv;
703
 
+    const gchar *file = XKB_RULES_XML_FILE;
704
 
+
705
 
+    priv = IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE (xkb_config);
706
 
+    parse_xkb_config_registry_file (priv, file);
707
 
+}
708
 
+
709
 
+static void
710
 
+ibus_xkb_config_registry_destroy (IBusXKBConfigRegistry *xkb_config)
711
 
+{
712
 
+    IBusXKBConfigRegistryPrivate *priv;
713
 
+
714
 
+    g_return_if_fail (xkb_config != NULL);
715
 
+
716
 
+    priv = IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE (xkb_config);
717
 
+
718
 
+    g_hash_table_destroy (priv->layout_list);
719
 
+    priv->layout_list = NULL;
720
 
+    g_hash_table_destroy (priv->layout_lang);
721
 
+    priv->layout_lang= NULL;
722
 
+    g_hash_table_destroy (priv->layout_desc);
723
 
+    priv->layout_desc= NULL;
724
 
+    g_hash_table_destroy (priv->variant_desc);
725
 
+    priv->variant_desc = NULL;
726
 
+
727
 
+    IBUS_OBJECT_CLASS(ibus_xkb_config_registry_parent_class)->destroy (IBUS_OBJECT (xkb_config));
728
 
+}
729
 
+
730
 
+static void
731
 
+ibus_xkb_config_registry_class_init (IBusXKBConfigRegistryClass *klass)
732
 
+{
733
 
+    IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass);
734
 
+
735
 
+    g_type_class_add_private (klass, sizeof (IBusXKBConfigRegistryPrivate));
736
 
+
737
 
+    ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_xkb_config_registry_destroy;
738
 
+}
739
 
+
740
 
+IBusXKBConfigRegistry *
741
 
+ibus_xkb_config_registry_new (void)
742
 
+{
743
 
+    IBusXKBConfigRegistry *xkb_config;
744
 
+
745
 
+    xkb_config = IBUS_XKB_CONFIG_REGISTRY (g_object_new (IBUS_TYPE_XKB_CONFIG_REGISTRY, NULL));
746
 
+    return xkb_config;
747
 
+}
748
 
+
749
 
+#define TABLE_FUNC(field_name) const GHashTable *                       \
750
 
+ibus_xkb_config_registry_get_##field_name  (IBusXKBConfigRegistry *xkb_config) \
751
 
+{                                                                       \
752
 
+    IBusXKBConfigRegistryPrivate *priv;                                 \
753
 
+                                                                        \
754
 
+    g_return_val_if_fail (xkb_config != NULL, NULL);                    \
755
 
+    priv = IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE (xkb_config);           \
756
 
+    return priv->field_name;                                            \
757
 
+}
758
 
+
759
 
+TABLE_FUNC (layout_list)
760
 
+TABLE_FUNC (layout_lang)
761
 
+TABLE_FUNC (layout_desc)
762
 
+TABLE_FUNC (variant_desc)
763
 
+
764
 
+#undef TABLE_FUNC
765
 
+
766
 
+GList *
767
 
+ibus_xkb_config_registry_layout_list_get_layouts (IBusXKBConfigRegistry *xkb_config)
768
 
+{
769
 
+    GHashTable *table;
770
 
+    GList *list = NULL;
771
 
+
772
 
+    table = (GHashTable *)
773
 
+        ibus_xkb_config_registry_get_layout_list (xkb_config);
774
 
+    list = (GList *) g_hash_table_get_keys (table);
775
 
+    return list;
776
 
+}
777
 
+
778
 
+/* vala could use GLib.List<string> for the returned pointer and
779
 
+ * the declaration calls g_list_foreach (retval, g_free, NULL).
780
 
+ * When I think about GLib.List<string> v.s. GLib.List, probably
781
 
+ * I think GLib.List<string> is better for the function and set
782
 
+ * g_strdup() here. I do not know about GJS implementation.
783
 
+ */
784
 
+#define TABLE_LOOKUP_LIST_FUNC(field_name, value) GList *               \
785
 
+ibus_xkb_config_registry_##field_name##_get_##value  (IBusXKBConfigRegistry *xkb_config, const gchar *key) \
786
 
+{                                                                       \
787
 
+    GHashTable *table;                                                  \
788
 
+    GList *list = NULL;                                                 \
789
 
+    GList *retval= NULL;                                                \
790
 
+    GList *p = NULL;                                                    \
791
 
+                                                                        \
792
 
+    table = (GHashTable *)                                              \
793
 
+        ibus_xkb_config_registry_get_##field_name (xkb_config);         \
794
 
+    list = (GList *) g_hash_table_lookup (table, key);                  \
795
 
+    retval = g_list_copy (list);                                        \
796
 
+    for (p = retval; p; p = p->next) {                                  \
797
 
+        p->data = g_strdup (p->data);                                   \
798
 
+    }                                                                   \
799
 
+    return retval;                                                      \
800
 
+}
801
 
+
802
 
+#define TABLE_LOOKUP_STRING_FUNC(field_name, value) gchar *             \
803
 
+ibus_xkb_config_registry_##field_name##_get_##value  (IBusXKBConfigRegistry *xkb_config, const gchar *key) \
804
 
+{                                                                       \
805
 
+    GHashTable *table;                                                  \
806
 
+    const gchar *desc = NULL;                                           \
807
 
+                                                                        \
808
 
+    table = (GHashTable *)                                              \
809
 
+        ibus_xkb_config_registry_get_##field_name (xkb_config);         \
810
 
+    desc = (const gchar *) g_hash_table_lookup (table, key);            \
811
 
+    return g_strdup (desc);                                             \
812
 
+}
813
 
+
814
 
+TABLE_LOOKUP_LIST_FUNC (layout_list, variants)
815
 
+TABLE_LOOKUP_LIST_FUNC (layout_lang, langs)
816
 
+TABLE_LOOKUP_STRING_FUNC (layout_desc, desc)
817
 
+TABLE_LOOKUP_STRING_FUNC (variant_desc, desc)
818
 
+
819
 
+#undef TABLE_LOOKUP_LIST_FUNC
820
 
+#undef TABLE_LOOKUP_STRING_FUNC
821
 
diff --git a/src/ibusxkbxml.h b/src/ibusxkbxml.h
822
 
new file mode 100644
823
 
index 0000000..5aa486d
824
 
--- /dev/null
825
 
+++ b/src/ibusxkbxml.h
826
 
@@ -0,0 +1,187 @@
827
 
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
828
 
+/* vim:set et sts=4: */
829
 
+/* bus - The Input Bus
830
 
+ * Copyright (C) 2013 Takao Fujiwara <takao.fujiwara1@gmail.com>
831
 
+ * Copyright (C) 2013 Peng Huang <shawn.p.huang@gmail.com>
832
 
+ * Copyright (C) 2013 Red Hat, Inc.
833
 
+ *
834
 
+ * This library is free software; you can redistribute it and/or
835
 
+ * modify it under the terms of the GNU Lesser General Public
836
 
+ * License as published by the Free Software Foundation; either
837
 
+ * version 2 of the License, or (at your option) any later version.
838
 
+ *
839
 
+ * This library is distributed in the hope that it will be useful,
840
 
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
841
 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
842
 
+ * Lesser General Public License for more details.
843
 
+ *
844
 
+ * You should have received a copy of the GNU Lesser General Public
845
 
+ * License along with this library; if not, write to the
846
 
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
847
 
+ * Boston, MA 02111-1307, USA.
848
 
+ */
849
 
+#ifndef __IBUS_XKBXML_H_
850
 
+#define __IBUS_XKBXML_H_
851
 
+
852
 
+#if !defined (__IBUS_H_INSIDE__) && !defined (IBUS_COMPILATION)
853
 
+#error "Only <ibus.h> can be included directly"
854
 
+#endif
855
 
+
856
 
+#include "ibus.h"
857
 
+
858
 
+/*
859
 
+ * Type macros.
860
 
+ */
861
 
+/* define IBusXKBConfigRegistry macros */
862
 
+#define IBUS_TYPE_XKB_CONFIG_REGISTRY                   \
863
 
+    (ibus_xkb_config_registry_get_type ())
864
 
+#define IBUS_XKB_CONFIG_REGISTRY(obj)                   \
865
 
+    (G_TYPE_CHECK_INSTANCE_CAST ((obj), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistry))
866
 
+#define IBUS_XKB_CONFIG_REGISTRY_CLASS(klass)           \
867
 
+    (G_TYPE_CHECK_CLASS_CAST ((klass), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistryClass))
868
 
+#define IBUS_IS_XKB_CONFIG_REGISTRY(obj)                \
869
 
+    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IBUS_TYPE_XKB_CONFIG_REGISTRY))
870
 
+#define IBUS_IS_XKB_CONFIG_REGISTRY_CLASS(klass)        \
871
 
+    (G_TYPE_CHECK_CLASS_TYPE ((klass), IBUS_TYPE_XKB_CONFIG_REGISTRY))
872
 
+#define IBUS_XKB_CONFIG_REGISTRY_GET_CLASS(obj)         \
873
 
+    (G_TYPE_INSTANCE_GET_CLASS ((obj), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistryClass))
874
 
+
875
 
+G_BEGIN_DECLS
876
 
+
877
 
+typedef struct _IBusXKBConfigRegistry IBusXKBConfigRegistry;
878
 
+typedef struct _IBusXKBConfigRegistryClass IBusXKBConfigRegistryClass;
879
 
+
880
 
+struct _IBusXKBConfigRegistry {
881
 
+    IBusObject parent;
882
 
+};
883
 
+
884
 
+struct _IBusXKBConfigRegistryClass {
885
 
+    IBusObjectClass parent;
886
 
+    /* signals */
887
 
+    /*< private >*/
888
 
+    /* padding */
889
 
+    gpointer pdummy[8];
890
 
+};
891
 
+
892
 
+
893
 
+GType            ibus_xkb_config_registry_get_type
894
 
+                                                 (void);
895
 
+
896
 
+/**
897
 
+ * ibus_xkb_config_registry_new:
898
 
+ * @returns: A newly allocated IBusXKBConfigRegistry
899
 
+ *
900
 
+ * New an IBusXKBConfigRegistry.
901
 
+ */
902
 
+IBusXKBConfigRegistry *
903
 
+                 ibus_xkb_config_registry_new
904
 
+                                                 (void);
905
 
+
906
 
+/**
907
 
+ * ibus_xkb_config_registry_get_layout_list: (skip)
908
 
+ * @xkb_config: An IBusXKBConfigRegistry.
909
 
+ * @returns: A const GHashTable
910
 
+ *
911
 
+ * a const GHashTable
912
 
+ */
913
 
+const GHashTable *
914
 
+                 ibus_xkb_config_registry_get_layout_list
915
 
+                                                 (IBusXKBConfigRegistry *xkb_config);
916
 
+
917
 
+/**
918
 
+ * ibus_xkb_config_registry_get_layout_lang: (skip)
919
 
+ * @xkb_config: An IBusXKBConfigRegistry.
920
 
+ * @returns: A const GHashTable
921
 
+ *
922
 
+ * a const GHashTable
923
 
+ */
924
 
+const GHashTable *
925
 
+                 ibus_xkb_config_registry_get_layout_lang
926
 
+                                                 (IBusXKBConfigRegistry *xkb_config);
927
 
+
928
 
+/**
929
 
+ * ibus_xkb_config_registry_get_layout_desc: (skip)
930
 
+ * @xkb_config: An IBusXKBConfigRegistry.
931
 
+ * @returns: A const GHashTable
932
 
+ *
933
 
+ * a const GHashTable
934
 
+ */
935
 
+const GHashTable *
936
 
+                 ibus_xkb_config_registry_get_layout_desc
937
 
+                                                 (IBusXKBConfigRegistry *xkb_config);
938
 
+
939
 
+/**
940
 
+ * ibus_xkb_config_registry_get_variant_desc: (skip)
941
 
+ * @xkb_config: An IBusXKBConfigRegistry.
942
 
+ * @returns: A const GHashTable
943
 
+ *
944
 
+ * a const GHashTable
945
 
+ */
946
 
+const GHashTable *
947
 
+                 ibus_xkb_config_registry_get_variant_desc
948
 
+                                                 (IBusXKBConfigRegistry *xkb_config);
949
 
+
950
 
+/**
951
 
+ * ibus_xkb_config_registry_layout_list_get_layouts:
952
 
+ * @xkb_config: An IBusXKBConfigRegistry.
953
 
+ * @returns: (transfer container) (element-type utf8): A GList of layouts
954
 
+ *
955
 
+ * a GList of layouts
956
 
+ */
957
 
+GList *
958
 
+                 ibus_xkb_config_registry_layout_list_get_layouts
959
 
+                                                 (IBusXKBConfigRegistry *xkb_config);
960
 
+
961
 
+/**
962
 
+ * ibus_xkb_config_registry_layout_list_get_variants:
963
 
+ * @xkb_config: An IBusXKBConfigRegistry.
964
 
+ * @layout: A layout.
965
 
+ * @returns: (transfer container) (element-type utf8): A GList
966
 
+ *
967
 
+ * a GList
968
 
+ */
969
 
+GList *
970
 
+                 ibus_xkb_config_registry_layout_list_get_variants
971
 
+                                                 (IBusXKBConfigRegistry *xkb_config,
972
 
+                                                  const gchar           *layout);
973
 
+
974
 
+/**
975
 
+ * ibus_xkb_config_registry_layout_lang_get_langs:
976
 
+ * @xkb_config: An IBusXKBConfigRegistry.
977
 
+ * @layout: A layout.
978
 
+ * @returns: (transfer container) (element-type utf8): A GList
979
 
+ *
980
 
+ * a GList
981
 
+ */
982
 
+GList *
983
 
+                 ibus_xkb_config_registry_layout_lang_get_langs
984
 
+                                                 (IBusXKBConfigRegistry *xkb_config,
985
 
+                                                  const gchar           *layout);
986
 
+
987
 
+/**
988
 
+ * ibus_xkb_config_registry_layout_desc_get_desc:
989
 
+ * @xkb_config: An IBusXKBConfigRegistry.
990
 
+ * @layout: A layout.
991
 
+ * @returns: A layout description
992
 
+ *
993
 
+ * a layout description
994
 
+ */
995
 
+gchar *
996
 
+                 ibus_xkb_config_registry_layout_desc_get_desc
997
 
+                                                 (IBusXKBConfigRegistry *xkb_config,
998
 
+                                                  const gchar           *layout);
999
 
+
1000
 
+/**
1001
 
+ * ibus_xkb_config_registry_variant_desc_get_desc:
1002
 
+ * @xkb_config: An IBusXKBConfigRegistry.
1003
 
+ * @variant: A variant.
1004
 
+ * @returns: A variant description
1005
 
+ *
1006
 
+ * a variant description
1007
 
+ */
1008
 
+gchar *
1009
 
+                 ibus_xkb_config_registry_variant_desc_get_desc
1010
 
+                                                 (IBusXKBConfigRegistry *xkb_config,
1011
 
+                                                  const gchar           *variant);
1012
 
+G_END_DECLS
1013
 
+#endif
1014
 
diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
1015
 
index b2fb800..f148202 100644
1016
 
--- a/ui/gtk3/Makefile.am
1017
 
+++ b/ui/gtk3/Makefile.am
1018
 
@@ -35,6 +35,8 @@ gtkpanel.xml: gtkpanel.xml.in
1019
 
                -e 's|@libexecdir[@]|$(libexecdir)|g' $< > $@.tmp && \
1020
 
                mv $@.tmp $@
1021
 
 
1022
 
+HAVE_IBUS_GKBD_C = $(strip $(subst false, FALSE, $(subst true, TRUE, $(HAVE_IBUS_GKBD))))
1023
 
+
1024
 
 # force include config.h before gi18n.h.
1025
 
 AM_CPPFLAGS = \
1026
 
        -I$(top_srcdir)/src \
1027
 
@@ -51,6 +53,8 @@ AM_CFLAGS = \
1028
 
        -DG_LOG_DOMAIN=\"IBUS\" \
1029
 
        -DBINDIR=\"$(bindir)\" \
1030
 
        -DIBUS_DISABLE_DEPRECATED \
1031
 
+       -DHAVE_IBUS_GKBD=$(HAVE_IBUS_GKBD_C) \
1032
 
+       -DXKB_LAYOUTS_MAX_LENGTH=4 \
1033
 
        -Wno-unused-variable \
1034
 
        -Wno-unused-but-set-variable \
1035
 
        -Wno-unused-function \
1036
 
@@ -94,12 +98,40 @@ AM_VALAFLAGS += \
1037
 
        $(NULL)
1038
 
 endif
1039
 
 
1040
 
+if ENABLE_LIBGNOMEKBD
1041
 
+AM_CFLAGS += \
1042
 
+       @LIBGNOMEKBDUI_CFLAGS@ \
1043
 
+       @ATK_CFLAGS@ \
1044
 
+       $(NULL)
1045
 
+
1046
 
+AM_LDADD += \
1047
 
+       @LIBGNOMEKBDUI_LIBS@ \
1048
 
+       @ATK_LIBS@ \
1049
 
+       $(NULL)
1050
 
+
1051
 
+AM_VALAFLAGS += \
1052
 
+       --vapidir=. \
1053
 
+       --metadatadir=$(top_srcdir)/bindings/vala \
1054
 
+       --pkg=glib-2.0 \
1055
 
+       --pkg=gmodule-2.0 \
1056
 
+       --pkg=gkbd \
1057
 
+       --pkg=Xkl-1.0 \
1058
 
+       $(NULL)
1059
 
+
1060
 
+$(srcdir)/gkbdlayout.vala: $(top_builddir)/bindings/vala/gkbd.vapi
1061
 
+       @cp $(srcdir)/gkbdlayout.vala.true $(srcdir)/gkbdlayout.vala
1062
 
+else
1063
 
+$(srcdir)/gkbdlayout.vala:
1064
 
+       @cp $(srcdir)/gkbdlayout.vala.false $(srcdir)/gkbdlayout.vala
1065
 
+endif
1066
 
+
1067
 
 libexec_PROGRAMS = ibus-ui-gtk3
1068
 
 
1069
 
 ibus_ui_gtk3_SOURCES = \
1070
 
        application.vala \
1071
 
        candidatearea.vala \
1072
 
        candidatepanel.vala \
1073
 
+       gkbdlayout.vala \
1074
 
        handle.vala \
1075
 
        iconwidget.vala \
1076
 
        keybindingmanager.vala \
1077
 
@@ -109,6 +141,7 @@ ibus_ui_gtk3_SOURCES = \
1078
 
        propertypanel.vala \
1079
 
        separator.vala \
1080
 
        switcher.vala \
1081
 
+       xkblayout.vala \
1082
 
        $(NULL)
1083
 
 
1084
 
 ibus_ui_gtk3_LDADD = \
1085
 
@@ -117,9 +150,12 @@ ibus_ui_gtk3_LDADD = \
1086
 
 
1087
 
 CLEANFILES = \
1088
 
        gtkpanel.xml \
1089
 
+       gkbdlayout.vala \
1090
 
        $(NULL)
1091
 
 
1092
 
 EXTRA_DIST = \
1093
 
+       gkbdlayout.vala.false \
1094
 
+       gkbdlayout.vala.true \
1095
 
        gtkpanel.xml.in \
1096
 
        $(NULL)
1097
 
 
1098
 
diff --git a/ui/gtk3/gkbdlayout.vala.false b/ui/gtk3/gkbdlayout.vala.false
1099
 
new file mode 100644
1100
 
index 0000000..506aff2
1101
 
--- /dev/null
1102
 
+++ b/ui/gtk3/gkbdlayout.vala.false
1103
 
@@ -0,0 +1,63 @@
1104
 
+/* vim:set et sts=4 sw=4:
1105
 
+ *
1106
 
+ * ibus - The Input Bus
1107
 
+ *
1108
 
+ * Copyright(c) 2013 Red Hat, Inc.
1109
 
+ * Copyright(c) 2013 Peng Huang <shawn.p.huang@gmail.com>
1110
 
+ * Copyright(c) 2013 Takao Fujiwara <tfujiwar@redhat.com>
1111
 
+ *
1112
 
+ * This library is free software; you can redistribute it and/or
1113
 
+ * modify it under the terms of the GNU Lesser General Public
1114
 
+ * License as published by the Free Software Foundation; either
1115
 
+ * version 2 of the License, or(at your option) any later version.
1116
 
+ *
1117
 
+ * This library is distributed in the hope that it will be useful,
1118
 
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1119
 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1120
 
+ * GNU Lesser General Public License for more details.
1121
 
+ *
1122
 
+ * You should have received a copy of the GNU Lesser General Public
1123
 
+ * License along with this program; if not, write to the
1124
 
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
1125
 
+ * Boston, MA  02111-1307  USA
1126
 
+ */
1127
 
+
1128
 
+public class GkbdLayout
1129
 
+{
1130
 
+    public signal void changed();
1131
 
+    public signal void group_changed (int object);
1132
 
+
1133
 
+    public GkbdLayout() {
1134
 
+    }
1135
 
+
1136
 
+    public string[] get_layouts() {
1137
 
+        return new string[0];
1138
 
+    }
1139
 
+
1140
 
+    public string[] get_group_names() {
1141
 
+        return new string[0];
1142
 
+    }
1143
 
+
1144
 
+    public void lock_group(int id) {
1145
 
+    }
1146
 
+
1147
 
+    public void start_listen() {
1148
 
+    }
1149
 
+
1150
 
+    public void stop_listen() {
1151
 
+    }
1152
 
+
1153
 
+    /*
1154
 
+    public static int main(string[] args) {
1155
 
+        GkbdLayout ibus_layouts = new GkbdLayout();
1156
 
+
1157
 
+        string[] layouts = ibus_layouts.get_layouts();
1158
 
+        string[] names = ibus_layouts.get_group_names();
1159
 
+        for (int i = 0; layouts != null && i < layouts.length; i++) {
1160
 
+            stdout.printf("%s %s\n", layouts[i], names[i]);
1161
 
+        }
1162
 
+
1163
 
+        return 0;
1164
 
+    }
1165
 
+    */
1166
 
+}
1167
 
diff --git a/ui/gtk3/gkbdlayout.vala.true b/ui/gtk3/gkbdlayout.vala.true
1168
 
new file mode 100644
1169
 
index 0000000..a6e0f8d
1170
 
--- /dev/null
1171
 
+++ b/ui/gtk3/gkbdlayout.vala.true
1172
 
@@ -0,0 +1,108 @@
1173
 
+/* vim:set et sts=4 sw=4:
1174
 
+ *
1175
 
+ * ibus - The Input Bus
1176
 
+ *
1177
 
+ * Copyright(c) 2013 Red Hat, Inc.
1178
 
+ * Copyright(c) 2013 Peng Huang <shawn.p.huang@gmail.com>
1179
 
+ * Copyright(c) 2013 Takao Fujiwara <tfujiwar@redhat.com>
1180
 
+ *
1181
 
+ * This library is free software; you can redistribute it and/or
1182
 
+ * modify it under the terms of the GNU Lesser General Public
1183
 
+ * License as published by the Free Software Foundation; either
1184
 
+ * version 2 of the License, or(at your option) any later version.
1185
 
+ *
1186
 
+ * This library is distributed in the hope that it will be useful,
1187
 
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1188
 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1189
 
+ * GNU Lesser General Public License for more details.
1190
 
+ *
1191
 
+ * You should have received a copy of the GNU Lesser General Public
1192
 
+ * License along with this program; if not, write to the
1193
 
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
1194
 
+ * Boston, MA  02111-1307  USA
1195
 
+ */
1196
 
+
1197
 
+public class GkbdLayout
1198
 
+{
1199
 
+    public signal void changed();
1200
 
+    public signal void group_changed (int object);
1201
 
+
1202
 
+    private Gkbd.Configuration m_config = null;
1203
 
+
1204
 
+    public GkbdLayout() {
1205
 
+        m_config = Gkbd.Configuration.get();
1206
 
+        if (m_config != null) {
1207
 
+            m_config.changed.connect(config_changed_cb);
1208
 
+            m_config.group_changed.connect(config_group_changed_cb);
1209
 
+        }
1210
 
+    }
1211
 
+
1212
 
+    ~GkbdLayout() {
1213
 
+        if (m_config != null) {
1214
 
+            m_config.changed.disconnect(config_changed_cb);
1215
 
+            m_config.group_changed.disconnect(config_group_changed_cb);
1216
 
+            /* gkbd_configuration_get reuses the object and do not
1217
 
+             * destroy m_config here. */
1218
 
+            m_config.ref();
1219
 
+            m_config = null;
1220
 
+        }
1221
 
+    }
1222
 
+
1223
 
+    private void config_changed_cb() {
1224
 
+        changed();
1225
 
+    }
1226
 
+
1227
 
+    private void config_group_changed_cb(int object) {
1228
 
+        group_changed(object);
1229
 
+    }
1230
 
+
1231
 
+    public string[] get_layouts() {
1232
 
+        if (m_config == null) {
1233
 
+            return new string[0];
1234
 
+        }
1235
 
+        return m_config.get_short_group_names();
1236
 
+    }
1237
 
+
1238
 
+    public string[] get_group_names() {
1239
 
+        if (m_config == null) {
1240
 
+            return new string[0];
1241
 
+        }
1242
 
+        return m_config.get_group_names();
1243
 
+    }
1244
 
+
1245
 
+    public void lock_group(int id) {
1246
 
+        if (m_config == null) {
1247
 
+            return;
1248
 
+        }
1249
 
+        m_config.lock_group(id);
1250
 
+    }
1251
 
+
1252
 
+    public void start_listen() {
1253
 
+        if (m_config == null) {
1254
 
+            return;
1255
 
+        }
1256
 
+        m_config.start_listen();
1257
 
+    }
1258
 
+
1259
 
+    public void stop_listen() {
1260
 
+        if (m_config == null) {
1261
 
+            return;
1262
 
+        }
1263
 
+        m_config.stop_listen();
1264
 
+    }
1265
 
+
1266
 
+    /*
1267
 
+    public static int main(string[] args) {
1268
 
+        Gtk.init(ref args);
1269
 
+        GkbdLayout ibus_layouts = new GkbdLayout();
1270
 
+
1271
 
+        string[] layouts = ibus_layouts.get_layouts();
1272
 
+        string[] names = ibus_layouts.get_group_names();
1273
 
+        for (int i = 0; layouts != null && i < layouts.length; i++) {
1274
 
+            stdout.printf("%s %s\n", layouts[i], names[i]);
1275
 
+        }
1276
 
+
1277
 
+        return 0;
1278
 
+    }
1279
 
+    */
1280
 
+}
1281
 
diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
1282
 
index 748cb32..7a15049 100644
1283
 
--- a/ui/gtk3/panel.vala
1284
 
+++ b/ui/gtk3/panel.vala
1285
 
@@ -63,6 +63,13 @@ class Panel : IBus.PanelService {
1286
 
                                                    GLib.str_equal);
1287
 
     private Gdk.RGBA m_xkb_icon_rgba = Gdk.RGBA(){
1288
 
             red = 0.0, green = 0.0, blue = 0.0, alpha = 1.0 };
1289
 
+    private GkbdLayout m_gkbdlayout = null;
1290
 
+    private XKBLayout m_xkblayout = null;
1291
 
+    private string[] m_layouts = {};
1292
 
+    private string[] m_variants = {};
1293
 
+    private int m_fallback_lock_id = -1;
1294
 
+    private bool m_changed_xkb_option = false;
1295
 
+    private GLib.Timer m_changed_layout_timer;
1296
 
 
1297
 
     private GLib.List<Keybinding> m_keybindings = new GLib.List<Keybinding>();
1298
 
 
1299
 
@@ -113,6 +120,14 @@ class Panel : IBus.PanelService {
1300
 
 
1301
 
     ~Panel() {
1302
 
         unbind_switch_shortcut();
1303
 
+
1304
 
+        if (HAVE_IBUS_GKBD && m_gkbdlayout != null) {
1305
 
+            m_gkbdlayout.changed.disconnect(gkbdlayout_changed_cb);
1306
 
+            m_gkbdlayout.stop_listen();
1307
 
+            m_gkbdlayout = null;
1308
 
+        }
1309
 
+
1310
 
+        m_xkblayout = null;
1311
 
     }
1312
 
 
1313
 
     private void init_settings() {
1314
 
@@ -487,6 +502,7 @@ class Panel : IBus.PanelService {
1315
 
     }
1316
 
 
1317
 
     public void load_settings() {
1318
 
+        init_engines_order();
1319
 
         // Update m_use_system_keyboard_layout before update_engines()
1320
 
         // is called.
1321
 
         set_use_system_keyboard_layout();
1322
 
@@ -508,6 +524,184 @@ class Panel : IBus.PanelService {
1323
 
         set_version();
1324
 
     }
1325
 
 
1326
 
+    private void gkbdlayout_changed_cb() {
1327
 
+        /* The callback is called four times after set_layout is called
1328
 
+         * so check the elapsed and take the first signal only. */
1329
 
+        double elapsed = m_changed_layout_timer.elapsed();
1330
 
+        if (elapsed < 1.0 && elapsed > 0.0) {
1331
 
+            return;
1332
 
+        }
1333
 
+
1334
 
+        if (m_fallback_lock_id != -1) {
1335
 
+            /* Call lock_group only when set_layout is called. */
1336
 
+            m_gkbdlayout.lock_group(m_fallback_lock_id);
1337
 
+            m_fallback_lock_id = -1;
1338
 
+        } else {
1339
 
+            /* Reset default layout when gnome-control-center is called. */
1340
 
+            m_xkblayout.reset_layout();
1341
 
+        }
1342
 
+
1343
 
+        update_xkb_engines();
1344
 
+        m_changed_layout_timer.reset();
1345
 
+    }
1346
 
+
1347
 
+    private void init_gkbd() {
1348
 
+        m_gkbdlayout = new GkbdLayout();
1349
 
+        m_gkbdlayout.changed.connect(gkbdlayout_changed_cb);
1350
 
+
1351
 
+        /* Probably we cannot support both keyboard and ibus indicators
1352
 
+         * How can I get the engine from keymap of group_id?
1353
 
+         * e.g. 'en' could be owned by xkb:en and pinyin engines. */
1354
 
+        //m_gkbdlayout.group_changed.connect((object) => {});
1355
 
+
1356
 
+        m_changed_layout_timer = new GLib.Timer();
1357
 
+        m_changed_layout_timer.start();
1358
 
+        m_gkbdlayout.start_listen();
1359
 
+    }
1360
 
+
1361
 
+    private void init_engines_order() {
1362
 
+        m_xkblayout = new XKBLayout();
1363
 
+        string session = Environment.get_variable("DESKTOP_SESSION");
1364
 
+
1365
 
+        if (HAVE_IBUS_GKBD &&
1366
 
+            session != null && session.length >= 5 &&
1367
 
+            session[0:5] == "gnome") {
1368
 
+            init_gkbd();
1369
 
+        }
1370
 
+
1371
 
+        update_xkb_engines();
1372
 
+    }
1373
 
+
1374
 
+    private void update_xkb_engines() {
1375
 
+        string var_layout = m_xkblayout.get_layout();
1376
 
+        string var_variant = m_xkblayout.get_variant();
1377
 
+        if (var_layout == "") {
1378
 
+            return;
1379
 
+        }
1380
 
+
1381
 
+        m_layouts = var_layout.split(",");
1382
 
+        m_variants = var_variant.split(",");
1383
 
+
1384
 
+        IBus.XKBConfigRegistry registry = new IBus.XKBConfigRegistry();
1385
 
+        string[] var_xkb_engine_names = {};
1386
 
+        for (int i = 0; i < m_layouts.length; i++) {
1387
 
+            string name = m_layouts[i];
1388
 
+            string lang = null;
1389
 
+
1390
 
+            if (i < m_variants.length && m_variants[i] != "") {
1391
 
+                name = "%s:%s".printf(name, m_variants[i]);
1392
 
+                string layout = "%s(%s)".printf(name, m_variants[i]);
1393
 
+                GLib.List<string> langs =
1394
 
+                        registry.layout_lang_get_langs(layout);
1395
 
+                if (langs.length() != 0) {
1396
 
+                    lang = langs.data;
1397
 
+                }
1398
 
+            } else {
1399
 
+                name = "%s:".printf(name);
1400
 
+            }
1401
 
+
1402
 
+            if (lang == null) {
1403
 
+                GLib.List<string> langs =
1404
 
+                        registry.layout_lang_get_langs(m_layouts[i]);
1405
 
+                if (langs.length() != 0) {
1406
 
+                    lang = langs.data;
1407
 
+                }
1408
 
+            }
1409
 
+
1410
 
+            var_xkb_engine_names += "%s:%s:%s".printf("xkb", name, lang);
1411
 
+        }
1412
 
+
1413
 
+        string[] engine_names =
1414
 
+                m_settings_general.get_strv("preload-engines");
1415
 
+        bool updated_engine_names = false;
1416
 
+
1417
 
+        foreach (string name in var_xkb_engine_names) {
1418
 
+            if (name in engine_names)
1419
 
+                continue;
1420
 
+            updated_engine_names = true;
1421
 
+            engine_names += name;
1422
 
+        }
1423
 
+
1424
 
+        if (updated_engine_names)
1425
 
+            m_settings_general.set_strv("preload-engines", engine_names);
1426
 
+
1427
 
+        string[] order_names =
1428
 
+                m_settings_general.get_strv("engines-order");
1429
 
+        bool updated_order_names = false;
1430
 
+
1431
 
+        foreach (var name in var_xkb_engine_names) {
1432
 
+            if (name in order_names)
1433
 
+                continue;
1434
 
+            order_names += name;
1435
 
+            updated_order_names = true;
1436
 
+        }
1437
 
+
1438
 
+        if (updated_order_names)
1439
 
+            m_settings_general.set_strv("engines-order", order_names);
1440
 
+    }
1441
 
+
1442
 
+    private void set_xkb_group_layout(IBus.EngineDesc engine) {
1443
 
+        int[] retval = m_xkblayout.set_layout(engine, true);
1444
 
+        if (retval[0] >= 0) {
1445
 
+            /* If an XKB keymap is added into the XKB group,
1446
 
+             * this._gkbdlayout.lock_group will be called after
1447
 
+             * 'group-changed' signal is received. */
1448
 
+            m_fallback_lock_id = retval[0];
1449
 
+            m_changed_xkb_option = (retval[1] != 0) ? true : false;
1450
 
+        }
1451
 
+    }
1452
 
+
1453
 
+    private bool set_gkbd_layout(IBus.EngineDesc engine) {
1454
 
+        string layout = engine.get_layout();
1455
 
+        string variant = engine.get_layout_variant();
1456
 
+
1457
 
+        /* If a previous ibus engine changed XKB options, need to set the
1458
 
+         * default XKB option. */
1459
 
+        if (m_changed_xkb_option == true) {
1460
 
+            m_changed_xkb_option = false;
1461
 
+            return false;
1462
 
+        }
1463
 
+
1464
 
+        if (variant != "" && variant != "default") {
1465
 
+            layout = "%s(%s)".printf(layout, variant);
1466
 
+        }
1467
 
+
1468
 
+        int gkbd_len = m_gkbdlayout.get_group_names().length;
1469
 
+        for (int i = 0; i < m_layouts.length && i < gkbd_len; i++) {
1470
 
+            string sys_layout = m_layouts[i];
1471
 
+            if (i < m_variants.length && m_variants[i] != "") {
1472
 
+                sys_layout = "%s(%s)".printf(sys_layout, m_variants[i]);
1473
 
+            }
1474
 
+            if (sys_layout == layout) {
1475
 
+                m_gkbdlayout.lock_group(i);
1476
 
+                return true;
1477
 
+            }
1478
 
+        }
1479
 
+        return false;
1480
 
+    }
1481
 
+
1482
 
+    private void set_layout(IBus.EngineDesc engine) {
1483
 
+        string layout = engine.get_layout();
1484
 
+
1485
 
+        if (layout == "" || layout == null) {
1486
 
+            return;
1487
 
+        }
1488
 
+
1489
 
+        if (m_xkblayout == null) {
1490
 
+            init_engines_order();
1491
 
+        }
1492
 
+
1493
 
+        if (HAVE_IBUS_GKBD && m_gkbdlayout != null) {
1494
 
+            if (set_gkbd_layout(engine)) {
1495
 
+                return;
1496
 
+            }
1497
 
+            set_xkb_group_layout(engine);
1498
 
+            return;
1499
 
+        }
1500
 
+
1501
 
+        m_xkblayout.set_layout(engine);
1502
 
+    }
1503
 
+
1504
 
     private void exec_setxkbmap(IBus.EngineDesc engine) {
1505
 
         string layout = engine.get_layout();
1506
 
         string variant = engine.get_layout_variant();
1507
 
@@ -573,7 +767,7 @@ class Panel : IBus.PanelService {
1508
 
 
1509
 
         // set xkb layout
1510
 
         if (!m_use_system_keyboard_layout)
1511
 
-            exec_setxkbmap(engine);
1512
 
+            set_layout(engine);
1513
 
 
1514
 
         engine_contexts_insert(engine);
1515
 
     }
1516
 
@@ -636,6 +830,39 @@ class Panel : IBus.PanelService {
1517
 
         }
1518
 
     }
1519
 
 
1520
 
+    /* IBus.Bus.get_engines_by_names() returns 'us' engine if the name 
1521
 
+     * does not exist in simple.xml and 'us' engine could be duplicated.
1522
 
+     */
1523
 
+    private IBus.EngineDesc[] uniq_engines(IBus.EngineDesc[] engines) {
1524
 
+        if (engines.length == 0)
1525
 
+            return engines;
1526
 
+
1527
 
+        int i = 0;
1528
 
+        IBus.EngineDesc[] retval = {};
1529
 
+
1530
 
+        for (; i < engines.length; i++) {
1531
 
+            if (engines[i].get_name() == "xkb:us::eng")
1532
 
+                break;
1533
 
+        }
1534
 
+
1535
 
+        if (i == engines.length)
1536
 
+            return engines;
1537
 
+
1538
 
+        for (int j = 0; j < engines.length; j++) {
1539
 
+            if (j <= i) {
1540
 
+                retval += engines[j];
1541
 
+                continue;
1542
 
+            }
1543
 
+
1544
 
+            if (engines[i].get_name() == engines[j].get_name())
1545
 
+                continue;
1546
 
+
1547
 
+            retval += engines[j];
1548
 
+        }
1549
 
+
1550
 
+        return retval;
1551
 
+    }
1552
 
+
1553
 
     private void run_preload_engines(IBus.EngineDesc[] engines, int index) {
1554
 
         string[] names = {};
1555
 
 
1556
 
@@ -668,6 +895,7 @@ class Panel : IBus.PanelService {
1557
 
         }
1558
 
 
1559
 
         var engines = m_bus.get_engines_by_names(names);
1560
 
+        engines = uniq_engines(engines);
1561
 
 
1562
 
         if (m_engines.length == 0) {
1563
 
             m_engines = engines;
1564
 
diff --git a/ui/gtk3/xkblayout.vala b/ui/gtk3/xkblayout.vala
1565
 
new file mode 100644
1566
 
index 0000000..b7dfb3e
1567
 
--- /dev/null
1568
 
+++ b/ui/gtk3/xkblayout.vala
1569
 
@@ -0,0 +1,429 @@
1570
 
+/* vim:set et sts=4 sw=4:
1571
 
+ *
1572
 
+ * ibus - The Input Bus
1573
 
+ *
1574
 
+ * Copyright(c) 2013 Red Hat, Inc.
1575
 
+ * Copyright(c) 2013 Peng Huang <shawn.p.huang@gmail.com>
1576
 
+ * Copyright(c) 2013 Takao Fujiwara <tfujiwar@redhat.com>
1577
 
+ *
1578
 
+ * This library is free software; you can redistribute it and/or
1579
 
+ * modify it under the terms of the GNU Lesser General Public
1580
 
+ * License as published by the Free Software Foundation; either
1581
 
+ * version 2 of the License, or(at your option) any later version.
1582
 
+ *
1583
 
+ * This library is distributed in the hope that it will be useful,
1584
 
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1585
 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1586
 
+ * GNU Lesser General Public License for more details.
1587
 
+ *
1588
 
+ * You should have received a copy of the GNU Lesser General Public
1589
 
+ * License along with this program; if not, write to the
1590
 
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
1591
 
+ * Boston, MA  02111-1307  USA
1592
 
+ */
1593
 
+
1594
 
+public extern const bool HAVE_IBUS_GKBD;
1595
 
+public extern const int XKB_LAYOUTS_MAX_LENGTH;
1596
 
+
1597
 
+class XKBLayout
1598
 
+{
1599
 
+    string m_xkb_command = "setxkbmap";
1600
 
+    GLib.Settings m_settings_general;
1601
 
+    string[] m_xkb_latin_layouts = {};
1602
 
+    GLib.Pid m_xkb_pid = -1;
1603
 
+    GLib.Pid m_xmodmap_pid = -1;
1604
 
+    string m_xmodmap_command = "xmodmap";
1605
 
+    bool m_use_xmodmap = true;
1606
 
+    string[] m_xmodmap_known_files = {".xmodmap", ".xmodmaprc",
1607
 
+                                      ".Xmodmap", ".Xmodmaprc"};
1608
 
+    string m_default_layout = "";
1609
 
+    string m_default_variant = "";
1610
 
+    string m_default_option = "";
1611
 
+
1612
 
+    public XKBLayout() {
1613
 
+        m_settings_general = new GLib.Settings("org.freedesktop.ibus.general");
1614
 
+
1615
 
+        var value  = m_settings_general.get_value("xkb-latin-layouts");
1616
 
+        for (int i = 0; value != null && i < value.n_children(); i++) {
1617
 
+            m_xkb_latin_layouts +=
1618
 
+                    value.get_child_value(i).dup_string();
1619
 
+        }
1620
 
+        if (m_use_xmodmap) {
1621
 
+            m_use_xmodmap = m_settings_general.get_boolean("use-xmodmap");
1622
 
+        }
1623
 
+    }
1624
 
+
1625
 
+    private string get_output_from_cmdline(string arg, string element) {
1626
 
+        string[] exec_command = {};
1627
 
+        exec_command += m_xkb_command;
1628
 
+        exec_command += arg;
1629
 
+        string standard_output = null;
1630
 
+        string standard_error = null;
1631
 
+        int exit_status = 0;
1632
 
+        string retval = "";
1633
 
+        try {
1634
 
+            GLib.Process.spawn_sync(null,
1635
 
+                                    exec_command,
1636
 
+                                    null,
1637
 
+                                    GLib.SpawnFlags.SEARCH_PATH,
1638
 
+                                    null,
1639
 
+                                    out standard_output,
1640
 
+                                    out standard_error,
1641
 
+                                    out exit_status);
1642
 
+        } catch (GLib.SpawnError err) {
1643
 
+            stderr.printf("IBUS_ERROR: %s\n", err.message);
1644
 
+        }
1645
 
+        if (exit_status != 0) {
1646
 
+            stderr.printf("IBUS_ERROR: %s\n", standard_error ?? "");
1647
 
+        }
1648
 
+        if (standard_output == null) {
1649
 
+            return "";
1650
 
+        }
1651
 
+        foreach (string line in standard_output.split("\n")) {
1652
 
+            if (element.length <= line.length &&
1653
 
+                line[0:element.length] == element) {
1654
 
+                retval = line[element.length:line.length];
1655
 
+                if (retval == null) {
1656
 
+                    retval = "";
1657
 
+                } else {
1658
 
+                    retval = retval.strip();
1659
 
+                }
1660
 
+            }
1661
 
+        }
1662
 
+        return retval;
1663
 
+    }
1664
 
+
1665
 
+    private void set_layout_cb(GLib.Pid pid, int status) {
1666
 
+        if (m_xkb_pid != pid) {
1667
 
+            stderr.printf("IBUS_ERROR: set_layout_cb has another pid\n");
1668
 
+            return;
1669
 
+        }
1670
 
+        GLib.Process.close_pid(m_xkb_pid);
1671
 
+        m_xkb_pid = -1;
1672
 
+        set_xmodmap();
1673
 
+    }
1674
 
+
1675
 
+    private void set_xmodmap_cb(GLib.Pid pid, int status) {
1676
 
+        if (m_xmodmap_pid != pid) {
1677
 
+            stderr.printf("IBUS_ERROR: set_xmodmap_cb has another pid\n");
1678
 
+            return;
1679
 
+        }
1680
 
+        GLib.Process.close_pid(m_xmodmap_pid);
1681
 
+        m_xmodmap_pid = -1;
1682
 
+    }
1683
 
+
1684
 
+    private string get_fullpath(string command) {
1685
 
+        string envpath = GLib.Environment.get_variable("PATH");
1686
 
+        foreach (string dir in envpath.split(":")) {
1687
 
+            string filepath = GLib.Path.build_filename(dir, command);
1688
 
+            if (GLib.FileUtils.test(filepath, GLib.FileTest.EXISTS)) {
1689
 
+                return filepath;
1690
 
+            }
1691
 
+        }
1692
 
+        return "";
1693
 
+    }
1694
 
+
1695
 
+    private string[] get_xkb_group_layout (string layout,
1696
 
+                                           string variant,
1697
 
+                                           int layouts_max_length) {
1698
 
+        int group_id = 0;
1699
 
+        int i = 0;
1700
 
+        string[] layouts = m_default_layout.split(",");
1701
 
+        string[] variants = m_default_variant.split(",");
1702
 
+        string group_layouts = "";
1703
 
+        string group_variants = "";
1704
 
+        bool has_variant = false;
1705
 
+        bool include_keymap = false;
1706
 
+
1707
 
+        for (i = 0; i < layouts.length; i++) {
1708
 
+            if (i >= layouts_max_length - 1) {
1709
 
+                break;
1710
 
+            }
1711
 
+
1712
 
+            if (i == 0) {
1713
 
+                group_layouts = layouts[i];
1714
 
+            } else {
1715
 
+                group_layouts = "%s,%s".printf(group_layouts, layouts[i]);
1716
 
+            }
1717
 
+
1718
 
+            if (i >= variants.length) {
1719
 
+                if (i == 0) {
1720
 
+                    group_variants = "";
1721
 
+                } else {
1722
 
+                    group_variants += ",";
1723
 
+                }
1724
 
+                if (layout == layouts[i] && variant == "") {
1725
 
+                    include_keymap = true;
1726
 
+                    group_id = i;
1727
 
+                }
1728
 
+                continue;
1729
 
+            }
1730
 
+            if (layout == layouts[i] && variant == variants[i]) {
1731
 
+                include_keymap = true;
1732
 
+                group_id = i;
1733
 
+            }
1734
 
+
1735
 
+            if (variants[i] != "") {
1736
 
+                has_variant = true;
1737
 
+            }
1738
 
+
1739
 
+            if (i == 0) {
1740
 
+                group_variants = variants[i];
1741
 
+            } else {
1742
 
+                group_variants = "%s,%s".printf(group_variants, variants[i]);
1743
 
+            }
1744
 
+        }
1745
 
+
1746
 
+        if (variant != "") {
1747
 
+            has_variant = true;
1748
 
+        }
1749
 
+
1750
 
+        if (!include_keymap) {
1751
 
+            group_layouts = "%s,%s".printf(group_layouts, layout);
1752
 
+            group_variants = "%s,%s".printf(group_variants, variant);
1753
 
+            group_id = i;
1754
 
+        }
1755
 
+
1756
 
+        if (!has_variant) {
1757
 
+            group_variants = null;
1758
 
+        }
1759
 
+
1760
 
+        return {group_layouts, group_variants, group_id.to_string()};
1761
 
+    }
1762
 
+
1763
 
+    public string[] get_variant_from_layout(string layout) {
1764
 
+        int left_bracket = layout.index_of("(");
1765
 
+        int right_bracket = layout.index_of(")");
1766
 
+        if (left_bracket >= 0 && right_bracket > left_bracket) {
1767
 
+            return {layout[0:left_bracket] +
1768
 
+                    layout[right_bracket + 1:layout.length],
1769
 
+                    layout[left_bracket + 1:right_bracket]};
1770
 
+        }
1771
 
+        return {layout, "default"};
1772
 
+    }
1773
 
+
1774
 
+    public string[] get_option_from_layout(string layout) {
1775
 
+        int left_bracket = layout.index_of("[");
1776
 
+        int right_bracket = layout.index_of("]");
1777
 
+        if (left_bracket >= 0 && right_bracket > left_bracket) {
1778
 
+            return {layout[0:left_bracket] +
1779
 
+                    layout[right_bracket + 1:layout.length],
1780
 
+                    layout[left_bracket + 1:right_bracket]};
1781
 
+        }
1782
 
+        return {layout, "default"};
1783
 
+    }
1784
 
+
1785
 
+    public string get_layout() {
1786
 
+        return get_output_from_cmdline("-query", "layout: ");
1787
 
+    }
1788
 
+
1789
 
+    public string get_variant() {
1790
 
+        return get_output_from_cmdline("-query", "variant: ");
1791
 
+    }
1792
 
+
1793
 
+    public string get_option() {
1794
 
+        return get_output_from_cmdline("-query", "options: ");
1795
 
+    }
1796
 
+
1797
 
+    /*
1798
 
+    public string get_group() {
1799
 
+        return get_output_from_cmdline("--get-group", "group: ");
1800
 
+    }
1801
 
+    */
1802
 
+
1803
 
+    public int[] set_layout(IBus.EngineDesc engine,
1804
 
+                            bool use_group_layout=false) {
1805
 
+        string layout = engine.get_layout();
1806
 
+        string variant = engine.get_layout_variant();
1807
 
+        string option = engine.get_layout_option();
1808
 
+
1809
 
+        assert (layout != null);
1810
 
+
1811
 
+        int xkb_group_id = 0;
1812
 
+        int changed_option = 0;
1813
 
+
1814
 
+        if (m_xkb_pid != -1) {
1815
 
+            return {-1, 0};
1816
 
+        }
1817
 
+
1818
 
+        if (layout == "default" &&
1819
 
+            (variant == "default" || variant == "") &&
1820
 
+            (option == "default" || option == "")) {
1821
 
+            return {-1, 0};
1822
 
+        }
1823
 
+
1824
 
+        bool need_us_layout = false;
1825
 
+        foreach (string latin_layout in m_xkb_latin_layouts) {
1826
 
+            if (layout == latin_layout && variant != "eng") {
1827
 
+                need_us_layout = true;
1828
 
+                break;
1829
 
+            }
1830
 
+            if (variant != null &&
1831
 
+                "%s(%s)".printf(layout, variant) == latin_layout) {
1832
 
+                need_us_layout = true;
1833
 
+                break;
1834
 
+            }
1835
 
+        }
1836
 
+
1837
 
+        int layouts_max_length =  XKB_LAYOUTS_MAX_LENGTH;
1838
 
+        if (need_us_layout) {
1839
 
+            layouts_max_length--;
1840
 
+        }
1841
 
+
1842
 
+        if (m_default_layout == "") {
1843
 
+            m_default_layout = get_layout();
1844
 
+        }
1845
 
+        if (m_default_variant  == "") {
1846
 
+            m_default_variant  = get_variant();
1847
 
+        }
1848
 
+        if (m_default_option == "") {
1849
 
+            m_default_option = get_option();
1850
 
+        }
1851
 
+
1852
 
+        if (layout == "default") {
1853
 
+            layout = m_default_layout;
1854
 
+            variant = m_default_variant;
1855
 
+        } else {
1856
 
+            if (use_group_layout) {
1857
 
+                if (variant == "default") {
1858
 
+                    variant = "";
1859
 
+                }
1860
 
+                string[] retval = get_xkb_group_layout (layout, variant,
1861
 
+                                                        layouts_max_length);
1862
 
+                layout = retval[0];
1863
 
+                variant = retval[1];
1864
 
+                xkb_group_id = int.parse(retval[2]);
1865
 
+            }
1866
 
+        }
1867
 
+
1868
 
+        if (layout == "") {
1869
 
+            warning("Could not get the correct layout");
1870
 
+            return {-1, 0};
1871
 
+        }
1872
 
+
1873
 
+        if (variant == "default" || variant == "") {
1874
 
+            variant = null;
1875
 
+        }
1876
 
+
1877
 
+        if (option == "default" || option == "") {
1878
 
+            option = m_default_option;
1879
 
+        } else {
1880
 
+            if (!(option in m_default_option.split(","))) {
1881
 
+                option = "%s,%s".printf(m_default_option, option);
1882
 
+                changed_option = 1;
1883
 
+            } else {
1884
 
+                option = m_default_option;
1885
 
+            }
1886
 
+        }
1887
 
+
1888
 
+        if (option == "") {
1889
 
+            option = null;
1890
 
+        }
1891
 
+
1892
 
+        if (need_us_layout) {
1893
 
+            layout += ",us";
1894
 
+            if (variant != null) {
1895
 
+                variant += ",";
1896
 
+            }
1897
 
+        }
1898
 
+
1899
 
+        string[] args = {};
1900
 
+        args += m_xkb_command;
1901
 
+        args += "-layout";
1902
 
+        args += layout;
1903
 
+        if (variant != null) {
1904
 
+            args += "-variant";
1905
 
+            args += variant;
1906
 
+        }
1907
 
+        if (option != null) {
1908
 
+            /* TODO: Need to get the session XKB options */
1909
 
+            args += "-option";
1910
 
+            args += "-option";
1911
 
+            args += option;
1912
 
+        }
1913
 
+
1914
 
+        GLib.Pid child_pid;
1915
 
+        try {
1916
 
+            GLib.Process.spawn_async(null,
1917
 
+                                     args,
1918
 
+                                     null,
1919
 
+                                     GLib.SpawnFlags.DO_NOT_REAP_CHILD |
1920
 
+                                         GLib.SpawnFlags.SEARCH_PATH,
1921
 
+                                     null,
1922
 
+                                     out child_pid);
1923
 
+        } catch (GLib.SpawnError err) {
1924
 
+            stderr.printf("Execute setxkbmap failed: %s\n", err.message);
1925
 
+            return {-1, 0};
1926
 
+        }
1927
 
+        m_xkb_pid = child_pid;
1928
 
+        GLib.ChildWatch.add(m_xkb_pid, set_layout_cb);
1929
 
+
1930
 
+        return {xkb_group_id, changed_option};
1931
 
+    }
1932
 
+
1933
 
+    public void set_xmodmap() {
1934
 
+        if (!m_use_xmodmap) {
1935
 
+            return;
1936
 
+        }
1937
 
+
1938
 
+        if (m_xmodmap_pid != -1) {
1939
 
+            return;
1940
 
+        }
1941
 
+
1942
 
+        string xmodmap_cmdpath = get_fullpath(m_xmodmap_command);
1943
 
+        if (xmodmap_cmdpath == "") {
1944
 
+            xmodmap_cmdpath = m_xmodmap_command;
1945
 
+        }
1946
 
+        string homedir = GLib.Environment.get_home_dir();
1947
 
+        foreach (string xmodmap_file in m_xmodmap_known_files) {
1948
 
+            string xmodmap_filepath = GLib.Path.build_filename(homedir, xmodmap_file);
1949
 
+            if (!GLib.FileUtils.test(xmodmap_filepath, GLib.FileTest.EXISTS)) {
1950
 
+                continue;
1951
 
+            }
1952
 
+            string[] args = {xmodmap_cmdpath, xmodmap_filepath};
1953
 
+
1954
 
+            GLib.Pid child_pid;
1955
 
+            try {
1956
 
+                GLib.Process.spawn_async(null,
1957
 
+                                         args,
1958
 
+                                         null,
1959
 
+                                         GLib.SpawnFlags.DO_NOT_REAP_CHILD |
1960
 
+                                             GLib.SpawnFlags.SEARCH_PATH,
1961
 
+                                         null,
1962
 
+                                         out child_pid);
1963
 
+            } catch (GLib.SpawnError err) {
1964
 
+                stderr.printf("IBUS_ERROR: %s\n", err.message);
1965
 
+                return;
1966
 
+            }
1967
 
+            m_xmodmap_pid = child_pid;
1968
 
+            GLib.ChildWatch.add(m_xmodmap_pid, set_xmodmap_cb);
1969
 
+
1970
 
+            break;
1971
 
+        }
1972
 
+    }
1973
 
+
1974
 
+    public void reset_layout() {
1975
 
+        m_default_layout = get_layout();
1976
 
+        m_default_variant = get_variant();
1977
 
+        m_default_option = get_option();
1978
 
+    }
1979
 
+
1980
 
+    /*
1981
 
+    public static int main(string[] args) {
1982
 
+        IBus.Bus bus = new IBus.Bus();
1983
 
+        IBus.Config config = bus.get_config();
1984
 
+        XKBLayout xkblayout = new XKBLayout(config);
1985
 
+        stdout.printf ("layout: %s\n", xkblayout.get_layout());
1986
 
+        stdout.printf ("variant: %s\n", xkblayout.get_variant());
1987
 
+        stdout.printf ("option: %s\n", xkblayout.get_option());
1988
 
+        xkblayout.set_layout("jp");
1989
 
+        if (config != null) {
1990
 
+            IBus.main();
1991
 
+        } else {
1992
 
+            Gtk.init (ref args);
1993
 
+            Gtk.main();
1994
 
+        }
1995
 
+        return 0;
1996
 
+    }
1997
 
+    */
1998
 
+}
1999
 
1.8.5.3
2000