Blob Blame History Raw
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