public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Takao Fujiwara <tfujiwar@redhat.com>
To: git-commits@fedoraproject.org
Subject: [rpms/ibus] autotool: Implement Unicode choice on Emojier
Date: Sun, 31 May 2026 02:06:44 GMT	[thread overview]
Message-ID: <178019320417.1.17625921082937905980.rpms-ibus-a3692b858299@fedoraproject.org> (raw)

A new commit has been pushed.

Repo   : rpms/ibus
Branch : autotool
Commit : a3692b858299cb34215faddd7332862f9999d32b
Author : Takao Fujiwara <tfujiwar@redhat.com>
Date   : 2018-02-06T14:43:55+09:00
Stats  : +4339/-48 in 3 file(s)
URL    : https://src.fedoraproject.org/rpms/ibus/c/a3692b858299cb34215faddd7332862f9999d32b?branch=autotool

Log:
Implement Unicode choice on Emojier

---
diff --git a/ibus-HEAD.patch b/ibus-HEAD.patch
index b0d7216..6b21ef5 100644
--- a/ibus-HEAD.patch
+++ b/ibus-HEAD.patch
@@ -170,3 +170,4275 @@ index 468aa324..33949fa1 100644
 -- 
 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
+

diff --git a/ibus-xx-emoji-harfbuzz.patch b/ibus-xx-emoji-harfbuzz.patch
index 3921c3e..89a37b1 100644
--- a/ibus-xx-emoji-harfbuzz.patch
+++ b/ibus-xx-emoji-harfbuzz.patch
@@ -1,14 +1,16 @@
-From 1e358f28a2b36743847584671ef533769036b40d Mon Sep 17 00:00:00 2001
+From c6c1e8ea01c8466dc97d7549e77538e2d7ec872a Mon Sep 17 00:00:00 2001
 From: fujiwarat <takao.fujiwara1@gmail.com>
-Date: Sun, 22 Oct 2017 10:45:33 +0900
+Date: Mon, 29 Jan 2018 18:27:09 +0900
 Subject: [PATCH] Integrate custom rendering to use HarfBuzz glyph info
 
 IBusFontSet offers FcFontSet, glyph info with HarfBuzz and rendering
 on Cairo context.
-Current Pango changes fonts by emoji variants and draws the separated
-glyphs [1] but actually the emoji characters with variants can be drawn
-as one glyph so this class  manages Fontconfig fontsets to select a font,
-HarfBuzz to get glyphs for emoji variants, Cairo to draw glyphs.
+Now the most issues in Pango were fixed and I appreciate the changes [1].
+However the latest changes in Pango, Fontconfig prevent users from
+setting emoji fonts with GtkFontChooser.
+This patch can enable the selected emoji font to draw emoji chars
+on Emojier.
+It's under the considerations if the font setting is deleted from ibus-setup.
 Need configure --enable-harfbuzz-for-emoji option to enable this feature.
 
 [1]: https://bugzilla.gnome.org/show_bug.cgi?id=780669
@@ -19,10 +21,10 @@ Need configure --enable-harfbuzz-for-emoji option to enable this feature.
  bindings/vala/ibus-fontset-1.0.deps    |    1 +
  configure.ac                           |   29 +
  ui/gtk3/Makefile.am                    |   32 +
- ui/gtk3/emojier.vala                   |  100 +++-
+ ui/gtk3/emojier.vala                   |  111 ++++
  ui/gtk3/ibusfontset.c                  | 1030 ++++++++++++++++++++++++++++++++
  ui/gtk3/ibusfontset.h                  |  302 ++++++++++
- 8 files changed, 1576 insertions(+), 2 deletions(-)
+ 8 files changed, 1589 insertions(+)
  create mode 100644 bindings/vala/IBusFontSet-1.0.metadata
  create mode 100644 bindings/vala/ibus-fontset-1.0.deps
  create mode 100644 ui/gtk3/ibusfontset.c
@@ -153,11 +155,11 @@ index 00000000..129fe166
 @@ -0,0 +1 @@
 +cairo
 diff --git a/configure.ac b/configure.ac
-index 14556a3a..6ff8f4a9 100644
+index bd41069b..243396ff 100644
 --- a/configure.ac
 +++ b/configure.ac
