diff --git a/ibus-HEAD.patch b/ibus-HEAD.patch index cb117e3..89a27b3 100644 --- a/ibus-HEAD.patch +++ b/ibus-HEAD.patch @@ -279,3 +279,3417 @@ index 2ca3a5e..cc19350 100644 -- 2.7.4 +From 160d3c975af91eea6b8271b757be769b8ceef98d Mon Sep 17 00:00:00 2001 +From: fujiwarat +Date: Tue, 21 Jun 2016 18:10:21 +0900 +Subject: [PATCH 1/4] engine: Implement Emoji typing with XKB engines + +Now Ctrl+Shift+e can convert an Emoji annotation to the Emoji characters +likes Ctrl+Shift+u. +The annotations are described as "Keywords" in the Unicode Emoji Data: +http://www.unicode.org/emoji/charts/emoji-list.html +'emoji-parser' compiles 'emoji-list.html' and generates 'emoji.dict' + +Review URL: https://codereview.appspot.com/295610043 +--- + configure.ac | 16 ++ + src/Makefile.am | 31 ++++ + src/emoji-parser.c | 217 ++++++++++++++++++++++++ + src/ibusenginesimple.c | 443 ++++++++++++++++++++++++++++++++++++++++++++----- + src/ibusutil.c | 193 ++++++++++++++++++++- + src/ibusutil.h | 31 +++- + 6 files changed, 890 insertions(+), 41 deletions(-) + create mode 100644 src/emoji-parser.c + +diff --git a/configure.ac b/configure.ac +index 1e1f5dd..3128ef9 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -599,6 +599,21 @@ if test x"$enable_libnotify" = x"yes"; then + enable_libnotify="yes (enabled, use --disable-libnotify to disable)" + fi + ++# --disable-emoji-dict option. ++AC_ARG_ENABLE(emoji-dict, ++ AS_HELP_STRING([--disable-emoji-dict], ++ [Do not build Emoji dict files]), ++ [enable_emoji_dict=$enableval], ++ [enable_emoji_dict=yes] ++) ++AM_CONDITIONAL([ENABLE_EMOJI_DICT], [test x"$enable_emoji_dict" = x"yes"]) ++if test x"$enable_emoji_dict" = x"yes"; then ++ PKG_CHECK_MODULES(LIBXML2, [ ++ libxml-2.0 ++ ]) ++ enable_emoji_dict="yes (enabled, use --disable-emoji-dict to disable)" ++fi ++ + # Check iso-codes. + PKG_CHECK_MODULES(ISOCODES, [ + iso-codes +@@ -682,6 +697,7 @@ Build options: + Panel icon "$IBUS_ICON_KEYBOARD" + Enable surrounding-text $enable_surrounding_text + Enable libnotify $enable_libnotify ++ Enable Emoji dict $enable_emoji_dict + Run test cases $enable_tests + ]) + +diff --git a/src/Makefile.am b/src/Makefile.am +index adaebe9..a33b67d 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -231,7 +231,38 @@ ibusmarshalers.c: ibusmarshalers.h ibusmarshalers.list + $(GLIB_GENMARSHAL) --prefix=_ibus_marshal $(srcdir)/ibusmarshalers.list --body --internal) > $@.tmp && \ + mv $@.tmp $@ + ++if ENABLE_EMOJI_DICT ++AM_CPPFLAGS += -DENABLE_EMOJI_DICT ++ ++dictdir = $(pkgdatadir)/dicts ++dict_DATA = emoji.dict ++ ++noinst_PROGRAMS = emoji-parser ++ ++emoji.dict: emoji-parser emoji-list.html ++ $(builddir)/emoji-parser emoji-list.html $@ ++ ++emoji_parser_SOURCES = \ ++ emoji-parser.c \ ++ $(NULL) ++emoji_parser_CFLAGS = \ ++ $(GLIB2_CFLAGS) \ ++ $(LIBXML2_CFLAGS) \ ++ $(NULL) ++emoji_parser_LDADD = \ ++ $(GLIB2_LIBS) \ ++ $(LIBXML2_LIBS) \ ++ $(libibus) \ ++ $(NULL) ++ ++CLEANFILES += \ ++ $(dict_DATA) \ ++ $(NULL) ++endif ++ + EXTRA_DIST = \ ++ emoji-list.html \ ++ emoji-parser.c \ + ibusversion.h.in \ + ibusmarshalers.list \ + ibusenumtypes.h.template \ +diff --git a/src/emoji-parser.c b/src/emoji-parser.c +new file mode 100644 +index 0000000..cf92fee +--- /dev/null ++++ b/src/emoji-parser.c +@@ -0,0 +1,217 @@ ++/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ ++/* vim:set et sts=4: */ ++/* ibus - The Input Bus ++ * Copyright (C) 2016 Takao Fujiwara ++ * Copyright (C) 2016 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 ++ */ ++ ++/* Convert http://www.unicode.org/emoji/charts/emoji-list.html ++ * to the dictionary file which look up the Emoji from the annotation. ++ */ ++ ++#include ++#include ++#include ++ ++#include "ibusutil.h" ++ ++typedef struct _EmojiData EmojiData; ++struct _EmojiData { ++ gchar *class; ++ gchar *emoji; ++ GSList *annotates; ++ GSList *prev_annotates; ++ GHashTable *dict; ++}; ++ ++const gchar *progname; ++ ++static gboolean parse_node (xmlNode *node, ++ gboolean is_child, ++ const gchar *prop_name, ++ EmojiData *data); ++ ++static void ++usage (void) ++{ ++ g_print ("%s emoji-list.html emoji.dict\n", progname); ++} ++ ++static void ++reset_emoji_element (EmojiData *data) ++{ ++ g_clear_pointer (&data->class, g_free); ++ g_clear_pointer (&data->emoji, g_free); ++ if (data->annotates) { ++ g_slist_free_full (data->prev_annotates, g_free); ++ data->prev_annotates = data->annotates; ++ data->annotates = NULL; ++ } ++} ++ ++static void ++free_dict_words (gpointer list) ++{ ++ g_slist_free_full (list, g_free); ++} ++ ++static gboolean ++parse_attr (xmlAttr *attr, ++ EmojiData *data) ++{ ++ if (g_strcmp0 ((const gchar *) attr->name, "class") == 0 && attr->children) ++ parse_node (attr->children, TRUE, (const gchar *) attr->name, data); ++ if (g_strcmp0 ((const gchar *) attr->name, "target") == 0 && attr->children) ++ parse_node (attr->children, TRUE, (const gchar *) attr->name, data); ++ if (attr->next) ++ parse_attr (attr->next, data); ++ return TRUE; ++} ++ ++static gboolean ++parse_node (xmlNode *node, ++ gboolean is_child, ++ const gchar *prop_name, ++ EmojiData *data) ++{ ++ if (g_strcmp0 ((const gchar *) node->name, "tr") == 0) { ++ GSList *annotates = data->annotates; ++ while (annotates) { ++ GSList *emojis = g_hash_table_lookup (data->dict, annotates->data); ++ if (emojis) { ++ emojis = g_slist_copy_deep (emojis, (GCopyFunc) g_strdup, NULL); ++ } ++ emojis = g_slist_append (emojis, g_strdup (data->emoji)); ++ g_hash_table_replace (data->dict, ++ g_strdup (annotates->data), ++ emojis); ++ annotates = annotates->next; ++ } ++ reset_emoji_element (data); ++ } ++ /* if node->name is "text" and is_child is FALSE, ++ * it's '\n' or Space between and . ++ */ ++ if (g_strcmp0 ((const gchar *) node->name, "text") == 0 && is_child) { ++ /* Get "chars" in */ ++ if (g_strcmp0 (prop_name, "class") == 0) { ++ if (g_strcmp0 (data->class, (const gchar *) node->content) != 0) { ++ g_clear_pointer (&data->class, g_free); ++ data->class = g_strdup ((const gchar *) node->content); ++ } ++ } ++ /* Get "annotate" in */ ++ if (g_strcmp0 (prop_name, "target") == 0 && ++ g_strcmp0 (data->class, "name") == 0) { ++ g_clear_pointer (&data->class, g_free); ++ data->class = g_strdup ((const gchar *) node->content); ++ } ++ /* Get "emoji" in emoji */ ++ if (g_strcmp0 (prop_name, "td") == 0 && ++ g_strcmp0 (data->class, "chars") == 0) { ++ data->emoji = g_strdup ((const gchar *) node->content); ++ } ++ /* We ignore "NAME" for NAME but ++ * takes "ANNOTATE" for ++ * ANNOTATE ++ */ ++ if (g_strcmp0 (prop_name, "td") == 0 && ++ g_strcmp0 (data->class, "name") == 0) { ++ g_slist_free_full (data->annotates, g_free); ++ data->annotates = NULL; ++ } ++ /* Get "ANNOTATE" in ++ * ANNOTATE ++ */ ++ if (g_strcmp0 (prop_name, "a") == 0 && ++ g_strcmp0 (data->class, "annotate") == 0) { ++ data->annotates = ++ g_slist_append (data->annotates, ++ g_strdup ((const gchar *) node->content)); ++ } ++ } ++ /* Get "foo" in */ ++ if (g_strcmp0 ((const gchar *) node->name, "td") == 0 && ++ node->properties != NULL) { ++ parse_attr (node->properties, data); ++ } ++ /* Get "foo" in */ ++ if (g_strcmp0 ((const gchar *) node->name, "a") == 0 && ++ node->properties != NULL) { ++ parse_attr (node->properties, data); ++ } ++ if (node->children) { ++ parse_node (node->children, TRUE, (const gchar *) node->name, data); ++ } else { ++ /* If annotate is NULL likes , ++ * the previous emoji cell has the same annotate. ++ */ ++ if (g_strcmp0 ((const gchar *) node->name, "td") == 0 && ++ g_strcmp0 (data->class, "name") == 0) { ++ data->annotates = g_slist_copy_deep (data->prev_annotates, ++ (GCopyFunc) g_strdup, ++ NULL); ++ } ++ } ++ if (node->next) ++ parse_node (node->next, FALSE, (const gchar *) node->name, data); ++ ++ return TRUE; ++} ++ ++static GHashTable * ++parse_html (const gchar *filename) ++{ ++ xmlDoc *doc = htmlParseFile (filename, "utf-8"); ++ EmojiData data = { 0, }; ++ ++ if (doc == NULL || doc->children == NULL) { ++ g_warning ("Parse Error in document type: %x", ++ doc ? doc->type : 0); ++ return FALSE; ++ } ++ ++ data.dict = g_hash_table_new_full (g_str_hash, ++ g_str_equal, ++ g_free, ++ free_dict_words); ++ parse_node (doc->children, TRUE, (const gchar *) doc->name, &data); ++ ++ reset_emoji_element (&data); ++ g_slist_free_full (data.prev_annotates, g_free); ++ ++ return data.dict; ++} ++ ++int ++main (int argc, char *argv[]) ++{ ++ GHashTable *dict; ++ progname = basename (argv[0]); ++ ++ if (argc < 3) { ++ usage (); ++ return -1; ++ } ++ ++ dict = parse_html (argv[1]); ++ ibus_emoji_dict_save (argv[2], dict); ++ g_hash_table_destroy (dict); ++ ++ return 0; ++} +diff --git a/src/ibusenginesimple.c b/src/ibusenginesimple.c +index 1b688b0..8efe5a9 100644 +--- a/src/ibusenginesimple.c ++++ b/src/ibusenginesimple.c +@@ -3,7 +3,7 @@ + /* ibus - The Input Bus + * Copyright (C) 2014 Peng Huang + * Copyright (C) 2015-2016 Takao Fujiwara +- * Copyright (C) 2014 Red Hat, Inc. ++ * Copyright (C) 2014-2016 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 +@@ -31,6 +31,7 @@ + + #include "ibuskeys.h" + #include "ibuskeysyms.h" ++#include "ibusutil.h" + + /* This file contains the table of the compose sequences, + * static const guint16 gtk_compose_seqs_compact[] = {} +@@ -42,16 +43,27 @@ + #include + + #define X11_DATADIR X11_DATA_PREFIX "/share/X11/locale" ++#define EMOJI_SOURCE_LEN 100 + #define IBUS_ENGINE_SIMPLE_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_ENGINE_SIMPLE, IBusEngineSimplePrivate)) + +-struct _IBusEngineSimplePrivate { +- guint16 compose_buffer[IBUS_MAX_COMPOSE_LEN + 1]; +- gunichar tentative_match; +- gint tentative_match_len; ++typedef struct { ++ GHashTable *dict; ++ int max_seq_len; ++} IBusEngineDict; + +- guint in_hex_sequence : 1; +- guint modifiers_dropped : 1; ++struct _IBusEngineSimplePrivate { ++ guint16 compose_buffer[EMOJI_SOURCE_LEN]; ++ gunichar tentative_match; ++ gchar *tentative_emoji; ++ gint tentative_match_len; ++ ++ guint in_hex_sequence : 1; ++ guint in_emoji_sequence : 1; ++ guint modifiers_dropped : 1; ++ IBusEngineDict *emoji_dict; ++ IBusLookupTable *lookup_table; ++ gboolean lookup_table_visible; + }; + + /* From the values below, the value 30 means the number of different first keysyms +@@ -97,6 +109,8 @@ static gboolean ibus_engine_simple_process_key_event + guint modifiers); + static void ibus_engine_simple_commit_char (IBusEngineSimple *simple, + gunichar ch); ++static void ibus_engine_simple_commit_str (IBusEngineSimple *simple, ++ const gchar *str); + static void ibus_engine_simple_update_preedit_text + (IBusEngineSimple *simple); + +@@ -128,6 +142,18 @@ ibus_engine_simple_init (IBusEngineSimple *simple) + static void + ibus_engine_simple_destroy (IBusEngineSimple *simple) + { ++ IBusEngineSimplePrivate *priv = simple->priv; ++ ++ if (priv->emoji_dict) { ++ if (priv->emoji_dict->dict) ++ g_clear_pointer (&priv->emoji_dict->dict, g_hash_table_destroy); ++ g_slice_free (IBusEngineDict, priv->emoji_dict); ++ priv->emoji_dict = NULL; ++ } ++ ++ g_clear_pointer (&priv->lookup_table, g_object_unref); ++ g_clear_pointer (&priv->tentative_emoji, g_free); ++ + IBUS_OBJECT_CLASS(ibus_engine_simple_parent_class)->destroy ( + IBUS_OBJECT (simple)); + } +@@ -146,6 +172,11 @@ ibus_engine_simple_reset (IBusEngine *engine) + priv->tentative_match_len = 0; + ibus_engine_hide_preedit_text ((IBusEngine *)simple); + } ++ if (priv->tentative_emoji || priv->in_emoji_sequence) { ++ priv->in_emoji_sequence = FALSE; ++ g_clear_pointer (&priv->tentative_emoji, g_free); ++ ibus_engine_hide_preedit_text ((IBusEngine *)simple); ++ } + } + + static void +@@ -162,23 +193,60 @@ ibus_engine_simple_commit_char (IBusEngineSimple *simple, + priv->tentative_match_len = 0; + ibus_engine_simple_update_preedit_text (simple); + } ++ if (priv->tentative_emoji || priv->in_emoji_sequence) { ++ priv->in_emoji_sequence = FALSE; ++ g_clear_pointer (&priv->tentative_emoji, g_free); ++ ibus_engine_simple_update_preedit_text (simple); ++ } + + ibus_engine_commit_text ((IBusEngine *)simple, + ibus_text_new_from_unichar (ch)); + } + + static void ++ibus_engine_simple_commit_str (IBusEngineSimple *simple, ++ const gchar *str) ++{ ++ IBusEngineSimplePrivate *priv = simple->priv; ++ gchar *backup_str; ++ ++ g_return_if_fail (str && *str); ++ ++ backup_str = g_strdup (str); ++ ++ if (priv->tentative_match || priv->in_hex_sequence) { ++ priv->in_hex_sequence = FALSE; ++ priv->tentative_match = 0; ++ priv->tentative_match_len = 0; ++ ibus_engine_simple_update_preedit_text (simple); ++ } ++ if (priv->tentative_emoji || priv->in_emoji_sequence) { ++ priv->in_emoji_sequence = FALSE; ++ g_clear_pointer (&priv->tentative_emoji, g_free); ++ ibus_engine_simple_update_preedit_text (simple); ++ } ++ ++ ibus_engine_commit_text ((IBusEngine *)simple, ++ ibus_text_new_from_string (backup_str)); ++ g_free (backup_str); ++} ++ ++static void + ibus_engine_simple_update_preedit_text (IBusEngineSimple *simple) + { + IBusEngineSimplePrivate *priv = simple->priv; + +- gunichar outbuf[IBUS_MAX_COMPOSE_LEN + 2]; ++ gunichar outbuf[EMOJI_SOURCE_LEN + 1]; + int len = 0; + +- if (priv->in_hex_sequence) { ++ if (priv->in_hex_sequence || priv->in_emoji_sequence) { + int hexchars = 0; + +- outbuf[0] = L'u'; ++ if (priv->in_hex_sequence) ++ outbuf[0] = L'u'; ++ else ++ outbuf[0] = L'@'; ++ + len = 1; + + while (priv->compose_buffer[hexchars] != 0) { +@@ -187,10 +255,22 @@ ibus_engine_simple_update_preedit_text (IBusEngineSimple *simple) + ++len; + ++hexchars; + } +- g_assert (len <= IBUS_MAX_COMPOSE_LEN + 1); ++ ++ if (priv->in_hex_sequence) ++ g_assert (len <= IBUS_MAX_COMPOSE_LEN + 1); ++ else ++ g_assert (len <= EMOJI_SOURCE_LEN + 1); + } +- else if (priv->tentative_match) ++ else if (priv->tentative_match) { + outbuf[len++] = priv->tentative_match; ++ } else if (priv->tentative_emoji && *priv->tentative_emoji) { ++ IBusText *text = ibus_text_new_from_string (priv->tentative_emoji); ++ len = strlen (priv->tentative_emoji); ++ ibus_text_append_attribute (text, ++ IBUS_ATTR_TYPE_UNDERLINE, IBUS_ATTR_UNDERLINE_SINGLE, 0, len); ++ ibus_engine_update_preedit_text ((IBusEngine *)simple, text, len, TRUE); ++ return; ++ } + + outbuf[len] = L'\0'; + if (len == 0) { +@@ -277,6 +357,104 @@ check_hex (IBusEngineSimple *simple, + return TRUE; + } + ++static IBusEngineDict * ++load_emoji_dict () ++{ ++ IBusEngineDict *emoji_dict; ++ GList *keys; ++ int max_length = 0; ++ ++ emoji_dict = g_slice_new0 (IBusEngineDict); ++ emoji_dict->dict = ibus_emoji_dict_load (IBUS_DATA_DIR "/dicts/emoji.dict"); ++ if (!emoji_dict->dict) ++ return emoji_dict; ++ ++ keys = g_hash_table_get_keys (emoji_dict->dict); ++ for (; keys; keys = keys->next) { ++ int length = strlen (keys->data); ++ if (max_length < length) ++ max_length = length; ++ } ++ emoji_dict->max_seq_len = max_length; ++ ++ return emoji_dict; ++} ++ ++static gboolean ++check_emoji_table (IBusEngineSimple *simple, ++ gint n_compose, ++ gint index) ++{ ++ IBusEngineSimplePrivate *priv = simple->priv; ++ IBusEngineDict *emoji_dict = priv->emoji_dict; ++ GString *str = NULL; ++ gint i; ++ gchar buf[7]; ++ GSList *words = NULL; ++ ++ g_assert (IBUS_IS_ENGINE_SIMPLE (simple)); ++ ++ if (priv->lookup_table == NULL) { ++ priv->lookup_table = ibus_lookup_table_new (10, 0, TRUE, TRUE); ++ g_object_ref_sink (priv->lookup_table); ++ } ++ if (emoji_dict == NULL) ++ emoji_dict = priv->emoji_dict = load_emoji_dict (simple); ++ ++ if (emoji_dict == NULL || emoji_dict->dict == NULL) ++ return FALSE; ++ ++ if (n_compose > emoji_dict->max_seq_len) ++ return FALSE; ++ ++ str = g_string_new (NULL); ++ priv->lookup_table_visible = FALSE; ++ ++ i = 0; ++ while (i < n_compose) { ++ gunichar ch; ++ ++ ch = ibus_keyval_to_unicode (priv->compose_buffer[i]); ++ ++ if (ch == 0) ++ return FALSE; ++ ++ if (!g_unichar_isprint (ch)) ++ return FALSE; ++ ++ buf[g_unichar_to_utf8 (ch, buf)] = '\0'; ++ ++ g_string_append (str, buf); ++ ++ ++i; ++ } ++ ++ if (str->str) { ++ words = g_hash_table_lookup (emoji_dict->dict, str->str); ++ } ++ g_string_free (str, TRUE); ++ ++ if (words != NULL) { ++ int i = 0; ++ ibus_lookup_table_clear (priv->lookup_table); ++ priv->lookup_table_visible = TRUE; ++ ++ while (words) { ++ if (i == index) { ++ g_clear_pointer (&priv->tentative_emoji, g_free); ++ priv->tentative_emoji = g_strdup (words->data); ++ } ++ IBusText *text = ibus_text_new_from_string (words->data); ++ ibus_lookup_table_append_candidate (priv->lookup_table, text); ++ words = words->next; ++ i++; ++ } ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ + static int + compare_seq_index (const void *key, const void *value) + { +@@ -626,10 +804,10 @@ ibus_check_algorithmically (const guint16 *compose_buffer, + + static gboolean + no_sequence_matches (IBusEngineSimple *simple, +- gint n_compose, +- guint keyval, +- guint keycode, +- guint modifiers) ++ gint n_compose, ++ guint keyval, ++ guint keycode, ++ guint modifiers) + { + IBusEngineSimplePrivate *priv = simple->priv; + +@@ -642,8 +820,7 @@ no_sequence_matches (IBusEngineSimple *simple, + gint len = priv->tentative_match_len; + int i; + +- ibus_engine_simple_commit_char (simple, +- priv->tentative_match); ++ ibus_engine_simple_commit_char (simple, priv->tentative_match); + priv->compose_buffer[0] = 0; + + for (i=0; i < n_compose - len - 1; i++) { +@@ -655,8 +832,11 @@ no_sequence_matches (IBusEngineSimple *simple, + + return ibus_engine_simple_process_key_event ( + (IBusEngine *)simple, keyval, keycode, modifiers); +- } +- else { ++ } else if (priv->tentative_emoji && *priv->tentative_emoji) { ++ ibus_engine_simple_commit_str (simple, priv->tentative_emoji); ++ g_clear_pointer (&priv->tentative_emoji, g_free); ++ priv->compose_buffer[0] = 0; ++ } else { + priv->compose_buffer[0] = 0; + if (n_compose > 1) { + /* Invalid sequence */ +@@ -676,6 +856,7 @@ no_sequence_matches (IBusEngineSimple *simple, + else + return FALSE; + } ++ return FALSE; + } + + static gboolean +@@ -687,6 +868,39 @@ is_hex_keyval (guint keyval) + } + + static gboolean ++is_graph_keyval (guint keyval) ++{ ++ gunichar ch = ibus_keyval_to_unicode (keyval); ++ ++ return g_unichar_isgraph (ch); ++} ++ ++static void ++ibus_engine_simple_update_lookup_and_aux_table (IBusEngineSimple *simple) ++{ ++ IBusEngineSimplePrivate *priv; ++ guint index, candidates; ++ gchar *aux_label = NULL; ++ IBusText *text = NULL; ++ ++ g_return_if_fail (IBUS_IS_ENGINE_SIMPLE (simple)); ++ ++ priv = simple->priv; ++ index = ibus_lookup_table_get_cursor_pos (priv->lookup_table) + 1; ++ candidates = ibus_lookup_table_get_number_of_candidates(priv->lookup_table); ++ aux_label = g_strdup_printf ("(%u / %u)", index, candidates); ++ text = ibus_text_new_from_string (aux_label); ++ g_free (aux_label); ++ ++ ibus_engine_update_auxiliary_text (IBUS_ENGINE (simple), ++ text, ++ priv->lookup_table_visible); ++ ibus_engine_update_lookup_table (IBUS_ENGINE (simple), ++ priv->lookup_table, ++ priv->lookup_table_visible); ++} ++ ++static gboolean + ibus_engine_simple_process_key_event (IBusEngine *engine, + guint keyval, + guint keycode, +@@ -697,10 +911,13 @@ ibus_engine_simple_process_key_event (IBusEngine *engine, + gint n_compose = 0; + gboolean have_hex_mods; + gboolean is_hex_start; ++ gboolean is_emoji_start = FALSE; + gboolean is_hex_end; ++ gboolean is_space; + gboolean is_backspace; + gboolean is_escape; + guint hex_keyval; ++ guint printable_keyval; + gint i; + gboolean compose_finish; + gunichar output_char; +@@ -714,17 +931,16 @@ ibus_engine_simple_process_key_event (IBusEngine *engine, + keyval == IBUS_KEY_Shift_L || keyval == IBUS_KEY_Shift_R)) { + if (priv->tentative_match && + g_unichar_validate (priv->tentative_match)) { +- ibus_engine_simple_commit_char (simple, +- priv->tentative_match); +- } +- else if (n_compose == 0) { ++ ibus_engine_simple_commit_char (simple, priv->tentative_match); ++ } else if (n_compose == 0) { + priv->modifiers_dropped = TRUE; +- } +- else { ++ } else { + /* invalid hex sequence */ + /* FIXME beep_window (event->window); */ + priv->tentative_match = 0; ++ g_clear_pointer (&priv->tentative_emoji, g_free); + priv->in_hex_sequence = FALSE; ++ priv->in_emoji_sequence = FALSE; + priv->compose_buffer[0] = 0; + + ibus_engine_simple_update_preedit_text (simple); +@@ -732,6 +948,26 @@ ibus_engine_simple_process_key_event (IBusEngine *engine, + + return TRUE; + } ++ /* Handle Shift + Space */ ++ else if (priv->in_emoji_sequence && ++ (keyval == IBUS_KEY_Control_L || keyval == IBUS_KEY_Control_R)) { ++ if (priv->tentative_emoji && *priv->tentative_emoji) { ++ ibus_engine_simple_commit_str (simple, priv->tentative_emoji); ++ g_clear_pointer (&priv->tentative_emoji, g_free); ++ } else if (n_compose == 0) { ++ priv->modifiers_dropped = TRUE; ++ } else { ++ /* invalid hex sequence */ ++ /* FIXME beep_window (event->window); */ ++ priv->tentative_match = 0; ++ g_clear_pointer (&priv->tentative_emoji, g_free); ++ priv->in_hex_sequence = FALSE; ++ priv->in_emoji_sequence = FALSE; ++ priv->compose_buffer[0] = 0; ++ ++ ibus_engine_simple_update_preedit_text (simple); ++ } ++ } + else + return FALSE; + } +@@ -741,25 +977,33 @@ ibus_engine_simple_process_key_event (IBusEngine *engine, + if (keyval == ibus_compose_ignore[i]) + return FALSE; + +- if (priv->in_hex_sequence && priv->modifiers_dropped) ++ if ((priv->in_hex_sequence || priv->in_emoji_sequence) ++ && priv->modifiers_dropped) { + have_hex_mods = TRUE; +- else ++ } else { + have_hex_mods = (modifiers & (HEX_MOD_MASK)) == HEX_MOD_MASK; ++ } + + is_hex_start = keyval == IBUS_KEY_U; ++#ifdef ENABLE_EMOJI_DICT ++ is_emoji_start = keyval == IBUS_KEY_E; ++#endif + is_hex_end = (keyval == IBUS_KEY_space || + keyval == IBUS_KEY_KP_Space || + keyval == IBUS_KEY_Return || + keyval == IBUS_KEY_ISO_Enter || + keyval == IBUS_KEY_KP_Enter); ++ is_space = (keyval == IBUS_KEY_space || keyval == IBUS_KEY_KP_Space); + is_backspace = keyval == IBUS_KEY_BackSpace; + is_escape = keyval == IBUS_KEY_Escape; + hex_keyval = is_hex_keyval (keyval) ? keyval : 0; ++ printable_keyval = is_graph_keyval (keyval) ? keyval : 0; + + /* gtkimcontextsimple causes a buffer overflow in priv->compose_buffer. + * Add the check code here. + */ +- if (n_compose >= IBUS_MAX_COMPOSE_LEN) { ++ if ((n_compose >= IBUS_MAX_COMPOSE_LEN && priv->in_hex_sequence) || ++ (n_compose >= EMOJI_SOURCE_LEN && priv->in_emoji_sequence)) { + if (is_backspace) { + priv->compose_buffer[--n_compose] = 0; + } +@@ -767,7 +1011,9 @@ ibus_engine_simple_process_key_event (IBusEngine *engine, + /* invalid hex sequence */ + // beep_window (event->window); + priv->tentative_match = 0; ++ g_clear_pointer (&priv->tentative_emoji, g_free); + priv->in_hex_sequence = FALSE; ++ priv->in_emoji_sequence = FALSE; + priv->compose_buffer[0] = 0; + } + else if (is_escape) { +@@ -789,12 +1035,16 @@ ibus_engine_simple_process_key_event (IBusEngine *engine, + * ISO_Level3_Switch. + */ + if (!have_hex_mods || +- (n_compose > 0 && !priv->in_hex_sequence) || +- (n_compose == 0 && !priv->in_hex_sequence && !is_hex_start) || ++ (n_compose > 0 && !priv->in_hex_sequence && !priv->in_emoji_sequence) || ++ (n_compose == 0 && !priv->in_hex_sequence && !is_hex_start && ++ !priv->in_emoji_sequence && !is_emoji_start) || + (priv->in_hex_sequence && !hex_keyval && +- !is_hex_start && !is_hex_end && !is_escape && !is_backspace)) { ++ !is_hex_start && !is_hex_end && !is_escape && !is_backspace) || ++ (priv->in_emoji_sequence && !printable_keyval && ++ !is_emoji_start && !is_hex_end && !is_escape && !is_backspace)) { + if (modifiers & (IBUS_MOD1_MASK | IBUS_CONTROL_MASK) || +- (priv->in_hex_sequence && priv->modifiers_dropped && ++ ((priv->in_hex_sequence || priv->in_emoji_sequence) && ++ priv->modifiers_dropped && + (keyval == IBUS_KEY_Return || + keyval == IBUS_KEY_ISO_Enter || + keyval == IBUS_KEY_KP_Enter))) { +@@ -816,6 +1066,20 @@ ibus_engine_simple_process_key_event (IBusEngine *engine, + + return TRUE; + } ++ if (priv->in_emoji_sequence && have_hex_mods && is_backspace) { ++ if (n_compose > 0) { ++ n_compose--; ++ priv->compose_buffer[n_compose] = 0; ++ check_emoji_table (simple, n_compose, -1); ++ ibus_engine_simple_update_lookup_and_aux_table (simple); ++ } else { ++ priv->in_emoji_sequence = FALSE; ++ } ++ ++ ibus_engine_simple_update_preedit_text (simple); ++ ++ return TRUE; ++ } + + /* Check for hex sequence restart */ + if (priv->in_hex_sequence && have_hex_mods && is_hex_start) { +@@ -833,13 +1097,41 @@ ibus_engine_simple_process_key_event (IBusEngine *engine, + } + } + } ++ if (priv->in_emoji_sequence && have_hex_mods && is_emoji_start) { ++ if (priv->tentative_emoji && *priv->tentative_emoji) { ++ ibus_engine_simple_commit_str (simple, priv->tentative_emoji); ++ g_clear_pointer (&priv->tentative_emoji, g_free); ++ } ++ else { ++ if (n_compose > 0) { ++ g_clear_pointer (&priv->tentative_emoji, g_free); ++ priv->in_emoji_sequence = FALSE; ++ priv->compose_buffer[0] = 0; ++ } ++ } ++ } + + /* Check for hex sequence start */ + if (!priv->in_hex_sequence && have_hex_mods && is_hex_start) { + priv->compose_buffer[0] = 0; + priv->in_hex_sequence = TRUE; ++ priv->in_emoji_sequence = FALSE; + priv->modifiers_dropped = FALSE; + priv->tentative_match = 0; ++ g_clear_pointer (&priv->tentative_emoji, g_free); ++ ++ // g_debug ("Start HEX MODE"); ++ ++ ibus_engine_simple_update_preedit_text (simple); ++ ++ return TRUE; ++ } else if (!priv->in_emoji_sequence && have_hex_mods && is_emoji_start) { ++ priv->compose_buffer[0] = 0; ++ priv->in_hex_sequence = FALSE; ++ priv->in_emoji_sequence = TRUE; ++ priv->modifiers_dropped = FALSE; ++ priv->tentative_match = 0; ++ g_clear_pointer (&priv->tentative_emoji, g_free); + + // g_debug ("Start HEX MODE"); + +@@ -864,9 +1156,20 @@ ibus_engine_simple_process_key_event (IBusEngine *engine, + // beep_window (event->window); + return TRUE; + } +- } +- else ++ } else if (priv->in_emoji_sequence) { ++ if (printable_keyval) { ++ priv->compose_buffer[n_compose++] = printable_keyval; ++ } ++ else if (is_space && (modifiers & IBUS_SHIFT_MASK)) { ++ priv->compose_buffer[n_compose++] = IBUS_KEY_space; ++ } ++ else if (is_escape) { ++ ibus_engine_simple_reset (engine); ++ return TRUE; ++ } ++ } else { + priv->compose_buffer[n_compose++] = keyval; ++ } + + priv->compose_buffer[n_compose] = 0; + +@@ -880,8 +1183,7 @@ ibus_engine_simple_process_key_event (IBusEngine *engine, + ibus_engine_simple_commit_char (simple, + priv->tentative_match); + priv->compose_buffer[0] = 0; +- } +- else { ++ } else { + // FIXME + /* invalid hex sequence */ + // beep_window (event->window); +@@ -899,6 +1201,73 @@ ibus_engine_simple_process_key_event (IBusEngine *engine, + return TRUE; + } + } ++ else if (priv->in_emoji_sequence) { ++ if (have_hex_mods && n_compose > 0) { ++ gboolean update_lookup_table = FALSE; ++ ++ if (priv->lookup_table_visible) { ++ switch (keyval) { ++ case IBUS_KEY_space: ++ case IBUS_KEY_KP_Space: ++ if ((modifiers & IBUS_SHIFT_MASK) == 0) { ++ ibus_lookup_table_cursor_down (priv->lookup_table); ++ update_lookup_table = TRUE; ++ } ++ break; ++ case IBUS_KEY_Down: ++ ibus_lookup_table_cursor_down (priv->lookup_table); ++ update_lookup_table = TRUE; ++ break; ++ case IBUS_KEY_Up: ++ ibus_lookup_table_cursor_up (priv->lookup_table); ++ update_lookup_table = TRUE; ++ break; ++ case IBUS_KEY_Page_Down: ++ ibus_lookup_table_page_down (priv->lookup_table); ++ update_lookup_table = TRUE; ++ break; ++ case IBUS_KEY_Page_Up: ++ ibus_lookup_table_page_up (priv->lookup_table); ++ update_lookup_table = TRUE; ++ break; ++ default:; ++ } ++ } ++ ++ if (!update_lookup_table) { ++ if (is_hex_end && !is_space) { ++ if (priv->lookup_table) { ++ int index = (int) ibus_lookup_table_get_cursor_pos ( ++ priv->lookup_table); ++ check_emoji_table (simple, n_compose, index); ++ priv->lookup_table_visible = FALSE; ++ update_lookup_table = TRUE; ++ } ++ } ++ else if (check_emoji_table (simple, n_compose, -1)) { ++ update_lookup_table = TRUE; ++ } ++ } ++ ++ if (update_lookup_table) ++ ibus_engine_simple_update_lookup_and_aux_table (simple); ++ if (is_hex_end && !is_space) { ++ if (priv->tentative_emoji && *priv->tentative_emoji) { ++ ibus_engine_simple_commit_str (simple, ++ priv->tentative_emoji); ++ priv->compose_buffer[0] = 0; ++ } else { ++ g_clear_pointer (&priv->tentative_emoji, g_free); ++ priv->in_emoji_sequence = FALSE; ++ priv->compose_buffer[0] = 0; ++ } ++ } ++ ++ ibus_engine_simple_update_preedit_text (simple); ++ ++ return TRUE; ++ } ++ } + else { + GSList *list = global_tables; + while (list) { +diff --git a/src/ibusutil.c b/src/ibusutil.c +index b9f3fdd..bfaa4f4 100644 +--- a/src/ibusutil.c ++++ b/src/ibusutil.c +@@ -2,8 +2,8 @@ + /* vim:set et sts=4: */ + /* bus - The Input Bus + * Copyright (C) 2008-2015 Peng Huang +- * Copyright (C) 2010-2015 Takao Fujiwara +- * Copyright (C) 2008-2015 Red Hat, Inc. ++ * Copyright (C) 2010-2016 Takao Fujiwara ++ * Copyright (C) 2008-2016 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 +@@ -34,6 +34,9 @@ + #include + #endif + ++#define IBUS_DICT_MAGIC "IBusDict" ++#define IBUS_DICT_VERSION (1) ++ + /* gettext macro */ + #define N_(t) t + +@@ -125,6 +128,74 @@ _load_lang() + ibus_xml_free (node); + } + ++static void ++free_dict_words (gpointer list) ++{ ++ g_slist_free_full (list, g_free); ++} ++ ++static void ++variant_foreach_add_emoji (gchar *annotation, ++ GSList *emojis, ++ GVariantBuilder *builder) ++{ ++ int i; ++ int length = (int) g_slist_length (emojis); ++ gchar **buff = g_new0 (gchar *, length); ++ GSList *l = emojis; ++ ++ for (i = 0; i < length; i++, l = l->next) ++ buff[i] = (gchar *) l->data; ++ ++ g_variant_builder_add (builder, ++ "{sv}", ++ annotation, ++ g_variant_new_strv ((const gchar * const *) buff, ++ length)); ++ g_free (buff); ++} ++ ++static GVariant * ++ibus_emoji_dict_serialize (GHashTable *dict) ++{ ++ GVariantBuilder builder; ++ ++ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); ++ g_hash_table_foreach (dict, (GHFunc) variant_foreach_add_emoji, &builder); ++ return g_variant_builder_end (&builder); ++} ++ ++static GHashTable * ++ibus_emoji_dict_deserialize (GVariant *variant) ++{ ++ GHashTable *dict = NULL; ++ GVariantIter iter; ++ gchar *annotate = NULL; ++ GVariant *emojis_variant = NULL; ++ ++ dict = g_hash_table_new_full (g_str_hash, ++ g_str_equal, ++ g_free, ++ free_dict_words); ++ ++ g_variant_iter_init (&iter, variant); ++ while (g_variant_iter_loop (&iter, "{sv}", &annotate, &emojis_variant)) { ++ gsize i; ++ gsize length = 0; ++ const gchar **array = g_variant_get_strv (emojis_variant, &length); ++ GSList *emojis = NULL; ++ ++ for (i = 0; i < length; i++) { ++ emojis = g_slist_append (emojis, g_strdup (array[i])); ++ } ++ g_hash_table_insert (dict, annotate, emojis); ++ annotate = NULL; ++ g_clear_pointer (&emojis_variant, g_variant_unref); ++ } ++ ++ return dict; ++} ++ + const gchar * + ibus_get_untranslated_language_name (const gchar *_locale) + { +@@ -171,3 +242,121 @@ ibus_g_variant_get_child_string (GVariant *variant, gsize index, char **str) + g_free (*str); + g_variant_get_child (variant, index, "s", str); + } ++ ++void ++ibus_emoji_dict_save (const gchar *path, GHashTable *dict) ++{ ++ GVariant *variant; ++ const gchar *header = IBUS_DICT_MAGIC; ++ const guint16 version = IBUS_DICT_VERSION; ++ const gchar *contents; ++ gsize length; ++ GError *error = NULL; ++ ++ variant = g_variant_new ("(sqv)", ++ header, ++ version, ++ ibus_emoji_dict_serialize (dict)); ++ ++ contents = g_variant_get_data (variant); ++ length = g_variant_get_size (variant); ++ ++ if (!g_file_set_contents (path, contents, length, &error)) { ++ g_warning ("Failed to save emoji dict %s: %s", path, error->message); ++ g_error_free (error); ++ } ++ ++ g_variant_unref (variant); ++} ++ ++GHashTable * ++ibus_emoji_dict_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; ++ GHashTable *retval = NULL; ++ ++ if (!g_file_test (path, G_FILE_TEST_EXISTS)) { ++ g_warning ("Emoji 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_DICT_MAGIC) != 0) { ++ g_warning ("cache is not IBusDict."); ++ goto out_load_cache; ++ } ++ ++ if (version != IBUS_DICT_VERSION) { ++ g_warning ("cache version is different: %u != %u", ++ version, IBUS_DICT_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_emoji_dict_deserialize (variant); ++ ++out_load_cache: ++ if (variant) ++ g_variant_unref (variant); ++ if (variant_table) ++ g_variant_unref (variant_table); ++ ++ return retval; ++} ++ ++GSList * ++ibus_emoji_dict_lookup (GHashTable *dict, ++ const gchar *annotation) ++{ ++ return (GSList *) g_hash_table_lookup (dict, annotation); ++} +diff --git a/src/ibusutil.h b/src/ibusutil.h +index 2c1360c..e619b67 100644 +--- a/src/ibusutil.h ++++ b/src/ibusutil.h +@@ -2,8 +2,8 @@ + /* vim:set et sts=4: */ + /* bus - The Input Bus + * Copyright (C) 2008-2015 Peng Huang +- * Copyright (C) 2010-2015 Takao Fujiwara +- * Copyright (C) 2008-2015 Red Hat, Inc. ++ * Copyright (C) 2010-2016 Takao Fujiwara ++ * Copyright (C) 2008-2016 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 +@@ -53,4 +53,31 @@ const gchar * ibus_get_untranslated_language_name + */ + const gchar * ibus_get_language_name (const gchar *_locale); + ++/** ++ * ibus_emoji_dict_save: ++ * @path: A path of the saved dictionary file. ++ * @dict: (element-type utf8 gpointer) (transfer none): An Emoji dictionary ++ * ++ * Save the Emoji dictionary to the cache file. ++ */ ++void ibus_emoji_dict_save (const gchar *path, ++ GHashTable *dict); ++/** ++ * ibus_emoji_dict_load: ++ * @path: A path of the saved dictionary file. ++ * ++ * Returns: (element-type utf8 gpointer) (transfer none): An Emoji dictionary file loaded from the saved cache file. ++ */ ++GHashTable * ibus_emoji_dict_load (const gchar *path); ++ ++/** ++ * ibus_emoji_dict_lookup: ++ * @dict: (element-type utf8 gpointer) (transfer none): An Emoji dictionary ++ * @annotation: Annotation for Emoji characters ++ * ++ * Returns: (element-type utf8) (transfer none): List of Emoji characters ++ * This API is for gobject-introspection. ++ */ ++GSList * ibus_emoji_dict_lookup (GHashTable *dict, ++ const gchar *annotation); + #endif +-- +2.7.4 + +From 0ed644cd2b6c1d15bdba0d1c6d45d162b9b34806 Mon Sep 17 00:00:00 2001 +From: fujiwarat +Date: Thu, 23 Jun 2016 11:52:48 +0900 +Subject: [PATCH 2/4] engine: Add emoji-list.html + +Now we copied http://unicode.org/emoji/charts/emoji-list.html to +http://ibus.github.io/files/ibus/emoji-list.html +and download the file in the build time. +We don't save emoji-list.html in the tarball because the file size is +more than 5MB. +We always don't get the latest emoji-list.html to avoid the build error. + +BUG=https://github.com/ibus/ibus/pull/1864 +R=shawn.p.huang@gmail.com + +Review URL: https://codereview.appspot.com/298580043 +--- + src/Makefile.am | 23 ++++++++++++++++++++++- + 1 file changed, 22 insertions(+), 1 deletion(-) + +diff --git a/src/Makefile.am b/src/Makefile.am +index a33b67d..22e031f 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -29,6 +29,15 @@ endif + + SUBDIRS = . $(TESTS_DIR) + ++IBUS_V_wget = $(ibus__v_wget_@AM_V@) ++ibus__v_wget_ = $(ibus__v_wget_@AM_DEFAULT_V@) ++ibus__v_wget_0 = -nv ++ibus__v_wget_1 = ++IBUS_V_diff = $(ibus__v_diff_@AM_V@) ++ibus__v_diff_ = $(ibus__v_diff_@AM_DEFAULT_V@) ++ibus__v_diff_0 = -q ++ibus__v_diff_1 = ++ + # libibus = libibus-@IBUS_API_VERSION@.la + libibus = libibus-1.0.la + +@@ -239,6 +248,18 @@ dict_DATA = emoji.dict + + noinst_PROGRAMS = emoji-parser + ++emoji-list.html: ++ $(AM_V_at)wget $(IBUS_V_wget) \ ++ http://ibus.github.io/files/ibus/emoji-list.html ++ $(AM_V_at)wget $(IBUS_V_wget) \ ++ http://unicode.org/emoji/charts/emoji-list.html \ ++ -O latest-emoji-list.html ++ $(AM_V_at)diff $(IBUS_V_diff) emoji-list.html latest-emoji-list.html; \ ++ if test $$? -ne 0; then \ ++ echo "#### WARNING: emoji-list.html is old." >&2; \ ++ fi; \ ++ rm latest-emoji-list.html; ++ + emoji.dict: emoji-parser emoji-list.html + $(builddir)/emoji-parser emoji-list.html $@ + +@@ -257,11 +278,11 @@ emoji_parser_LDADD = \ + + CLEANFILES += \ + $(dict_DATA) \ ++ emoji-list.html \ + $(NULL) + endif + + EXTRA_DIST = \ +- emoji-list.html \ + emoji-parser.c \ + ibusversion.h.in \ + ibusmarshalers.list \ +-- +2.7.4 + +From 0ee1896a2b3e75494f8f9fd9d04c27436f0877b8 Mon Sep 17 00:00:00 2001 +From: fujiwarat +Date: Thu, 7 Jul 2016 12:47:34 +0900 +Subject: [PATCH 3/4] engine: Use annotations/en.xml from unocode.org but not + emoji-list.html + +Downloading emoji-list would cause a different build by build site. +Now save annotations/en.xml from unicode.org and get emoji.json from +Emoji One. +en.xml is used for Unicode annotations and emoji.json is used for +aliases_ascii, e.g. ":)", and category, e.g. "people". + +BUG=https://github.com/ibus/ibus/issues/1865 +R=Shawn.P.Huang@gmail.com + +Review URL: https://codereview.appspot.com/299530044 +--- + COPYING.unicode | 52 +++ + Makefile.am | 1 + + configure.ac | 14 +- + data/Makefile.am | 5 +- + data/annotations/Makefile.am | 27 ++ + data/annotations/en.xml | 1042 ++++++++++++++++++++++++++++++++++++++++++ + ibus.spec.in | 14 + + src/Makefile.am | 31 +- + src/emoji-parser.c | 605 +++++++++++++++++++----- + 9 files changed, 1634 insertions(+), 157 deletions(-) + create mode 100644 COPYING.unicode + create mode 100644 data/annotations/Makefile.am + create mode 100644 data/annotations/en.xml + +diff --git a/COPYING.unicode b/COPYING.unicode +new file mode 100644 +index 0000000..28d3060 +--- /dev/null ++++ b/COPYING.unicode +@@ -0,0 +1,52 @@ ++(Apply to data/annotations/en.xml) ++ ++UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE ++ ++ Unicode Data Files include all data files under the directories ++http://www.unicode.org/Public/, http://www.unicode.org/reports/, and ++http://www.unicode.org/cldr/data/. Unicode Data Files do not include PDF ++online code charts under the directory http://www.unicode.org/Public/. ++Software includes any source code published in the Unicode Standard or under ++the directories http://www.unicode.org/Public/, ++http://www.unicode.org/reports/, and http://www.unicode.org/cldr/data/. ++ ++ NOTICE TO USER: Carefully read the following legal agreement. BY ++DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA FILES ++("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY ACCEPT, AND ++AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF ++YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA ++FILES OR SOFTWARE. ++ ++ COPYRIGHT AND PERMISSION NOTICE ++ ++ Copyright ยฉ 1991-2016 Unicode, Inc. All rights reserved. Distributed under ++the Terms of Use in http://www.unicode.org/copyright.html. ++ ++ Permission is hereby granted, free of charge, to any person obtaining a ++copy of the Unicode data files and any associated documentation (the "Data ++Files") or Unicode software and any associated documentation (the "Software") ++to deal in the Data Files or Software without restriction, including without ++limitation the rights to use, copy, modify, merge, publish, distribute, and/or ++sell copies of the Data Files or Software, and to permit persons to whom the ++Data Files or Software are furnished to do so, provided that (a) the above ++copyright notice(s) and this permission notice appear with all copies of the ++Data Files or Software, (b) both the above copyright notice(s) and this ++permission notice appear in associated documentation, and (c) there is clear ++notice in each modified Data File or in the Software as well as in the ++documentation associated with the Data File(s) or Software that the data or ++software has been modified. ++ ++ THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY ++KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD ++PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN ++THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL ++DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR ++PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ++ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE ++DATA FILES OR SOFTWARE. ++ ++ Except as contained in this notice, the name of a copyright holder shall ++not be used in advertising or otherwise to promote the sale, use or other ++dealings in these Data Files or Software without prior written authorization ++of the copyright holder. +diff --git a/Makefile.am b/Makefile.am +index 3c4702c..425d1ec 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -69,6 +69,7 @@ pkgconfig_DATA = ibus-@IBUS_API_VERSION@.pc + + ibus_pc_in = ibus-@IBUS_API_VERSION@.pc.in + EXTRA_DIST = \ ++ COPYING.unicode \ + autogen.sh \ + $(ibus_pc_in) \ + ibus.spec.in \ +diff --git a/configure.ac b/configure.ac +index 3128ef9..f789819 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -608,12 +608,20 @@ AC_ARG_ENABLE(emoji-dict, + ) + AM_CONDITIONAL([ENABLE_EMOJI_DICT], [test x"$enable_emoji_dict" = x"yes"]) + if test x"$enable_emoji_dict" = x"yes"; then +- PKG_CHECK_MODULES(LIBXML2, [ +- libxml-2.0 ++ PKG_CHECK_MODULES(JSON_GLIB1, [ ++ json-glib-1.0 + ]) + enable_emoji_dict="yes (enabled, use --disable-emoji-dict to disable)" + fi + ++AC_ARG_WITH(emoji-json-file, ++ AS_HELP_STRING([--with-emoji-json-file[=DIR/emoji.json]], ++ [Set emoji.json. (default: "/usr/lib/node_modules/emojione/emoji.json")]), ++ EMOJI_JSON_FILE=$with_emoji_json_file, ++ EMOJI_JSON_FILE="/usr/lib/node_modules/emojione/emoji.json" ++) ++AC_SUBST(EMOJI_JSON_FILE) ++ + # Check iso-codes. + PKG_CHECK_MODULES(ISOCODES, [ + iso-codes +@@ -639,6 +647,7 @@ engine/Makefile + util/Makefile + util/IMdkit/Makefile + data/Makefile ++data/annotations/Makefile.am + data/icons/Makefile + data/keymaps/Makefile + data/dconf/Makefile +@@ -698,6 +707,7 @@ Build options: + Enable surrounding-text $enable_surrounding_text + Enable libnotify $enable_libnotify + Enable Emoji dict $enable_emoji_dict ++ emoji.json path $EMOJI_JSON_FILE + Run test cases $enable_tests + ]) + +diff --git a/data/Makefile.am b/data/Makefile.am +index e41c9a2..d9d613f 100644 +--- a/data/Makefile.am ++++ b/data/Makefile.am +@@ -2,8 +2,8 @@ + # + # ibus - The Input Bus + # +-# Copyright (c) 2007-2010 Peng Huang +-# Copyright (c) 2007-2010 Red Hat, Inc. ++# Copyright (c) 2007-2016 Peng Huang ++# Copyright (c) 2007-2016 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 +@@ -21,6 +21,7 @@ + # USA + + SUBDIRS = \ ++ annotations \ + icons \ + keymaps \ + $(NULL) +diff --git a/data/annotations/Makefile.am b/data/annotations/Makefile.am +new file mode 100644 +index 0000000..d87b933 +--- /dev/null ++++ b/data/annotations/Makefile.am +@@ -0,0 +1,27 @@ ++# vim:set noet ts=4: ++# ++# ibus - The Input Bus ++# ++# Copyright (c) 2016 Takao Fujiwara ++# Copyright (c) 2016 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 ++ ++EXTRA_DIST = \ ++ en.xml \ ++ $(NULL) ++ ++-include $(top_srcdir)/git.mk +diff --git a/data/annotations/en.xml b/data/annotations/en.xml +new file mode 100644 +index 0000000..ff7aa89 +--- /dev/null ++++ b/data/annotations/en.xml +@@ -0,0 +1,1042 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ face; grin ++ eye; face; grin; smile ++ face; joy; laugh; tear ++ face; mouth; open; smile ++ eye; face; mouth; open; smile ++ cold; face; open; smile; sweat ++ face; laugh; mouth; open; satisfied; smile ++ face; wink ++ blush; eye; face; smile ++ delicious; face; savouring; smile; um; yum ++ bright; cool; eye; eyewear; face; glasses; smile; sun; sunglasses; weather ++ eye; face; heart; love; smile ++ face; heart; kiss ++ face; kiss ++ eye; face; kiss; smile ++ closed; eye; face; kiss ++ face; outlined; relaxed; smile ++ face; smile ++ face; hug; hugging ++ angel; face; fairy tale; fantasy; halo; innocent; smile ++ face; thinking ++ deadpan; face; neutral ++ expressionless; face; inexpressive; unexpressive ++ face; mouth; quiet; silent ++ eyes; face; rolling ++ face; smirk ++ face; persevere ++ disappointed; face; relieved; whew ++ face; mouth; open; sympathy ++ face; mouth; zipper ++ face; hushed; stunned; surprised ++ face; sleep ++ face; tired ++ face; sleep; zzz ++ face; relieved ++ face; geek; nerd ++ face; tongue ++ eye; face; joke; tongue; wink ++ eye; face; horrible; taste; tongue ++ face; frown ++ face; frown ++ face; unamused; unhappy ++ cold; face; sweat ++ dejected; face; pensive ++ confused; face ++ confounded; face ++ face; upside-down ++ cold; doctor; face; mask; medicine; sick ++ face; ill; sick; thermometer ++ bandage; face; hurt; injury ++ face; money; mouth ++ astonished; face; shocked; totally ++ disappointed; face ++ face; worried ++ face; triumph; won ++ cry; face; sad; tear ++ cry; face; sad; sob; tear ++ face; frown; mouth; open ++ anguished; face ++ face; fear; fearful; scared ++ face; tired; weary ++ face; grimace ++ blue; cold; face; mouth; open; rushed; sweat ++ face; fear; fearful; munch; scared; scream ++ dazed; face; flushed ++ dizzy; face ++ angry; face; mad; pouting; rage; red ++ angry; face; mad ++ face; fairy tale; fantasy; horns; smile ++ demon; devil; face; fairy tale; fantasy ++ creature; face; fairy tale; fantasy; japanese; monster ++ creature; face; fairy tale; fantasy; japanese; monster ++ body; death; face; fairy tale; monster ++ body; crossbones; death; face; monster; skull ++ creature; face; fairy tale; fantasy; monster ++ creature; extraterrestrial; face; fairy tale; fantasy; monster; space; ufo ++ alien; creature; extraterrestrial; face; fairy tale; fantasy; monster; space; ufo ++ face; monster; robot ++ comic; dung; face; monster; poo; poop ++ cat; face; mouth; open; smile ++ cat; eye; face; grin; smile ++ cat; face; joy; tear ++ cat; eye; face; heart; love; smile ++ cat; face; ironic; smile; wry ++ cat; eye; face; kiss ++ cat; face; oh; surprised; weary ++ cat; cry; face; sad; tear ++ cat; face; pouting ++ evil; face; forbidden; gesture; monkey; no; not; prohibited; see ++ evil; face; forbidden; gesture; hear; monkey; no; not; prohibited ++ evil; face; forbidden; gesture; monkey; no; not; prohibited; speak ++ boy ++ maiden; virgin; virgo; zodiac ++ man ++ woman ++ man; old ++ old; woman ++ baby ++ blond ++ cop; officer; police ++ gua pi mao; hat; man ++ man; turban ++ construction; hat; worker ++ aid; cross; face; hat; helmet ++ fairy tale; fantasy ++ guard ++ sleuth; spy ++ celebration; christmas; fairy tale; fantasy; father; santa ++ angel; baby; face; fairy tale; fantasy ++ massage; salon ++ barber; beauty; parlor ++ bride; veil; wedding ++ frown; gesture ++ gesture; pouting ++ forbidden; gesture; hand; no; not; prohibited ++ gesture; hand; ok ++ hand; help; information; sassy ++ gesture; hand; happy; raised ++ apology; bow; gesture; sorry ++ body; celebration; gesture; hand; hooray; raised ++ ask; body; bow; folded; gesture; hand; please; pray; thanks ++ face; head; silhouette; speak; speaking ++ bust; silhouette ++ bust; silhouette ++ hike; walk; walking ++ marathon; running ++ bunny; dancer; ear; girl; woman ++ dancer ++ business; man; suit ++ couple; romance ++ couple; heart; love; romance ++ child; father; mother ++ couple; hand; hold; man; woman ++ couple; gemini; hand; hold; man; twins; zodiac ++ couple; hand; hold; woman ++ emoji modifier; fitzpatrick; skin; tone ++ emoji modifier; fitzpatrick; skin; tone ++ emoji modifier; fitzpatrick; skin; tone ++ emoji modifier; fitzpatrick; skin; tone ++ emoji modifier; fitzpatrick; skin; tone ++ biceps; body; comic; flex; muscle ++ backhand; body; finger; hand; index; point ++ backhand; body; finger; hand; index; point ++ body; finger; hand; index; point; up ++ backhand; body; finger; hand; index; point; up ++ body; finger; hand ++ backhand; body; down; finger; hand; index; point ++ body; hand; v; victory ++ body; finger; hand; spock; vulcan ++ body; finger; hand; horns; rock-on ++ body; finger; hand; splayed ++ body; hand ++ body; hand; ok ++ +1; body; hand; thumb; up ++ -1; body; down; hand; thumb ++ body; clenched; fist; hand; punch ++ body; clenched; fist; hand; punch ++ body; hand; wave; waving ++ body; clap; hand ++ body; hand; open ++ body; hand; write ++ body; care; cosmetics; manicure; nail; polish ++ body ++ body ++ body; clothing; footprint; print ++ body; eye; face ++ body ++ body ++ body; lips ++ heart; kiss; lips; mark; romance ++ arrow; cupid; heart; romance ++ heart ++ beating; heart; heartbeat; pulsating ++ break; broken; heart ++ heart; love ++ excited; heart; sparkle ++ excited; growing; heart; heartpulse; nervous ++ blue; heart ++ green; heart ++ heart; yellow ++ heart; purple ++ heart; ribbon; valentine ++ heart; revolving ++ heart ++ exclamation; heart; mark; punctuation ++ heart; letter; love; mail; romance ++ comic; sleep ++ angry; comic; mad ++ comic ++ boom; comic ++ comic; splashing; sweat ++ comic; dash; running ++ comic; star ++ balloon; bubble; comic; dialog; speech ++ dialog; speech ++ angry; balloon; bubble; mad ++ balloon; bubble; comic; thought ++ hole ++ clothing; eye; eyeglasses; eyewear ++ dark; eye; eyewear; glasses ++ clothing ++ clothing; shirt; tshirt ++ clothing; pants; trousers ++ clothing ++ clothing ++ clothing; swim ++ clothing; woman ++ clothing; coin ++ bag; clothing ++ bag; clothing ++ bag; hotel; shopping ++ bag; satchel; school ++ clothing; man; shoe ++ athletic; clothing; shoe; sneaker ++ clothing; heel; shoe; woman ++ clothing; sandal; shoe; woman ++ boot; clothing; shoe; woman ++ clothing; king; queen ++ clothing; hat; woman ++ clothing; hat; top; tophat ++ cap; celebration; clothing; graduation; hat ++ beads; clothing; necklace; prayer; religion ++ cosmetics; makeup ++ diamond; romance ++ diamond; gem; jewel; romance ++ face; monkey ++ monkey ++ dog; face; pet ++ pet ++ dog ++ face; wolf ++ cat; face; pet ++ pet ++ face; leo; lion; zodiac ++ face; tiger ++ tiger ++ leopard ++ face; horse ++ racehorse; racing ++ face; unicorn ++ cow; face ++ bull; taurus; zodiac ++ buffalo; water ++ cow ++ face; pig ++ sow ++ pig ++ face; nose; pig ++ aries; sheep; zodiac ++ ewe ++ capricorn; zodiac ++ dromedary; hump ++ bactrian; camel; hump ++ elephant ++ face; mouse ++ mouse ++ rat ++ face; hamster; pet ++ bunny; face; pet; rabbit ++ bunny; pet ++ chipmunk ++ bear; face ++ bear ++ face; panda ++ feet; paw; print ++ turkey ++ chicken ++ rooster ++ baby; chick; hatching ++ baby; chick ++ baby; chick ++ bird ++ penguin ++ bird; fly; peace ++ face; frog ++ crocodile ++ turtle ++ bearer; ophiuchus; serpent; zodiac ++ dragon; face; fairy tale ++ fairy tale ++ face; spouting; whale ++ whale ++ flipper ++ pisces; zodiac ++ fish; tropical ++ fish ++ octopus ++ shell; spiral ++ cancer; zodiac ++ snail ++ insect ++ insect ++ bee; insect ++ beetle; insect; ladybird; ladybug ++ insect ++ spider; web ++ scorpio; scorpius; zodiac ++ flower; plant; romance ++ blossom; cherry; flower; plant ++ flower ++ plant ++ flower; plant ++ flower; plant ++ flower; plant; sun ++ flower; plant ++ flower; plant ++ plant; young ++ plant; tree ++ deciduous; plant; shedding; tree ++ palm; plant; tree ++ plant ++ ear; plant; rice ++ leaf; plant ++ plant ++ 4; clover; four; leaf; plant ++ falling; leaf; maple; plant ++ falling; leaf; plant ++ blow; flutter; leaf; plant; wind ++ fruit; grape; plant ++ fruit; plant ++ fruit; plant ++ fruit; orange; plant ++ citrus; fruit; plant ++ fruit; plant ++ fruit; plant ++ apple; fruit; plant; red ++ apple; fruit; green; plant ++ fruit; plant ++ fruit; plant ++ cherry; fruit; plant ++ berry; fruit; plant ++ plant; vegetable ++ aubergine; plant; vegetable ++ corn; ear; maize; maze; plant ++ hot; pepper; plant ++ plant ++ plant ++ loaf ++ cheese ++ bone; meat ++ bone; chicken; leg; poultry ++ burger ++ french; fries ++ cheese; slice ++ frankfurter; hotdog; sausage ++ mexican ++ mexican ++ popcorn ++ pot; stew ++ bento; box ++ cracker; rice ++ ball; japanese; rice ++ cooked; rice ++ curry; rice ++ bowl; noodle; ramen; steaming ++ pasta ++ potato; roasted; sweet ++ kebab; seafood; skewer; stick ++ sushi ++ fried; prawn; shrimp; tempura ++ cake; fish; pastry; swirl ++ dessert; japanese; skewer; stick; sweet ++ cream; dessert; ice; icecream; soft; sweet ++ dessert; ice; shaved; sweet ++ cream; dessert; ice; sweet ++ dessert; donut; sweet ++ dessert; sweet ++ birthday; cake; celebration; dessert; pastry; sweet ++ cake; dessert; pastry; slice; sweet ++ bar; chocolate; dessert; sweet ++ dessert; sweet ++ candy; dessert; sweet ++ dessert; pudding; sweet ++ honey; honeypot; pot; sweet ++ baby; bottle; drink; milk ++ beverage; coffee; drink; hot; steaming; tea ++ beverage; cup; drink; tea; teacup ++ bar; beverage; bottle; cup; drink ++ bar; bottle; cork; drink; popping ++ bar; beverage; drink; glass; wine ++ bar; cocktail; drink; glass ++ bar; drink; tropical ++ bar; beer; drink; mug ++ bar; beer; clink; drink; mug ++ cooking; fork; knife; plate ++ cooking; fork; knife ++ egg; frying; pan ++ aquarius; cooking; drink; jug; tool; weapon; zodiac ++ africa; earth; europe; globe; world ++ americas; earth; globe; world ++ asia; australia; earth; globe; world ++ earth; globe; meridians; world ++ map; world ++ cold; mountain; snow ++ mountain ++ eruption; mountain; weather ++ fuji; mountain ++ camping ++ beach; umbrella ++ desert ++ desert; island ++ park ++ stadium ++ building; classical ++ building; construction ++ building; house ++ building; city ++ building; derelict; house ++ building; home; house ++ building; garden; home; house ++ building; christian; cross; religion ++ islam; muslim; religion ++ islam; muslim; religion ++ jew; jewish; religion; temple ++ religion; shinto; shrine ++ building ++ building; japanese; post ++ building; european; post ++ building; doctor; medicine ++ building ++ building ++ building; hotel; love ++ building; convenience; store ++ building ++ building; department; store ++ building ++ building; castle; japanese ++ building; european ++ chapel; romance ++ tokyo; tower ++ liberty; statue ++ japan; map ++ fountain ++ camping ++ fog; weather ++ night; star; weather ++ morning; mountain; sun; sunrise; weather ++ morning; sun; weather ++ building; city; dusk; evening; landscape; sun; sunset; weather ++ building; dusk; sun; weather ++ bridge; night; weather ++ hot; hotsprings; springs; steaming ++ space; weather ++ carousel; horse ++ amusement park; ferris; wheel ++ amusement park; coaster; roller ++ barber; haircut; pole ++ circus; tent ++ art; mask; performing; theater; theatre ++ art; frame; museum; painting; picture ++ art; museum; painting; palette ++ game; slot ++ engine; railway; steam; train; vehicle ++ car; electric; railway; train; tram; trolleybus; vehicle ++ railway; shinkansen; speed; train; vehicle ++ bullet; railway; shinkansen; speed; train; vehicle ++ railway; vehicle ++ subway; vehicle ++ railway; vehicle ++ railway; train; vehicle ++ trolleybus; vehicle ++ vehicle ++ car; mountain; railway; vehicle ++ car; tram; trolleybus; vehicle ++ vehicle ++ bus; oncoming; vehicle ++ bus; tram; trolley; vehicle ++ bus; busstop; stop ++ bus; vehicle ++ vehicle ++ engine; fire; truck; vehicle ++ car; patrol; police; vehicle ++ car; oncoming; police; vehicle ++ vehicle ++ oncoming; taxi; vehicle ++ car; vehicle ++ automobile; car; oncoming; vehicle ++ recreational; rv; vehicle ++ delivery; truck; vehicle ++ lorry; semi; truck; vehicle ++ vehicle ++ bike; vehicle ++ fuel; fuelpump; gas; pump; station ++ highway; road ++ railway; train ++ beacon; car; light; police; revolving; vehicle ++ light; signal; traffic ++ light; signal; traffic ++ barrier ++ ship; tool ++ boat; resort; sea; vehicle; yacht ++ boat; vehicle ++ boat; vehicle ++ passenger; ship; vehicle ++ boat ++ boat; motorboat; vehicle ++ vehicle ++ vehicle ++ airplane; vehicle ++ airplane; check-in; departure; departures; vehicle ++ airplane; arrivals; arriving; landing; vehicle ++ chair ++ vehicle ++ railway; suspension; vehicle ++ cable; gondola; mountain; vehicle ++ aerial; cable; car; gondola; ropeway; tramway; vehicle ++ space; vehicle ++ space; vehicle ++ bell; bellhop; hotel ++ door ++ hotel; sleep ++ hotel; sleep ++ couch; hotel; lamp ++ toilet ++ water ++ bathtub; bath ++ bath ++ sand; timer ++ hourglass; sand; timer ++ clock ++ alarm; clock ++ clock ++ clock; timer ++ clock ++ 00; 12; 12:00; clock; oโ€™clock; twelve ++ 12; 12:30; 30; clock; thirty; twelve ++ 00; 1; 1:00; clock; oโ€™clock; one ++ 1; 1:30; 30; clock; one; thirty ++ 00; 2; 2:00; clock; oโ€™clock; two ++ 2; 2:30; 30; clock; thirty; two ++ 00; 3; 3:00; clock; oโ€™clock; three ++ 3; 3:30; 30; clock; thirty; three ++ 00; 4; 4:00; clock; four; oโ€™clock ++ 30; 4; 4:30; clock; four; thirty ++ 00; 5; 5:00; clock; five; oโ€™clock ++ 30; 5; 5:30; clock; five; thirty ++ 00; 6; 6:00; clock; oโ€™clock; six ++ 30; 6; 6:30; clock; six; thirty ++ 00; 7; 7:00; clock; oโ€™clock; seven ++ 30; 7; 7:30; clock; seven; thirty ++ 00; 8; 8:00; clock; eight; oโ€™clock ++ 30; 8; 8:30; clock; eight; thirty ++ 00; 9; 9:00; clock; nine; oโ€™clock ++ 30; 9; 9:30; clock; nine; thirty ++ 00; 10; 10:00; clock; oโ€™clock; ten ++ 10; 10:30; 30; clock; ten; thirty ++ 00; 11; 11:00; clock; eleven; oโ€™clock ++ 11; 11:30; 30; clock; eleven; thirty ++ dark; moon; space; weather ++ crescent; moon; space; waxing; weather ++ moon; quarter; space; weather ++ gibbous; moon; space; waxing; weather ++ full; moon; space; weather ++ gibbous; moon; space; waning; weather ++ moon; quarter; space; weather ++ crescent; moon; space; waning; weather ++ crescent; moon; space; weather ++ face; moon; space; weather ++ face; moon; quarter; space; weather ++ face; moon; quarter; space; weather ++ weather ++ bright; rays; space; sunny; weather ++ bright; face; full; moon; space; weather ++ bright; face; space; sun; weather ++ star ++ glittery; glow; shining; sparkle; star ++ falling; shooting; space; star ++ weather ++ cloud; sun; weather ++ cloud; rain; thunder; weather ++ cloud; sun; weather ++ cloud; sun; weather ++ cloud; rain; sun; weather ++ cloud; rain; weather ++ cloud; cold; snow; weather ++ cloud; lightning; weather ++ cloud; weather; whirlwind ++ cloud; weather ++ blow; cloud; face; weather; wind ++ dizzy; twister; typhoon; weather ++ rain; weather ++ clothing; rain; umbrella; weather ++ clothing; rain; weather ++ clothing; drop; rain; umbrella; weather ++ rain; sun; umbrella; weather ++ danger; electric; electricity; lightning; voltage; zap ++ cold; snow; weather ++ cold; snow; weather ++ cold; snow; snowman; weather ++ space ++ flame; tool ++ cold; comic; drop; sweat; weather ++ ocean; water; wave; weather ++ celebration; halloween; jack; lantern ++ celebration; christmas; tree ++ celebration ++ celebration; fireworks; sparkle ++ sparkle; star ++ celebration ++ celebration; party; popper; tada ++ ball; celebration; confetti ++ banner; celebration; japanese; tree ++ celebration; cross; crossed; japanese ++ bamboo; celebration; japanese; pine; plant ++ celebration; doll; festival; japanese ++ carp; celebration; streamer ++ bell; celebration; chime; wind ++ celebration; ceremony; moon ++ celebration ++ box; celebration; gift; present; wrapped ++ celebration; medal; military ++ celebration; reminder; ribbon ++ cinema; film; frames; movie ++ admission; ticket ++ admission ++ label ++ ball; soccer ++ ball ++ ball; hoop ++ american; ball; football ++ ball; football; rugby ++ ball; racquet ++ 8; 8 ball; ball; billiard; eight; game ++ ball; game ++ golf; hole ++ ball; golf ++ ice; skate ++ fish; pole ++ running; sash; shirt ++ ski; snow ++ ski; snow ++ ski; snow; snowboard ++ surfing ++ horse; jockey; racehorse; racing ++ swim ++ ball ++ lifter; weight ++ bicycle; bike; cyclist ++ bicycle; bicyclist; bike; cyclist; mountain ++ car; racing ++ racing ++ medal ++ prize ++ ball; bat; game ++ ball; game ++ ball; field; game; hockey; stick ++ game; hockey; ice; puck; stick ++ ball; bat; game; paddle; table tennis ++ birdie; game; racquet; shuttlecock ++ bull; bullseye; dart; eye; game; hit; target ++ controller; game ++ game; video game ++ dice; die; game ++ card; game; spade; suit ++ card; game; heart; hearts; suit ++ card; diamond; diamonds; game; suit ++ card; club; clubs; game; suit ++ card; game; playing ++ game; mahjong; red ++ card; flower; game; japanese; playing ++ mute; quiet; silent; speaker; volume ++ volume ++ low; speaker; volume; wave ++ 3; high; loud; speaker; three; volume ++ loud; public address ++ cheering ++ horn; post; postal ++ bell ++ bell; forbidden; mute; no; not; prohibited; quiet; silent ++ music; score ++ music; note ++ music; note; notes ++ mic; microphone; music; studio ++ level; music; slider ++ control; knobs; music ++ karaoke; mic ++ earbud ++ instrument; music; sax ++ instrument; music ++ instrument; keyboard; music; piano ++ instrument; music ++ instrument; music ++ video ++ cell; mobile; phone; telephone ++ arrow; call; cell; mobile; phone; receive; telephone ++ phone ++ phone; receiver; telephone ++ pager ++ fax ++ battery ++ electric; electricity; plug ++ computer; pc; personal ++ computer; desktop ++ computer ++ computer ++ 3; button; computer; mouse; three ++ computer ++ computer; disk; minidisk; optical ++ computer; disk; floppy ++ dvd; blu-ray; cd; computer; disk; optical ++ blu-ray; cd; computer; disk; optical ++ camera; cinema; movie ++ clapper; movie ++ cinema; film; movie; projector; video ++ tv; video ++ video ++ camera; flash; video ++ camera; video ++ tape; vhs; video ++ glass; magnifying; search; tool ++ glass; magnifying; search; tool ++ tool ++ tool ++ antenna; dish; satellite ++ light ++ bulb; comic; electric; idea; light ++ electric; light; tool; torch ++ bar; japanese; lantern; light; red ++ book; cover; decorated; notebook ++ book; closed ++ book; open ++ book; green ++ blue; book ++ book; orange ++ book ++ notebook ++ notebook ++ curl; document; page ++ paper ++ document; page ++ news; paper ++ news; newspaper; paper; rolled ++ bookmark; mark; marker; tabs ++ mark ++ bag; dollar; money; moneybag ++ bank; banknote; bill; currency; money; note; yen ++ bank; banknote; bill; currency; dollar; money; note ++ bank; banknote; bill; currency; euro; money; note ++ bank; banknote; bill; currency; money; note; pound ++ bank; banknote; bill; dollar; fly; money; note; wings ++ bank; card; credit; money ++ bank; chart; currency; graph; growth; market; money; rise; trend; upward; yen ++ e-mail; email ++ email; letter; mail ++ e-mail; email; envelope; incoming; letter; mail; receive ++ arrow; down; e-mail; email; envelope; letter; mail; outgoing; sent ++ box; letter; mail; outbox; sent; tray ++ box; inbox; letter; mail; receive; tray ++ box; parcel ++ closed; mail; mailbox; postbox ++ closed; lowered; mail; mailbox; postbox ++ mail; mailbox; open; postbox ++ lowered; mail; mailbox; open; postbox ++ mail; mailbox ++ ballot; box ++ pencil ++ nib; pen ++ fountain; pen ++ ballpoint ++ painting ++ crayon ++ pencil ++ briefcase ++ file; folder ++ file; folder; open ++ card; dividers; index ++ date ++ calendar ++ note; pad; spiral ++ calendar; pad; spiral ++ card; index; rolodex ++ chart; graph; growth; trend; upward ++ chart; down; graph; trend ++ bar; chart; graph ++ clipboard ++ pin ++ pin; pushpin ++ paperclip ++ link; paperclip ++ ruler; straight edge ++ ruler; set; triangle ++ tool ++ box; card; file ++ cabinet; file ++ wastebasket ++ closed ++ lock; open; unlock ++ ink; lock; nib; pen; privacy ++ closed; key; lock; secure ++ lock; password ++ clue; key; lock; old ++ tool ++ mining; tool ++ hammer; pick; tool ++ hammer; tool; wrench ++ tool ++ bolt; nut; tool ++ tool ++ tool; vice ++ chemistry; tool ++ balance; justice; libra; scales; tool; weight; zodiac ++ link ++ chain ++ doctor; medicine; needle; shot; sick; tool ++ doctor; medicine; sick ++ knife; weapon ++ cooking; hocho; knife; tool; weapon ++ crossed; swords; weapon ++ gun; handgun; revolver; tool; weapon ++ weapon ++ archer; arrow; bow; sagittarius; tool; weapon; zodiac ++ checkered; chequered; racing ++ waving ++ waving ++ post ++ smoking ++ death ++ death; funeral; urn ++ face; moyai; statue ++ drum; oil ++ ball; crystal; fairy tale; fantasy; fortune; tool ++ atm; automated; bank; teller ++ litter; litterbox ++ drink; potable; water ++ access ++ lavatory; man; restroom; wc ++ lavatory; restroom; wc; woman ++ lavatory; wc ++ baby; changing ++ closet; lavatory; restroom; water; wc ++ control; passport ++ customs ++ baggage; claim ++ baggage; locker; luggage ++ warning ++ child; crossing; pedestrian; traffic ++ entry; forbidden; no; not; prohibited; traffic ++ entry; forbidden; no; not ++ bicycle; bike; forbidden; no; not; prohibited; vehicle ++ forbidden; no; not; prohibited; smoking ++ forbidden; litter; no; not; prohibited ++ drink; forbidden; no; not; potable; prohibited; water ++ forbidden; no; not; pedestrian; prohibited ++ radioactive ++ biohazard ++ arrow; cardinal; direction; north ++ arrow; direction; intercardinal; northeast ++ arrow; cardinal; direction; east ++ arrow; direction; intercardinal; southeast ++ arrow; cardinal; direction; down; south ++ arrow; direction; intercardinal; southwest ++ arrow; cardinal; direction; west ++ arrow; direction; intercardinal; northwest ++ arrow ++ arrow ++ arrow ++ arrow ++ arrow ++ arrow; down ++ arrow; clockwise; reload ++ anticlockwise; arrow; counterclockwise; withershins ++ arrow; back ++ arrow; end ++ arrow; mark; on ++ arrow; soon ++ arrow; top; up ++ religion; worship ++ atheist; atom ++ hindu; religion ++ david; jew; jewish; religion; star ++ buddhist; dharma; religion; wheel ++ religion; tao; taoist; yang; yin ++ christian; cross; religion ++ christian; cross; religion ++ islam; muslim; religion ++ peace ++ candelabrum; candlestick; religion ++ fortune; star ++ recycle ++ badge; name ++ fleur-de-lis ++ beginner; chevron; green; japanese; leaf; tool; yellow ++ anchor; emblem; ship; tool; trident ++ circle; o ++ check; mark ++ ballot; box; check ++ check; mark ++ cancel; multiplication; multiply; x ++ cancel; mark; multiplication; multiply; x ++ mark; square ++ math; plus ++ math; minus ++ division; math ++ curl; loop ++ curl; double; loop ++ mark; part ++ asterisk ++ star ++ sparkle ++ bank; currency; exchange; money ++ currency; dollar; money ++ bangbang; exclamation; mark; punctuation ++ exclamation; interrobang; mark; punctuation; question ++ mark; punctuation; question ++ mark; outlined; punctuation; question ++ exclamation; mark; outlined; punctuation ++ exclamation; mark; punctuation ++ dash; punctuation; wavy ++ copyright ++ registered ++ mark; tm; trademark ++ ram; zodiac ++ bull; ox; zodiac ++ twins; zodiac ++ crab; zodiac ++ lion; zodiac ++ maiden; virgin; zodiac ++ balance; justice; scales; zodiac ++ scorpio; scorpion; zodiac ++ archer; zodiac ++ goat; zodiac ++ bearer; water; zodiac ++ fish; zodiac ++ bearer; serpent; snake; zodiac ++ arrow; crossed ++ arrow; clockwise; repeat ++ arrow; clockwise; once ++ arrow; play; right; triangle ++ arrow; double; fast; forward ++ arrow; next scene; next track; triangle ++ arrow; pause; play; right; triangle ++ arrow; left; reverse; triangle ++ arrow; double; rewind ++ arrow; previous scene; previous track; triangle ++ arrow; button; red ++ arrow; double ++ arrow; button; down; red ++ arrow; double; down ++ bar; double; pause; vertical ++ square; stop ++ circle; record ++ eject ++ camera; film; movie ++ brightness; dim; low ++ bright; brightness ++ antenna; bar; cell; mobile; phone; signal; telephone ++ cell; forbidden; mobile; no; not; phone; prohibited; telephone ++ cell; mobile; mode; phone; telephone; vibration ++ cell; mobile; off; phone; telephone ++ hash; keycap; pound ++ asterisk; keycap; star ++ 0; keycap; zero ++ 1; keycap; one ++ 2; keycap; two ++ 3; keycap; three ++ 4; four; keycap ++ 5; five; keycap ++ 6; keycap; six ++ 7; keycap; seven ++ 8; eight; keycap ++ 9; keycap; nine ++ 10; keycap; ten ++ 100; full; hundred; score ++ 18; age restriction; eighteen; forbidden; no; not; prohibited; underage ++ input; latin; letters; uppercase ++ abcd; input; latin; letters; lowercase ++ 1234; input; numbers ++ input ++ abc; alphabet; input; latin; letters ++ a; blood ++ ab; blood ++ b; blood ++ cl ++ cool ++ free ++ i; information ++ id; identity ++ circle; m ++ new ++ ng ++ blood; o ++ ok ++ parking ++ help; sos ++ mark; up ++ versus; vs ++ japanese ++ japanese ++ japanese ++ japanese ++ japanese ++ japanese ++ japanese ++ japanese ++ japanese ++ chinese ++ chinese ++ chinese ++ chinese ++ chinese; congratulation; congratulations; ideograph ++ chinese; ideograph; secret ++ chinese ++ chinese ++ geometric; square ++ geometric; square ++ geometric; square ++ geometric; square ++ geometric; square ++ geometric; square ++ geometric; square ++ geometric; square ++ diamond; geometric; orange ++ blue; diamond; geometric ++ diamond; geometric; orange ++ blue; diamond; geometric ++ geometric; red ++ down; geometric; red ++ comic; diamond; geometric; inside ++ button; geometric; radio ++ button; geometric; square ++ button; geometric; outlined; square ++ circle; geometric ++ circle; geometric ++ circle; geometric; red ++ blue; circle; geometric ++ ++ +\ No newline at end of file +diff --git a/ibus.spec.in b/ibus.spec.in +index 7fdaccb..40aead1 100644 +--- a/ibus.spec.in ++++ b/ibus.spec.in +@@ -5,6 +5,7 @@ + + # Build flags + %define build_python_library 0 ++%define build_emoji_dictionary 1 + + %define glib_ver %([ -a %{_libdir}/pkgconfig/glib-2.0.pc ] && pkg-config --modversion glib-2.0 | cut -d. -f 1,2 || echo -n "999") + %define gconf2_version 2.12.0 +@@ -40,6 +41,10 @@ BuildRequires: dconf-devel + BuildRequires: pygobject2-devel + BuildRequires: intltool + BuildRequires: iso-codes-devel ++%if %build_emoji_dictionary ++BuildRequires: nodejs-emojione ++BuildRequires: json-glib ++%endif + + Requires: %{name}-libs = %{version}-%{release} + Requires: %{name}-gtk2 = %{version}-%{release} +@@ -151,6 +156,12 @@ OPTIONS="$OPTIONS --enable-python-library" + %else + OPTIONS="$OPTIONS --disable-python-library" + %endif ++%if %build_emoji_dictionary ++OPTIONS="$OPTIONS --enable-emoji-dict" ++%else ++OPTIONS="$OPTIONS --disable-emoji-dict" ++%endif ++ + + %configure $OPTIONS + +@@ -258,6 +269,9 @@ dconf update &> /dev/null || : + %{_bindir}/ibus-setup + %{_datadir}/ibus/component/gtkpanel.xml + %{_datadir}/ibus/component/simple.xml ++%if %build_emoji_dictionary ++%{_datadir}/ibus/dicts ++%endif + %{_datadir}/ibus/keymaps/* + %{_datadir}/ibus/setup/* + +diff --git a/src/Makefile.am b/src/Makefile.am +index 22e031f..b58ab95 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -29,15 +29,6 @@ endif + + SUBDIRS = . $(TESTS_DIR) + +-IBUS_V_wget = $(ibus__v_wget_@AM_V@) +-ibus__v_wget_ = $(ibus__v_wget_@AM_DEFAULT_V@) +-ibus__v_wget_0 = -nv +-ibus__v_wget_1 = +-IBUS_V_diff = $(ibus__v_diff_@AM_V@) +-ibus__v_diff_ = $(ibus__v_diff_@AM_DEFAULT_V@) +-ibus__v_diff_0 = -q +-ibus__v_diff_1 = +- + # libibus = libibus-@IBUS_API_VERSION@.la + libibus = libibus-1.0.la + +@@ -248,37 +239,25 @@ dict_DATA = emoji.dict + + noinst_PROGRAMS = emoji-parser + +-emoji-list.html: +- $(AM_V_at)wget $(IBUS_V_wget) \ +- http://ibus.github.io/files/ibus/emoji-list.html +- $(AM_V_at)wget $(IBUS_V_wget) \ +- http://unicode.org/emoji/charts/emoji-list.html \ +- -O latest-emoji-list.html +- $(AM_V_at)diff $(IBUS_V_diff) emoji-list.html latest-emoji-list.html; \ +- if test $$? -ne 0; then \ +- echo "#### WARNING: emoji-list.html is old." >&2; \ +- fi; \ +- rm latest-emoji-list.html; +- +-emoji.dict: emoji-parser emoji-list.html +- $(builddir)/emoji-parser emoji-list.html $@ ++emoji.dict: emoji-parser ++ $(builddir)/emoji-parser --xml $(srcdir)/../data/annotations/en.xml \ ++ --json $(EMOJI_JSON_FILE) --out $@ + + emoji_parser_SOURCES = \ + emoji-parser.c \ + $(NULL) + emoji_parser_CFLAGS = \ + $(GLIB2_CFLAGS) \ +- $(LIBXML2_CFLAGS) \ ++ $(JSON_GLIB1_CFLAGS) \ + $(NULL) + emoji_parser_LDADD = \ + $(GLIB2_LIBS) \ +- $(LIBXML2_LIBS) \ ++ $(JSON_GLIB1_LIBS) \ + $(libibus) \ + $(NULL) + + CLEANFILES += \ + $(dict_DATA) \ +- emoji-list.html \ + $(NULL) + endif + +diff --git a/src/emoji-parser.c b/src/emoji-parser.c +index cf92fee..e97f266 100644 +--- a/src/emoji-parser.c ++++ b/src/emoji-parser.c +@@ -20,47 +20,59 @@ + * USA + */ + +-/* Convert http://www.unicode.org/emoji/charts/emoji-list.html ++/* Convert ../data/annotations/en.xml and ++ * /usr/lib/node_modules/emojione/emoji.json + * to the dictionary file which look up the Emoji from the annotation. ++ * Get emoji.json with 'npm install -g emojione'. ++ * en.xml is used for the Unicode annotations and emoji.json is used ++ * for the aliases_ascii, e.g. ":)", and category, e.g. "people". + */ + + #include +-#include +-#include ++#include ++ ++#include + + #include "ibusutil.h" + + typedef struct _EmojiData EmojiData; + struct _EmojiData { +- gchar *class; + gchar *emoji; +- GSList *annotates; +- GSList *prev_annotates; ++ GSList *annotations; ++ gboolean is_annotation; + GHashTable *dict; + }; + +-const gchar *progname; +- +-static gboolean parse_node (xmlNode *node, +- gboolean is_child, +- const gchar *prop_name, +- EmojiData *data); +- + static void +-usage (void) ++reset_emoji_element (EmojiData *data) + { +- g_print ("%s emoji-list.html emoji.dict\n", progname); ++ g_assert (data != NULL); ++ ++ g_clear_pointer (&data->emoji, g_free); ++ g_slist_free_full (data->annotations, g_free); ++ data->annotations = NULL; + } + + static void +-reset_emoji_element (EmojiData *data) ++update_emoji_dict (EmojiData *data) + { +- g_clear_pointer (&data->class, g_free); +- g_clear_pointer (&data->emoji, g_free); +- if (data->annotates) { +- g_slist_free_full (data->prev_annotates, g_free); +- data->prev_annotates = data->annotates; +- data->annotates = NULL; ++ GSList *annotations = data->annotations; ++ while (annotations) { ++ const gchar *annotation = (const gchar *) annotations->data; ++ GSList *emojis = g_hash_table_lookup (data->dict, annotation); ++ if (emojis) { ++ GSList *duplicated = g_slist_find_custom (emojis, ++ data->emoji, ++ (GCompareFunc) g_strcmp0); ++ if (duplicated != NULL) ++ continue; ++ emojis = g_slist_copy_deep (emojis, (GCopyFunc) g_strdup, NULL); ++ } ++ emojis = g_slist_append (emojis, g_strdup (data->emoji)); ++ g_hash_table_replace (data->dict, ++ g_strdup (annotation), ++ emojis); ++ annotations = annotations->next; + } + } + +@@ -70,147 +82,486 @@ free_dict_words (gpointer list) + g_slist_free_full (list, g_free); + } + +-static gboolean +-parse_attr (xmlAttr *attr, +- EmojiData *data) +-{ +- if (g_strcmp0 ((const gchar *) attr->name, "class") == 0 && attr->children) +- parse_node (attr->children, TRUE, (const gchar *) attr->name, data); +- if (g_strcmp0 ((const gchar *) attr->name, "target") == 0 && attr->children) +- parse_node (attr->children, TRUE, (const gchar *) attr->name, data); +- if (attr->next) +- parse_attr (attr->next, data); +- return TRUE; +-} ++static void ++unicode_annotations_start_element_cb (GMarkupParseContext *context, ++ const gchar *element_name, ++ const gchar **attribute_names, ++ const gchar **attribute_values, ++ gpointer user_data, ++ GError **error) ++{ ++ EmojiData *data = (EmojiData *) user_data; ++ int i; ++ const gchar *attribute; ++ const gchar *value; + +-static gboolean +-parse_node (xmlNode *node, +- gboolean is_child, +- const gchar *prop_name, +- EmojiData *data) +-{ +- if (g_strcmp0 ((const gchar *) node->name, "tr") == 0) { +- GSList *annotates = data->annotates; +- while (annotates) { +- GSList *emojis = g_hash_table_lookup (data->dict, annotates->data); +- if (emojis) { +- emojis = g_slist_copy_deep (emojis, (GCopyFunc) g_strdup, NULL); ++ g_assert (data != NULL); ++ ++ if (g_strcmp0 (element_name, "annotation") != 0) ++ return; ++ ++ reset_emoji_element (data); ++ ++ for (i = 0; (attribute = attribute_names[i]) != NULL; i++) { ++ value = attribute_values[i]; ++ ++ if (g_strcmp0 (attribute, "cp") == 0) { ++ if (value == NULL || *value == '\0') { ++ g_warning ("cp='' in unicode.org annotations file"); ++ return; ++ } else if (value[0] != '[' || value[strlen(value) - 1] != ']') { ++ g_warning ("cp!='[emoji]' in unicode.org annotations file"); ++ return; + } +- emojis = g_slist_append (emojis, g_strdup (data->emoji)); +- g_hash_table_replace (data->dict, +- g_strdup (annotates->data), +- emojis); +- annotates = annotates->next; ++ data->emoji = g_strndup (value + 1, strlen(value) - 2); + } +- reset_emoji_element (data); +- } +- /* if node->name is "text" and is_child is FALSE, +- * it's '\n' or Space between and . +- */ +- if (g_strcmp0 ((const gchar *) node->name, "text") == 0 && is_child) { +- /* Get "chars" in */ +- if (g_strcmp0 (prop_name, "class") == 0) { +- if (g_strcmp0 (data->class, (const gchar *) node->content) != 0) { +- g_clear_pointer (&data->class, g_free); +- data->class = g_strdup ((const gchar *) node->content); ++ else if (g_strcmp0 (attribute, "tts") == 0) { ++ GSList *duplicated = g_slist_find_custom (data->annotations, ++ value, ++ (GCompareFunc) g_strcmp0); ++ if (duplicated == NULL) { ++ data->annotations = g_slist_prepend (data->annotations, ++ g_strdup (value)); + } + } +- /* Get "annotate" in */ +- if (g_strcmp0 (prop_name, "target") == 0 && +- g_strcmp0 (data->class, "name") == 0) { +- g_clear_pointer (&data->class, g_free); +- data->class = g_strdup ((const gchar *) node->content); +- } +- /* Get "emoji" in emoji */ +- if (g_strcmp0 (prop_name, "td") == 0 && +- g_strcmp0 (data->class, "chars") == 0) { +- data->emoji = g_strdup ((const gchar *) node->content); +- } +- /* We ignore "NAME" for NAME but +- * takes "ANNOTATE" for +- * ANNOTATE +- */ +- if (g_strcmp0 (prop_name, "td") == 0 && +- g_strcmp0 (data->class, "name") == 0) { +- g_slist_free_full (data->annotates, g_free); +- data->annotates = NULL; ++ } ++ ++ data->is_annotation = TRUE; ++} ++ ++static void ++unicode_annotations_end_element_cb (GMarkupParseContext *context, ++ const gchar *element_name, ++ gpointer user_data, ++ GError **error) ++{ ++ EmojiData *data = (EmojiData *) user_data; ++ ++ g_assert (data != NULL); ++ if (!data->is_annotation) ++ return; ++ ++ update_emoji_dict (data); ++ data->is_annotation = FALSE; ++} ++ ++void ++unicode_annotations_text_cb (GMarkupParseContext *context, ++ const gchar *text, ++ gsize text_len, ++ gpointer user_data, ++ GError **error) ++{ ++ EmojiData *data = (EmojiData *) user_data; ++ gchar **annotations = NULL; ++ const gchar *annotation; ++ int i; ++ ++ g_assert (data != NULL); ++ if (!data->is_annotation) ++ return; ++ annotations = g_strsplit (text, "; ", -1); ++ for (i = 0; (annotation = annotations[i]) != NULL; i++) { ++ GSList *duplicated = g_slist_find_custom (data->annotations, ++ annotation, ++ (GCompareFunc) g_strcmp0); ++ if (duplicated == NULL) { ++ data->annotations = g_slist_prepend (data->annotations, ++ g_strdup (annotation)); + } +- /* Get "ANNOTATE" in +- * ANNOTATE +- */ +- if (g_strcmp0 (prop_name, "a") == 0 && +- g_strcmp0 (data->class, "annotate") == 0) { +- data->annotates = +- g_slist_append (data->annotates, +- g_strdup ((const gchar *) node->content)); ++ } ++ g_strfreev (annotations); ++} ++ ++static gboolean ++unicode_annotations_parse_xml_file (const gchar *filename, ++ GHashTable *dict) ++{ ++ gchar *content = NULL; ++ gsize length = 0; ++ GError *error = NULL; ++ const static GMarkupParser parser = { ++ unicode_annotations_start_element_cb, ++ unicode_annotations_end_element_cb, ++ unicode_annotations_text_cb, ++ NULL, ++ NULL ++ }; ++ GMarkupParseContext *context = NULL; ++ EmojiData data = { 0, }; ++ ++ g_return_val_if_fail (filename != NULL, FALSE); ++ g_return_val_if_fail (dict != 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_unicode_annotations; ++ } ++ ++ data.dict = dict; ++ ++ context = g_markup_parse_context_new (&parser, 0, &data, NULL); ++ if (!g_markup_parse_context_parse (context, content, length, &error)) { ++ g_warning ("Failed to parse %s: %s", filename, error->message); ++ goto failed_to_parse_unicode_annotations; ++ } ++ ++ reset_emoji_element (&data); ++ g_markup_parse_context_free (context); ++ g_free (content); ++ return TRUE; ++ ++failed_to_parse_unicode_annotations: ++ if (error) ++ g_error_free (error); ++ if (data.dict) ++ g_hash_table_destroy (data.dict); ++ if (context) ++ g_markup_parse_context_free (context); ++ g_free (content); ++ return FALSE; ++} ++ ++static gboolean ++parse_emojione_unicode (JsonNode *node, ++ EmojiData *data) ++{ ++ const gchar *str, *unicode; ++ gchar *endptr = NULL; ++ guint32 uch; ++ static gchar outbuf[8] = { 0, }; ++ GString *emoji; ++ ++ if (json_node_get_node_type (node) != JSON_NODE_VALUE) { ++ g_warning ("'unicode' element is not string"); ++ return FALSE; ++ } ++ ++ emoji = g_string_new (NULL); ++ str = unicode = json_node_get_string (node); ++ while (str && *str) { ++ uch = g_ascii_strtoull (str, &endptr, 16); ++ outbuf[g_unichar_to_utf8 (uch, outbuf)] = '\0'; ++ g_string_append (emoji, outbuf); ++ if (*endptr == '\0') { ++ break; ++ } else { ++ switch (*endptr) { ++ case '-': ++ endptr++; ++ break; ++ default: ++ g_warning ("Failed to parse unicode %s", unicode); ++ } + } ++ str = endptr; ++ endptr = NULL; ++ } ++ ++ data->emoji = g_string_free (emoji, FALSE); ++ ++ return TRUE; ++} ++ ++static gboolean ++parse_emojione_shortname (JsonNode *node, ++ EmojiData *data) ++{ ++#if 0 ++ const gchar *shortname; ++ gchar *head, *s; ++ int length; ++ GSList *duplicated; ++ ++ if (json_node_get_node_type (node) != JSON_NODE_VALUE) { ++ g_warning ("'shortname' element is not string"); ++ return FALSE; + } +- /* Get "foo" in */ +- if (g_strcmp0 ((const gchar *) node->name, "td") == 0 && +- node->properties != NULL) { +- parse_attr (node->properties, data); ++ ++ /* The format is ':short_name:' */ ++ shortname = json_node_get_string (node); ++ if (shortname == 0 || *shortname == '\0') ++ return TRUE; ++ if (*shortname != ':') { ++ g_warning ("'shortname' format is different: %s", shortname); ++ return FALSE; + } +- /* Get "foo" in */ +- if (g_strcmp0 ((const gchar *) node->name, "a") == 0 && +- node->properties != NULL) { +- parse_attr (node->properties, data); ++ ++ length = strlen (shortname); ++ head = g_new0 (gchar, length); ++ strcpy (head, shortname + 1); ++ for (s = head; *s; s++) { ++ if (*s == ':') { ++ *s = '\0'; ++ break; ++ } else if (*s == '_') { ++ *s = ' '; ++ } + } +- if (node->children) { +- parse_node (node->children, TRUE, (const gchar *) node->name, data); ++ ++ if (head == NULL || *head == '\0') { ++ g_warning ("'shortname' format is different: %s", shortname); ++ g_free (head); ++ return FALSE; ++ } ++ ++ duplicated = g_slist_find_custom (data->annotations, ++ head, ++ (GCompareFunc) g_strcmp0); ++ if (duplicated == NULL) { ++ data->annotations = g_slist_prepend (data->annotations, ++ head); + } else { +- /* If annotate is NULL likes , +- * the previous emoji cell has the same annotate. +- */ +- if (g_strcmp0 ((const gchar *) node->name, "td") == 0 && +- g_strcmp0 (data->class, "name") == 0) { +- data->annotates = g_slist_copy_deep (data->prev_annotates, +- (GCopyFunc) g_strdup, +- NULL); ++ g_free (head); ++ } ++ ++#endif ++ return TRUE; ++} ++ ++static gboolean ++parse_emojione_category (JsonNode *node, ++ EmojiData *data) ++{ ++ const gchar *category; ++ GSList *duplicated; ++ ++ if (json_node_get_node_type (node) != JSON_NODE_VALUE) { ++ g_warning ("'category' element is not string"); ++ return FALSE; ++ } ++ ++ category = json_node_get_string (node); ++ ++ if (category == NULL || *category == '\0') ++ return TRUE; ++ ++ duplicated = g_slist_find_custom (data->annotations, ++ category, ++ (GCompareFunc) g_strcmp0); ++ if (duplicated == NULL) { ++ data->annotations = g_slist_prepend (data->annotations, ++ g_strdup (category)); ++ } ++ ++ return TRUE; ++} ++ ++static gboolean ++parse_emojione_aliases_ascii (JsonNode *node, ++ EmojiData *data) ++{ ++ JsonArray *aliases_ascii; ++ guint i, length; ++ ++ if (json_node_get_node_type (node) != JSON_NODE_ARRAY) { ++ g_warning ("'aliases_ascii' element is not array"); ++ return FALSE; ++ } ++ ++ aliases_ascii = json_node_get_array (node); ++ length = json_array_get_length (aliases_ascii); ++ for (i = 0; i < length; i++) { ++ const gchar *alias = json_array_get_string_element (aliases_ascii, i); ++ GSList *duplicated = g_slist_find_custom (data->annotations, ++ alias, ++ (GCompareFunc) g_strcmp0); ++ if (duplicated == NULL) { ++ data->annotations = g_slist_prepend (data->annotations, ++ g_strdup (alias)); + } + } +- if (node->next) +- parse_node (node->next, FALSE, (const gchar *) node->name, data); + + return TRUE; + } + +-static GHashTable * +-parse_html (const gchar *filename) ++static gboolean ++parse_emojione_keywords (JsonNode *node, ++ EmojiData *data) + { +- xmlDoc *doc = htmlParseFile (filename, "utf-8"); +- EmojiData data = { 0, }; ++#if 0 ++ JsonArray *keywords; ++ guint i, length; + +- if (doc == NULL || doc->children == NULL) { +- g_warning ("Parse Error in document type: %x", +- doc ? doc->type : 0); ++ if (json_node_get_node_type (node) != JSON_NODE_ARRAY) { ++ g_warning ("'keywords' element is not array"); + return FALSE; + } + +- data.dict = g_hash_table_new_full (g_str_hash, +- g_str_equal, +- g_free, +- free_dict_words); +- parse_node (doc->children, TRUE, (const gchar *) doc->name, &data); ++ keywords = json_node_get_array (node); ++ length = json_array_get_length (keywords); ++ for (i = 0; i < length; i++) { ++ const gchar *keyword = json_array_get_string_element (keywords, i); ++ GSList *duplicated = g_slist_find_custom (data->annotations, ++ keyword, ++ (GCompareFunc) g_strcmp0); ++ if (duplicated == NULL) { ++ data->annotations = g_slist_prepend (data->annotations, ++ g_strdup (keyword)); ++ } ++ } + ++#endif ++ return TRUE; ++} ++ ++static gboolean ++parse_emojione_emoji_data (JsonNode *node, ++ const gchar *member, ++ EmojiData *data) ++{ ++ if (g_strcmp0 (member, "unicode") == 0) ++ return parse_emojione_unicode (node, data); ++ else if (g_strcmp0 (member, "shortname") == 0) ++ return parse_emojione_shortname (node, data); ++ else if (g_strcmp0 (member, "category") == 0) ++ return parse_emojione_category (node, data); ++ else if (g_strcmp0 (member, "aliases_ascii") == 0) ++ return parse_emojione_aliases_ascii (node, data); ++ else if (g_strcmp0 (member, "keywords") == 0) ++ return parse_emojione_keywords (node, data); ++ return TRUE; ++} ++ ++static gboolean ++parse_emojione_element (JsonNode *node, ++ EmojiData *data) ++{ ++ JsonObject *object; ++ GList *members, *m; ++ ++ if (json_node_get_node_type (node) != JSON_NODE_OBJECT) { ++ return FALSE; ++ } ++ ++ reset_emoji_element (data); ++ ++ object = json_node_get_object (node); ++ m = members = json_object_get_members (object); ++ while (m) { ++ const gchar *member = (const gchar *) m->data; ++ if (!parse_emojione_emoji_data (json_object_get_member (object, member), ++ member, ++ data)) { ++ g_list_free (members); ++ return FALSE; ++ } ++ m = m->next; ++ } ++ g_list_free (members); ++ ++ update_emoji_dict (data); ++ ++ return TRUE; ++} ++ ++static gboolean ++emojione_parse_json_file (const gchar *filename, ++ GHashTable *dict) ++{ ++ JsonParser *parser = json_parser_new (); ++ JsonNode *node; ++ JsonObject *object; ++ GList *members, *m; ++ GError *error = NULL; ++ EmojiData data = { 0, }; ++ ++ g_return_val_if_fail (filename != NULL, FALSE); ++ g_return_val_if_fail (dict != NULL, FALSE); ++ ++ if (!json_parser_load_from_file (parser, filename, &error)) { ++ g_error ("%s", error->message); ++ g_error_free (error); ++ goto fail_to_json_file; ++ } ++ ++ node = json_parser_get_root (parser); ++ if (json_node_get_node_type (node) != JSON_NODE_OBJECT) { ++ g_warning ("Json file does not have Json object %s", filename); ++ goto fail_to_json_file; ++ } ++ ++ object = json_node_get_object (node); ++ members = json_object_get_members (object); ++ data.dict = dict; ++ ++ m = members; ++ while (m) { ++ const gchar *member = (const gchar *) m->data; ++ if (!parse_emojione_element (json_object_get_member (object, member), ++ &data)) { ++ g_warning ("Failed to parse member '%s' in %s", member, filename); ++ } ++ m = m->next; ++ } ++ ++ g_list_free (members); + reset_emoji_element (&data); +- g_slist_free_full (data.prev_annotates, g_free); ++ g_object_unref (parser); ++ ++ return TRUE; + +- return data.dict; ++fail_to_json_file: ++ g_object_unref (parser); ++ return FALSE; + } + + int + main (int argc, char *argv[]) + { ++ gchar *prgname; ++ gchar *json_file = NULL; ++ gchar *xml_file = NULL; ++ gchar *output = NULL; ++ GOptionEntry entries[] = { ++ { "json", 'j', 0, G_OPTION_ARG_STRING, &json_file, ++ "Parse Emoji One JSON file", ++ "JSON" ++ }, ++ { "out", 'o', 0, G_OPTION_ARG_STRING, &output, ++ "Save the emoji dictionary as FILE", ++ "FILE" ++ }, ++ { "xml", 'x', 0, G_OPTION_ARG_STRING, &xml_file, ++ "Parse Unocode.org ANNOTATIONS file", ++ "ANNOTATIONS" ++ }, ++ { NULL } ++ }; ++ GOptionContext *context; ++ GError *error = NULL; + GHashTable *dict; +- progname = basename (argv[0]); ++ ++ 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) { +- usage (); ++ 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); + +- dict = parse_html (argv[1]); +- ibus_emoji_dict_save (argv[2], dict); ++ dict = g_hash_table_new_full (g_str_hash, ++ g_str_equal, ++ g_free, ++ free_dict_words); ++ if (xml_file) ++ unicode_annotations_parse_xml_file (xml_file, dict); ++ if (json_file) ++ emojione_parse_json_file (json_file, dict); ++ if (g_hash_table_size (dict) > 0 && output) ++ ibus_emoji_dict_save (output, dict); + g_hash_table_destroy (dict); + + return 0; +-- +2.7.4 + +From f88c48750538eaaf7c7b182ba3763b45c2745074 Mon Sep 17 00:00:00 2001 +From: fujiwarat +Date: Wed, 20 Jul 2016 11:43:49 +0900 +Subject: [PATCH 4/4] Fix typo in configure.ac + +R=shawn.p.huang@gmail.com + +Review URL: https://codereview.appspot.com/303110043 +--- + configure.ac | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/configure.ac b/configure.ac +index f789819..76897f0 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -647,7 +647,7 @@ engine/Makefile + util/Makefile + util/IMdkit/Makefile + data/Makefile +-data/annotations/Makefile.am ++data/annotations/Makefile + data/icons/Makefile + data/keymaps/Makefile + data/dconf/Makefile +-- +2.7.4 + diff --git a/ibus.spec b/ibus.spec index f1fc515..4e73ff8 100644 --- a/ibus.spec +++ b/ibus.spec @@ -28,7 +28,7 @@ Name: ibus Version: 1.5.13 -Release: 4%{?dist} +Release: 5%{?dist} Summary: Intelligent Input Bus for Linux OS License: LGPLv2+ Group: System Environment/Libraries @@ -67,6 +67,8 @@ BuildRequires: libwayland-client-devel %if %with_kde5 BuildRequires: qt5-qtbase-devel %endif +BuildRequires: nodejs-emojione-json +BuildRequires: json-glib-devel Requires: %{name}-libs%{?_isa} = %{version}-%{release} Requires: %{name}-gtk2%{?_isa} = %{version}-%{release} @@ -347,6 +349,7 @@ gtk-query-immodules-3.0-%{__isa_bits} --update-cache &> /dev/null || : %{_datadir}/GConf/gsettings/* %{_datadir}/glib-2.0/schemas/*.xml %{_datadir}/ibus/component +%{_datadir}/ibus/dicts %{_datadir}/ibus/engine %{_datadir}/ibus/keymaps %{_datadir}/icons/hicolor/*/apps/* @@ -412,6 +415,9 @@ gtk-query-immodules-3.0-%{__isa_bits} --update-cache &> /dev/null || : %{_datadir}/gtk-doc/html/* %changelog +* Tue Jul 26 2016 Takao Fujiwara - 1.5.13-5 +- Bug 1359753 - Implement Emoji typing + * Tue Jul 19 2016 Fedora Release Engineering - 1.5.13-4 - https://fedoraproject.org/wiki/Changes/Automatic_Provides_for_Python_RPM_Packages