public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/ibus] autotool: Removed ibus-HEAD.patch
@ 2026-05-31  2:06 Takao Fujiwara
  0 siblings, 0 replies; only message in thread
From: Takao Fujiwara @ 2026-05-31  2:06 UTC (permalink / raw)
  To: git-commits

A new commit has been pushed.

Repo   : rpms/ibus
Branch : autotool
Commit : d6bf0e42b2c6515f33cd3f8dc621431b277a9fb1
Author : Takao Fujiwara <tfujiwar@redhat.com>
Date   : 2018-03-02T17:34:59+09:00
Stats  : +0/-10021 in 1 file(s)
URL    : https://src.fedoraproject.org/rpms/ibus/c/d6bf0e42b2c6515f33cd3f8dc621431b277a9fb1?branch=autotool

Log:
Removed ibus-HEAD.patch

---
diff --git a/ibus-HEAD.patch b/ibus-HEAD.patch
deleted file mode 100644
index fa81971..0000000
--- a/ibus-HEAD.patch
+++ /dev/null
@@ -1,10021 +0,0 @@
-From bfe57d20e9d39d52428e95e493d9af0bd034a82f Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Mon, 15 Jan 2018 14:44:07 +0900
-Subject: [PATCH] Added DBus filtering against malware
-
-The proposal prevents non-ower of the GDBusConnection from accessing
-DBus methods against malicious usages.
-
-BUG=https://github.com/ibus/ibus/issues/1955
-
-Review URL: https://codereview.appspot.com/335380043
----
- bus/inputcontext.c     | 24 +++++++++++++++++++++++-
- src/ibusengine.c       | 18 +++++++++++++++++-
- src/ibuspanelservice.c | 14 +++++++++++++-
- 3 files changed, 53 insertions(+), 3 deletions(-)
-
-diff --git a/bus/inputcontext.c b/bus/inputcontext.c
-index d8be9e3f..4f2ecafc 100644
---- a/bus/inputcontext.c
-+++ b/bus/inputcontext.c
-@@ -2,7 +2,7 @@
- /* vim:set et sts=4: */
- /* ibus - The Input Bus
-  * Copyright (C) 2008-2014 Peng Huang <shawn.p.huang@gmail.com>
-- * Copyright (C) 2015-2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2015-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-  * Copyright (C) 2008-2016 Red Hat, Inc.
-  *
-  * This library is free software; you can redistribute it and/or
-@@ -1148,6 +1148,20 @@ _ic_set_surrounding_text (BusInputContext       *context,
-     g_dbus_method_invocation_return_value (invocation, NULL);
- }
- 
-+/*
-+ * Since IBusService is inherited by IBusImpl, this method cannot be
-+ * applied to IBusServiceClass.method_call() directly but can be in
-+ * each child class.method_call().
-+ */
-+static gboolean
-+bus_input_context_service_authorized_method (IBusService     *service,
-+                                             GDBusConnection *connection)
-+{
-+    if (ibus_service_get_connection (service) == connection)
-+        return TRUE;
-+    return FALSE;
-+}
-+
- /**
-  * bus_input_context_service_method_call:
-  *
-@@ -1197,6 +1211,10 @@ bus_input_context_service_method_call (IBusService            *service,
-     };
- 
-     gint i;
-+
-+    if (!bus_input_context_service_authorized_method (service, connection))
-+        return;
-+
-     for (i = 0; i < G_N_ELEMENTS (methods); i++) {
-         if (g_strcmp0 (method_name, methods[i].method_name) == 0) {
-             methods[i].method_callback ((BusInputContext *)service, parameters, invocation);
-@@ -1270,6 +1288,9 @@ bus_input_context_service_set_property (IBusService     *service,
-                                   error);
-     }
- 
-+    if (!bus_input_context_service_authorized_method (service, connection))
-+        return FALSE;
-+
-     if (g_strcmp0 (property_name, "ContentType") == 0) {
-         BusInputContext *context = (BusInputContext *) service;
-         _ic_set_content_type (context, value);
-@@ -1279,6 +1300,7 @@ bus_input_context_service_set_property (IBusService     *service,
-     g_return_val_if_reached (FALSE);
- }
- 
-+
- gboolean
- bus_input_context_has_focus (BusInputContext *context)
- {
-diff --git a/src/ibusengine.c b/src/ibusengine.c
-index b2a8022a..da648d11 100644
---- a/src/ibusengine.c
-+++ b/src/ibusengine.c
-@@ -2,7 +2,8 @@
- /* vim:set et sts=4: */
- /* ibus - The Input Bus
-  * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
-- * Copyright (C) 2008-2013 Red Hat, Inc.
-+ * Copyright (C) 2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2008-2018 Red Hat, Inc.
-  *
-  * This library is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU Lesser General Public
-@@ -851,6 +852,15 @@ ibus_engine_get_property (IBusEngine *engine,
-     }
- }
- 
-+static gboolean
-+ibus_engine_service_authorized_method (IBusService     *service,
-+                                       GDBusConnection *connection)
-+{
-+    if (ibus_service_get_connection (service) == connection)
-+        return TRUE;
-+    return FALSE;
-+}
-+
- static void
- ibus_engine_service_method_call (IBusService           *service,
-                                  GDBusConnection       *connection,
-@@ -876,6 +886,9 @@ ibus_engine_service_method_call (IBusService           *service,
-         return;
-     }
- 
-+    if (!ibus_engine_service_authorized_method (service, connection))
-+        return;
-+
-     if (g_strcmp0 (method_name, "ProcessKeyEvent") == 0) {
-         guint keyval, keycode, state;
-         gboolean retval = FALSE;
-@@ -1085,6 +1098,9 @@ ibus_engine_service_set_property (IBusService        *service,
-                                   error);
-     }
- 
-+    if (!ibus_engine_service_authorized_method (service, connection))
-+        return FALSE;
-+
-     if (g_strcmp0 (property_name, "ContentType") == 0) {
-         guint purpose = 0;
-         guint hints = 0;
-diff --git a/src/ibuspanelservice.c b/src/ibuspanelservice.c
-index 468aa324..33949fa1 100644
---- a/src/ibuspanelservice.c
-+++ b/src/ibuspanelservice.c
-@@ -3,7 +3,7 @@
- /* ibus - The Input Bus
-  * Copyright (c) 2009-2014 Google Inc. All rights reserved.
-  * Copyright (C) 2010-2014 Peng Huang <shawn.p.huang@gmail.com>
-- * Copyright (C) 2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2017-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-  *
-  * This library is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU Lesser General Public
-@@ -936,6 +936,15 @@ _g_object_unref_if_floating (gpointer instance)
-         g_object_unref (instance);
- }
- 
-+static gboolean
-+ibus_panel_service_service_authorized_method (IBusService     *service,
-+                                              GDBusConnection *connection)
-+{
-+    if (ibus_service_get_connection (service) == connection)
-+        return TRUE;
-+    return FALSE;
-+}
-+
- static void
- ibus_panel_service_service_method_call (IBusService           *service,
-                                         GDBusConnection       *connection,
-@@ -961,6 +970,9 @@ ibus_panel_service_service_method_call (IBusService           *service,
-         return;
-     }
- 
-+    if (!ibus_panel_service_service_authorized_method (service, connection))
-+        return;
-+
-     if (g_strcmp0 (method_name, "UpdatePreeditText") == 0) {
-         GVariant *variant = NULL;
-         guint cursor = 0;
--- 
-2.14.3
-
-From e17c99859d06ab75326730e45072e1061f7d87c7 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Mon, 29 Jan 2018 16:50:07 +0900
-Subject: [PATCH] Implement Unicode choice on Emojier
-
-BUG=https://github.com/ibus/ibus/issues/1922
-
-Review URL: https://codereview.appspot.com/340190043
----
- configure.ac              |   41 +-
- po/POTFILES.in            |    1 +
- src/Makefile.am           |   55 ++-
- src/emoji-parser.c        |    3 +-
- src/ibus.h                |    4 +-
- src/ibusunicode.c         | 1069 +++++++++++++++++++++++++++++++++++++++++
- src/ibusunicode.h         |  299 ++++++++++++
- src/ibusunicodegen.h      | 1151 +++++++++++++++++++++++++++++++++++++++++++++
- src/unicode-parser.c      |  502 ++++++++++++++++++++
- ui/gtk3/emojier.vala      |  550 +++++++++++++++++++---
- ui/gtk3/emojierapp.vala   |    2 +
- ui/gtk3/ibusemojidialog.h |    9 +-
- ui/gtk3/panel.vala        |    3 +-
- 13 files changed, 3613 insertions(+), 76 deletions(-)
- create mode 100644 src/ibusunicode.c
- create mode 100644 src/ibusunicode.h
- create mode 100644 src/ibusunicodegen.h
- create mode 100644 src/unicode-parser.c
-
-diff --git a/configure.ac b/configure.ac
-index 4789328e..bd41069b 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -3,8 +3,8 @@
- # ibus - The Input Bus
- #
- # Copyright (c) 2007-2016 Peng Huang <shawn.p.huang@gmail.com>
--# Copyright (c) 2015-2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
--# Copyright (c) 2007-2017 Red Hat, Inc.
-+# Copyright (c) 2015-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+# Copyright (c) 2007-2018 Red Hat, Inc.
- #
- # This library is free software; you can redistribute it and/or
- # modify it under the terms of the GNU Lesser General Public
-@@ -653,6 +653,41 @@ https://github.com/fujiwarat/cldr-emoji-annotation)
-     enable_emoji_dict="yes (enabled, use --disable-emoji-dict to disable)"
- fi
- 
-+# --disable-unicode-dict option.
-+AC_ARG_ENABLE(unicode-dict,
-+    AS_HELP_STRING([--disable-unicode-dict],
-+                   [Do not build Unicode dict files]),
-+    [enable_unicode_dict=$enableval],
-+    [enable_unicode_dict=yes]
-+)
-+AM_CONDITIONAL([ENABLE_UNICODE_DICT], [test x"$enable_unicode_dict" = x"yes"])
-+
-+AC_ARG_WITH(ucd-dir,
-+    AS_HELP_STRING([--with-ucd-dir[=DIR]],
-+        [Set the directory of UCD (Unicode Character Database) files.
-+         (default: "/usr/share/unicode/ucd")]),
-+    UCD_DIR=$with_emoji_annotation_dir,
-+    UCD_DIR="/usr/share/unicode/ucd"
-+)
-+AC_SUBST(UCD_DIR)
-+
-+if test x"$enable_unicode_dict" = x"yes"; then
-+    if test ! -f $UCD_DIR/NamesList.txt ; then
-+        AC_MSG_ERROR(Not found $UCD_DIR/NamesList.txt. You can get \
-+the UCD files from https://www.unicode.org/Public/UNIDATA/)
-+    elif test ! -f $UCD_DIR/Blocks.txt ; then
-+        AC_MSG_ERROR(Not found $UCD_DIR/Blocks.txt. You can get \
-+the UCD files from https://www.unicode.org/Public/UNIDATA/)
-+    else
-+        # POSIX SHELL has no ${FOO:0:1}
-+        head=`echo "$UCD_DIR" | cut -c1`;
-+        if test $head != "/" ; then
-+            UCD_DIR=`realpath "$UCD_DIR"`
-+        fi
-+    fi
-+    enable_unicode_dict="yes (enabled, use --disable-unicode-dict to disable)"
-+fi
-+
- # Check iso-codes.
- PKG_CHECK_MODULES(ISOCODES, [
-     iso-codes
-@@ -743,6 +778,8 @@ Build options:
-   Enable Emoji dict             $enable_emoji_dict
-   Unicode Emoji directory       $UNICODE_EMOJI_DIR
-   CLDR annotation directory     $EMOJI_ANNOTATION_DIR
-+  Enable Unicode dict           $enable_unicode_dict
-+  UCD directory                 $UCD_DIR
-   Run test cases                $enable_tests
- ])
- 
-diff --git a/po/POTFILES.in b/po/POTFILES.in
-index 00f7c7f6..58d1e39d 100644
---- a/po/POTFILES.in
-+++ b/po/POTFILES.in
-@@ -51,6 +51,7 @@ src/ibuspanelservice.c
- src/ibusproxy.c
- src/ibusregistry.c
- src/ibusservice.c
-+src/ibusunicodegen.h
- src/ibusutil.c
- src/keyname-table.h
- tools/main.vala
-diff --git a/src/Makefile.am b/src/Makefile.am
-index 303250f5..1ba418d8 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -41,6 +41,7 @@ INTROSPECTION_COMPILER_ARGS = \
-     $(NULL)
- INTROSPECTION_GIRS =
- CLEANFILES =
-+noinst_PROGRAMS =
- 
- # C preprocessor flags
- AM_CPPFLAGS =                                           \
-@@ -100,6 +101,7 @@ ibus_sources =              \
-     ibusservice.c           \
-     ibusshare.c             \
-     ibustext.c              \
-+    ibusunicode.c           \
-     ibusutil.c              \
-     ibusxml.c               \
-     $(NULL)
-@@ -151,6 +153,7 @@ ibus_headers =              \
-     ibusshare.h             \
-     ibustext.h              \
-     ibustypes.h             \
-+    ibusunicode.h           \
-     ibusutil.h              \
-     ibusxml.h               \
-     $(NULL)
-@@ -169,6 +172,7 @@ ibus_private_headers =          \
-     ibusemojigen.h              \
-     ibusenginesimpleprivate.h   \
-     ibusinternal.h              \
-+    ibusunicodegen.h            \
-     keyname-table.h             \
-     $(NULL)
- noinst_HEADERS =            \
-@@ -241,7 +245,7 @@ dictdir = $(pkgdatadir)/dicts
- dict_DATA = dicts/emoji-en.dict
- LANG_FILES = $(basename $(notdir $(wildcard $(EMOJI_ANNOTATION_DIR)/*.xml)))
- 
--noinst_PROGRAMS = emoji-parser
-+noinst_PROGRAMS += emoji-parser
- 
- dicts/emoji-en.dict: emoji-parser
- 	$(AM_V_at)if test x"$(LANG_FILES)" = x ; then \
-@@ -325,12 +329,60 @@ clean-local:
- 	$(NULL)
- endif
- 
-+if ENABLE_UNICODE_DICT
-+unicodedir = $(pkgdatadir)/dicts
-+unicode_DATA = dicts/unicode-names.dict dicts/unicode-blocks.dict
-+noinst_PROGRAMS += unicode-parser
-+
-+dicts/unicode-names.dict: unicode-parser
-+	$(AM_V_at)input_file="$(UCD_DIR)/NamesList.txt"; \
-+	if test ! -f "$$input_file" ; then \
-+	    echo "WARNING: Not found $$input_file" 1>&2; \
-+	else \
-+	    $(builddir)/unicode-parser \
-+	        --input-names-list $$input_file \
-+	        --output-names-list $@; \
-+	    echo "Generated $@"; \
-+	fi;
-+
-+dicts/unicode-blocks.dict: unicode-parser
-+	$(AM_V_at)input_file="$(UCD_DIR)/Blocks.txt"; \
-+	if test ! -f "$$input_file" ; then \
-+	    echo "WARNING: Not found $$input_file" 1>&2; \
-+	else \
-+	    $(builddir)/unicode-parser \
-+	        --input-blocks $$input_file \
-+	        --output-blocks-trans ibusunicodegen.h \
-+	        --output-blocks $@; \
-+	    echo "Generated $@"; \
-+	fi;
-+
-+ibusunicodegen.h: dicts/unicode-blocks.dict
-+	$(NULL)
-+
-+unicode_parser_SOURCES =        \
-+    unicode-parser.c            \
-+    $(NULL)
-+unicode_parser_CFLAGS =         \
-+    $(GLIB2_CFLAGS)             \
-+    $(NULL)
-+unicode_parser_LDADD =          \
-+    $(GLIB2_LIBS)               \
-+    $(libibus)                  \
-+    $(NULL)
-+
-+clean-local:
-+	-rm -rf dicts
-+	$(NULL)
-+endif
-+
- EXTRA_DIST =                    \
-     emoji-parser.c              \
-     ibusversion.h.in            \
-     ibusmarshalers.list         \
-     ibusenumtypes.h.template    \
-     ibusenumtypes.c.template    \
-+    unicode-parser.c            \
-     $(NULL)
- 
- CLEANFILES +=                   \
-@@ -341,6 +393,7 @@ CLEANFILES +=                   \
- 
- DISTCLEANFILES =                \
-     ibusemojigen.h              \
-+    ibusunicodegen.h            \
-     ibusversion.h               \
-     $(NULL)
- 
-diff --git a/src/emoji-parser.c b/src/emoji-parser.c
-index fe3e4ef8..0f7c8cfb 100644
---- a/src/emoji-parser.c
-+++ b/src/emoji-parser.c
-@@ -1,7 +1,7 @@
- /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
- /* vim:set et sts=4: */
- /* ibus - The Input Bus
-- * Copyright (C) 2016-2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2016-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-  * Copyright (C) 2016 Red Hat, Inc.
-  *
-  * This library is free software; you can redistribute it and/or
-@@ -1126,6 +1126,7 @@ category_file_save (const gchar *filename,
-     if (!g_file_get_contents (__FILE__, &content, &length, &error)) {
-         g_warning ("Failed to load %s: %s", __FILE__, error->message);
-         g_clear_pointer (&error, g_error_free);
-+        return;
-     }
-     buff = g_string_new (NULL);
-     p = content;
-diff --git a/src/ibus.h b/src/ibus.h
-index c6cf58cf..8011729f 100644
---- a/src/ibus.h
-+++ b/src/ibus.h
-@@ -2,7 +2,8 @@
- /* vim:set et sts=4: */
- /* ibus - The Input Bus
-  * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
-- * Copyright (C) 2008-2013 Red Hat, Inc.
-+ * Copyright (C) 2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2008-2018 Red Hat, Inc.
-  *
-  * This library is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU Lesser General Public
-@@ -57,6 +58,7 @@
- #include <ibusutil.h>
- #include <ibusregistry.h>
- #include <ibusemoji.h>
-+#include <ibusunicode.h>
- 
- #ifndef IBUS_DISABLE_DEPRECATED
- #include <ibuskeysyms-compat.h>
-diff --git a/src/ibusunicode.c b/src/ibusunicode.c
-new file mode 100644
-index 00000000..8559819d
---- /dev/null
-+++ b/src/ibusunicode.c
-@@ -0,0 +1,1069 @@
-+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
-+/* vim:set et sts=4: */
-+/* bus - The Input Bus
-+ * Copyright (C) 2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2018 Red Hat, Inc.
-+ *
-+ * This library is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU Lesser General Public
-+ * License as published by the Free Software Foundation; either
-+ * version 2.1 of the License, or (at your option) any later version.
-+ *
-+ * This library is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-+ * Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public
-+ * License along with this library; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
-+ * USA
-+ */
-+#ifdef HAVE_CONFIG_H
-+#include <config.h>
-+#endif
-+
-+#include <glib.h>
-+#include <glib/gstdio.h>
-+#include "ibusinternal.h"
-+#include "ibuserror.h"
-+#include "ibusunicode.h"
-+
-+#define IBUS_UNICODE_DATA_MAGIC "IBusUnicodeData"
-+#define IBUS_UNICODE_BLOCK_MAGIC "IBusUnicodeBlock"
-+#define IBUS_UNICODE_DATA_VERSION (1)
-+#define IBUS_UNICODE_DESERIALIZE_SIGNALL_STR \
-+        "deserialize-unicode"
-+
-+enum {
-+    PROP_0 = 0,
-+    PROP_CODE,
-+    PROP_NAME,
-+    PROP_ALIAS,
-+    PROP_BLOCK_NAME,
-+    PROP_START,
-+    PROP_END
-+};
-+
-+struct _IBusUnicodeDataPrivate {
-+    gunichar    code;
-+    gchar      *name;
-+    gchar      *alias;
-+    gchar      *block_name;
-+};
-+
-+struct _IBusUnicodeBlockPrivate {
-+    gunichar    start;
-+    gunichar    end;
-+    gchar      *name;
-+};
-+
-+typedef struct {
-+    IBusUnicodeDataLoadAsyncFinish callback;
-+    gpointer                       user_data;
-+} IBusUnicodeDataLoadData;
-+
-+#define IBUS_UNICODE_DATA_GET_PRIVATE(o)  \
-+   (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
-+    IBUS_TYPE_UNICODE_DATA, \
-+    IBusUnicodeDataPrivate))
-+#define IBUS_UNICODE_BLOCK_GET_PRIVATE(o)  \
-+   (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
-+    IBUS_TYPE_UNICODE_BLOCK, \
-+    IBusUnicodeBlockPrivate))
-+
-+/* functions prototype */
-+static void      ibus_unicode_data_set_property (IBusUnicodeData      *unicode,
-+                                                 guint                 prop_id,
-+                                                 const GValue         *value,
-+                                                 GParamSpec           *pspec);
-+static void      ibus_unicode_data_get_property (IBusUnicodeData      *unicode,
-+                                                 guint                 prop_id,
-+                                                 GValue               *value,
-+                                                 GParamSpec           *pspec);
-+static void      ibus_unicode_data_destroy      (IBusUnicodeData      *unicode);
-+static gboolean  ibus_unicode_data_serialize    (IBusUnicodeData      *unicode,
-+                                                 GVariantBuilder      *builder);
-+static gint      ibus_unicode_data_deserialize  (IBusUnicodeData      *unicode,
-+                                                 GVariant             *variant);
-+static gboolean  ibus_unicode_data_copy         (IBusUnicodeData      *dest,
-+                                                const IBusUnicodeData *src);
-+static void      ibus_unicode_block_set_property
-+                                                (IBusUnicodeBlock     *block,
-+                                                 guint                 prop_id,
-+                                                 const GValue         *value,
-+                                                 GParamSpec           *pspec);
-+static void      ibus_unicode_block_get_property
-+                                                (IBusUnicodeBlock     *block,
-+                                                 guint                 prop_id,
-+                                                 GValue               *value,
-+                                                 GParamSpec           *pspec);
-+static void      ibus_unicode_block_destroy     (IBusUnicodeBlock     *block);
-+static gboolean  ibus_unicode_block_serialize   (IBusUnicodeBlock     *block,
-+                                                 GVariantBuilder      *builder);
-+static gint      ibus_unicode_block_deserialize (IBusUnicodeBlock     *block,
-+                                                 GVariant             *variant);
-+static gboolean  ibus_unicode_block_copy        (IBusUnicodeBlock     *dest,
-+                                                const IBusUnicodeBlock *src);
-+
-+G_DEFINE_TYPE (IBusUnicodeData, ibus_unicode_data, IBUS_TYPE_SERIALIZABLE)
-+G_DEFINE_TYPE (IBusUnicodeBlock, ibus_unicode_block, IBUS_TYPE_SERIALIZABLE)
-+
-+static void
-+ibus_unicode_data_class_init (IBusUnicodeDataClass *class)
-+{
-+    IBusObjectClass *object_class = IBUS_OBJECT_CLASS (class);
-+    GObjectClass *gobject_class = G_OBJECT_CLASS (class);
-+    IBusSerializableClass *serializable_class = IBUS_SERIALIZABLE_CLASS (class);
-+
-+    object_class->destroy = (IBusObjectDestroyFunc) ibus_unicode_data_destroy;
-+    gobject_class->set_property =
-+            (GObjectSetPropertyFunc) ibus_unicode_data_set_property;
-+    gobject_class->get_property =
-+            (GObjectGetPropertyFunc) ibus_unicode_data_get_property;
-+    serializable_class->serialize   =
-+            (IBusSerializableSerializeFunc) ibus_unicode_data_serialize;
-+    serializable_class->deserialize =
-+            (IBusSerializableDeserializeFunc) ibus_unicode_data_deserialize;
-+    serializable_class->copy        =
-+            (IBusSerializableCopyFunc) ibus_unicode_data_copy;
-+
-+    g_type_class_add_private (class, sizeof (IBusUnicodeDataPrivate));
-+
-+    /* install properties */
-+    /**
-+     * IBusUnicodeData:code:
-+     *
-+     * The Uniode code point
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_CODE,
-+                    g_param_spec_unichar ("code",
-+                        "code point",
-+                        "The Unicode code point",
-+                        0,
-+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-+
-+
-+    /**
-+     * IBusUnicodeData:name:
-+     *
-+     * The Uniode name
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_NAME,
-+                    g_param_spec_string ("name",
-+                        "name",
-+                        "The Unicode name",
-+                        "",
-+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-+
-+    /**
-+     * IBusUnicodeData:alias:
-+     *
-+     * The Uniode alias name
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_ALIAS,
-+                    g_param_spec_string ("alias",
-+                        "alias name",
-+                        "The Unicode alias name",
-+                        "",
-+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-+
-+    /**
-+     * IBusUnicodeData:block-name:
-+     *
-+     * The Uniode block name
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_BLOCK_NAME,
-+                    g_param_spec_string ("block-name",
-+                        "block name",
-+                        "The Unicode block name",
-+                        "",
-+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-+}
-+
-+static void
-+ibus_unicode_data_init (IBusUnicodeData *unicode)
-+{
-+    unicode->priv = IBUS_UNICODE_DATA_GET_PRIVATE (unicode);
-+}
-+
-+static void
-+ibus_unicode_data_destroy (IBusUnicodeData *unicode)
-+{
-+    g_clear_pointer (&unicode->priv->name, g_free);
-+    g_clear_pointer (&unicode->priv->alias, g_free);
-+    g_clear_pointer (&unicode->priv->block_name, g_free);
-+
-+    IBUS_OBJECT_CLASS (ibus_unicode_data_parent_class)->
-+            destroy (IBUS_OBJECT (unicode));
-+}
-+
-+static void
-+ibus_unicode_data_set_property (IBusUnicodeData *unicode,
-+                                guint            prop_id,
-+                                const GValue    *value,
-+                                GParamSpec      *pspec)
-+{
-+    switch (prop_id) {
-+    case PROP_CODE:
-+        g_assert (unicode->priv->code == 0);
-+        unicode->priv->code = g_value_get_uint (value);
-+        break;
-+    case PROP_NAME:
-+        g_assert (unicode->priv->name == NULL);
-+        unicode->priv->name = g_value_dup_string (value);
-+        break;
-+    case PROP_ALIAS:
-+        g_assert (unicode->priv->alias == NULL);
-+        unicode->priv->alias = g_value_dup_string (value);
-+        break;
-+    case PROP_BLOCK_NAME:
-+        g_free (unicode->priv->block_name);
-+        unicode->priv->block_name = g_value_dup_string (value);
-+        break;
-+    default:
-+        G_OBJECT_WARN_INVALID_PROPERTY_ID (unicode, prop_id, pspec);
-+    }
-+}
-+
-+static void
-+ibus_unicode_data_get_property (IBusUnicodeData *unicode,
-+                                guint            prop_id,
-+                                GValue          *value,
-+                                GParamSpec      *pspec)
-+{
-+    switch (prop_id) {
-+    case PROP_CODE:
-+        g_value_set_uint (value, ibus_unicode_data_get_code (unicode));
-+        break;
-+    case PROP_NAME:
-+        g_value_set_string (value, ibus_unicode_data_get_name (unicode));
-+        break;
-+    case PROP_ALIAS:
-+        g_value_set_string (value, ibus_unicode_data_get_alias (unicode));
-+        break;
-+    case PROP_BLOCK_NAME:
-+        g_value_set_string (value, ibus_unicode_data_get_block_name (unicode));
-+        break;
-+    default:
-+        G_OBJECT_WARN_INVALID_PROPERTY_ID (unicode, prop_id, pspec);
-+    }
-+}
-+
-+static gboolean
-+ibus_unicode_data_serialize (IBusUnicodeData   *unicode,
-+                             GVariantBuilder   *builder)
-+{
-+    gboolean retval = IBUS_SERIALIZABLE_CLASS (ibus_unicode_data_parent_class)->
-+            serialize ((IBusSerializable *)unicode, builder);
-+    g_return_val_if_fail (retval, FALSE);
-+
-+#define NOTNULL(s) ((s) != NULL ? (s) : "")
-+    /* If you will add a new property, you can append it at the end and
-+     * you should not change the serialized order of name, longname,
-+     * description, ... because the order is also used in other applications
-+     * likes ibus-qt. */
-+    g_variant_builder_add (builder, "u", unicode->priv->code);
-+    g_variant_builder_add (builder, "s", NOTNULL (unicode->priv->name));
-+    g_variant_builder_add (builder, "s", NOTNULL (unicode->priv->alias));
-+    /* Use IBusUnicodeBlock for memory usage.
-+    g_variant_builder_add (builder, "s", NOTNULL (unicode->priv->block_name));
-+     */
-+#undef NOTNULL
-+    return TRUE;
-+}
-+
-+static gint
-+ibus_unicode_data_deserialize (IBusUnicodeData *unicode,
-+                               GVariant        *variant)
-+{
-+    gint retval = IBUS_SERIALIZABLE_CLASS (ibus_unicode_data_parent_class)->
-+            deserialize ((IBusSerializable *)unicode, variant);
-+    g_return_val_if_fail (retval, 0);
-+
-+    /* If you will add a new property, you can append it at the end and
-+     * you should not change the serialized order of name, longname,
-+     * description, ... because the order is also used in other applications
-+     * likes ibus-qt. */
-+    g_variant_get_child (variant, retval++, "u", &unicode->priv->code);
-+    ibus_g_variant_get_child_string (variant, retval++,
-+                                     &unicode->priv->name);
-+    ibus_g_variant_get_child_string (variant, retval++,
-+                                     &unicode->priv->alias);
-+    /* Use IBusUnicodeBlock for memory usage.
-+    ibus_g_variant_get_child_string (variant, retval++,
-+                                     &unicode->priv->block_name);
-+     */
-+    return retval;
-+}
-+
-+static gboolean
-+ibus_unicode_data_copy (IBusUnicodeData       *dest,
-+                        const IBusUnicodeData *src)
-+{
-+    gboolean retval = IBUS_SERIALIZABLE_CLASS (ibus_unicode_data_parent_class)->
-+            copy ((IBusSerializable *)dest,
-+                  (IBusSerializable *)src);
-+    g_return_val_if_fail (retval, FALSE);
-+
-+    dest->priv->code             = src->priv->code;
-+    dest->priv->name             = g_strdup (src->priv->name);
-+    dest->priv->alias            = g_strdup (src->priv->alias);
-+    dest->priv->block_name       = g_strdup (src->priv->block_name);
-+    return TRUE;
-+}
-+
-+IBusUnicodeData *
-+ibus_unicode_data_new (const gchar *first_property_name, ...)
-+{
-+    va_list var_args;
-+    IBusUnicodeData *unicode;
-+
-+    g_assert (first_property_name != NULL);
-+    va_start (var_args, first_property_name);
-+    unicode = (IBusUnicodeData *) g_object_new_valist (IBUS_TYPE_UNICODE_DATA,
-+                                                       first_property_name,
-+                                                       var_args);
-+    va_end (var_args);
-+    /* code is required. Other properties are set in class_init by default. */
-+    g_assert (unicode->priv->name != NULL);
-+    g_assert (unicode->priv->alias != NULL);
-+    g_assert (unicode->priv->block_name != NULL);
-+    return unicode;
-+}
-+
-+gunichar
-+ibus_unicode_data_get_code (IBusUnicodeData *unicode)
-+{
-+    g_return_val_if_fail (IBUS_IS_UNICODE_DATA (unicode), G_MAXUINT32);
-+
-+    return unicode->priv->code;
-+}
-+
-+const gchar *
-+ibus_unicode_data_get_name (IBusUnicodeData *unicode)
-+{
-+    g_return_val_if_fail (IBUS_IS_UNICODE_DATA (unicode), "");
-+
-+    return unicode->priv->name;
-+}
-+
-+const gchar *
-+ibus_unicode_data_get_alias (IBusUnicodeData *unicode)
-+{
-+    g_return_val_if_fail (IBUS_IS_UNICODE_DATA (unicode), "");
-+
-+    return unicode->priv->alias;
-+}
-+
-+const gchar *
-+ibus_unicode_data_get_block_name (IBusUnicodeData *unicode)
-+{
-+    g_return_val_if_fail (IBUS_IS_UNICODE_DATA (unicode), "");
-+
-+    return unicode->priv->block_name;
-+}
-+
-+void
-+ibus_unicode_data_set_block_name (IBusUnicodeData *unicode,
-+                                  const gchar     *block_name)
-+{
-+    g_return_if_fail (IBUS_IS_UNICODE_DATA (unicode));
-+
-+    g_free (unicode->priv->block_name);
-+    unicode->priv->block_name = g_strdup (block_name);
-+}
-+
-+static void
-+variant_foreach_add_unicode (IBusUnicodeData *unicode,
-+                             GVariantBuilder *builder)
-+{
-+    g_variant_builder_add (
-+            builder, "v",
-+            ibus_serializable_serialize (IBUS_SERIALIZABLE (unicode)));
-+}
-+
-+static GVariant *
-+ibus_unicode_data_list_serialize (GSList *list)
-+{
-+    GVariantBuilder builder;
-+
-+    g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
-+    g_slist_foreach (list,  (GFunc) variant_foreach_add_unicode, &builder);
-+    return g_variant_builder_end (&builder);
-+}
-+
-+static GSList *
-+ibus_unicode_data_list_deserialize (GVariant *variant,
-+                                    GObject  *source_object)
-+{
-+    GSList *list = NULL;
-+    GVariantIter iter;
-+    GVariant *unicode_variant = NULL;
-+    gsize i, size;
-+    gboolean has_signal = FALSE;
-+
-+    if (G_IS_OBJECT (source_object)) {
-+        has_signal = g_signal_lookup (
-+                IBUS_UNICODE_DESERIALIZE_SIGNALL_STR,
-+                G_OBJECT_TYPE (source_object));
-+        if (!has_signal) {
-+            const gchar type_name = g_type_name (source_object);
-+            g_warning ("GObject %s does not have the signal \"%s\"",
-+                       type_name ? type_name : "(null)",
-+                       IBUS_UNICODE_DESERIALIZE_SIGNALL_STR);
-+        }
-+    }
-+    g_variant_iter_init (&iter, variant);
-+    size = g_variant_iter_n_children (&iter);
-+    i = 0;
-+    while (g_variant_iter_loop (&iter, "v", &unicode_variant)) {
-+        IBusUnicodeData *data =
-+                IBUS_UNICODE_DATA (ibus_serializable_deserialize (
-+                        unicode_variant));
-+        list = g_slist_append (list, data);
-+        g_clear_pointer (&unicode_variant, g_variant_unref);
-+        if (has_signal && (i == 0 || ((i + 1) % 100) == 0)) {
-+            g_signal_emit_by_name (source_object,
-+                                   IBUS_UNICODE_DESERIALIZE_SIGNALL_STR,
-+                                   i + 1, size);
-+        }
-+        i++;
-+    }
-+    if (has_signal && (i != 1 && (i % 100) != 0)) {
-+        g_signal_emit_by_name (source_object,
-+                               IBUS_UNICODE_DESERIALIZE_SIGNALL_STR,
-+                               i, size);
-+    }
-+
-+    return list;
-+}
-+
-+void
-+ibus_unicode_data_save (const gchar *path,
-+                        GSList      *list)
-+{
-+    GVariant *variant;
-+    const gchar *header = IBUS_UNICODE_DATA_MAGIC;
-+    const guint16 version = IBUS_UNICODE_DATA_VERSION;
-+    const gchar *contents;
-+    gsize length;
-+    gchar *dir;
-+    GStatBuf buf = { 0, };
-+    GError *error = NULL;
-+
-+    g_return_if_fail (path != NULL);
-+    g_return_if_fail (list != NULL);
-+    if (list->data == NULL) {
-+        g_warning ("Failed to save IBus Unicode data: Need a list data.");
-+        return;
-+    }
-+
-+    variant = g_variant_new ("(sqv)",
-+                             header,
-+                             version,
-+                             ibus_unicode_data_list_serialize (list));
-+
-+    contents =  g_variant_get_data (variant);
-+    length =  g_variant_get_size (variant);
-+
-+    dir = g_path_get_dirname (path);
-+    if (g_strcmp0 (dir, ".") != 0 && g_stat (dir, &buf) != 0) {
-+        g_mkdir_with_parents (dir, 0777);
-+    }
-+    g_free (dir);
-+    if (!g_file_set_contents (path, contents, length, &error)) {
-+        g_warning ("Failed to save Unicode dict %s: %s", path, error->message);
-+        g_error_free (error);
-+    }
-+
-+    g_variant_unref (variant);
-+}
-+
-+static GSList *
-+ibus_unicode_data_load_with_error (const gchar *path,
-+                                   GObject     *source_object,
-+                                   GError     **error)
-+{
-+    gchar *contents = NULL;
-+    gsize length = 0;
-+    GVariant *variant_table = NULL;
-+    GVariant *variant = NULL;
-+    const gchar *header = NULL;
-+    guint16 version = 0;
-+    GSList *retval = NULL;
-+
-+    if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
-+        g_set_error (error,
-+                     IBUS_ERROR,
-+                     IBUS_ERROR_FAILED,
-+                     "Unicode dict does not exist: %s", path);
-+        goto out_load_cache;
-+    }
-+
-+    if (!g_file_get_contents (path, &contents, &length, error)) {
-+        goto out_load_cache;
-+    }
-+
-+    variant_table = g_variant_new_from_data (G_VARIANT_TYPE ("(sq)"),
-+                                             contents,
-+                                             length,
-+                                             FALSE,
-+                                             NULL,
-+                                             NULL);
-+
-+    if (variant_table == NULL) {
-+        g_set_error (error,
-+                     IBUS_ERROR,
-+                     IBUS_ERROR_FAILED,
-+                     "cache table is broken.");
-+        goto out_load_cache;
-+    }
-+
-+    g_variant_get (variant_table, "(&sq)", &header, &version);
-+
-+    if (g_strcmp0 (header, IBUS_UNICODE_DATA_MAGIC) != 0) {
-+        g_set_error (error,
-+                     IBUS_ERROR,
-+                     IBUS_ERROR_FAILED,
-+                     "cache is not IBusUnicodeData.");
-+        goto out_load_cache;
-+    }
-+
-+    if (version > IBUS_UNICODE_DATA_VERSION) {
-+        g_set_error (error,
-+                     IBUS_ERROR,
-+                     IBUS_ERROR_FAILED,
-+                     "cache version is different: %u != %u",
-+                     version, IBUS_UNICODE_DATA_VERSION);
-+        goto out_load_cache;
-+    }
-+
-+    version = 0;
-+    header = NULL;
-+    g_variant_unref (variant_table);
-+
-+    variant_table = g_variant_new_from_data (G_VARIANT_TYPE ("(sqv)"),
-+                                             contents,
-+                                             length,
-+                                             FALSE,
-+                                             NULL,
-+                                             NULL);
-+
-+    if (variant_table == NULL) {
-+        g_set_error (error,
-+                     IBUS_ERROR,
-+                     IBUS_ERROR_FAILED,
-+                     "cache table is broken.");
-+        goto out_load_cache;
-+    }
-+
-+    g_variant_get (variant_table, "(&sqv)",
-+                   NULL,
-+                   NULL,
-+                   &variant);
-+
-+    if (variant == NULL) {
-+        g_set_error (error,
-+                     IBUS_ERROR,
-+                     IBUS_ERROR_FAILED,
-+                     "cache dict is broken.");
-+        goto out_load_cache;
-+    }
-+
-+    retval = ibus_unicode_data_list_deserialize (variant, source_object);
-+
-+out_load_cache:
-+    if (variant)
-+        g_variant_unref (variant);
-+    if (variant_table)
-+        g_variant_unref (variant_table);
-+    g_free (contents);
-+
-+    return retval;
-+}
-+
-+GSList *
-+ibus_unicode_data_load (const gchar *path,
-+                        GObject     *source_object)
-+{
-+    GError *error = NULL;
-+    GSList *retval = ibus_unicode_data_load_with_error (path,
-+                                                        source_object,
-+                                                        &error);
-+
-+    if (retval == NULL) {
-+        g_warning ("%s", error->message);
-+        g_error_free (error);
-+    }
-+
-+    return retval;
-+}
-+
-+static void
-+ibus_unicode_data_load_async_thread (GTask        *task,
-+                                     gpointer      source_object,
-+                                     gpointer      task_data,
-+                                     GCancellable *cancellable)
-+{
-+    GSList *retval;
-+    gchar *path = (gchar *)task_data;
-+    GError *error = NULL;
-+
-+    g_assert (path != NULL);
-+
-+    retval = ibus_unicode_data_load_with_error (path, source_object, &error);
-+    g_free (path);
-+    if (retval == NULL)
-+        g_task_return_error (task, error);
-+    else
-+        g_task_return_pointer (task, retval, NULL);
-+    g_object_unref (task);
-+}
-+
-+static void
-+ibus_unicode_data_load_async_done (GObject *source_object,
-+                                   GAsyncResult *res,
-+                                   gpointer user_data)
-+{
-+    IBusUnicodeDataLoadData *data = (IBusUnicodeDataLoadData*)user_data;
-+    GSList *list;
-+    GError *error = NULL;
-+    g_assert (data != NULL);
-+    list = g_task_propagate_pointer (G_TASK (res), &error);
-+    if (error) {
-+        g_warning ("%s", error->message);
-+        g_error_free (error);
-+        data->callback (NULL, data->user_data);
-+    } else {
-+        data->callback (list, data->user_data);
-+    }
-+    g_slice_free (IBusUnicodeDataLoadData, data);
-+}
-+
-+void
-+ibus_unicode_data_load_async (const gchar        *path,
-+                              GObject            *source_object,
-+                              GCancellable       *cancellable,
-+                              IBusUnicodeDataLoadAsyncFinish
-+                                                  callback,
-+                              gpointer            user_data)
-+{
-+    GTask *task;
-+    IBusUnicodeDataLoadData *data;
-+
-+    g_return_if_fail (path != NULL);
-+
-+    data = g_slice_new0 (IBusUnicodeDataLoadData);
-+    data->callback = callback;
-+    data->user_data = user_data;
-+    task = g_task_new (source_object,
-+                       cancellable,
-+                       ibus_unicode_data_load_async_done,
-+                       data);
-+    g_task_set_source_tag (task, ibus_unicode_data_load_async);
-+    g_task_set_task_data (task, g_strdup (path), NULL);
-+    g_task_run_in_thread (task, ibus_unicode_data_load_async_thread);
-+}
-+
-+static void
-+ibus_unicode_block_class_init (IBusUnicodeBlockClass *class)
-+{
-+    IBusObjectClass *object_class = IBUS_OBJECT_CLASS (class);
-+    GObjectClass *gobject_class = G_OBJECT_CLASS (class);
-+    IBusSerializableClass *serializable_class = IBUS_SERIALIZABLE_CLASS (class);
-+
-+    object_class->destroy = (IBusObjectDestroyFunc) ibus_unicode_data_destroy;
-+    gobject_class->set_property =
-+            (GObjectSetPropertyFunc) ibus_unicode_block_set_property;
-+    gobject_class->get_property =
-+            (GObjectGetPropertyFunc) ibus_unicode_block_get_property;
-+    serializable_class->serialize   =
-+            (IBusSerializableSerializeFunc) ibus_unicode_block_serialize;
-+    serializable_class->deserialize =
-+            (IBusSerializableDeserializeFunc) ibus_unicode_block_deserialize;
-+    serializable_class->copy        =
-+            (IBusSerializableCopyFunc) ibus_unicode_block_copy;
-+
-+    g_type_class_add_private (class, sizeof (IBusUnicodeBlockPrivate));
-+
-+    /* install properties */
-+    /**
-+     * IBusUnicodeBlock:start:
-+     *
-+     * The Uniode start code point
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_START,
-+                    /* Cannot use g_param_spec_unichar() for the Unicode
-+                     * boundary values because the function checks
-+                     * if the value is a valid Unicode besides MAXUINT.
-+                     */
-+                    g_param_spec_uint ("start",
-+                        "start code point",
-+                        "The Unicode start code point",
-+                        0,
-+                        G_MAXUINT,
-+                        0,
-+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-+
-+
-+    /**
-+     * IBusUnicodeBlock:end:
-+     *
-+     * The Uniode end code point
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_END,
-+                    /* Cannot use g_param_spec_unichar() for the Unicode
-+                     * boundary values because the function checks
-+                     * if the value is a valid Unicode besides MAXUINT.
-+                     */
-+                    g_param_spec_uint ("end",
-+                        "end code point",
-+                        "The Unicode end code point",
-+                        0,
-+                        G_MAXUINT,
-+                        0,
-+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-+
-+
-+    /**
-+     * IBusUnicodeBlock:name:
-+     *
-+     * The Uniode block name
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_NAME,
-+                    g_param_spec_string ("name",
-+                        "name",
-+                        "The Unicode name",
-+                        "",
-+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-+}
-+
-+static void
-+ibus_unicode_block_init (IBusUnicodeBlock *block)
-+{
-+    block->priv = IBUS_UNICODE_BLOCK_GET_PRIVATE (block);
-+}
-+
-+static void
-+ibus_unicode_block_destroy (IBusUnicodeBlock *block)
-+{
-+    g_clear_pointer (&block->priv->name, g_free);
-+
-+    IBUS_OBJECT_CLASS (ibus_unicode_data_parent_class)->
-+            destroy (IBUS_OBJECT (block));
-+}
-+
-+static void
-+ibus_unicode_block_set_property (IBusUnicodeBlock *block,
-+                                 guint             prop_id,
-+                                 const GValue     *value,
-+                                 GParamSpec       *pspec)
-+{
-+    switch (prop_id) {
-+    case PROP_START:
-+        g_assert (block->priv->start == 0);
-+        block->priv->start = g_value_get_uint (value);
-+        break;
-+    case PROP_END:
-+        g_assert (block->priv->end == 0);
-+        block->priv->end = g_value_get_uint (value);
-+        break;
-+    case PROP_NAME:
-+        g_assert (block->priv->name == NULL);
-+        block->priv->name = g_value_dup_string (value);
-+        break;
-+    default:
-+        G_OBJECT_WARN_INVALID_PROPERTY_ID (block, prop_id, pspec);
-+    }
-+}
-+
-+static void
-+ibus_unicode_block_get_property (IBusUnicodeBlock *block,
-+                                 guint             prop_id,
-+                                 GValue           *value,
-+                                 GParamSpec       *pspec)
-+{
-+    switch (prop_id) {
-+    case PROP_START:
-+        g_value_set_uint (value, ibus_unicode_block_get_start (block));
-+        break;
-+    case PROP_END:
-+        g_value_set_uint (value, ibus_unicode_block_get_end (block));
-+        break;
-+    case PROP_NAME:
-+        g_value_set_string (value, ibus_unicode_block_get_name (block));
-+        break;
-+    default:
-+        G_OBJECT_WARN_INVALID_PROPERTY_ID (block, prop_id, pspec);
-+    }
-+}
-+
-+static gboolean
-+ibus_unicode_block_serialize (IBusUnicodeBlock *block,
-+                              GVariantBuilder  *builder)
-+{
-+    gboolean retval = IBUS_SERIALIZABLE_CLASS (ibus_unicode_block_parent_class)->
-+            serialize ((IBusSerializable *)block, builder);
-+    g_return_val_if_fail (retval, FALSE);
-+
-+#define NOTNULL(s) ((s) != NULL ? (s) : "")
-+    /* If you will add a new property, you can append it at the end and
-+     * you should not change the serialized order of name, longname,
-+     * description, ... because the order is also used in other applications
-+     * likes ibus-qt. */
-+    g_variant_builder_add (builder, "u", block->priv->start);
-+    g_variant_builder_add (builder, "u", block->priv->end);
-+    g_variant_builder_add (builder, "s", NOTNULL (block->priv->name));
-+#undef NOTNULL
-+    return TRUE;
-+}
-+
-+static gint
-+ibus_unicode_block_deserialize (IBusUnicodeBlock *block,
-+                                GVariant        *variant)
-+{
-+    gint retval = IBUS_SERIALIZABLE_CLASS (ibus_unicode_block_parent_class)->
-+            deserialize ((IBusSerializable *)block, variant);
-+    g_return_val_if_fail (retval, 0);
-+
-+    /* If you will add a new property, you can append it at the end and
-+     * you should not change the serialized order of name, longname,
-+     * description, ... because the order is also used in other applications
-+     * likes ibus-qt. */
-+    g_variant_get_child (variant, retval++, "u", &block->priv->start);
-+    g_variant_get_child (variant, retval++, "u", &block->priv->end);
-+    ibus_g_variant_get_child_string (variant, retval++,
-+                                     &block->priv->name);
-+    return retval;
-+}
-+
-+static gboolean
-+ibus_unicode_block_copy (IBusUnicodeBlock       *dest,
-+                         const IBusUnicodeBlock *src)
-+{
-+    gboolean retval = IBUS_SERIALIZABLE_CLASS (ibus_unicode_block_parent_class)->
-+            copy ((IBusSerializable *)dest,
-+                  (IBusSerializable *)src);
-+    g_return_val_if_fail (retval, FALSE);
-+
-+    dest->priv->start            = src->priv->start;
-+    dest->priv->end              = src->priv->end;
-+    dest->priv->name             = g_strdup (src->priv->name);
-+    return TRUE;
-+}
-+
-+IBusUnicodeBlock *
-+ibus_unicode_block_new (const gchar *first_property_name, ...)
-+{
-+    va_list var_args;
-+    IBusUnicodeBlock *block;
-+
-+    g_assert (first_property_name != NULL);
-+    va_start (var_args, first_property_name);
-+    block = (IBusUnicodeBlock *) g_object_new_valist (IBUS_TYPE_UNICODE_BLOCK,
-+                                                     first_property_name,
-+                                                     var_args);
-+    va_end (var_args);
-+    /* end is required. Other properties are set in class_init by default. */
-+    g_assert (block->priv->start != block->priv->end);
-+    g_assert (block->priv->name != NULL);
-+    return block;
-+}
-+
-+gunichar
-+ibus_unicode_block_get_start (IBusUnicodeBlock *block)
-+{
-+    g_return_val_if_fail (IBUS_IS_UNICODE_BLOCK (block), G_MAXUINT32);
-+
-+    return block->priv->start;
-+}
-+
-+gunichar
-+ibus_unicode_block_get_end (IBusUnicodeBlock *block)
-+{
-+    g_return_val_if_fail (IBUS_IS_UNICODE_BLOCK (block), G_MAXUINT32);
-+
-+    return block->priv->end;
-+}
-+
-+const gchar *
-+ibus_unicode_block_get_name (IBusUnicodeBlock *block)
-+{
-+    g_return_val_if_fail (IBUS_IS_UNICODE_BLOCK (block), "");
-+
-+    return block->priv->name;
-+}
-+
-+static void
-+variant_foreach_add_block (IBusUnicodeBlock *block,
-+                           GVariantBuilder *builder)
-+{
-+    g_variant_builder_add (
-+            builder, "v",
-+            ibus_serializable_serialize (IBUS_SERIALIZABLE (block)));
-+}
-+
-+static GVariant *
-+ibus_unicode_block_list_serialize (GSList *list)
-+{
-+    GVariantBuilder builder;
-+
-+    g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
-+    g_slist_foreach (list,  (GFunc) variant_foreach_add_block, &builder);
-+    return g_variant_builder_end (&builder);
-+}
-+
-+static GSList *
-+ibus_unicode_block_list_deserialize (GVariant *variant)
-+{
-+    GSList *list = NULL;
-+    GVariantIter iter;
-+    GVariant *unicode_variant = NULL;
-+
-+    g_variant_iter_init (&iter, variant);
-+    while (g_variant_iter_loop (&iter, "v", &unicode_variant)) {
-+        IBusUnicodeBlock *data =
-+                IBUS_UNICODE_BLOCK (ibus_serializable_deserialize (
-+                        unicode_variant));
-+        list = g_slist_append (list, data);
-+        g_clear_pointer (&unicode_variant, g_variant_unref);
-+    }
-+
-+    return list;
-+}
-+
-+void
-+ibus_unicode_block_save (const gchar *path,
-+                         GSList      *list)
-+{
-+    GVariant *variant;
-+    const gchar *header = IBUS_UNICODE_BLOCK_MAGIC;
-+    const guint16 version = IBUS_UNICODE_DATA_VERSION;
-+    const gchar *contents;
-+    gsize length;
-+    gchar *dir;
-+    GStatBuf buf = { 0, };
-+    GError *error = NULL;
-+
-+    g_return_if_fail (path != NULL);
-+    g_return_if_fail (list != NULL);
-+    if (list->data == NULL) {
-+        g_warning ("Failed to save IBus Unicode block: Need a list data.");
-+        return;
-+    }
-+
-+    variant = g_variant_new ("(sqv)",
-+                             header,
-+                             version,
-+                             ibus_unicode_block_list_serialize (list));
-+
-+    contents =  g_variant_get_data (variant);
-+    length =  g_variant_get_size (variant);
-+
-+    dir = g_path_get_dirname (path);
-+    if (g_strcmp0 (dir, ".") != 0 && g_stat (dir, &buf) != 0) {
-+        g_mkdir_with_parents (dir, 0777);
-+    }
-+    g_free (dir);
-+    if (!g_file_set_contents (path, contents, length, &error)) {
-+        g_warning ("Failed to save Unicode dict %s: %s", path, error->message);
-+        g_error_free (error);
-+    }
-+
-+    g_variant_unref (variant);
-+}
-+
-+GSList *
-+ibus_unicode_block_load (const gchar *path)
-+{
-+    gchar *contents = NULL;
-+    gsize length = 0;
-+    GError *error = NULL;
-+    GVariant *variant_table = NULL;
-+    GVariant *variant = NULL;
-+    const gchar *header = NULL;
-+    guint16 version = 0;
-+    GSList *retval = NULL;
-+
-+    if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
-+        g_warning ("Unicode dict does not exist: %s", path);
-+        goto out_load_cache;
-+    }
-+
-+    if (!g_file_get_contents (path, &contents, &length, &error)) {
-+        g_warning ("Failed to get dict content %s: %s", path, error->message);
-+        g_error_free (error);
-+        goto out_load_cache;
-+    }
-+
-+    variant_table = g_variant_new_from_data (G_VARIANT_TYPE ("(sq)"),
-+                                             contents,
-+                                             length,
-+                                             FALSE,
-+                                             NULL,
-+                                             NULL);
-+
-+    if (variant_table == NULL) {
-+        g_warning ("cache table is broken.");
-+        goto out_load_cache;
-+    }
-+
-+    g_variant_get (variant_table, "(&sq)", &header, &version);
-+
-+    if (g_strcmp0 (header, IBUS_UNICODE_BLOCK_MAGIC) != 0) {
-+        g_warning ("cache is not IBusUnicodeBlock.");
-+        goto out_load_cache;
-+    }
-+
-+    if (version > IBUS_UNICODE_DATA_VERSION) {
-+        g_warning ("cache version is different: %u != %u",
-+                   version, IBUS_UNICODE_DATA_VERSION);
-+        goto out_load_cache;
-+    }
-+
-+    version = 0;
-+    header = NULL;
-+    g_variant_unref (variant_table);
-+
-+    variant_table = g_variant_new_from_data (G_VARIANT_TYPE ("(sqv)"),
-+                                             contents,
-+                                             length,
-+                                             FALSE,
-+                                             NULL,
-+                                             NULL);
-+
-+    if (variant_table == NULL) {
-+        g_warning ("cache table is broken.");
-+        goto out_load_cache;
-+    }
-+
-+    g_variant_get (variant_table, "(&sqv)",
-+                   NULL,
-+                   NULL,
-+                   &variant);
-+
-+    if (variant == NULL) {
-+        g_warning ("cache dict is broken.");
-+        goto out_load_cache;
-+    }
-+
-+    retval = ibus_unicode_block_list_deserialize (variant);
-+
-+out_load_cache:
-+    if (variant)
-+        g_variant_unref (variant);
-+    if (variant_table)
-+        g_variant_unref (variant_table);
-+    g_free (contents);
-+
-+    return retval;
-+}
-+
-diff --git a/src/ibusunicode.h b/src/ibusunicode.h
-new file mode 100644
-index 00000000..99de9451
---- /dev/null
-+++ b/src/ibusunicode.h
-@@ -0,0 +1,299 @@
-+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
-+/* vim:set et sts=4: */
-+/* bus - The Input Bus
-+ * Copyright (C) 2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2018 Red Hat, Inc.
-+ *
-+ * This library is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU Lesser General Public
-+ * License as published by the Free Software Foundation; either
-+ * version 2.1 of the License, or (at your option) any later version.
-+ *
-+ * This library is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-+ * Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public
-+ * License along with this library; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
-+ * USA
-+ */
-+
-+#if !defined (__IBUS_H_INSIDE__) && !defined (IBUS_COMPILATION)
-+#error "Only <ibus.h> can be included directly"
-+#endif
-+
-+#ifndef __IBUS_UNICODE_H_
-+#define __IBUS_UNICODE_H_
-+
-+/**
-+ * SECTION: ibusunicode
-+ * @short_description: unicode utility.
-+ * @stability: Unstable
-+ *
-+ * miscellaneous unicode APIs.
-+ */
-+
-+#include <gio/gio.h>
-+#include "ibusserializable.h"
-+
-+/*
-+ * Type macros.
-+ */
-+/* define GOBJECT macros */
-+#define IBUS_TYPE_UNICODE_DATA       (ibus_unicode_data_get_type ())
-+#define IBUS_UNICODE_DATA(obj)       (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
-+                                      IBUS_TYPE_UNICODE_DATA, IBusUnicodeData))
-+#define IBUS_UNICODE_DATA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \
-+                                      IBUS_TYPE_UNICODE_DATA, \
-+                                      IBusUnicodeDataClass))
-+#define IBUS_IS_UNICODE_DATA(obj)    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
-+                                      IBUS_TYPE_UNICODE_DATA))
-+#define IBUS_TYPE_UNICODE_BLOCK      (ibus_unicode_block_get_type ())
-+#define IBUS_UNICODE_BLOCK(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
-+                                      IBUS_TYPE_UNICODE_BLOCK, \
-+                                      IBusUnicodeBlock))
-+#define IBUS_UNICODE_BLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \
-+                                      IBUS_TYPE_UNICODE_BLOCK, \
-+                                      IBusUnicodeBlockClass))
-+#define IBUS_IS_UNICODE_BLOCK(obj)   (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
-+                                      IBUS_TYPE_UNICODE_BLOCK))
-+
-+
-+G_BEGIN_DECLS
-+
-+typedef struct _IBusUnicodeData IBusUnicodeData;
-+typedef struct _IBusUnicodeDataPrivate IBusUnicodeDataPrivate;
-+typedef struct _IBusUnicodeDataClass IBusUnicodeDataClass;
-+typedef struct _IBusUnicodeBlock IBusUnicodeBlock;
-+typedef struct _IBusUnicodeBlockPrivate IBusUnicodeBlockPrivate;
-+typedef struct _IBusUnicodeBlockClass IBusUnicodeBlockClass;
-+
-+/**
-+ * IBusUnicodeDataLoadAsyncFinish:
-+ * @data_list: (transfer full) (element-type IBusUnicodeData):
-+ *
-+ * This callback can receive the list of #IBusUnicodeData.
-+ */
-+typedef void (*IBusUnicodeDataLoadAsyncFinish) (GSList  *data_list,
-+                                                gpointer user_data);
-+
-+/**
-+ * IBusUnicodeData:
-+ *
-+ * Unicode data likes code, name, alias, block-name.
-+ * You can get extended values with g_object_get_properties.
-+ */
-+struct _IBusUnicodeData {
-+    IBusSerializable parent;
-+    /* instance members */
-+
-+    /*< public >*/
-+    /*< private >*/
-+    IBusUnicodeDataPrivate *priv;
-+};
-+
-+struct _IBusUnicodeDataClass {
-+    IBusSerializableClass parent;
-+    /* class members */
-+};
-+
-+struct _IBusUnicodeBlock {
-+    IBusSerializable parent;
-+    /* instance members */
-+
-+    /*< public >*/
-+    /*< private >*/
-+    IBusUnicodeBlockPrivate *priv;
-+};
-+
-+struct _IBusUnicodeBlockClass {
-+    IBusSerializableClass parent;
-+    /* class members */
-+};
-+
-+GType           ibus_unicode_data_get_type    (void);
-+GType           ibus_unicode_block_get_type   (void);
-+
-+/**
-+ * ibus_unicode_data_new:
-+ * @first_property_name: Name of the first property.
-+ * @...: the NULL-terminated arguments of the properties and values.
-+ *
-+ * Creates a new #IBusUnicodeData.
-+ * code property is required. e.g.
-+ * ibus_unicode_data_new ("code", 0x3042, NULL)
-+ *
-+ * Returns: A newly allocated #IBusUnicodeData.
-+ */
-+IBusUnicodeData * ibus_unicode_data_new       (const gchar *first_property_name,
-+                                               ...);
-+
-+/**
-+ * ibus_unicode_data_get_code:
-+ * @unicode: An #IBusUnicodeData
-+ *
-+ * Gets the code point in #IBusUnicodeData.
-+ *
-+ * Returns: code property in #IBusUnicodeData
-+ */
-+gunichar          ibus_unicode_data_get_code  (IBusUnicodeData    *unicode);
-+
-+/**
-+ * ibus_unicode_data_get_name:
-+ * @unicode: An #IBusUnicodeData
-+ *
-+ * Gets the name in #IBusUnicodeData. It should not be freed.
-+ *
-+ * Returns: name property in #IBusUnicodeData
-+ */
-+const gchar *     ibus_unicode_data_get_name  (IBusUnicodeData    *unicode);
-+
-+/**
-+ * ibus_unicode_data_get_alias:
-+ * @unicode: An #IBusUnicodeData
-+ *
-+ * Gets the alias in #IBusUnicodeData. It should not be freed.
-+ *
-+ * Returns: alias property in #IBusUnicodeData
-+ */
-+const gchar *     ibus_unicode_data_get_alias (IBusUnicodeData    *unicode);
-+
-+/**
-+ * ibus_unicode_data_get_block_name:
-+ * @unicode: An #IBusUnicodeData
-+ *
-+ * Gets the block name in #IBusUnicodeData. It should not be freed.
-+ *
-+ * Returns: block-name property in #IBusUnicodeData
-+ */
-+const gchar *     ibus_unicode_data_get_block_name
-+                                              (IBusUnicodeData    *unicode);
-+
-+/**
-+ * ibus_unicode_data_set_block_name:
-+ * @unicode: An #IBusUnicodeData
-+ * @block_name: A block name
-+ *
-+ * Sets the block name in #IBusUnicodeData.
-+ */
-+void              ibus_unicode_data_set_block_name
-+                                              (IBusUnicodeData    *unicode,
-+                                               const gchar        *block_name);
-+
-+/**
-+ * ibus_unicode_data_save:
-+ * @path: A path of the saved Unicode data.
-+ * @list: (element-type IBusUnicodeData) (transfer none): A list of unicode
-+ *  data.
-+ *
-+ * Save the list of #IBusUnicodeData to the cache file.
-+ */
-+void              ibus_unicode_data_save      (const gchar        *path,
-+                                               GSList             *list);
-+
-+/**
-+ * ibus_unicode_data_load:
-+ * @path: A path of the saved dictionary file.
-+ * @object: (nullable): If the #GObject has "unicode-deserialize-progress"
-+ *    signal, this function will emit (the number of desrialized
-+ *    #IBusUnicodeData, * the total number of #IBusUnicodeData) of uint values
-+ *    with that signal by 100 times. Otherwise %NULL.
-+ *
-+ * Returns: (element-type IBusUnicodeData) (transfer container):
-+ * An #IBusUnicodeData list loaded from the saved cache file.
-+ */
-+GSList *          ibus_unicode_data_load      (const gchar        *path,
-+                                               GObject            *object);
-+
-+/**
-+ * ibus_unicode_data_load_async:
-+ * @path: A path of the saved dictionary file.
-+ * @object: (nullable): If the #GObject has "unicode-deserialize-progress"
-+ *    signal, this function will emit (the number of desrialized
-+ *    #IBusUnicodeData, * the total number of #IBusUnicodeData) of uint values
-+ *    with that signal by 100 times. Otherwise %NULL.
-+ * @cancellable: cancellable.
-+ * @callback: (scope notified): IBusUnicodeDataLoadAsyncFinish.
-+ * @user_data: User data.
-+ *
-+ * IBusUnicodeDataLoadAsyncFinish can receive the list of #IBusUnicodeData.
-+ */
-+void              ibus_unicode_data_load_async
-+                                              (const gchar        *path,
-+                                               GObject            *object,
-+                                               GCancellable       *cancellable,
-+                                               IBusUnicodeDataLoadAsyncFinish
-+                                                                   callback,
-+                                               gpointer            user_data);
-+
-+/**
-+ * ibus_unicode_block_new:
-+ * @first_property_name: Name of the first property.
-+ * @...: the NULL-terminated arguments of the properties and values.
-+ *
-+ * Creates a new #IBusUnicodeBlock.
-+ * block property is required. e.g.
-+ * ibus_unicode_block_new ("start", 0x0000, "end", "0x007f", "name", "basic",
-+ * NULL)
-+ *
-+ * Returns: A newly allocated #IBusUnicodeBlock.
-+ */
-+IBusUnicodeBlock *ibus_unicode_block_new      (const gchar *first_property_name,
-+                                               ...);
-+
-+/**
-+ * ibus_unicode_block_get_start:
-+ * @block: An #IBusUnicodeData
-+ *
-+ * Gets the start code point in #IBusUnicodeBlock.
-+ *
-+ * Returns: start property in #IBusUnicodeBlock
-+ */
-+gunichar          ibus_unicode_block_get_start
-+                                              (IBusUnicodeBlock   *block);
-+
-+/**
-+ * ibus_unicode_block_get_end:
-+ * @block: An #IBusUnicodeData
-+ *
-+ * Gets the end code point in #IBusUnicodeBlock.
-+ *
-+ * Returns: end property in #IBusUnicodeBlock
-+ */
-+gunichar          ibus_unicode_block_get_end
-+                                              (IBusUnicodeBlock   *block);
-+
-+/**
-+ * ibus_unicode_block_get_name:
-+ * @block: An #IBusUnicodeBlock
-+ *
-+ * Gets the name in #IBusUnicodeBlock. It should not be freed.
-+ *
-+ * Returns: name property in #IBusUnicodeBlock
-+ */
-+const gchar *     ibus_unicode_block_get_name (IBusUnicodeBlock   *block);
-+
-+/**
-+ * ibus_unicode_block_save:
-+ * @path: A path of the saved Unicode block.
-+ * @list: (element-type IBusUnicodeBlock) (transfer none): A list of unicode
-+ *  block.
-+ *
-+ * Save the list of #IBusUnicodeBlock to the cache file.
-+ */
-+void              ibus_unicode_block_save     (const gchar        *path,
-+                                               GSList             *list);
-+
-+/**
-+ * ibus_unicode_block_load:
-+ * @path: A path of the saved dictionary file.
-+ *
-+ * Returns: (element-type IBusUnicodeBlock) (transfer container):
-+ * An #IBusUnicodeBlock list loaded from the saved cache file.
-+ */
-+GSList *          ibus_unicode_block_load     (const gchar        *path);
-+
-+G_END_DECLS
-+#endif
-diff --git a/src/ibusunicodegen.h b/src/ibusunicodegen.h
-new file mode 100644
-index 00000000..c613b81b
---- /dev/null
-+++ b/src/ibusunicodegen.h
-@@ -0,0 +1,1151 @@
-+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
-+/* vim:set et sts=4: */
-+/* ibus - The Input Bus
-+ * Copyright (C) 2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2018 Red Hat, Inc.
-+ *
-+ * This library is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU Lesser General Public
-+ * License as published by the Free Software Foundation; either
-+ * version 2.1 of the License, or (at your option) any later version.
-+ *
-+ * This library is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-+ * Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public
-+ * License along with this library; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
-+ * USA
-+ */
-+
-+
-+/* This file is generated by unicode-parser.c. */
-+include <glib/gi18n.h>
-+
-+#ifndef __IBUS_UNICODE_GEN_H_
-+#define __IBUS_UNICODE_GEN_H_
-+const static char *unicode_blocks[] = {
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Basic Latin"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Latin-1 Supplement"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Latin Extended-A"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Latin Extended-B"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("IPA Extensions"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Spacing Modifier Letters"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Combining Diacritical Marks"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Greek and Coptic"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Cyrillic"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Cyrillic Supplement"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Armenian"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Hebrew"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Arabic"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Syriac"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Arabic Supplement"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Thaana"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("NKo"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Samaritan"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Mandaic"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Syriac Supplement"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Arabic Extended-A"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Devanagari"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Bengali"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Gurmukhi"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Gujarati"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Oriya"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Tamil"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Telugu"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Kannada"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Malayalam"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Sinhala"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Thai"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Lao"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Tibetan"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Myanmar"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Georgian"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Hangul Jamo"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Ethiopic"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Ethiopic Supplement"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Cherokee"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Unified Canadian Aboriginal Syllabics"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Ogham"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Runic"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Tagalog"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Hanunoo"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Buhid"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Tagbanwa"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Khmer"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Mongolian"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Unified Canadian Aboriginal Syllabics Extended"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Limbu"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Tai Le"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("New Tai Lue"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Khmer Symbols"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Buginese"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Tai Tham"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Combining Diacritical Marks Extended"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Balinese"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Sundanese"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Batak"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Lepcha"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Ol Chiki"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Cyrillic Extended-C"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Sundanese Supplement"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Vedic Extensions"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Phonetic Extensions"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Phonetic Extensions Supplement"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Combining Diacritical Marks Supplement"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Latin Extended Additional"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Greek Extended"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("General Punctuation"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Superscripts and Subscripts"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Currency Symbols"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Combining Diacritical Marks for Symbols"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Letterlike Symbols"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Number Forms"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Arrows"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Mathematical Operators"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Miscellaneous Technical"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Control Pictures"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Optical Character Recognition"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Enclosed Alphanumerics"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Box Drawing"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Block Elements"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Geometric Shapes"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Miscellaneous Symbols"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Dingbats"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Miscellaneous Mathematical Symbols-A"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Supplemental Arrows-A"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Braille Patterns"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Supplemental Arrows-B"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Miscellaneous Mathematical Symbols-B"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Supplemental Mathematical Operators"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Miscellaneous Symbols and Arrows"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Glagolitic"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Latin Extended-C"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Coptic"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Georgian Supplement"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Tifinagh"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Ethiopic Extended"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Cyrillic Extended-A"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Supplemental Punctuation"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("CJK Radicals Supplement"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Kangxi Radicals"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Ideographic Description Characters"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("CJK Symbols and Punctuation"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Hiragana"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Katakana"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Bopomofo"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Hangul Compatibility Jamo"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Kanbun"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Bopomofo Extended"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("CJK Strokes"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Katakana Phonetic Extensions"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Enclosed CJK Letters and Months"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("CJK Compatibility"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("CJK Unified Ideographs Extension A"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Yijing Hexagram Symbols"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("CJK Unified Ideographs"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Yi Syllables"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Yi Radicals"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Lisu"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Vai"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Cyrillic Extended-B"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Bamum"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Modifier Tone Letters"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Latin Extended-D"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Syloti Nagri"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Common Indic Number Forms"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Phags-pa"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Saurashtra"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Devanagari Extended"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Kayah Li"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Rejang"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Hangul Jamo Extended-A"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Javanese"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Myanmar Extended-B"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Cham"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Myanmar Extended-A"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Tai Viet"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Meetei Mayek Extensions"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Ethiopic Extended-A"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Latin Extended-E"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Cherokee Supplement"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Meetei Mayek"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Hangul Syllables"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Hangul Jamo Extended-B"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("High Surrogates"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("High Private Use Surrogates"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Low Surrogates"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Private Use Area"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("CJK Compatibility Ideographs"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Alphabetic Presentation Forms"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Arabic Presentation Forms-A"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Variation Selectors"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Vertical Forms"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Combining Half Marks"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("CJK Compatibility Forms"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Small Form Variants"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Arabic Presentation Forms-B"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Halfwidth and Fullwidth Forms"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Specials"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Linear B Syllabary"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Linear B Ideograms"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Aegean Numbers"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Ancient Greek Numbers"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Ancient Symbols"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Phaistos Disc"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Lycian"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Carian"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Coptic Epact Numbers"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Old Italic"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Gothic"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Old Permic"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Ugaritic"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Old Persian"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Deseret"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Shavian"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Osmanya"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Osage"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Elbasan"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Caucasian Albanian"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Linear A"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Cypriot Syllabary"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Imperial Aramaic"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Palmyrene"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Nabataean"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Hatran"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Phoenician"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Lydian"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Meroitic Hieroglyphs"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Meroitic Cursive"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Kharoshthi"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Old South Arabian"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Old North Arabian"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Manichaean"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Avestan"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Inscriptional Parthian"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Inscriptional Pahlavi"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Psalter Pahlavi"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Old Turkic"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Old Hungarian"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Rumi Numeral Symbols"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Brahmi"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Kaithi"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Sora Sompeng"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Chakma"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Mahajani"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Sharada"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Sinhala Archaic Numbers"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Khojki"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Multani"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Khudawadi"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Grantha"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Newa"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Tirhuta"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Siddham"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Modi"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Mongolian Supplement"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Takri"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Ahom"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Warang Citi"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Zanabazar Square"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Soyombo"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Pau Cin Hau"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Bhaiksuki"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Marchen"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Masaram Gondi"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Cuneiform"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Cuneiform Numbers and Punctuation"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Early Dynastic Cuneiform"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Egyptian Hieroglyphs"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Anatolian Hieroglyphs"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Bamum Supplement"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Mro"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Bassa Vah"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Pahawh Hmong"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Miao"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Ideographic Symbols and Punctuation"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Tangut"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Tangut Components"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Kana Supplement"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Kana Extended-A"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Nushu"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Duployan"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Shorthand Format Controls"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Byzantine Musical Symbols"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Musical Symbols"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Ancient Greek Musical Notation"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Tai Xuan Jing Symbols"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Counting Rod Numerals"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Mathematical Alphanumeric Symbols"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Sutton SignWriting"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Glagolitic Supplement"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Mende Kikakui"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Adlam"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Arabic Mathematical Alphabetic Symbols"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Mahjong Tiles"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Domino Tiles"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Playing Cards"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Enclosed Alphanumeric Supplement"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Enclosed Ideographic Supplement"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Miscellaneous Symbols and Pictographs"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Emoticons"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Ornamental Dingbats"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Transport and Map Symbols"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Alchemical Symbols"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Geometric Shapes Extended"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Supplemental Arrows-C"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Supplemental Symbols and Pictographs"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("CJK Unified Ideographs Extension B"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("CJK Unified Ideographs Extension C"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("CJK Unified Ideographs Extension D"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("CJK Unified Ideographs Extension E"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("CJK Unified Ideographs Extension F"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("CJK Compatibility Ideographs Supplement"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Tags"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Variation Selectors Supplement"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Supplementary Private Use Area-A"),
-+    /* TRANSLATORS: You might refer the translations from gucharmap with
-+                    the following command:
-+       msgmerge -C gucharmap.po ibus.po ibus.pot */
-+    N_("Supplementary Private Use Area-B"),
-+};
-+#endif
-diff --git a/src/unicode-parser.c b/src/unicode-parser.c
-new file mode 100644
-index 00000000..e98c6d5f
---- /dev/null
-+++ b/src/unicode-parser.c
-@@ -0,0 +1,502 @@
-+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
-+/* vim:set et sts=4: */
-+/* ibus - The Input Bus
-+ * Copyright (C) 2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2018 Red Hat, Inc.
-+ *
-+ * This library is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU Lesser General Public
-+ * License as published by the Free Software Foundation; either
-+ * version 2.1 of the License, or (at your option) any later version.
-+ *
-+ * This library is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-+ * Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public
-+ * License along with this library; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
-+ * USA
-+ */
-+
-+#ifdef HAVE_CONFIG_H
-+#include <config.h>
-+#endif
-+
-+#include <glib.h>
-+#include <stdlib.h>
-+#include <string.h>
-+
-+#ifdef HAVE_LOCALE_H
-+#include <locale.h>
-+#endif
-+
-+#include "ibusunicode.h"
-+
-+#define NAMES_LIST_SUBJECT "The Unicode Standard"
-+#define BLOCKS_SUBJECT "Blocks-"
-+
-+/* This file has 21 lines about the license at the top of the file. */
-+#define LICENSE_LINES 21
-+
-+typedef enum
-+{
-+    UCD_NAMES_LIST,
-+    UCD_BLOCKS
-+} UCDType;
-+
-+typedef struct _UnicodeData UnicodeData;
-+typedef struct _UnicodeDataIndex UnicodeDataIndex;
-+
-+struct _UnicodeData{
-+    gunichar code;
-+    gchar   *name;
-+    gchar   *alias;
-+    gunichar start;
-+    gunichar end;
-+    GSList  *list;
-+};
-+
-+struct _UnicodeDataIndex {
-+    gchar *index;
-+    UnicodeData *data_list;
-+};
-+
-+static gchar *unicode_version;
-+
-+static void
-+unicode_data_new_object (UnicodeData *data)
-+{
-+    g_return_if_fail (data != NULL);
-+    if (!data->name) {
-+        g_warning ("No name in U+%04X", data->code);
-+    }
-+    IBusUnicodeData *unicode =
-+            ibus_unicode_data_new ("code",
-+                                   data->code,
-+                                   "name",
-+                                   data->name ? g_strdup (data->name)
-+                                           : g_strdup (""),
-+                                   "alias",
-+                                   data->alias ? g_strdup (data->alias)
-+                                           : g_strdup (""),
-+                                   NULL);
-+    data->list = g_slist_append (data->list, unicode);
-+}
-+
-+static void
-+unicode_block_new_object (UnicodeData *data)
-+{
-+    g_return_if_fail (data != NULL);
-+    if (!data->name) {
-+        g_warning ("No name in U+%04X", data->start);
-+    }
-+    IBusUnicodeBlock *block =
-+            ibus_unicode_block_new ("start",
-+                                    data->start,
-+                                    "end",
-+                                    data->end,
-+                                    "name",
-+                                    data->name ? g_strdup (data->name)
-+                                           : g_strdup (""),
-+                                   NULL);
-+    data->list = g_slist_append (data->list, block);
-+}
-+
-+static void
-+unicode_data_reset (UnicodeData *data)
-+{
-+    g_return_if_fail (data != NULL);
-+    data->code = 0;
-+    g_clear_pointer (&data->name, g_free);
-+    g_clear_pointer (&data->alias, g_free);
-+    data->start = 0;
-+    data->end = 0;
-+}
-+
-+static gboolean
-+ucd_names_list_parse_comment (const gchar *line)
-+{
-+    static gboolean has_version = FALSE;
-+
-+    if (has_version)
-+        return TRUE;
-+    if (strlen (line) > 4 && strncmp (line, "@@@", 3) == 0) {
-+        gchar **elements = g_strsplit (line, "\t", -1);
-+        if (strncmp (elements[1], NAMES_LIST_SUBJECT,
-+            strlen (NAMES_LIST_SUBJECT)) == 0) {
-+            unicode_version =
-+                    g_strdup (elements[1] + strlen (NAMES_LIST_SUBJECT) + 1);
-+            has_version = TRUE;
-+        }
-+        g_strfreev (elements);
-+    }
-+    return TRUE;
-+}
-+
-+static gboolean
-+ucd_names_list_parse_alias (const gchar *line,
-+                            UnicodeData *data)
-+{
-+    g_return_val_if_fail (line != NULL, FALSE);
-+    g_return_val_if_fail (data != NULL, FALSE);
-+
-+    if (*line == '\0')
-+        return FALSE;
-+    data->alias = g_strdup (line);
-+    return TRUE;
-+}
-+
-+static gboolean
-+ucd_names_list_parse_indent_line (const gchar *line,
-+                                  UnicodeData *data)
-+{
-+    g_return_val_if_fail (line != NULL, FALSE);
-+
-+    switch (*line) {
-+    case '\0':
-+        return FALSE;
-+    case '=':
-+        line++;
-+        while (*line == ' ') line++;
-+        return ucd_names_list_parse_alias (line, data);
-+    default:;
-+    }
-+    return TRUE;
-+}
-+
-+static gboolean
-+ucd_names_list_parse_line (const gchar *line,
-+                           UnicodeData *data)
-+{
-+    g_return_val_if_fail (line != NULL, FALSE);
-+
-+    switch (*line) {
-+    case '\0':
-+        return TRUE;
-+    case ';':
-+        return TRUE;
-+    case '@':
-+        return ucd_names_list_parse_comment (line);
-+    case '\t':
-+        return ucd_names_list_parse_indent_line (line + 1, data);
-+    default:;
-+    }
-+    if (g_ascii_isxdigit (*line)) {
-+        gchar **elements = g_strsplit (line, "\t", -1);
-+        gunichar code;
-+        gchar *name;
-+
-+        if (g_strv_length (elements) < 2) {
-+            g_strfreev (elements);
-+            return FALSE;
-+        }
-+        code = g_ascii_strtoull (elements[0], NULL, 16);
-+        name = g_strdup (elements[1]);
-+        if (data->name) {
-+            unicode_data_new_object (data);
-+            unicode_data_reset (data);
-+        }
-+        data->code = code;
-+        data->name = name;
-+    }
-+    return TRUE;
-+}
-+
-+static gboolean
-+ucd_blocks_parse_comment (const gchar *line)
-+{
-+    static gboolean has_version = FALSE;
-+
-+    g_return_val_if_fail (line != NULL, FALSE);
-+
-+    if (has_version)
-+        return TRUE;
-+    while (*line == ' ') line++;
-+    if (strlen (line) > strlen (BLOCKS_SUBJECT) &&
-+        strncmp (line, BLOCKS_SUBJECT, strlen (BLOCKS_SUBJECT)) == 0) {
-+            unicode_version = g_strdup (line + strlen (BLOCKS_SUBJECT) + 1);
-+            has_version = TRUE;
-+    }
-+    return TRUE;
-+}
-+
-+static gboolean
-+ucd_blocks_parse_line (const gchar *line,
-+                       UnicodeData *data)
-+{
-+    g_return_val_if_fail (line != NULL, FALSE);
-+
-+    switch (*line) {
-+    case '\0':
-+        return TRUE;
-+    case '#':
-+        return ucd_blocks_parse_comment (line + 1);
-+    default:;
-+    }
-+    if (g_ascii_isxdigit (*line)) {
-+        gchar *endptr = NULL;
-+        gunichar start = g_ascii_strtoull (line, &endptr, 16);
-+        gunichar end;
-+        gchar *name = NULL;
-+
-+        if (endptr == NULL || *endptr == '\0')
-+            return FALSE;
-+        while (*endptr == '.') endptr++;
-+        line = endptr;
-+        endptr = NULL;
-+        end = g_ascii_strtoull (line, &endptr, 16);
-+        if (endptr == NULL || *endptr == '\0')
-+            return FALSE;
-+        while (*endptr == ';') endptr++;
-+        while (*endptr == ' ') endptr++;
-+        if (*endptr == '\0')
-+            return FALSE;
-+        name = g_strdup (endptr);
-+        if (data->name) {
-+            unicode_block_new_object (data);
-+            unicode_data_reset (data);
-+        }
-+        data->start = start;
-+        data->end = end;
-+        data->name = name;
-+    }
-+    return TRUE;
-+}
-+
-+static gboolean
-+ucd_parse_file (const gchar *filename,
-+                GSList     **list,
-+                UCDType      type)
-+{
-+    UnicodeData data = { 0, };
-+    gchar *content = NULL;
-+    gsize length = 0;
-+    GError *error = NULL;
-+    gchar *head, *end, *line;
-+    int n = 1;
-+
-+    g_return_val_if_fail (filename != NULL, FALSE);
-+    g_return_val_if_fail (list != NULL, FALSE);
-+
-+    if (!g_file_get_contents (filename, &content, &length, &error)) {
-+        g_warning ("Failed to load %s: %s", filename, error->message);
-+        goto failed_to_parse_ucd_names_list;
-+    }
-+    head = end = content;
-+    while (*end == '\n' && end - content < length) {
-+        end++;
-+        n++;
-+    }
-+    head = end;
-+    while (end - content < length) {
-+        while (*end != '\n' && end - content < length)
-+            end++;
-+        if (end - content >= length)
-+            break;
-+        line = g_strndup (head, end - head);
-+        switch (type) {
-+        case UCD_NAMES_LIST:
-+            if (!ucd_names_list_parse_line (line, &data)) {
-+                g_warning ("parse error #%d in %s version %s: %s",
-+                           n, filename,
-+                           unicode_version ? unicode_version : "(null)",
-+                           line);
-+            }
-+            break;
-+        case UCD_BLOCKS:
-+            if (!ucd_blocks_parse_line (line, &data)) {
-+                g_warning ("parse error #%d in %s version %s: %s",
-+                           n, filename,
-+                           unicode_version ? unicode_version : "(null)",
-+                           line);
-+            }
-+            break;
-+        default:
-+            g_abort ();
-+        }
-+        while (*end == '\n' && end - content < length) {
-+            end++;
-+            n++;
-+        }
-+        g_free (line);
-+        head = end;
-+    }
-+    if (data.name != NULL) {
-+        switch (type) {
-+        case UCD_NAMES_LIST:
-+            unicode_data_new_object (&data);
-+            break;
-+        case UCD_BLOCKS:
-+            unicode_block_new_object (&data);
-+            break;
-+        default:;
-+        }
-+        unicode_data_reset (&data);
-+    }
-+    g_free (content);
-+    *list = data.list;
-+    return TRUE;
-+
-+failed_to_parse_ucd_names_list:
-+    if (error)
-+        g_error_free (error);
-+    g_clear_pointer (&content, g_free);
-+    *list = data.list;
-+    return FALSE;
-+}
-+
-+static void
-+block_list_dump (IBusUnicodeBlock *block,
-+                 GString          *buff)
-+{
-+    g_return_if_fail (buff != NULL);
-+
-+    g_string_append (buff, "    /* TRANSLATORS: You might refer the "         \
-+                           "translations from gucharmap with\n"               \
-+                           "                    the following command:\n"     \
-+                           "       msgmerge -C gucharmap.po ibus.po "         \
-+                           "ibus.pot */\n");
-+    gchar *line = g_strdup_printf ("    N_(\"%s\"),\n",
-+                                   ibus_unicode_block_get_name (block));
-+    g_string_append (buff, line);
-+}
-+
-+static void
-+ucd_block_translatable_save (const gchar *filename,
-+                             GSList      *blocks_list)
-+{
-+    gchar *content = NULL;
-+    gsize length = 0;
-+    GError *error = NULL;
-+    gchar *p;
-+    GString *buff = NULL;
-+    int i;
-+    GSList *list = blocks_list;
-+
-+    g_return_if_fail (filename != NULL);
-+    g_return_if_fail (list != NULL);
-+
-+    if (!g_file_get_contents (__FILE__, &content, &length, &error)) {
-+        g_warning ("Failed to load %s: %s", __FILE__, error->message);
-+        g_clear_pointer (&error, g_error_free);
-+        return;
-+    }
-+
-+    buff = g_string_new (NULL);
-+    p = content;
-+    for (i = 0; i < LICENSE_LINES; i++, p++) {
-+        if ((p = strchr (p, '\n')) == NULL)
-+            break;
-+    }
-+    if (p != NULL) {
-+        g_string_append (buff, g_strndup (content, p - content));
-+        g_string_append_c (buff, '\n');
-+    }
-+    g_clear_pointer (&content, g_free);
-+
-+    g_string_append (buff, g_strdup ("\n"));
-+    g_string_append (buff, g_strdup_printf ("/* This file is generated by %s. */", __FILE__));
-+    g_string_append (buff, g_strdup ("\n"));
-+    g_string_append (buff, g_strdup ("include <glib/gi18n.h>\n"));
-+    g_string_append (buff, g_strdup ("\n"));
-+    g_string_append (buff, g_strdup ("#ifndef __IBUS_UNICODE_GEN_H_\n"));
-+    g_string_append (buff, g_strdup ("#define __IBUS_UNICODE_GEN_H_\n"));
-+    g_string_append (buff, g_strdup ("const static char *unicode_blocks[] = {\n"));
-+    g_slist_foreach (list, (GFunc)block_list_dump, buff);
-+    g_string_append (buff, g_strdup ("};\n"));
-+    g_string_append (buff, g_strdup ("#endif\n"));
-+
-+    if (!g_file_set_contents (filename, buff->str, -1, &error)) {
-+        g_warning ("Failed to save emoji category file %s: %s", filename, error->message);
-+        g_error_free (error);
-+    }
-+
-+    g_string_free (buff, TRUE);
-+}
-+
-+int
-+main (int argc, char *argv[])
-+{
-+    gchar *prgname;
-+    gchar *input_names_list = NULL;
-+    gchar *input_blocks = NULL;
-+    gchar *output_names_list = NULL;
-+    gchar *output_blocks = NULL;
-+    gchar *output_blocks_trans = NULL;
-+    GOptionEntry     entries[] = {
-+        { "input-names-list", 'n', 0, G_OPTION_ARG_STRING, &input_names_list,
-+          "Parse NamesList.txt FILE in unicode.org ",
-+          "FILE"
-+        },
-+        { "input-blocks", 'b', 0, G_OPTION_ARG_STRING, &input_blocks,
-+          "Parse Blocks.txt FILE in unicode.org ",
-+          "FILE"
-+        },
-+        { "output-names-list", 'o', 0, G_OPTION_ARG_STRING, &output_names_list,
-+          "Save the Unicode data as FILE",
-+          "FILE"
-+        },
-+        { "output-blocks", 'B', 0, G_OPTION_ARG_STRING, &output_blocks,
-+          "Save the Unicode block list as FILE",
-+          "FILE"
-+        },
-+        { "output-blocks-trans", 'C', 0, G_OPTION_ARG_STRING,
-+          &output_blocks_trans,
-+          "Save the translatable Unicode blocks as FILE",
-+          "FILE"
-+        },
-+        { NULL }
-+    };
-+    GOptionContext *context;
-+    GError *error = NULL;
-+    GSList *names_list = NULL;
-+    GSList *blocks_list = NULL;
-+
-+#ifdef HAVE_LOCALE_H
-+    /* To output emoji warnings. */
-+    setlocale (LC_ALL, "");
-+#endif
-+
-+    prgname = g_path_get_basename (argv[0]);
-+    g_set_prgname (prgname);
-+    g_free (prgname);
-+
-+    context = g_option_context_new (NULL);
-+    g_option_context_add_main_entries (context, entries, NULL);
-+
-+    if (argc < 3) {
-+        g_print ("%s", g_option_context_get_help (context, TRUE, NULL));
-+        g_option_context_free (context);
-+        return -1;
-+    }
-+
-+    if (!g_option_context_parse (context, &argc, &argv, &error)) {
-+        g_warning ("Failed options: %s", error->message);
-+        g_error_free (error);
-+        return -1;
-+    }
-+    g_option_context_free (context);
-+
-+    if (input_names_list) {
-+        ucd_parse_file (input_names_list, &names_list, UCD_NAMES_LIST);
-+        g_free (input_names_list);
-+    }
-+    if (output_names_list && names_list)
-+        ibus_unicode_data_save (output_names_list, names_list);
-+    g_free (output_names_list);
-+
-+    if (input_blocks) {
-+        ucd_parse_file (input_blocks, &blocks_list, UCD_BLOCKS);
-+        g_free (input_blocks);
-+    }
-+    if (output_blocks && blocks_list)
-+        ibus_unicode_block_save (output_blocks, blocks_list);
-+    if (output_blocks_trans && blocks_list)
-+        ucd_block_translatable_save (output_blocks_trans, blocks_list);
-+    g_free (output_blocks);
-+
-+    g_free (unicode_version);
-+    return 0;
-+}
-diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
-index f3e9f15c..555ea68f 100644
---- a/ui/gtk3/emojier.vala
-+++ b/ui/gtk3/emojier.vala
-@@ -2,7 +2,7 @@
-  *
-  * ibus - The Input Bus
-  *
-- * Copyright (c) 2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (c) 2017-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-  *
-  * This library is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU Lesser General Public
-@@ -20,7 +20,7 @@
-  * USA
-  */
- 
--class IBusEmojier : Gtk.ApplicationWindow {
-+public class IBusEmojier : Gtk.ApplicationWindow {
-     private class EEntry : Gtk.SearchEntry {
-         public EEntry() {
-             GLib.Object(
-@@ -99,15 +99,70 @@ class IBusEmojier : Gtk.ApplicationWindow {
-         }
-     }
-     private class EWhiteLabel : Gtk.Label {
-+        private int m_minimum_width = 0;
-+        private int m_natural_width = 0;
-+        private int m_minimum_height = 0;
-+        private int m_natural_height = 0;
-         public EWhiteLabel(string text) {
-             GLib.Object(
-                 name : "IBusEmojierWhiteLabel"
-             );
--            if (text != "")
--                set_label(text);
-+            set_label(text);
-+        }
-+        public override void get_preferred_width(out int minimum_width,
-+                                                 out int natural_width) {
-+            if (m_minimum_height == 0 && m_natural_height == 0) {
-+                base.get_preferred_height(out m_minimum_height,
-+                                          out m_natural_height);
-+            }
-+            var text = get_label();
-+            var ch = text.get_char();
-+            if (text.length == 1 && ch == '\t') {
-+                m_minimum_width = minimum_width = m_minimum_height;
-+                m_natural_width = natural_width = m_natural_height;
-+                return;
-+            }
-+            base.get_preferred_width(out minimum_width, out natural_width);
-+            if (text.length == 1 && (ch == '\n' || ch == '\r')) {
-+                minimum_width /= 2;
-+                natural_width /= 2;
-+                m_minimum_width = minimum_width;
-+                m_natural_width = natural_width;
-+                return;
-+            }
-+            if (minimum_width < m_minimum_height)
-+                minimum_width = m_minimum_height;
-+            if (natural_width < m_natural_height)
-+                natural_width = m_natural_height;
-+            m_minimum_width = minimum_width;
-+            m_natural_width = natural_width;
-+        }
-+        public override void get_preferred_height(out int minimum_height,
-+                                                  out int natural_height) {
-+            if (m_minimum_width == 0 && m_natural_width == 0) {
-+                base.get_preferred_width(out m_minimum_width,
-+                                         out m_natural_width);
-+            }
-+            var text = get_label();
-+            var ch = text.get_char();
-+            if (text.length == 1 && ch == '\v') {
-+                m_minimum_height = minimum_height = m_minimum_width;
-+                m_natural_height = natural_height = m_natural_width;
-+                return;
-+            }
-+            base.get_preferred_height(out minimum_height, out natural_height);
-+            if (text.length == 1 && (ch == '\n' || ch == '\r')) {
-+                minimum_height /= 2;
-+                natural_height /= 2;
-+                m_minimum_height = minimum_height;
-+                m_natural_height = natural_height;
-+                return;
-+            }
-+            m_minimum_height = minimum_height;
-+            m_natural_height = natural_height;
-         }
-     }
--    private class ESelectedLabel : Gtk.Label {
-+    private class ESelectedLabel : EWhiteLabel {
-         public ESelectedLabel(string text) {
-             GLib.Object(
-                 name : "IBusEmojierSelectedLabel"
-@@ -116,7 +171,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
-                 set_label(text);
-         }
-     }
--    private class EGoldLabel : Gtk.Label {
-+    private class EGoldLabel : EWhiteLabel {
-         public EGoldLabel(string text) {
-             GLib.Object(
-                 name : "IBusEmojierGoldLabel"
-@@ -167,6 +222,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
-     }
-     private class ETitleLabelBox : Gtk.HeaderBar {
-         private Gtk.Label m_lang_label;
-+        private Gtk.Label m_title_label;
- 
-         public ETitleLabelBox(string title) {
-             GLib.Object(
-@@ -177,9 +233,9 @@ class IBusEmojier : Gtk.ApplicationWindow {
-             );
-             var vbox = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
-             set_custom_title(vbox);
--            var label = new Gtk.Label(title);
--            label.get_style_context().add_class(Gtk.STYLE_CLASS_TITLE);
--            vbox.pack_start(label, true, false, 0);
-+            m_title_label = new Gtk.Label(title);
-+            m_title_label.get_style_context().add_class(Gtk.STYLE_CLASS_TITLE);
-+            vbox.pack_start(m_title_label, true, false, 0);
-             m_lang_label = new Gtk.Label(null);
-             m_lang_label.get_style_context().add_class(
-                     Gtk.STYLE_CLASS_SUBTITLE);
-@@ -194,10 +250,19 @@ class IBusEmojier : Gtk.ApplicationWindow {
-             menu_button.set_tooltip_text(_("Menu"));
-             pack_end(menu_button);
-         }
-+        public new void set_title(string title) {
-+            m_title_label.set_text(title);
-+        }
-         public void set_lang_label(string str) {
-             m_lang_label.set_text(str);
-         }
-     }
-+    private class LoadProgressObject : GLib.Object {
-+        public LoadProgressObject() {
-+        }
-+        public signal void deserialize_unicode(uint done, uint total);
-+    }
-+
- 
-     private enum TravelDirection {
-         NONE,
-@@ -207,6 +272,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
-     private const uint EMOJI_GRID_PAGE = 10;
-     private const string EMOJI_CATEGORY_FAVORITES = N_("Favorites");
-     private const string EMOJI_CATEGORY_OTHERS = N_("Others");
-+    private const string EMOJI_CATEGORY_UNICODE = N_("Open Unicode choice");
-     private const unichar[] EMOJI_VARIANT_LIST = {
-             0x1f3fb, 0x1f3fc, 0x1f3fd, 0x1f3fe, 0x1f3ff, 0x200d };
- 
-@@ -223,6 +289,8 @@ class IBusEmojier : Gtk.ApplicationWindow {
-     private static uint m_partial_match_length;
-     private static uint m_partial_match_condition;
-     private static bool m_show_emoji_variant = false;
-+    private static int m_default_window_width;
-+    private static int m_default_window_height;
-     private static GLib.HashTable<string, GLib.SList<string>>?
-             m_annotation_to_emojis_dict;
-     private static GLib.HashTable<string, IBus.EmojiData>?
-@@ -231,6 +299,14 @@ class IBusEmojier : Gtk.ApplicationWindow {
-             m_category_to_emojis_dict;
-     private static GLib.HashTable<string, GLib.SList<string>>?
-             m_emoji_to_emoji_variants_dict;
-+    private static GLib.HashTable<unichar, IBus.UnicodeData>?
-+            m_unicode_to_data_dict;
-+    private static GLib.HashTable<string, GLib.SList<unichar>>?
-+            m_name_to_unicodes_dict;
-+    private static GLib.SList<IBus.UnicodeBlock> m_unicode_block_list;
-+    private static bool m_show_unicode = false;
-+    private static LoadProgressObject m_unicode_progress_object;
-+    private static bool m_loaded_unicode = false;
- 
-     private ThemedRGBA m_rgba;
-     private Gtk.Box m_vbox;
-@@ -246,7 +322,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
-     private string? m_result;
-     private string? m_unicode_point = null;
-     private bool m_candidate_panel_is_visible;
--    private int m_category_active_index;
-+    private int m_category_active_index = -1;
-     private IBus.LookupTable m_lookup_table;
-     private Gtk.Label[] m_candidates;
-     private bool m_enter_notify_enable = true;
-@@ -254,6 +330,9 @@ class IBusEmojier : Gtk.ApplicationWindow {
-     private uint m_entry_notify_disable_id;
-     protected static double m_mouse_x;
-     protected static double m_mouse_y;
-+    private Gtk.ProgressBar m_unicode_progress_bar;
-+    private Gtk.Label m_unicode_percent_label;
-+    private double m_unicode_percent;
- 
-     public signal void candidate_clicked(uint index, uint button, uint state);
- 
-@@ -402,6 +481,8 @@ class IBusEmojier : Gtk.ApplicationWindow {
-         if (m_annotation_to_emojis_dict == null) {
-             reload_emoji_dict();
-         }
-+
-+        get_load_progress_object();
-     }
- 
- 
-@@ -433,6 +514,13 @@ class IBusEmojier : Gtk.ApplicationWindow {
-         m_emoji_to_emoji_variants_dict =
-                 new GLib.HashTable<string, GLib.SList<string>>(GLib.str_hash,
-                                                                GLib.str_equal);
-+        m_unicode_to_data_dict =
-+                new GLib.HashTable<unichar, IBus.UnicodeData>(
-+                        GLib.direct_hash,
-+                        GLib.direct_equal);
-+        m_name_to_unicodes_dict =
-+                new GLib.HashTable<string, GLib.SList<unichar>>(GLib.str_hash,
-+                                                                GLib.str_equal);
-     }
- 
- 
-@@ -482,6 +570,10 @@ class IBusEmojier : Gtk.ApplicationWindow {
-     private static string utf8_code_point(string str) {
-         var buff = new GLib.StringBuilder();
-         int length = str.char_count();
-+        if (length == 0) {
-+            buff.append("U+%04X".printf(0));
-+            return buff.str;
-+        }
-         for (int i = 0; i < length; i++) {
-             unichar ch = str.get_char(0);
-             if (i == 0)
-@@ -644,6 +736,72 @@ class IBusEmojier : Gtk.ApplicationWindow {
-     }
- 
- 
-+    private static void make_unicode_block_dict() {
-+        m_unicode_block_list = IBus.UnicodeBlock.load(
-+                    Config.PKGDATADIR + "/dicts/unicode-blocks.dict");
-+        foreach (unowned IBus.UnicodeBlock block in m_unicode_block_list) {
-+            unowned string name = block.get_name();
-+            if (m_emoji_max_seq_len < name.length)
-+                m_emoji_max_seq_len = name.length;
-+        }
-+    }
-+
-+
-+    private static void make_unicode_name_dict(Object source_object) {
-+        IBus.UnicodeData.load_async(
-+                    Config.PKGDATADIR + "/dicts/unicode-names.dict",
-+                    source_object,
-+                    null,
-+        (IBus.UnicodeDataLoadAsyncFinish)make_unicode_name_dict_finish);
-+    }
-+
-+    private static void
-+    make_unicode_name_dict_finish(GLib.SList<IBus.UnicodeData> unicode_list) {
-+        if (unicode_list == null)
-+            return;
-+        foreach (IBus.UnicodeData data in unicode_list) {
-+            update_unicode_to_data_dict(data);
-+            update_name_to_unicodes_dict(data);
-+        }
-+        GLib.List<unowned string> names =
-+                m_name_to_unicodes_dict.get_keys();
-+        foreach (unowned string name in names) {
-+            if (m_emoji_max_seq_len < name.length)
-+                m_emoji_max_seq_len = name.length;
-+        }
-+        m_loaded_unicode = true;
-+    }
-+
-+
-+    private static void update_unicode_to_data_dict(IBus.UnicodeData data) {
-+        unichar code = data.get_code();
-+        m_unicode_to_data_dict.replace(code, data);
-+    }
-+
-+
-+    private static void update_name_to_unicodes_dict(IBus.UnicodeData data) {
-+        unichar code = data.get_code();
-+        string[] names = {data.get_name().down(), data.get_alias().down()};
-+        foreach (unowned string name in names) {
-+            if (name == "")
-+                continue;
-+            bool has_code = false;
-+            GLib.SList<unichar> hits =
-+                    m_name_to_unicodes_dict.lookup(name).copy();
-+            foreach (unichar hit_code in hits) {
-+                if (hit_code == code) {
-+                    has_code = true;
-+                    break;
-+                }
-+            }
-+            if (!has_code) {
-+                hits.append(code);
-+                m_name_to_unicodes_dict.replace(name, hits.copy());
-+            }
-+        }
-+    }
-+
-+
-     private void set_fixed_size() {
-         resize(20, 1);
-     }
-@@ -665,6 +823,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
-         m_scrolled_window = new EScrolledWindow();
-         set_fixed_size();
- 
-+        m_title.set_title(_("Emoji Choice"));
-         string language =
-             IBus.get_language_name(m_current_lang_id);
-         m_title.set_lang_label(language);
-@@ -677,12 +836,12 @@ class IBusEmojier : Gtk.ApplicationWindow {
-         Gtk.Adjustment adjustment = m_scrolled_window.get_vadjustment();
-         m_list_box.set_adjustment(adjustment);
-         m_list_box.row_activated.connect((box, gtkrow) => {
--            m_category_active_index = 0;
-+            m_category_active_index = -1;
-             EBoxRow row = gtkrow as EBoxRow;
-             show_emoji_for_category(row.text);
-         });
- 
--        uint n = 1;
-+        uint n = 0;
-         if (m_favorites.length > 0) {
-             EBoxRow row = new EBoxRow(EMOJI_CATEGORY_FAVORITES);
-             EPaddedLabelBox widget =
-@@ -716,9 +875,19 @@ class IBusEmojier : Gtk.ApplicationWindow {
-             if (n++ == m_category_active_index)
-                 m_list_box.select_row(row);
-         }
-+        if (m_unicode_block_list.length() > 0) {
-+            EBoxRow row = new EBoxRow(EMOJI_CATEGORY_UNICODE);
-+            EPaddedLabelBox widget =
-+                    new EPaddedLabelBox(_(EMOJI_CATEGORY_UNICODE),
-+                                        Gtk.Align.CENTER);
-+            row.add(widget);
-+            m_list_box.add(row);
-+            if (n++ == m_category_active_index)
-+                m_list_box.select_row(row);
-+        }
- 
-         m_scrolled_window.show_all();
--        if (m_category_active_index == 0)
-+        if (m_category_active_index == -1)
-             m_list_box.unselect_all();
-         m_list_box.invalidate_filter();
-         m_list_box.set_selection_mode(Gtk.SelectionMode.SINGLE);
-@@ -733,6 +902,11 @@ class IBusEmojier : Gtk.ApplicationWindow {
-                 m_lookup_table.append_candidate(text);
-             }
-             m_backward = category;
-+        } else if (category == EMOJI_CATEGORY_UNICODE) {
-+            m_category_active_index = -1;
-+            m_show_unicode = true;
-+            show_unicode_blocks();
-+            return;
-         } else {
-             unowned GLib.SList<unowned string> emojis =
-                     m_category_to_emojis_dict.lookup(category);
-@@ -764,6 +938,126 @@ class IBusEmojier : Gtk.ApplicationWindow {
-     }
- 
- 
-+    private void show_unicode_blocks() {
-+        m_show_unicode = true;
-+        if (m_default_window_width == 0 && m_default_window_height == 0)
-+            get_size(out m_default_window_width, out m_default_window_height);
-+        remove_all_children();
-+        set_fixed_size();
-+
-+        m_title.set_title(_("Unicode Choice"));
-+        EPaddedLabelBox label =
-+                new EPaddedLabelBox(_("Bring back emoji choice"),
-+                                    Gtk.Align.CENTER,
-+                                    TravelDirection.BACKWARD);
-+        Gtk.Button button = new Gtk.Button();
-+        button.add(label);
-+        m_vbox.add(button);
-+        button.show_all();
-+        button.button_press_event.connect((w, e) => {
-+            m_category_active_index = -1;
-+            m_show_unicode = false;
-+            hide_candidate_panel();
-+            return true;
-+        });
-+        m_scrolled_window = new EScrolledWindow();
-+        m_title.set_lang_label("");
-+        m_vbox.add(m_scrolled_window);
-+        Gtk.Viewport viewport = new Gtk.Viewport(null, null);
-+        m_scrolled_window.add(viewport);
-+
-+        m_list_box = new EListBox();
-+        viewport.add(m_list_box);
-+        Gtk.Adjustment adjustment = m_scrolled_window.get_vadjustment();
-+        m_list_box.set_adjustment(adjustment);
-+        m_list_box.row_activated.connect((box, gtkrow) => {
-+            m_category_active_index = -1;
-+            EBoxRow row = gtkrow as EBoxRow;
-+            show_unicode_for_block(row.text);
-+        });
-+
-+        uint n = 0;
-+        foreach (unowned IBus.UnicodeBlock block in m_unicode_block_list) {
-+            string name = block.get_name();
-+            EBoxRow row = new EBoxRow(name);
-+            EPaddedLabelBox widget =
-+                    new EPaddedLabelBox(_(name), Gtk.Align.CENTER);
-+            row.add(widget);
-+            m_list_box.add(row);
-+            if (n++ == m_category_active_index) {
-+                m_list_box.select_row(row);
-+            }
-+        }
-+
-+        set_size_request(-1, m_default_window_height + 100);
-+        m_scrolled_window.set_policy(Gtk.PolicyType.NEVER,
-+                                     Gtk.PolicyType.AUTOMATIC);
-+        m_scrolled_window.show_all();
-+        if (m_category_active_index == -1)
-+            m_list_box.unselect_all();
-+        m_list_box.invalidate_filter();
-+        m_list_box.set_selection_mode(Gtk.SelectionMode.SINGLE);
-+    }
-+
-+    private void show_unicode_for_block(string block_name) {
-+        if (!m_loaded_unicode) {
-+            remove_all_children();
-+            set_fixed_size();
-+            m_unicode_progress_bar = new Gtk.ProgressBar();
-+            m_unicode_progress_bar.set_ellipsize(Pango.EllipsizeMode.MIDDLE);
-+            m_unicode_progress_bar.set_halign(Gtk.Align.CENTER);
-+            m_unicode_progress_bar.set_valign(Gtk.Align.CENTER);
-+            m_vbox.add(m_unicode_progress_bar);
-+            m_unicode_progress_bar.show();
-+            var hbox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 5);
-+            hbox.set_halign(Gtk.Align.CENTER);
-+            hbox.set_valign(Gtk.Align.CENTER);
-+            m_vbox.add(hbox);
-+            var label = new Gtk.Label(_("Loading a Unicode dictionary:"));
-+            hbox.pack_start(label, false, true, 0);
-+            m_unicode_percent_label = new Gtk.Label("");
-+            hbox.pack_start(m_unicode_percent_label, false, true, 0);
-+            hbox.show_all();
-+
-+            m_unicode_progress_object.deserialize_unicode.connect((i, n) => {
-+                m_unicode_percent = (double)i / n;
-+            });
-+            GLib.Timeout.add(100, () => {
-+                m_unicode_progress_bar.set_fraction(m_unicode_percent);
-+                m_unicode_percent_label.set_text(
-+                        "%.0f%%\n".printf(m_unicode_percent * 100));
-+                m_unicode_progress_bar.show();
-+                m_unicode_percent_label.show();
-+                if (m_loaded_unicode) {
-+                    show_unicode_for_block(block_name);
-+                }
-+                return !m_loaded_unicode;
-+            });
-+            return;
-+        }
-+        unichar start = 0;
-+        unichar end = 0;
-+        foreach (unowned IBus.UnicodeBlock block in m_unicode_block_list) {
-+            string name = block.get_name();
-+            if (block_name == name) {
-+                start = block.get_start();
-+                end = block.get_end();
-+            }
-+        }
-+        m_lookup_table.clear();
-+        for (unichar ch = start; ch < end; ch++) {
-+            unowned IBus.UnicodeData? data =
-+                    m_unicode_to_data_dict.lookup(ch);
-+            if (data == null)
-+                continue;
-+            IBus.Text text = new IBus.Text.from_unichar(ch);
-+            m_lookup_table.append_candidate(text);
-+        }
-+        m_backward = block_name;
-+        show_candidate_panel();
-+    }
-+
-+
-     private void show_arrow_buttons() {
-         Gtk.Button next_button = new Gtk.Button();
-         next_button.clicked.connect(() => {
-@@ -840,6 +1134,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
-     lookup_emojis_from_annotation(string annotation) {
-         GLib.SList<string>? total_emojis = null;
-         unowned GLib.SList<string>? sub_emojis = null;
-+        unowned GLib.SList<unichar>? sub_unicodes = null;
-         int length = annotation.length;
-         if (m_has_partial_match && length >= m_partial_match_length) {
-             foreach (unowned string key in
-@@ -877,6 +1172,22 @@ class IBusEmojier : Gtk.ApplicationWindow {
-             foreach (unowned string emoji in sub_emojis)
-                 total_emojis.append(emoji);
-         }
-+        if (length >= m_partial_match_length) {
-+            foreach (unowned string key in m_name_to_unicodes_dict.get_keys()) {
-+                bool matched = false;
-+                if (key.index_of(annotation) >= 0)
-+                        matched = true;
-+                if (!matched)
-+                    continue;
-+                sub_unicodes = m_name_to_unicodes_dict.lookup(key);
-+                foreach (unichar code in sub_unicodes) {
-+                    string ch = code.to_string();
-+                    if (total_emojis.find_custom(ch, GLib.strcmp) == null) {
-+                        total_emojis.append(ch);
-+                    }
-+                }
-+            }
-+        }
-         return total_emojis;
-     }
- 
-@@ -1049,58 +1360,99 @@ class IBusEmojier : Gtk.ApplicationWindow {
-             grid.show_all();
-             string text = m_lookup_table.get_candidate(cursor).text;
-             unowned IBus.EmojiData? data = m_emoji_to_data_dict.lookup(text);
--            if (data == null) {
--                // TODO: Provide a custom description and annotation for
--                // the favorite emojis.
--                EPaddedLabelBox widget = new EPaddedLabelBox(
--                        _("Description: %s").printf(_("None")),
--                        Gtk.Align.START);
--                m_vbox.add(widget);
--                widget.show_all();
--                show_code_point_description(text);
-+            if (data != null) {
-+                show_emoji_description(data, text);
-                 return;
--            } else {
--                unowned string description = data.get_description();
--                EPaddedLabelBox widget = new EPaddedLabelBox(
--                        _("Description: %s").printf(description),
--                        Gtk.Align.START);
--                m_vbox.add(widget);
--                widget.show_all();
-             }
--            unowned GLib.SList<unowned string>? annotations =
--                    data.get_annotations();
--            var buff = new GLib.StringBuilder();
--            int i = 0;
--            foreach (unowned string annotation in annotations) {
--                if (i++ == 0)
--                    buff.append_printf(_("Annotations: %s"), annotation);
--                else
--                    buff.append_printf(" | %s", annotation);
--                if (buff.str.char_count() > 30) {
--                    EPaddedLabelBox widget =
--                            new EPaddedLabelBox(buff.str,
--                                                Gtk.Align.START);
--                    m_vbox.add(widget);
--                    widget.show_all();
--                    buff.erase();
-+            if (text.char_count() <= 1) {
-+                unichar code = text.get_char();
-+                unowned IBus.UnicodeData? udata =
-+                        m_unicode_to_data_dict.lookup(code);
-+                if (udata != null) {
-+                    show_unicode_description(udata, text);
-+                    return;
-                 }
-             }
--            if (buff.str != "") {
--                EPaddedLabelBox widget = new EPaddedLabelBox(buff.str,
--                                                             Gtk.Align.START);
-+            // TODO: Provide a custom description and annotation for
-+            // the favorite emojis.
-+            EPaddedLabelBox widget = new EPaddedLabelBox(
-+                        _("Description: %s").printf(_("None")),
-+                        Gtk.Align.START);
-+            m_vbox.add(widget);
-+            widget.show_all();
-+            show_code_point_description(text);
-+        }
-+    }
-+
-+
-+    private void show_emoji_description(IBus.EmojiData data,
-+                                        string         text) {
-+        unowned string description = data.get_description();
-+        {
-+            EPaddedLabelBox widget = new EPaddedLabelBox(
-+                    _("Description: %s").printf(description),
-+                    Gtk.Align.START);
-+            m_vbox.add(widget);
-+            widget.show_all();
-+        }
-+        unowned GLib.SList<unowned string>? annotations =
-+                data.get_annotations();
-+        var buff = new GLib.StringBuilder();
-+        int i = 0;
-+        foreach (unowned string annotation in annotations) {
-+            if (i++ == 0)
-+                buff.append_printf(_("Annotations: %s"), annotation);
-+            else
-+                buff.append_printf(" | %s", annotation);
-+            if (buff.str.char_count() > 30) {
-+                EPaddedLabelBox widget =
-+                        new EPaddedLabelBox(buff.str,
-+                                            Gtk.Align.START);
-                 m_vbox.add(widget);
-                 widget.show_all();
-+                buff.erase();
-             }
--            show_code_point_description(text);
-         }
-+        if (buff.str != "") {
-+            EPaddedLabelBox widget = new EPaddedLabelBox(buff.str,
-+                                                         Gtk.Align.START);
-+            m_vbox.add(widget);
-+            widget.show_all();
-+        }
-+        show_code_point_description(text);
-+    }
-+
-+    private void show_unicode_description(IBus.UnicodeData data,
-+                                          string           text) {
-+        unowned string name = data.get_name();
-+        {
-+            EPaddedLabelBox widget = new EPaddedLabelBox(
-+                    _("Name: %s").printf(name),
-+                    Gtk.Align.START);
-+            m_vbox.add(widget);
-+            widget.show_all();
-+        }
-+        unowned string alias = data.get_alias();
-+        {
-+            EPaddedLabelBox widget = new EPaddedLabelBox(
-+                    _("Alias: %s").printf(alias),
-+                    Gtk.Align.START);
-+            m_vbox.add(widget);
-+            widget.show_all();
-+        }
-+        show_code_point_description(text);
-     }
- 
- 
-     private void hide_candidate_panel() {
-         m_enter_notify_enable = true;
-         m_candidate_panel_is_visible = false;
--        if (m_loop.is_running())
--            show_category_list();
-+        if (m_loop.is_running()) {
-+            if (m_show_unicode)
-+                show_unicode_blocks();
-+            else
-+                show_category_list();
-+        }
-     }
- 
- 
-@@ -1165,19 +1517,41 @@ class IBusEmojier : Gtk.ApplicationWindow {
-     }
- 
- 
--    private void category_list_cursor_move(uint keyval) {
-+    private bool category_list_cursor_move(uint keyval) {
-         GLib.List<weak Gtk.Widget> list = m_list_box.get_children();
--        if (keyval == Gdk.Key.Down) {
--            m_category_active_index =
--                    ++m_category_active_index % ((int)list.length() + 1);
--        } else if (keyval == Gdk.Key.Up) {
-+        int length = (int)list.length();
-+        if (length == 0)
-+            return false;
-+        switch(keyval) {
-+        case Gdk.Key.Down:
-+            if (++m_category_active_index == length)
-+                m_category_active_index = 0;
-+            break;
-+        case Gdk.Key.Up:
-             if (--m_category_active_index < 0)
--                    m_category_active_index = (int)list.length();
-+                    m_category_active_index = length - 1;
-+            break;
-+        case Gdk.Key.Home:
-+            m_category_active_index = 0;
-+            break;
-+        case Gdk.Key.End:
-+            m_category_active_index = length - 1;
-+            break;
-         }
--        Gtk.Adjustment adjustment = m_list_box.get_adjustment();
--        m_scrolled_window.set_hadjustment(new Gtk.Adjustment(0, 0, 0, 0, 0, 0));
--        m_scrolled_window.set_vadjustment(adjustment);
--        show_category_list();
-+        var row = m_list_box.get_selected_row();
-+        if (row != null)
-+            m_list_box.unselect_row(row);
-+        if (m_category_active_index >= 0) {
-+            row = m_list_box.get_row_at_index(m_category_active_index);
-+            m_list_box.select_row(row);
-+        } else {
-+            row = m_list_box.get_row_at_index(0);
-+        }
-+        Gtk.Allocation alloc = { 0, 0, 0, 0 };
-+        row.get_allocation(out alloc);
-+        var adjustment = m_scrolled_window.get_vadjustment();
-+        adjustment.clamp_page(alloc.y, alloc.y + alloc.height);
-+        return true;
-     }
- 
- 
-@@ -1211,7 +1585,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
-                 keyval = Gdk.Key.Up;
-             else if (keyval == Gdk.Key.Right)
-                 keyval = Gdk.Key.Down;
--            category_list_cursor_move(keyval);
-+            return category_list_cursor_move(keyval);
-         }
-         return true;
-     }
-@@ -1227,7 +1601,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
-             else if (keyval == Gdk.Key.Up)
-                 candidate_panel_cursor_up();
-         } else {
--            category_list_cursor_move(keyval);
-+            return category_list_cursor_move(keyval);
-         }
-         return true;
-     }
-@@ -1262,12 +1636,25 @@ class IBusEmojier : Gtk.ApplicationWindow {
-                             ? true : false);
-             return true;
-         }
-+        if (!m_candidate_panel_is_visible)
-+            return category_list_cursor_move(keyval);
-         return false;
-     }
- 
- 
-     private bool key_press_escape() {
--        if (m_backward_index >= 0 && m_backward != null) {
-+        if (m_show_unicode) {
-+            if (m_candidate_panel_is_visible) {
-+                m_candidate_panel_is_visible = false;
-+                show_unicode_blocks();
-+                return true;
-+            } else {
-+                m_show_unicode = false;
-+                m_category_active_index = -1;
-+                hide_candidate_panel();
-+                return true;
-+            }
-+        } else if (m_backward_index >= 0 && m_backward != null) {
-             show_emoji_for_category(m_backward);
-             return true;
-         } else if (m_candidate_panel_is_visible) {
-@@ -1287,10 +1674,13 @@ class IBusEmojier : Gtk.ApplicationWindow {
-         if (m_candidate_panel_is_visible) {
-             uint index = m_lookup_table.get_cursor_pos();
-             candidate_panel_select_index(index);
--        } else if (m_category_active_index > 0) {
-+        } else if (m_category_active_index >= 0) {
-             Gtk.ListBoxRow gtkrow = m_list_box.get_selected_row();
-             EBoxRow row = gtkrow as EBoxRow;
--            show_emoji_for_category(row.text);
-+            if (m_show_unicode)
-+                show_unicode_for_block(row.text);
-+            else
-+                show_emoji_for_category(row.text);
-         }
-         return true;
-     }
-@@ -1380,6 +1770,7 @@ class IBusEmojier : Gtk.ApplicationWindow {
-         m_candidate_panel_is_visible = false;
-         m_result = null;
-         m_enter_notify_enable = true;
-+        m_show_unicode = false;
- 
-         /* Let gtk recalculate the window size. */
-         resize(1, 1);
-@@ -1399,8 +1790,9 @@ class IBusEmojier : Gtk.ApplicationWindow {
-          * prevention logic:
-          * https://mail.gnome.org/archives/gtk-devel-list/2017-May/msg00026.html
-          */
--        uint32 timestamp = event.get_time();
--        present_with_time(timestamp);
-+        //uint32 timestamp = event.get_time();
-+        //present_with_time(timestamp);
-+        present_centralize(event);
- 
-         Gdk.Device pointer;
- #if VALA_0_34
-@@ -1646,18 +2038,24 @@ class IBusEmojier : Gtk.ApplicationWindow {
-         Gtk.Allocation allocation;
-         get_allocation(out allocation);
-         Gdk.Rectangle monitor_area;
-+        Gdk.Rectangle work_area;
- #if VALA_0_34
-         Gdk.Display display = Gdk.Display.get_default();
-         Gdk.Monitor monitor = display.get_monitor_at_window(this.get_window());
-         monitor_area = monitor.get_geometry();
-+        work_area = monitor.get_workarea();
- #else
-         Gdk.Screen screen = Gdk.Screen.get_default();
-         int monitor_num = screen.get_monitor_at_window(this.get_window());
-         screen.get_monitor_geometry(monitor_num, out monitor_area);
-+        work_area = screen.get_monitor_workarea(monitor_num);
- #endif
-         int x = (monitor_area.x + monitor_area.width - allocation.width)/2;
-         int y = (monitor_area.y + monitor_area.height
-                  - allocation.height)/2;
-+        // Do not hide a bottom panel in XFCE4
-+        if (work_area.y < y)
-+            y = work_area.y;
-         move(x, y);
- 
-         uint32 timestamp = event.get_time();
-@@ -1723,7 +2121,6 @@ class IBusEmojier : Gtk.ApplicationWindow {
-             string? favorite = unowned_favorites[i];
-             // Avoid gsetting value error by manual setting
-             GLib.return_if_fail(favorite != null);
--            GLib.return_if_fail(favorite != "");
-             m_favorites += favorite;
-         }
-         for(int i = 0; i < unowned_favorite_annotations.length; i++) {
-@@ -1733,4 +2130,19 @@ class IBusEmojier : Gtk.ApplicationWindow {
-         }
-         update_favorite_emoji_dict();
-     }
-+
-+
-+    private static GLib.Object get_load_progress_object() {
-+            if (m_unicode_progress_object == null)
-+                m_unicode_progress_object = new LoadProgressObject();
-+            return m_unicode_progress_object as GLib.Object;
-+    }
-+
-+
-+    public static void load_unicode_dict() {
-+        if (m_unicode_block_list.length() == 0)
-+            make_unicode_block_dict();
-+        if (m_name_to_unicodes_dict.size() == 0)
-+            make_unicode_name_dict(IBusEmojier.get_load_progress_object());
-+    }
- }
-diff --git a/ui/gtk3/emojierapp.vala b/ui/gtk3/emojierapp.vala
-index 6615f22b..d816352e 100644
---- a/ui/gtk3/emojierapp.vala
-+++ b/ui/gtk3/emojierapp.vala
-@@ -176,6 +176,8 @@ public class EmojiApplication : Application {
-                 m_settings_emoji.get_strv("favorites"),
-                 m_settings_emoji.get_strv("favorite-annotations"));
- 
-+        IBusEmojier.load_unicode_dict();
-+
-         activate_dialog(command_line);
- 
-         return Posix.EXIT_SUCCESS;
-diff --git a/ui/gtk3/ibusemojidialog.h b/ui/gtk3/ibusemojidialog.h
-index ed8886a8..3b420b21 100644
---- a/ui/gtk3/ibusemojidialog.h
-+++ b/ui/gtk3/ibusemojidialog.h
-@@ -1,7 +1,7 @@
- /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
- /* vim:set et sts=4: */
- /* bus - The Input Bus
-- * Copyright (C) 2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2017-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-  * Copyright (C) 2017 Red Hat, Inc.
-  *
-  * This library is free software; you can redistribute it and/or
-@@ -196,5 +196,12 @@ void          ibus_emojier_set_partial_match_length
-  */
- void          ibus_emojier_set_partial_match_condition
-                                                   (gint         condition);
-+/**
-+ * ibus_emojier_load_unicode_dict:
-+ *
-+ * Load the dictionary of #IBusUnicodeData.
-+ */
-+void          ibus_emojier_load_unicode_dict      (void);
-+
- G_END_DECLS
- #endif
-diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
-index 4f032f6f..bcb3ed75 100644
---- a/ui/gtk3/panel.vala
-+++ b/ui/gtk3/panel.vala
-@@ -3,7 +3,7 @@
-  * ibus - The Input Bus
-  *
-  * Copyright(c) 2011-2014 Peng Huang <shawn.p.huang@gmail.com>
-- * Copyright(c) 2015-2017 Takao Fujwiara <takao.fujiwara1@gmail.com>
-+ * Copyright(c) 2015-2018 Takao Fujwiara <takao.fujiwara1@gmail.com>
-  *
-  * This library is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU Lesser General Public
-@@ -871,6 +871,7 @@ class Panel : IBus.PanelService {
-             IBusEmojier.set_annotation_lang(
-                     m_settings_emoji.get_string("lang"));
-             m_emojier_set_emoji_lang_id = 0;
-+            IBusEmojier.load_unicode_dict();
-             return false;
-         });
-     }
--- 
-2.14.3
-
-From 4cfd5ad7c6d071cfef6c7d678cc027ea480b8fc9 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Tue, 6 Feb 2018 11:02:09 +0900
-Subject: [PATCH] Fix typo in ibusunicode.c
-
-Review URL: https://codereview.appspot.com/340740043
----
- src/ibusunicode.c    | 5 +++--
- ui/gtk3/emojier.vala | 4 ----
- 2 files changed, 3 insertions(+), 6 deletions(-)
-
-diff --git a/src/ibusunicode.c b/src/ibusunicode.c
-index 8559819d..aac9c135 100644
---- a/src/ibusunicode.c
-+++ b/src/ibusunicode.c
-@@ -412,7 +412,8 @@ ibus_unicode_data_list_deserialize (GVariant *variant,
-                 IBUS_UNICODE_DESERIALIZE_SIGNALL_STR,
-                 G_OBJECT_TYPE (source_object));
-         if (!has_signal) {
--            const gchar type_name = g_type_name (source_object);
-+            const gchar *type_name =
-+                    g_type_name (G_OBJECT_TYPE (source_object));
-             g_warning ("GObject %s does not have the signal \"%s\"",
-                        type_name ? type_name : "(null)",
-                        IBUS_UNICODE_DESERIALIZE_SIGNALL_STR);
-@@ -677,7 +678,7 @@ ibus_unicode_block_class_init (IBusUnicodeBlockClass *class)
-     GObjectClass *gobject_class = G_OBJECT_CLASS (class);
-     IBusSerializableClass *serializable_class = IBUS_SERIALIZABLE_CLASS (class);
- 
--    object_class->destroy = (IBusObjectDestroyFunc) ibus_unicode_data_destroy;
-+    object_class->destroy = (IBusObjectDestroyFunc) ibus_unicode_block_destroy;
-     gobject_class->set_property =
-             (GObjectSetPropertyFunc) ibus_unicode_block_set_property;
-     gobject_class->get_property =
-diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
-index 555ea68f..0bf34da8 100644
---- a/ui/gtk3/emojier.vala
-+++ b/ui/gtk3/emojier.vala
-@@ -1373,8 +1373,6 @@ public class IBusEmojier : Gtk.ApplicationWindow {
-                     return;
-                 }
-             }
--            // TODO: Provide a custom description and annotation for
--            // the favorite emojis.
-             EPaddedLabelBox widget = new EPaddedLabelBox(
-                         _("Description: %s").printf(_("None")),
-                         Gtk.Align.START);
-@@ -1790,8 +1788,6 @@ public class IBusEmojier : Gtk.ApplicationWindow {
-          * prevention logic:
-          * https://mail.gnome.org/archives/gtk-devel-list/2017-May/msg00026.html
-          */
--        //uint32 timestamp = event.get_time();
--        //present_with_time(timestamp);
-         present_centralize(event);
- 
-         Gdk.Device pointer;
--- 
-2.14.3
-
-From fb07f64764f18f702221ff5574b2fd2193f051f0 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Tue, 20 Feb 2018 17:25:07 +0900
-Subject: [PATCH] Implement ibus-extension-gtk3 for the global keybinding
-
-Currently IBus panel (ibus-ui-gtk3) is not available in GNOME and Plasma
-so the emoji and unicode point typings are not available in GNOME and Plasma.
-The workaround `ibus emoji` command is available but it put the selected
-character into the copy buffer and users have to paste the character.
-
-Originaly the emoji feature was implemented in IBus GtkIMModule but
-it had several problems; the first is the keybinding is hard-coded
-and IBus GtkIMModule does not use GSettings for the customized settings.
-The second is the feature was available for GTK applications.
-The third is that XKB input sources uses gtk-im-context-simple
-but not ibus in GNOME desktop so users have to add an IM input sources
-to enable IBus for the XKB input sources. The fourth is the feature
-was available for IBusEngineSimple only and other IBus IMEs need to
-inherit that class to get the emoji feature. The fifth is that
-emoji typing is available for English only since IBusEngineSimple
-had the feature. The sixth is that the default one dimension lookup
-window was not useful to choose an emoji and needed two dimensions
-lookup window.
-
-And the implementation was moved from IBus GtkIMModule to IBus panel
-to fix above problems.
-But users have to use `ibus emoji` at present if ibus-ui-gtk3
-is not available.
-
-Now I think to move the emoji feature from ibus-ui-gtk3 to another
-IBus component; ibus-extension-gtk3 which manages the Ctrl-Shift-e.
-GNOME and Plasma desktops still do not show the GUI menu but
-the shortcut key is available in this implementation.
-
-BUG=RHBZ#1430501
-R=Shawn.P.Huang@gmail.com
-
-Review URL: https://codereview.appspot.com/339300043
----
- bindings/vala/IBus-1.0-custom.vala |    4 +
- bus/Makefile.am                    |    7 +-
- bus/ibusimpl.c                     |   82 ++-
- bus/main.c                         |   25 +-
- bus/marshalers.list                |    1 +
- bus/panelproxy.c                   |   57 +-
- bus/panelproxy.h                   |   20 +-
- src/Makefile.am                    |    4 +-
- src/ibus.h                         |    1 +
- src/ibusmarshalers.list            |    1 +
- src/ibuspanelservice.c             |   81 ++-
- src/ibuspanelservice.h             |   15 +-
- src/ibusserializable.c             |    7 +-
- src/ibusserializable.h             |   18 +-
- src/ibusshare.c                    |    4 +-
- src/ibusshare.h                    |   17 +-
- src/ibusxevent.c                   | 1004 ++++++++++++++++++++++++++++++++++++
- src/ibusxevent.h                   |  294 +++++++++++
- src/tests/runtest                  |    1 +
- ui/gtk3/Makefile.am                |   59 ++-
- ui/gtk3/bindingcommon.vala         |  215 ++++++++
- ui/gtk3/candidatearea.vala         |  102 ----
- ui/gtk3/extension.vala             |  124 +++++
- ui/gtk3/gtkextension.xml.in        |   12 +
- ui/gtk3/iconwidget.vala            |  103 ++++
- ui/gtk3/panel.vala                 |  408 +++------------
- ui/gtk3/panelbinding.vala          |  335 ++++++++++++
- 27 files changed, 2506 insertions(+), 495 deletions(-)
- create mode 100644 src/ibusxevent.c
- create mode 100644 src/ibusxevent.h
- create mode 100644 ui/gtk3/bindingcommon.vala
- create mode 100644 ui/gtk3/extension.vala
- create mode 100644 ui/gtk3/gtkextension.xml.in
- create mode 100644 ui/gtk3/panelbinding.vala
-
-diff --git a/bindings/vala/IBus-1.0-custom.vala b/bindings/vala/IBus-1.0-custom.vala
-index 144d75e2..cf1fc3fa 100644
---- a/bindings/vala/IBus-1.0-custom.vala
-+++ b/bindings/vala/IBus-1.0-custom.vala
-@@ -6,4 +6,8 @@ namespace IBus {
- 		[CCode (cname = "ibus_text_new_from_static_string", has_construct_function = false)]
- 		public Text.from_static_string (string str);
- 	}
-+	public class XEvent : IBus.Serializable {
-+		[CCode (cname = "ibus_x_event_new", has_construct_function = true)]
-+		public XEvent (string first_property_name, ...);
-+	}
- }
-diff --git a/bus/Makefile.am b/bus/Makefile.am
-index 864ba923..8bcc8e16 100644
---- a/bus/Makefile.am
-+++ b/bus/Makefile.am
-@@ -3,7 +3,8 @@
- # ibus - The Input Bus
- #
- # Copyright (c) 2007-2013 Peng Huang <shawn.p.huang@gmail.com>
--# Copyright (c) 2007-2013 Red Hat, Inc.
-+# Copyright (c) 2013-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+# Copyright (c) 2007-2018 Red Hat, Inc.
- #
- # This library is free software; you can redistribute it and/or
- # modify it under the terms of the GNU Lesser General Public
-@@ -105,6 +106,10 @@ marshalers.c: marshalers.h marshalers.list
- 	$(GLIB_GENMARSHAL) --prefix=bus_marshal $(srcdir)/marshalers.list --body --internal) > $@.tmp && \
- 	mv $@.tmp $@
- 
-+if ENABLE_EMOJI_DICT
-+AM_CFLAGS += -DEMOJI_DICT
-+endif
-+
- 
- if ENABLE_TESTS
- TESTS = \
-diff --git a/bus/ibusimpl.c b/bus/ibusimpl.c
-index f99307ad..58d205cf 100644
---- a/bus/ibusimpl.c
-+++ b/bus/ibusimpl.c
-@@ -2,7 +2,8 @@
- /* vim:set et sts=4: */
- /* ibus - The Input Bus
-  * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
-- * Copyright (C) 2008-2013 Red Hat, Inc.
-+ * Copyright (C) 2011-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2008-2018 Red Hat, Inc.
-  *
-  * This library is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU Lesser General Public
-@@ -73,6 +74,7 @@ struct _BusIBusImpl {
- 
-     BusInputContext *focused_context;
-     BusPanelProxy   *panel;
-+    BusPanelProxy   *extension;
- 
-     /* a default keymap of ibus-daemon (usually "us") which is used only
-      * when use_sys_layout is FALSE. */
-@@ -290,12 +292,37 @@ _panel_destroy_cb (BusPanelProxy *panel,
-     g_assert (BUS_IS_PANEL_PROXY (panel));
-     g_assert (BUS_IS_IBUS_IMPL (ibus));
- 
--    g_return_if_fail (ibus->panel == panel);
--
--    ibus->panel = NULL;
-+    if (ibus->panel == panel)
-+        ibus->panel = NULL;
-+    else if (ibus->extension == panel)
-+        ibus->extension = NULL;
-+    else
-+        g_return_if_reached ();
-     g_object_unref (panel);
- }
- 
-+static void
-+_panel_panel_extension_cb (BusPanelProxy *panel,
-+                           GVariant      *parameters,
-+                           BusIBusImpl  *ibus)
-+{
-+    if (!ibus->extension) {
-+        g_warning ("Panel extension is not running.");
-+        return;
-+    }
-+
-+    g_return_if_fail (BUS_IS_IBUS_IMPL (ibus));
-+    g_return_if_fail (BUS_IS_PANEL_PROXY (ibus->extension));
-+
-+    /* Use the DBus method because it seems any DBus signal,
-+     * g_dbus_message_new_signal(), cannot be reached to the server. */
-+    g_dbus_proxy_call (G_DBUS_PROXY (ibus->extension),
-+                       "PanelExtensionReceived",
-+                       parameters,
-+                       G_DBUS_CALL_FLAGS_NONE,
-+                       -1, NULL, NULL, NULL);
-+}
-+
- static void
- _registry_changed_cb (IBusRegistry *registry,
-                       BusIBusImpl  *ibus)
-@@ -317,33 +344,47 @@ _dbus_name_owner_changed_cb (BusDBusImpl   *dbus,
-                              const gchar   *new_name,
-                              BusIBusImpl   *ibus)
- {
-+    PanelType panel_type = PANEL_TYPE_NONE;
-+
-     g_assert (BUS_IS_DBUS_IMPL (dbus));
-     g_assert (name != NULL);
-     g_assert (old_name != NULL);
-     g_assert (new_name != NULL);
-     g_assert (BUS_IS_IBUS_IMPL (ibus));
- 
--    if (g_strcmp0 (name, IBUS_SERVICE_PANEL) == 0) {
-+    if (!g_strcmp0 (name, IBUS_SERVICE_PANEL))
-+        panel_type = PANEL_TYPE_PANEL;
-+    else if (!g_strcmp0 (name, IBUS_SERVICE_PANEL_EXTENSION))
-+        panel_type = PANEL_TYPE_EXTENSION;
-+
-+    if (panel_type != PANEL_TYPE_NONE) {
-         if (g_strcmp0 (new_name, "") != 0) {
-             /* a Panel process is started. */
-             BusConnection *connection;
-             BusInputContext *context = NULL;
--
--            if (ibus->panel != NULL) {
--                ibus_proxy_destroy ((IBusProxy *) ibus->panel);
--                /* panel should be NULL after destroy. See _panel_destroy_cb for details. */
--                g_assert (ibus->panel == NULL);
-+            BusPanelProxy   **panel = (panel_type == PANEL_TYPE_PANEL) ?
-+                                       &ibus->panel : &ibus->extension;
-+
-+            if (*panel != NULL) {
-+                ibus_proxy_destroy ((IBusProxy *)(*panel));
-+                /* panel should be NULL after destroy. See _panel_destroy_cb
-+                 * for details. */
-+                g_assert (*panel == NULL);
-             }
- 
-             connection = bus_dbus_impl_get_connection_by_name (BUS_DEFAULT_DBUS, new_name);
-             g_return_if_fail (connection != NULL);
- 
--            ibus->panel = bus_panel_proxy_new (connection);
-+            *panel = bus_panel_proxy_new (connection, panel_type);
- 
--            g_signal_connect (ibus->panel,
-+            g_signal_connect (*panel,
-                               "destroy",
-                               G_CALLBACK (_panel_destroy_cb),
-                               ibus);
-+            g_signal_connect (*panel,
-+                              "panel-extension",
-+                              G_CALLBACK (_panel_panel_extension_cb),
-+                              ibus);
- 
-             if (ibus->focused_context != NULL) {
-                 context = ibus->focused_context;
-@@ -355,14 +396,13 @@ _dbus_name_owner_changed_cb (BusDBusImpl   *dbus,
-             if (context != NULL) {
-                 BusEngineProxy *engine;
- 
--                bus_panel_proxy_focus_in (ibus->panel, context);
-+                bus_panel_proxy_focus_in (*panel, context);
- 
-                 engine = bus_input_context_get_engine (context);
-                 if (engine != NULL) {
-                     IBusPropList *prop_list =
-                         bus_engine_proxy_get_properties (engine);
--                    bus_panel_proxy_register_properties (ibus->panel,
--                                                         prop_list);
-+                    bus_panel_proxy_register_properties (*panel, prop_list);
-                 }
-             }
-         }
-@@ -403,6 +443,7 @@ bus_ibus_impl_init (BusIBusImpl *ibus)
-     ibus->contexts = NULL;
-     ibus->focused_context = NULL;
-     ibus->panel = NULL;
-+    ibus->extension = NULL;
- 
-     ibus->keymap = ibus_keymap_get ("us");
- 
-@@ -635,6 +676,8 @@ bus_ibus_impl_set_focused_context (BusIBusImpl     *ibus,
- 
-         if (ibus->panel != NULL)
-             bus_panel_proxy_focus_out (ibus->panel, ibus->focused_context);
-+        if (ibus->extension != NULL)
-+            bus_panel_proxy_focus_out (ibus->extension, ibus->focused_context);
- 
-         bus_input_context_get_content_type (ibus->focused_context,
-                                             &purpose, &hints);
-@@ -658,6 +701,8 @@ bus_ibus_impl_set_focused_context (BusIBusImpl     *ibus,
- 
-         if (ibus->panel != NULL)
-             bus_panel_proxy_focus_in (ibus->panel, context);
-+        if (ibus->extension != NULL)
-+            bus_panel_proxy_focus_in (ibus->extension, context);
-     }
- 
-     if (engine != NULL)
-@@ -846,8 +891,13 @@ _context_destroy_cb (BusInputContext    *context,
-         bus_ibus_impl_set_focused_context (ibus, NULL);
- 
-     if (ibus->panel &&
--        bus_input_context_get_capabilities (context) & IBUS_CAP_FOCUS)
-+        bus_input_context_get_capabilities (context) & IBUS_CAP_FOCUS) {
-         bus_panel_proxy_destroy_context (ibus->panel, context);
-+    }
-+    if (ibus->extension &&
-+        bus_input_context_get_capabilities (context) & IBUS_CAP_FOCUS) {
-+        bus_panel_proxy_destroy_context (ibus->extension, context);
-+    }
- 
-     ibus->contexts = g_list_remove (ibus->contexts, context);
-     g_object_unref (context);
-diff --git a/bus/main.c b/bus/main.c
-index 6ad60179..5b2589b1 100644
---- a/bus/main.c
-+++ b/bus/main.c
-@@ -2,7 +2,8 @@
- /* vim:set et sts=4: */
- /* ibus - The Input Bus
-  * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
-- * Copyright (C) 2008-2013 Red Hat, Inc.
-+ * Copyright (C) 2013-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2008-2018 Red Hat, Inc.
-  *
-  * This library is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU Lesser General Public
-@@ -42,6 +43,7 @@ static gboolean xim = FALSE;
- static gboolean replace = FALSE;
- static gboolean restart = FALSE;
- static gchar *panel = "default";
-+static gchar *panel_extension = "default";
- static gchar *config = "default";
- static gchar *desktop = "gnome";
- 
-@@ -60,6 +62,7 @@ static const GOptionEntry entries[] =
-     { "xim",       'x', 0, G_OPTION_ARG_NONE,   &xim,       "execute ibus XIM server.", NULL },
-     { "desktop",   'n', 0, G_OPTION_ARG_STRING, &desktop,   "specify the name of desktop session. [default=gnome]", "name" },
-     { "panel",     'p', 0, G_OPTION_ARG_STRING, &panel,     "specify the cmdline of panel program. pass 'disable' not to start a panel program.", "cmdline" },
-+    { "panel-extension", 'E', 0, G_OPTION_ARG_STRING, &panel_extension, "specify the cmdline of panel extension program. pass 'disable' not to start an extension program.", "cmdline" },
-     { "config",    'c', 0, G_OPTION_ARG_STRING, &config,    "specify the cmdline of config program. pass 'disable' not to start a config program.", "cmdline" },
-     { "address",   'a', 0, G_OPTION_ARG_STRING, &g_address,   "specify the address of ibus daemon.", "address" },
-     { "replace",   'r', 0, G_OPTION_ARG_NONE,   &replace,   "if there is an old ibus-daemon is running, it will be replaced.", NULL },
-@@ -268,7 +271,27 @@ main (gint argc, gchar **argv)
-             if (!execute_cmdline (panel))
-                 exit (-1);
-         }
-+
-+#ifdef EMOJI_DICT
-+        if (g_strcmp0 (panel_extension, "default") == 0) {
-+            BusComponent *component;
-+            component = bus_ibus_impl_lookup_component_by_name (
-+                    BUS_DEFAULT_IBUS, IBUS_SERVICE_PANEL_EXTENSION);
-+            if (component) {
-+                bus_component_set_restart (component, restart);
-+            }
-+            if (component == NULL ||
-+                !bus_component_start (component, g_verbose)) {
-+                g_printerr ("Can not execute default panel program\n");
-+                exit (-1);
-+            }
-+        } else if (g_strcmp0 (panel_extension, "disable") != 0 &&
-+                   g_strcmp0 (panel_extension, "") != 0) {
-+            if (!execute_cmdline (panel_extension))
-+                exit (-1);
-+        }
-     }
-+#endif
- 
-     /* execute ibus xim server */
-     if (xim) {
-diff --git a/bus/marshalers.list b/bus/marshalers.list
-index c032cdaa..437c6fee 100644
---- a/bus/marshalers.list
-+++ b/bus/marshalers.list
-@@ -12,4 +12,5 @@ VOID:STRING
- VOID:STRING,INT
- VOID:UINT,UINT
- VOID:UINT,UINT,UINT
-+VOID:VARIANT
- VOID:VOID
-diff --git a/bus/panelproxy.c b/bus/panelproxy.c
-index 8381d7dc..c3908fcf 100644
---- a/bus/panelproxy.c
-+++ b/bus/panelproxy.c
-@@ -2,8 +2,8 @@
- /* vim:set et sts=4: */
- /* ibus - The Input Bus
-  * Copyright (C) 2008-2014 Peng Huang <shawn.p.huang@gmail.com>
-- * Copyright (C) 2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
-- * Copyright (C) 2008-2014 Red Hat, Inc.
-+ * Copyright (C) 2017-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2008-2018 Red Hat, Inc.
-  *
-  * This library is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU Lesser General Public
-@@ -51,6 +51,7 @@ enum {
-     PROPERTY_SHOW,
-     PROPERTY_HIDE,
-     COMMIT_TEXT,
-+    PANEL_EXTENSION,
-     LAST_SIGNAL,
- };
- 
-@@ -59,6 +60,7 @@ struct _BusPanelProxy {
- 
-     /* instance members */
-     BusInputContext *focused_context;
-+    PanelType panel_type;
- };
- 
- struct _BusPanelProxyClass {
-@@ -110,22 +112,39 @@ static void     bus_panel_proxy_commit_text
- G_DEFINE_TYPE(BusPanelProxy, bus_panel_proxy, IBUS_TYPE_PROXY)
- 
- BusPanelProxy *
--bus_panel_proxy_new (BusConnection *connection)
-+bus_panel_proxy_new (BusConnection *connection,
-+                     PanelType      panel_type)
- {
-+    const gchar *path = NULL;
-+    GObject *obj;
-+    BusPanelProxy *panel;
-+
-     g_assert (BUS_IS_CONNECTION (connection));
- 
--    GObject *obj;
-+    switch (panel_type) {
-+    case PANEL_TYPE_PANEL:
-+        path = IBUS_PATH_PANEL;
-+        break;
-+    case PANEL_TYPE_EXTENSION:
-+        path = IBUS_PATH_PANEL_EXTENSION;
-+        break;
-+    default:
-+        g_return_val_if_reached (NULL);
-+    }
-+
-     obj = g_initable_new (BUS_TYPE_PANEL_PROXY,
-                           NULL,
-                           NULL,
--                          "g-object-path",     IBUS_PATH_PANEL,
-+                          "g-object-path",     path,
-                           "g-interface-name",  IBUS_INTERFACE_PANEL,
-                           "g-connection",      bus_connection_get_dbus_connection (connection),
-                           "g-default-timeout", g_gdbus_timeout,
-                           "g-flags",           G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
-                           NULL);
- 
--    return BUS_PANEL_PROXY (obj);
-+    panel = BUS_PANEL_PROXY (obj);
-+    panel->panel_type = panel_type;
-+    return panel;
- }
- 
- static void
-@@ -231,6 +250,16 @@ bus_panel_proxy_class_init (BusPanelProxyClass *class)
-             bus_marshal_VOID__OBJECT,
-             G_TYPE_NONE, 1,
-             IBUS_TYPE_TEXT);
-+
-+    panel_signals[PANEL_EXTENSION] =
-+        g_signal_new (I_("panel-extension"),
-+            G_TYPE_FROM_CLASS (class),
-+            G_SIGNAL_RUN_LAST,
-+            0,
-+            NULL, NULL,
-+            bus_marshal_VOID__VARIANT,
-+            G_TYPE_NONE, 1,
-+            G_TYPE_VARIANT);
- }
- 
- static void
-@@ -337,6 +366,15 @@ bus_panel_proxy_g_signal (GDBusProxy  *proxy,
-         return;
-     }
- 
-+    if (g_strcmp0 ("PanelExtension", signal_name) == 0) {
-+        if (panel->panel_type != PANEL_TYPE_PANEL) {
-+            g_warning ("Wrong signal");
-+            return;
-+        }
-+        g_signal_emit (panel, panel_signals[PANEL_EXTENSION], 0, parameters);
-+        return;
-+    }
-+
-     /* shound not be reached */
-     g_return_if_reached ();
- }
-@@ -832,3 +870,10 @@ bus_panel_proxy_destroy_context (BusPanelProxy    *panel,
- 
-     g_object_unref (context);
- }
-+
-+PanelType
-+bus_panel_proxy_get_panel_type (BusPanelProxy    *panel)
-+{
-+    g_assert (BUS_IS_PANEL_PROXY (panel));
-+    return panel->panel_type;
-+}
-diff --git a/bus/panelproxy.h b/bus/panelproxy.h
-index 5002f86d..b5a7af17 100644
---- a/bus/panelproxy.h
-+++ b/bus/panelproxy.h
-@@ -2,7 +2,8 @@
- /* vim:set et sts=4: */
- /* ibus - The Input Bus
-  * Copyright (C) 2008-2014 Peng Huang <shawn.p.huang@gmail.com>
-- * Copyright (C) 2008-2014 Red Hat, Inc.
-+ * Copyright (C) 2017-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2008-2018 Red Hat, Inc.
-  *
-  * This library is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU Lesser General Public
-@@ -50,12 +51,25 @@
- 
- G_BEGIN_DECLS
- 
-+typedef enum
-+{
-+    PANEL_TYPE_NONE,
-+    PANEL_TYPE_PANEL,
-+    PANEL_TYPE_EXTENSION
-+} PanelType;
-+
- typedef struct _BusPanelProxy BusPanelProxy;
- typedef struct _BusPanelProxyClass BusPanelProxyClass;
- 
- GType            bus_panel_proxy_get_type      (void);
--BusPanelProxy   *bus_panel_proxy_new           (BusConnection     *connection);
-+BusPanelProxy   *bus_panel_proxy_new           (BusConnection     *connection,
-+                                                PanelType          panel_type);
- 
-+gboolean         bus_panel_proxy_send_signal   (BusPanelProxy   *panel,
-+                                                const gchar     *interface_name,
-+                                                const gchar     *signal_name,
-+                                                GVariant        *parameters,
-+                                                GError         **error);
- /* functions that invoke D-Bus methods of the panel component. */
- void             bus_panel_proxy_focus_in      (BusPanelProxy     *panel,
-                                                 BusInputContext   *context);
-@@ -119,6 +133,8 @@ void             bus_panel_proxy_set_content_type
-                                                (BusPanelProxy     *panel,
-                                                 guint              purpose,
-                                                 guint              hints);
-+PanelType        bus_panel_proxy_get_panel_type
-+                                               (BusPanelProxy     *panel);
- G_END_DECLS
- #endif
- 
-diff --git a/src/Makefile.am b/src/Makefile.am
-index 1ba418d8..72ec05ab 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -3,7 +3,7 @@
- # ibus - The Input Bus
- #
- # Copyright (c) 2007-2015 Peng Huang <shawn.p.huang@gmail.com>
--# Copyright (c) 2015-2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+# Copyright (c) 2015-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
- # Copyright (c) 2007-2017 Red Hat, Inc.
- #
- # This library is free software; you can redistribute it and/or
-@@ -103,6 +103,7 @@ ibus_sources =              \
-     ibustext.c              \
-     ibusunicode.c           \
-     ibusutil.c              \
-+    ibusxevent.c            \
-     ibusxml.c               \
-     $(NULL)
- libibus_1_0_la_SOURCES =    \
-@@ -155,6 +156,7 @@ ibus_headers =              \
-     ibustypes.h             \
-     ibusunicode.h           \
-     ibusutil.h              \
-+    ibusxevent.h            \
-     ibusxml.h               \
-     $(NULL)
- ibusincludedir = $(includedir)/ibus-@IBUS_API_VERSION@
-diff --git a/src/ibus.h b/src/ibus.h
-index 8011729f..b15dded9 100644
---- a/src/ibus.h
-+++ b/src/ibus.h
-@@ -59,6 +59,7 @@
- #include <ibusregistry.h>
- #include <ibusemoji.h>
- #include <ibusunicode.h>
-+#include <ibusxevent.h>
- 
- #ifndef IBUS_DISABLE_DEPRECATED
- #include <ibuskeysyms-compat.h>
-diff --git a/src/ibusmarshalers.list b/src/ibusmarshalers.list
-index 918bc7f7..8d91937e 100644
---- a/src/ibusmarshalers.list
-+++ b/src/ibusmarshalers.list
-@@ -24,4 +24,5 @@ VOID:STRING,STRING,STRING
- VOID:UINT
- VOID:UINT,POINTER
- VOID:POINTER,UINT
-+VOID:VARIANT
- OBJECT:STRING
-diff --git a/src/ibuspanelservice.c b/src/ibuspanelservice.c
-index 33949fa1..f37b91c3 100644
---- a/src/ibuspanelservice.c
-+++ b/src/ibuspanelservice.c
-@@ -25,6 +25,10 @@
- #include "ibusmarshalers.h"
- #include "ibusinternal.h"
- 
-+#define IBUS_PANEL_SERVICE_GET_PRIVATE(o)  \
-+   (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_PANEL_SERVICE, \
-+                                 IBusPanelServicePrivate))
-+
- enum {
-     UPDATE_PREEDIT_TEXT,
-     UPDATE_AUXILIARY_TEXT,
-@@ -52,6 +56,7 @@ enum {
-     STATE_CHANGED,
-     DESTROY_CONTEXT,
-     SET_CONTENT_TYPE,
-+    PANEL_EXTENSION_RECEIVED,
-     LAST_SIGNAL,
- };
- 
-@@ -146,6 +151,9 @@ static void      ibus_panel_service_set_content_type
-                                    (IBusPanelService       *panel,
-                                     guint                   purpose,
-                                     guint                   hints);
-+static void      ibus_panel_service_panel_extension_received
-+                                   (IBusPanelService       *panel,
-+                                    GVariant               *data);
- 
- G_DEFINE_TYPE (IBusPanelService, ibus_panel_service, IBUS_TYPE_SERVICE)
- 
-@@ -212,6 +220,9 @@ static const gchar introspection_xml[] =
-     "      <arg direction='in'  type='u' name='purpose' />"
-     "      <arg direction='in'  type='u' name='hints' />"
-     "    </method>"
-+    "    <method name='PanelExtensionReceived'>"
-+    "      <arg direction='in' type='v' name='data' />"
-+    "    </method>"
-     /* Signals */
-     "    <signal name='CursorUp' />"
-     "    <signal name='CursorDown' />"
-@@ -235,6 +246,9 @@ static const gchar introspection_xml[] =
-     "    <signal name='CommitText'>"
-     "      <arg type='v' name='text' />"
-     "    </signal>"
-+    "    <signal name='PanelExtension'>"
-+    "      <arg type='v' name='data' />"
-+    "    </signal>"
-     "  </interface>"
-     "</node>";
- 
-@@ -274,6 +288,8 @@ ibus_panel_service_class_init (IBusPanelServiceClass *class)
-     class->update_preedit_text   = ibus_panel_service_update_preedit_text;
-     class->update_property       = ibus_panel_service_update_property;
-     class->set_content_type      = ibus_panel_service_set_content_type;
-+    class->panel_extension_received =
-+            ibus_panel_service_panel_extension_received;
- 
-     class->cursor_down_lookup_table = ibus_panel_service_not_implemented;
-     class->cursor_up_lookup_table   = ibus_panel_service_not_implemented;
-@@ -891,6 +907,30 @@ ibus_panel_service_class_init (IBusPanelServiceClass *class)
-             2,
-             G_TYPE_UINT,
-             G_TYPE_UINT);
-+
-+    /**
-+     * IBusPanelService::panel-extension-received:
-+     * @panel: An #IBusPanelService
-+     * @data: A #GVariant
-+     *
-+     * Emitted when the client application get the ::panel-extension-received.
-+     * Implement the member function
-+     * IBusPanelServiceClass::panel_extension_received in extended class to
-+     * receive this signal.
-+     *
-+     * <note><para>Argument @user_data is ignored in this function.</para>
-+     * </note>
-+     */
-+    panel_signals[PANEL_EXTENSION_RECEIVED] =
-+        g_signal_new (I_("panel-extension-received"),
-+            G_TYPE_FROM_CLASS (gobject_class),
-+            G_SIGNAL_RUN_LAST,
-+            G_STRUCT_OFFSET (IBusPanelServiceClass, panel_extension_received),
-+            NULL, NULL,
-+            _ibus_marshal_VOID__VARIANT,
-+            G_TYPE_NONE,
-+            1,
-+            G_TYPE_VARIANT);
- }
- 
- static void
-@@ -1088,6 +1128,24 @@ ibus_panel_service_service_method_call (IBusService           *service,
-         return;
-     }
- 
-+    if (g_strcmp0 (method_name, "PanelExtensionReceived") == 0) {
-+        GVariant *variant = NULL;
-+        g_variant_get (parameters, "(v)", &variant);
-+        if (variant == NULL) {
-+            g_dbus_method_invocation_return_error (
-+                    invocation,
-+                    G_DBUS_ERROR,
-+                    G_DBUS_ERROR_FAILED,
-+                    "PanelExtensionReceived method gives NULL");
-+            return;
-+        }
-+        g_signal_emit (panel, panel_signals[PANEL_EXTENSION_RECEIVED], 0,
-+                       variant);
-+        g_variant_unref (variant);
-+        g_dbus_method_invocation_return_value (invocation, NULL);
-+        return;
-+    }
-+
-     const static struct {
-         const gchar *name;
-         const gint signal_id;
-@@ -1259,6 +1317,13 @@ ibus_panel_service_set_content_type (IBusPanelService *panel,
-     ibus_panel_service_not_implemented(panel);
- }
- 
-+static void
-+ibus_panel_service_panel_extension_received (IBusPanelService *panel,
-+                                             GVariant         *data)
-+{
-+    ibus_panel_service_not_implemented(panel);
-+}
-+
- IBusPanelService *
- ibus_panel_service_new (GDBusConnection *connection)
- {
-@@ -1347,6 +1412,21 @@ ibus_panel_service_commit_text (IBusPanelService *panel,
-     }
- }
- 
-+void
-+ibus_panel_service_panel_extension (IBusPanelService *panel,
-+                                    GVariant         *variant)
-+{
-+    g_return_if_fail (IBUS_IS_PANEL_SERVICE (panel));
-+    g_return_if_fail (variant);
-+
-+    ibus_service_emit_signal ((IBusService *) panel,
-+                              NULL,
-+                              IBUS_INTERFACE_PANEL,
-+                              "PanelExtension",
-+                              g_variant_new ("(v)", variant),
-+                              NULL);
-+}
-+
- #define DEFINE_FUNC(name, Name)                             \
-     void                                                    \
-     ibus_panel_service_##name (IBusPanelService *panel)     \
-@@ -1364,4 +1444,3 @@ DEFINE_FUNC (cursor_up, CursorUp)
- DEFINE_FUNC (page_down, PageDown)
- DEFINE_FUNC (page_up, PageUp)
- #undef DEFINE_FUNC
--
-diff --git a/src/ibuspanelservice.h b/src/ibuspanelservice.h
-index a5b13c73..60ef842b 100644
---- a/src/ibuspanelservice.h
-+++ b/src/ibuspanelservice.h
-@@ -2,7 +2,7 @@
- /* vim:set et sts=4: */
- /* ibus - The Input Bus
-  * Copyright (c) 2009-2014 Google Inc. All rights reserved.
-- * Copyright (c) 2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (c) 2017-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-  *
-  * This library is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU Lesser General Public
-@@ -128,6 +128,9 @@ struct _IBusPanelServiceClass {
-                                             gint                    y,
-                                             gint                    w,
-                                             gint                    h);
-+    void     (* panel_extension_received)
-+                                           (IBusPanelService       *panel,
-+                                            GVariant               *data);
- 
-     /*< private >*/
-     /* padding */
-@@ -242,5 +245,15 @@ void ibus_panel_service_property_hide     (IBusPanelService *panel,
- void ibus_panel_service_commit_text       (IBusPanelService *panel,
-                                            IBusText         *text);
- 
-+/**
-+ * ibus_panel_service_panel_extension:
-+ * @panel: An #IBusPanelService
-+ * @data: (transfer full): A #GVariant data which is sent to a panel extension. 
-+ *
-+ * Notify that a data is sent
-+ * by sending a "PanelExtension" message to IBus panel extension service.
-+ */
-+void ibus_panel_service_panel_extension   (IBusPanelService *panel,
-+                                           GVariant         *data);
- G_END_DECLS
- #endif
-diff --git a/src/ibusserializable.c b/src/ibusserializable.c
-index d7f867f4..a377b613 100644
---- a/src/ibusserializable.c
-+++ b/src/ibusserializable.c
-@@ -2,7 +2,8 @@
- /* vim:set et sts=4: */
- /* ibus - The Input Bus
-  * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
-- * Copyright (C) 2008-2010 Red Hat, Inc.
-+ * Copyright (C) 2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2008-2018 Red Hat, Inc.
-  *
-  * This library is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU Lesser General Public
-@@ -251,7 +252,7 @@ ibus_serializable_copy (IBusSerializable *object)
- }
- 
- GVariant *
--ibus_serializable_serialize (IBusSerializable *object)
-+ibus_serializable_serialize_object (IBusSerializable *object)
- {
-     g_return_val_if_fail (IBUS_IS_SERIALIZABLE (object), FALSE);
-     gboolean retval;
-@@ -267,7 +268,7 @@ ibus_serializable_serialize (IBusSerializable *object)
- }
- 
- IBusSerializable *
--ibus_serializable_deserialize (GVariant *variant)
-+ibus_serializable_deserialize_object (GVariant *variant)
- {
-     g_return_val_if_fail (variant != NULL, NULL);
- 
-diff --git a/src/ibusserializable.h b/src/ibusserializable.h
-index 4327eaee..102de1bd 100644
---- a/src/ibusserializable.h
-+++ b/src/ibusserializable.h
-@@ -2,7 +2,8 @@
- /* vim:set et sts=4: */
- /* ibus - The Input Bus
-  * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
-- * Copyright (C) 2008-2013 Red Hat, Inc.
-+ * Copyright (C) 2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2008-2018 Red Hat, Inc.
-  *
-  * This library is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU Lesser General Public
-@@ -248,10 +249,10 @@ void                 ibus_serializable_remove_qattachment
-  *
-  * See also: IBusSerializableCopyFunc().
-  */
--IBusSerializable    *ibus_serializable_copy             (IBusSerializable   *serializable);
-+IBusSerializable    *ibus_serializable_copy (IBusSerializable   *serializable);
- 
- /**
-- * ibus_serializable_serialize:
-+ * ibus_serializable_serialize_object:
-  * @serializable: An #IBusSerializable.
-  *
-  * Serialize an #IBusSerializable to a #GVariant.
-@@ -261,10 +262,11 @@ IBusSerializable    *ibus_serializable_copy             (IBusSerializable   *ser
-  *
-  * See also: IBusSerializableCopyFunc().
-  */
--GVariant            *ibus_serializable_serialize        (IBusSerializable   *serializable);
-+GVariant            *ibus_serializable_serialize_object
-+                                            (IBusSerializable   *serializable);
- 
- /**
-- * ibus_serializable_deserialize:
-+ * ibus_serializable_deserialize_object:
-  * @variant: A #GVariant.
-  *
-  * Deserialize a #GVariant to an #IBusSerializable/
-@@ -274,7 +276,11 @@ GVariant            *ibus_serializable_serialize        (IBusSerializable   *ser
-  *
-  * See also: IBusSerializableCopyFunc().
-  */
--IBusSerializable    *ibus_serializable_deserialize      (GVariant           *variant);
-+IBusSerializable    *ibus_serializable_deserialize_object
-+                                            (GVariant           *variant);
-+
-+#define ibus_serializable_serialize ibus_serializable_serialize_object
-+#define ibus_serializable_deserialize ibus_serializable_deserialize_object
- 
- G_END_DECLS
- #endif
-diff --git a/src/ibusshare.c b/src/ibusshare.c
-index b793a962..d7724a6b 100644
---- a/src/ibusshare.c
-+++ b/src/ibusshare.c
-@@ -2,7 +2,8 @@
- /* vim:set et sts=4: */
- /* ibus - The Input Bus
-  * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
-- * Copyright (C) 2008-2010 Red Hat, Inc.
-+ * Copyright (C) 2015-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2008-2018 Red Hat, Inc.
-  *
-  * This library is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU Lesser General Public
-@@ -287,6 +288,7 @@ ibus_init (void)
-     IBUS_TYPE_ENGINE_DESC;
-     IBUS_TYPE_OBSERVED_PATH;
-     IBUS_TYPE_REGISTRY;
-+    IBUS_TYPE_X_EVENT;
- }
- 
- static GMainLoop *main_loop = NULL;
-diff --git a/src/ibusshare.h b/src/ibusshare.h
-index f3e2011e..757d915b 100644
---- a/src/ibusshare.h
-+++ b/src/ibusshare.h
-@@ -2,7 +2,8 @@
- /* vim:set et sts=4: */
- /* ibus - The Input Bus
-  * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
-- * Copyright (C) 2008-2013 Red Hat, Inc.
-+ * Copyright (C) 2015-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2008-2018 Red Hat, Inc.
-  *
-  * This library is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU Lesser General Public
-@@ -65,6 +66,13 @@
-  */
- #define IBUS_SERVICE_PANEL      "org.freedesktop.IBus.Panel"
- 
-+/**
-+ * IBUS_SERVICE_PANEL_EXTENSION:
-+ *
-+ * Address of IBus panel extension service.
-+ */
-+#define IBUS_SERVICE_PANEL_EXTENSION "org.freedesktop.IBus.Panel.Extension"
-+
- /**
-  * IBUS_SERVICE_CONFIG:
-  *
-@@ -100,6 +108,13 @@
-  */
- #define IBUS_PATH_PANEL         "/org/freedesktop/IBus/Panel"
- 
-+/**
-+ * IBUS_PATH_PANEL_EXTENSION:
-+ *
-+ * D-Bus path for IBus panel.
-+ */
-+#define IBUS_PATH_PANEL_EXTENSION "/org/freedesktop/IBus/Panel/Extension"
-+
- /**
-  * IBUS_PATH_CONFIG:
-  *
-diff --git a/src/ibusxevent.c b/src/ibusxevent.c
-new file mode 100644
-index 00000000..dea80272
---- /dev/null
-+++ b/src/ibusxevent.c
-@@ -0,0 +1,1004 @@
-+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
-+/* vim:set et sts=4: */
-+/* ibus - The Input Bus
-+ * Copyright (C) 2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2018 Red Hat, Inc.
-+ *
-+ * This library is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU Lesser General Public
-+ * License as published by the Free Software Foundation; either
-+ * version 2.1 of the License, or (at your option) any later version.
-+ *
-+ * This library is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-+ * Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public
-+ * License along with this library; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
-+ * USA
-+ */
-+#include "ibusinternal.h"
-+#include "ibusxevent.h"
-+
-+#define IBUS_X_EVENT_VERSION 1
-+#define IBUS_X_EVENT_GET_PRIVATE(o)  \
-+   (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_X_EVENT, IBusXEventPrivate))
-+
-+enum {
-+    PROP_0,
-+    PROP_VERSION,
-+    PROP_EVENT_TYPE,
-+    PROP_WINDOW,
-+    PROP_SEND_EVENT,
-+    PROP_SERIAL,
-+    PROP_TIME,
-+    PROP_STATE,
-+    PROP_KEYVAL,
-+    PROP_LENGTH,
-+    PROP_STRING,
-+    PROP_HARDWARE_KEYCODE,
-+    PROP_GROUP,
-+    PROP_IS_MODIFIER,
-+    PROP_ROOT,
-+    PROP_SUBWINDOW,
-+    PROP_X,
-+    PROP_Y,
-+    PROP_X_ROOT,
-+    PROP_Y_ROOT,
-+    PROP_SAME_SCREEN,
-+    PROP_PURPOSE
-+};
-+
-+
-+struct _IBusXEventPrivate {
-+    guint    version;
-+    guint32  time;
-+    guint    state;
-+    guint    keyval;
-+    gint     length;
-+    gchar   *string;
-+    guint16  hardware_keycode;
-+    guint8   group;
-+    gboolean is_modifier;
-+    guint    root;
-+    guint    subwindow;
-+    gint     x;
-+    gint     y;
-+    gint     x_root;
-+    gint     y_root;
-+    gboolean same_screen;
-+    gchar   *purpose;
-+};
-+
-+/* functions prototype */
-+static void      ibus_x_event_destroy        (IBusXEvent         *event);
-+static void      ibus_x_event_set_property   (IBusXEvent         *event,
-+                                              guint               prop_id,
-+                                              const GValue       *value,
-+                                              GParamSpec         *pspec);
-+static void      ibus_x_event_get_property   (IBusXEvent         *event,
-+                                              guint               prop_id,
-+                                              GValue             *value,
-+                                              GParamSpec         *pspec);
-+static gboolean  ibus_x_event_serialize      (IBusXEvent         *event,
-+                                              GVariantBuilder    *builder);
-+static gint      ibus_x_event_deserialize    (IBusXEvent         *event,
-+                                              GVariant           *variant);
-+static gboolean  ibus_x_event_copy           (IBusXEvent         *dest,
-+                                              const IBusXEvent   *src);
-+
-+G_DEFINE_TYPE (IBusXEvent, ibus_x_event, IBUS_TYPE_SERIALIZABLE)
-+
-+static void
-+ibus_x_event_class_init (IBusXEventClass *class)
-+{
-+    GObjectClass *gobject_class = G_OBJECT_CLASS (class);
-+    IBusObjectClass *object_class = IBUS_OBJECT_CLASS (class);
-+    IBusSerializableClass *serializable_class = IBUS_SERIALIZABLE_CLASS (class);
-+
-+    gobject_class->set_property =
-+            (GObjectSetPropertyFunc) ibus_x_event_set_property;
-+    gobject_class->get_property =
-+            (GObjectGetPropertyFunc) ibus_x_event_get_property;
-+
-+    object_class->destroy = (IBusObjectDestroyFunc) ibus_x_event_destroy;
-+
-+    serializable_class->serialize   =
-+            (IBusSerializableSerializeFunc) ibus_x_event_serialize;
-+    serializable_class->deserialize =
-+            (IBusSerializableDeserializeFunc) ibus_x_event_deserialize;
-+    serializable_class->copy        =
-+            (IBusSerializableCopyFunc) ibus_x_event_copy;
-+
-+    /* install properties */
-+    /**
-+     * IBusXEvent:version:
-+     *
-+     * Version of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_VERSION,
-+                    g_param_spec_uint ("version",
-+                        "version",
-+                        "version",
-+                        0,
-+                        G_MAXUINT32,
-+                        IBUS_X_EVENT_VERSION,
-+                        G_PARAM_READABLE));
-+
-+    /**
-+     * IBusXEvent:event-type:
-+     *
-+     * IBusXEventType of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_EVENT_TYPE,
-+                    g_param_spec_int ("event-type",
-+                        "event type",
-+                        "event type",
-+                        -1,
-+                        G_MAXINT32,
-+                        -1,
-+                        G_PARAM_READWRITE |
-+                        G_PARAM_CONSTRUCT_ONLY));
-+
-+    /**
-+     * IBusXEvent:window:
-+     *
-+     * window of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_WINDOW,
-+                    g_param_spec_uint ("window",
-+                        "window",
-+                        "window",
-+                        0,
-+                        G_MAXUINT32,
-+                        0,
-+                        G_PARAM_READWRITE |
-+                        G_PARAM_CONSTRUCT_ONLY));
-+
-+    /**
-+     * IBusXEvent:send-event:
-+     *
-+     * send_event of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_SEND_EVENT,
-+                    g_param_spec_int ("send-event",
-+                        "send event",
-+                        "send event",
-+                        0,
-+                        G_MAXINT8,
-+                        0,
-+                        G_PARAM_READWRITE |
-+                        G_PARAM_CONSTRUCT_ONLY));
-+
-+    /**
-+     * IBusXEvent:serial:
-+     *
-+     * serial of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_SERIAL,
-+                    g_param_spec_ulong ("serial",
-+                        "serial",
-+                        "serial",
-+                        0,
-+                        G_MAXUINT64,
-+                        0,
-+                        G_PARAM_READWRITE |
-+                        G_PARAM_CONSTRUCT_ONLY));
-+
-+    /**
-+     * IBusXEvent:time:
-+     *
-+     * time of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_TIME,
-+                    g_param_spec_uint ("time",
-+                        "time",
-+                        "time",
-+                        0,
-+                        G_MAXUINT32,
-+                        0,
-+                        G_PARAM_READWRITE |
-+                        G_PARAM_CONSTRUCT_ONLY));
-+
-+    /**
-+     * IBusXEvent:state:
-+     *
-+     * state of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_STATE,
-+                    g_param_spec_uint ("state",
-+                        "state",
-+                        "state",
-+                        0,
-+                        G_MAXUINT32,
-+                        0,
-+                        G_PARAM_READWRITE |
-+                        G_PARAM_CONSTRUCT_ONLY));
-+
-+    /**
-+     * IBusXEvent:keyval:
-+     *
-+     * keyval of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_KEYVAL,
-+                    g_param_spec_uint ("keyval",
-+                        "keyval",
-+                        "keyval",
-+                        0,
-+                        G_MAXUINT32,
-+                        0,
-+                        G_PARAM_READWRITE |
-+                        G_PARAM_CONSTRUCT_ONLY));
-+
-+    /**
-+     * IBusXEvent:length:
-+     *
-+     * keyval of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_LENGTH,
-+                    g_param_spec_int ("length",
-+                        "length",
-+                        "length",
-+                        -1,
-+                        G_MAXINT32,
-+                        0,
-+                        G_PARAM_READWRITE |
-+                        G_PARAM_CONSTRUCT_ONLY));
-+
-+    /**
-+     * IBusXEvent:string:
-+     *
-+     * string of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_STRING,
-+                    g_param_spec_string ("string",
-+                        "string",
-+                        "string",
-+                        "",
-+                        G_PARAM_READWRITE |
-+                        G_PARAM_CONSTRUCT_ONLY));
-+
-+    /**
-+     * IBusXEvent:hardware-keycode:
-+     *
-+     * hardware keycode of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_HARDWARE_KEYCODE,
-+                    g_param_spec_uint ("hardware-keycode",
-+                        "hardware keycode",
-+                        "hardware keycode",
-+                        0,
-+                        G_MAXUINT16,
-+                        0,
-+                        G_PARAM_READWRITE |
-+                        G_PARAM_CONSTRUCT_ONLY));
-+
-+    /**
-+     * IBusXEvent:group:
-+     *
-+     * group of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_GROUP,
-+                    g_param_spec_uint ("group",
-+                        "group",
-+                        "group",
-+                        0,
-+                        G_MAXUINT8,
-+                        0,
-+                        G_PARAM_READWRITE |
-+                        G_PARAM_CONSTRUCT_ONLY));
-+
-+    /**
-+     * IBusXEvent:is-modifier:
-+     *
-+     * is_modifier of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_IS_MODIFIER,
-+                    g_param_spec_boolean ("is-modifier",
-+                        "is modifier",
-+                        "is modifier",
-+                        FALSE,
-+                        G_PARAM_READWRITE |
-+                        G_PARAM_CONSTRUCT_ONLY));
-+
-+    /**
-+     * IBusXEvent:root:
-+     *
-+     * root window of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_ROOT,
-+                    g_param_spec_uint ("root",
-+                        "root",
-+                        "root",
-+                        0,
-+                        G_MAXUINT32,
-+                        0,
-+                        G_PARAM_READWRITE |
-+                        G_PARAM_CONSTRUCT_ONLY));
-+
-+    /**
-+     * IBusXEvent:subwindow:
-+     *
-+     * subwindow of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_SUBWINDOW,
-+                    g_param_spec_uint ("subwindow",
-+                        "subwindow",
-+                        "subwindow",
-+                        0,
-+                        G_MAXUINT32,
-+                        0,
-+                        G_PARAM_READWRITE |
-+                        G_PARAM_CONSTRUCT_ONLY));
-+
-+    /**
-+     * IBusXEvent:x:
-+     *
-+     * x of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_X,
-+                    g_param_spec_int ("x",
-+                        "x",
-+                        "x",
-+                        G_MININT32,
-+                        G_MAXINT32,
-+                        0,
-+                        G_PARAM_READWRITE |
-+                        G_PARAM_CONSTRUCT_ONLY));
-+
-+    /**
-+     * IBusXEvent:y:
-+     *
-+     * x of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_Y,
-+                    g_param_spec_int ("y",
-+                        "y",
-+                        "y",
-+                        G_MININT32,
-+                        G_MAXINT32,
-+                        0,
-+                        G_PARAM_READWRITE |
-+                        G_PARAM_CONSTRUCT_ONLY));
-+
-+    /**
-+     * IBusXEvent:x-root:
-+     *
-+     * root-x of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_X_ROOT,
-+                    g_param_spec_int ("x-root",
-+                        "x root",
-+                        "x root",
-+                        G_MININT32,
-+                        G_MAXINT32,
-+                        0,
-+                        G_PARAM_READWRITE |
-+                        G_PARAM_CONSTRUCT_ONLY));
-+
-+    /**
-+     * IBusXEvent:y-root:
-+     *
-+     * root-y of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_Y_ROOT,
-+                    g_param_spec_int ("y-root",
-+                        "y root",
-+                        "y root",
-+                        G_MININT32,
-+                        G_MAXINT32,
-+                        0,
-+                        G_PARAM_READWRITE |
-+                        G_PARAM_CONSTRUCT_ONLY));
-+
-+    /**
-+     * IBusXEvent:same-screen:
-+     *
-+     * same_screen of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_SAME_SCREEN,
-+                    g_param_spec_boolean ("same-screen",
-+                        "same screen",
-+                        "same screen",
-+                        TRUE,
-+                        G_PARAM_READWRITE |
-+                        G_PARAM_CONSTRUCT_ONLY));
-+
-+    /**
-+     * IBusXEvent:purpose:
-+     *
-+     * purpose of this IBusXEvent.
-+     */
-+    g_object_class_install_property (gobject_class,
-+                    PROP_PURPOSE,
-+                    g_param_spec_string ("purpose",
-+                        "purpose",
-+                        "purpose",
-+                        "",
-+                        G_PARAM_READWRITE |
-+                        G_PARAM_CONSTRUCT_ONLY));
-+
-+    g_type_class_add_private (class, sizeof (IBusXEventPrivate));
-+}
-+
-+static void
-+ibus_x_event_init (IBusXEvent *event)
-+{
-+    event->priv = IBUS_X_EVENT_GET_PRIVATE (event);
-+    event->priv->version = IBUS_X_EVENT_VERSION;
-+}
-+
-+static void
-+ibus_x_event_destroy (IBusXEvent *event)
-+{
-+    g_clear_pointer (&event->priv->string, g_free);
-+
-+    IBUS_OBJECT_CLASS(ibus_x_event_parent_class)->destroy (IBUS_OBJECT (event));
-+}
-+
-+static void
-+ibus_x_event_set_property (IBusXEvent   *event,
-+                           guint         prop_id,
-+                           const GValue *value,
-+                           GParamSpec   *pspec)
-+{
-+    IBusXEventPrivate *priv = event->priv;
-+
-+    switch (prop_id) {
-+    case PROP_EVENT_TYPE:
-+        event->event_type = g_value_get_int (value);
-+        break;
-+    case PROP_WINDOW:
-+        event->window = g_value_get_uint (value);
-+        break;
-+    case PROP_SEND_EVENT:
-+        event->send_event = g_value_get_int (value);
-+        break;
-+    case PROP_SERIAL:
-+        event->serial = g_value_get_ulong (value);
-+        break;
-+    case PROP_TIME:
-+        priv->time = g_value_get_uint (value);
-+        break;
-+    case PROP_STATE:
-+        priv->state = g_value_get_uint (value);
-+        break;
-+    case PROP_KEYVAL:
-+        priv->keyval = g_value_get_uint (value);
-+        break;
-+    case PROP_LENGTH:
-+        priv->length = g_value_get_int (value);
-+        break;
-+    case PROP_STRING:
-+        g_free (priv->string);
-+        priv->string = g_value_dup_string (value);
-+        break;
-+    case PROP_HARDWARE_KEYCODE:
-+        priv->hardware_keycode = g_value_get_uint (value);
-+        break;
-+    case PROP_GROUP:
-+        priv->group = g_value_get_uint (value);
-+        break;
-+    case PROP_IS_MODIFIER:
-+        priv->is_modifier = g_value_get_boolean (value);
-+        break;
-+    case PROP_ROOT:
-+        priv->root = g_value_get_uint (value);
-+        break;
-+    case PROP_SUBWINDOW:
-+        priv->subwindow = g_value_get_uint (value);
-+        break;
-+    case PROP_X:
-+        priv->x = g_value_get_int (value);
-+        break;
-+    case PROP_Y:
-+        priv->y = g_value_get_int (value);
-+        break;
-+    case PROP_X_ROOT:
-+        priv->x_root = g_value_get_int (value);
-+        break;
-+    case PROP_Y_ROOT:
-+        priv->y_root = g_value_get_int (value);
-+        break;
-+    case PROP_SAME_SCREEN:
-+        priv->same_screen = g_value_get_boolean (value);
-+        break;
-+    case PROP_PURPOSE:
-+        g_free (priv->purpose);
-+        priv->purpose = g_value_dup_string (value);
-+        break;
-+    default:
-+        G_OBJECT_WARN_INVALID_PROPERTY_ID (event, prop_id, pspec);
-+    }
-+}
-+
-+static void
-+ibus_x_event_get_property (IBusXEvent *event,
-+                          guint        prop_id,
-+                          GValue      *value,
-+                          GParamSpec  *pspec)
-+{
-+    IBusXEventPrivate *priv = event->priv;
-+    switch (prop_id) {
-+    case PROP_VERSION:
-+        g_value_set_uint (value, priv->version);
-+        break;
-+    case PROP_EVENT_TYPE:
-+        g_value_set_int (value, event->event_type);
-+        break;
-+    case PROP_WINDOW:
-+        g_value_set_uint (value, event->window);
-+        break;
-+    case PROP_SEND_EVENT:
-+        g_value_set_int (value, event->send_event);
-+        break;
-+    case PROP_SERIAL:
-+        g_value_set_ulong (value, event->serial);
-+        break;
-+    case PROP_TIME:
-+        g_value_set_uint (value, priv->time);
-+        break;
-+    case PROP_STATE:
-+        g_value_set_uint (value, priv->state);
-+        break;
-+    case PROP_KEYVAL:
-+        g_value_set_uint (value, priv->keyval);
-+        break;
-+    case PROP_LENGTH:
-+        g_value_set_int (value, priv->length);
-+        break;
-+    case PROP_STRING:
-+        g_value_set_string (value, priv->string);
-+        break;
-+    case PROP_HARDWARE_KEYCODE:
-+        g_value_set_uint (value, priv->hardware_keycode);
-+        break;
-+    case PROP_GROUP:
-+        g_value_set_uint (value, priv->group);
-+        break;
-+    case PROP_IS_MODIFIER:
-+        g_value_set_boolean (value, priv->is_modifier);
-+        break;
-+    case PROP_ROOT:
-+        g_value_set_uint (value, priv->root);
-+        break;
-+    case PROP_SUBWINDOW:
-+        g_value_set_uint (value, priv->subwindow);
-+        break;
-+    case PROP_X:
-+        g_value_set_int (value, priv->x);
-+        break;
-+    case PROP_Y:
-+        g_value_set_int (value, priv->y);
-+        break;
-+    case PROP_X_ROOT:
-+        g_value_set_int (value, priv->x_root);
-+        break;
-+    case PROP_Y_ROOT:
-+        g_value_set_int (value, priv->y_root);
-+        break;
-+    case PROP_SAME_SCREEN:
-+        g_value_set_boolean (value, priv->same_screen);
-+        break;
-+    case PROP_PURPOSE:
-+        g_value_set_string (value, priv->purpose);
-+        break;
-+    default:
-+        G_OBJECT_WARN_INVALID_PROPERTY_ID (event, prop_id, pspec);
-+    }
-+}
-+
-+static gboolean
-+ibus_x_event_serialize (IBusXEvent      *event,
-+                        GVariantBuilder *builder)
-+{
-+    gboolean retval;
-+    IBusXEventPrivate *priv;
-+
-+    retval = IBUS_SERIALIZABLE_CLASS (ibus_x_event_parent_class)->
-+            serialize ((IBusSerializable *)event, builder);
-+    g_return_val_if_fail (retval, FALSE);
-+    /* End dict iter */
-+
-+    priv = event->priv;
-+#define NOTNULL(s) ((s) != NULL ? (s) : "")
-+    /* If you will add a new property, you can append it at the end and
-+     * you should not change the serialized order of name, longname,
-+     * description, ... because the order is also used in other applications
-+     * likes ibus-qt. */
-+    g_variant_builder_add (builder, "u", priv->version);
-+    g_variant_builder_add (builder, "u", event->event_type);
-+    g_variant_builder_add (builder, "u", event->window);
-+    g_variant_builder_add (builder, "i", event->send_event);
-+    g_variant_builder_add (builder, "t", event->serial);
-+    g_variant_builder_add (builder, "u", priv->time);
-+    g_variant_builder_add (builder, "u", priv->state);
-+    g_variant_builder_add (builder, "u", priv->keyval);
-+    g_variant_builder_add (builder, "i", priv->length);
-+    g_variant_builder_add (builder, "s", NOTNULL (priv->string));
-+    g_variant_builder_add (builder, "u", priv->hardware_keycode);
-+    g_variant_builder_add (builder, "u", priv->group);
-+    g_variant_builder_add (builder, "b", priv->is_modifier);
-+    g_variant_builder_add (builder, "u", priv->root);
-+    g_variant_builder_add (builder, "u", priv->subwindow);
-+    g_variant_builder_add (builder, "i", priv->x);
-+    g_variant_builder_add (builder, "i", priv->y);
-+    g_variant_builder_add (builder, "i", priv->x_root);
-+    g_variant_builder_add (builder, "i", priv->y_root);
-+    g_variant_builder_add (builder, "b", priv->same_screen);
-+    g_variant_builder_add (builder, "s", NOTNULL (priv->purpose));
-+#undef NOTNULL
-+
-+    return TRUE;
-+}
-+
-+static gint
-+ibus_x_event_deserialize (IBusXEvent *event,
-+                          GVariant   *variant)
-+{
-+    gint retval;
-+    IBusXEventPrivate *priv;
-+
-+    retval = IBUS_SERIALIZABLE_CLASS (ibus_x_event_parent_class)->
-+            deserialize ((IBusSerializable *)event, variant);
-+    g_return_val_if_fail (retval, 0);
-+
-+    priv = event->priv;
-+    /* If you will add a new property, you can append it at the end and
-+     * you should not change the serialized order of name, longname,
-+     * description, ... because the order is also used in other applications
-+     * likes ibus-qt. */
-+    g_variant_get_child (variant, retval++, "u", &priv->version);
-+    g_variant_get_child (variant, retval++, "u", &event->event_type);
-+    g_variant_get_child (variant, retval++, "u", &event->window);
-+    g_variant_get_child (variant, retval++, "i", &event->send_event);
-+    g_variant_get_child (variant, retval++, "t", &event->serial);
-+    g_variant_get_child (variant, retval++, "u", &priv->time);
-+    g_variant_get_child (variant, retval++, "u", &priv->state);
-+    g_variant_get_child (variant, retval++, "u", &priv->keyval);
-+    g_variant_get_child (variant, retval++, "i", &priv->length);
-+    ibus_g_variant_get_child_string (variant, retval++,
-+                                     &priv->string);
-+    g_variant_get_child (variant, retval++, "u", &priv->hardware_keycode);
-+    g_variant_get_child (variant, retval++, "u", &priv->group);
-+    g_variant_get_child (variant, retval++, "b", &priv->is_modifier);
-+    g_variant_get_child (variant, retval++, "u", &priv->root);
-+    g_variant_get_child (variant, retval++, "u", &priv->subwindow);
-+    g_variant_get_child (variant, retval++, "i", &priv->x);
-+    g_variant_get_child (variant, retval++, "i", &priv->y);
-+    g_variant_get_child (variant, retval++, "i", &priv->x_root);
-+    g_variant_get_child (variant, retval++, "i", &priv->y_root);
-+    g_variant_get_child (variant, retval++, "b", &priv->same_screen);
-+    ibus_g_variant_get_child_string (variant, retval++,
-+                                     &priv->purpose);
-+
-+    return retval;
-+}
-+
-+static gboolean
-+ibus_x_event_copy (IBusXEvent       *dest,
-+                   const IBusXEvent *src)
-+{
-+    gboolean retval;
-+    IBusXEventPrivate *dest_priv = dest->priv;
-+    IBusXEventPrivate *src_priv = src->priv;
-+
-+    retval = IBUS_SERIALIZABLE_CLASS (ibus_x_event_parent_class)->
-+            copy ((IBusSerializable *)dest, (IBusSerializable *)src);
-+    g_return_val_if_fail (retval, FALSE);
-+
-+    dest_priv->version           = src_priv->version;
-+    dest->event_type             = src->event_type;
-+    dest->window                 = src->window;
-+    dest->send_event             = src->send_event;
-+    dest->serial                 = src->serial;
-+    dest_priv->time              = src_priv->time;
-+    dest_priv->state             = src_priv->state;
-+    dest_priv->keyval            = src_priv->keyval;
-+    dest_priv->length            = src_priv->length;
-+    dest_priv->string            = g_strdup (src_priv->string);
-+    dest_priv->hardware_keycode  = src_priv->hardware_keycode;
-+    dest_priv->group             = src_priv->group;
-+    dest_priv->is_modifier       = src_priv->is_modifier;
-+    dest_priv->root              = src_priv->root;
-+    dest_priv->subwindow         = src_priv->subwindow;
-+    dest_priv->x                 = src_priv->x;
-+    dest_priv->y                 = src_priv->y;
-+    dest_priv->x_root            = src_priv->x_root;
-+    dest_priv->y_root            = src_priv->y_root;
-+    dest_priv->same_screen       = src_priv->same_screen;
-+    dest_priv->purpose           = g_strdup (src_priv->purpose);
-+
-+    return TRUE;
-+}
-+
-+IBusXEvent *
-+ibus_x_event_new (const gchar   *first_property_name,
-+                  ...)
-+{
-+    va_list var_args;
-+    IBusXEvent *event;
-+
-+    va_start (var_args, first_property_name);
-+    event = (IBusXEvent *) g_object_new_valist (IBUS_TYPE_X_EVENT,
-+                                                first_property_name,
-+                                                var_args);
-+    va_end (var_args);
-+    g_assert (event->priv->version != 0);
-+    g_assert (event->event_type != IBUS_X_EVENT_NOTHING);
-+    return event;
-+}
-+
-+guint
-+ibus_x_event_get_version (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0);
-+    return event->priv->version;
-+}
-+
-+IBusXEventType
-+ibus_x_event_get_event_type (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0);
-+    return event->event_type;
-+}
-+
-+guint32
-+ibus_x_event_get_window (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0);
-+    return event->window;
-+}
-+
-+gint8
-+ibus_x_event_get_send_event (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), -1);
-+    return event->send_event;
-+}
-+
-+gulong
-+ibus_x_event_get_serial (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0);
-+    return event->serial;
-+}
-+
-+guint32
-+ibus_x_event_get_time (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0);
-+    switch (event->event_type) {
-+    case IBUS_X_EVENT_KEY_PRESS:
-+    case IBUS_X_EVENT_KEY_RELEASE:
-+        break;
-+    default:
-+        g_return_val_if_reached (0);
-+    }
-+    return event->priv->time;
-+}
-+
-+guint
-+ibus_x_event_get_state (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0);
-+    switch (event->event_type) {
-+    case IBUS_X_EVENT_KEY_PRESS:
-+    case IBUS_X_EVENT_KEY_RELEASE:
-+        break;
-+    default:
-+        g_return_val_if_reached (0);
-+    }
-+    return event->priv->state;
-+}
-+
-+guint
-+ibus_x_event_get_keyval (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0);
-+    switch (event->event_type) {
-+    case IBUS_X_EVENT_KEY_PRESS:
-+    case IBUS_X_EVENT_KEY_RELEASE:
-+        break;
-+    default:
-+        g_return_val_if_reached (0);
-+    }
-+    return event->priv->keyval;
-+}
-+
-+gint
-+ibus_x_event_get_length (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), -1);
-+    switch (event->event_type) {
-+    case IBUS_X_EVENT_KEY_PRESS:
-+    case IBUS_X_EVENT_KEY_RELEASE:
-+        break;
-+    default:
-+        g_return_val_if_reached (-1);
-+    }
-+    return event->priv->length;
-+}
-+
-+const gchar *
-+ibus_x_event_get_string (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), "");
-+    switch (event->event_type) {
-+    case IBUS_X_EVENT_KEY_PRESS:
-+    case IBUS_X_EVENT_KEY_RELEASE:
-+        break;
-+    default:
-+        g_return_val_if_reached ("");
-+    }
-+    return event->priv->string;
-+}
-+
-+guint16
-+ibus_x_event_get_hardware_keycode (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0);
-+    switch (event->event_type) {
-+    case IBUS_X_EVENT_KEY_PRESS:
-+    case IBUS_X_EVENT_KEY_RELEASE:
-+        break;
-+    default:
-+        g_return_val_if_reached (0);
-+    }
-+    return event->priv->hardware_keycode;
-+}
-+
-+guint8
-+ibus_x_event_get_group (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0);
-+    switch (event->event_type) {
-+    case IBUS_X_EVENT_KEY_PRESS:
-+    case IBUS_X_EVENT_KEY_RELEASE:
-+        break;
-+    default:
-+        g_return_val_if_reached (0);
-+    }
-+    return event->priv->group;
-+}
-+
-+gboolean
-+ibus_x_event_get_is_modifier (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0);
-+    switch (event->event_type) {
-+    case IBUS_X_EVENT_KEY_PRESS:
-+    case IBUS_X_EVENT_KEY_RELEASE:
-+        break;
-+    default:
-+        g_return_val_if_reached (0);
-+    }
-+    return event->priv->is_modifier;
-+}
-+
-+guint32
-+ibus_x_event_get_root (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0);
-+    switch (event->event_type) {
-+    case IBUS_X_EVENT_KEY_PRESS:
-+    case IBUS_X_EVENT_KEY_RELEASE:
-+        break;
-+    default:
-+        g_return_val_if_reached (0);
-+    }
-+    return event->priv->root;
-+}
-+
-+guint32
-+ibus_x_event_get_subwindow (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0);
-+    switch (event->event_type) {
-+    case IBUS_X_EVENT_KEY_PRESS:
-+    case IBUS_X_EVENT_KEY_RELEASE:
-+        break;
-+    default:
-+        g_return_val_if_reached (0);
-+    }
-+    return event->priv->subwindow;
-+}
-+
-+gint
-+ibus_x_event_get_x (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0);
-+    switch (event->event_type) {
-+    case IBUS_X_EVENT_KEY_PRESS:
-+    case IBUS_X_EVENT_KEY_RELEASE:
-+        break;
-+    default:
-+        g_return_val_if_reached (0);
-+    }
-+    return event->priv->x;
-+}
-+
-+gint
-+ibus_x_event_get_y (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0);
-+    switch (event->event_type) {
-+    case IBUS_X_EVENT_KEY_PRESS:
-+    case IBUS_X_EVENT_KEY_RELEASE:
-+        break;
-+    default:
-+        g_return_val_if_reached (0);
-+    }
-+    return event->priv->y;
-+}
-+
-+gint
-+ibus_x_event_get_x_root (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0);
-+    switch (event->event_type) {
-+    case IBUS_X_EVENT_KEY_PRESS:
-+    case IBUS_X_EVENT_KEY_RELEASE:
-+        break;
-+    default:
-+        g_return_val_if_reached (0);
-+    }
-+    return event->priv->x_root;
-+}
-+
-+gint
-+ibus_x_event_get_y_root (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), 0);
-+    switch (event->event_type) {
-+    case IBUS_X_EVENT_KEY_PRESS:
-+    case IBUS_X_EVENT_KEY_RELEASE:
-+        break;
-+    default:
-+        g_return_val_if_reached (0);
-+    }
-+    return event->priv->y_root;
-+}
-+
-+gboolean
-+ibus_x_event_get_same_screen (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), TRUE);
-+    switch (event->event_type) {
-+    case IBUS_X_EVENT_KEY_PRESS:
-+    case IBUS_X_EVENT_KEY_RELEASE:
-+        break;
-+    default:
-+        g_return_val_if_reached (TRUE);
-+    }
-+    return event->priv->same_screen;
-+}
-+
-+const gchar *
-+ibus_x_event_get_purpose (IBusXEvent *event)
-+{
-+    g_return_val_if_fail (IBUS_IS_X_EVENT (event), "");
-+    return event->priv->purpose;
-+}
-diff --git a/src/ibusxevent.h b/src/ibusxevent.h
-new file mode 100644
-index 00000000..f35f14e4
---- /dev/null
-+++ b/src/ibusxevent.h
-@@ -0,0 +1,294 @@
-+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
-+/* vim:set et sts=4: */
-+/* ibus - The Input Bus
-+ * Copyright (C) 2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2018 Red Hat, Inc.
-+ *
-+ * This library is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU Lesser General Public
-+ * License as published by the Free Software Foundation; either
-+ * version 2.1 of the License, or (at your option) any later version.
-+ *
-+ * This library is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-+ * Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public
-+ * License along with this library; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
-+ * USA
-+ */
-+
-+#if !defined (__IBUS_H_INSIDE__) && !defined (IBUS_COMPILATION)
-+#error "Only <ibus.h> can be included directly"
-+#endif
-+
-+#ifndef __IBUS_X_EVENT_H_
-+#define __IBUS_X_EVENT_H_
-+
-+/**
-+ * SECTION: ibusxevent
-+ * @short_description: XEvent wrapper object
-+ * @title: IBusXEvent
-+ * @stability: Unstable
-+ *
-+ * An IBusXEvent provides a wrapper of XEvent.
-+ *
-+ * see_also: #IBusComponent, #IBusEngineDesc
-+ */
-+
-+#include "ibusserializable.h"
-+
-+/*
-+ * Type macros.
-+ */
-+
-+/* define GOBJECT macros */
-+#define IBUS_TYPE_X_EVENT            \
-+    (ibus_x_event_get_type ())
-+#define IBUS_X_EVENT(obj)            \
-+    (G_TYPE_CHECK_INSTANCE_CAST ((obj), IBUS_TYPE_X_EVENT, IBusXEvent))
-+#define IBUS_X_EVENT_CLASS(klass)    \
-+    (G_TYPE_CHECK_CLASS_CAST ((klass), IBUS_TYPE_X_EVENT, IBusXEventClass))
-+#define IBUS_IS_X_EVENT(obj)         \
-+    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IBUS_TYPE_X_EVENT))
-+#define IBUS_IS_X_EVENT_CLASS(klass) \
-+    (G_TYPE_CHECK_CLASS_TYPE ((klass), IBUS_TYPE_X_EVENT))
-+#define IBUS_X_EVENT_GET_CLASS(obj)  \
-+    (G_TYPE_INSTANCE_GET_CLASS ((obj), IBUS_TYPE_X_EVENT, IBusXEventClass))
-+
-+G_BEGIN_DECLS
-+
-+typedef struct _IBusXEvent IBusXEvent;
-+typedef struct _IBusXEventClass IBusXEventClass;
-+typedef struct _IBusXEventPrivate IBusXEventPrivate;
-+
-+typedef enum {
-+    IBUS_X_EVENT_NOTHING           = -1,
-+    IBUS_X_EVENT_KEY_PRESS         = 0,
-+    IBUS_X_EVENT_KEY_RELEASE       = 1,
-+    IBUS_X_EVENT_OTHER             = 2,
-+    IBUS_X_EVENT_EVENT_LAST        /* helper variable for decls */
-+} IBusXEventType;
-+
-+/**
-+ * IBusXEvent:
-+ * @type: event type
-+ *
-+ * IBusEngine properties.
-+ */
-+struct _IBusXEvent {
-+    /*< private >*/
-+    IBusSerializable parent;
-+    IBusXEventPrivate *priv;
-+
-+    /* instance members */
-+    /*< public >*/
-+    IBusXEventType event_type;
-+    guint          window;
-+    gint8          send_event;
-+    gulong         serial;
-+};
-+
-+struct _IBusXEventClass {
-+    /*< private >*/
-+    IBusSerializableClass parent;
-+
-+    /* class members */
-+    /*< public >*/
-+
-+    /*< private >*/
-+    /* padding */
-+    gpointer pdummy[10];
-+};
-+
-+GType        ibus_x_event_get_type       (void);
-+
-+/**
-+ * ibus_x_event_new:
-+ * @first_property_name: Name of the first property.
-+ * @...: the NULL-terminated arguments of the properties and values.
-+ *
-+ * Create a new #IBusXEvent.
-+ *
-+ * Returns: A newly allocated #IBusXEvent. E.g.
-+ * ibus_x_event_new ("event-type", IBUS_X_EVENT_KEY_PRESS, NULL);
-+ */
-+IBusXEvent *   ibus_x_event_new            (const gchar
-+                                                           *first_property_name,
-+                                            ...);
-+
-+/**
-+ * ibus_x_event_get_version:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: Version of #IBusXEvent
-+ */
-+guint          ibus_x_event_get_version    (IBusXEvent         *event);
-+
-+/**
-+ * ibus_x_event_get_event_type:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: IBusXEventType of #IBusXEvent
-+ */
-+IBusXEventType ibus_x_event_get_event_type (IBusXEvent         *event);
-+
-+/**
-+ * ibus_x_event_get_window:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: XID of #IBusXEvent
-+ */
-+guint32        ibus_x_event_get_window     (IBusXEvent         *event);
-+
-+/**
-+ * ibus_x_event_get_send_event:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: send_event of #IBusXEvent
-+ */
-+gint8          ibus_x_event_get_send_event (IBusXEvent         *event);
-+
-+/**
-+ * ibus_x_event_get_serial:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: serial of #IBusXEvent
-+ */
-+gulong         ibus_x_event_get_serial     (IBusXEvent         *event);
-+
-+/**
-+ * ibus_x_event_get_time:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: time of #IBusXEvent
-+ */
-+guint32        ibus_x_event_get_time       (IBusXEvent         *event);
-+
-+/**
-+ * ibus_x_event_get_state:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: state of #IBusXEvent
-+ */
-+guint          ibus_x_event_get_state      (IBusXEvent         *event);
-+
-+/**
-+ * ibus_x_event_get_keyval:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: keyval of #IBusXEvent
-+ */
-+guint          ibus_x_event_get_keyval     (IBusXEvent         *event);
-+
-+/**
-+ * ibus_x_event_get_length:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: length of #IBusXEvent
-+ */
-+gint           ibus_x_event_get_length     (IBusXEvent         *event);
-+
-+/**
-+ * ibus_x_event_get_string:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: string of #IBusXEvent
-+ */
-+const gchar *  ibus_x_event_get_string     (IBusXEvent         *event);
-+
-+/**
-+ * ibus_x_event_get_hardware_keycode:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: hardware keycode of #IBusXEvent
-+ */
-+guint16        ibus_x_event_get_hardware_keycode
-+                                           (IBusXEvent         *event);
-+
-+/**
-+ * ibus_x_event_get_group:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: group of #IBusXEvent
-+ */
-+guint8         ibus_x_event_get_group      (IBusXEvent         *event);
-+
-+/**
-+ * ibus_x_event_get_is_modifier:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: is_modifier of #IBusXEvent
-+ */
-+gboolean       ibus_x_event_get_is_modifier
-+                                           (IBusXEvent         *event);
-+
-+/**
-+ * ibus_x_event_get_subwindow:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: subwindow of #IBusXEvent
-+ */
-+guint32        ibus_x_event_get_subwindow  (IBusXEvent         *event);
-+
-+/**
-+ * ibus_x_event_get_root:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: root window of #IBusXEvent
-+ */
-+guint32        ibus_x_event_get_root       (IBusXEvent         *event);
-+
-+/**
-+ * ibus_x_event_get_x:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: x of #IBusXEvent
-+ */
-+gint           ibus_x_event_get_x          (IBusXEvent         *event);
-+
-+/**
-+ * ibus_x_event_get_y:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: y of #IBusXEvent
-+ */
-+gint           ibus_x_event_get_y          (IBusXEvent         *event);
-+
-+/**
-+ * ibus_x_event_get_x_root:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: x-root of #IBusXEvent
-+ */
-+gint           ibus_x_event_get_x_root     (IBusXEvent         *event);
-+
-+/**
-+ * ibus_x_event_get_y_root:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: y-root of #IBusXEvent
-+ */
-+gint           ibus_x_event_get_y_root     (IBusXEvent         *event);
-+
-+/**
-+ * ibus_x_event_get_same_screen:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: same_screen of #IBusXEvent
-+ */
-+gboolean       ibus_x_event_get_same_screen
-+                                           (IBusXEvent         *event);
-+
-+/**
-+ * ibus_x_event_get_purpose:
-+ * @event: An #IBusXEvent.
-+ *
-+ * Returns: purpose of #IBusXEvent
-+ */
-+const gchar *  ibus_x_event_get_purpose    (IBusXEvent         *event);
-+
-+G_END_DECLS
-+#endif
-diff --git a/src/tests/runtest b/src/tests/runtest
-index 91c4e95f..0e43fee5 100755
---- a/src/tests/runtest
-+++ b/src/tests/runtest
-@@ -106,6 +106,7 @@ test -d $tstdir || mkdir $tstdir
-     --daemonize \
-     --cache=none \
-     --panel=disable \
-+    --panel-extension=disable \
-     --config=default \
-     --verbose;
- 
-diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
-index 786b80e6..0a8f4200 100644
---- a/ui/gtk3/Makefile.am
-+++ b/ui/gtk3/Makefile.am
-@@ -3,8 +3,8 @@
- # ibus - The Input Bus
- #
- # Copyright (c) 2007-2015 Peng Huang <shawn.p.huang@gmail.com>
--# Copyright (c) 2015-2017 Takao Fujwiara <takao.fujiwara1@gmail.com>
--# Copyright (c) 2007-2017 Red Hat, Inc.
-+# Copyright (c) 2015-2018 Takao Fujwiara <takao.fujiwara1@gmail.com>
-+# Copyright (c) 2007-2018 Red Hat, Inc.
- #
- # This library is free software; you can redistribute it and/or
- # modify it under the terms of the GNU Lesser General Public
-@@ -30,7 +30,7 @@ component_DATA = \
- 	$(NULL)
- componentdir = $(pkgdatadir)/component
- 
--gtkpanel.xml: gtkpanel.xml.in
-+%.xml: %.xml.in
- 	$(AM_V_GEN) sed \
- 		-e 's|@VERSION[@]|$(VERSION)|g' \
- 		-e 's|@libexecdir[@]|$(libexecdir)|g' $< > $@.tmp && \
-@@ -108,6 +108,7 @@ libexec_PROGRAMS = ibus-ui-gtk3
- 
- ibus_ui_gtk3_SOURCES = \
- 	application.vala \
-+	bindingcommon.vala \
- 	candidatearea.vala \
- 	candidatepanel.vala \
- 	emojier.vala \
-@@ -155,9 +156,12 @@ EXTRA_DIST =                            \
-     $(emoji_headers)                    \
-     $(man_seven_in_files)               \
-     emojierapp.vala                     \
-+    extension.vala                      \
-+    gtkextension.xml.in                 \
-     gtkpanel.xml.in                     \
-     notification-item.xml               \
-     notification-watcher.xml            \
-+    panelbinding.vala                   \
-     $(NULL)
- 
- if ENABLE_EMOJI_DICT
-@@ -167,10 +171,8 @@ libexec_PROGRAMS += ibus-ui-emojier
- 
- ibus_ui_emojier_VALASOURCES =                   \
-     emojierapp.vala                             \
--    candidatearea.vala                          \
-     emojier.vala                                \
-     iconwidget.vala                             \
--    pango.vala                                  \
-     separator.vala                              \
-     $(NULL)
- ibus_ui_emojier_SOURCES =                       \
-@@ -198,6 +200,53 @@ emojierapp.o: $(srcdir)/emojierapp.c
- 	$(AM_V_CC_no)$(COMPILE) -c -o $@ $<
- 	$(NULL)
- 
-+component_DATA += gtkextension.xml
-+CLEANFILES += gtkextension.xml
-+libexec_PROGRAMS += ibus-extension-gtk3
-+
-+ibus_extension_gtk3_VALASOURCES =               \
-+    bindingcommon.vala                          \
-+    emojier.vala                                \
-+    extension.vala                              \
-+    iconwidget.vala                             \
-+    keybindingmanager.vala                      \
-+    panelbinding.vala                           \
-+    $(NULL)
-+ibus_extension_gtk3_SOURCES =                   \
-+    $(ibus_extension_gtk3_VALASOURCES:.vala=.c) \
-+    $(NULL)
-+
-+ibus_extension_gtk3_LDADD =                     \
-+    $(AM_LDADD)                                 \
-+    $(NULL)
-+ibus_extension_gtk3_VALAFLAGS =                 \
-+    $(AM_VALAFLAGS)                             \
-+    $(NULL)
-+
-+# This line and foo_VALASOURCES line can delete the duplicated entries
-+# of emojier.c: emojier.vala
-+extension.c: $(ibus_extension_gtk3_VALASOURCES)
-+	$(AM_V_VALAC)$(am__cd) $(srcdir) && $(VALAC) $(AM_VALAFLAGS) \
-+$(VALAFLAGS) -C $(ibus_extension_gtk3_VALASOURCES)
-+	$(NULL)
-+# make dist creates .c files in a different srcdir
-+extension.o: $(srcdir)/extension.c
-+	$(AM_V_CC)source='$<' object='$@' libtool=no \
-+	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \
-+	$(AM_V_CC_no)$(COMPILE) -c -o $@ $<
-+	$(NULL)
-+# of emojier.c: emojier.vala
-+panelbinding.c: $(ibus_extension_gtk3_VALASOURCES)
-+	$(AM_V_VALAC)$(am__cd) $(srcdir) && $(VALAC) $(AM_VALAFLAGS) \
-+$(VALAFLAGS) -C $(ibus_extension_gtk3_VALASOURCES)
-+	$(NULL)
-+# make dist creates .c files in a different srcdir
-+panelbinding.o: $(srcdir)/panelbinding.c
-+	$(AM_V_CC)source='$<' object='$@' libtool=no \
-+	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \
-+	$(AM_V_CC_no)$(COMPILE) -c -o $@ $<
-+	$(NULL)
-+
- man_seven_files = $(man_seven_in_files:.7.in=.7)
- man_seven_DATA =$(man_seven_files:.7=.7.gz)
- man_sevendir = $(mandir)/man7
-diff --git a/ui/gtk3/bindingcommon.vala b/ui/gtk3/bindingcommon.vala
-new file mode 100644
-index 00000000..4171f29d
---- /dev/null
-+++ b/ui/gtk3/bindingcommon.vala
-@@ -0,0 +1,215 @@
-+/* vim:set et sts=4 sw=4:
-+ *
-+ * ibus - The Input Bus
-+ *
-+ * Copyright(c) 2018 Peng Huang <shawn.p.huang@gmail.com>
-+ * Copyright(c) 2018 Takao Fujwiara <takao.fujiwara1@gmail.com>
-+ *
-+ * This library is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU Lesser General Public
-+ * License as published by the Free Software Foundation; either
-+ * version 2.1 of the License, or (at your option) any later version.
-+ *
-+ * This library is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-+ * Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public
-+ * License along with this library; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
-+ * USA
-+ */
-+
-+/* This file depends on keybindingmanager.vala */
-+
-+class BindingCommon {
-+    public enum KeyEventFuncType {
-+        ANY,
-+        IME_SWITCHER,
-+        EMOJI_TYPING,
-+    }
-+
-+    public class Keybinding : GLib.Object {
-+        public Keybinding(uint             keysym,
-+                          Gdk.ModifierType modifiers,
-+                          bool             reverse,
-+                          KeyEventFuncType ftype) {
-+            this.keysym = keysym;
-+            this.modifiers = modifiers;
-+            this.reverse = reverse;
-+            this.ftype = ftype;
-+        }
-+
-+        public uint keysym { get; set; }
-+        public Gdk.ModifierType modifiers { get; set; }
-+        public bool reverse { get; set; }
-+        public KeyEventFuncType ftype { get; set; }
-+    }
-+
-+    public delegate void KeybindingFuncHandlerFunc(Gdk.Event event);
-+
-+    public static void
-+    keybinding_manager_bind(KeybindingManager           keybinding_manager,
-+                            ref GLib.List<Keybinding>   keybindings,
-+                            string?                     accelerator,
-+                            KeyEventFuncType            ftype,
-+                            KeybindingManager.KeybindingHandlerFunc
-+                                                        handler_normal,
-+                            KeybindingManager.KeybindingHandlerFunc?
-+                                                        handler_reverse) {
-+        uint switch_keysym = 0;
-+        Gdk.ModifierType switch_modifiers = 0;
-+        Gdk.ModifierType reverse_modifier = Gdk.ModifierType.SHIFT_MASK;
-+        Keybinding keybinding;
-+
-+        Gtk.accelerator_parse(accelerator,
-+                out switch_keysym, out switch_modifiers);
-+
-+        // Map virtual modifiers to (i.e. Mod2, Mod3, ...)
-+        const Gdk.ModifierType VIRTUAL_MODIFIERS = (
-+                Gdk.ModifierType.SUPER_MASK |
-+                Gdk.ModifierType.HYPER_MASK |
-+                Gdk.ModifierType.META_MASK);
-+        if ((switch_modifiers & VIRTUAL_MODIFIERS) != 0) {
-+        // workaround a bug in gdk vapi vala > 0.18
-+        // https://bugzilla.gnome.org/show_bug.cgi?id=677559
-+#if VALA_0_18
-+            Gdk.Keymap.get_default().map_virtual_modifiers(
-+                    ref switch_modifiers);
-+#else
-+            if ((switch_modifiers & Gdk.ModifierType.SUPER_MASK) != 0)
-+                switch_modifiers |= Gdk.ModifierType.MOD4_MASK;
-+            if ((switch_modifiers & Gdk.ModifierType.HYPER_MASK) != 0)
-+                switch_modifiers |= Gdk.ModifierType.MOD4_MASK;
-+#endif
-+            switch_modifiers &= ~VIRTUAL_MODIFIERS;
-+        }
-+
-+        if (switch_keysym == 0 && switch_modifiers == 0) {
-+            warning("Parse accelerator '%s' failed!", accelerator);
-+            return;
-+        }
-+
-+        keybinding = new Keybinding(switch_keysym,
-+                                    switch_modifiers,
-+                                    false,
-+                                    ftype);
-+        keybindings.append(keybinding);
-+
-+        keybinding_manager.bind(switch_keysym, switch_modifiers,
-+                                handler_normal);
-+        if (ftype == KeyEventFuncType.EMOJI_TYPING) {
-+            return;
-+        }
-+
-+        // accelerator already has Shift mask
-+        if ((switch_modifiers & reverse_modifier) != 0) {
-+            return;
-+        }
-+
-+        switch_modifiers |= reverse_modifier;
-+
-+        keybinding = new Keybinding(switch_keysym,
-+                                    switch_modifiers,
-+                                    true,
-+                                    ftype);
-+        keybindings.append(keybinding);
-+
-+        if (ftype == KeyEventFuncType.IME_SWITCHER) {
-+            keybinding_manager.bind(switch_keysym, switch_modifiers,
-+                                    handler_reverse);
-+        }
-+        return;
-+    }
-+
-+    public static void
-+    unbind_switch_shortcut(KeyEventFuncType      ftype,
-+                           GLib.List<Keybinding> keybindings) {
-+        var keybinding_manager = KeybindingManager.get_instance();
-+
-+        while (keybindings != null) {
-+            Keybinding keybinding = keybindings.data;
-+
-+            if (ftype == KeyEventFuncType.ANY ||
-+                ftype == keybinding.ftype) {
-+                keybinding_manager.unbind(keybinding.keysym,
-+                                          keybinding.modifiers);
-+            }
-+            keybindings = keybindings.next;
-+        }
-+    }
-+
-+    public static void
-+    set_custom_font(GLib.Settings?       settings_panel,
-+                    GLib.Settings?       settings_emoji,
-+                    ref Gtk.CssProvider? css_provider) {
-+        Gdk.Display display = Gdk.Display.get_default();
-+        Gdk.Screen screen = (display != null) ?
-+                display.get_default_screen() : null;
-+
-+        if (screen == null) {
-+            warning("Could not open display.");
-+            return;
-+        }
-+
-+        if (settings_emoji != null) {
-+            string emoji_font = settings_emoji.get_string("font");
-+            if (emoji_font == null) {
-+                warning("No config emoji:font.");
-+                return;
-+            }
-+            IBusEmojier.set_emoji_font(emoji_font);
-+        }
-+
-+        if (settings_panel == null)
-+            return;
-+
-+        bool use_custom_font = settings_panel.get_boolean("use-custom-font");
-+
-+        if (css_provider != null) {
-+            Gtk.StyleContext.remove_provider_for_screen(screen,
-+                                                        css_provider);
-+            css_provider = null;
-+        }
-+
-+        if (use_custom_font == false) {
-+            return;
-+        }
-+
-+        string custom_font = settings_panel.get_string("custom-font");
-+        if (custom_font == null) {
-+            warning("No config panel:custom-font.");
-+            return;
-+        }
-+
-+        Pango.FontDescription font_desc =
-+                Pango.FontDescription.from_string(custom_font);
-+        string font_family = font_desc.get_family();
-+        int font_size = font_desc.get_size() / Pango.SCALE;
-+        string data;
-+
-+        if (Gtk.MAJOR_VERSION < 3 ||
-+            (Gtk.MAJOR_VERSION == 3 && Gtk.MINOR_VERSION < 20)) {
-+            data = "GtkLabel { font: %s; }".printf(custom_font);
-+        } else {
-+            data = "label { font-family: %s; font-size: %dpt; }"
-+                           .printf(font_family, font_size);
-+        }
-+
-+        css_provider = new Gtk.CssProvider();
-+
-+        try {
-+            css_provider.load_from_data(data, -1);
-+        } catch (GLib.Error e) {
-+            warning("Failed css_provider_from_data: %s: %s", custom_font,
-+                                                             e.message);
-+            return;
-+        }
-+
-+        Gtk.StyleContext.add_provider_for_screen(
-+                screen,
-+                css_provider,
-+                Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
-+    }
-+}
-diff --git a/ui/gtk3/candidatearea.vala b/ui/gtk3/candidatearea.vala
-index e162a960..f590cf3a 100644
---- a/ui/gtk3/candidatearea.vala
-+++ b/ui/gtk3/candidatearea.vala
-@@ -21,108 +21,6 @@
-  * USA
-  */
- 
--class ThemedRGBA {
--    public Gdk.RGBA *normal_fg { get; set; }
--    public Gdk.RGBA *normal_bg { get; set; }
--    public Gdk.RGBA *selected_fg { get; set; }
--    public Gdk.RGBA *selected_bg { get; set; }
--
--    private Gtk.StyleContext m_style_context;
--
--    public ThemedRGBA(Gtk.Widget widget) {
--        this.normal_fg = null;
--        this.normal_bg = null;
--        this.selected_fg = null;
--        this.selected_bg = null;
--
--        /* Use the color of Gtk.TextView instead of Gtk.Label
--         * because the selected label "color" is not configured
--         * in "Adwaita" theme and the selected label "background-color"
--         * is not configured in "Maia" theme.
--         * https://github.com/ibus/ibus/issues/1871
--         */
--        Gtk.WidgetPath widget_path = new Gtk.WidgetPath();
--        widget_path.append_type(typeof(Gtk.TextView));
--        m_style_context = new Gtk.StyleContext();
--        m_style_context.set_path(widget_path);
--        m_style_context.add_class(Gtk.STYLE_CLASS_VIEW);
--
--        /* "-gtk-secondary-caret-color" value is different
--         * if the parent widget is set in "Menta" theme.
--         */
--        m_style_context.set_parent(widget.get_style_context());
--
--        get_rgba();
--
--        m_style_context.changed.connect(() => { get_rgba(); });
--    }
--
--    ~ThemedRGBA() {
--        reset_rgba();
--    }
--
--    private void reset_rgba() {
--        if (this.normal_fg != null) {
--            this.normal_fg.free();
--            this.normal_fg = null;
--        }
--        if (this.normal_bg != null) {
--            this.normal_bg.free();
--            this.normal_bg = null;
--        }
--        if (this.selected_fg != null) {
--            this.selected_fg.free();
--            this.selected_fg = null;
--        }
--        if (this.selected_bg != null) {
--            this.selected_bg.free();
--            this.selected_bg = null;
--        }
--    }
--
--    private void get_rgba() {
--        reset_rgba();
--        Gdk.RGBA *normal_fg = null;
--        Gdk.RGBA *normal_bg = null;
--        Gdk.RGBA *selected_fg = null;
--        Gdk.RGBA *selected_bg = null;
--        m_style_context.get(Gtk.StateFlags.NORMAL,
--                            "color",
--                            out normal_fg);
--        m_style_context.get(Gtk.StateFlags.SELECTED,
--                            "color",
--                            out selected_fg);
--
--        string bg_prop = "background-color";
--        m_style_context.get(Gtk.StateFlags.NORMAL,
--                            bg_prop,
--                            out normal_bg);
--        m_style_context.get(Gtk.StateFlags.SELECTED,
--                            bg_prop,
--                            out selected_bg);
--        if (normal_bg.red   == selected_bg.red &&
--            normal_bg.green == selected_bg.green &&
--            normal_bg.blue  == selected_bg.blue &&
--            normal_bg.alpha == selected_bg.alpha) {
--            normal_bg.free();
--            normal_bg = null;
--            normal_bg.free();
--            normal_bg = null;
--            bg_prop = "-gtk-secondary-caret-color";
--            m_style_context.get(Gtk.StateFlags.NORMAL,
--                                bg_prop,
--                                out normal_bg);
--            m_style_context.get(Gtk.StateFlags.SELECTED,
--                                bg_prop,
--                                out selected_bg);
--        }
--        this.normal_fg   = normal_fg;
--        this.normal_bg   = normal_bg;
--        this.selected_fg = selected_fg;
--        this.selected_bg = selected_bg;
--    }
--}
--
- class CandidateArea : Gtk.Box {
-     private bool m_vertical;
-     private Gtk.Label[] m_labels;
-diff --git a/ui/gtk3/extension.vala b/ui/gtk3/extension.vala
-new file mode 100644
-index 00000000..a170280b
---- /dev/null
-+++ b/ui/gtk3/extension.vala
-@@ -0,0 +1,124 @@
-+/* vim:set et sts=4 sw=4:
-+ *
-+ * ibus - The Input Bus
-+ *
-+ * Copyright(c) 2018 Peng Huang <shawn.p.huang@gmail.com>
-+ * Copyright(c) 2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ *
-+ * This library is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU Lesser General Public
-+ * License as published by the Free Software Foundation; either
-+ * version 2.1 of the License, or (at your option) any later version.
-+ *
-+ * This library is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-+ * Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public
-+ * License along with this library; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
-+ * USA
-+ */
-+
-+class ExtensionGtk {
-+    private IBus.Bus m_bus;
-+    private PanelBinding m_panel;
-+
-+    public ExtensionGtk(string[] argv) {
-+        GLib.Intl.bindtextdomain(Config.GETTEXT_PACKAGE,
-+                                 Config.GLIB_LOCALE_DIR);
-+        GLib.Intl.bind_textdomain_codeset(Config.GETTEXT_PACKAGE, "UTF-8");
-+        IBus.init();
-+        Gtk.init(ref argv);
-+
-+        m_bus = new IBus.Bus();
-+
-+        m_bus.connected.connect(bus_connected);
-+        m_bus.disconnected.connect(bus_disconnected);
-+
-+        if (m_bus.is_connected()) {
-+            init();
-+        }
-+    }
-+
-+    private void init() {
-+        DBusConnection connection = m_bus.get_connection();
-+        connection.signal_subscribe("org.freedesktop.DBus",
-+                                    "org.freedesktop.DBus",
-+                                    "NameAcquired",
-+                                    "/org/freedesktop/DBus",
-+                                    IBus.SERVICE_PANEL_EXTENSION,
-+                                    DBusSignalFlags.NONE,
-+                                    bus_name_acquired_cb);
-+        connection.signal_subscribe("org.freedesktop.DBus",
-+                                    "org.freedesktop.DBus",
-+                                    "NameLost",
-+                                    "/org/freedesktop/DBus",
-+                                    IBus.SERVICE_PANEL_EXTENSION,
-+                                    DBusSignalFlags.NONE,
-+                                    bus_name_lost_cb);
-+        var flags =
-+                IBus.BusNameFlag.ALLOW_REPLACEMENT |
-+                IBus.BusNameFlag.REPLACE_EXISTING;
-+        m_bus.request_name(IBus.SERVICE_PANEL_EXTENSION, flags);
-+    }
-+
-+    public int run() {
-+        Gtk.main();
-+        return 0;
-+    }
-+
-+    private void bus_name_acquired_cb(DBusConnection connection,
-+                                      string sender_name,
-+                                      string object_path,
-+                                      string interface_name,
-+                                      string signal_name,
-+                                      Variant parameters) {
-+        debug("signal_name = %s", signal_name);
-+        m_panel = new PanelBinding(m_bus);
-+        m_panel.load_settings();
-+    }
-+
-+    private void bus_name_lost_cb(DBusConnection connection,
-+                                  string sender_name,
-+                                  string object_path,
-+                                  string interface_name,
-+                                  string signal_name,
-+                                  Variant parameters) {
-+        // "Destroy" dbus method was called before this callback is called.
-+        // "Destroy" dbus method -> ibus_service_destroy()
-+        // -> g_dbus_connection_unregister_object()
-+        // -> g_object_unref(m_panel) will be called later with an idle method,
-+        // which was assigned in the arguments of
-+        // g_dbus_connection_register_object()
-+        debug("signal_name = %s", signal_name);
-+
-+        // unref m_panel
-+        m_panel.disconnect_signals();
-+        m_panel = null;
-+    }
-+
-+    private void bus_disconnected(IBus.Bus bus) {
-+        debug("connection is lost.");
-+        Gtk.main_quit();
-+    }
-+
-+    private void bus_connected(IBus.Bus bus) {
-+        init();
-+    }
-+
-+    public static void main(string[] argv) {
-+        // https://bugzilla.redhat.com/show_bug.cgi?id=1226465#c20
-+        // In /etc/xdg/plasma-workspace/env/gtk3_scrolling.sh
-+        // Plasma deskop sets this variable and prevents Super-space,
-+        // and Ctrl-Shift-e when ibus-ui-gtk3 runs after the
-+        // desktop is launched.
-+        GLib.Environment.unset_variable("GDK_CORE_DEVICE_EVENTS");
-+        // for Gdk.X11.get_default_xdisplay()
-+        Gdk.set_allowed_backends("x11");
-+
-+        ExtensionGtk extension = new ExtensionGtk(argv);
-+        extension.run();
-+    }
-+}
-diff --git a/ui/gtk3/gtkextension.xml.in b/ui/gtk3/gtkextension.xml.in
-new file mode 100644
-index 00000000..b8157c97
---- /dev/null
-+++ b/ui/gtk3/gtkextension.xml.in
-@@ -0,0 +1,12 @@
-+<?xml version="1.0" encoding="utf-8"?>
-+<!-- filename: gtkextension.xml -->
-+<component>
-+	<name>org.freedesktop.IBus.Panel.Extension</name>
-+	<description>Gtk Panel Extension Component</description>
-+	<exec>@libexecdir@/ibus-extension-gtk3</exec>
-+	<version>@VERSION@</version>
-+	<author>Takao Fujiwara &lt;takao.fujiwara1@gmail.com&gt;</author>
-+	<license>GPL</license>
-+	<homepage>https://github.com/ibus/ibus/wiki</homepage>
-+	<textdomain>ibus</textdomain>
-+</component>
-diff --git a/ui/gtk3/iconwidget.vala b/ui/gtk3/iconwidget.vala
-index d322650c..36643c74 100644
---- a/ui/gtk3/iconwidget.vala
-+++ b/ui/gtk3/iconwidget.vala
-@@ -3,6 +3,7 @@
-  * ibus - The Input Bus
-  *
-  * Copyright(c) 2011-2014 Peng Huang <shawn.p.huang@gmail.com>
-+ * Copyright(c) 2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-  *
-  * This library is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU Lesser General Public
-@@ -20,6 +21,108 @@
-  * USA
-  */
- 
-+class ThemedRGBA {
-+    public Gdk.RGBA *normal_fg { get; set; }
-+    public Gdk.RGBA *normal_bg { get; set; }
-+    public Gdk.RGBA *selected_fg { get; set; }
-+    public Gdk.RGBA *selected_bg { get; set; }
-+
-+    private Gtk.StyleContext m_style_context;
-+
-+    public ThemedRGBA(Gtk.Widget widget) {
-+        this.normal_fg = null;
-+        this.normal_bg = null;
-+        this.selected_fg = null;
-+        this.selected_bg = null;
-+
-+        /* Use the color of Gtk.TextView instead of Gtk.Label
-+         * because the selected label "color" is not configured
-+         * in "Adwaita" theme and the selected label "background-color"
-+         * is not configured in "Maia" theme.
-+         * https://github.com/ibus/ibus/issues/1871
-+         */
-+        Gtk.WidgetPath widget_path = new Gtk.WidgetPath();
-+        widget_path.append_type(typeof(Gtk.TextView));
-+        m_style_context = new Gtk.StyleContext();
-+        m_style_context.set_path(widget_path);
-+        m_style_context.add_class(Gtk.STYLE_CLASS_VIEW);
-+
-+        /* "-gtk-secondary-caret-color" value is different
-+         * if the parent widget is set in "Menta" theme.
-+         */
-+        m_style_context.set_parent(widget.get_style_context());
-+
-+        get_rgba();
-+
-+        m_style_context.changed.connect(() => { get_rgba(); });
-+    }
-+
-+    ~ThemedRGBA() {
-+        reset_rgba();
-+    }
-+
-+    private void reset_rgba() {
-+        if (this.normal_fg != null) {
-+            this.normal_fg.free();
-+            this.normal_fg = null;
-+        }
-+        if (this.normal_bg != null) {
-+            this.normal_bg.free();
-+            this.normal_bg = null;
-+        }
-+        if (this.selected_fg != null) {
-+            this.selected_fg.free();
-+            this.selected_fg = null;
-+        }
-+        if (this.selected_bg != null) {
-+            this.selected_bg.free();
-+            this.selected_bg = null;
-+        }
-+    }
-+
-+    private void get_rgba() {
-+        reset_rgba();
-+        Gdk.RGBA *normal_fg = null;
-+        Gdk.RGBA *normal_bg = null;
-+        Gdk.RGBA *selected_fg = null;
-+        Gdk.RGBA *selected_bg = null;
-+        m_style_context.get(Gtk.StateFlags.NORMAL,
-+                            "color",
-+                            out normal_fg);
-+        m_style_context.get(Gtk.StateFlags.SELECTED,
-+                            "color",
-+                            out selected_fg);
-+
-+        string bg_prop = "background-color";
-+        m_style_context.get(Gtk.StateFlags.NORMAL,
-+                            bg_prop,
-+                            out normal_bg);
-+        m_style_context.get(Gtk.StateFlags.SELECTED,
-+                            bg_prop,
-+                            out selected_bg);
-+        if (normal_bg.red   == selected_bg.red &&
-+            normal_bg.green == selected_bg.green &&
-+            normal_bg.blue  == selected_bg.blue &&
-+            normal_bg.alpha == selected_bg.alpha) {
-+            normal_bg.free();
-+            normal_bg = null;
-+            normal_bg.free();
-+            normal_bg = null;
-+            bg_prop = "-gtk-secondary-caret-color";
-+            m_style_context.get(Gtk.StateFlags.NORMAL,
-+                                bg_prop,
-+                                out normal_bg);
-+            m_style_context.get(Gtk.StateFlags.SELECTED,
-+                                bg_prop,
-+                                out selected_bg);
-+        }
-+        this.normal_fg   = normal_fg;
-+        this.normal_bg   = normal_bg;
-+        this.selected_fg = selected_fg;
-+        this.selected_bg = selected_bg;
-+    }
-+}
-+
- class IconWidget: Gtk.Image {
-     /**
-      * IconWidget:
-diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
-index bcb3ed75..d9238c89 100644
---- a/ui/gtk3/panel.vala
-+++ b/ui/gtk3/panel.vala
-@@ -22,39 +22,16 @@
-  */
- 
- class Panel : IBus.PanelService {
--    private class Keybinding {
--        public Keybinding(uint keysym,
--                          Gdk.ModifierType modifiers,
--                          bool reverse,
--                          KeyEventFuncType ftype) {
--            this.keysym = keysym;
--            this.modifiers = modifiers;
--            this.reverse = reverse;
--            this.ftype = ftype;
--        }
--
--        public uint keysym { get; set; }
--        public Gdk.ModifierType modifiers { get; set; }
--        public bool reverse { get; set; }
--        public KeyEventFuncType ftype { get; set; }
--    }
- 
-     private enum IconType {
-         STATUS_ICON,
-         INDICATOR,
-     }
- 
--    private enum KeyEventFuncType {
--        ANY,
--        IME_SWITCHER,
--        EMOJI_TYPING,
--    }
--
-     private IBus.Bus m_bus;
-     private GLib.Settings m_settings_general = null;
-     private GLib.Settings m_settings_hotkey = null;
-     private GLib.Settings m_settings_panel = null;
--    private GLib.Settings m_settings_emoji = null;
-     private IconType m_icon_type = IconType.STATUS_ICON;
-     private Indicator m_indicator;
- #if INDICATOR
-@@ -73,10 +50,6 @@ class Panel : IBus.PanelService {
-     private CandidatePanel m_candidate_panel;
-     private Switcher m_switcher;
-     private uint m_switcher_focus_set_engine_id;
--    private IBusEmojier? m_emojier;
--    private uint m_emojier_set_emoji_lang_id;
--    private uint m_emojier_focus_commit_text_id;
--    private string[] m_emojier_favorites = {};
-     private PropertyManager m_property_manager;
-     private PropertyPanel m_property_panel;
-     private GLib.Pid m_setup_pid = 0;
-@@ -108,7 +81,8 @@ class Panel : IBus.PanelService {
-     private ulong m_activate_id;
-     private ulong m_registered_status_notifier_item_id;
- 
--    private GLib.List<Keybinding> m_keybindings = new GLib.List<Keybinding>();
-+    private GLib.List<BindingCommon.Keybinding> m_keybindings =
-+            new GLib.List<BindingCommon.Keybinding>();
- 
-     public Panel(IBus.Bus bus) {
-         GLib.assert(bus.is_connected());
-@@ -147,8 +121,6 @@ class Panel : IBus.PanelService {
-             m_switcher.set_popup_delay_time((uint) m_switcher_delay_time);
-         }
- 
--        bind_emoji_shortcut();
--
-         m_property_manager = new PropertyManager();
-         m_property_manager.property_activate.connect((w, k, s) => {
-             property_activate(k, s);
-@@ -168,7 +140,9 @@ class Panel : IBus.PanelService {
-         if (m_indicator != null)
-             m_indicator.unregister_connection();
- #endif
--        unbind_switch_shortcut(KeyEventFuncType.ANY);
-+        BindingCommon.unbind_switch_shortcut(
-+                BindingCommon.KeyEventFuncType.ANY, m_keybindings);
-+        m_keybindings = null;
-     }
- 
-     private void init_settings() {
-@@ -176,7 +150,6 @@ class Panel : IBus.PanelService {
-         m_settings_hotkey =
-                 new GLib.Settings("org.freedesktop.ibus.general.hotkey");
-         m_settings_panel = new GLib.Settings("org.freedesktop.ibus.panel");
--        m_settings_emoji = new GLib.Settings("org.freedesktop.ibus.panel.emoji");
- 
-         m_settings_general.changed["preload-engines"].connect((key) => {
-                 update_engines(m_settings_general.get_strv(key),
-@@ -205,16 +178,23 @@ class Panel : IBus.PanelService {
-         });
- 
-         m_settings_hotkey.changed["triggers"].connect((key) => {
--                unbind_switch_shortcut(KeyEventFuncType.IME_SWITCHER);
-+                BindingCommon.unbind_switch_shortcut(
-+                        BindingCommon.KeyEventFuncType.IME_SWITCHER,
-+                        m_keybindings);
-+                m_keybindings = null;
-                 bind_switch_shortcut();
-         });
- 
-         m_settings_panel.changed["custom-font"].connect((key) => {
--                set_custom_font();
-+                BindingCommon.set_custom_font(m_settings_panel,
-+                                              null,
-+                                              ref m_css_provider);
-         });
- 
-         m_settings_panel.changed["use-custom-font"].connect((key) => {
--                set_custom_font();
-+                BindingCommon.set_custom_font(m_settings_panel,
-+                                              null,
-+                                              ref m_css_provider);
-         });
- 
-         m_settings_panel.changed["show-icon-on-systray"].connect((key) => {
-@@ -245,39 +225,6 @@ class Panel : IBus.PanelService {
-         m_settings_panel.changed["property-icon-delay-time"].connect((key) => {
-                 set_property_icon_delay_time();
-         });
--
--        m_settings_emoji.changed["hotkey"].connect((key) => {
--                unbind_switch_shortcut(KeyEventFuncType.EMOJI_TYPING);
--                bind_emoji_shortcut();
--        });
--
--        m_settings_emoji.changed["font"].connect((key) => {
--                set_custom_font();
--        });
--
--        m_settings_emoji.changed["favorites"].connect((key) => {
--                set_emoji_favorites();
--        });
--
--        m_settings_emoji.changed["favorite-annotations"].connect((key) => {
--                set_emoji_favorites();
--        });
--
--        m_settings_emoji.changed["lang"].connect((key) => {
--                set_emoji_lang();
--        });
--
--        m_settings_emoji.changed["has-partial-match"].connect((key) => {
--                set_emoji_partial_match();
--        });
--
--        m_settings_emoji.changed["partial-match-length"].connect((key) => {
--                set_emoji_partial_match();
--        });
--
--        m_settings_emoji.changed["partial-match-condition"].connect((key) => {
--                set_emoji_partial_match();
--        });
-     }
- 
-     private void popup_menu_at_area_window(Gtk.Menu              menu,
-@@ -409,120 +356,40 @@ class Panel : IBus.PanelService {
-         m_status_icon.set_from_icon_name("ibus-keyboard");
-     }
- 
--    private void keybinding_manager_bind(KeybindingManager keybinding_manager,
--                                         string?           accelerator,
--                                         KeyEventFuncType  ftype) {
--        uint switch_keysym = 0;
--        Gdk.ModifierType switch_modifiers = 0;
--        Gdk.ModifierType reverse_modifier = Gdk.ModifierType.SHIFT_MASK;
--        Keybinding keybinding;
--
--        Gtk.accelerator_parse(accelerator,
--                out switch_keysym, out switch_modifiers);
--
--        // Map virtual modifiers to (i.e. Mod2, Mod3, ...)
--        const Gdk.ModifierType VIRTUAL_MODIFIERS = (
--                Gdk.ModifierType.SUPER_MASK |
--                Gdk.ModifierType.HYPER_MASK |
--                Gdk.ModifierType.META_MASK);
--        if ((switch_modifiers & VIRTUAL_MODIFIERS) != 0) {
--        // workaround a bug in gdk vapi vala > 0.18
--        // https://bugzilla.gnome.org/show_bug.cgi?id=677559
--#if VALA_0_18
--            Gdk.Keymap.get_default().map_virtual_modifiers(
--                    ref switch_modifiers);
--#else
--            if ((switch_modifiers & Gdk.ModifierType.SUPER_MASK) != 0)
--                switch_modifiers |= Gdk.ModifierType.MOD4_MASK;
--            if ((switch_modifiers & Gdk.ModifierType.HYPER_MASK) != 0)
--                switch_modifiers |= Gdk.ModifierType.MOD4_MASK;
--#endif
--            switch_modifiers &= ~VIRTUAL_MODIFIERS;
--        }
--
--        if (switch_keysym == 0 && switch_modifiers == 0) {
--            warning("Parse accelerator '%s' failed!", accelerator);
--            return;
--        }
--
--        keybinding = new Keybinding(switch_keysym,
--                                    switch_modifiers,
--                                    false,
--                                    ftype);
--        m_keybindings.append(keybinding);
--
--        /* Workaround not to free the pointer of handle_engine_switch() */
--        if (ftype == KeyEventFuncType.IME_SWITCHER) {
--            keybinding_manager.bind(switch_keysym, switch_modifiers,
--                    (e) => handle_engine_switch(e, false));
--        } else if (ftype == KeyEventFuncType.EMOJI_TYPING) {
--            keybinding_manager.bind(switch_keysym, switch_modifiers,
--                    (e) => handle_emoji_typing(e));
--            return;
--        }
--
--        // accelerator already has Shift mask
--        if ((switch_modifiers & reverse_modifier) != 0) {
--            return;
--        }
--
--        switch_modifiers |= reverse_modifier;
--
--        keybinding = new Keybinding(switch_keysym,
--                                    switch_modifiers,
--                                    true,
--                                    ftype);
--        m_keybindings.append(keybinding);
--
--        if (ftype == KeyEventFuncType.IME_SWITCHER) {
--            keybinding_manager.bind(switch_keysym, switch_modifiers,
--                    (e) => handle_engine_switch(e, true));
--        }
--    }
--
-     private void bind_switch_shortcut() {
-         string[] accelerators = m_settings_hotkey.get_strv("triggers");
- 
-         var keybinding_manager = KeybindingManager.get_instance();
- 
-         foreach (var accelerator in accelerators) {
--            keybinding_manager_bind(keybinding_manager,
--                                    accelerator,
--                                    KeyEventFuncType.IME_SWITCHER);
--        }
--    }
--
--    private void bind_emoji_shortcut() {
--#if EMOJI_DICT
--        string[] accelerators = m_settings_emoji.get_strv("hotkey");
--
--        var keybinding_manager = KeybindingManager.get_instance();
--
--        foreach (var accelerator in accelerators) {
--            keybinding_manager_bind(keybinding_manager,
--                                    accelerator,
--                                    KeyEventFuncType.EMOJI_TYPING);
-+            BindingCommon.keybinding_manager_bind(
-+                    keybinding_manager,
-+                    ref m_keybindings,
-+                    accelerator,
-+                    BindingCommon.KeyEventFuncType.IME_SWITCHER,
-+                    handle_engine_switch_normal,
-+                    handle_engine_switch_reverse);
-         }
--#endif
-     }
- 
--    private void unbind_switch_shortcut(KeyEventFuncType ftype) {
-+/*
-+    public static void
-+    unbind_switch_shortcut(KeyEventFuncType      ftype,
-+                           GLib.List<Keybinding> keybindings) {
-         var keybinding_manager = KeybindingManager.get_instance();
- 
--        unowned GLib.List<Keybinding> keybindings = m_keybindings;
--
-         while (keybindings != null) {
-             Keybinding keybinding = keybindings.data;
- 
--            if (ftype == KeyEventFuncType.ANY || ftype == keybinding.ftype) {
-+            if (ftype == KeyEventFuncType.ANY ||
-+                ftype == keybinding.ftype) {
-                 keybinding_manager.unbind(keybinding.keysym,
-                                           keybinding.modifiers);
-             }
-             keybindings = keybindings.next;
-         }
--
--        m_keybindings = null;
-     }
-+*/
- 
-     /**
-      * panel_get_engines_from_xkb:
-@@ -670,69 +537,6 @@ class Panel : IBus.PanelService {
-         m_settings_general.set_strv("preload-engines", names);
-     }
- 
--    private void set_custom_font() {
--        Gdk.Display display = Gdk.Display.get_default();
--        Gdk.Screen screen = (display != null) ?
--                display.get_default_screen() : null;
--
--        if (screen == null) {
--            warning("Could not open display.");
--            return;
--        }
--
--        string emoji_font = m_settings_emoji.get_string("font");
--        if (emoji_font == null) {
--            warning("No config emoji:font.");
--            return;
--        }
--        IBusEmojier.set_emoji_font(emoji_font);
--
--        bool use_custom_font = m_settings_panel.get_boolean("use-custom-font");
--
--        if (m_css_provider != null) {
--            Gtk.StyleContext.remove_provider_for_screen(screen,
--                                                        m_css_provider);
--            m_css_provider = null;
--        }
--
--        if (use_custom_font == false) {
--            return;
--        }
--
--        string custom_font = m_settings_panel.get_string("custom-font");
--        if (custom_font == null) {
--            warning("No config panel:custom-font.");
--            return;
--        }
--
--        Pango.FontDescription font_desc =
--                Pango.FontDescription.from_string(custom_font);
--        string font_family = font_desc.get_family();
--        int font_size = font_desc.get_size() / Pango.SCALE;
--        string data;
--
--        if (Gtk.MAJOR_VERSION < 3 ||
--            (Gtk.MAJOR_VERSION == 3 && Gtk.MINOR_VERSION < 20)) {
--            data = "GtkLabel { font: %s; }".printf(custom_font);
--        } else {
--            data = "label { font-family: %s; font-size: %dpt; }"
--                           .printf(font_family, font_size);
--        }
--
--        m_css_provider = new Gtk.CssProvider();
--
--        try {
--            m_css_provider.load_from_data(data, -1);
--        } catch (GLib.Error e) {
--            warning("Failed css_provider_from_data: %s: %s", custom_font,
--                                                             e.message);
--            return;
--        }
--
--        Gtk.StyleContext.add_provider_for_screen(screen,
--                                                 m_css_provider,
--                                                 Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
--    }
- 
-     private void set_switcher_delay_time() {
-         m_switcher_delay_time =
-@@ -855,35 +659,6 @@ class Panel : IBus.PanelService {
-                 m_settings_panel.get_int("property-icon-delay-time");
-     }
- 
--    private void set_emoji_favorites() {
--        m_emojier_favorites = m_settings_emoji.get_strv("favorites");
--        IBusEmojier.set_favorites(
--                m_emojier_favorites,
--                m_settings_emoji.get_strv("favorite-annotations"));
--    }
--
--    private void set_emoji_lang() {
--        if (m_emojier_set_emoji_lang_id > 0) {
--            GLib.Source.remove(m_emojier_set_emoji_lang_id);
--            m_emojier_set_emoji_lang_id = 0;
--        }
--        m_emojier_set_emoji_lang_id = GLib.Idle.add(() => {
--            IBusEmojier.set_annotation_lang(
--                    m_settings_emoji.get_string("lang"));
--            m_emojier_set_emoji_lang_id = 0;
--            IBusEmojier.load_unicode_dict();
--            return false;
--        });
--    }
--
--    private void set_emoji_partial_match() {
--        IBusEmojier.set_partial_match(
--                m_settings_emoji.get_boolean("has-partial-match"));
--        IBusEmojier.set_partial_match_length(
--                m_settings_emoji.get_int("partial-match-length"));
--        IBusEmojier.set_partial_match_condition(
--                m_settings_emoji.get_int("partial-match-condition"));
--    }
- 
-     private int compare_versions(string version1, string version2) {
-         string[] version1_list = version1.split(".");
-@@ -985,12 +760,16 @@ class Panel : IBus.PanelService {
-         set_use_xmodmap();
-         update_engines(m_settings_general.get_strv("preload-engines"),
-                        m_settings_general.get_strv("engines-order"));
--        unbind_switch_shortcut(KeyEventFuncType.ANY);
-+        BindingCommon.unbind_switch_shortcut(
-+                BindingCommon.KeyEventFuncType.ANY,
-+                m_keybindings);
-+        m_keybindings = null;
-         bind_switch_shortcut();
--        bind_emoji_shortcut();
-         set_switcher_delay_time();
-         set_embed_preedit_text();
--        set_custom_font();
-+        BindingCommon.set_custom_font(m_settings_panel,
-+                                      null,
-+                                      ref m_css_provider);
-         set_show_icon_on_systray();
-         set_lookup_table_orientation();
-         set_show_property_panel();
-@@ -998,9 +777,6 @@ class Panel : IBus.PanelService {
-         set_follow_input_cursor_when_always_shown_property_panel();
-         set_xkb_icon_rgba();
-         set_property_icon_delay_time();
--        set_emoji_favorites();
--        set_emoji_lang();
--        set_emoji_partial_match();
-     }
- 
-     /**
-@@ -1037,10 +813,6 @@ class Panel : IBus.PanelService {
-             GLib.Source.remove(m_preload_engines_id);
-             m_preload_engines_id = 0;
-         }
--        if (m_emojier_set_emoji_lang_id > 0) {
--            GLib.Source.remove(m_emojier_set_emoji_lang_id);
--            m_emojier_set_emoji_lang_id = 0;
--        }
-     }
- 
-     private void engine_contexts_insert(IBus.EngineDesc engine) {
-@@ -1091,7 +863,15 @@ class Panel : IBus.PanelService {
-         set_engine(engine);
-     }
- 
--    private void handle_engine_switch(Gdk.Event event, bool revert) {
-+    private void handle_engine_switch_normal(Gdk.Event event) {
-+        handle_engine_switch(event, false);
-+    }
-+
-+    private void handle_engine_switch_reverse(Gdk.Event event) {
-+        handle_engine_switch(event, true);
-+    }
-+
-+    private void handle_engine_switch(Gdk.Event event, bool reverse) {
-         // Do not need switch IME
-         if (m_engines.length <= 1)
-             return;
-@@ -1105,12 +885,12 @@ class Panel : IBus.PanelService {
-         bool pressed = KeybindingManager.primary_modifier_still_pressed(
-                 event, primary_modifiers);
- 
--        if (revert) {
-+        if (reverse) {
-             modifiers &= ~Gdk.ModifierType.SHIFT_MASK;
-         }
- 
-         if (pressed && m_switcher_delay_time >= 0) {
--            int i = revert ? m_engines.length - 1 : 1;
-+            int i = reverse ? m_engines.length - 1 : 1;
- 
-             /* The flag of m_switcher.is_running avoids the following problem:
-              *
-@@ -1132,28 +912,11 @@ class Panel : IBus.PanelService {
-                 this.switcher_focus_set_engine();
-             }
-         } else {
--            int i = revert ? m_engines.length - 1 : 1;
-+            int i = reverse ? m_engines.length - 1 : 1;
-             switch_engine(i);
-         }
-     }
- 
--    private void show_emojier(Gdk.Event event) {
--        m_emojier = new IBusEmojier();
--        string emoji = m_emojier.run(m_real_current_context_path, event);
--        if (emoji == null) {
--            m_emojier = null;
--            return;
--        }
--        this.emojier_focus_commit();
--    }
--
--    private void handle_emoji_typing(Gdk.Event event) {
--        if (m_emojier != null && m_emojier.is_running()) {
--            m_emojier.present_centralize(event);
--            return;
--        }
--        show_emojier(event);
--    }
- 
-     private void run_preload_engines(IBus.EngineDesc[] engines, int index) {
-         string[] names = {};
-@@ -1393,7 +1156,18 @@ class Panel : IBus.PanelService {
-                     event.key.window = Gdk.get_default_root_window();
-                     event.key.window.ref();
-                 }
--                handle_emoji_typing(event);
-+                IBus.XEvent xevent = new IBus.XEvent(
-+                        "event-type", IBus.XEventType.KEY_PRESS,
-+                        "window",
-+                        (event.key.window as Gdk.X11.Window).get_xid(),
-+                        "time", event.key.time,
-+                        "purpose", "emoji");
-+                /* new GLib.Variant("(sv)", "emoji", xevent.serialize_object())
-+                 * will call g_variant_unref() for the child variant by vala.
-+                 * I have no idea not to unref the object so integrated
-+                 * the purpose to IBus.XEvent above.
-+                 */
-+                panel_extension(xevent.serialize_object());
-             });
-             m_sys_menu.append(item);
- #endif
-@@ -1557,67 +1331,6 @@ class Panel : IBus.PanelService {
-         }
-     }
- 
--    private bool emojier_focus_commit_real() {
--        if (m_emojier == null)
--            return true;
--        string selected_string = m_emojier.get_selected_string();
--        string prev_context_path = m_emojier.get_input_context_path();
--        if (selected_string != null &&
--            prev_context_path != "" &&
--            prev_context_path == m_current_context_path) {
--            IBus.Text text = new IBus.Text.from_string(selected_string);
--            commit_text(text);
--            m_emojier = null;
--            bool has_favorite = false;
--            foreach (unowned string favorite in m_emojier_favorites) {
--                if (favorite == selected_string) {
--                    has_favorite = true;
--                    break;
--                }
--            }
--            if (!has_favorite) {
--                m_emojier_favorites += selected_string;
--                m_settings_emoji.set_strv("favorites", m_emojier_favorites);
--            }
--            return true;
--        }
--
--        return false;
--    }
--
--    private void emojier_focus_commit() {
--        if (m_emojier == null)
--            return;
--        string selected_string = m_emojier.get_selected_string();
--        string prev_context_path = m_emojier.get_input_context_path();
--        if (selected_string == null &&
--            prev_context_path != "" &&
--            m_emojier.is_running()) {
--            var context = GLib.MainContext.default();
--            if (m_emojier_focus_commit_text_id > 0 &&
--                context.find_source_by_id(m_emojier_focus_commit_text_id)
--                        != null) {
--                GLib.Source.remove(m_emojier_focus_commit_text_id);
--            }
--            m_emojier_focus_commit_text_id = GLib.Timeout.add(100, () => {
--                // focus_in is comming before switcher returns
--                emojier_focus_commit_real();
--                m_emojier_focus_commit_text_id = -1;
--                return false;
--            });
--        } else {
--            if (emojier_focus_commit_real()) {
--                var context = GLib.MainContext.default();
--                if (m_emojier_focus_commit_text_id > 0 &&
--                    context.find_source_by_id(m_emojier_focus_commit_text_id)
--                            != null) {
--                    GLib.Source.remove(m_emojier_focus_commit_text_id);
--                }
--                m_emojier_focus_commit_text_id = -1;
--            }
--        }
--    }
--
-     public override void focus_in(string input_context_path) {
-         m_current_context_path = input_context_path;
- 
-@@ -1632,7 +1345,6 @@ class Panel : IBus.PanelService {
-             m_real_current_context_path = m_current_context_path;
-             m_property_panel.focus_in();
-             this.switcher_focus_set_engine();
--            this.emojier_focus_commit();
-         }
- 
-         if (m_use_global_engine)
-diff --git a/ui/gtk3/panelbinding.vala b/ui/gtk3/panelbinding.vala
-new file mode 100644
-index 00000000..50700121
---- /dev/null
-+++ b/ui/gtk3/panelbinding.vala
-@@ -0,0 +1,335 @@
-+/* vim:set et sts=4 sw=4:
-+ *
-+ * ibus - The Input Bus
-+ *
-+ * Copyright(c) 2018 Peng Huang <shawn.p.huang@gmail.com>
-+ * Copyright(c) 2018 Takao Fujwiara <takao.fujiwara1@gmail.com>
-+ *
-+ * This library is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU Lesser General Public
-+ * License as published by the Free Software Foundation; either
-+ * version 2.1 of the License, or (at your option) any later version.
-+ *
-+ * This library is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-+ * Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public
-+ * License along with this library; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
-+ * USA
-+ */
-+
-+class PanelBinding : IBus.PanelService {
-+    private IBus.Bus m_bus;
-+    private GLib.Settings m_settings_panel = null;
-+    private GLib.Settings m_settings_emoji = null;
-+    private string m_current_context_path = "";
-+    private string m_real_current_context_path = "";
-+    private IBusEmojier? m_emojier;
-+    private uint m_emojier_set_emoji_lang_id;
-+    private uint m_emojier_focus_commit_text_id;
-+    private string[] m_emojier_favorites = {};
-+    private Gtk.CssProvider m_css_provider;
-+    private const uint PRELOAD_ENGINES_DELAY_TIME = 30000;
-+    private GLib.List<BindingCommon.Keybinding> m_keybindings =
-+            new GLib.List<BindingCommon.Keybinding>();
-+
-+    public PanelBinding(IBus.Bus bus) {
-+        GLib.assert(bus.is_connected());
-+        // Chain up base class constructor
-+        GLib.Object(connection : bus.get_connection(),
-+                    object_path : IBus.PATH_PANEL_EXTENSION);
-+
-+        m_bus = bus;
-+
-+        init_settings();
-+
-+        bind_emoji_shortcut();
-+    }
-+
-+
-+    ~PanelBinding() {
-+        BindingCommon.unbind_switch_shortcut(
-+                BindingCommon.KeyEventFuncType.ANY,
-+                m_keybindings);
-+    }
-+
-+
-+    private void init_settings() {
-+        m_settings_panel = new GLib.Settings("org.freedesktop.ibus.panel");
-+        m_settings_emoji = new GLib.Settings("org.freedesktop.ibus.panel.emoji");
-+
-+        m_settings_panel.changed["custom-font"].connect((key) => {
-+                BindingCommon.set_custom_font(m_settings_panel,
-+                                              m_settings_emoji,
-+                                              ref m_css_provider);
-+        });
-+
-+        m_settings_panel.changed["use-custom-font"].connect((key) => {
-+                BindingCommon.set_custom_font(m_settings_panel,
-+                                              m_settings_emoji,
-+                                              ref m_css_provider);
-+        });
-+
-+        m_settings_emoji.changed["hotkey"].connect((key) => {
-+                BindingCommon.unbind_switch_shortcut(
-+                        BindingCommon.KeyEventFuncType.EMOJI_TYPING,
-+                        m_keybindings);
-+                bind_emoji_shortcut();
-+        });
-+
-+        m_settings_emoji.changed["font"].connect((key) => {
-+                BindingCommon.set_custom_font(m_settings_panel,
-+                                              m_settings_emoji,
-+                                              ref m_css_provider);
-+        });
-+
-+        m_settings_emoji.changed["favorites"].connect((key) => {
-+                set_emoji_favorites();
-+        });
-+
-+        m_settings_emoji.changed["favorite-annotations"].connect((key) => {
-+                set_emoji_favorites();
-+        });
-+
-+        m_settings_emoji.changed["lang"].connect((key) => {
-+                set_emoji_lang();
-+        });
-+
-+        m_settings_emoji.changed["has-partial-match"].connect((key) => {
-+                set_emoji_partial_match();
-+        });
-+
-+        m_settings_emoji.changed["partial-match-length"].connect((key) => {
-+                set_emoji_partial_match();
-+        });
-+
-+        m_settings_emoji.changed["partial-match-condition"].connect((key) => {
-+                set_emoji_partial_match();
-+        });
-+    }
-+
-+
-+    private void bind_emoji_shortcut() {
-+#if EMOJI_DICT
-+        string[] accelerators = m_settings_emoji.get_strv("hotkey");
-+
-+        var keybinding_manager = KeybindingManager.get_instance();
-+
-+        foreach (var accelerator in accelerators) {
-+            BindingCommon.keybinding_manager_bind(
-+                    keybinding_manager,
-+                    ref m_keybindings,
-+                    accelerator,
-+                    BindingCommon.KeyEventFuncType.EMOJI_TYPING,
-+                    handle_emoji_typing,
-+                    null);
-+        }
-+#endif
-+    }
-+
-+
-+    private void set_emoji_favorites() {
-+        m_emojier_favorites = m_settings_emoji.get_strv("favorites");
-+        IBusEmojier.set_favorites(
-+                m_emojier_favorites,
-+                m_settings_emoji.get_strv("favorite-annotations"));
-+    }
-+
-+
-+    private void set_emoji_lang() {
-+        if (m_emojier_set_emoji_lang_id > 0) {
-+            GLib.Source.remove(m_emojier_set_emoji_lang_id);
-+            m_emojier_set_emoji_lang_id = 0;
-+        }
-+        m_emojier_set_emoji_lang_id = GLib.Idle.add(() => {
-+            IBusEmojier.set_annotation_lang(
-+                    m_settings_emoji.get_string("lang"));
-+            m_emojier_set_emoji_lang_id = 0;
-+            IBusEmojier.load_unicode_dict();
-+            return false;
-+        });
-+    }
-+
-+
-+    private void set_emoji_partial_match() {
-+        IBusEmojier.set_partial_match(
-+                m_settings_emoji.get_boolean("has-partial-match"));
-+        IBusEmojier.set_partial_match_length(
-+                m_settings_emoji.get_int("partial-match-length"));
-+        IBusEmojier.set_partial_match_condition(
-+                m_settings_emoji.get_int("partial-match-condition"));
-+    }
-+
-+
-+    public void load_settings() {
-+        BindingCommon.unbind_switch_shortcut(BindingCommon.KeyEventFuncType.ANY,
-+                                             m_keybindings);
-+        bind_emoji_shortcut();
-+        BindingCommon.set_custom_font(m_settings_panel,
-+                                      m_settings_emoji,
-+                                      ref m_css_provider);
-+        set_emoji_favorites();
-+        set_emoji_lang();
-+        set_emoji_partial_match();
-+    }
-+
-+
-+    /**
-+     * disconnect_signals:
-+     *
-+     * Call this API before m_panel = null so that the ref_count becomes 0
-+     */
-+    public void disconnect_signals() {
-+        if (m_emojier_set_emoji_lang_id > 0) {
-+            GLib.Source.remove(m_emojier_set_emoji_lang_id);
-+            m_emojier_set_emoji_lang_id = 0;
-+        }
-+    }
-+
-+
-+    private void show_emojier(Gdk.Event event) {
-+        m_emojier = new IBusEmojier();
-+        string emoji = m_emojier.run(m_real_current_context_path, event);
-+        if (emoji == null) {
-+            m_emojier = null;
-+            return;
-+        }
-+        this.emojier_focus_commit();
-+    }
-+
-+
-+    private void handle_emoji_typing(Gdk.Event event) {
-+        if (m_emojier != null && m_emojier.is_running()) {
-+            m_emojier.present_centralize(event);
-+            return;
-+        }
-+        show_emojier(event);
-+    }
-+
-+
-+    private bool emojier_focus_commit_real() {
-+        if (m_emojier == null)
-+            return true;
-+        string selected_string = m_emojier.get_selected_string();
-+        string prev_context_path = m_emojier.get_input_context_path();
-+        if (selected_string != null &&
-+            prev_context_path != "" &&
-+            prev_context_path == m_current_context_path) {
-+            IBus.Text text = new IBus.Text.from_string(selected_string);
-+            commit_text(text);
-+            m_emojier = null;
-+            bool has_favorite = false;
-+            foreach (unowned string favorite in m_emojier_favorites) {
-+                if (favorite == selected_string) {
-+                    has_favorite = true;
-+                    break;
-+                }
-+            }
-+            if (!has_favorite) {
-+                m_emojier_favorites += selected_string;
-+                m_settings_emoji.set_strv("favorites", m_emojier_favorites);
-+            }
-+            return true;
-+        }
-+
-+        return false;
-+    }
-+
-+
-+    private void emojier_focus_commit() {
-+        if (m_emojier == null)
-+            return;
-+        string selected_string = m_emojier.get_selected_string();
-+        string prev_context_path = m_emojier.get_input_context_path();
-+        if (selected_string == null &&
-+            prev_context_path != "" &&
-+            m_emojier.is_running()) {
-+            var context = GLib.MainContext.default();
-+            if (m_emojier_focus_commit_text_id > 0 &&
-+                context.find_source_by_id(m_emojier_focus_commit_text_id)
-+                        != null) {
-+                GLib.Source.remove(m_emojier_focus_commit_text_id);
-+            }
-+            m_emojier_focus_commit_text_id = GLib.Timeout.add(100, () => {
-+                // focus_in is comming before switcher returns
-+                emojier_focus_commit_real();
-+                m_emojier_focus_commit_text_id = -1;
-+                return false;
-+            });
-+        } else {
-+            if (emojier_focus_commit_real()) {
-+                var context = GLib.MainContext.default();
-+                if (m_emojier_focus_commit_text_id > 0 &&
-+                    context.find_source_by_id(m_emojier_focus_commit_text_id)
-+                            != null) {
-+                    GLib.Source.remove(m_emojier_focus_commit_text_id);
-+                }
-+                m_emojier_focus_commit_text_id = -1;
-+            }
-+        }
-+    }
-+
-+
-+    public override void focus_in(string input_context_path) {
-+        m_current_context_path = input_context_path;
-+
-+        /* 'fake' input context is named as 
-+         * '/org/freedesktop/IBus/InputContext_1' and always send in
-+         * focus-out events by ibus-daemon for the global engine mode.
-+         * Now ibus-daemon assumes to always use the global engine.
-+         * But this event should not be used for modal dialogs
-+         * such as Switcher.
-+         */
-+        if (!input_context_path.has_suffix("InputContext_1")) {
-+            m_real_current_context_path = m_current_context_path;
-+            this.emojier_focus_commit();
-+        }
-+    }
-+
-+
-+    public override void focus_out(string input_context_path) {
-+        m_current_context_path = "";
-+    }
-+
-+
-+    public override void panel_extension_received(GLib.Variant data) {
-+        IBus.XEvent? xevent = IBus.Serializable.deserialize_object(data)
-+                as IBus.XEvent;
-+        if (xevent == null) {
-+            warning ("Failed to deserialize IBusXEvent");
-+            return;
-+        }
-+        if (xevent.get_purpose() != "emoji") {
-+            string format = "The purpose %s is not implemented in PanelExtension";
-+            warning (format.printf(xevent.get_purpose()));
-+            return;
-+        }
-+        Gdk.EventType event_type;
-+        if (xevent.get_event_type() == IBus.XEventType.KEY_PRESS) {
-+            event_type = Gdk.EventType.KEY_PRESS;
-+        } else if (xevent.get_event_type() == IBus.XEventType.KEY_RELEASE) {
-+            event_type = Gdk.EventType.KEY_RELEASE;
-+        } else {
-+            warning ("Not supported type %d".printf(xevent.get_event_type()));
-+            return;
-+        }
-+        Gdk.Event event = new Gdk.Event(event_type);
-+        event.key.time = xevent.get_time();
-+        Gdk.Display? display = Gdk.Display.get_default();
-+        X.Window xid = xevent.get_window();
-+        Gdk.X11.Window window;
-+        window = Gdk.X11.Window.lookup_for_display(
-+                display as Gdk.X11.Display, xid);
-+        if (window != null) {
-+            event.key.window = window;
-+        } else {
-+            window = new Gdk.X11.Window.foreign_for_display(
-+                    display as Gdk.X11.Display, xid);
-+            event.key.window = window;
-+        }
-+        handle_emoji_typing(event);
-+    }
-+}
--- 
-2.14.3
-
-From 366963d57d1468914611c71929cc64c83be9affd Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Piotr=20Dr=C4=85g?= <piotrdrag@gmail.com>
-Date: Tue, 20 Feb 2018 18:57:32 +0900
-Subject: [PATCH] Fix typos in translatable strings
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-BUG=https://github.com/ibus/ibus/pull/1983
-R=Shawn.P.Huang@gmail.com
-
-Review URL: https://codereview.appspot.com/333670043
-
-Patch from Piotr Drąg <piotrdrag@gmail.com>.
----
- data/ibus.schemas.in    | 6 +++---
- ui/gtk3/emojierapp.vala | 2 +-
- 2 files changed, 4 insertions(+), 4 deletions(-)
-
-diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in
-index 278362d7..4523ccc4 100644
---- a/data/ibus.schemas.in
-+++ b/data/ibus.schemas.in
-@@ -64,7 +64,7 @@
-       <default>[ara,bg,cz,dev,gr,gur,in,jp(kana),mal,mkd,ru,ua]</default>
-       <locale name="C">
-         <short>Latin layouts which have no ASCII</short>
--           <long>US layout is appended to the latin layouts. variant can be
-+           <long>US layout is appended to the Latin layouts. variant can be
-                  omitted.
-            </long>
-       </locale>
-@@ -299,7 +299,7 @@
-                   and blue, 3. a RGB color in form 'rgb(r,g,b)' or
-                   4. a RGBA color in form 'rgba(r,g,b,a)' where 'r',
-                   'g', and 'b' are either integers in the range 0 to 255
--                  or precentage values in the range 0% to 100%, and
-+                  or percentage values in the range 0% to 100%, and
-                   'a' is a floating point value in the range 0 to 1
-                   of the alpha.</long>
-       </locale>
-@@ -373,7 +373,7 @@
-       <default>Monospace 16</default>
-       <locale name="C">
-         <short>Custom font</short>
--	    <long>Custom font name for emoji chracters on emoji dialog</long>
-+	    <long>Custom font name for emoji characters on emoji dialog</long>
-       </locale>
-     </schema>
-     <schema>
-diff --git a/ui/gtk3/emojierapp.vala b/ui/gtk3/emojierapp.vala
-index d816352e..efedf344 100644
---- a/ui/gtk3/emojierapp.vala
-+++ b/ui/gtk3/emojierapp.vala
-@@ -94,7 +94,7 @@ public class EmojiApplication : Application {
-               /* TRANSLATORS: "FONT" should be capital and translatable.
-                * It's used for an argument command --font=FONT
-                */
--              N_("\"FONT\" for emoji chracters on emoji dialog"),
-+              N_("\"FONT\" for emoji characters on emoji dialog"),
-               N_("FONT") },
-             { "lang", 0, 0, OptionArg.STRING, out annotation_lang,
-               /* TRANSLATORS: "LANG" should be capital and translatable.
--- 
-2.14.3
-
-From d1ebb3d77ebfe8f58188261c383d8122e2125fe6 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Wed, 21 Feb 2018 12:12:11 +0900
-Subject: [PATCH] ui/gtk3: Show code points on Unicode name list dialog
-
-The code points are useful since the list has many names.
-
-R=Shawn.P.Huang@gmail.com
-
-Review URL: https://codereview.appspot.com/340770043
----
- ui/gtk3/emojier.vala | 14 ++++++++++++--
- 1 file changed, 12 insertions(+), 2 deletions(-)
-
-diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
-index 0bf34da8..c85dfa86 100644
---- a/ui/gtk3/emojier.vala
-+++ b/ui/gtk3/emojier.vala
-@@ -198,7 +198,8 @@ public class IBusEmojier : Gtk.ApplicationWindow {
-     private class EPaddedLabelBox : Gtk.Box {
-         public EPaddedLabelBox(string          text,
-                                Gtk.Align       align,
--                               TravelDirection direction=TravelDirection.NONE) {
-+                               TravelDirection direction=TravelDirection.NONE,
-+                               string?         caption=null) {
-             GLib.Object(
-                 name : "IBusEmojierPaddedLabelBox",
-                 orientation : Gtk.Orientation.HORIZONTAL,
-@@ -218,6 +219,11 @@ public class IBusEmojier : Gtk.ApplicationWindow {
-             }
-             EPaddedLabel label = new EPaddedLabel(text, align);
-             pack_start(label, true, true, 0);
-+            if (caption != null) {
-+                EPaddedLabel label_r = new EPaddedLabel(caption,
-+                                                        Gtk.Align.END);
-+                pack_end(label_r, true, true, 0);
-+            }
-         }
-     }
-     private class ETitleLabelBox : Gtk.HeaderBar {
-@@ -979,9 +985,13 @@ public class IBusEmojier : Gtk.ApplicationWindow {
-         uint n = 0;
-         foreach (unowned IBus.UnicodeBlock block in m_unicode_block_list) {
-             string name = block.get_name();
-+            string caption = "U+%08X".printf(block.get_start());
-             EBoxRow row = new EBoxRow(name);
-             EPaddedLabelBox widget =
--                    new EPaddedLabelBox(_(name), Gtk.Align.CENTER);
-+                    new EPaddedLabelBox(_(name),
-+                                        Gtk.Align.CENTER,
-+                                        TravelDirection.NONE,
-+                                        caption);
-             row.add(widget);
-             m_list_box.add(row);
-             if (n++ == m_category_active_index) {
--- 
-2.14.3
-
-From fc54b0c051c2eb4a2c1f836b1415def00314cfc1 Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Wed, 21 Feb 2018 12:17:31 +0900
-Subject: [PATCH] ui/gtk3: Load Unicode data when open the dialog by
- default
-
-The emoji data requires about 10MB and the Unicode data requires about 15MB.
-Now the emoji data is loaded at the time of startup and the Unicode data
-is loaded if users open the dialog at the beginning.
-The settings can be customized with gsettings command and the keys
-of 'load-emoji-at-startup' and 'load-unicode-at-startup' in
-'org.freedesktop.ibus.panel.emoji' schema.
-
-Review URL: https://codereview.appspot.com/340780043
----
- data/ibus.schemas.in      | 32 ++++++++++++++++++++++++++++++++
- ui/gtk3/panelbinding.vala | 41 +++++++++++++++++++++++++++++++++++++++--
- 2 files changed, 71 insertions(+), 2 deletions(-)
-
-diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in
-index 4523ccc4..3c6b6f69 100644
---- a/data/ibus.schemas.in
-+++ b/data/ibus.schemas.in
-@@ -455,6 +455,38 @@
-                   </long>
-       </locale>
-     </schema>
-+    <schema>
-+      <key>/schemas/desktop/ibus/panel/emoji/load-emoji-at-startup</key>
-+      <applyto>/desktop/ibus/panel/emoji/load-emoji-at-startup</applyto>
-+      <owner>ibus</owner>
-+      <type>bool</type>
-+      <default>true</default>
-+      <locale name="C">
-+        <short>Load the emoji data at the time of startup</short>
-+	    <long>Load the emoji data at the time of startup if true.
-+                  About 10MB memory is needed to load the data.
-+                  Load the emoji data when open the emoji dialog at the
-+                  beginning if false.
-+            </long>
-+      </locale>
-+    </schema>
-+    <schema>
-+      <key>/schemas/desktop/ibus/panel/emoji/load-unicode-at-startup</key>
-+      <applyto>/desktop/ibus/panel/emoji/load-unicode-at-startup</applyto>
-+      <owner>ibus</owner>
-+      <type>bool</type>
-+      <default>false</default>
-+      <locale name="C">
-+        <short>Load the Unicode data at the time of startup</short>
-+	    <long>Load the Unicode data at the time of startup if true.
-+                  About 15MB memory is needed to load the data.
-+                  Load the Unicode data when open the emoji dialog at the
-+                  beginning if false.
-+                  The Unicode data is always loaded after the emoji data
-+                  is loaded even if true.
-+            </long>
-+      </locale>
-+    </schema>
-     <schema>
-       <key>/schemas/desktop/ibus/general/embed_preedit_text</key>
-       <applyto>/desktop/ibus/general/embed_preedit_text</applyto>
-diff --git a/ui/gtk3/panelbinding.vala b/ui/gtk3/panelbinding.vala
-index 50700121..1fbf6cfc 100644
---- a/ui/gtk3/panelbinding.vala
-+++ b/ui/gtk3/panelbinding.vala
-@@ -35,6 +35,10 @@ class PanelBinding : IBus.PanelService {
-     private const uint PRELOAD_ENGINES_DELAY_TIME = 30000;
-     private GLib.List<BindingCommon.Keybinding> m_keybindings =
-             new GLib.List<BindingCommon.Keybinding>();
-+    private bool m_load_emoji_at_startup;
-+    private bool m_loaded_emoji = false;
-+    private bool m_load_unicode_at_startup;
-+    private bool m_loaded_unicode = false;
- 
-     public PanelBinding(IBus.Bus bus) {
-         GLib.assert(bus.is_connected());
-@@ -109,6 +113,14 @@ class PanelBinding : IBus.PanelService {
-         m_settings_emoji.changed["partial-match-condition"].connect((key) => {
-                 set_emoji_partial_match();
-         });
-+
-+        m_settings_emoji.changed["load-emoji-at-startup"].connect((key) => {
-+                set_load_emoji_at_startup();
-+        });
-+
-+        m_settings_emoji.changed["load-unicode-at-startup"].connect((key) => {
-+                set_load_unicode_at_startup();
-+        });
-     }
- 
- 
-@@ -148,7 +160,11 @@ class PanelBinding : IBus.PanelService {
-             IBusEmojier.set_annotation_lang(
-                     m_settings_emoji.get_string("lang"));
-             m_emojier_set_emoji_lang_id = 0;
--            IBusEmojier.load_unicode_dict();
-+            m_loaded_emoji = true;
-+            if (m_load_unicode_at_startup && !m_loaded_unicode) {
-+                IBusEmojier.load_unicode_dict();
-+                m_loaded_unicode = true;
-+            }
-             return false;
-         });
-     }
-@@ -164,7 +180,21 @@ class PanelBinding : IBus.PanelService {
-     }
- 
- 
-+    private void set_load_emoji_at_startup() {
-+        m_load_emoji_at_startup =
-+            m_settings_emoji.get_boolean("load-emoji-at-startup");
-+    }
-+
-+
-+    private void set_load_unicode_at_startup() {
-+        m_load_unicode_at_startup =
-+            m_settings_emoji.get_boolean("load-unicode-at-startup");
-+    }
-+
-     public void load_settings() {
-+
-+        set_load_emoji_at_startup();
-+        set_load_unicode_at_startup();
-         BindingCommon.unbind_switch_shortcut(BindingCommon.KeyEventFuncType.ANY,
-                                              m_keybindings);
-         bind_emoji_shortcut();
-@@ -172,7 +202,8 @@ class PanelBinding : IBus.PanelService {
-                                       m_settings_emoji,
-                                       ref m_css_provider);
-         set_emoji_favorites();
--        set_emoji_lang();
-+        if (m_load_emoji_at_startup && !m_loaded_emoji)
-+            set_emoji_lang();
-         set_emoji_partial_match();
-     }
- 
-@@ -191,6 +222,12 @@ class PanelBinding : IBus.PanelService {
- 
- 
-     private void show_emojier(Gdk.Event event) {
-+        if (!m_loaded_emoji)
-+            set_emoji_lang();
-+        if (!m_loaded_unicode && m_loaded_emoji) {
-+            IBusEmojier.load_unicode_dict();
-+            m_loaded_unicode = true;
-+        }
-         m_emojier = new IBusEmojier();
-         string emoji = m_emojier.run(m_real_current_context_path, event);
-         if (emoji == null) {
--- 
-2.14.3
-
-From 7ccbd21ca7d163cdd71dc6c0405d8a32d63d324d Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Tue, 27 Feb 2018 14:06:30 +0900
-Subject: [PATCH 1/2] Disable panel extension for gdm user
-
-The gdm user's process is also running while the login user's process
-is running so the double panel extensions are better to be avoided for
-the memory usage.
-
-Review URL: https://codereview.appspot.com/340330044
----
- bus/main.c | 49 ++++++++++++++++++++++++++++++-------------------
- 1 file changed, 30 insertions(+), 19 deletions(-)
-
-diff --git a/bus/main.c b/bus/main.c
-index 5b2589b1..fe603778 100644
---- a/bus/main.c
-+++ b/bus/main.c
-@@ -47,6 +47,10 @@ static gchar *panel_extension = "default";
- static gchar *config = "default";
- static gchar *desktop = "gnome";
- 
-+static gchar *panel_extension_disable_users[] = {
-+    "gdm"
-+};
-+
- static void
- show_version_and_quit (void)
- {
-@@ -170,6 +174,9 @@ _sig_usr2_handler (int sig)
- gint
- main (gint argc, gchar **argv)
- {
-+    int i;
-+    const gchar *username = ibus_get_user_name ();
-+
-     setlocale (LC_ALL, "");
- 
-     GOptionContext *context = g_option_context_new ("- ibus daemon");
-@@ -194,9 +201,7 @@ main (gint argc, gchar **argv)
- 
-     /* check uid */
-     {
--        const gchar *username = ibus_get_user_name ();
--        uid_t uid = getuid ();
--        struct passwd *pwd = getpwuid (uid);
-+        struct passwd *pwd = getpwuid (getuid ());
- 
-         if (pwd == NULL || g_strcmp0 (pwd->pw_name, username) != 0) {
-             g_printerr ("Please run ibus-daemon with login user! Do not run ibus-daemon with sudo or su.\n");
-@@ -237,6 +242,12 @@ main (gint argc, gchar **argv)
-     }
- 
-     bus_server_init ();
-+    for (i = 0; i < G_N_ELEMENTS(panel_extension_disable_users); i++) {
-+        if (!g_strcmp0 (username, panel_extension_disable_users[i]) != 0) {
-+            panel_extension = "disable";
-+            break;
-+        }
-+    }
-     if (!single) {
-         /* execute config component */
-         if (g_strcmp0 (config, "default") == 0) {
-@@ -271,25 +282,25 @@ main (gint argc, gchar **argv)
-             if (!execute_cmdline (panel))
-                 exit (-1);
-         }
-+    }
- 
- #ifdef EMOJI_DICT
--        if (g_strcmp0 (panel_extension, "default") == 0) {
--            BusComponent *component;
--            component = bus_ibus_impl_lookup_component_by_name (
--                    BUS_DEFAULT_IBUS, IBUS_SERVICE_PANEL_EXTENSION);
--            if (component) {
--                bus_component_set_restart (component, restart);
--            }
--            if (component == NULL ||
--                !bus_component_start (component, g_verbose)) {
--                g_printerr ("Can not execute default panel program\n");
--                exit (-1);
--            }
--        } else if (g_strcmp0 (panel_extension, "disable") != 0 &&
--                   g_strcmp0 (panel_extension, "") != 0) {
--            if (!execute_cmdline (panel_extension))
--                exit (-1);
-+    if (g_strcmp0 (panel_extension, "default") == 0) {
-+        BusComponent *component;
-+        component = bus_ibus_impl_lookup_component_by_name (
-+                BUS_DEFAULT_IBUS, IBUS_SERVICE_PANEL_EXTENSION);
-+        if (component) {
-+            bus_component_set_restart (component, restart);
-+        }
-+        if (component == NULL ||
-+            !bus_component_start (component, g_verbose)) {
-+            g_printerr ("Can not execute default panel program\n");
-+            exit (-1);
-         }
-+    } else if (g_strcmp0 (panel_extension, "disable") != 0 &&
-+               g_strcmp0 (panel_extension, "") != 0) {
-+        if (!execute_cmdline (panel_extension))
-+            exit (-1);
-     }
- #endif
- 
--- 
-2.14.3
-
-From c57b7c34d75871db172e023b0094b979000f62fc Mon Sep 17 00:00:00 2001
-From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Tue, 27 Feb 2018 14:10:29 +0900
-Subject: [PATCH 2/2] Enable emoji keybinding in Wayland
-
-XI2 keybinding does not work for the root window in Wayland because
-of a security issue maybe. Now I think to move the keybinding in
-ibus-extension-gtk3 to each IBusEngine.
-
-FIXME: Unfortunatelly gtk_get_current_event_time() cannot get time
-for the delayed DBus events and gtk_window_move() does not work for
-GtkDialog without a parent window in Wayland.
-
-R=Shawn.P.Huang@gmail.com
-
-Review URL: https://codereview.appspot.com/333700043
----
- bus/engineproxy.c         |  19 +-
- bus/ibusimpl.c            |  47 +++-
- bus/inputcontext.c        |  29 ++-
- src/Makefile.am           |   2 +
- src/ibus.h                |   1 +
- src/ibusaccelgroup.c      | 529 ++++++++++++++++++++++++++++++++++++++++++++++
- src/ibusaccelgroup.h      |  51 +++++
- src/ibusengine.c          | 179 +++++++++++++++-
- ui/gtk3/emojier.vala      |   1 +
- ui/gtk3/extension.vala    |   5 +-
- ui/gtk3/panelbinding.vala |  65 ++----
- 11 files changed, 863 insertions(+), 65 deletions(-)
- create mode 100644 src/ibusaccelgroup.c
- create mode 100644 src/ibusaccelgroup.h
-
-diff --git a/bus/engineproxy.c b/bus/engineproxy.c
-index cd4d54fd..175aec56 100644
---- a/bus/engineproxy.c
-+++ b/bus/engineproxy.c
-@@ -2,7 +2,7 @@
- /* vim:set et sts=4: */
- /* ibus - The Input Bus
-  * Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
-- * Copyright (C) 2015-2016 Takao Fujiwara <takao.fujiwara1@gmail.com>
-+ * Copyright (C) 2015-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
-  * Copyright (C) 2008-2016 Red Hat, Inc.
-  *
-  * This library is free software; you can redistribute it and/or
-@@ -90,6 +90,7 @@ enum {
-     CURSOR_DOWN_LOOKUP_TABLE,
-     REGISTER_PROPERTIES,
-     UPDATE_PROPERTY,
-+    PANEL_EXTENSION,
-     LAST_SIGNAL,
- };
- 
-@@ -370,6 +371,17 @@ bus_engine_proxy_class_init (BusEngineProxyClass *class)
-             1,
-             IBUS_TYPE_PROPERTY);
- 
-+    engine_signals[PANEL_EXTENSION] =
-+        g_signal_new (I_("panel-extension"),
-+            G_TYPE_FROM_CLASS (class),
-+            G_SIGNAL_RUN_LAST,
-+            0,
-+            NULL, NULL,
-+            bus_marshal_VOID__VARIANT,
-+            G_TYPE_NONE,
-+            1,
-+            G_TYPE_VARIANT);
-+
-     text_empty = ibus_text_new_from_static_string ("");
-     g_object_ref_sink (text_empty);
- 
-@@ -631,6 +643,11 @@ bus_engine_proxy_g_signal (GDBusProxy  *proxy,
-         return;
-     }
- 
-+    if (g_strcmp0 (signal_name, "PanelExtension") == 0) {
-+        g_signal_emit (engine, engine_signals[PANEL_EXTENSION], 0, parameters);
-+        return;
-+    }
-+
-     g_return_if_reached ();
- }
- 
-diff --git a/bus/ibusimpl.c b/bus/ibusimpl.c
-index 58d205cf..a4ce3d9d 100644
---- a/bus/ibusimpl.c
-+++ b/bus/ibusimpl.c
-@@ -302,9 +302,8 @@ _panel_destroy_cb (BusPanelProxy *panel,
- }
- 
- static void
--_panel_panel_extension_cb (BusPanelProxy *panel,
--                           GVariant      *parameters,
--                           BusIBusImpl  *ibus)
-+bus_ibus_impl_panel_extension_received (BusIBusImpl *ibus,
-+                                        GVariant    *parameters)
- {
-     if (!ibus->extension) {
-         g_warning ("Panel extension is not running.");
-@@ -323,6 +322,14 @@ _panel_panel_extension_cb (BusPanelProxy *panel,
-                        -1, NULL, NULL, NULL);
- }
- 
-+static void
-+_panel_panel_extension_cb (BusPanelProxy *panel,
-+                           GVariant      *parameters,
-+                           BusIBusImpl  *ibus)
-+{
-+    bus_ibus_impl_panel_extension_received (ibus, parameters);
-+}
-+
- static void
- _registry_changed_cb (IBusRegistry *registry,
-                       BusIBusImpl  *ibus)
-@@ -642,6 +649,21 @@ bus_ibus_impl_set_context_engine_from_desc (BusIBusImpl     *ibus,
-                                           NULL);
- }
- 
-+static void
-+_context_panel_extension_cb (BusInputContext *context,
-+                             GVariant        *parameters,
-+                             BusIBusImpl     *ibus)
-+{
-+    bus_ibus_impl_panel_extension_received (ibus, parameters);
-+}
-+
-+const static struct {
-+    const gchar *name;
-+    GCallback    callback;
-+} context_signals [] = {
-+    { "panel-extension",             G_CALLBACK (_context_panel_extension_cb) }
-+};
-+
- /**
-  * bus_ibus_impl_set_focused_context:
-  *
-@@ -651,6 +673,11 @@ static void
- bus_ibus_impl_set_focused_context (BusIBusImpl     *ibus,
-                                    BusInputContext *context)
- {
-+    gint i;
-+    BusEngineProxy *engine = NULL;
-+    guint purpose = 0;
-+    guint hints = 0;
-+
-     g_assert (BUS_IS_IBUS_IMPL (ibus));
-     g_assert (context == NULL || BUS_IS_INPUT_CONTEXT (context));
-     g_assert (context == NULL || bus_input_context_get_capabilities (context) & IBUS_CAP_FOCUS);
-@@ -660,10 +687,6 @@ bus_ibus_impl_set_focused_context (BusIBusImpl     *ibus,
-         return;
-     }
- 
--    BusEngineProxy *engine = NULL;
--    guint purpose = 0;
--    guint hints = 0;
--
-     if (ibus->focused_context) {
-         if (ibus->use_global_engine) {
-             /* dettach engine from the focused context */
-@@ -681,6 +704,10 @@ bus_ibus_impl_set_focused_context (BusIBusImpl     *ibus,
- 
-         bus_input_context_get_content_type (ibus->focused_context,
-                                             &purpose, &hints);
-+        for (i = 0; i < G_N_ELEMENTS(context_signals); i++) {
-+            g_signal_handlers_disconnect_by_func (ibus->focused_context,
-+                    context_signals[i].callback, ibus);
-+        }
-         g_object_unref (ibus->focused_context);
-         ibus->focused_context = NULL;
-     }
-@@ -698,6 +725,12 @@ bus_ibus_impl_set_focused_context (BusIBusImpl     *ibus,
-             bus_input_context_set_engine (context, engine);
-             bus_input_context_enable (context);
-         }
-+        for (i = 0; i < G_N_ELEMENTS(context_signals); i++) {
-+            g_signal_connect (ibus->focused_context,
-+                              context_signals[i].name,
-+                              context_signals[i].callback,
-+                              ibus);
-+        }
- 
-         if (ibus->panel != NULL)
-             bus_panel_proxy_focus_in (ibus->panel, context);
-diff --git a/bus/inputcontext.c b/bus/inputcontext.c
-index 4f2ecafc..a957d107 100644
---- a/bus/inputcontext.c
-+++ b/bus/inputcontext.c
-@@ -127,6 +127,7 @@ enum {
-     ENGINE_CHANGED,
-     REQUEST_ENGINE,
-     SET_CONTENT_TYPE,
-+    PANEL_EXTENSION,
-     LAST_SIGNAL,
- };
- 
-@@ -598,6 +599,17 @@ bus_input_context_class_init (BusInputContextClass *class)
-             G_TYPE_UINT,
-             G_TYPE_UINT);
- 
-+    context_signals[PANEL_EXTENSION] =
-+        g_signal_new (I_("panel-extension"),
-+            G_TYPE_FROM_CLASS (class),
-+            G_SIGNAL_RUN_LAST,
-+            0,
-+            NULL, NULL,
-+            bus_marshal_VOID__VARIANT,
-+            G_TYPE_NONE,
-+            1,
-+            G_TYPE_VARIANT);
-+
-     text_empty = ibus_text_new_from_string ("");
-     g_object_ref_sink (text_empty);
-     lookup_table_empty = ibus_lookup_table_new (9 /* page size */, 0, FALSE, FALSE);
-@@ -2102,6 +2114,20 @@ _engine_update_property_cb (BusEngineProxy  *engine,
-     bus_input_context_update_property (context, prop);
- }
- 
-+/**
-+ * _engine_panel_extension_cb:
-+ *
-+ * A function to be called when "panel-extension" glib signal is sent
-+ * from the engine object.
-+ */
-+static void
-+_engine_panel_extension_cb (BusEngineProxy  *engine,
-+                            GVariant        *parameters,
-+                            BusInputContext *context)
-+{
-+    g_signal_emit (context, context_signals[PANEL_EXTENSION], 0, parameters);
-+}
-+
- #define DEFINE_FUNCTION(name)                                   \
-     static void                                                 \
-     _engine_##name##_cb (BusEngineProxy   *engine,              \
-@@ -2244,7 +2270,8 @@ const static struct {
-     { "cursor-down-lookup-table", G_CALLBACK (_engine_cursor_down_lookup_table_cb) },
-     { "register-properties",      G_CALLBACK (_engine_register_properties_cb) },
-     { "update-property",          G_CALLBACK (_engine_update_property_cb) },
--    { "destroy",                  G_CALLBACK (_engine_destroy_cb) },
-+    { "panel-extension",          G_CALLBACK (_engine_panel_extension_cb) },
-+    { "destroy",                  G_CALLBACK (_engine_destroy_cb) }
- };
- 
- static void
-diff --git a/src/Makefile.am b/src/Makefile.am
-index 72ec05ab..6a62e0f0 100644
---- a/src/Makefile.am
-+++ b/src/Makefile.am
-@@ -72,6 +72,7 @@ libibus_1_0_la_LDFLAGS =            \
-     $(NULL)
- 
- ibus_sources =              \
-+    ibusaccelgroup.c        \
-     ibusattribute.c         \
-     ibusattrlist.c          \
-     ibusbus.c               \
-@@ -122,6 +123,7 @@ ibus_enumtypes_sources =    \
-     $(NULL)
- ibus_headers =              \
-     ibus.h                  \
-+    ibusaccelgroup.h        \
-     ibusattribute.h         \
-     ibusattrlist.h          \
-     ibusbus.h               \
-diff --git a/src/ibus.h b/src/ibus.h
-index b15dded9..256d57ba 100644
---- a/src/ibus.h
-+++ b/src/ibus.h
-@@ -60,6 +60,7 @@
- #include <ibusemoji.h>
- #include <ibusunicode.h>
- #include <ibusxevent.h>
-+#include <ibusaccelgroup.h>
- 
- #ifndef IBUS_DISABLE_DEPRECATED
- #include <ibuskeysyms-compat.h>
-diff --git a/src/ibusaccelgroup.c b/src/ibusaccelgroup.c
-new file mode 100644
-index 00000000..8a81597e
---- /dev/null
-+++ b/src/ibusaccelgroup.c
-@@ -0,0 +1,529 @@
-+/* GTK - The GIMP Toolkit
-+ * Copyright (C) 1998, 2001 Tim Janik
-+ *
-+ * This library is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU Lesser General Public
-+ * License as published by the Free Software Foundation; either
-+ * version 2 of the License, or (at your option) any later version.
-+ *
-+ * This library is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-+ * Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public
-+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
-+ */
-+
-+/*
-+ * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
-+ * file for a list of people on the GTK+ Team.  See the ChangeLog
-+ * files for a list of changes.  These files are distributed with
-+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
-+ */
-+
-+#include "config.h"
-+#include <string.h>
-+#include <stdlib.h>
-+
-+#include "ibusaccelgroup.h"
-+#include "ibuskeys.h"
-+#include "ibuskeysyms.h"
-+
-+
-+/* for _gtk_get_primary_accel_mod() */
-+#define _IBUS_GET_PRIMARY_ACCEL_MOD IBUS_CONTROL_MASK
-+
-+/**
-+ * SECTION: ibusaccelgroup
-+ * @short_description: Groups of global keyboard accelerators for an
-+ *     entire GtkWindow
-+ * @title: Accelerator Groups
-+ * @stability: Unstable
-+ *
-+ * Provides ibus_accelerator_parse()
-+ */
-+
-+
-+/**
-+ * ibus_accelerator_valid:
-+ * @keyval: a GDK keyval
-+ * @modifiers: modifier mask
-+ *
-+ * Determines whether a given keyval and modifier mask constitute
-+ * a valid keyboard accelerator. For example, the #IBUS_KEY_a keyval
-+ * plus #IBUS_CONTROL_MASK is valid - this is a “Ctrl+a” accelerator.
-+ * But, you can't, for instance, use the #IBUS_KEY_Control_L keyval
-+ * as an accelerator.
-+ *
-+ * Returns: %TRUE if the accelerator is valid
-+ */
-+gboolean
-+ibus_accelerator_valid (guint           keyval,
-+                        IBusModifierType modifiers)
-+{
-+    static const guint invalid_accelerator_vals[] = {
-+        IBUS_KEY_Shift_L, IBUS_KEY_Shift_R, IBUS_KEY_Shift_Lock,
-+        IBUS_KEY_Caps_Lock, IBUS_KEY_ISO_Lock, IBUS_KEY_Control_L,
-+        IBUS_KEY_Control_R, IBUS_KEY_Meta_L, IBUS_KEY_Meta_R,
-+        IBUS_KEY_Alt_L, IBUS_KEY_Alt_R, IBUS_KEY_Super_L, IBUS_KEY_Super_R,
-+        IBUS_KEY_Hyper_L, IBUS_KEY_Hyper_R, IBUS_KEY_ISO_Level3_Shift,
-+        IBUS_KEY_ISO_Next_Group, IBUS_KEY_ISO_Prev_Group,
-+        IBUS_KEY_ISO_First_Group, IBUS_KEY_ISO_Last_Group,
-+        IBUS_KEY_Mode_switch, IBUS_KEY_Num_Lock, IBUS_KEY_Multi_key,
-+        IBUS_KEY_Scroll_Lock, IBUS_KEY_Sys_Req,
-+        IBUS_KEY_Tab, IBUS_KEY_ISO_Left_Tab, IBUS_KEY_KP_Tab,
-+        IBUS_KEY_First_Virtual_Screen, IBUS_KEY_Prev_Virtual_Screen,
-+        IBUS_KEY_Next_Virtual_Screen, IBUS_KEY_Last_Virtual_Screen,
-+        IBUS_KEY_Terminate_Server, IBUS_KEY_AudibleBell_Enable,
-+        0
-+    };
-+    static const guint invalid_unmodified_vals[] = {
-+        IBUS_KEY_Up, IBUS_KEY_Down, IBUS_KEY_Left, IBUS_KEY_Right,
-+        IBUS_KEY_KP_Up, IBUS_KEY_KP_Down, IBUS_KEY_KP_Left, IBUS_KEY_KP_Right,
-+        0
-+    };
-+    const guint *ac_val;
-+
-+    modifiers &= IBUS_MODIFIER_MASK;
-+
-+    if (keyval <= 0xFF)
-+        return keyval >= 0x20;
-+
-+    ac_val = invalid_accelerator_vals;
-+    while (*ac_val) {
-+        if (keyval == *ac_val++)
-+            return FALSE;
-+    }
-+
-+    if (!modifiers) {
-+        ac_val = invalid_unmodified_vals;
-+        while (*ac_val) {
-+            if (keyval == *ac_val++)
-+                return FALSE;
-+        }
-+    }
-+
-+    return TRUE;
-+}
-+
-+static inline gboolean
-+is_alt (const gchar *string)
-+{
-+    return ((string[0] == '<') &&
-+            (string[1] == 'a' || string[1] == 'A') &&
-+            (string[2] == 'l' || string[2] == 'L') &&
-+            (string[3] == 't' || string[3] == 'T') &&
-+            (string[4] == '>'));
-+}
-+
-+static inline gboolean
-+is_ctl (const gchar *string)
-+{
-+    return ((string[0] == '<') &&
-+            (string[1] == 'c' || string[1] == 'C') &&
-+            (string[2] == 't' || string[2] == 'T') &&
-+            (string[3] == 'l' || string[3] == 'L') &&
-+            (string[4] == '>'));
-+}
-+
-+static inline gboolean
-+is_modx (const gchar *string)
-+{
-+    return ((string[0] == '<') &&
-+            (string[1] == 'm' || string[1] == 'M') &&
-+            (string[2] == 'o' || string[2] == 'O') &&
-+            (string[3] == 'd' || string[3] == 'D') &&
-+            (string[4] >= '1' && string[4] <= '5') &&
-+            (string[5] == '>'));
-+}
-+
-+static inline gboolean
-+is_ctrl (const gchar *string)
-+{
-+    return ((string[0] == '<') &&
-+            (string[1] == 'c' || string[1] == 'C') &&
-+            (string[2] == 't' || string[2] == 'T') &&
-+            (string[3] == 'r' || string[3] == 'R') &&
-+            (string[4] == 'l' || string[4] == 'L') &&
-+            (string[5] == '>'));
-+}
-+
-+static inline gboolean
-+is_shft (const gchar *string)
-+{
-+    return ((string[0] == '<') &&
-+            (string[1] == 's' || string[1] == 'S') &&
-+            (string[2] == 'h' || string[2] == 'H') &&
-+            (string[3] == 'f' || string[3] == 'F') &&
-+            (string[4] == 't' || string[4] == 'T') &&
-+            (string[5] == '>'));
-+}
-+
-+static inline gboolean
-+is_shift (const gchar *string)
-+{
-+    return ((string[0] == '<') &&
-+            (string[1] == 's' || string[1] == 'S') &&
-+            (string[2] == 'h' || string[2] == 'H') &&
-+            (string[3] == 'i' || string[3] == 'I') &&
-+            (string[4] == 'f' || string[4] == 'F') &&
-+            (string[5] == 't' || string[5] == 'T') &&
-+            (string[6] == '>'));
-+}
-+
-+static inline gboolean
-+is_control (const gchar *string)
-+{
-+    return ((string[0] == '<') &&
-+            (string[1] == 'c' || string[1] == 'C') &&
-+            (string[2] == 'o' || string[2] == 'O') &&
-+            (string[3] == 'n' || string[3] == 'N') &&
-+            (string[4] == 't' || string[4] == 'T') &&
-+            (string[5] == 'r' || string[5] == 'R') &&
-+            (string[6] == 'o' || string[6] == 'O') &&
-+            (string[7] == 'l' || string[7] == 'L') &&
-+            (string[8] == '>'));
-+}
-+
-+static inline gboolean
-+is_release (const gchar *string)
-+{
-+    return ((string[0] == '<') &&
-+            (string[1] == 'r' || string[1] == 'R') &&
-+            (string[2] == 'e' || string[2] == 'E') &&
-+            (string[3] == 'l' || string[3] == 'L') &&
-+            (string[4] == 'e' || string[4] == 'E') &&
-+            (string[5] == 'a' || string[5] == 'A') &&
-+            (string[6] == 's' || string[6] == 'S') &&
-+            (string[7] == 'e' || string[7] == 'E') &&
-+            (string[8] == '>'));
-+}
-+
-+static inline gboolean
-+is_meta (const gchar *string)
-+{
-+    return ((string[0] == '<') &&
-+            (string[1] == 'm' || string[1] == 'M') &&
-+            (string[2] == 'e' || string[2] == 'E') &&
-+            (string[3] == 't' || string[3] == 'T') &&
-+            (string[4] == 'a' || string[4] == 'A') &&
-+            (string[5] == '>'));
-+}
-+
-+static inline gboolean
-+is_super (const gchar *string)
-+{
-+    return ((string[0] == '<') &&
-+            (string[1] == 's' || string[1] == 'S') &&
-+            (string[2] == 'u' || string[2] == 'U') &&
-+            (string[3] == 'p' || string[3] == 'P') &&
-+            (string[4] == 'e' || string[4] == 'E') &&
-+            (string[5] == 'r' || string[5] == 'R') &&
-+            (string[6] == '>'));
-+}
-+
-+static inline gboolean
-+is_hyper (const gchar *string)
-+{
-+    return ((string[0] == '<') &&
-+            (string[1] == 'h' || string[1] == 'H') &&
-+            (string[2] == 'y' || string[2] == 'Y') &&
-+            (string[3] == 'p' || string[3] == 'P') &&
-+            (string[4] == 'e' || string[4] == 'E') &&
-+            (string[5] == 'r' || string[5] == 'R') &&
-+            (string[6] == '>'));
-+}
-+
-+static inline gboolean
-+is_primary (const gchar *string)
-+{
-+    return ((string[0] == '<') &&
-+            (string[1] == 'p' || string[1] == 'P') &&
-+            (string[2] == 'r' || string[2] == 'R') &&
-+            (string[3] == 'i' || string[3] == 'I') &&
-+            (string[4] == 'm' || string[4] == 'M') &&
-+            (string[5] == 'a' || string[5] == 'A') &&
-+            (string[6] == 'r' || string[6] == 'R') &&
-+            (string[7] == 'y' || string[7] == 'Y') &&
-+            (string[8] == '>'));
-+}
-+
-+static inline gboolean
-+is_keycode (const gchar *string)
-+{
-+    return (string[0] == '0' &&
-+            string[1] == 'x' &&
-+            g_ascii_isxdigit (string[2]) &&
-+            g_ascii_isxdigit (string[3]));
-+}
-+
-+/**
-+ * ibus_accelerator_parse:
-+ * @accelerator: string representing an accelerator
-+ * @accelerator_key: (out) (allow-none): return location for accelerator
-+ *     keyval, or %NULL
-+ * @accelerator_mods: (out) (allow-none): return location for accelerator
-+ *     modifier mask, %NULL
-+ *
-+ * Parses a string representing an accelerator. The format looks like
-+ * “<Control>a” or “<Shift><Alt>F1” or “<Release>z” (the last one is
-+ * for key release).
-+ *
-+ * The parser is fairly liberal and allows lower or upper case, and also
-+ * abbreviations such as “<Ctl>” and “<Ctrl>”. Key names are parsed using
-+ * gdk_keyval_from_name(). For character keys the name is not the symbol,
-+ * but the lowercase name, e.g. one would use “<Ctrl>minus” instead of
-+ * “<Ctrl>-”.
-+ *
-+ * If the parse fails, @accelerator_key and @accelerator_mods will
-+ * be set to 0 (zero).
-+ *
-+ * Since: 1.5.18
-+ */
-+void
-+ibus_accelerator_parse (const gchar      *accelerator,
-+                        guint            *accelerator_key,
-+                        IBusModifierType *accelerator_mods)
-+{
-+    guint keyval;
-+    IBusModifierType mods;
-+    gint len;
-+    gboolean error;
-+
-+    if (accelerator_key)
-+        *accelerator_key = 0;
-+    if (accelerator_mods)
-+        *accelerator_mods = 0;
-+    g_return_if_fail (accelerator != NULL);
-+
-+    error = FALSE;
-+    keyval = 0;
-+    mods = 0;
-+    len = strlen (accelerator);
-+    while (len) {
-+        if (*accelerator == '<') {
-+            if (len >= 9 && is_release (accelerator)) {
-+                accelerator += 9;
-+                len -= 9;
-+                mods |= IBUS_RELEASE_MASK;
-+            } else if (len >= 9 && is_primary (accelerator)) {
-+                accelerator += 9;
-+                len -= 9;
-+                mods |= _IBUS_GET_PRIMARY_ACCEL_MOD;
-+            } else if (len >= 9 && is_control (accelerator)) {
-+                accelerator += 9;
-+                len -= 9;
-+                mods |= IBUS_CONTROL_MASK;
-+            } else if (len >= 7 && is_shift (accelerator)) {
-+                accelerator += 7;
-+                len -= 7;
-+                mods |= IBUS_SHIFT_MASK;
-+            } else if (len >= 6 && is_shft (accelerator)) {
-+                accelerator += 6;
-+                len -= 6;
-+                mods |= IBUS_SHIFT_MASK;
-+            } else if (len >= 6 && is_ctrl (accelerator)) {
-+                accelerator += 6;
-+                len -= 6;
-+                mods |= IBUS_CONTROL_MASK;
-+            } else if (len >= 6 && is_modx (accelerator)) {
-+                static const guint mod_vals[] = {
-+                    IBUS_MOD1_MASK, IBUS_MOD2_MASK, IBUS_MOD3_MASK,
-+                    IBUS_MOD4_MASK, IBUS_MOD5_MASK
-+                };
-+
-+                len -= 6;
-+                accelerator += 4;
-+                mods |= mod_vals[*accelerator - '1'];
-+                accelerator += 2;
-+            } else if (len >= 5 && is_ctl (accelerator)) {
-+                accelerator += 5;
-+                len -= 5;
-+                mods |= IBUS_CONTROL_MASK;
-+            } else if (len >= 5 && is_alt (accelerator)) {
-+                accelerator += 5;
-+                len -= 5;
-+                mods |= IBUS_MOD1_MASK;
-+            } else if (len >= 6 && is_meta (accelerator)) {
-+                accelerator += 6;
-+                len -= 6;
-+                mods |= IBUS_META_MASK;
-+            } else if (len >= 7 && is_hyper (accelerator)) {
-+                accelerator += 7;
-+                len -= 7;
-+                mods |= IBUS_HYPER_MASK;
-+            } else if (len >= 7 && is_super (accelerator)) {
-+                accelerator += 7;
-+                len -= 7;
-+                mods |= IBUS_SUPER_MASK;
-+            } else {
-+                gchar last_ch;
-+
-+                last_ch = *accelerator;
-+                while (last_ch && last_ch != '>') {
-+                    last_ch = *accelerator;
-+                    accelerator += 1;
-+                    len -= 1;
-+                }
-+            }
-+        } else {
-+            if (len >= 4 && is_keycode (accelerator)) {
-+                /* There was a keycode in the string, but
-+                 * we cannot store it, so we have an error */
-+                error = TRUE;
-+                goto out;
-+            } else {
-+	        keyval = ibus_keyval_from_name (accelerator);
-+	        if (keyval == IBUS_KEY_VoidSymbol) {
-+	            error = TRUE;
-+	            goto out;
-+		}
-+	    }
-+
-+            accelerator += len;
-+            len -= len;
-+        }
-+    }
-+
-+out:
-+    if (error)
-+        keyval = mods = 0;
-+
-+    if (accelerator_key)
-+        *accelerator_key = ibus_keyval_to_lower (keyval);
-+    if (accelerator_mods)
-+        *accelerator_mods = mods;
-+}
-+
-+/**
-+ * ibus_accelerator_name:
-+ * @accelerator_key: accelerator keyval
-+ * @accelerator_mods: accelerator modifier mask
-+ *
-+ * Converts an accelerator keyval and modifier mask into a string
-+ * parseable by gtk_accelerator_parse(). For example, if you pass in
-+ * #IBUS_KEY_q and #IBUS_CONTROL_MASK, this function returns “<Control>q”.
-+ *
-+ * If you need to display accelerators in the user interface,
-+ * see gtk_accelerator_get_label().
-+ *
-+ * Returns: a newly-allocated accelerator name
-+ */
-+gchar*
-+ibus_accelerator_name (guint            accelerator_key,
-+                       IBusModifierType accelerator_mods)
-+{
-+   static const gchar text_release[] = "<Release>";
-+   static const gchar text_primary[] = "<Primary>";
-+   static const gchar text_shift[] = "<Shift>";
-+   static const gchar text_control[] = "<Control>";
-+   static const gchar text_mod1[] = "<Alt>";
-+   static const gchar text_mod2[] = "<Mod2>";
-+   static const gchar text_mod3[] = "<Mod3>";
-+   static const gchar text_mod4[] = "<Mod4>";
-+   static const gchar text_mod5[] = "<Mod5>";
-+   static const gchar text_meta[] = "<Meta>";
-+   static const gchar text_super[] = "<Super>";
-+   static const gchar text_hyper[] = "<Hyper>";
-+   IBusModifierType saved_mods;
-+   guint l;
-+   const gchar *keyval_name;
-+   gchar *accelerator;
-+
-+    accelerator_mods &= IBUS_MODIFIER_MASK;
-+
-+    keyval_name = ibus_keyval_name (ibus_keyval_to_lower (accelerator_key));
-+    if (!keyval_name)
-+        keyval_name = "";
-+
-+    saved_mods = accelerator_mods;
-+    l = 0;
-+    if (accelerator_mods & IBUS_RELEASE_MASK)
-+        l += sizeof (text_release) - 1;
-+    if (accelerator_mods & _IBUS_GET_PRIMARY_ACCEL_MOD) {
-+        l += sizeof (text_primary) - 1;
-+        /* consume the default accel */
-+        accelerator_mods &= ~_IBUS_GET_PRIMARY_ACCEL_MOD;
-+    }
-+    if (accelerator_mods & IBUS_SHIFT_MASK)
-+        l += sizeof (text_shift) - 1;
-+    if (accelerator_mods & IBUS_CONTROL_MASK)
-+        l += sizeof (text_control) - 1;
-+    if (accelerator_mods & IBUS_MOD1_MASK)
-+        l += sizeof (text_mod1) - 1;
-+    if (accelerator_mods & IBUS_MOD2_MASK)
-+        l += sizeof (text_mod2) - 1;
-+    if (accelerator_mods & IBUS_MOD3_MASK)
-+        l += sizeof (text_mod3) - 1;
-+    if (accelerator_mods & IBUS_MOD4_MASK)
-+        l += sizeof (text_mod4) - 1;
-+    if (accelerator_mods & IBUS_MOD5_MASK)
-+        l += sizeof (text_mod5) - 1;
-+    l += strlen (keyval_name);
-+    if (accelerator_mods & IBUS_META_MASK)
-+        l += sizeof (text_meta) - 1;
-+    if (accelerator_mods & IBUS_HYPER_MASK)
-+        l += sizeof (text_hyper) - 1;
-+    if (accelerator_mods & IBUS_SUPER_MASK)
-+        l += sizeof (text_super) - 1;
-+
-+    accelerator = g_new (gchar, l + 1);
-+
-+    accelerator_mods = saved_mods;
-+    l = 0;
-+    accelerator[l] = 0;
-+    if (accelerator_mods & IBUS_RELEASE_MASK) {
-+        strcpy (accelerator + l, text_release);
-+        l += sizeof (text_release) - 1;
-+    }
-+    if (accelerator_mods & _IBUS_GET_PRIMARY_ACCEL_MOD) {
-+        strcpy (accelerator + l, text_primary);
-+        l += sizeof (text_primary) - 1;
-+        /* consume the default accel */
-+        accelerator_mods &= ~_IBUS_GET_PRIMARY_ACCEL_MOD;
-+    }
-+    if (accelerator_mods & IBUS_SHIFT_MASK) {
-+        strcpy (accelerator + l, text_shift);
-+        l += sizeof (text_shift) - 1;
-+    }
-+    if (accelerator_mods & IBUS_CONTROL_MASK) {
-+        strcpy (accelerator + l, text_control);
-+        l += sizeof (text_control) - 1;
-+    }
-+    if (accelerator_mods & IBUS_MOD1_MASK) {
-+        strcpy (accelerator + l, text_mod1);
-+        l += sizeof (text_mod1) - 1;
-+    }
-+    if (accelerator_mods & IBUS_MOD2_MASK) {
-+        strcpy (accelerator + l, text_mod2);
-+        l += sizeof (text_mod2) - 1;
-+    }
-+    if (accelerator_mods & IBUS_MOD3_MASK) {
-+        strcpy (accelerator + l, text_mod3);
-+        l += sizeof (text_mod3) - 1;
-+    }
-+    if (accelerator_mods & IBUS_MOD4_MASK) {
-+        strcpy (accelerator + l, text_mod4);
-+        l += sizeof (text_mod4) - 1;
-+    }
-+    if (accelerator_mods & IBUS_MOD5_MASK) {
-+        strcpy (accelerator + l, text_mod5);
-+        l += sizeof (text_mod5) - 1;
-+    }
-+    if (accelerator_mods & IBUS_META_MASK) {
-+        strcpy (accelerator + l, text_meta);
-+        l += sizeof (text_meta) - 1;
-+    }
-+    if (accelerator_mods & IBUS_HYPER_MASK) {
-+        strcpy (accelerator + l, text_hyper);
-+        l += sizeof (text_hyper) - 1;
-+    }
-+    if (accelerator_mods & IBUS_SUPER_MASK) {
-+        strcpy (accelerator + l, text_super);
-+        l += sizeof (text_super) - 1;
-+    }
-+    strcpy (accelerator + l, keyval_name);
-+
-+    return accelerator;
-+}
-diff --git a/src/ibusaccelgroup.h b/src/ibusaccelgroup.h
-new file mode 100644
-index 00000000..cb38bee4
---- /dev/null
-+++ b/src/ibusaccelgroup.h
-@@ -0,0 +1,51 @@
-+/* GTK - The GIMP Toolkit
-+ * Copyright (C) 1998, 2001 Tim Janik
-+ *
-+ * This library is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU Lesser General Public
-+ * License as published by the Free Software Foundation; either
-+ * version 2 of the License, or (at your option) any later version.
-+ *
-+ * This library is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
-+ * Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public
-+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
-+ */
-+
-+/*
-+ * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
-+ * file for a list of people on the GTK+ Team.  See the ChangeLog
-+ * files for a list of changes.  These files are distributed with
-+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
-+ */
-+
-+#ifndef __IBUS_ACCEL_GROUP_H_
-+#define __IBUS_ACCEL_GROUP_H_
-+
-+
-+#if !defined (__IBUS_H_INSIDE__) && !defined (IBUS_COMPILATION)
-+#error "Only <ibus.h> can be included directly"
-+#endif
-+
-+#include <glib.h>
-+#include <ibustypes.h>
-+
-+G_BEGIN_DECLS
-+
-+
-+/* --- Accelerators--- */
-+gboolean ibus_accelerator_valid               (guint            keyval,
-+                                               IBusModifierType modifiers)
-+                                               G_GNUC_CONST;
-+void     ibus_accelerator_parse               (const gchar      *accelerator,
-+                                               guint            *accelerator_key,
-+                                               IBusModifierType *accelerator_mods);
-+gchar*   ibus_accelerator_name                (guint            accelerator_key,
-+                                               IBusModifierType accelerator_mods);
-+
-+G_END_DECLS
-+
-+#endif /* __IBUS_ACCEL_GROUP_H_ */
-diff --git a/src/ibusengine.c b/src/ibusengine.c
-index da648d11..9a0b1a8a 100644
---- a/src/ibusengine.c
-+++ b/src/ibusengine.c
-@@ -20,12 +20,16 @@
-  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
-  * USA
-  */
--#include "ibusengine.h"
- #include <stdarg.h>
- #include <string.h>
-+
-+#include "ibusaccelgroup.h"
-+#include "ibusengine.h"
-+#include "ibuskeysyms.h"
- #include "ibusmarshalers.h"
- #include "ibusinternal.h"
- #include "ibusshare.h"
-+#include "ibusxevent.h"
- 
- #define IBUS_ENGINE_GET_PRIVATE(o)  \
-    (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_ENGINE, IBusEnginePrivate))
-@@ -60,6 +64,8 @@ enum {
- };
- 
- 
-+typedef struct _IBusEngineKeybinding IBusEngineKeybinding;
-+
- /* IBusEnginePriv */
- struct _IBusEnginePrivate {
-     gchar *engine_name;
-@@ -74,11 +80,19 @@ struct _IBusEnginePrivate {
-     /* cached content-type */
-     guint content_purpose;
-     guint content_hints;
-+
-+    GSettings             *settings_emoji;
-+    IBusEngineKeybinding **emoji_keybindings;
-+};
-+
-+struct _IBusEngineKeybinding {
-+    guint            keyval;
-+    IBusModifierType modifiers;
- };
- 
- static guint            engine_signals[LAST_SIGNAL] = { 0 };
- 
--static IBusText *text_empty = NULL;
-+static IBusText *text_empty;
- 
- /* functions prototype */
- static void      ibus_engine_destroy         (IBusEngine         *engine);
-@@ -176,6 +190,11 @@ static void      ibus_engine_dbus_property_changed
-                                              (IBusEngine         *engine,
-                                               const gchar        *property_name,
-                                               GVariant           *value);
-+static void      ibus_engine_keybinding_free (IBusEngine         *engine);
-+static void      settings_emoji_hotkey_changed_cb 
-+                                             (GSettings          *settings,
-+                                              const gchar        *key,
-+                                              gpointer            data);
- 
- 
- G_DEFINE_TYPE (IBusEngine, ibus_engine, IBUS_TYPE_SERVICE)
-@@ -263,11 +282,27 @@ static const gchar introspection_xml[] =
-     "      <arg type='u' name='keycode' />"
-     "      <arg type='u' name='state' />"
-     "    </signal>"
-+    "    <signal name='PanelExtension'>"
-+    "      <arg type='v' name='data' />"
-+    "    </signal>"
-     /* FIXME properties */
-     "    <property name='ContentType' type='(uu)' access='write' />"
-     "  </interface>"
-     "</node>";
- 
-+static const guint IBUS_MODIFIER_FILTER =
-+        IBUS_MODIFIER_MASK & ~(
-+        IBUS_LOCK_MASK |  /* Caps Lock */
-+        IBUS_MOD2_MASK |  /* Num Lock */
-+        IBUS_BUTTON1_MASK |
-+        IBUS_BUTTON2_MASK |
-+        IBUS_BUTTON3_MASK |
-+        IBUS_BUTTON4_MASK |
-+        IBUS_BUTTON5_MASK |
-+        IBUS_SUPER_MASK |
-+        IBUS_HYPER_MASK |
-+        IBUS_META_MASK);
-+
- static void
- ibus_engine_class_init (IBusEngineClass *class)
- {
-@@ -802,9 +837,15 @@ ibus_engine_class_init (IBusEngineClass *class)
- static void
- ibus_engine_init (IBusEngine *engine)
- {
--    engine->priv = IBUS_ENGINE_GET_PRIVATE (engine);
--
--    engine->priv->surrounding_text = g_object_ref_sink (text_empty);
-+    IBusEnginePrivate *priv;
-+    engine->priv = priv = IBUS_ENGINE_GET_PRIVATE (engine);
-+
-+    priv->surrounding_text = g_object_ref_sink (text_empty);
-+    priv->settings_emoji =
-+            g_settings_new ("org.freedesktop.ibus.panel.emoji");
-+    settings_emoji_hotkey_changed_cb (priv->settings_emoji, "hotkey", engine);
-+    g_signal_connect (priv->settings_emoji, "changed::hotkey",
-+                      G_CALLBACK (settings_emoji_hotkey_changed_cb), engine);
- }
- 
- static void
-@@ -817,6 +858,7 @@ ibus_engine_destroy (IBusEngine *engine)
-         g_object_unref (engine->priv->surrounding_text);
-         engine->priv->surrounding_text = NULL;
-     }
-+    ibus_engine_keybinding_free (engine);
- 
-     IBUS_OBJECT_CLASS(ibus_engine_parent_class)->destroy (IBUS_OBJECT (engine));
- }
-@@ -852,6 +894,53 @@ ibus_engine_get_property (IBusEngine *engine,
-     }
- }
- 
-+static void
-+ibus_engine_panel_extension (IBusEngine *engine)
-+{
-+    IBusXEvent *xevent = ibus_x_event_new (
-+            "event-type", IBUS_X_EVENT_KEY_PRESS,
-+            "purpose", "emoji",
-+            NULL);
-+    GVariant *data = ibus_serializable_serialize_object (
-+            IBUS_SERIALIZABLE (xevent));
-+
-+    g_assert (data != NULL);
-+    ibus_engine_emit_signal (engine,
-+                             "PanelExtension",
-+                             g_variant_new ("(v)", data));
-+}
-+
-+static gboolean
-+ibus_engine_filter_key_event (IBusEngine *engine,
-+                              guint       keyval,
-+                              guint       keycode,
-+                              guint       state)
-+{
-+    IBusEnginePrivate *priv;
-+    int i;
-+    guint modifiers;
-+
-+    if ((state & IBUS_RELEASE_MASK) != 0)
-+        return FALSE;
-+    g_return_val_if_fail (IBUS_IS_ENGINE (engine), FALSE);
-+
-+    priv = engine->priv;
-+    modifiers = state & IBUS_MODIFIER_FILTER;
-+    if (keyval >= IBUS_KEY_A && keyval <= IBUS_KEY_Z &&
-+        (modifiers & IBUS_SHIFT_MASK) != 0) {
-+        keyval = keyval - IBUS_KEY_A + IBUS_KEY_a;
-+    }
-+    for (i = 0; priv->emoji_keybindings[i]; i++) {
-+        IBusEngineKeybinding *binding = priv->emoji_keybindings[i];
-+        if (binding->keyval == keyval &&
-+            binding->modifiers == modifiers) {
-+            ibus_engine_panel_extension (engine);
-+            return TRUE;
-+        }
-+    }
-+    return FALSE;
-+}
-+
- static gboolean
- ibus_engine_service_authorized_method (IBusService     *service,
-                                        GDBusConnection *connection)
-@@ -892,6 +981,7 @@ ibus_engine_service_method_call (IBusService           *service,
-     if (g_strcmp0 (method_name, "ProcessKeyEvent") == 0) {
-         guint keyval, keycode, state;
-         gboolean retval = FALSE;
-+
-         g_variant_get (parameters, "(uuu)", &keyval, &keycode, &state);
-         g_signal_emit (engine,
-                        engine_signals[PROCESS_KEY_EVENT],
-@@ -900,6 +990,12 @@ ibus_engine_service_method_call (IBusService           *service,
-                        keycode,
-                        state,
-                        &retval);
-+        if (!retval) {
-+            retval = ibus_engine_filter_key_event (engine,
-+                                                   keyval,
-+                                                   keycode,
-+                                                   state);
-+        }
-         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", retval));
-         return;
-     }
-@@ -1338,6 +1434,79 @@ ibus_engine_dbus_property_changed (IBusEngine  *engine,
-     g_object_unref (message);
- }
- 
-+static void
-+ibus_engine_keybinding_free (IBusEngine *engine)
-+{
-+    IBusEnginePrivate *priv;
-+    int i;
-+
-+    g_return_if_fail (IBUS_IS_ENGINE (engine));
-+
-+    priv = engine->priv;
-+    if (priv->emoji_keybindings) {
-+        for (i = 0; priv->emoji_keybindings[i]; i++)
-+            g_slice_free (IBusEngineKeybinding, priv->emoji_keybindings[i]);
-+        g_clear_pointer (&priv->emoji_keybindings, g_free);
-+    }
-+}
-+
-+static IBusEngineKeybinding *
-+ibus_engine_keybinding_new (IBusEngine  *engine,
-+                            const gchar *accelerator)
-+{
-+    guint keyval = 0U;
-+    IBusModifierType modifiers = 0;
-+    IBusEngineKeybinding *binding = NULL;
-+
-+    ibus_accelerator_parse (accelerator, &keyval, &modifiers);
-+    if (keyval == 0U && modifiers == 0) {
-+        g_warning ("Failed to parse shortcut key '%s'", accelerator);
-+        return NULL;
-+    }
-+    if (modifiers & IBUS_SUPER_MASK) {
-+        modifiers^=IBUS_SUPER_MASK;
-+        modifiers|=IBUS_MOD4_MASK;
-+    }
-+
-+    binding = g_slice_new0 (IBusEngineKeybinding);
-+    binding->keyval = keyval;
-+    binding->modifiers = modifiers;
-+    return binding;
-+}
-+
-+static void
-+settings_emoji_hotkey_changed_cb (GSettings   *settings,
-+                                  const gchar *key,
-+                                  gpointer     data)
-+{
-+    IBusEngine *engine;
-+    IBusEnginePrivate *priv;
-+    gchar **accelerators;
-+    int i, j, length;
-+    g_return_if_fail (IBUS_IS_ENGINE (data));
-+    engine = IBUS_ENGINE (data);
-+    priv = engine->priv;
-+
-+    if (g_strcmp0 (key, "hotkey") != 0)
-+        return;
-+    accelerators = g_settings_get_strv (settings, key);
-+    length = g_strv_length (accelerators);
-+    ibus_engine_keybinding_free (engine);
-+    if (length == 0) {
-+        g_strfreev (accelerators);
-+        return;
-+    }
-+    priv->emoji_keybindings = g_new0 (IBusEngineKeybinding*, length + 1);
-+    for (i = 0, j = 0; i < length; i++) {
-+        IBusEngineKeybinding *binding =
-+                ibus_engine_keybinding_new (engine, accelerators[i]);
-+        if (!binding)
-+            continue;
-+        priv->emoji_keybindings[j++] = binding;
-+    }
-+    g_strfreev (accelerators);
-+}
-+
- IBusEngine *
- ibus_engine_new (const gchar     *engine_name,
-                  const gchar     *object_path,
-diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
-index c85dfa86..8217a000 100644
---- a/ui/gtk3/emojier.vala
-+++ b/ui/gtk3/emojier.vala
-@@ -2062,6 +2062,7 @@ public class IBusEmojier : Gtk.ApplicationWindow {
-         // Do not hide a bottom panel in XFCE4
-         if (work_area.y < y)
-             y = work_area.y;
-+        // FIXME: move() does not work in Wayland
-         move(x, y);
- 
-         uint32 timestamp = event.get_time();
-diff --git a/ui/gtk3/extension.vala b/ui/gtk3/extension.vala
-index a170280b..03026d00 100644
---- a/ui/gtk3/extension.vala
-+++ b/ui/gtk3/extension.vala
-@@ -115,8 +115,9 @@ class ExtensionGtk {
-         // and Ctrl-Shift-e when ibus-ui-gtk3 runs after the
-         // desktop is launched.
-         GLib.Environment.unset_variable("GDK_CORE_DEVICE_EVENTS");
--        // for Gdk.X11.get_default_xdisplay()
--        Gdk.set_allowed_backends("x11");
-+        // Gdk.set_allowed_backends("x11") let present_with_time() failed on
-+        // launching the dialog secondly in Wayland.
-+        //Gdk.set_allowed_backends("x11");
- 
-         ExtensionGtk extension = new ExtensionGtk(argv);
-         extension.run();
-diff --git a/ui/gtk3/panelbinding.vala b/ui/gtk3/panelbinding.vala
-index 1fbf6cfc..e3f39e12 100644
---- a/ui/gtk3/panelbinding.vala
-+++ b/ui/gtk3/panelbinding.vala
-@@ -33,8 +33,6 @@ class PanelBinding : IBus.PanelService {
-     private string[] m_emojier_favorites = {};
-     private Gtk.CssProvider m_css_provider;
-     private const uint PRELOAD_ENGINES_DELAY_TIME = 30000;
--    private GLib.List<BindingCommon.Keybinding> m_keybindings =
--            new GLib.List<BindingCommon.Keybinding>();
-     private bool m_load_emoji_at_startup;
-     private bool m_loaded_emoji = false;
-     private bool m_load_unicode_at_startup;
-@@ -49,15 +47,6 @@ class PanelBinding : IBus.PanelService {
-         m_bus = bus;
- 
-         init_settings();
--
--        bind_emoji_shortcut();
--    }
--
--
--    ~PanelBinding() {
--        BindingCommon.unbind_switch_shortcut(
--                BindingCommon.KeyEventFuncType.ANY,
--                m_keybindings);
-     }
- 
- 
-@@ -77,13 +66,6 @@ class PanelBinding : IBus.PanelService {
-                                               ref m_css_provider);
-         });
- 
--        m_settings_emoji.changed["hotkey"].connect((key) => {
--                BindingCommon.unbind_switch_shortcut(
--                        BindingCommon.KeyEventFuncType.EMOJI_TYPING,
--                        m_keybindings);
--                bind_emoji_shortcut();
--        });
--
-         m_settings_emoji.changed["font"].connect((key) => {
-                 BindingCommon.set_custom_font(m_settings_panel,
-                                               m_settings_emoji,
-@@ -124,25 +106,6 @@ class PanelBinding : IBus.PanelService {
-     }
- 
- 
--    private void bind_emoji_shortcut() {
--#if EMOJI_DICT
--        string[] accelerators = m_settings_emoji.get_strv("hotkey");
--
--        var keybinding_manager = KeybindingManager.get_instance();
--
--        foreach (var accelerator in accelerators) {
--            BindingCommon.keybinding_manager_bind(
--                    keybinding_manager,
--                    ref m_keybindings,
--                    accelerator,
--                    BindingCommon.KeyEventFuncType.EMOJI_TYPING,
--                    handle_emoji_typing,
--                    null);
--        }
--#endif
--    }
--
--
-     private void set_emoji_favorites() {
-         m_emojier_favorites = m_settings_emoji.get_strv("favorites");
-         IBusEmojier.set_favorites(
-@@ -195,9 +158,6 @@ class PanelBinding : IBus.PanelService {
- 
-         set_load_emoji_at_startup();
-         set_load_unicode_at_startup();
--        BindingCommon.unbind_switch_shortcut(BindingCommon.KeyEventFuncType.ANY,
--                                             m_keybindings);
--        bind_emoji_shortcut();
-         BindingCommon.set_custom_font(m_settings_panel,
-                                       m_settings_emoji,
-                                       ref m_css_provider);
-@@ -354,19 +314,26 @@ class PanelBinding : IBus.PanelService {
-             return;
-         }
-         Gdk.Event event = new Gdk.Event(event_type);
--        event.key.time = xevent.get_time();
--        Gdk.Display? display = Gdk.Display.get_default();
-+        uint32 time = xevent.get_time();
-+        if (time == 0)
-+            time = Gtk.get_current_event_time();
-+        event.key.time = time;
-         X.Window xid = xevent.get_window();
--        Gdk.X11.Window window;
--        window = Gdk.X11.Window.lookup_for_display(
--                display as Gdk.X11.Display, xid);
--        if (window != null) {
--            event.key.window = window;
--        } else {
-+        Gdk.Display? display = Gdk.Display.get_default();
-+        Gdk.Window? window = null;
-+        if (window == null && xid != 0) {
-+            window = Gdk.X11.Window.lookup_for_display(
-+                    display as Gdk.X11.Display, xid);
-+        }
-+        if (window == null && xid != 0) {
-             window = new Gdk.X11.Window.foreign_for_display(
-                     display as Gdk.X11.Display, xid);
--            event.key.window = window;
-         }
-+        if (window == null) {
-+            window = Gdk.get_default_root_window();
-+            window.ref();
-+        }
-+        event.key.window = window;
-         handle_emoji_typing(event);
-     }
- }
--- 
-2.14.3
-

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-05-31  2:06 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-31  2:06 [rpms/ibus] autotool: Removed ibus-HEAD.patch Takao Fujiwara

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox