From 0432aa66b8728bc266da3c2cca84587bc44b3557 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 8 Mar 2016 11:16:24 +0900 Subject: [PATCH] Don't warn if DISPLAY is not set This is normal under Wayland, and not worth warning about. The warnings disrupt unit tests in GNOME continuous, which treat warnings as fatal. BUG=https://github.com/ibus/ibus/pull/1844 R=shawn.p.huang@gmail.com Review URL: https://codereview.appspot.com/289430043 Patch from Matthias Clasen . --- src/ibusshare.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ibusshare.c b/src/ibusshare.c index 63800a6..b793a96 100644 --- a/src/ibusshare.c +++ b/src/ibusshare.c @@ -113,10 +113,7 @@ ibus_get_socket_path (void) display = g_strdup (_display); } - if (display == NULL) { - g_warning ("DISPLAY is empty! We use default DISPLAY (:0.0)"); - } - else { + if (display) { p = display; hostname = display; for (; *p != ':' && *p != '\0'; p++); -- 2.7.4 From 84c18f1d382548c52138822a11473d2dac79e485 Mon Sep 17 00:00:00 2001 From: Rui Matos Date: Wed, 25 May 2016 11:21:09 +0900 Subject: [PATCH] Install a DBus service file With the transition to user scoped DBus sessions (vs. login sessions) there's a need to start ibus-daemon via DBus activation so that the process gets properly tracked and disposed of when the login session ends. Otherwise the ibus-daemon process lingers on and keeps the whole login session up. We already connect and own a well known name on DBus. The remaining missing piece is the DBus service file which we introduce here. BUG=https://github.com/ibus/ibus/pull/1853 R=Shawn.P.Huang@gmail.com Review URL: https://codereview.appspot.com/295340043 Patch from Rui Matos . --- bus/Makefile.am | 11 +++++++++++ bus/org.freedesktop.IBus.service.in | 3 +++ 2 files changed, 14 insertions(+) create mode 100644 bus/org.freedesktop.IBus.service.in diff --git a/bus/Makefile.am b/bus/Makefile.am index 26cb2f8..4dabacf 100644 --- a/bus/Makefile.am +++ b/bus/Makefile.am @@ -174,4 +174,15 @@ man_onedir = $(mandir)/man1 %.1.gz: %.1 $(AM_V_GEN) gzip -c $< > $@.tmp && mv $@.tmp $@ + +dbusservice_in_files = org.freedesktop.IBus.service.in +dbusservice_DATA = $(dbusservice_in_files:.service.in=.service) +dbusservicedir=${datadir}/dbus-1/services + +org.freedesktop.IBus.service: org.freedesktop.IBus.service.in + $(AM_V_GEN) sed -e "s|\@bindir\@|$(bindir)|" $< > $@.tmp && mv $@.tmp $@ + +EXTRA_DIST += $(dbusservice_in_files) +CLEANFILES += $(dbusservice_DATA) + -include $(top_srcdir)/git.mk diff --git a/bus/org.freedesktop.IBus.service.in b/bus/org.freedesktop.IBus.service.in new file mode 100644 index 0000000..cc88834 --- /dev/null +++ b/bus/org.freedesktop.IBus.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.freedesktop.IBus +Exec=@bindir@/ibus-daemon --replace --xim --panel disable -- 2.7.4 From 3ef21fef0135f7b4fe9611d201f15611734f6c51 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Fri, 3 Jun 2016 11:52:29 +0900 Subject: [PATCH] client/gtk2: Fix SEGV with Wayland display Delete gdk_display_get_name() in GTK clients because ibus-daemon does not use gdk_display_get_name(). GdkX11Dislay and GdkX11Window does not work with Wayland. BUG=https://github.com/ibus/ibus/issues/1859 R=Shawn.P.Huang@gmail.com Review URL: https://codereview.appspot.com/301760043 --- client/gtk2/ibusimcontext.c | 5 ----- setup/main.py | 7 +++++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c index 9d927e6..0df0062 100644 --- a/client/gtk2/ibusimcontext.c +++ b/client/gtk2/ibusimcontext.c @@ -583,11 +583,6 @@ ibus_im_context_class_init (IBusIMContextClass *class) /* init bus object */ if (_bus == NULL) { - const gchar *dname = gdk_display_get_name (gdk_display_get_default ()); - /* ibus-daemon uses DISPLAY variable. */ - if (g_strcmp0 (dname, "Wayland") == 0) - dname = g_getenv ("DISPLAY"); - ibus_set_display (dname); _bus = ibus_bus_new_async (); /* init the global fake context */ diff --git a/setup/main.py b/setup/main.py index 26c2b0f..e1f7a9d 100644 --- a/setup/main.py +++ b/setup/main.py @@ -275,8 +275,11 @@ class Setup(object): self.__init_general() def __gdk_window_set_cb(self, object, pspec): - str = '%u' % GdkX11.X11Window.get_xid(object.get_window()) - GLib.setenv('IBUS_SETUP_XID', str, True) + window = object.get_window() + if type(window) != GdkX11.X11Window: + return + s = '%u' % GdkX11.X11Window.get_xid(window) + GLib.setenv('IBUS_SETUP_XID', s, True) def __combobox_notify_active_engine_cb(self, combobox, property): engine = self.__combobox.get_active_engine() -- 2.7.4 From a598ae29223d1ca25e76bf7d7de9703f63ea337e Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Fri, 3 Jun 2016 19:44:11 +0900 Subject: [PATCH] ui/gtk3: Fix panel CSS format for GTK 3.20 CSS node names have been changed since GTK 3.20 and the font size and widget color no longer work with the previous node names. BUG=https://github.com/ibus/ibus/issues/1856 Review URL: https://codereview.appspot.com/297380043 --- ui/gtk3/candidatearea.vala | 16 +++++++++++++--- ui/gtk3/handle.vala | 10 ++++++++-- ui/gtk3/panel.vala | 9 +++++++-- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/ui/gtk3/candidatearea.vala b/ui/gtk3/candidatearea.vala index c969312..3848f0d 100644 --- a/ui/gtk3/candidatearea.vala +++ b/ui/gtk3/candidatearea.vala @@ -3,7 +3,7 @@ * ibus - The Input Bus * * Copyright(c) 2011-2015 Peng Huang - * Copyright(c) 2015 Takao Fujiwara + * Copyright(c) 2015-2016 Takao Fujiwara * * 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,10 @@ class CandidateArea : Gtk.Box { private uint m_focus_candidate; private bool m_show_cursor; + private bool m_use_latest_css_format = + ((Gtk.MAJOR_VERSION > 3) || + (Gtk.MAJOR_VERSION == 3) && (Gtk.MINOR_VERSION >= 20)); + private const string LABELS[] = { "1.", "2.", "3.", "4.", "5.", "6.", "7.", "8.", "9.", "0.", "a.", "b.", "c.", "d.", "e.", "f." @@ -103,7 +107,8 @@ class CandidateArea : Gtk.Box { Pango.AttrList attrs = get_pango_attr_list_from_ibus_text(candidates[i]); if (i == focus_candidate && show_cursor) { Gtk.StyleContext context = m_candidates[i].get_style_context(); - Gdk.RGBA color = context.get_color(Gtk.StateFlags.SELECTED); + Gdk.RGBA *color = null; + context.get(Gtk.StateFlags.SELECTED, "color", out color); Pango.Attribute pango_attr = Pango.attr_foreground_new( (uint16)(color.red * uint16.MAX), (uint16)(color.green * uint16.MAX), @@ -112,7 +117,12 @@ class CandidateArea : Gtk.Box { pango_attr.end_index = candidates[i].get_text().length; attrs.insert((owned)pango_attr); - color = context.get_background_color(Gtk.StateFlags.SELECTED); + color = null; + string bg_prop = + m_use_latest_css_format + ? "-gtk-secondary-caret-color" + : "background-color"; + context.get(Gtk.StateFlags.SELECTED, bg_prop, out color); pango_attr = Pango.attr_background_new( (uint16)(color.red * uint16.MAX), (uint16)(color.green * uint16.MAX), diff --git a/ui/gtk3/handle.vala b/ui/gtk3/handle.vala index 1edb537..bef5e8b 100644 --- a/ui/gtk3/handle.vala +++ b/ui/gtk3/handle.vala @@ -2,7 +2,8 @@ * * ibus - The Input Bus * - * Copyright(c) 2011-2015 Peng Huang + * Copyright(c) 2011-2016 Peng Huang + * Copyright(c) 2016 Takao Fujiwara * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -29,6 +30,11 @@ class Handle : Gtk.EventBox { public signal void move_end(); public Handle() { + // Call base class constructor + GLib.Object( + name : "IBusHandle" + ); + set_size_request(6, -1); Gdk.EventMask mask = Gdk.EventMask.EXPOSURE_MASK | Gdk.EventMask.BUTTON_PRESS_MASK | @@ -42,7 +48,7 @@ class Handle : Gtk.EventBox { Gtk.CssProvider css_provider = new Gtk.CssProvider(); try { css_provider.load_from_data( - "GtkEventBox { background-color: gray }", -1); + "#IBusHandle { background-color: gray }", -1); } catch (GLib.Error error) { warning("Parse error in Handle: %s", error.message); } diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala index 2ca3a5e..cc19350 100644 --- a/ui/gtk3/panel.vala +++ b/ui/gtk3/panel.vala @@ -3,7 +3,7 @@ * ibus - The Input Bus * * Copyright(c) 2011-2014 Peng Huang - * Copyright(c) 2015 Takao Fujwiara + * Copyright(c) 2015-2016 Takao Fujwiara * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -543,7 +543,12 @@ class Panel : IBus.PanelService { return; } - string data_format = "GtkLabel { font: %s; }"; + string data_format = "label { font: %s; }"; + if (Gtk.MAJOR_VERSION < 3 || + (Gtk.MAJOR_VERSION == 3 && Gtk.MINOR_VERSION < 20)) { + data_format = "GtkLabel { font: %s; }"; + } + string data = data_format.printf(font_name); m_css_provider = new Gtk.CssProvider(); -- 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