-@@ -653,6 +653,34 @@ https://github.com/fujiwarat/cldr-emoji-annotation)
-     enable_emoji_dict="yes (enabled, use --disable-emoji-dict to disable)"
+@@ -688,6 +688,34 @@ the UCD files from https://www.unicode.org/Public/UNIDATA/)
+     enable_unicode_dict="yes (enabled, use --disable-unicode-dict to disable)"
  fi
  
 +AC_ARG_ENABLE(harfbuzz-for-emoji,
@@ -191,10 +193,10 @@ index 14556a3a..6ff8f4a9 100644
  # Check iso-codes.
  PKG_CHECK_MODULES(ISOCODES, [
      iso-codes
-@@ -743,6 +771,7 @@ Build options:
-   Enable Emoji dict             $enable_emoji_dict
-   Unicode Emoji directory       $UNICODE_EMOJI_DIR
+@@ -780,6 +808,7 @@ Build options:
    CLDR annotation directory     $EMOJI_ANNOTATION_DIR
+   Enable Unicode dict           $enable_unicode_dict
+   UCD directory                 $UCD_DIR
 +  Enable HarfBuzz for Emoji     $enable_harfbuzz_for_emoji
    Run test cases                $enable_tests
  ])
@@ -250,22 +252,26 @@ index 786b80e6..cd1e9c2c 100644
  man_seven_DATA =$(man_seven_files:.7=.7.gz)
  man_sevendir = $(mandir)/man7
 diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
-index f3e9f15c..58a26dd6 100644
+index 555ea68f..0a703383 100644
 --- a/ui/gtk3/emojier.vala
 +++ b/ui/gtk3/emojier.vala
-@@ -99,6 +99,9 @@ class IBusEmojier : Gtk.ApplicationWindow {
+@@ -99,16 +99,103 @@ public class IBusEmojier : Gtk.ApplicationWindow {
          }
      }
      private class EWhiteLabel : Gtk.Label {
 +#if ENABLE_HARFBUZZ_FOR_EMOJI
-+        IBus.RequisitionEx m_requisition;
++        private IBus.RequisitionEx m_requisition;
++#else
+         private int m_minimum_width = 0;
+         private int m_natural_width = 0;
+         private int m_minimum_height = 0;
+         private int m_natural_height = 0;
 +#endif
          public EWhiteLabel(string text) {
              GLib.Object(
                  name : "IBusEmojierWhiteLabel"
-@@ -106,8 +109,78 @@ class IBusEmojier : Gtk.ApplicationWindow {
-             if (text != "")
-                 set_label(text);
+             );
+             set_label(text);
          }
 +#if ENABLE_HARFBUZZ_FOR_EMOJI
 +        private void get_preferred_size_with_hb(out int minimum_width,
@@ -277,7 +283,8 @@ index f3e9f15c..58a26dd6 100644
 +            minimum_height = 0;
 +            natural_height = 0;
 +            var text = this.get_text();
-+            if (text == null || text == "")
++            GLib.return_if_fail (text != null);
++            if (text == "")
 +                return;
 +            var context = this.get_pango_context();
 +            var language = context.get_language();
@@ -288,6 +295,18 @@ index f3e9f15c..58a26dd6 100644
 +            natural_width = widest.width;
 +            minimum_height = widest.height;
 +            natural_height = widest.height;
++            if (minimum_width <= minimum_height)
++                natural_width = minimum_width = minimum_height;
++            if (text.length == 1) {
++                switch(text.get_char()) {
++                case '\t':
++                    natural_width = minimum_width = minimum_height;
++                    break;
++                case '\v':
++                    natural_height = minimum_height = minimum_width;
++                    break;
++                }
++            }
 +        }
 +        public override void get_preferred_width(out int minimum_width,
 +                                                 out int natural_width) {
@@ -302,12 +321,6 @@ index f3e9f15c..58a26dd6 100644
 +                                       out natural_height);
 +        }
 +        public override bool draw(Cairo.Context cr) {
-+            if (m_fontset == null)
-+                return true;
-+            if (m_requisition == null)
-+                return true;
-+            if (m_requisition.cairo_lines == null)
-+                return true;
 +            var style_context = get_style_context();
 +            Gtk.Allocation allocation;
 +            get_allocation(out allocation);
@@ -315,6 +328,12 @@ index f3e9f15c..58a26dd6 100644
 +                                            0, 0,
 +                                            allocation.width,
 +                                            allocation.height);
++            if (m_fontset == null)
++                return true;
++            if (m_requisition == null)
++                return true;
++            if (m_requisition.cairo_lines == null)
++                return true;
 +            Gdk.RGBA *normal_fg = null;
 +            style_context.get(Gtk.StateFlags.NORMAL,
 +                              "color",
@@ -336,33 +355,29 @@ index f3e9f15c..58a26dd6 100644
 +            normal_fg = null;
 +            return true;
 +        }
++#else
+         public override void get_preferred_width(out int minimum_width,
+                                                  out int natural_width) {
+             if (m_minimum_height == 0 && m_natural_height == 0) {
+@@ -161,6 +248,7 @@ public class IBusEmojier : Gtk.ApplicationWindow {
+             m_minimum_height = minimum_height;
+             m_natural_height = natural_height;
+         }
 +#endif
      }
--    private class ESelectedLabel : Gtk.Label {
-+    private class ESelectedLabel : EWhiteLabel {
+     private class ESelectedLabel : EWhiteLabel {
          public ESelectedLabel(string text) {
-             GLib.Object(
-                 name : "IBusEmojierSelectedLabel"
-@@ -116,7 +189,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"
-@@ -231,6 +304,9 @@ class IBusEmojier : Gtk.ApplicationWindow {
-             m_category_to_emojis_dict;
-     private static GLib.HashTable<string, GLib.SList<string>>?
-             m_emoji_to_emoji_variants_dict;
+@@ -307,6 +395,9 @@ public class IBusEmojier : Gtk.ApplicationWindow {
+     private static bool m_show_unicode = false;
+     private static LoadProgressObject m_unicode_progress_object;
+     private static bool m_loaded_unicode = false;
 +#if ENABLE_HARFBUZZ_FOR_EMOJI
 +    private static IBus.FontSet m_fontset;
 +#endif
  
      private ThemedRGBA m_rgba;
      private Gtk.Box m_vbox;
-@@ -1666,6 +1742,22 @@ class IBusEmojier : Gtk.ApplicationWindow {
+@@ -2064,6 +2155,22 @@ public class IBusEmojier : Gtk.ApplicationWindow {
      }
  
  
@@ -385,7 +400,7 @@ index f3e9f15c..58a26dd6 100644
      public static bool has_loaded_emoji_dict() {
          if (m_emoji_to_data_dict == null)
              return false;
-@@ -1696,6 +1788,10 @@ class IBusEmojier : Gtk.ApplicationWindow {
+@@ -2094,6 +2201,10 @@ public class IBusEmojier : Gtk.ApplicationWindow {
          int font_size = font_desc.get_size() / Pango.SCALE;
          if (font_size != 0)
              m_emoji_font_size = font_size;
@@ -1741,5 +1756,5 @@ index 00000000..efcaa286
 +G_END_DECLS
 +#endif
 -- 
-2.13.4
+2.14.3
 

diff --git a/ibus.spec b/ibus.spec
index 2240e2b..b0ac4ca 100644
--- a/ibus.spec
+++ b/ibus.spec
@@ -30,7 +30,7 @@
 
 Name:           ibus
 Version:        1.5.17
-Release:        6%{?dist}
+Release:        7%{?dist}
 Summary:        Intelligent Input Bus for Linux OS
 License:        LGPLv2+
 Group:          System Environment/Libraries
@@ -78,6 +78,7 @@ BuildRequires:  qt5-qtbase-devel
 %endif
 BuildRequires:  cldr-emoji-annotation
 BuildRequires:  unicode-emoji
+BuildRequires:  unicode-ucd
 %if %with_emoji_harfbuzz
 BuildRequires:  cairo-devel
 BuildRequires:  fontconfig-devel
@@ -427,6 +428,9 @@ dconf update || :
 %{_datadir}/gtk-doc/html/*
 
 %changelog
+* Tue Feb 06 2018 Takao Fujiwara <tfujiwar@redhat.com> - 1.5.17-7
+- Added Unicode typing on Emojier
+
 * Sat Feb 03 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 1.5.17-6
 - Switch to %%ldconfig_scriptlets
 

                 reply	other threads:[~2026-05-31  2:06 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=178019320417.1.17625921082937905980.rpms-ibus-a3692b858299@fedoraproject.org \
    --to=tfujiwar@redhat.com \
    --cc=git-commits@fedoraproject.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox