From a3692b858299cb34215faddd7332862f9999d32b Mon Sep 17 00:00:00 2001 From: Takao Fujiwara Date: Tue, 6 Feb 2018 14:43:55 +0900 Subject: [PATCH] Implement Unicode choice on Emojier --- ibus-HEAD.patch | 4272 ++++++++++++++++++++++++++++++++++ ibus-xx-emoji-harfbuzz.patch | 109 +- ibus.spec | 6 +- 3 files changed, 4339 insertions(+), 48 deletions(-) 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 +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 +-# Copyright (c) 2015-2017 Takao Fujiwara +-# Copyright (c) 2007-2017 Red Hat, Inc. ++# Copyright (c) 2015-2018 Takao Fujiwara ++# 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 ++ * Copyright (C) 2016-2018 Takao Fujiwara + * 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 +- * Copyright (C) 2008-2013 Red Hat, Inc. ++ * Copyright (C) 2018 Takao Fujiwara ++ * 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 + #include + #include ++#include + + #ifndef IBUS_DISABLE_DEPRECATED + #include +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 ++ * 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 ++#endif ++ ++#include ++#include ++#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 ++ * 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 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 ++#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 ++ * 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 ++ ++#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 ++ * 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 ++#endif ++ ++#include ++#include ++#include ++ ++#ifdef HAVE_LOCALE_H ++#include ++#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 \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 ++ * Copyright (c) 2017-2018 Takao Fujiwara + * + * 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>? + m_annotation_to_emojis_dict; + private static GLib.HashTable? +@@ -231,6 +299,14 @@ class IBusEmojier : Gtk.ApplicationWindow { + m_category_to_emojis_dict; + private static GLib.HashTable>? + m_emoji_to_emoji_variants_dict; ++ private static GLib.HashTable? ++ m_unicode_to_data_dict; ++ private static GLib.HashTable>? ++ m_name_to_unicodes_dict; ++ private static GLib.SList 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>(GLib.str_hash, + GLib.str_equal); ++ m_unicode_to_data_dict = ++ new GLib.HashTable( ++ GLib.direct_hash, ++ GLib.direct_equal); ++ m_name_to_unicodes_dict = ++ new GLib.HashTable>(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 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 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 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 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? total_emojis = null; + unowned GLib.SList? sub_emojis = null; ++ unowned GLib.SList? 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? 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? 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 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 ++ * Copyright (C) 2017-2018 Takao Fujiwara + * 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 +- * Copyright(c) 2015-2017 Takao Fujwiara ++ * Copyright(c) 2015-2018 Takao Fujwiara + * + * 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 +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 -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>? - 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 - 1.5.17-7 +- Added Unicode typing on Emojier + * Sat Feb 03 2018 Igor Gnatenko - 1.5.17-6 - Switch to %%ldconfig_scriptlets